qtnfmac: process HE capabilities requests
authorMikhail Karpenko <mkarpenko@quantenna.com>
Mon, 18 Nov 2019 08:23:16 +0000 (08:23 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Wed, 20 Nov 2019 07:43:01 +0000 (09:43 +0200)
Pass HE interface type data requests between firmware and driver.

Signed-off-by: Mikhail Karpenko <mkarpenko@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/core.c

index 1c1b377932cfbabb18cf35d3fcca262ba4fb18f3..548f6ff6d0f20fa886d42a62a54d862c8450b4b2 100644 (file)
@@ -214,6 +214,20 @@ static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
        return true;
 }
 
+static void qtnf_cmd_tlv_ie_ext_add(struct sk_buff *cmd_skb, u8 eid_ext,
+                                   const void *buf, size_t len)
+{
+       struct qlink_tlv_ext_ie *tlv;
+
+       tlv = (struct qlink_tlv_ext_ie *)skb_put(cmd_skb, sizeof(*tlv) + len);
+       tlv->hdr.type = cpu_to_le16(WLAN_EID_EXTENSION);
+       tlv->hdr.len = cpu_to_le16(sizeof(*tlv) + len - sizeof(tlv->hdr));
+       tlv->eid_ext = eid_ext;
+
+       if (len && buf)
+               memcpy(tlv->ie_data, buf, len);
+}
+
 int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
                           const struct cfg80211_ap_settings *s)
 {
@@ -309,6 +323,10 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
                memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
        }
 
+       if (s->he_cap)
+               qtnf_cmd_tlv_ie_ext_add(cmd_skb, WLAN_EID_EXT_HE_CAPABILITY,
+                                       s->he_cap, sizeof(*s->he_cap));
+
        if (s->acl) {
                size_t acl_size = struct_size(s->acl, mac_addrs,
                                              s->acl->n_acl_entries);
@@ -1292,6 +1310,69 @@ static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info,
        memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs));
 }
 
+static void qtnf_cmd_conv_iftype(struct ieee80211_sband_iftype_data
+                                 *iftype_data,
+                                 const struct qlink_sband_iftype_data
+                                 *qlink_data)
+{
+       iftype_data->types_mask = le16_to_cpu(qlink_data->types_mask);
+
+       iftype_data->he_cap.has_he = true;
+       memcpy(&iftype_data->he_cap.he_cap_elem, &qlink_data->he_cap_elem,
+              sizeof(qlink_data->he_cap_elem));
+       memcpy(iftype_data->he_cap.ppe_thres, qlink_data->ppe_thres,
+              ARRAY_SIZE(qlink_data->ppe_thres));
+
+       iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80 =
+               qlink_data->he_mcs_nss_supp.rx_mcs_80;
+       iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80 =
+               qlink_data->he_mcs_nss_supp.tx_mcs_80;
+       iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160 =
+               qlink_data->he_mcs_nss_supp.rx_mcs_160;
+       iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160 =
+               qlink_data->he_mcs_nss_supp.tx_mcs_160;
+       iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80p80 =
+               qlink_data->he_mcs_nss_supp.rx_mcs_80p80;
+       iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80p80 =
+               qlink_data->he_mcs_nss_supp.tx_mcs_80p80;
+}
+
+static int qtnf_cmd_band_fill_iftype(const u8 *data,
+                                    struct ieee80211_supported_band *band)
+{
+       unsigned int i;
+       struct ieee80211_sband_iftype_data *iftype_data;
+       const struct qlink_tlv_iftype_data *tlv =
+               (const struct qlink_tlv_iftype_data *)data;
+       size_t payload_len = tlv->n_iftype_data * sizeof(*tlv->iftype_data) +
+               sizeof(*tlv) -
+               sizeof(struct qlink_tlv_hdr);
+
+       if (tlv->hdr.len != cpu_to_le16(payload_len)) {
+               pr_err("bad IFTYPE_DATA TLV len %u\n", tlv->hdr.len);
+               return -EINVAL;
+       }
+
+       kfree(band->iftype_data);
+       band->iftype_data = NULL;
+       band->n_iftype_data = tlv->n_iftype_data;
+       if (band->n_iftype_data == 0)
+               return 0;
+
+       iftype_data = kcalloc(band->n_iftype_data, sizeof(*iftype_data),
+                             GFP_KERNEL);
+       if (!iftype_data) {
+               band->n_iftype_data = 0;
+               return -ENOMEM;
+       }
+       band->iftype_data = iftype_data;
+
+       for (i = 0; i < band->n_iftype_data; i++)
+               qtnf_cmd_conv_iftype(iftype_data++, &tlv->iftype_data[i]);
+
+       return 0;
+}
+
 static int
 qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
                             struct qlink_resp_band_info_get *resp,
@@ -1305,6 +1386,7 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
        struct ieee80211_channel *chan;
        unsigned int chidx = 0;
        u32 qflags;
+       int ret = -EINVAL;
 
        memset(&band->ht_cap, 0, sizeof(band->ht_cap));
        memset(&band->vht_cap, 0, sizeof(band->vht_cap));
@@ -1442,6 +1524,12 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
                        qtnf_cmd_resp_band_fill_vhtcap(tlv->val,
                                                       &band->vht_cap);
                        break;
+               case QTN_TLV_ID_IFTYPE_DATA:
+                       ret = qtnf_cmd_band_fill_iftype((const uint8_t *)tlv,
+                                                       band);
+                       if (ret)
+                               goto error_ret;
+                       break;
                default:
                        pr_warn("unknown TLV type: %#x\n", tlv_type);
                        break;
@@ -1469,7 +1557,7 @@ error_ret:
        band->channels = NULL;
        band->n_channels = 0;
 
-       return -EINVAL;
+       return ret;
 }
 
 static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac,
index f5aa2c547b942c4214f9b69452dca564e9a90550..5fb598389487729f2a653559d155b3b626b7b80e 100644 (file)
@@ -543,6 +543,9 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
                if (!wiphy->bands[band])
                        continue;
 
+               kfree(wiphy->bands[band]->iftype_data);
+               wiphy->bands[band]->n_iftype_data = 0;
+
                kfree(wiphy->bands[band]->channels);
                wiphy->bands[band]->n_channels = 0;