diff options
Diffstat (limited to 'drivers/net/wireless/quantenna/qtnfmac/commands.c')
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/commands.c | 689 |
1 files changed, 316 insertions, 373 deletions
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index d0d7ec8794c4..f40d8c3c3d9e 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -11,11 +11,11 @@ #include "bus.h" #include "commands.h" +/* Let device itself to select best values for current conditions */ #define QTNF_SCAN_TIME_AUTO 0 -/* Let device itself to select best values for current conditions */ -#define QTNF_SCAN_DWELL_ACTIVE_DEFAULT QTNF_SCAN_TIME_AUTO -#define QTNF_SCAN_DWELL_PASSIVE_DEFAULT QTNF_SCAN_TIME_AUTO +#define QTNF_SCAN_DWELL_ACTIVE_DEFAULT 90 +#define QTNF_SCAN_DWELL_PASSIVE_DEFAULT 100 #define QTNF_SCAN_SAMPLE_DURATION_DEFAULT QTNF_SCAN_TIME_AUTO static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp, @@ -175,7 +175,8 @@ static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type, { struct qlink_tlv_ie_set *tlv; - tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + len); + tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + + round_up(len, QLINK_ALIGN)); tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET); tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr)); tlv->type = frame_type; @@ -190,20 +191,24 @@ static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif, { unsigned int len = sizeof(struct qlink_cmd_start_ap); - len += s->ssid_len; - len += s->beacon.head_len; - len += s->beacon.tail_len; - len += s->beacon.beacon_ies_len; - len += s->beacon.proberesp_ies_len; - len += s->beacon.assocresp_ies_len; - len += s->beacon.probe_resp_len; + len += round_up(s->ssid_len, QLINK_ALIGN); + len += round_up(s->beacon.head_len, QLINK_ALIGN); + len += round_up(s->beacon.tail_len, QLINK_ALIGN); + len += round_up(s->beacon.beacon_ies_len, QLINK_ALIGN); + len += round_up(s->beacon.proberesp_ies_len, QLINK_ALIGN); + len += round_up(s->beacon.assocresp_ies_len, QLINK_ALIGN); + len += round_up(s->beacon.probe_resp_len, QLINK_ALIGN); if (cfg80211_chandef_valid(&s->chandef)) len += sizeof(struct qlink_tlv_chandef); - if (s->acl) + if (s->acl) { + unsigned int acl_len = struct_size(s->acl, mac_addrs, + s->acl->n_acl_entries); + len += sizeof(struct qlink_tlv_hdr) + - struct_size(s->acl, mac_addrs, s->acl->n_acl_entries); + round_up(acl_len, QLINK_ALIGN); + } if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) { pr_err("VIF%u.%u: can not fit AP settings: %u\n", @@ -315,7 +320,8 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, if (s->ht_cap) { struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) - skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->ht_cap)); + skb_put(cmd_skb, sizeof(*tlv) + + round_up(sizeof(*s->ht_cap), QLINK_ALIGN)); tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); tlv->len = cpu_to_le16(sizeof(*s->ht_cap)); @@ -339,7 +345,8 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, size_t acl_size = struct_size(s->acl, mac_addrs, s->acl->n_acl_entries); struct qlink_tlv_hdr *tlv = - skb_put(cmd_skb, sizeof(*tlv) + acl_size); + skb_put(cmd_skb, + sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN)); tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); tlv->len = cpu_to_le16(acl_size); @@ -581,10 +588,10 @@ qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst, } static void -qtnf_cmd_sta_info_parse(struct station_info *sinfo, - const struct qlink_tlv_hdr *tlv, +qtnf_cmd_sta_info_parse(struct station_info *sinfo, const u8 *data, size_t resp_size) { + const struct qlink_tlv_hdr *tlv; const struct qlink_sta_stats *stats = NULL; const u8 *map = NULL; unsigned int map_len = 0; @@ -595,11 +602,11 @@ qtnf_cmd_sta_info_parse(struct station_info *sinfo, (qtnf_utils_is_bit_set(map, bitn, map_len) && \ (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len)) - while (resp_size >= sizeof(*tlv)) { + qlink_for_each_tlv(tlv, data, resp_size) { tlv_len = le16_to_cpu(tlv->len); switch (le16_to_cpu(tlv->type)) { - case QTN_TLV_ID_STA_STATS_MAP: + case QTN_TLV_ID_BITMAP: map_len = tlv_len; map = tlv->val; break; @@ -610,9 +617,11 @@ qtnf_cmd_sta_info_parse(struct station_info *sinfo, default: break; } + } - resp_size -= tlv_len + sizeof(*tlv); - tlv = (const struct qlink_tlv_hdr *)(tlv->val + tlv_len); + if (!qlink_tlv_parsing_ok(tlv, data, resp_size)) { + pr_err("Malformed TLV buffer\n"); + return; } if (!map || !stats) @@ -736,9 +745,7 @@ int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac, goto out; } - qtnf_cmd_sta_info_parse(sinfo, - (const struct qlink_tlv_hdr *)resp->info, - var_resp_len); + qtnf_cmd_sta_info_parse(sinfo, resp->info, var_resp_len); out: qtnf_bus_unlock(vif->mac->bus); @@ -895,31 +902,21 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, const char *uboot_ver = NULL; u32 hw_ver = 0; u16 tlv_type; - u16 tlv_value_len; + u16 tlv_len; hwinfo->num_mac = resp->num_mac; hwinfo->mac_bitmap = resp->mac_bitmap; hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); - hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver); 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); bld_tmstamp = le32_to_cpu(resp->bld_tmstamp); plat_id = le32_to_cpu(resp->plat_id); hw_ver = le32_to_cpu(resp->hw_ver); - tlv = (const struct qlink_tlv_hdr *)resp->info; - - while (info_len >= sizeof(*tlv)) { + qlink_for_each_tlv(tlv, resp->info, info_len) { tlv_type = le16_to_cpu(tlv->type); - tlv_value_len = le16_to_cpu(tlv->len); - - if (tlv_value_len + sizeof(*tlv) > info_len) { - pr_warn("malformed TLV 0x%.2X; LEN: %u\n", - tlv_type, tlv_value_len); - return -EINVAL; - } + tlv_len = le16_to_cpu(tlv->len); switch (tlv_type) { case QTN_TLV_ID_BUILD_NAME: @@ -943,36 +940,43 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, case QTN_TLV_ID_UBOOT_VER: uboot_ver = (const void *)tlv->val; break; - case QTN_TLV_ID_MAX_SCAN_SSIDS: - hwinfo->max_scan_ssids = *tlv->val; + case QTN_TLV_ID_BITMAP: + memcpy(hwinfo->hw_capab, tlv->val, + min(sizeof(hwinfo->hw_capab), (size_t)tlv_len)); break; default: break; } + } + + if (!qlink_tlv_parsing_ok(tlv, resp->info, info_len)) { + pr_err("Malformed TLV buffer\n"); + return -EINVAL; + } - info_len -= tlv_value_len + sizeof(*tlv); - tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); - } - - pr_info("fw_version=%d, MACs map %#x, chains Tx=%u Rx=%u, capab=0x%x\n", - hwinfo->fw_ver, hwinfo->mac_bitmap, - hwinfo->total_tx_chain, hwinfo->total_rx_chain, - hwinfo->hw_capab); - - pr_info("\nBuild name: %s" \ - "\nBuild revision: %s" \ - "\nBuild type: %s" \ - "\nBuild label: %s" \ - "\nBuild timestamp: %lu" \ - "\nPlatform ID: %lu" \ - "\nHardware ID: %s" \ - "\nCalibration version: %s" \ - "\nU-Boot version: %s" \ - "\nHardware version: 0x%08x\n", + pr_info("\nBuild name: %s\n" + "Build revision: %s\n" + "Build type: %s\n" + "Build label: %s\n" + "Build timestamp: %lu\n" + "Platform ID: %lu\n" + "Hardware ID: %s\n" + "Calibration version: %s\n" + "U-Boot version: %s\n" + "Hardware version: 0x%08x\n" + "Qlink ver: %u.%u\n" + "MACs map: %#x\n" + "Chains Rx-Tx: %ux%u\n" + "FW version: 0x%x\n", bld_name, bld_rev, bld_type, bld_label, (unsigned long)bld_tmstamp, (unsigned long)plat_id, - hw_id, calibration_ver, uboot_ver, hw_ver); + hw_id, calibration_ver, uboot_ver, hw_ver, + QLINK_VER_MAJOR(bus->hw_info.ql_proto_ver), + QLINK_VER_MINOR(bus->hw_info.ql_proto_ver), + hwinfo->mac_bitmap, + hwinfo->total_rx_chain, hwinfo->total_tx_chain, + hwinfo->fw_ver); strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version)); hwinfo->hw_version = hw_ver; @@ -1016,18 +1020,15 @@ 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; + struct ieee80211_iface_combination *comb = mac->macinfo.if_comb; size_t n_comb = 0; struct ieee80211_iface_limit *limits; - const struct qlink_iface_comb_num *comb_num; const struct qlink_iface_limit_record *rec; const struct qlink_iface_limit *lim; const struct qlink_wowlan_capab_data *wowlan; u16 rec_len; u16 tlv_type; u16 tlv_value_len; - size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; u8 *ext_capa = NULL; u8 *ext_capa_mask = NULL; @@ -1066,44 +1067,11 @@ qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, break; } - tlv = (const struct qlink_tlv_hdr *)tlv_buf; - while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) { + qlink_for_each_tlv(tlv, resp->var_info, tlv_buf_size) { tlv_type = le16_to_cpu(tlv->type); tlv_value_len = le16_to_cpu(tlv->len); - tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); - if (tlv_full_len > tlv_buf_size) { - pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", - mac->macid, tlv_type, tlv_value_len); - return -EINVAL; - } switch (tlv_type) { - case QTN_TLV_ID_NUM_IFACE_COMB: - if (tlv_value_len != sizeof(*comb_num)) - return -EINVAL; - - comb_num = (void *)tlv->val; - - /* free earlier iface comb memory */ - qtnf_mac_iface_comb_free(mac); - - mac->macinfo.n_if_comb = - le32_to_cpu(comb_num->iface_comb_num); - - mac->macinfo.if_comb = - kcalloc(mac->macinfo.n_if_comb, - sizeof(*mac->macinfo.if_comb), - GFP_KERNEL); - - if (!mac->macinfo.if_comb) - return -ENOMEM; - - comb = mac->macinfo.if_comb; - - pr_debug("MAC%u: %zu iface combinations\n", - mac->macid, mac->macinfo.n_if_comb); - - break; case QTN_TLV_ID_IFACE_LIMIT: if (unlikely(!comb)) { pr_warn("MAC%u: no combinations advertised\n", @@ -1207,14 +1175,10 @@ qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, mac->macid, tlv_type); break; } - - tlv_buf_size -= tlv_full_len; - tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } - if (tlv_buf_size) { - pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", - mac->macid, tlv_buf_size); + if (!qlink_tlv_parsing_ok(tlv, resp->var_info, tlv_buf_size)) { + pr_err("Malformed TLV buffer\n"); return -EINVAL; } @@ -1260,13 +1224,15 @@ qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, return 0; } -static void +static int qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, const struct qlink_resp_get_mac_info *resp_info) { struct qtnf_mac_info *mac_info; struct qtnf_vif *vif; + qtnf_mac_iface_comb_free(mac); + mac_info = &mac->macinfo; mac_info->bands_cap = resp_info->bands_cap; @@ -1285,12 +1251,28 @@ qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, mac_info->radar_detect_widths = qlink_chan_width_mask_to_nl(le16_to_cpu( resp_info->radar_detect_widths)); - mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs); + mac_info->max_acl_mac_addrs = le16_to_cpu(resp_info->max_acl_mac_addrs); + mac_info->frag_thr = le32_to_cpu(resp_info->frag_threshold); + mac_info->rts_thr = le32_to_cpu(resp_info->rts_threshold); + mac_info->sretry_limit = resp_info->retry_short; + mac_info->lretry_limit = resp_info->retry_long; + mac_info->coverage_class = resp_info->coverage_class; + mac_info->max_scan_ssids = resp_info->max_scan_ssids; memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask, sizeof(mac_info->ht_cap_mod_mask)); memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask, sizeof(mac_info->vht_cap_mod_mask)); + + mac_info->n_if_comb = resp_info->n_iface_combinations; + mac_info->if_comb = kcalloc(mac->macinfo.n_if_comb, + sizeof(*mac->macinfo.if_comb), + GFP_KERNEL); + + if (!mac->macinfo.if_comb) + return -ENOMEM; + + return 0; } static void qtnf_cmd_resp_band_fill_htcap(const u8 *info, @@ -1389,7 +1371,6 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, size_t payload_len) { u16 tlv_type; - size_t tlv_len; size_t tlv_dlen; const struct qlink_tlv_hdr *tlv; const struct qlink_channel *qchan; @@ -1424,24 +1405,15 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, return -ENOMEM; } - tlv = (struct qlink_tlv_hdr *)resp->info; - - while (payload_len >= sizeof(*tlv)) { + qlink_for_each_tlv(tlv, resp->info, payload_len) { tlv_type = le16_to_cpu(tlv->type); tlv_dlen = le16_to_cpu(tlv->len); - tlv_len = tlv_dlen + sizeof(*tlv); - - if (tlv_len > payload_len) { - pr_warn("malformed TLV 0x%.2X; LEN: %zu\n", - tlv_type, tlv_len); - goto error_ret; - } switch (tlv_type) { case QTN_TLV_ID_CHANNEL: if (unlikely(tlv_dlen != sizeof(*qchan))) { pr_err("invalid channel TLV len %zu\n", - tlv_len); + tlv_dlen); goto error_ret; } @@ -1544,13 +1516,10 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, pr_warn("unknown TLV type: %#x\n", tlv_type); break; } - - payload_len -= tlv_len; - tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_dlen); } - if (payload_len) { - pr_err("malformed TLV buf; bytes left: %zu\n", payload_len); + if (!qlink_tlv_parsing_ok(tlv, resp->info, payload_len)) { + pr_err("Malformed TLV buffer\n"); goto error_ret; } @@ -1570,128 +1539,6 @@ error_ret: return ret; } -static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, - const u8 *payload, size_t payload_len) -{ - struct qtnf_mac_info *mac_info; - struct qlink_tlv_frag_rts_thr *phy_thr; - struct qlink_tlv_rlimit *limit; - struct qlink_tlv_cclass *class; - u16 tlv_type; - u16 tlv_value_len; - size_t tlv_full_len; - const struct qlink_tlv_hdr *tlv; - - mac_info = &mac->macinfo; - - tlv = (struct qlink_tlv_hdr *)payload; - while (payload_len >= sizeof(struct qlink_tlv_hdr)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_value_len = le16_to_cpu(tlv->len); - tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); - - if (tlv_full_len > payload_len) { - pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", - mac->macid, tlv_type, tlv_value_len); - return -EINVAL; - } - - switch (tlv_type) { - case QTN_TLV_ID_FRAG_THRESH: - phy_thr = (void *)tlv; - mac_info->frag_thr = le32_to_cpu(phy_thr->thr); - break; - case QTN_TLV_ID_RTS_THRESH: - phy_thr = (void *)tlv; - mac_info->rts_thr = le32_to_cpu(phy_thr->thr); - break; - case QTN_TLV_ID_SRETRY_LIMIT: - limit = (void *)tlv; - mac_info->sretry_limit = limit->rlimit; - break; - case QTN_TLV_ID_LRETRY_LIMIT: - limit = (void *)tlv; - mac_info->lretry_limit = limit->rlimit; - break; - case QTN_TLV_ID_COVERAGE_CLASS: - class = (void *)tlv; - mac_info->coverage_class = class->cclass; - break; - default: - pr_err("MAC%u: Unknown TLV type: %#x\n", mac->macid, - le16_to_cpu(tlv->type)); - break; - } - - payload_len -= tlv_full_len; - tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); - } - - if (payload_len) { - pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", - mac->macid, payload_len); - return -EINVAL; - } - - return 0; -} - -static int -qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, - const u8 *payload, size_t payload_len) -{ - struct qlink_chan_stats *qlink_stats; - const struct qlink_tlv_hdr *tlv; - size_t tlv_full_len; - u16 tlv_value_len; - u16 tlv_type; - - tlv = (struct qlink_tlv_hdr *)payload; - while (payload_len >= sizeof(struct qlink_tlv_hdr)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_value_len = le16_to_cpu(tlv->len); - tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); - if (tlv_full_len > payload_len) { - pr_warn("malformed TLV 0x%.2X; LEN: %u\n", - tlv_type, tlv_value_len); - return -EINVAL; - } - switch (tlv_type) { - case QTN_TLV_ID_CHANNEL_STATS: - if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { - pr_err("invalid CHANNEL_STATS entry size\n"); - return -EINVAL; - } - - qlink_stats = (void *)tlv->val; - - stats->chan_num = le32_to_cpu(qlink_stats->chan_num); - stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); - stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); - stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); - stats->cca_try = le32_to_cpu(qlink_stats->cca_try); - stats->chan_noise = qlink_stats->chan_noise; - - pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", - stats->chan_num, stats->cca_try, - stats->cca_busy, stats->chan_noise); - break; - default: - pr_warn("Unknown TLV type: %#x\n", - le16_to_cpu(tlv->type)); - } - payload_len -= tlv_full_len; - tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); - } - - if (payload_len) { - pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); - return -EINVAL; - } - - return 0; -} - int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) { struct sk_buff *cmd_skb, *resp_skb = NULL; @@ -1712,7 +1559,10 @@ int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) goto out; resp = (const struct qlink_resp_get_mac_info *)resp_skb->data; - qtnf_cmd_resp_proc_mac_info(mac, resp); + ret = qtnf_cmd_resp_proc_mac_info(mac, resp); + if (ret) + goto out; + ret = qtnf_parse_variable_mac_info(mac, resp, var_data_len); out: @@ -1793,35 +1643,6 @@ out: return ret; } -int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac) -{ - struct sk_buff *cmd_skb, *resp_skb = NULL; - struct qlink_resp_phy_params *resp; - size_t response_size = 0; - int ret = 0; - - cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, - QLINK_CMD_PHY_PARAMS_GET, - sizeof(struct qlink_cmd)); - if (!cmd_skb) - return -ENOMEM; - - qtnf_bus_lock(mac->bus); - ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, - sizeof(*resp), &response_size); - if (ret) - goto out; - - resp = (struct qlink_resp_phy_params *)resp_skb->data; - ret = qtnf_cmd_resp_proc_phy_params(mac, resp->info, response_size); - -out: - qtnf_bus_unlock(mac->bus); - consume_skb(resp_skb); - - return ret; -} - int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) { struct wiphy *wiphy = priv_to_wiphy(mac); @@ -1843,16 +1664,16 @@ int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH, wiphy->rts_threshold); if (changed & WIPHY_PARAM_COVERAGE_CLASS) - qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, - wiphy->coverage_class); + qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, + wiphy->coverage_class); if (changed & WIPHY_PARAM_RETRY_LONG) - qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT, - wiphy->retry_long); + qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT, + wiphy->retry_long); if (changed & WIPHY_PARAM_RETRY_SHORT) - qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT, - wiphy->retry_short); + qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT, + wiphy->retry_short); ret = qtnf_cmd_send(mac->bus, cmd_skb); if (ret) @@ -1866,23 +1687,35 @@ out: int qtnf_cmd_send_init_fw(struct qtnf_bus *bus) { + struct sk_buff *resp_skb = NULL; + struct qlink_resp_init_fw *resp; + struct qlink_cmd_init_fw *cmd; struct sk_buff *cmd_skb; - int ret = 0; + size_t info_len = 0; + int ret; cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, QLINK_CMD_FW_INIT, - sizeof(struct qlink_cmd)); + sizeof(*cmd)); if (!cmd_skb) return -ENOMEM; + cmd = (struct qlink_cmd_init_fw *)cmd_skb->data; + cmd->qlink_proto_ver = cpu_to_le32(QLINK_PROTO_VER); + qtnf_bus_lock(bus); - ret = qtnf_cmd_send(bus, cmd_skb); + ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, + sizeof(*resp), &info_len); + qtnf_bus_unlock(bus); + if (ret) goto out; -out: - qtnf_bus_unlock(bus); + resp = (struct qlink_resp_init_fw *)resp_skb->data; + bus->hw_info.ql_proto_ver = le32_to_cpu(resp->qlink_proto_ver); +out: + consume_skb(resp_skb); return ret; } @@ -2178,108 +2011,90 @@ static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb, memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN); } -static void qtnf_cmd_scan_set_dwell(struct qtnf_wmac *mac, - struct sk_buff *cmd_skb) +int qtnf_cmd_send_scan(struct qtnf_wmac *mac) { struct cfg80211_scan_request *scan_req = mac->scan_req; - u16 dwell_active = QTNF_SCAN_DWELL_ACTIVE_DEFAULT; u16 dwell_passive = QTNF_SCAN_DWELL_PASSIVE_DEFAULT; - u16 duration = QTNF_SCAN_SAMPLE_DURATION_DEFAULT; - - if (scan_req->duration) { - dwell_active = scan_req->duration; - dwell_passive = scan_req->duration; - } - - pr_debug("MAC%u: %s scan dwell active=%u, passive=%u, duration=%u\n", - mac->macid, - scan_req->duration_mandatory ? "mandatory" : "max", - dwell_active, dwell_passive, duration); - - qtnf_cmd_skb_put_tlv_u16(cmd_skb, - QTN_TLV_ID_SCAN_DWELL_ACTIVE, - dwell_active); - qtnf_cmd_skb_put_tlv_u16(cmd_skb, - QTN_TLV_ID_SCAN_DWELL_PASSIVE, - dwell_passive); - qtnf_cmd_skb_put_tlv_u16(cmd_skb, - QTN_TLV_ID_SCAN_SAMPLE_DURATION, - duration); -} - -int qtnf_cmd_send_scan(struct qtnf_wmac *mac) -{ - struct sk_buff *cmd_skb; + u16 dwell_active = QTNF_SCAN_DWELL_ACTIVE_DEFAULT; + struct wireless_dev *wdev = scan_req->wdev; struct ieee80211_channel *sc; - struct cfg80211_scan_request *scan_req = mac->scan_req; - int n_channels; - int count = 0; + struct qlink_cmd_scan *cmd; + struct sk_buff *cmd_skb; + int n_channels = 0; + u64 flags = 0; + int count; int ret; cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, QLINK_CMD_SCAN, - sizeof(struct qlink_cmd)); + sizeof(*cmd)); if (!cmd_skb) return -ENOMEM; - qtnf_bus_lock(mac->bus); + cmd = (struct qlink_cmd_scan *)cmd_skb->data; - if (scan_req->n_ssids != 0) { - while (count < scan_req->n_ssids) { - qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, - scan_req->ssids[count].ssid, - scan_req->ssids[count].ssid_len); - count++; - } + if (scan_req->duration) { + dwell_active = scan_req->duration; + dwell_passive = scan_req->duration; + } else if (wdev->iftype == NL80211_IFTYPE_STATION && + wdev->current_bss) { + /* let device select dwell based on traffic conditions */ + dwell_active = QTNF_SCAN_TIME_AUTO; + dwell_passive = QTNF_SCAN_TIME_AUTO; + } + + cmd->n_ssids = cpu_to_le16(scan_req->n_ssids); + for (count = 0; count < scan_req->n_ssids; ++count) { + qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, + scan_req->ssids[count].ssid, + scan_req->ssids[count].ssid_len); } if (scan_req->ie_len != 0) qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ, scan_req->ie, scan_req->ie_len); - if (scan_req->n_channels) { - n_channels = scan_req->n_channels; - count = 0; - - while (n_channels != 0) { - sc = scan_req->channels[count]; - if (sc->flags & IEEE80211_CHAN_DISABLED) { - n_channels--; - continue; - } + for (count = 0; count < scan_req->n_channels; ++count) { + sc = scan_req->channels[count]; + if (sc->flags & IEEE80211_CHAN_DISABLED) + continue; - pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n", - mac->macid, sc->hw_value, sc->center_freq, - sc->flags); + pr_debug("[MAC%u] scan chan=%d, freq=%d, flags=%#x\n", + mac->macid, sc->hw_value, sc->center_freq, + sc->flags); - qtnf_cmd_channel_tlv_add(cmd_skb, sc); - n_channels--; - count++; - } + qtnf_cmd_channel_tlv_add(cmd_skb, sc); + ++n_channels; } - qtnf_cmd_scan_set_dwell(mac, cmd_skb); + if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH) + flags |= QLINK_SCAN_FLAG_FLUSH; + + if (scan_req->duration_mandatory) + flags |= QLINK_SCAN_FLAG_DURATION_MANDATORY; + + cmd->n_channels = cpu_to_le16(n_channels); + cmd->active_dwell = cpu_to_le16(dwell_active); + cmd->passive_dwell = cpu_to_le16(dwell_passive); + cmd->sample_duration = cpu_to_le16(QTNF_SCAN_SAMPLE_DURATION_DEFAULT); + cmd->flags = cpu_to_le64(flags); + + pr_debug("[MAC%u] %s scan dwell active=%u passive=%u duration=%u\n", + mac->macid, + scan_req->duration_mandatory ? "mandatory" : "max", + dwell_active, dwell_passive, + QTNF_SCAN_SAMPLE_DURATION_DEFAULT); if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - pr_debug("MAC%u: scan with random addr=%pM, mask=%pM\n", + pr_debug("[MAC%u] scan with random addr=%pM, mask=%pM\n", mac->macid, scan_req->mac_addr, scan_req->mac_addr_mask); - qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr, scan_req->mac_addr_mask); } - if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH) { - pr_debug("MAC%u: flush cache before scan\n", mac->macid); - - qtnf_cmd_skb_put_tlv_tag(cmd_skb, QTN_TLV_ID_SCAN_FLUSH); - } - + qtnf_bus_lock(mac->bus); ret = qtnf_cmd_send(mac->bus, cmd_skb); - if (ret) - goto out; - -out: qtnf_bus_unlock(mac->bus); return ret; @@ -2396,7 +2211,7 @@ int qtnf_cmd_send_external_auth(struct qtnf_vif *vif, cmd = (struct qlink_cmd_external_auth *)cmd_skb->data; - ether_addr_copy(cmd->bssid, auth->bssid); + ether_addr_copy(cmd->peer, auth->bssid); cmd->status = cpu_to_le16(auth->status); qtnf_bus_lock(vif->mac->bus); @@ -2552,8 +2367,91 @@ int qtnf_cmd_reg_notify(struct qtnf_wmac *mac, struct regulatory_request *req, return ret; } -int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, - struct qtnf_chan_stats *stats) +static int +qtnf_cmd_resp_proc_chan_stat_info(struct survey_info *survey, + const u8 *payload, size_t payload_len) +{ + const struct qlink_chan_stats *stats = NULL; + const struct qlink_tlv_hdr *tlv; + u16 tlv_value_len; + u16 tlv_type; + const u8 *map = NULL; + unsigned int map_len = 0; + unsigned int stats_len = 0; + + qlink_for_each_tlv(tlv, payload, payload_len) { + tlv_type = le16_to_cpu(tlv->type); + tlv_value_len = le16_to_cpu(tlv->len); + + switch (tlv_type) { + case QTN_TLV_ID_BITMAP: + map = tlv->val; + map_len = tlv_value_len; + break; + case QTN_TLV_ID_CHANNEL_STATS: + stats = (struct qlink_chan_stats *)tlv->val; + stats_len = tlv_value_len; + break; + default: + pr_info("Unknown TLV type: %#x\n", tlv_type); + break; + } + } + + if (!qlink_tlv_parsing_ok(tlv, payload, payload_len)) { + pr_err("Malformed TLV buffer\n"); + return -EINVAL; + } + + if (!map || !stats) + return 0; + +#define qtnf_chan_stat_avail(stat_name, bitn) \ + (qtnf_utils_is_bit_set(map, bitn, map_len) && \ + (offsetofend(struct qlink_chan_stats, stat_name) <= stats_len)) + + if (qtnf_chan_stat_avail(time_on, QLINK_CHAN_STAT_TIME_ON)) { + survey->filled |= SURVEY_INFO_TIME; + survey->time = le64_to_cpu(stats->time_on); + } + + if (qtnf_chan_stat_avail(time_tx, QLINK_CHAN_STAT_TIME_TX)) { + survey->filled |= SURVEY_INFO_TIME_TX; + survey->time_tx = le64_to_cpu(stats->time_tx); + } + + if (qtnf_chan_stat_avail(time_rx, QLINK_CHAN_STAT_TIME_RX)) { + survey->filled |= SURVEY_INFO_TIME_RX; + survey->time_rx = le64_to_cpu(stats->time_rx); + } + + if (qtnf_chan_stat_avail(cca_busy, QLINK_CHAN_STAT_CCA_BUSY)) { + survey->filled |= SURVEY_INFO_TIME_BUSY; + survey->time_busy = le64_to_cpu(stats->cca_busy); + } + + if (qtnf_chan_stat_avail(cca_busy_ext, QLINK_CHAN_STAT_CCA_BUSY_EXT)) { + survey->filled |= SURVEY_INFO_TIME_EXT_BUSY; + survey->time_ext_busy = le64_to_cpu(stats->cca_busy_ext); + } + + if (qtnf_chan_stat_avail(time_scan, QLINK_CHAN_STAT_TIME_SCAN)) { + survey->filled |= SURVEY_INFO_TIME_SCAN; + survey->time_scan = le64_to_cpu(stats->time_scan); + } + + if (qtnf_chan_stat_avail(chan_noise, QLINK_CHAN_STAT_CHAN_NOISE)) { + survey->filled |= SURVEY_INFO_NOISE_DBM; + survey->noise = stats->chan_noise; + } + +#undef qtnf_chan_stat_avail + + return 0; +} + +int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u32 chan_freq, + struct survey_info *survey) { struct sk_buff *cmd_skb, *resp_skb = NULL; struct qlink_cmd_get_chan_stats *cmd; @@ -2567,22 +2465,30 @@ int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, if (!cmd_skb) return -ENOMEM; - qtnf_bus_lock(mac->bus); - cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; - cmd->channel = cpu_to_le16(channel); + cmd->channel_freq = cpu_to_le32(chan_freq); + qtnf_bus_lock(mac->bus); ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, sizeof(*resp), &var_data_len); + qtnf_bus_unlock(mac->bus); + if (ret) goto out; resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; - ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, + + if (le32_to_cpu(resp->chan_freq) != chan_freq) { + pr_err("[MAC%u] channel stats freq %u != requested %u\n", + mac->macid, le32_to_cpu(resp->chan_freq), chan_freq); + ret = -EINVAL; + goto out; + } + + ret = qtnf_cmd_resp_proc_chan_stat_info(survey, resp->info, var_data_len); out: - qtnf_bus_unlock(mac->bus); consume_skb(resp_skb); return ret; @@ -2595,6 +2501,7 @@ int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, struct qlink_cmd_chan_switch *cmd; struct sk_buff *cmd_skb; int ret; + u64 flags = 0; cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, QLINK_CMD_CHAN_SWITCH, @@ -2602,19 +2509,19 @@ int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, if (!cmd_skb) return -ENOMEM; - qtnf_bus_lock(mac->bus); + if (params->radar_required) + flags |= QLINK_CHAN_SW_RADAR_REQUIRED; + + if (params->block_tx) + flags |= QLINK_CHAN_SW_BLOCK_TX; cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; - cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); - cmd->radar_required = params->radar_required; - cmd->block_tx = params->block_tx; + qlink_chandef_cfg2q(¶ms->chandef, &cmd->channel); + cmd->flags = cpu_to_le64(flags); cmd->beacon_count = params->count; + qtnf_bus_lock(mac->bus); ret = qtnf_cmd_send(mac->bus, cmd_skb); - if (ret) - goto out; - -out: qtnf_bus_unlock(mac->bus); return ret; @@ -2695,7 +2602,7 @@ int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif, if (!cmd_skb) return -ENOMEM; - tlv = skb_put(cmd_skb, sizeof(*tlv) + acl_size); + tlv = skb_put(cmd_skb, sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN)); tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); tlv->len = cpu_to_le16(acl_size); qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val); @@ -2884,3 +2791,39 @@ int qtnf_cmd_netdev_changeupper(const struct qtnf_vif *vif, int br_domain) return ret; } + +int qtnf_cmd_send_update_owe(struct qtnf_vif *vif, + struct cfg80211_update_owe_info *owe) +{ + struct qlink_cmd_update_owe *cmd; + struct sk_buff *cmd_skb; + int ret; + + if (sizeof(*cmd) + owe->ie_len > QTNF_MAX_CMD_BUF_SIZE) { + pr_warn("VIF%u.%u: OWE update IEs too big: %zu\n", + vif->mac->macid, vif->vifid, owe->ie_len); + return -E2BIG; + } + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, + QLINK_CMD_UPDATE_OWE, + sizeof(*cmd)); + if (!cmd_skb) + return -ENOMEM; + + cmd = (struct qlink_cmd_update_owe *)cmd_skb->data; + ether_addr_copy(cmd->peer, owe->peer); + cmd->status = cpu_to_le16(owe->status); + if (owe->ie_len && owe->ie) + qtnf_cmd_skb_put_buffer(cmd_skb, owe->ie, owe->ie_len); + + qtnf_bus_lock(vif->mac->bus); + ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); + if (ret) + goto out; + +out: + qtnf_bus_unlock(vif->mac->bus); + + return ret; +} |