qtnfmac: allow each MAC to specify its own regulatory rules
authorIgor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Wed, 20 Mar 2019 10:03:57 +0000 (10:03 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 4 Apr 2019 09:57:28 +0000 (12:57 +0300)
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>
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/core.c
drivers/net/wireless/quantenna/qtnfmac/core.h
drivers/net/wireless/quantenna/qtnfmac/qlink.h
drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
drivers/net/wireless/quantenna/qtnfmac/qlink_util.h

index 3131ced8801fa890589d180e67fee7a34915a03c..cea9484667448bfe2c66d95239f0a10ea506c7c7 100644 (file)
@@ -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;
index e61bec7c5d8a57d5fd5416285277a140568da5cd..1a248d9f2e4cd52a937738a1eb132ae31f986162 100644 (file)
@@ -831,55 +831,6 @@ out:
        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);
index ee1b75fda1dd2c1b7a0125a46838d8cdf862c14e..f04f4e1f7d68749f5b9fecc640029fd24476379a 100644 (file)
@@ -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);
index a31cff46e9640faa706813d7a407577d9d7b2730..1b983ec9afcc8d9d8c2e99f685a827022ad9a2d3 100644 (file)
@@ -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];
index ca84684a1a93cdef085682d22608d3c36c3ed826..6951f6370985e98c73554e39425ffc97a6f79f43 100644 (file)
@@ -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;
 
index 8cae9d8d1ab6fd7eb6bdd9642c699844d847d95d..1a972bce7b8bdc5c39ddd1ead1fba3b071aedfcd 100644 (file)
@@ -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));
+}
index 9d10a2098ca74f7b6ccf435827ab18790d26ec6f..f873beed2ae75b34539d50a1ef190c03dcb4c160 100644 (file)
@@ -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_ */