Commit c698bce0 authored by Igor Mitsyanko's avatar Igor Mitsyanko Committed by Kalle Valo

qtnfmac: allow each MAC to specify its own regulatory rules

Currently driver uses the same regulatory rules to register all wiphy
instances. This is not logically correct since each wiphy may have
different capabilities (different supported bands, EIRP etc).
Allow firmware to pass regulatory rules for each MAC separately.
Signed-off-by: default avatarIgor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 48cefdfb
......@@ -1145,17 +1145,16 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->wowlan = macinfo->wowlan;
#endif
regdomain_is_known = isalpha(hw_info->rd->alpha2[0]) &&
isalpha(hw_info->rd->alpha2[1]);
regdomain_is_known = isalpha(mac->rd->alpha2[0]) &&
isalpha(mac->rd->alpha2[1]);
if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) {
wiphy->reg_notifier = qtnf_cfg80211_reg_notifier;
if (hw_info->rd->alpha2[0] == '9' &&
hw_info->rd->alpha2[1] == '9') {
if (mac->rd->alpha2[0] == '9' && mac->rd->alpha2[1] == '9') {
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_STRICT_REG;
wiphy_apply_custom_regulatory(wiphy, hw_info->rd);
wiphy_apply_custom_regulatory(wiphy, mac->rd);
} else if (regdomain_is_known) {
wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
}
......@@ -1181,9 +1180,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
goto out;
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd);
ret = regulatory_set_wiphy_regd(wiphy, mac->rd);
else if (regdomain_is_known)
ret = regulatory_hint(wiphy, hw_info->rd->alpha2);
ret = regulatory_hint(wiphy, mac->rd->alpha2);
out:
return ret;
......
......@@ -831,55 +831,6 @@ int qtnf_cmd_send_del_intf(struct qtnf_vif *vif)
return ret;
}
static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags)
{
u32 flags = 0;
if (qflags & QLINK_RRF_NO_OFDM)
flags |= NL80211_RRF_NO_OFDM;
if (qflags & QLINK_RRF_NO_CCK)
flags |= NL80211_RRF_NO_CCK;
if (qflags & QLINK_RRF_NO_INDOOR)
flags |= NL80211_RRF_NO_INDOOR;
if (qflags & QLINK_RRF_NO_OUTDOOR)
flags |= NL80211_RRF_NO_OUTDOOR;
if (qflags & QLINK_RRF_DFS)
flags |= NL80211_RRF_DFS;
if (qflags & QLINK_RRF_PTP_ONLY)
flags |= NL80211_RRF_PTP_ONLY;
if (qflags & QLINK_RRF_PTMP_ONLY)
flags |= NL80211_RRF_PTMP_ONLY;
if (qflags & QLINK_RRF_NO_IR)
flags |= NL80211_RRF_NO_IR;
if (qflags & QLINK_RRF_AUTO_BW)
flags |= NL80211_RRF_AUTO_BW;
if (qflags & QLINK_RRF_IR_CONCURRENT)
flags |= NL80211_RRF_IR_CONCURRENT;
if (qflags & QLINK_RRF_NO_HT40MINUS)
flags |= NL80211_RRF_NO_HT40MINUS;
if (qflags & QLINK_RRF_NO_HT40PLUS)
flags |= NL80211_RRF_NO_HT40PLUS;
if (qflags & QLINK_RRF_NO_80MHZ)
flags |= NL80211_RRF_NO_80MHZ;
if (qflags & QLINK_RRF_NO_160MHZ)
flags |= NL80211_RRF_NO_160MHZ;
return flags;
}
static int
qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
const struct qlink_resp_get_hw_info *resp,
......@@ -887,7 +838,6 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
{
struct qtnf_hw_info *hwinfo = &bus->hw_info;
const struct qlink_tlv_hdr *tlv;
const struct qlink_tlv_reg_rule *tlv_rule;
const char *bld_name = NULL;
const char *bld_rev = NULL;
const char *bld_type = NULL;
......@@ -898,19 +848,8 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
const char *calibration_ver = NULL;
const char *uboot_ver = NULL;
u32 hw_ver = 0;
struct ieee80211_reg_rule *rule;
u16 tlv_type;
u16 tlv_value_len;
unsigned int rule_idx = 0;
if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
return -E2BIG;
hwinfo->rd = kzalloc(struct_size(hwinfo->rd, reg_rules,
resp->n_reg_rules), GFP_KERNEL);
if (!hwinfo->rd)
return -ENOMEM;
hwinfo->num_mac = resp->num_mac;
hwinfo->mac_bitmap = resp->mac_bitmap;
......@@ -919,30 +858,11 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
hwinfo->total_tx_chain = resp->total_tx_chain;
hwinfo->total_rx_chain = resp->total_rx_chain;
hwinfo->hw_capab = le32_to_cpu(resp->hw_capab);
hwinfo->rd->n_reg_rules = resp->n_reg_rules;
hwinfo->rd->alpha2[0] = resp->alpha2[0];
hwinfo->rd->alpha2[1] = resp->alpha2[1];
bld_tmstamp = le32_to_cpu(resp->bld_tmstamp);
plat_id = le32_to_cpu(resp->plat_id);
hw_ver = le32_to_cpu(resp->hw_ver);
switch (resp->dfs_region) {
case QLINK_DFS_FCC:
hwinfo->rd->dfs_region = NL80211_DFS_FCC;
break;
case QLINK_DFS_ETSI:
hwinfo->rd->dfs_region = NL80211_DFS_ETSI;
break;
case QLINK_DFS_JP:
hwinfo->rd->dfs_region = NL80211_DFS_JP;
break;
case QLINK_DFS_UNSET:
default:
hwinfo->rd->dfs_region = NL80211_DFS_UNSET;
break;
}
tlv = (const struct qlink_tlv_hdr *)resp->info;
while (info_len >= sizeof(*tlv)) {
......@@ -956,37 +876,6 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
}
switch (tlv_type) {
case QTN_TLV_ID_REG_RULE:
if (rule_idx >= resp->n_reg_rules) {
pr_warn("unexpected number of rules: %u\n",
resp->n_reg_rules);
return -EINVAL;
}
if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) {
pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
tlv_type, tlv_value_len);
return -EINVAL;
}
tlv_rule = (const struct qlink_tlv_reg_rule *)tlv;
rule = &hwinfo->rd->reg_rules[rule_idx++];
rule->freq_range.start_freq_khz =
le32_to_cpu(tlv_rule->start_freq_khz);
rule->freq_range.end_freq_khz =
le32_to_cpu(tlv_rule->end_freq_khz);
rule->freq_range.max_bandwidth_khz =
le32_to_cpu(tlv_rule->max_bandwidth_khz);
rule->power_rule.max_antenna_gain =
le32_to_cpu(tlv_rule->max_antenna_gain);
rule->power_rule.max_eirp =
le32_to_cpu(tlv_rule->max_eirp);
rule->dfs_cac_ms =
le32_to_cpu(tlv_rule->dfs_cac_ms);
rule->flags = qtnf_cmd_resp_reg_rule_flags_parse(
le32_to_cpu(tlv_rule->flags));
break;
case QTN_TLV_ID_BUILD_NAME:
bld_name = (const void *)tlv->val;
break;
......@@ -1019,17 +908,8 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
}
if (rule_idx != resp->n_reg_rules) {
pr_warn("unexpected number of rules: expected %u got %u\n",
resp->n_reg_rules, rule_idx);
kfree(hwinfo->rd);
hwinfo->rd = NULL;
return -EINVAL;
}
pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n",
pr_info("fw_version=%d, MACs map %#x, chains Tx=%u Rx=%u, capab=0x%x\n",
hwinfo->fw_ver, hwinfo->mac_bitmap,
hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1],
hwinfo->total_tx_chain, hwinfo->total_rx_chain,
hwinfo->hw_capab);
......@@ -1085,9 +965,12 @@ qtnf_parse_wowlan_info(struct qtnf_wmac *mac,
}
}
static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
const u8 *tlv_buf, size_t tlv_buf_size)
static int
qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
const struct qlink_resp_get_mac_info *resp,
size_t tlv_buf_size)
{
const u8 *tlv_buf = resp->var_info;
struct ieee80211_iface_combination *comb = NULL;
size_t n_comb = 0;
struct ieee80211_iface_limit *limits;
......@@ -1105,6 +988,38 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
u8 ext_capa_len = 0;
u8 ext_capa_mask_len = 0;
int i = 0;
struct ieee80211_reg_rule *rule;
unsigned int rule_idx = 0;
const struct qlink_tlv_reg_rule *tlv_rule;
if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
return -E2BIG;
mac->rd = kzalloc(sizeof(*mac->rd) +
sizeof(struct ieee80211_reg_rule) *
resp->n_reg_rules, GFP_KERNEL);
if (!mac->rd)
return -ENOMEM;
mac->rd->n_reg_rules = resp->n_reg_rules;
mac->rd->alpha2[0] = resp->alpha2[0];
mac->rd->alpha2[1] = resp->alpha2[1];
switch (resp->dfs_region) {
case QLINK_DFS_FCC:
mac->rd->dfs_region = NL80211_DFS_FCC;
break;
case QLINK_DFS_ETSI:
mac->rd->dfs_region = NL80211_DFS_ETSI;
break;
case QLINK_DFS_JP:
mac->rd->dfs_region = NL80211_DFS_JP;
break;
case QLINK_DFS_UNSET:
default:
mac->rd->dfs_region = NL80211_DFS_UNSET;
break;
}
tlv = (const struct qlink_tlv_hdr *)tlv_buf;
while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) {
......@@ -1225,6 +1140,23 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
mac->macinfo.wowlan = NULL;
qtnf_parse_wowlan_info(mac, wowlan);
break;
case QTN_TLV_ID_REG_RULE:
if (rule_idx >= resp->n_reg_rules) {
pr_warn("unexpected number of rules: %u\n",
resp->n_reg_rules);
return -EINVAL;
}
if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) {
pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
tlv_type, tlv_value_len);
return -EINVAL;
}
tlv_rule = (const struct qlink_tlv_reg_rule *)tlv;
rule = &mac->rd->reg_rules[rule_idx++];
qlink_utils_regrule_q2nl(rule, tlv_rule);
break;
default:
pr_warn("MAC%u: unknown TLV type %u\n",
mac->macid, tlv_type);
......@@ -1253,6 +1185,12 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
return -EINVAL;
}
if (rule_idx != resp->n_reg_rules) {
pr_warn("unexpected number of rules: expected %u got %u\n",
resp->n_reg_rules, rule_idx);
return -EINVAL;
}
if (ext_capa_len > 0) {
ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL);
if (!ext_capa)
......@@ -1663,7 +1601,7 @@ int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
resp = (const struct qlink_resp_get_mac_info *)resp_skb->data;
qtnf_cmd_resp_proc_mac_info(mac, resp);
ret = qtnf_parse_variable_mac_info(mac, resp->var_info, var_data_len);
ret = qtnf_parse_variable_mac_info(mac, resp, var_data_len);
out:
qtnf_bus_unlock(mac->bus);
......
......@@ -499,6 +499,8 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
qtnf_mac_iface_comb_free(mac);
qtnf_mac_ext_caps_free(mac);
kfree(mac->macinfo.wowlan);
kfree(mac->rd);
mac->rd = NULL;
wiphy_free(wiphy);
bus->mac[macid] = NULL;
}
......@@ -665,9 +667,6 @@ void qtnf_core_detach(struct qtnf_bus *bus)
destroy_workqueue(bus->workqueue);
}
kfree(bus->hw_info.rd);
bus->hw_info.rd = NULL;
qtnf_trans_free(bus);
}
EXPORT_SYMBOL_GPL(qtnf_core_detach);
......
......@@ -112,6 +112,7 @@ struct qtnf_wmac {
struct cfg80211_scan_request *scan_req;
struct mutex mac_lock; /* lock during wmac speicific ops */
struct delayed_work scan_timeout;
struct ieee80211_regdomain *rd;
};
struct qtnf_hw_info {
......@@ -120,7 +121,6 @@ struct qtnf_hw_info {
u8 mac_bitmap;
u32 fw_ver;
u32 hw_capab;
struct ieee80211_regdomain *rd;
u8 total_tx_chain;
u8 total_rx_chain;
char fw_version[ETHTOOL_FWVERS_LEN];
......
......@@ -6,7 +6,7 @@
#include <linux/ieee80211.h>
#define QLINK_PROTO_VER 14
#define QLINK_PROTO_VER 15
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
......@@ -770,6 +770,18 @@ struct qlink_resp {
u8 vifid;
} __packed;
/**
* enum qlink_dfs_regions - regulatory DFS regions
*
* Corresponds to &enum nl80211_dfs_regions.
*/
enum qlink_dfs_regions {
QLINK_DFS_UNSET = 0,
QLINK_DFS_FCC = 1,
QLINK_DFS_ETSI = 2,
QLINK_DFS_JP = 3,
};
/**
* struct qlink_resp_get_mac_info - response for QLINK_CMD_MAC_INFO command
*
......@@ -785,6 +797,10 @@ struct qlink_resp {
* @bands_cap: wireless bands WMAC can operate in, bitmap of &enum qlink_band.
* @max_ap_assoc_sta: Maximum number of associations supported by WMAC.
* @radar_detect_widths: bitmask of channels BW for which WMAC can detect radar.
* @alpha2: country code ID firmware is configured to.
* @n_reg_rules: number of regulatory rules TLVs in variable portion of the
* message.
* @dfs_region: regulatory DFS region, one of @enum qlink_dfs_region.
* @var_info: variable-length WMAC info data.
*/
struct qlink_resp_get_mac_info {
......@@ -798,22 +814,13 @@ struct qlink_resp_get_mac_info {
__le16 radar_detect_widths;
__le32 max_acl_mac_addrs;
u8 bands_cap;
u8 alpha2[2];
u8 n_reg_rules;
u8 dfs_region;
u8 rsvd[1];
u8 var_info[0];
} __packed;
/**
* enum qlink_dfs_regions - regulatory DFS regions
*
* Corresponds to &enum nl80211_dfs_regions.
*/
enum qlink_dfs_regions {
QLINK_DFS_UNSET = 0,
QLINK_DFS_FCC = 1,
QLINK_DFS_ETSI = 2,
QLINK_DFS_JP = 3,
};
/**
* struct qlink_resp_get_hw_info - response for QLINK_CMD_GET_HW_INFO command
*
......@@ -826,11 +833,7 @@ enum qlink_dfs_regions {
* @mac_bitmap: Bitmap of MAC IDs that are active and can be used in firmware.
* @total_tx_chains: total number of transmit chains used by device.
* @total_rx_chains: total number of receive chains.
* @alpha2: country code ID firmware is configured to.
* @n_reg_rules: number of regulatory rules TLVs in variable portion of the
* message.
* @dfs_region: regulatory DFS region, one of @enum qlink_dfs_region.
* @info: variable-length HW info, can contain QTN_TLV_ID_REG_RULE.
* @info: variable-length HW info.
*/
struct qlink_resp_get_hw_info {
struct qlink_resp rhdr;
......@@ -844,9 +847,6 @@ struct qlink_resp_get_hw_info {
u8 mac_bitmap;
u8 total_tx_chain;
u8 total_rx_chain;
u8 alpha2[2];
u8 n_reg_rules;
u8 dfs_region;
u8 info[0];
} __packed;
......
......@@ -237,3 +237,65 @@ u32 qlink_utils_chflags_cfg2q(u32 cfgflags)
return flags;
}
static u32 qtnf_reg_rule_flags_parse(u32 qflags)
{
u32 flags = 0;
if (qflags & QLINK_RRF_NO_OFDM)
flags |= NL80211_RRF_NO_OFDM;
if (qflags & QLINK_RRF_NO_CCK)
flags |= NL80211_RRF_NO_CCK;
if (qflags & QLINK_RRF_NO_INDOOR)
flags |= NL80211_RRF_NO_INDOOR;
if (qflags & QLINK_RRF_NO_OUTDOOR)
flags |= NL80211_RRF_NO_OUTDOOR;
if (qflags & QLINK_RRF_DFS)
flags |= NL80211_RRF_DFS;
if (qflags & QLINK_RRF_PTP_ONLY)
flags |= NL80211_RRF_PTP_ONLY;
if (qflags & QLINK_RRF_PTMP_ONLY)
flags |= NL80211_RRF_PTMP_ONLY;
if (qflags & QLINK_RRF_NO_IR)
flags |= NL80211_RRF_NO_IR;
if (qflags & QLINK_RRF_AUTO_BW)
flags |= NL80211_RRF_AUTO_BW;
if (qflags & QLINK_RRF_IR_CONCURRENT)
flags |= NL80211_RRF_IR_CONCURRENT;
if (qflags & QLINK_RRF_NO_HT40MINUS)
flags |= NL80211_RRF_NO_HT40MINUS;
if (qflags & QLINK_RRF_NO_HT40PLUS)
flags |= NL80211_RRF_NO_HT40PLUS;
if (qflags & QLINK_RRF_NO_80MHZ)
flags |= NL80211_RRF_NO_80MHZ;
if (qflags & QLINK_RRF_NO_160MHZ)
flags |= NL80211_RRF_NO_160MHZ;
return flags;
}
void qlink_utils_regrule_q2nl(struct ieee80211_reg_rule *rule,
const struct qlink_tlv_reg_rule *tlv)
{
rule->freq_range.start_freq_khz = le32_to_cpu(tlv->start_freq_khz);
rule->freq_range.end_freq_khz = le32_to_cpu(tlv->end_freq_khz);
rule->freq_range.max_bandwidth_khz =
le32_to_cpu(tlv->max_bandwidth_khz);
rule->power_rule.max_antenna_gain = le32_to_cpu(tlv->max_antenna_gain);
rule->power_rule.max_eirp = le32_to_cpu(tlv->max_eirp);
rule->dfs_cac_ms = le32_to_cpu(tlv->dfs_cac_ms);
rule->flags = qtnf_reg_rule_flags_parse(le32_to_cpu(tlv->flags));
}
......@@ -82,5 +82,7 @@ void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
enum qlink_band qlink_utils_band_cfg2q(enum nl80211_band band);
enum qlink_dfs_state qlink_utils_dfs_state_cfg2q(enum nl80211_dfs_state state);
u32 qlink_utils_chflags_cfg2q(u32 cfgflags);
void qlink_utils_regrule_q2nl(struct ieee80211_reg_rule *rule,
const struct qlink_tlv_reg_rule *tlv_rule);
#endif /* _QTN_FMAC_QLINK_UTIL_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment