Skip to content

Commit

Permalink
qtnfmac: allow each MAC to specify its own regulatory rules
Browse files Browse the repository at this point in the history
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: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Igor Mitsyanko authored and Kalle Valo committed Apr 4, 2019
1 parent 48cefdf commit c698bce
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 156 deletions.
13 changes: 6 additions & 7 deletions drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
186 changes: 62 additions & 124 deletions drivers/net/wireless/quantenna/qtnfmac/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -831,63 +831,13 @@ 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,
size_t info_len)
{
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;
Expand All @@ -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;
Expand All @@ -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)) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 2 additions & 3 deletions drivers/net/wireless/quantenna/qtnfmac/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/quantenna/qtnfmac/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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];
Expand Down
Loading

0 comments on commit c698bce

Please sign in to comment.