1ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+ 2ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ 398f44cb0SIgor Mitsyanko 498f44cb0SIgor Mitsyanko #include <linux/types.h> 598f44cb0SIgor Mitsyanko #include <linux/skbuff.h> 698f44cb0SIgor Mitsyanko 798f44cb0SIgor Mitsyanko #include "cfg80211.h" 898f44cb0SIgor Mitsyanko #include "core.h" 998f44cb0SIgor Mitsyanko #include "qlink.h" 1098f44cb0SIgor Mitsyanko #include "qlink_util.h" 1198f44cb0SIgor Mitsyanko #include "bus.h" 1298f44cb0SIgor Mitsyanko #include "commands.h" 1398f44cb0SIgor Mitsyanko 14501c3be1SIgor Mitsyanko /* Let device itself to select best values for current conditions */ 15b63967caSIgor Mitsyanko #define QTNF_SCAN_TIME_AUTO 0 16b63967caSIgor Mitsyanko 17501c3be1SIgor Mitsyanko #define QTNF_SCAN_DWELL_ACTIVE_DEFAULT 90 18501c3be1SIgor Mitsyanko #define QTNF_SCAN_DWELL_PASSIVE_DEFAULT 100 19b63967caSIgor Mitsyanko #define QTNF_SCAN_SAMPLE_DURATION_DEFAULT QTNF_SCAN_TIME_AUTO 20b63967caSIgor Mitsyanko 2198f44cb0SIgor Mitsyanko static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp, 2298f44cb0SIgor Mitsyanko u16 cmd_id, u8 mac_id, u8 vif_id, 2398f44cb0SIgor Mitsyanko size_t resp_size) 2498f44cb0SIgor Mitsyanko { 2598f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->cmd_id) != cmd_id)) { 2698f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad cmd_id in response: 0x%.4X\n", 2798f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, le16_to_cpu(resp->cmd_id)); 2898f44cb0SIgor Mitsyanko return -EINVAL; 2998f44cb0SIgor Mitsyanko } 3098f44cb0SIgor Mitsyanko 3198f44cb0SIgor Mitsyanko if (unlikely(resp->macid != mac_id)) { 3298f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad MAC in response: %u\n", 3398f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->macid); 3498f44cb0SIgor Mitsyanko return -EINVAL; 3598f44cb0SIgor Mitsyanko } 3698f44cb0SIgor Mitsyanko 3798f44cb0SIgor Mitsyanko if (unlikely(resp->vifid != vif_id)) { 3898f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad VIF in response: %u\n", 3998f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->vifid); 4098f44cb0SIgor Mitsyanko return -EINVAL; 4198f44cb0SIgor Mitsyanko } 4298f44cb0SIgor Mitsyanko 4398f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->mhdr.len) < resp_size)) { 4498f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad response size %u < %zu\n", 4598f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, 4698f44cb0SIgor Mitsyanko le16_to_cpu(resp->mhdr.len), resp_size); 4798f44cb0SIgor Mitsyanko return -ENOSPC; 4898f44cb0SIgor Mitsyanko } 4998f44cb0SIgor Mitsyanko 5098f44cb0SIgor Mitsyanko return 0; 5198f44cb0SIgor Mitsyanko } 5298f44cb0SIgor Mitsyanko 5336e8c538SIgor Mitsyanko static int qtnf_cmd_resp_result_decode(enum qlink_cmd_result qcode) 5436e8c538SIgor Mitsyanko { 5536e8c538SIgor Mitsyanko switch (qcode) { 5636e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_OK: 5736e8c538SIgor Mitsyanko return 0; 5836e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_INVALID: 5936e8c538SIgor Mitsyanko return -EINVAL; 6036e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_ENOTSUPP: 6136e8c538SIgor Mitsyanko return -ENOTSUPP; 6236e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_ENOTFOUND: 6336e8c538SIgor Mitsyanko return -ENOENT; 6436e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EALREADY: 6536e8c538SIgor Mitsyanko return -EALREADY; 6636e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EADDRINUSE: 6736e8c538SIgor Mitsyanko return -EADDRINUSE; 6836e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EADDRNOTAVAIL: 6936e8c538SIgor Mitsyanko return -EADDRNOTAVAIL; 70126824f5SAndrey Shevchenko case QLINK_CMD_RESULT_EBUSY: 71126824f5SAndrey Shevchenko return -EBUSY; 7236e8c538SIgor Mitsyanko default: 7336e8c538SIgor Mitsyanko return -EFAULT; 7436e8c538SIgor Mitsyanko } 7536e8c538SIgor Mitsyanko } 7636e8c538SIgor Mitsyanko 7798f44cb0SIgor Mitsyanko static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus, 7898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, 7998f44cb0SIgor Mitsyanko struct sk_buff **response_skb, 8098f44cb0SIgor Mitsyanko size_t const_resp_size, 8198f44cb0SIgor Mitsyanko size_t *var_resp_size) 8298f44cb0SIgor Mitsyanko { 8398f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 841066bd19SSergey Matyukevich struct qlink_resp *resp = NULL; 8598f44cb0SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 864a33f21cSSergey Matyukevich int resp_res = 0; 8798f44cb0SIgor Mitsyanko u16 cmd_id; 88c6ed298fSSergey Matyukevich u8 mac_id; 89c6ed298fSSergey Matyukevich u8 vif_id; 9098f44cb0SIgor Mitsyanko int ret; 9198f44cb0SIgor Mitsyanko 9298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 9398f44cb0SIgor Mitsyanko cmd_id = le16_to_cpu(cmd->cmd_id); 9498f44cb0SIgor Mitsyanko mac_id = cmd->macid; 9598f44cb0SIgor Mitsyanko vif_id = cmd->vifid; 9698f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 9798f44cb0SIgor Mitsyanko 983844dec0SSergey Matyukevich pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id, cmd_id); 99c6ed298fSSergey Matyukevich 10083b00f6eSSergey Matyukevich if (!qtnf_fw_is_up(bus) && cmd_id != QLINK_CMD_FW_INIT) { 10198f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n", 1023844dec0SSergey Matyukevich mac_id, vif_id, cmd_id, bus->fw_state); 103b60769e2SDmitry Lebed dev_kfree_skb(cmd_skb); 10498f44cb0SIgor Mitsyanko return -ENODEV; 10598f44cb0SIgor Mitsyanko } 10698f44cb0SIgor Mitsyanko 10798f44cb0SIgor Mitsyanko ret = qtnf_trans_send_cmd_with_resp(bus, cmd_skb, &resp_skb); 108c6ed298fSSergey Matyukevich if (ret) 10998f44cb0SIgor Mitsyanko goto out; 11098f44cb0SIgor Mitsyanko 1111066bd19SSergey Matyukevich if (WARN_ON(!resp_skb || !resp_skb->data)) { 1121066bd19SSergey Matyukevich ret = -EFAULT; 1131066bd19SSergey Matyukevich goto out; 1141066bd19SSergey Matyukevich } 1151066bd19SSergey Matyukevich 1161066bd19SSergey Matyukevich resp = (struct qlink_resp *)resp_skb->data; 1174a33f21cSSergey Matyukevich resp_res = le16_to_cpu(resp->result); 11898f44cb0SIgor Mitsyanko ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id, 11998f44cb0SIgor Mitsyanko const_resp_size); 120c6ed298fSSergey Matyukevich if (ret) 12198f44cb0SIgor Mitsyanko goto out; 12298f44cb0SIgor Mitsyanko 12398f44cb0SIgor Mitsyanko /* Return length of variable part of response */ 12498f44cb0SIgor Mitsyanko if (response_skb && var_resp_size) 12598f44cb0SIgor Mitsyanko *var_resp_size = le16_to_cpu(resp->mhdr.len) - const_resp_size; 12698f44cb0SIgor Mitsyanko 12798f44cb0SIgor Mitsyanko out: 12898f44cb0SIgor Mitsyanko if (response_skb) 12998f44cb0SIgor Mitsyanko *response_skb = resp_skb; 13098f44cb0SIgor Mitsyanko else 13198f44cb0SIgor Mitsyanko consume_skb(resp_skb); 13298f44cb0SIgor Mitsyanko 1334a33f21cSSergey Matyukevich if (!ret) 1344a33f21cSSergey Matyukevich return qtnf_cmd_resp_result_decode(resp_res); 135c6ed298fSSergey Matyukevich 136c6ed298fSSergey Matyukevich pr_warn("VIF%u.%u: cmd 0x%.4X failed: %d\n", 1373844dec0SSergey Matyukevich mac_id, vif_id, cmd_id, ret); 138c6ed298fSSergey Matyukevich 13998f44cb0SIgor Mitsyanko return ret; 14098f44cb0SIgor Mitsyanko } 14198f44cb0SIgor Mitsyanko 142c6ed298fSSergey Matyukevich static inline int qtnf_cmd_send(struct qtnf_bus *bus, struct sk_buff *cmd_skb) 14398f44cb0SIgor Mitsyanko { 144c6ed298fSSergey Matyukevich return qtnf_cmd_send_with_reply(bus, cmd_skb, NULL, 14598f44cb0SIgor Mitsyanko sizeof(struct qlink_resp), NULL); 14698f44cb0SIgor Mitsyanko } 14798f44cb0SIgor Mitsyanko 14898f44cb0SIgor Mitsyanko static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no, 14998f44cb0SIgor Mitsyanko size_t cmd_size) 15098f44cb0SIgor Mitsyanko { 15198f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 15298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 15398f44cb0SIgor Mitsyanko 15498f44cb0SIgor Mitsyanko cmd_skb = __dev_alloc_skb(sizeof(*cmd) + 15598f44cb0SIgor Mitsyanko QTNF_MAX_CMD_BUF_SIZE, GFP_KERNEL); 15698f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) { 15798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u CMD %u: alloc failed\n", macid, vifid, cmd_no); 15898f44cb0SIgor Mitsyanko return NULL; 15998f44cb0SIgor Mitsyanko } 16098f44cb0SIgor Mitsyanko 161b080db58SJohannes Berg skb_put_zero(cmd_skb, cmd_size); 16298f44cb0SIgor Mitsyanko 16398f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 16498f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 16598f44cb0SIgor Mitsyanko cmd->mhdr.type = cpu_to_le16(QLINK_MSG_TYPE_CMD); 16698f44cb0SIgor Mitsyanko cmd->cmd_id = cpu_to_le16(cmd_no); 16798f44cb0SIgor Mitsyanko cmd->macid = macid; 16898f44cb0SIgor Mitsyanko cmd->vifid = vifid; 16998f44cb0SIgor Mitsyanko 17098f44cb0SIgor Mitsyanko return cmd_skb; 17198f44cb0SIgor Mitsyanko } 17298f44cb0SIgor Mitsyanko 17318b7470fSIgor Mitsyanko static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type, 17418b7470fSIgor Mitsyanko const u8 *buf, size_t len) 17518b7470fSIgor Mitsyanko { 17618b7470fSIgor Mitsyanko struct qlink_tlv_ie_set *tlv; 17718b7470fSIgor Mitsyanko 1788b0b5f1bSIgor Mitsyanko tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + 1798b0b5f1bSIgor Mitsyanko round_up(len, QLINK_ALIGN)); 18018b7470fSIgor Mitsyanko tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET); 18118b7470fSIgor Mitsyanko tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr)); 18218b7470fSIgor Mitsyanko tlv->type = frame_type; 18318b7470fSIgor Mitsyanko tlv->flags = 0; 18418b7470fSIgor Mitsyanko 18518b7470fSIgor Mitsyanko if (len && buf) 18618b7470fSIgor Mitsyanko memcpy(tlv->ie_data, buf, len); 18718b7470fSIgor Mitsyanko } 18818b7470fSIgor Mitsyanko 18917011da0SIgor Mitsyanko static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif, 19017011da0SIgor Mitsyanko const struct cfg80211_ap_settings *s) 19198f44cb0SIgor Mitsyanko { 19217011da0SIgor Mitsyanko unsigned int len = sizeof(struct qlink_cmd_start_ap); 19398f44cb0SIgor Mitsyanko 1948b0b5f1bSIgor Mitsyanko len += round_up(s->ssid_len, QLINK_ALIGN); 1958b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.head_len, QLINK_ALIGN); 1968b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.tail_len, QLINK_ALIGN); 1978b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.beacon_ies_len, QLINK_ALIGN); 1988b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.proberesp_ies_len, QLINK_ALIGN); 1998b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.assocresp_ies_len, QLINK_ALIGN); 2008b0b5f1bSIgor Mitsyanko len += round_up(s->beacon.probe_resp_len, QLINK_ALIGN); 20198f44cb0SIgor Mitsyanko 20217011da0SIgor Mitsyanko if (cfg80211_chandef_valid(&s->chandef)) 20317011da0SIgor Mitsyanko len += sizeof(struct qlink_tlv_chandef); 20498f44cb0SIgor Mitsyanko 2058b0b5f1bSIgor Mitsyanko if (s->acl) { 2068b0b5f1bSIgor Mitsyanko unsigned int acl_len = struct_size(s->acl, mac_addrs, 2078b0b5f1bSIgor Mitsyanko s->acl->n_acl_entries); 2088b0b5f1bSIgor Mitsyanko 20933f98992SVasily Ulyanov len += sizeof(struct qlink_tlv_hdr) + 2108b0b5f1bSIgor Mitsyanko round_up(acl_len, QLINK_ALIGN); 2118b0b5f1bSIgor Mitsyanko } 212f1398fd2SVasily Ulyanov 21317011da0SIgor Mitsyanko if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) { 21417011da0SIgor Mitsyanko pr_err("VIF%u.%u: can not fit AP settings: %u\n", 21517011da0SIgor Mitsyanko vif->mac->macid, vif->vifid, len); 21617011da0SIgor Mitsyanko return false; 21798f44cb0SIgor Mitsyanko } 21898f44cb0SIgor Mitsyanko 21917011da0SIgor Mitsyanko return true; 22098f44cb0SIgor Mitsyanko } 22198f44cb0SIgor Mitsyanko 222df0af4c7SMikhail Karpenko static void qtnf_cmd_tlv_ie_ext_add(struct sk_buff *cmd_skb, u8 eid_ext, 223df0af4c7SMikhail Karpenko const void *buf, size_t len) 224df0af4c7SMikhail Karpenko { 225df0af4c7SMikhail Karpenko struct qlink_tlv_ext_ie *tlv; 226df0af4c7SMikhail Karpenko 227df0af4c7SMikhail Karpenko tlv = (struct qlink_tlv_ext_ie *)skb_put(cmd_skb, sizeof(*tlv) + len); 228df0af4c7SMikhail Karpenko tlv->hdr.type = cpu_to_le16(WLAN_EID_EXTENSION); 229df0af4c7SMikhail Karpenko tlv->hdr.len = cpu_to_le16(sizeof(*tlv) + len - sizeof(tlv->hdr)); 230df0af4c7SMikhail Karpenko tlv->eid_ext = eid_ext; 231df0af4c7SMikhail Karpenko 232df0af4c7SMikhail Karpenko if (len && buf) 233df0af4c7SMikhail Karpenko memcpy(tlv->ie_data, buf, len); 234df0af4c7SMikhail Karpenko } 235df0af4c7SMikhail Karpenko 23617011da0SIgor Mitsyanko int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, 2379b692df1SIgor Mitsyanko const struct cfg80211_ap_settings *s) 23898f44cb0SIgor Mitsyanko { 23998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 24017011da0SIgor Mitsyanko struct qlink_cmd_start_ap *cmd; 2418b5f4aa7SIgor Mitsyanko struct qlink_auth_encr *aen; 24298f44cb0SIgor Mitsyanko int ret; 24398f44cb0SIgor Mitsyanko int i; 24498f44cb0SIgor Mitsyanko 24517011da0SIgor Mitsyanko if (!qtnf_cmd_start_ap_can_fit(vif, s)) 24617011da0SIgor Mitsyanko return -E2BIG; 24717011da0SIgor Mitsyanko 24898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 24917011da0SIgor Mitsyanko QLINK_CMD_START_AP, 2508b5f4aa7SIgor Mitsyanko sizeof(*cmd)); 251c93fe71cSSergey Matyukevich if (!cmd_skb) 25298f44cb0SIgor Mitsyanko return -ENOMEM; 25398f44cb0SIgor Mitsyanko 25417011da0SIgor Mitsyanko cmd = (struct qlink_cmd_start_ap *)cmd_skb->data; 2558b5f4aa7SIgor Mitsyanko cmd->dtim_period = s->dtim_period; 2568b5f4aa7SIgor Mitsyanko cmd->beacon_interval = cpu_to_le16(s->beacon_interval); 2578b5f4aa7SIgor Mitsyanko cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid); 2588b5f4aa7SIgor Mitsyanko cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout); 2598b5f4aa7SIgor Mitsyanko cmd->smps_mode = s->smps_mode; 2608b5f4aa7SIgor Mitsyanko cmd->p2p_ctwindow = s->p2p_ctwindow; 2618b5f4aa7SIgor Mitsyanko cmd->p2p_opp_ps = s->p2p_opp_ps; 2628b5f4aa7SIgor Mitsyanko cmd->pbss = s->pbss; 2638b5f4aa7SIgor Mitsyanko cmd->ht_required = s->ht_required; 2648b5f4aa7SIgor Mitsyanko cmd->vht_required = s->vht_required; 265ed7791d9SMikhail Karpenko cmd->twt_responder = s->twt_responder; 266ed7791d9SMikhail Karpenko if (s->he_obss_pd.enable) { 267ed7791d9SMikhail Karpenko cmd->sr_params.sr_control |= QLINK_SR_SRG_INFORMATION_PRESENT; 268ed7791d9SMikhail Karpenko cmd->sr_params.srg_obss_pd_min_offset = 269ed7791d9SMikhail Karpenko s->he_obss_pd.min_offset; 270ed7791d9SMikhail Karpenko cmd->sr_params.srg_obss_pd_max_offset = 271ed7791d9SMikhail Karpenko s->he_obss_pd.max_offset; 272ed7791d9SMikhail Karpenko } 27398f44cb0SIgor Mitsyanko 2748b5f4aa7SIgor Mitsyanko aen = &cmd->aen; 2758b5f4aa7SIgor Mitsyanko aen->auth_type = s->auth_type; 2768b5f4aa7SIgor Mitsyanko aen->privacy = !!s->privacy; 2778b5f4aa7SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions); 2788b5f4aa7SIgor Mitsyanko aen->cipher_group = cpu_to_le32(s->crypto.cipher_group); 2798b5f4aa7SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise); 28098f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2818b5f4aa7SIgor Mitsyanko aen->ciphers_pairwise[i] = 2829b692df1SIgor Mitsyanko cpu_to_le32(s->crypto.ciphers_pairwise[i]); 2838b5f4aa7SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); 28498f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2858b5f4aa7SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); 2868b5f4aa7SIgor Mitsyanko aen->control_port = s->crypto.control_port; 2878b5f4aa7SIgor Mitsyanko aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; 2888b5f4aa7SIgor Mitsyanko aen->control_port_ethertype = 2899b692df1SIgor Mitsyanko cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype)); 29098f44cb0SIgor Mitsyanko 291f99201cbSIgor Mitsyanko if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN) 292f99201cbSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid, 293f99201cbSIgor Mitsyanko s->ssid_len); 294f99201cbSIgor Mitsyanko 295f99201cbSIgor Mitsyanko if (cfg80211_chandef_valid(&s->chandef)) { 296f99201cbSIgor Mitsyanko struct qlink_tlv_chandef *chtlv = 297f99201cbSIgor Mitsyanko (struct qlink_tlv_chandef *)skb_put(cmd_skb, 298f99201cbSIgor Mitsyanko sizeof(*chtlv)); 299f99201cbSIgor Mitsyanko 300f99201cbSIgor Mitsyanko chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF); 301f99201cbSIgor Mitsyanko chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) - 302f99201cbSIgor Mitsyanko sizeof(chtlv->hdr)); 3035bf374abSSergey Matyukevich qlink_chandef_cfg2q(&s->chandef, &chtlv->chdef); 304f99201cbSIgor Mitsyanko } 305f99201cbSIgor Mitsyanko 30617011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD, 30717011da0SIgor Mitsyanko s->beacon.head, s->beacon.head_len); 30817011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_TAIL, 30917011da0SIgor Mitsyanko s->beacon.tail, s->beacon.tail_len); 31017011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_IES, 31117011da0SIgor Mitsyanko s->beacon.beacon_ies, s->beacon.beacon_ies_len); 31217011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP, 31317011da0SIgor Mitsyanko s->beacon.probe_resp, s->beacon.probe_resp_len); 31417011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP_IES, 31517011da0SIgor Mitsyanko s->beacon.proberesp_ies, 31617011da0SIgor Mitsyanko s->beacon.proberesp_ies_len); 31717011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_RESP, 31817011da0SIgor Mitsyanko s->beacon.assocresp_ies, 31917011da0SIgor Mitsyanko s->beacon.assocresp_ies_len); 32017011da0SIgor Mitsyanko 321a3945f43SIgor Mitsyanko if (s->ht_cap) { 322a3945f43SIgor Mitsyanko struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) 3238b0b5f1bSIgor Mitsyanko skb_put(cmd_skb, sizeof(*tlv) + 3248b0b5f1bSIgor Mitsyanko round_up(sizeof(*s->ht_cap), QLINK_ALIGN)); 325a3945f43SIgor Mitsyanko 326a3945f43SIgor Mitsyanko tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); 327a3945f43SIgor Mitsyanko tlv->len = cpu_to_le16(sizeof(*s->ht_cap)); 328a3945f43SIgor Mitsyanko memcpy(tlv->val, s->ht_cap, sizeof(*s->ht_cap)); 329a3945f43SIgor Mitsyanko } 330a3945f43SIgor Mitsyanko 331a3945f43SIgor Mitsyanko if (s->vht_cap) { 332a3945f43SIgor Mitsyanko struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) 333a3945f43SIgor Mitsyanko skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->vht_cap)); 334a3945f43SIgor Mitsyanko 335a3945f43SIgor Mitsyanko tlv->type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); 336a3945f43SIgor Mitsyanko tlv->len = cpu_to_le16(sizeof(*s->vht_cap)); 337a3945f43SIgor Mitsyanko memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap)); 338a3945f43SIgor Mitsyanko } 339a3945f43SIgor Mitsyanko 340df0af4c7SMikhail Karpenko if (s->he_cap) 341df0af4c7SMikhail Karpenko qtnf_cmd_tlv_ie_ext_add(cmd_skb, WLAN_EID_EXT_HE_CAPABILITY, 342df0af4c7SMikhail Karpenko s->he_cap, sizeof(*s->he_cap)); 343df0af4c7SMikhail Karpenko 344f1398fd2SVasily Ulyanov if (s->acl) { 34595336d4cSGustavo A. R. Silva size_t acl_size = struct_size(s->acl, mac_addrs, 34695336d4cSGustavo A. R. Silva s->acl->n_acl_entries); 347f1398fd2SVasily Ulyanov struct qlink_tlv_hdr *tlv = 3488b0b5f1bSIgor Mitsyanko skb_put(cmd_skb, 3498b0b5f1bSIgor Mitsyanko sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN)); 350f1398fd2SVasily Ulyanov 351f1398fd2SVasily Ulyanov tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); 352f1398fd2SVasily Ulyanov tlv->len = cpu_to_le16(acl_size); 353f1398fd2SVasily Ulyanov qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val); 354f1398fd2SVasily Ulyanov } 355f1398fd2SVasily Ulyanov 3568b5f4aa7SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 357c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 358c6ed298fSSergey Matyukevich if (ret) 35998f44cb0SIgor Mitsyanko goto out; 36098f44cb0SIgor Mitsyanko 36117011da0SIgor Mitsyanko netif_carrier_on(vif->netdev); 36217011da0SIgor Mitsyanko 36398f44cb0SIgor Mitsyanko out: 36498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 365c6ed298fSSergey Matyukevich 36698f44cb0SIgor Mitsyanko return ret; 36798f44cb0SIgor Mitsyanko } 36898f44cb0SIgor Mitsyanko 36998f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif) 37098f44cb0SIgor Mitsyanko { 37198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 37298f44cb0SIgor Mitsyanko int ret; 37398f44cb0SIgor Mitsyanko 37498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 37598f44cb0SIgor Mitsyanko QLINK_CMD_STOP_AP, 37698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 377c93fe71cSSergey Matyukevich if (!cmd_skb) 37898f44cb0SIgor Mitsyanko return -ENOMEM; 37998f44cb0SIgor Mitsyanko 38098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 381c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 382c6ed298fSSergey Matyukevich if (ret) 38398f44cb0SIgor Mitsyanko goto out; 38498f44cb0SIgor Mitsyanko 38598f44cb0SIgor Mitsyanko out: 38698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 387c6ed298fSSergey Matyukevich 38898f44cb0SIgor Mitsyanko return ret; 38998f44cb0SIgor Mitsyanko } 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg) 39298f44cb0SIgor Mitsyanko { 39398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 39498f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_frame_register *cmd; 39598f44cb0SIgor Mitsyanko int ret; 39698f44cb0SIgor Mitsyanko 39798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 39898f44cb0SIgor Mitsyanko QLINK_CMD_REGISTER_MGMT, 39998f44cb0SIgor Mitsyanko sizeof(*cmd)); 400c93fe71cSSergey Matyukevich if (!cmd_skb) 40198f44cb0SIgor Mitsyanko return -ENOMEM; 40298f44cb0SIgor Mitsyanko 40398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 40498f44cb0SIgor Mitsyanko 40598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_mgmt_frame_register *)cmd_skb->data; 40698f44cb0SIgor Mitsyanko cmd->frame_type = cpu_to_le16(frame_type); 40798f44cb0SIgor Mitsyanko cmd->do_register = reg; 40898f44cb0SIgor Mitsyanko 409c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 410c6ed298fSSergey Matyukevich if (ret) 41198f44cb0SIgor Mitsyanko goto out; 41298f44cb0SIgor Mitsyanko 41398f44cb0SIgor Mitsyanko out: 41498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 415c6ed298fSSergey Matyukevich 41698f44cb0SIgor Mitsyanko return ret; 41798f44cb0SIgor Mitsyanko } 41898f44cb0SIgor Mitsyanko 419bc70732fSIgor Mitsyanko int qtnf_cmd_send_frame(struct qtnf_vif *vif, u32 cookie, u16 flags, 42098f44cb0SIgor Mitsyanko u16 freq, const u8 *buf, size_t len) 42198f44cb0SIgor Mitsyanko { 42298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 423bc70732fSIgor Mitsyanko struct qlink_cmd_frame_tx *cmd; 42498f44cb0SIgor Mitsyanko int ret; 42598f44cb0SIgor Mitsyanko 42698f44cb0SIgor Mitsyanko if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) { 42798f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: frame is too big: %zu\n", vif->mac->macid, 42898f44cb0SIgor Mitsyanko vif->vifid, len); 42998f44cb0SIgor Mitsyanko return -E2BIG; 43098f44cb0SIgor Mitsyanko } 43198f44cb0SIgor Mitsyanko 43298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 433bc70732fSIgor Mitsyanko QLINK_CMD_SEND_FRAME, 43498f44cb0SIgor Mitsyanko sizeof(*cmd)); 435c93fe71cSSergey Matyukevich if (!cmd_skb) 43698f44cb0SIgor Mitsyanko return -ENOMEM; 43798f44cb0SIgor Mitsyanko 43898f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 43998f44cb0SIgor Mitsyanko 440bc70732fSIgor Mitsyanko cmd = (struct qlink_cmd_frame_tx *)cmd_skb->data; 44198f44cb0SIgor Mitsyanko cmd->cookie = cpu_to_le32(cookie); 44298f44cb0SIgor Mitsyanko cmd->freq = cpu_to_le16(freq); 44398f44cb0SIgor Mitsyanko cmd->flags = cpu_to_le16(flags); 44498f44cb0SIgor Mitsyanko 44598f44cb0SIgor Mitsyanko if (len && buf) 44698f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_buffer(cmd_skb, buf, len); 44798f44cb0SIgor Mitsyanko 448c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 449c6ed298fSSergey Matyukevich if (ret) 45098f44cb0SIgor Mitsyanko goto out; 45198f44cb0SIgor Mitsyanko 45298f44cb0SIgor Mitsyanko out: 45398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 454c6ed298fSSergey Matyukevich 45598f44cb0SIgor Mitsyanko return ret; 45698f44cb0SIgor Mitsyanko } 45798f44cb0SIgor Mitsyanko 45898f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, 45998f44cb0SIgor Mitsyanko const u8 *buf, size_t len) 46098f44cb0SIgor Mitsyanko { 46198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 46298f44cb0SIgor Mitsyanko int ret; 46398f44cb0SIgor Mitsyanko 4644d1f0fabSIgor Mitsyanko if (len > QTNF_MAX_CMD_BUF_SIZE) { 46598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid, 46698f44cb0SIgor Mitsyanko vif->vifid, frame_type, len); 46798f44cb0SIgor Mitsyanko return -E2BIG; 46898f44cb0SIgor Mitsyanko } 46998f44cb0SIgor Mitsyanko 47098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 47198f44cb0SIgor Mitsyanko QLINK_CMD_MGMT_SET_APPIE, 4724d1f0fabSIgor Mitsyanko sizeof(struct qlink_cmd)); 473c93fe71cSSergey Matyukevich if (!cmd_skb) 47498f44cb0SIgor Mitsyanko return -ENOMEM; 47598f44cb0SIgor Mitsyanko 4764d1f0fabSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len); 4774d1f0fabSIgor Mitsyanko 47898f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 479c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 480c6ed298fSSergey Matyukevich if (ret) 48198f44cb0SIgor Mitsyanko goto out; 48298f44cb0SIgor Mitsyanko 48398f44cb0SIgor Mitsyanko out: 48498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 485c6ed298fSSergey Matyukevich 48698f44cb0SIgor Mitsyanko return ret; 48798f44cb0SIgor Mitsyanko } 48898f44cb0SIgor Mitsyanko 48998f44cb0SIgor Mitsyanko static void 49098f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(struct rate_info *rate_dst, 49198f44cb0SIgor Mitsyanko const struct qlink_sta_info_rate *rate_src) 49298f44cb0SIgor Mitsyanko { 49398f44cb0SIgor Mitsyanko rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10; 49498f44cb0SIgor Mitsyanko 49598f44cb0SIgor Mitsyanko rate_dst->mcs = rate_src->mcs; 49698f44cb0SIgor Mitsyanko rate_dst->nss = rate_src->nss; 49798f44cb0SIgor Mitsyanko rate_dst->flags = 0; 49898f44cb0SIgor Mitsyanko 49998f44cb0SIgor Mitsyanko switch (rate_src->bw) { 5004d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_5: 50198f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_5; 50298f44cb0SIgor Mitsyanko break; 5034d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_10: 50498f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_10; 50598f44cb0SIgor Mitsyanko break; 5064d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_20: 5074d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_20_NOHT: 50898f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_20; 50998f44cb0SIgor Mitsyanko break; 5104d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_40: 51198f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_40; 51298f44cb0SIgor Mitsyanko break; 5134d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_80: 51498f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_80; 51598f44cb0SIgor Mitsyanko break; 5164d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_160: 51798f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_160; 51898f44cb0SIgor Mitsyanko break; 51998f44cb0SIgor Mitsyanko default: 52098f44cb0SIgor Mitsyanko rate_dst->bw = 0; 52198f44cb0SIgor Mitsyanko break; 52298f44cb0SIgor Mitsyanko } 52398f44cb0SIgor Mitsyanko 52498f44cb0SIgor Mitsyanko if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HT_MCS) 52598f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_MCS; 52698f44cb0SIgor Mitsyanko else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS) 52798f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS; 528b73f0aacSSergey Matyukevich else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HE_MCS) 529b73f0aacSSergey Matyukevich rate_dst->flags |= RATE_INFO_FLAGS_HE_MCS; 530d5657b70SSergey Matyukevich 531d5657b70SSergey Matyukevich if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_SHORT_GI) 532d5657b70SSergey Matyukevich rate_dst->flags |= RATE_INFO_FLAGS_SHORT_GI; 53398f44cb0SIgor Mitsyanko } 53498f44cb0SIgor Mitsyanko 53598f44cb0SIgor Mitsyanko static void 53698f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst, 53798f44cb0SIgor Mitsyanko const struct qlink_sta_info_state *src) 53898f44cb0SIgor Mitsyanko { 53998f44cb0SIgor Mitsyanko u32 mask, value; 54098f44cb0SIgor Mitsyanko 54198f44cb0SIgor Mitsyanko dst->mask = 0; 54298f44cb0SIgor Mitsyanko dst->set = 0; 54398f44cb0SIgor Mitsyanko 54498f44cb0SIgor Mitsyanko mask = le32_to_cpu(src->mask); 54598f44cb0SIgor Mitsyanko value = le32_to_cpu(src->value); 54698f44cb0SIgor Mitsyanko 54798f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHORIZED) { 54898f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHORIZED); 54998f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHORIZED) 55098f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); 55198f44cb0SIgor Mitsyanko } 55298f44cb0SIgor Mitsyanko 55398f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_SHORT_PREAMBLE) { 55498f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 55598f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_SHORT_PREAMBLE) 55698f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 55798f44cb0SIgor Mitsyanko } 55898f44cb0SIgor Mitsyanko 55998f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_WME) { 56098f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_WME); 56198f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_WME) 56298f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_WME); 56398f44cb0SIgor Mitsyanko } 56498f44cb0SIgor Mitsyanko 56598f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_MFP) { 56698f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_MFP); 56798f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_MFP) 56898f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_MFP); 56998f44cb0SIgor Mitsyanko } 57098f44cb0SIgor Mitsyanko 57198f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHENTICATED) { 57298f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 57398f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHENTICATED) 57498f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 57598f44cb0SIgor Mitsyanko } 57698f44cb0SIgor Mitsyanko 57798f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_TDLS_PEER) { 57898f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); 57998f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_TDLS_PEER) 58098f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_TDLS_PEER); 58198f44cb0SIgor Mitsyanko } 58298f44cb0SIgor Mitsyanko 58398f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_ASSOCIATED) { 58498f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); 58598f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_ASSOCIATED) 58698f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); 58798f44cb0SIgor Mitsyanko } 58898f44cb0SIgor Mitsyanko } 58998f44cb0SIgor Mitsyanko 59098f44cb0SIgor Mitsyanko static void 5918b0b5f1bSIgor Mitsyanko qtnf_cmd_sta_info_parse(struct station_info *sinfo, const u8 *data, 5924d2a7a1cSIgor Mitsyanko size_t resp_size) 59398f44cb0SIgor Mitsyanko { 5948b0b5f1bSIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 5954d2a7a1cSIgor Mitsyanko const struct qlink_sta_stats *stats = NULL; 5964d2a7a1cSIgor Mitsyanko const u8 *map = NULL; 5974d2a7a1cSIgor Mitsyanko unsigned int map_len = 0; 5984d2a7a1cSIgor Mitsyanko unsigned int stats_len = 0; 5994d2a7a1cSIgor Mitsyanko u16 tlv_len; 60098f44cb0SIgor Mitsyanko 6014d2a7a1cSIgor Mitsyanko #define qtnf_sta_stat_avail(stat_name, bitn) \ 6024d2a7a1cSIgor Mitsyanko (qtnf_utils_is_bit_set(map, bitn, map_len) && \ 6034d2a7a1cSIgor Mitsyanko (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len)) 60498f44cb0SIgor Mitsyanko 6058b0b5f1bSIgor Mitsyanko qlink_for_each_tlv(tlv, data, resp_size) { 6064d2a7a1cSIgor Mitsyanko tlv_len = le16_to_cpu(tlv->len); 60798f44cb0SIgor Mitsyanko 6084d2a7a1cSIgor Mitsyanko switch (le16_to_cpu(tlv->type)) { 609310cd5ddSIgor Mitsyanko case QTN_TLV_ID_BITMAP: 6104d2a7a1cSIgor Mitsyanko map_len = tlv_len; 6114d2a7a1cSIgor Mitsyanko map = tlv->val; 61298f44cb0SIgor Mitsyanko break; 6134d2a7a1cSIgor Mitsyanko case QTN_TLV_ID_STA_STATS: 6144d2a7a1cSIgor Mitsyanko stats_len = tlv_len; 6154d2a7a1cSIgor Mitsyanko stats = (const struct qlink_sta_stats *)tlv->val; 61698f44cb0SIgor Mitsyanko break; 61798f44cb0SIgor Mitsyanko default: 61898f44cb0SIgor Mitsyanko break; 61998f44cb0SIgor Mitsyanko } 6208b0b5f1bSIgor Mitsyanko } 6214d2a7a1cSIgor Mitsyanko 6228b0b5f1bSIgor Mitsyanko if (!qlink_tlv_parsing_ok(tlv, data, resp_size)) { 6238b0b5f1bSIgor Mitsyanko pr_err("Malformed TLV buffer\n"); 6248b0b5f1bSIgor Mitsyanko return; 62598f44cb0SIgor Mitsyanko } 62698f44cb0SIgor Mitsyanko 6274d2a7a1cSIgor Mitsyanko if (!map || !stats) 6284d2a7a1cSIgor Mitsyanko return; 6294d2a7a1cSIgor Mitsyanko 6304d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) { 63122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME); 6324d2a7a1cSIgor Mitsyanko sinfo->inactive_time = le32_to_cpu(stats->inactive_time); 63398f44cb0SIgor Mitsyanko } 63498f44cb0SIgor Mitsyanko 6354d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(connected_time, 6364d2a7a1cSIgor Mitsyanko QLINK_STA_INFO_CONNECTED_TIME)) { 63722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME); 6384d2a7a1cSIgor Mitsyanko sinfo->connected_time = le32_to_cpu(stats->connected_time); 6394d2a7a1cSIgor Mitsyanko } 6404d2a7a1cSIgor Mitsyanko 6414d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) { 64222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 6434d2a7a1cSIgor Mitsyanko sinfo->signal = stats->signal - QLINK_RSSI_OFFSET; 6444d2a7a1cSIgor Mitsyanko } 6454d2a7a1cSIgor Mitsyanko 6464d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) { 64722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); 6484d2a7a1cSIgor Mitsyanko sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET; 6494d2a7a1cSIgor Mitsyanko } 6504d2a7a1cSIgor Mitsyanko 6514d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) { 65222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); 6534d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate); 6544d2a7a1cSIgor Mitsyanko } 6554d2a7a1cSIgor Mitsyanko 6564d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) { 65722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 6584d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate); 6594d2a7a1cSIgor Mitsyanko } 6604d2a7a1cSIgor Mitsyanko 6614d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) { 66222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS); 6634d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags); 6644d2a7a1cSIgor Mitsyanko } 6654d2a7a1cSIgor Mitsyanko 6664d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) { 66722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); 6684d2a7a1cSIgor Mitsyanko sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes); 6694d2a7a1cSIgor Mitsyanko } 6704d2a7a1cSIgor Mitsyanko 6714d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) { 67222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES); 6734d2a7a1cSIgor Mitsyanko sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes); 6744d2a7a1cSIgor Mitsyanko } 6754d2a7a1cSIgor Mitsyanko 6764d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) { 67722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); 6784d2a7a1cSIgor Mitsyanko sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes); 6794d2a7a1cSIgor Mitsyanko } 6804d2a7a1cSIgor Mitsyanko 6814d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) { 68222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); 6834d2a7a1cSIgor Mitsyanko sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes); 6844d2a7a1cSIgor Mitsyanko } 6854d2a7a1cSIgor Mitsyanko 6864d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) { 68722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); 6884d2a7a1cSIgor Mitsyanko sinfo->rx_packets = le32_to_cpu(stats->rx_packets); 6894d2a7a1cSIgor Mitsyanko } 6904d2a7a1cSIgor Mitsyanko 6914d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) { 69222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); 6934d2a7a1cSIgor Mitsyanko sinfo->tx_packets = le32_to_cpu(stats->tx_packets); 6944d2a7a1cSIgor Mitsyanko } 6954d2a7a1cSIgor Mitsyanko 6964d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) { 69722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX); 6984d2a7a1cSIgor Mitsyanko sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon); 6994d2a7a1cSIgor Mitsyanko } 7004d2a7a1cSIgor Mitsyanko 7014d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) { 70222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC); 7034d2a7a1cSIgor Mitsyanko sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc); 7044d2a7a1cSIgor Mitsyanko } 7054d2a7a1cSIgor Mitsyanko 7064d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) { 70722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); 7084d2a7a1cSIgor Mitsyanko sinfo->tx_failed = le32_to_cpu(stats->tx_failed); 7094d2a7a1cSIgor Mitsyanko } 7104d2a7a1cSIgor Mitsyanko 7114d2a7a1cSIgor Mitsyanko #undef qtnf_sta_stat_avail 71298f44cb0SIgor Mitsyanko } 71398f44cb0SIgor Mitsyanko 71498f44cb0SIgor Mitsyanko int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac, 71598f44cb0SIgor Mitsyanko struct station_info *sinfo) 71698f44cb0SIgor Mitsyanko { 71798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 71898f44cb0SIgor Mitsyanko struct qlink_cmd_get_sta_info *cmd; 71998f44cb0SIgor Mitsyanko const struct qlink_resp_get_sta_info *resp; 7201066bd19SSergey Matyukevich size_t var_resp_len = 0; 72198f44cb0SIgor Mitsyanko int ret = 0; 72298f44cb0SIgor Mitsyanko 72398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 72498f44cb0SIgor Mitsyanko QLINK_CMD_GET_STA_INFO, 72598f44cb0SIgor Mitsyanko sizeof(*cmd)); 726c93fe71cSSergey Matyukevich if (!cmd_skb) 72798f44cb0SIgor Mitsyanko return -ENOMEM; 72898f44cb0SIgor Mitsyanko 72998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 73098f44cb0SIgor Mitsyanko 73198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_get_sta_info *)cmd_skb->data; 73298f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, sta_mac); 73398f44cb0SIgor Mitsyanko 73498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 735c6ed298fSSergey Matyukevich sizeof(*resp), &var_resp_len); 736c6ed298fSSergey Matyukevich if (ret) 73798f44cb0SIgor Mitsyanko goto out; 73898f44cb0SIgor Mitsyanko 73998f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_sta_info *)resp_skb->data; 74098f44cb0SIgor Mitsyanko 741c6ed298fSSergey Matyukevich if (!ether_addr_equal(sta_mac, resp->sta_addr)) { 74298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: wrong mac in reply: %pM != %pM\n", 74398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, resp->sta_addr, sta_mac); 74498f44cb0SIgor Mitsyanko ret = -EINVAL; 74598f44cb0SIgor Mitsyanko goto out; 74698f44cb0SIgor Mitsyanko } 74798f44cb0SIgor Mitsyanko 7488b0b5f1bSIgor Mitsyanko qtnf_cmd_sta_info_parse(sinfo, resp->info, var_resp_len); 74998f44cb0SIgor Mitsyanko 75098f44cb0SIgor Mitsyanko out: 75198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 75298f44cb0SIgor Mitsyanko consume_skb(resp_skb); 75398f44cb0SIgor Mitsyanko 75498f44cb0SIgor Mitsyanko return ret; 75598f44cb0SIgor Mitsyanko } 75698f44cb0SIgor Mitsyanko 75798f44cb0SIgor Mitsyanko static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif, 75898f44cb0SIgor Mitsyanko enum nl80211_iftype iftype, 759de624a35SSergey Matyukevich int use4addr, 76098f44cb0SIgor Mitsyanko u8 *mac_addr, 76198f44cb0SIgor Mitsyanko enum qlink_cmd_type cmd_type) 76298f44cb0SIgor Mitsyanko { 76398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 76498f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 76598f44cb0SIgor Mitsyanko const struct qlink_resp_manage_intf *resp; 76698f44cb0SIgor Mitsyanko int ret = 0; 76798f44cb0SIgor Mitsyanko 76898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 76998f44cb0SIgor Mitsyanko cmd_type, 77098f44cb0SIgor Mitsyanko sizeof(*cmd)); 771c93fe71cSSergey Matyukevich if (!cmd_skb) 77298f44cb0SIgor Mitsyanko return -ENOMEM; 77398f44cb0SIgor Mitsyanko 77498f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 77598f44cb0SIgor Mitsyanko 77698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 777de624a35SSergey Matyukevich cmd->intf_info.use4addr = use4addr; 77898f44cb0SIgor Mitsyanko 77998f44cb0SIgor Mitsyanko switch (iftype) { 78098f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 78198f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 78298f44cb0SIgor Mitsyanko break; 78398f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 78498f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 78598f44cb0SIgor Mitsyanko break; 78698f44cb0SIgor Mitsyanko default: 78798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: unsupported type %d\n", vif->mac->macid, 78898f44cb0SIgor Mitsyanko vif->vifid, iftype); 78998f44cb0SIgor Mitsyanko ret = -EINVAL; 79098f44cb0SIgor Mitsyanko goto out; 79198f44cb0SIgor Mitsyanko } 79298f44cb0SIgor Mitsyanko 79398f44cb0SIgor Mitsyanko if (mac_addr) 79498f44cb0SIgor Mitsyanko ether_addr_copy(cmd->intf_info.mac_addr, mac_addr); 79598f44cb0SIgor Mitsyanko else 79698f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 79798f44cb0SIgor Mitsyanko 79898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 799c6ed298fSSergey Matyukevich sizeof(*resp), NULL); 800c6ed298fSSergey Matyukevich if (ret) 80198f44cb0SIgor Mitsyanko goto out; 80298f44cb0SIgor Mitsyanko 80398f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_manage_intf *)resp_skb->data; 80498f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, resp->intf_info.mac_addr); 80598f44cb0SIgor Mitsyanko 80698f44cb0SIgor Mitsyanko out: 80798f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 80898f44cb0SIgor Mitsyanko consume_skb(resp_skb); 80998f44cb0SIgor Mitsyanko 81098f44cb0SIgor Mitsyanko return ret; 81198f44cb0SIgor Mitsyanko } 81298f44cb0SIgor Mitsyanko 813de624a35SSergey Matyukevich int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype, 814de624a35SSergey Matyukevich int use4addr, u8 *mac_addr) 81598f44cb0SIgor Mitsyanko { 816de624a35SSergey Matyukevich return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr, 81798f44cb0SIgor Mitsyanko QLINK_CMD_ADD_INTF); 81898f44cb0SIgor Mitsyanko } 81998f44cb0SIgor Mitsyanko 82098f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif, 821de624a35SSergey Matyukevich enum nl80211_iftype iftype, 822de624a35SSergey Matyukevich int use4addr, 823de624a35SSergey Matyukevich u8 *mac_addr) 82498f44cb0SIgor Mitsyanko { 82593eeab26SIgor Mitsyanko int ret; 82693eeab26SIgor Mitsyanko 82793eeab26SIgor Mitsyanko ret = qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr, 82898f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_INTF); 82993eeab26SIgor Mitsyanko 83093eeab26SIgor Mitsyanko /* Regulatory settings may be different for different interface types */ 83193eeab26SIgor Mitsyanko if (ret == 0 && vif->wdev.iftype != iftype) { 83293eeab26SIgor Mitsyanko enum nl80211_band band; 83393eeab26SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(vif->mac); 83493eeab26SIgor Mitsyanko 83593eeab26SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 83693eeab26SIgor Mitsyanko if (!wiphy->bands[band]) 83793eeab26SIgor Mitsyanko continue; 83893eeab26SIgor Mitsyanko 83993eeab26SIgor Mitsyanko qtnf_cmd_band_info_get(vif->mac, wiphy->bands[band]); 84093eeab26SIgor Mitsyanko } 84193eeab26SIgor Mitsyanko } 84293eeab26SIgor Mitsyanko 84393eeab26SIgor Mitsyanko return ret; 84498f44cb0SIgor Mitsyanko } 84598f44cb0SIgor Mitsyanko 84698f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_intf(struct qtnf_vif *vif) 84798f44cb0SIgor Mitsyanko { 84898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 84998f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 85098f44cb0SIgor Mitsyanko int ret = 0; 85198f44cb0SIgor Mitsyanko 85298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 85398f44cb0SIgor Mitsyanko QLINK_CMD_DEL_INTF, 85498f44cb0SIgor Mitsyanko sizeof(*cmd)); 855c93fe71cSSergey Matyukevich if (!cmd_skb) 85698f44cb0SIgor Mitsyanko return -ENOMEM; 85798f44cb0SIgor Mitsyanko 85898f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 85998f44cb0SIgor Mitsyanko 86098f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 86198f44cb0SIgor Mitsyanko 86298f44cb0SIgor Mitsyanko switch (vif->wdev.iftype) { 86398f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 86498f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 86598f44cb0SIgor Mitsyanko break; 86698f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 86798f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 86898f44cb0SIgor Mitsyanko break; 86998f44cb0SIgor Mitsyanko default: 87098f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported iftype %d\n", vif->mac->macid, 87198f44cb0SIgor Mitsyanko vif->vifid, vif->wdev.iftype); 87298f44cb0SIgor Mitsyanko ret = -EINVAL; 87398f44cb0SIgor Mitsyanko goto out; 87498f44cb0SIgor Mitsyanko } 87598f44cb0SIgor Mitsyanko 87698f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 87798f44cb0SIgor Mitsyanko 878c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 879c6ed298fSSergey Matyukevich if (ret) 88098f44cb0SIgor Mitsyanko goto out; 88198f44cb0SIgor Mitsyanko 88298f44cb0SIgor Mitsyanko out: 88398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 88498f44cb0SIgor Mitsyanko return ret; 88598f44cb0SIgor Mitsyanko } 88698f44cb0SIgor Mitsyanko 88798f44cb0SIgor Mitsyanko static int 88898f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, 8894dd07d2bSSergey Matyukevich const struct qlink_resp_get_hw_info *resp, 8904dd07d2bSSergey Matyukevich size_t info_len) 89198f44cb0SIgor Mitsyanko { 89298f44cb0SIgor Mitsyanko struct qtnf_hw_info *hwinfo = &bus->hw_info; 8934dd07d2bSSergey Matyukevich const struct qlink_tlv_hdr *tlv; 8945ec5b532SVasily Ulyanov const char *bld_name = NULL; 8955ec5b532SVasily Ulyanov const char *bld_rev = NULL; 8965ec5b532SVasily Ulyanov const char *bld_type = NULL; 8975ec5b532SVasily Ulyanov const char *bld_label = NULL; 8985ec5b532SVasily Ulyanov u32 bld_tmstamp = 0; 8995ec5b532SVasily Ulyanov u32 plat_id = 0; 9005ec5b532SVasily Ulyanov const char *hw_id = NULL; 9015ec5b532SVasily Ulyanov const char *calibration_ver = NULL; 9025ec5b532SVasily Ulyanov const char *uboot_ver = NULL; 9035ec5b532SVasily Ulyanov u32 hw_ver = 0; 9044dd07d2bSSergey Matyukevich u16 tlv_type; 905310cd5ddSIgor Mitsyanko u16 tlv_len; 90698f44cb0SIgor Mitsyanko 90798f44cb0SIgor Mitsyanko hwinfo->num_mac = resp->num_mac; 90898f44cb0SIgor Mitsyanko hwinfo->mac_bitmap = resp->mac_bitmap; 90998f44cb0SIgor Mitsyanko hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); 91098f44cb0SIgor Mitsyanko hwinfo->total_tx_chain = resp->total_tx_chain; 91198f44cb0SIgor Mitsyanko hwinfo->total_rx_chain = resp->total_rx_chain; 9124dd07d2bSSergey Matyukevich 9135ec5b532SVasily Ulyanov bld_tmstamp = le32_to_cpu(resp->bld_tmstamp); 9145ec5b532SVasily Ulyanov plat_id = le32_to_cpu(resp->plat_id); 9155ec5b532SVasily Ulyanov hw_ver = le32_to_cpu(resp->hw_ver); 9165ec5b532SVasily Ulyanov 9178b0b5f1bSIgor Mitsyanko qlink_for_each_tlv(tlv, resp->info, info_len) { 9184dd07d2bSSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 919310cd5ddSIgor Mitsyanko tlv_len = le16_to_cpu(tlv->len); 9204dd07d2bSSergey Matyukevich 9214dd07d2bSSergey Matyukevich switch (tlv_type) { 9225ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_NAME: 9235ec5b532SVasily Ulyanov bld_name = (const void *)tlv->val; 9245ec5b532SVasily Ulyanov break; 9255ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_REV: 9265ec5b532SVasily Ulyanov bld_rev = (const void *)tlv->val; 9275ec5b532SVasily Ulyanov break; 9285ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_TYPE: 9295ec5b532SVasily Ulyanov bld_type = (const void *)tlv->val; 9305ec5b532SVasily Ulyanov break; 9315ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_LABEL: 9325ec5b532SVasily Ulyanov bld_label = (const void *)tlv->val; 9335ec5b532SVasily Ulyanov break; 9345ec5b532SVasily Ulyanov case QTN_TLV_ID_HW_ID: 9355ec5b532SVasily Ulyanov hw_id = (const void *)tlv->val; 9365ec5b532SVasily Ulyanov break; 9375ec5b532SVasily Ulyanov case QTN_TLV_ID_CALIBRATION_VER: 9385ec5b532SVasily Ulyanov calibration_ver = (const void *)tlv->val; 9395ec5b532SVasily Ulyanov break; 9405ec5b532SVasily Ulyanov case QTN_TLV_ID_UBOOT_VER: 9415ec5b532SVasily Ulyanov uboot_ver = (const void *)tlv->val; 9425ec5b532SVasily Ulyanov break; 943310cd5ddSIgor Mitsyanko case QTN_TLV_ID_BITMAP: 944310cd5ddSIgor Mitsyanko memcpy(hwinfo->hw_capab, tlv->val, 945310cd5ddSIgor Mitsyanko min(sizeof(hwinfo->hw_capab), (size_t)tlv_len)); 946310cd5ddSIgor Mitsyanko break; 9474dd07d2bSSergey Matyukevich default: 9484dd07d2bSSergey Matyukevich break; 9494dd07d2bSSergey Matyukevich } 9508b0b5f1bSIgor Mitsyanko } 9514dd07d2bSSergey Matyukevich 9528b0b5f1bSIgor Mitsyanko if (!qlink_tlv_parsing_ok(tlv, resp->info, info_len)) { 9538b0b5f1bSIgor Mitsyanko pr_err("Malformed TLV buffer\n"); 9548b0b5f1bSIgor Mitsyanko return -EINVAL; 9554dd07d2bSSergey Matyukevich } 9564dd07d2bSSergey Matyukevich 957a3ebb033SIgor Mitsyanko pr_info("\nBuild name: %s\n" 958a3ebb033SIgor Mitsyanko "Build revision: %s\n" 959a3ebb033SIgor Mitsyanko "Build type: %s\n" 960a3ebb033SIgor Mitsyanko "Build label: %s\n" 961a3ebb033SIgor Mitsyanko "Build timestamp: %lu\n" 962a3ebb033SIgor Mitsyanko "Platform ID: %lu\n" 963a3ebb033SIgor Mitsyanko "Hardware ID: %s\n" 964a3ebb033SIgor Mitsyanko "Calibration version: %s\n" 965a3ebb033SIgor Mitsyanko "U-Boot version: %s\n" 966a3ebb033SIgor Mitsyanko "Hardware version: 0x%08x\n" 967a3ebb033SIgor Mitsyanko "Qlink ver: %u.%u\n" 968a3ebb033SIgor Mitsyanko "MACs map: %#x\n" 969a3ebb033SIgor Mitsyanko "Chains Rx-Tx: %ux%u\n" 970a3ebb033SIgor Mitsyanko "FW version: 0x%x\n", 9715ec5b532SVasily Ulyanov bld_name, bld_rev, bld_type, bld_label, 9725ec5b532SVasily Ulyanov (unsigned long)bld_tmstamp, 9735ec5b532SVasily Ulyanov (unsigned long)plat_id, 974a3ebb033SIgor Mitsyanko hw_id, calibration_ver, uboot_ver, hw_ver, 975a3ebb033SIgor Mitsyanko QLINK_VER_MAJOR(bus->hw_info.ql_proto_ver), 976a3ebb033SIgor Mitsyanko QLINK_VER_MINOR(bus->hw_info.ql_proto_ver), 977a3ebb033SIgor Mitsyanko hwinfo->mac_bitmap, 978a3ebb033SIgor Mitsyanko hwinfo->total_rx_chain, hwinfo->total_tx_chain, 979a3ebb033SIgor Mitsyanko hwinfo->fw_ver); 9805ec5b532SVasily Ulyanov 9810b419d01SVasily Ulyanov strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version)); 9820b419d01SVasily Ulyanov hwinfo->hw_version = hw_ver; 9830b419d01SVasily Ulyanov 98498f44cb0SIgor Mitsyanko return 0; 98598f44cb0SIgor Mitsyanko } 98698f44cb0SIgor Mitsyanko 98728b91884SSergey Matyukevich static void 98828b91884SSergey Matyukevich qtnf_parse_wowlan_info(struct qtnf_wmac *mac, 98928b91884SSergey Matyukevich const struct qlink_wowlan_capab_data *wowlan) 99028b91884SSergey Matyukevich { 99128b91884SSergey Matyukevich struct qtnf_mac_info *mac_info = &mac->macinfo; 99228b91884SSergey Matyukevich const struct qlink_wowlan_support *data1; 99328b91884SSergey Matyukevich struct wiphy_wowlan_support *supp; 99428b91884SSergey Matyukevich 99528b91884SSergey Matyukevich supp = kzalloc(sizeof(*supp), GFP_KERNEL); 99628b91884SSergey Matyukevich if (!supp) 99728b91884SSergey Matyukevich return; 99828b91884SSergey Matyukevich 99928b91884SSergey Matyukevich switch (le16_to_cpu(wowlan->version)) { 100028b91884SSergey Matyukevich case 0x1: 100128b91884SSergey Matyukevich data1 = (struct qlink_wowlan_support *)wowlan->data; 100228b91884SSergey Matyukevich 100328b91884SSergey Matyukevich supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT; 100428b91884SSergey Matyukevich supp->n_patterns = le32_to_cpu(data1->n_patterns); 100528b91884SSergey Matyukevich supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len); 100628b91884SSergey Matyukevich supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len); 100728b91884SSergey Matyukevich 100828b91884SSergey Matyukevich mac_info->wowlan = supp; 100928b91884SSergey Matyukevich break; 101028b91884SSergey Matyukevich default: 101128b91884SSergey Matyukevich pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n", 101228b91884SSergey Matyukevich mac->macid, le16_to_cpu(wowlan->version)); 101328b91884SSergey Matyukevich kfree(supp); 101428b91884SSergey Matyukevich break; 101528b91884SSergey Matyukevich } 101628b91884SSergey Matyukevich } 101728b91884SSergey Matyukevich 1018c698bce0SIgor Mitsyanko static int 1019c698bce0SIgor Mitsyanko qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, 1020c698bce0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp, 1021c698bce0SIgor Mitsyanko size_t tlv_buf_size) 102298f44cb0SIgor Mitsyanko { 1023bc5db734SIgor Mitsyanko struct ieee80211_iface_combination *comb = mac->macinfo.if_comb; 1024537faf26SSergey Matyukevich size_t n_comb = 0; 1025537faf26SSergey Matyukevich struct ieee80211_iface_limit *limits; 1026537faf26SSergey Matyukevich const struct qlink_iface_limit_record *rec; 1027537faf26SSergey Matyukevich const struct qlink_iface_limit *lim; 102828b91884SSergey Matyukevich const struct qlink_wowlan_capab_data *wowlan; 1029537faf26SSergey Matyukevich u16 rec_len; 1030537faf26SSergey Matyukevich u16 tlv_type; 1031537faf26SSergey Matyukevich u16 tlv_value_len; 103298f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 10339cbd5999SVasily Ulyanov u8 *ext_capa = NULL; 10349cbd5999SVasily Ulyanov u8 *ext_capa_mask = NULL; 10359cbd5999SVasily Ulyanov u8 ext_capa_len = 0; 10369cbd5999SVasily Ulyanov u8 ext_capa_mask_len = 0; 1037537faf26SSergey Matyukevich int i = 0; 1038c698bce0SIgor Mitsyanko struct ieee80211_reg_rule *rule; 1039c698bce0SIgor Mitsyanko unsigned int rule_idx = 0; 1040c698bce0SIgor Mitsyanko const struct qlink_tlv_reg_rule *tlv_rule; 1041c698bce0SIgor Mitsyanko 1042c698bce0SIgor Mitsyanko if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) 1043c698bce0SIgor Mitsyanko return -E2BIG; 1044c698bce0SIgor Mitsyanko 10459a1ace64SGustavo A. R. Silva mac->rd = kzalloc(struct_size(mac->rd, reg_rules, resp->n_reg_rules), 10469a1ace64SGustavo A. R. Silva GFP_KERNEL); 1047c698bce0SIgor Mitsyanko if (!mac->rd) 1048c698bce0SIgor Mitsyanko return -ENOMEM; 1049c698bce0SIgor Mitsyanko 1050c698bce0SIgor Mitsyanko mac->rd->n_reg_rules = resp->n_reg_rules; 1051c698bce0SIgor Mitsyanko mac->rd->alpha2[0] = resp->alpha2[0]; 1052c698bce0SIgor Mitsyanko mac->rd->alpha2[1] = resp->alpha2[1]; 1053c698bce0SIgor Mitsyanko 1054c698bce0SIgor Mitsyanko switch (resp->dfs_region) { 1055c698bce0SIgor Mitsyanko case QLINK_DFS_FCC: 1056c698bce0SIgor Mitsyanko mac->rd->dfs_region = NL80211_DFS_FCC; 1057c698bce0SIgor Mitsyanko break; 1058c698bce0SIgor Mitsyanko case QLINK_DFS_ETSI: 1059c698bce0SIgor Mitsyanko mac->rd->dfs_region = NL80211_DFS_ETSI; 1060c698bce0SIgor Mitsyanko break; 1061c698bce0SIgor Mitsyanko case QLINK_DFS_JP: 1062c698bce0SIgor Mitsyanko mac->rd->dfs_region = NL80211_DFS_JP; 1063c698bce0SIgor Mitsyanko break; 1064c698bce0SIgor Mitsyanko case QLINK_DFS_UNSET: 1065c698bce0SIgor Mitsyanko default: 1066c698bce0SIgor Mitsyanko mac->rd->dfs_region = NL80211_DFS_UNSET; 1067c698bce0SIgor Mitsyanko break; 1068c698bce0SIgor Mitsyanko } 106998f44cb0SIgor Mitsyanko 10708b0b5f1bSIgor Mitsyanko qlink_for_each_tlv(tlv, resp->var_info, tlv_buf_size) { 107198f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 107298f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 107398f44cb0SIgor Mitsyanko 107498f44cb0SIgor Mitsyanko switch (tlv_type) { 107598f44cb0SIgor Mitsyanko case QTN_TLV_ID_IFACE_LIMIT: 1076537faf26SSergey Matyukevich if (unlikely(!comb)) { 1077537faf26SSergey Matyukevich pr_warn("MAC%u: no combinations advertised\n", 107898f44cb0SIgor Mitsyanko mac->macid); 107998f44cb0SIgor Mitsyanko return -EINVAL; 108098f44cb0SIgor Mitsyanko } 108198f44cb0SIgor Mitsyanko 1082537faf26SSergey Matyukevich if (n_comb >= mac->macinfo.n_if_comb) { 1083537faf26SSergey Matyukevich pr_warn("MAC%u: combinations count exceeded\n", 108498f44cb0SIgor Mitsyanko mac->macid); 1085537faf26SSergey Matyukevich n_comb++; 1086537faf26SSergey Matyukevich break; 1087537faf26SSergey Matyukevich } 1088537faf26SSergey Matyukevich 1089537faf26SSergey Matyukevich rec = (void *)tlv->val; 1090537faf26SSergey Matyukevich rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim); 1091537faf26SSergey Matyukevich 1092537faf26SSergey Matyukevich if (unlikely(tlv_value_len != rec_len)) { 1093537faf26SSergey Matyukevich pr_warn("MAC%u: record %zu size mismatch\n", 1094537faf26SSergey Matyukevich mac->macid, n_comb); 109598f44cb0SIgor Mitsyanko return -EINVAL; 109698f44cb0SIgor Mitsyanko } 109798f44cb0SIgor Mitsyanko 10986396bb22SKees Cook limits = kcalloc(rec->n_limits, sizeof(*limits), 1099537faf26SSergey Matyukevich GFP_KERNEL); 1100537faf26SSergey Matyukevich if (!limits) 1101537faf26SSergey Matyukevich return -ENOMEM; 110241c8fa0cSSergey Matyukevich 1103537faf26SSergey Matyukevich comb[n_comb].num_different_channels = 1104537faf26SSergey Matyukevich rec->num_different_channels; 1105537faf26SSergey Matyukevich comb[n_comb].max_interfaces = 1106537faf26SSergey Matyukevich le16_to_cpu(rec->max_interfaces); 1107537faf26SSergey Matyukevich comb[n_comb].n_limits = rec->n_limits; 1108537faf26SSergey Matyukevich comb[n_comb].limits = limits; 110998f44cb0SIgor Mitsyanko 1110537faf26SSergey Matyukevich for (i = 0; i < rec->n_limits; i++) { 1111537faf26SSergey Matyukevich lim = &rec->limits[i]; 1112537faf26SSergey Matyukevich limits[i].max = le16_to_cpu(lim->max_num); 1113537faf26SSergey Matyukevich limits[i].types = 1114537faf26SSergey Matyukevich qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type)); 1115537faf26SSergey Matyukevich pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n", 1116537faf26SSergey Matyukevich mac->macid, n_comb, 1117537faf26SSergey Matyukevich limits[i].max, limits[i].types); 1118537faf26SSergey Matyukevich } 111998f44cb0SIgor Mitsyanko 1120537faf26SSergey Matyukevich n_comb++; 112198f44cb0SIgor Mitsyanko break; 11229cbd5999SVasily Ulyanov case WLAN_EID_EXT_CAPABILITY: 11239cbd5999SVasily Ulyanov if (unlikely(tlv_value_len > U8_MAX)) 11249cbd5999SVasily Ulyanov return -EINVAL; 11259cbd5999SVasily Ulyanov ext_capa = (u8 *)tlv->val; 11269cbd5999SVasily Ulyanov ext_capa_len = tlv_value_len; 11279cbd5999SVasily Ulyanov break; 11289cbd5999SVasily Ulyanov case QTN_TLV_ID_EXT_CAPABILITY_MASK: 11299cbd5999SVasily Ulyanov if (unlikely(tlv_value_len > U8_MAX)) 11309cbd5999SVasily Ulyanov return -EINVAL; 11319cbd5999SVasily Ulyanov ext_capa_mask = (u8 *)tlv->val; 11329cbd5999SVasily Ulyanov ext_capa_mask_len = tlv_value_len; 11339cbd5999SVasily Ulyanov break; 113428b91884SSergey Matyukevich case QTN_TLV_ID_WOWLAN_CAPAB: 113528b91884SSergey Matyukevich if (tlv_value_len < sizeof(*wowlan)) 113628b91884SSergey Matyukevich return -EINVAL; 113728b91884SSergey Matyukevich 113828b91884SSergey Matyukevich wowlan = (void *)tlv->val; 113928b91884SSergey Matyukevich if (!le16_to_cpu(wowlan->len)) { 114028b91884SSergey Matyukevich pr_warn("MAC%u: skip empty WoWLAN data\n", 114128b91884SSergey Matyukevich mac->macid); 114228b91884SSergey Matyukevich break; 114328b91884SSergey Matyukevich } 114428b91884SSergey Matyukevich 114528b91884SSergey Matyukevich rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len); 114628b91884SSergey Matyukevich if (unlikely(tlv_value_len != rec_len)) { 114728b91884SSergey Matyukevich pr_warn("MAC%u: WoWLAN data size mismatch\n", 114828b91884SSergey Matyukevich mac->macid); 114928b91884SSergey Matyukevich return -EINVAL; 115028b91884SSergey Matyukevich } 115128b91884SSergey Matyukevich 115228b91884SSergey Matyukevich kfree(mac->macinfo.wowlan); 115328b91884SSergey Matyukevich mac->macinfo.wowlan = NULL; 115428b91884SSergey Matyukevich qtnf_parse_wowlan_info(mac, wowlan); 115528b91884SSergey Matyukevich break; 1156c698bce0SIgor Mitsyanko case QTN_TLV_ID_REG_RULE: 1157c698bce0SIgor Mitsyanko if (rule_idx >= resp->n_reg_rules) { 1158c698bce0SIgor Mitsyanko pr_warn("unexpected number of rules: %u\n", 1159c698bce0SIgor Mitsyanko resp->n_reg_rules); 1160c698bce0SIgor Mitsyanko return -EINVAL; 1161c698bce0SIgor Mitsyanko } 1162c698bce0SIgor Mitsyanko 1163c698bce0SIgor Mitsyanko if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) { 1164c698bce0SIgor Mitsyanko pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 1165c698bce0SIgor Mitsyanko tlv_type, tlv_value_len); 1166c698bce0SIgor Mitsyanko return -EINVAL; 1167c698bce0SIgor Mitsyanko } 1168c698bce0SIgor Mitsyanko 1169c698bce0SIgor Mitsyanko tlv_rule = (const struct qlink_tlv_reg_rule *)tlv; 1170c698bce0SIgor Mitsyanko rule = &mac->rd->reg_rules[rule_idx++]; 1171c698bce0SIgor Mitsyanko qlink_utils_regrule_q2nl(rule, tlv_rule); 1172c698bce0SIgor Mitsyanko break; 117398f44cb0SIgor Mitsyanko default: 117428b91884SSergey Matyukevich pr_warn("MAC%u: unknown TLV type %u\n", 117528b91884SSergey Matyukevich mac->macid, tlv_type); 117698f44cb0SIgor Mitsyanko break; 117798f44cb0SIgor Mitsyanko } 117898f44cb0SIgor Mitsyanko } 117998f44cb0SIgor Mitsyanko 11808b0b5f1bSIgor Mitsyanko if (!qlink_tlv_parsing_ok(tlv, resp->var_info, tlv_buf_size)) { 11818b0b5f1bSIgor Mitsyanko pr_err("Malformed TLV buffer\n"); 118298f44cb0SIgor Mitsyanko return -EINVAL; 118398f44cb0SIgor Mitsyanko } 118498f44cb0SIgor Mitsyanko 1185537faf26SSergey Matyukevich if (mac->macinfo.n_if_comb != n_comb) { 118698f44cb0SIgor Mitsyanko pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n", 1187537faf26SSergey Matyukevich mac->macid, mac->macinfo.n_if_comb, n_comb); 118898f44cb0SIgor Mitsyanko return -EINVAL; 118998f44cb0SIgor Mitsyanko } 119098f44cb0SIgor Mitsyanko 11919cbd5999SVasily Ulyanov if (ext_capa_len != ext_capa_mask_len) { 11929cbd5999SVasily Ulyanov pr_err("MAC%u: ext_capa/_mask lengths mismatch: %u != %u\n", 11939cbd5999SVasily Ulyanov mac->macid, ext_capa_len, ext_capa_mask_len); 11949cbd5999SVasily Ulyanov return -EINVAL; 11959cbd5999SVasily Ulyanov } 11969cbd5999SVasily Ulyanov 1197c698bce0SIgor Mitsyanko if (rule_idx != resp->n_reg_rules) { 1198c698bce0SIgor Mitsyanko pr_warn("unexpected number of rules: expected %u got %u\n", 1199c698bce0SIgor Mitsyanko resp->n_reg_rules, rule_idx); 1200c698bce0SIgor Mitsyanko return -EINVAL; 1201c698bce0SIgor Mitsyanko } 1202c698bce0SIgor Mitsyanko 12039cbd5999SVasily Ulyanov if (ext_capa_len > 0) { 12049cbd5999SVasily Ulyanov ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL); 12059cbd5999SVasily Ulyanov if (!ext_capa) 12069cbd5999SVasily Ulyanov return -ENOMEM; 12079cbd5999SVasily Ulyanov 12089cbd5999SVasily Ulyanov ext_capa_mask = 12099cbd5999SVasily Ulyanov kmemdup(ext_capa_mask, ext_capa_mask_len, GFP_KERNEL); 12109cbd5999SVasily Ulyanov if (!ext_capa_mask) { 12119cbd5999SVasily Ulyanov kfree(ext_capa); 12129cbd5999SVasily Ulyanov return -ENOMEM; 12139cbd5999SVasily Ulyanov } 12149cbd5999SVasily Ulyanov } else { 12159cbd5999SVasily Ulyanov ext_capa = NULL; 12169cbd5999SVasily Ulyanov ext_capa_mask = NULL; 12179cbd5999SVasily Ulyanov } 12189cbd5999SVasily Ulyanov 1219ab1c64a1SSergey Matyukevich qtnf_mac_ext_caps_free(mac); 12209cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities = ext_capa; 12219cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities_mask = ext_capa_mask; 12229cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities_len = ext_capa_len; 12239cbd5999SVasily Ulyanov 122498f44cb0SIgor Mitsyanko return 0; 122598f44cb0SIgor Mitsyanko } 122698f44cb0SIgor Mitsyanko 1227bc5db734SIgor Mitsyanko static int 122898f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, 122998f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp_info) 123098f44cb0SIgor Mitsyanko { 123198f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 123298f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 123398f44cb0SIgor Mitsyanko 1234bc5db734SIgor Mitsyanko qtnf_mac_iface_comb_free(mac); 1235bc5db734SIgor Mitsyanko 123698f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 123798f44cb0SIgor Mitsyanko 123898f44cb0SIgor Mitsyanko mac_info->bands_cap = resp_info->bands_cap; 12394e14e76cSIgor Mitsyanko ether_addr_copy(mac->macaddr, resp_info->dev_mac); 124098f44cb0SIgor Mitsyanko 124198f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 124298f44cb0SIgor Mitsyanko if (vif) 124398f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, mac->macaddr); 124498f44cb0SIgor Mitsyanko else 124598f44cb0SIgor Mitsyanko pr_err("could not get valid base vif\n"); 124698f44cb0SIgor Mitsyanko 124798f44cb0SIgor Mitsyanko mac_info->num_tx_chain = resp_info->num_tx_chain; 124898f44cb0SIgor Mitsyanko mac_info->num_rx_chain = resp_info->num_rx_chain; 124998f44cb0SIgor Mitsyanko 125098f44cb0SIgor Mitsyanko mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta); 125198f44cb0SIgor Mitsyanko mac_info->radar_detect_widths = 125298f44cb0SIgor Mitsyanko qlink_chan_width_mask_to_nl(le16_to_cpu( 125398f44cb0SIgor Mitsyanko resp_info->radar_detect_widths)); 1254e70cf22bSIgor Mitsyanko mac_info->max_acl_mac_addrs = le16_to_cpu(resp_info->max_acl_mac_addrs); 1255e70cf22bSIgor Mitsyanko mac_info->frag_thr = le32_to_cpu(resp_info->frag_threshold); 1256e70cf22bSIgor Mitsyanko mac_info->rts_thr = le32_to_cpu(resp_info->rts_threshold); 1257e70cf22bSIgor Mitsyanko mac_info->sretry_limit = resp_info->retry_short; 1258e70cf22bSIgor Mitsyanko mac_info->lretry_limit = resp_info->retry_long; 1259e70cf22bSIgor Mitsyanko mac_info->coverage_class = resp_info->coverage_class; 12600d18a9c0SIgor Mitsyanko mac_info->max_scan_ssids = resp_info->max_scan_ssids; 126198f44cb0SIgor Mitsyanko 1262d42df85fSIgor Mitsyanko memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask, 1263d42df85fSIgor Mitsyanko sizeof(mac_info->ht_cap_mod_mask)); 1264d42df85fSIgor Mitsyanko memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask, 1265d42df85fSIgor Mitsyanko sizeof(mac_info->vht_cap_mod_mask)); 1266bc5db734SIgor Mitsyanko 1267bc5db734SIgor Mitsyanko mac_info->n_if_comb = resp_info->n_iface_combinations; 1268bc5db734SIgor Mitsyanko mac_info->if_comb = kcalloc(mac->macinfo.n_if_comb, 1269bc5db734SIgor Mitsyanko sizeof(*mac->macinfo.if_comb), 1270bc5db734SIgor Mitsyanko GFP_KERNEL); 1271bc5db734SIgor Mitsyanko 1272bc5db734SIgor Mitsyanko if (!mac->macinfo.if_comb) 1273bc5db734SIgor Mitsyanko return -ENOMEM; 1274bc5db734SIgor Mitsyanko 1275bc5db734SIgor Mitsyanko return 0; 127698f44cb0SIgor Mitsyanko } 127798f44cb0SIgor Mitsyanko 1278e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_htcap(const u8 *info, 1279e294cbfdSIgor Mitsyanko struct ieee80211_sta_ht_cap *bcap) 1280e294cbfdSIgor Mitsyanko { 1281e294cbfdSIgor Mitsyanko const struct ieee80211_ht_cap *ht_cap = 1282e294cbfdSIgor Mitsyanko (const struct ieee80211_ht_cap *)info; 1283e294cbfdSIgor Mitsyanko 1284e294cbfdSIgor Mitsyanko bcap->ht_supported = true; 1285e294cbfdSIgor Mitsyanko bcap->cap = le16_to_cpu(ht_cap->cap_info); 1286e294cbfdSIgor Mitsyanko bcap->ampdu_factor = 1287e294cbfdSIgor Mitsyanko ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 1288e294cbfdSIgor Mitsyanko bcap->ampdu_density = 1289e294cbfdSIgor Mitsyanko (ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 1290e294cbfdSIgor Mitsyanko IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 1291e294cbfdSIgor Mitsyanko memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs)); 1292e294cbfdSIgor Mitsyanko } 1293e294cbfdSIgor Mitsyanko 1294e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info, 1295e294cbfdSIgor Mitsyanko struct ieee80211_sta_vht_cap *bcap) 1296e294cbfdSIgor Mitsyanko { 1297e294cbfdSIgor Mitsyanko const struct ieee80211_vht_cap *vht_cap = 1298e294cbfdSIgor Mitsyanko (const struct ieee80211_vht_cap *)info; 1299e294cbfdSIgor Mitsyanko 1300e294cbfdSIgor Mitsyanko bcap->vht_supported = true; 1301e294cbfdSIgor Mitsyanko bcap->cap = le32_to_cpu(vht_cap->vht_cap_info); 1302e294cbfdSIgor Mitsyanko memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs)); 1303e294cbfdSIgor Mitsyanko } 1304e294cbfdSIgor Mitsyanko 1305df0af4c7SMikhail Karpenko static void qtnf_cmd_conv_iftype(struct ieee80211_sband_iftype_data 1306df0af4c7SMikhail Karpenko *iftype_data, 1307df0af4c7SMikhail Karpenko const struct qlink_sband_iftype_data 1308df0af4c7SMikhail Karpenko *qlink_data) 1309df0af4c7SMikhail Karpenko { 1310df0af4c7SMikhail Karpenko iftype_data->types_mask = le16_to_cpu(qlink_data->types_mask); 1311df0af4c7SMikhail Karpenko 1312df0af4c7SMikhail Karpenko iftype_data->he_cap.has_he = true; 1313df0af4c7SMikhail Karpenko memcpy(&iftype_data->he_cap.he_cap_elem, &qlink_data->he_cap_elem, 1314df0af4c7SMikhail Karpenko sizeof(qlink_data->he_cap_elem)); 1315df0af4c7SMikhail Karpenko memcpy(iftype_data->he_cap.ppe_thres, qlink_data->ppe_thres, 1316df0af4c7SMikhail Karpenko ARRAY_SIZE(qlink_data->ppe_thres)); 1317df0af4c7SMikhail Karpenko 1318df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80 = 1319df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.rx_mcs_80; 1320df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80 = 1321df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.tx_mcs_80; 1322df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160 = 1323df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.rx_mcs_160; 1324df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160 = 1325df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.tx_mcs_160; 1326df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80p80 = 1327df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.rx_mcs_80p80; 1328df0af4c7SMikhail Karpenko iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80p80 = 1329df0af4c7SMikhail Karpenko qlink_data->he_mcs_nss_supp.tx_mcs_80p80; 1330df0af4c7SMikhail Karpenko } 1331df0af4c7SMikhail Karpenko 1332df0af4c7SMikhail Karpenko static int qtnf_cmd_band_fill_iftype(const u8 *data, 1333df0af4c7SMikhail Karpenko struct ieee80211_supported_band *band) 1334df0af4c7SMikhail Karpenko { 1335df0af4c7SMikhail Karpenko unsigned int i; 1336df0af4c7SMikhail Karpenko struct ieee80211_sband_iftype_data *iftype_data; 1337df0af4c7SMikhail Karpenko const struct qlink_tlv_iftype_data *tlv = 1338df0af4c7SMikhail Karpenko (const struct qlink_tlv_iftype_data *)data; 1339df0af4c7SMikhail Karpenko size_t payload_len = tlv->n_iftype_data * sizeof(*tlv->iftype_data) + 1340df0af4c7SMikhail Karpenko sizeof(*tlv) - 1341df0af4c7SMikhail Karpenko sizeof(struct qlink_tlv_hdr); 1342df0af4c7SMikhail Karpenko 1343df0af4c7SMikhail Karpenko if (tlv->hdr.len != cpu_to_le16(payload_len)) { 1344df0af4c7SMikhail Karpenko pr_err("bad IFTYPE_DATA TLV len %u\n", tlv->hdr.len); 1345df0af4c7SMikhail Karpenko return -EINVAL; 1346df0af4c7SMikhail Karpenko } 1347df0af4c7SMikhail Karpenko 1348df0af4c7SMikhail Karpenko kfree(band->iftype_data); 1349df0af4c7SMikhail Karpenko band->iftype_data = NULL; 1350df0af4c7SMikhail Karpenko band->n_iftype_data = tlv->n_iftype_data; 1351df0af4c7SMikhail Karpenko if (band->n_iftype_data == 0) 1352df0af4c7SMikhail Karpenko return 0; 1353df0af4c7SMikhail Karpenko 1354df0af4c7SMikhail Karpenko iftype_data = kcalloc(band->n_iftype_data, sizeof(*iftype_data), 1355df0af4c7SMikhail Karpenko GFP_KERNEL); 1356df0af4c7SMikhail Karpenko if (!iftype_data) { 1357df0af4c7SMikhail Karpenko band->n_iftype_data = 0; 1358df0af4c7SMikhail Karpenko return -ENOMEM; 1359df0af4c7SMikhail Karpenko } 1360df0af4c7SMikhail Karpenko band->iftype_data = iftype_data; 1361df0af4c7SMikhail Karpenko 1362df0af4c7SMikhail Karpenko for (i = 0; i < band->n_iftype_data; i++) 1363df0af4c7SMikhail Karpenko qtnf_cmd_conv_iftype(iftype_data++, &tlv->iftype_data[i]); 1364df0af4c7SMikhail Karpenko 1365df0af4c7SMikhail Karpenko return 0; 1366df0af4c7SMikhail Karpenko } 1367df0af4c7SMikhail Karpenko 136898f44cb0SIgor Mitsyanko static int 1369e294cbfdSIgor Mitsyanko qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, 1370e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp, 137198f44cb0SIgor Mitsyanko size_t payload_len) 137298f44cb0SIgor Mitsyanko { 137398f44cb0SIgor Mitsyanko u16 tlv_type; 1374e294cbfdSIgor Mitsyanko size_t tlv_dlen; 137598f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 13765bf374abSSergey Matyukevich const struct qlink_channel *qchan; 137798f44cb0SIgor Mitsyanko struct ieee80211_channel *chan; 137898f44cb0SIgor Mitsyanko unsigned int chidx = 0; 137998f44cb0SIgor Mitsyanko u32 qflags; 1380df0af4c7SMikhail Karpenko int ret = -EINVAL; 138198f44cb0SIgor Mitsyanko 1382e294cbfdSIgor Mitsyanko memset(&band->ht_cap, 0, sizeof(band->ht_cap)); 1383e294cbfdSIgor Mitsyanko memset(&band->vht_cap, 0, sizeof(band->vht_cap)); 1384e294cbfdSIgor Mitsyanko 13854dd07d2bSSergey Matyukevich if (band->channels) { 13864dd07d2bSSergey Matyukevich if (band->n_channels == resp->num_chans) { 13874dd07d2bSSergey Matyukevich memset(band->channels, 0, 13884dd07d2bSSergey Matyukevich sizeof(*band->channels) * band->n_channels); 13894dd07d2bSSergey Matyukevich } else { 139098f44cb0SIgor Mitsyanko kfree(band->channels); 13914dd07d2bSSergey Matyukevich band->n_channels = 0; 139298f44cb0SIgor Mitsyanko band->channels = NULL; 13934dd07d2bSSergey Matyukevich } 13944dd07d2bSSergey Matyukevich } 139598f44cb0SIgor Mitsyanko 139698f44cb0SIgor Mitsyanko band->n_channels = resp->num_chans; 139798f44cb0SIgor Mitsyanko if (band->n_channels == 0) 139898f44cb0SIgor Mitsyanko return 0; 139998f44cb0SIgor Mitsyanko 14004dd07d2bSSergey Matyukevich if (!band->channels) 14014dd07d2bSSergey Matyukevich band->channels = kcalloc(band->n_channels, sizeof(*chan), 14024dd07d2bSSergey Matyukevich GFP_KERNEL); 140398f44cb0SIgor Mitsyanko if (!band->channels) { 140498f44cb0SIgor Mitsyanko band->n_channels = 0; 140598f44cb0SIgor Mitsyanko return -ENOMEM; 140698f44cb0SIgor Mitsyanko } 140798f44cb0SIgor Mitsyanko 14088b0b5f1bSIgor Mitsyanko qlink_for_each_tlv(tlv, resp->info, payload_len) { 140998f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 1410e294cbfdSIgor Mitsyanko tlv_dlen = le16_to_cpu(tlv->len); 141198f44cb0SIgor Mitsyanko 141298f44cb0SIgor Mitsyanko switch (tlv_type) { 141398f44cb0SIgor Mitsyanko case QTN_TLV_ID_CHANNEL: 14145bf374abSSergey Matyukevich if (unlikely(tlv_dlen != sizeof(*qchan))) { 141598f44cb0SIgor Mitsyanko pr_err("invalid channel TLV len %zu\n", 14168b0b5f1bSIgor Mitsyanko tlv_dlen); 141798f44cb0SIgor Mitsyanko goto error_ret; 141898f44cb0SIgor Mitsyanko } 141998f44cb0SIgor Mitsyanko 142098f44cb0SIgor Mitsyanko if (chidx == band->n_channels) { 142198f44cb0SIgor Mitsyanko pr_err("too many channel TLVs\n"); 142298f44cb0SIgor Mitsyanko goto error_ret; 142398f44cb0SIgor Mitsyanko } 142498f44cb0SIgor Mitsyanko 14255bf374abSSergey Matyukevich qchan = (const struct qlink_channel *)tlv->val; 142698f44cb0SIgor Mitsyanko chan = &band->channels[chidx++]; 142798f44cb0SIgor Mitsyanko qflags = le32_to_cpu(qchan->flags); 142898f44cb0SIgor Mitsyanko 142998f44cb0SIgor Mitsyanko chan->hw_value = le16_to_cpu(qchan->hw_value); 143098f44cb0SIgor Mitsyanko chan->band = band->band; 143198f44cb0SIgor Mitsyanko chan->center_freq = le16_to_cpu(qchan->center_freq); 143298f44cb0SIgor Mitsyanko chan->max_antenna_gain = (int)qchan->max_antenna_gain; 143398f44cb0SIgor Mitsyanko chan->max_power = (int)qchan->max_power; 143498f44cb0SIgor Mitsyanko chan->max_reg_power = (int)qchan->max_reg_power; 143598f44cb0SIgor Mitsyanko chan->beacon_found = qchan->beacon_found; 143698f44cb0SIgor Mitsyanko chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms); 143798f44cb0SIgor Mitsyanko chan->flags = 0; 143898f44cb0SIgor Mitsyanko 143998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_DISABLED) 144098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_DISABLED; 144198f44cb0SIgor Mitsyanko 144298f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_IR) 144398f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_IR; 144498f44cb0SIgor Mitsyanko 144598f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40PLUS) 144698f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40PLUS; 144798f44cb0SIgor Mitsyanko 144898f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40MINUS) 144998f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40MINUS; 145098f44cb0SIgor Mitsyanko 145198f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_OFDM) 145298f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_OFDM; 145398f44cb0SIgor Mitsyanko 145498f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_80MHZ) 145598f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_80MHZ; 145698f44cb0SIgor Mitsyanko 145798f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_160MHZ) 145898f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_160MHZ; 145998f44cb0SIgor Mitsyanko 146098f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_INDOOR_ONLY) 146198f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_INDOOR_ONLY; 146298f44cb0SIgor Mitsyanko 146398f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_IR_CONCURRENT) 146498f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_IR_CONCURRENT; 146598f44cb0SIgor Mitsyanko 146698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_20MHZ) 146798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_20MHZ; 146898f44cb0SIgor Mitsyanko 146998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_10MHZ) 147098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_10MHZ; 147198f44cb0SIgor Mitsyanko 147298f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_RADAR) { 147398f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_RADAR; 147498f44cb0SIgor Mitsyanko chan->dfs_state_entered = jiffies; 147598f44cb0SIgor Mitsyanko 147698f44cb0SIgor Mitsyanko if (qchan->dfs_state == QLINK_DFS_USABLE) 147798f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_USABLE; 147898f44cb0SIgor Mitsyanko else if (qchan->dfs_state == 147998f44cb0SIgor Mitsyanko QLINK_DFS_AVAILABLE) 148098f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_AVAILABLE; 148198f44cb0SIgor Mitsyanko else 148298f44cb0SIgor Mitsyanko chan->dfs_state = 148398f44cb0SIgor Mitsyanko NL80211_DFS_UNAVAILABLE; 148498f44cb0SIgor Mitsyanko } 148598f44cb0SIgor Mitsyanko 148698f44cb0SIgor Mitsyanko pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n", 148798f44cb0SIgor Mitsyanko chan->hw_value, chan->flags, chan->max_power, 148898f44cb0SIgor Mitsyanko chan->max_reg_power); 148998f44cb0SIgor Mitsyanko break; 1490e294cbfdSIgor Mitsyanko case WLAN_EID_HT_CAPABILITY: 1491e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1492e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_ht_cap))) { 1493e294cbfdSIgor Mitsyanko pr_err("bad HTCAP TLV len %zu\n", tlv_dlen); 1494e294cbfdSIgor Mitsyanko goto error_ret; 1495e294cbfdSIgor Mitsyanko } 1496e294cbfdSIgor Mitsyanko 1497e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap); 1498e294cbfdSIgor Mitsyanko break; 1499e294cbfdSIgor Mitsyanko case WLAN_EID_VHT_CAPABILITY: 1500e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1501e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_vht_cap))) { 1502e294cbfdSIgor Mitsyanko pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen); 1503e294cbfdSIgor Mitsyanko goto error_ret; 1504e294cbfdSIgor Mitsyanko } 1505e294cbfdSIgor Mitsyanko 1506e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_vhtcap(tlv->val, 1507e294cbfdSIgor Mitsyanko &band->vht_cap); 1508e294cbfdSIgor Mitsyanko break; 1509df0af4c7SMikhail Karpenko case QTN_TLV_ID_IFTYPE_DATA: 1510df0af4c7SMikhail Karpenko ret = qtnf_cmd_band_fill_iftype((const uint8_t *)tlv, 1511df0af4c7SMikhail Karpenko band); 1512df0af4c7SMikhail Karpenko if (ret) 1513df0af4c7SMikhail Karpenko goto error_ret; 1514df0af4c7SMikhail Karpenko break; 151598f44cb0SIgor Mitsyanko default: 151698f44cb0SIgor Mitsyanko pr_warn("unknown TLV type: %#x\n", tlv_type); 151798f44cb0SIgor Mitsyanko break; 151898f44cb0SIgor Mitsyanko } 151998f44cb0SIgor Mitsyanko } 152098f44cb0SIgor Mitsyanko 15218b0b5f1bSIgor Mitsyanko if (!qlink_tlv_parsing_ok(tlv, resp->info, payload_len)) { 15228b0b5f1bSIgor Mitsyanko pr_err("Malformed TLV buffer\n"); 152398f44cb0SIgor Mitsyanko goto error_ret; 152498f44cb0SIgor Mitsyanko } 152598f44cb0SIgor Mitsyanko 152698f44cb0SIgor Mitsyanko if (band->n_channels != chidx) { 152798f44cb0SIgor Mitsyanko pr_err("channel count mismatch: reported=%d, parsed=%d\n", 152898f44cb0SIgor Mitsyanko band->n_channels, chidx); 152998f44cb0SIgor Mitsyanko goto error_ret; 153098f44cb0SIgor Mitsyanko } 153198f44cb0SIgor Mitsyanko 153298f44cb0SIgor Mitsyanko return 0; 153398f44cb0SIgor Mitsyanko 153498f44cb0SIgor Mitsyanko error_ret: 153598f44cb0SIgor Mitsyanko kfree(band->channels); 153698f44cb0SIgor Mitsyanko band->channels = NULL; 153798f44cb0SIgor Mitsyanko band->n_channels = 0; 153898f44cb0SIgor Mitsyanko 1539df0af4c7SMikhail Karpenko return ret; 154098f44cb0SIgor Mitsyanko } 154198f44cb0SIgor Mitsyanko 154298f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) 154398f44cb0SIgor Mitsyanko { 154498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 154598f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp; 15461066bd19SSergey Matyukevich size_t var_data_len = 0; 154798f44cb0SIgor Mitsyanko int ret = 0; 154898f44cb0SIgor Mitsyanko 154998f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 155098f44cb0SIgor Mitsyanko QLINK_CMD_MAC_INFO, 155198f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 1552c93fe71cSSergey Matyukevich if (!cmd_skb) 155398f44cb0SIgor Mitsyanko return -ENOMEM; 155498f44cb0SIgor Mitsyanko 155598f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 1556c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 155798f44cb0SIgor Mitsyanko sizeof(*resp), &var_data_len); 1558c6ed298fSSergey Matyukevich if (ret) 155998f44cb0SIgor Mitsyanko goto out; 156098f44cb0SIgor Mitsyanko 156198f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_mac_info *)resp_skb->data; 1562bc5db734SIgor Mitsyanko ret = qtnf_cmd_resp_proc_mac_info(mac, resp); 1563bc5db734SIgor Mitsyanko if (ret) 1564bc5db734SIgor Mitsyanko goto out; 1565bc5db734SIgor Mitsyanko 1566c698bce0SIgor Mitsyanko ret = qtnf_parse_variable_mac_info(mac, resp, var_data_len); 156798f44cb0SIgor Mitsyanko 156898f44cb0SIgor Mitsyanko out: 156998f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 157098f44cb0SIgor Mitsyanko consume_skb(resp_skb); 157198f44cb0SIgor Mitsyanko 157298f44cb0SIgor Mitsyanko return ret; 157398f44cb0SIgor Mitsyanko } 157498f44cb0SIgor Mitsyanko 157598f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) 157698f44cb0SIgor Mitsyanko { 157798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 157898f44cb0SIgor Mitsyanko const struct qlink_resp_get_hw_info *resp; 15791066bd19SSergey Matyukevich size_t info_len = 0; 158098f44cb0SIgor Mitsyanko int ret = 0; 158198f44cb0SIgor Mitsyanko 158298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 158398f44cb0SIgor Mitsyanko QLINK_CMD_GET_HW_INFO, 158498f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 1585c93fe71cSSergey Matyukevich if (!cmd_skb) 158698f44cb0SIgor Mitsyanko return -ENOMEM; 158798f44cb0SIgor Mitsyanko 158898f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1589c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 15904dd07d2bSSergey Matyukevich sizeof(*resp), &info_len); 1591c6ed298fSSergey Matyukevich if (ret) 159298f44cb0SIgor Mitsyanko goto out; 159398f44cb0SIgor Mitsyanko 159498f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; 15954dd07d2bSSergey Matyukevich ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); 159698f44cb0SIgor Mitsyanko 159798f44cb0SIgor Mitsyanko out: 159898f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 159998f44cb0SIgor Mitsyanko consume_skb(resp_skb); 160098f44cb0SIgor Mitsyanko 160198f44cb0SIgor Mitsyanko return ret; 160298f44cb0SIgor Mitsyanko } 160398f44cb0SIgor Mitsyanko 1604e294cbfdSIgor Mitsyanko int qtnf_cmd_band_info_get(struct qtnf_wmac *mac, 160598f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 160698f44cb0SIgor Mitsyanko { 160798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 1608e294cbfdSIgor Mitsyanko struct qlink_cmd_band_info_get *cmd; 1609e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp; 16101066bd19SSergey Matyukevich size_t info_len = 0; 161198f44cb0SIgor Mitsyanko int ret = 0; 16122c31129fSIgor Mitsyanko u8 qband = qlink_utils_band_cfg2q(band->band); 161398f44cb0SIgor Mitsyanko 1614bc0384eeSColin Ian King cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 1615e294cbfdSIgor Mitsyanko QLINK_CMD_BAND_INFO_GET, 1616bc0384eeSColin Ian King sizeof(*cmd)); 1617bc0384eeSColin Ian King if (!cmd_skb) 1618bc0384eeSColin Ian King return -ENOMEM; 1619bc0384eeSColin Ian King 1620e294cbfdSIgor Mitsyanko cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data; 162198f44cb0SIgor Mitsyanko cmd->band = qband; 16229ef75095SSergey Matyukevich 16239ef75095SSergey Matyukevich qtnf_bus_lock(mac->bus); 1624c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 162598f44cb0SIgor Mitsyanko sizeof(*resp), &info_len); 1626c6ed298fSSergey Matyukevich if (ret) 162798f44cb0SIgor Mitsyanko goto out; 162898f44cb0SIgor Mitsyanko 1629e294cbfdSIgor Mitsyanko resp = (struct qlink_resp_band_info_get *)resp_skb->data; 163098f44cb0SIgor Mitsyanko if (resp->band != qband) { 163198f44cb0SIgor Mitsyanko pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid, 163298f44cb0SIgor Mitsyanko resp->band, qband); 163398f44cb0SIgor Mitsyanko ret = -EINVAL; 163498f44cb0SIgor Mitsyanko goto out; 163598f44cb0SIgor Mitsyanko } 163698f44cb0SIgor Mitsyanko 1637e294cbfdSIgor Mitsyanko ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len); 163898f44cb0SIgor Mitsyanko 163998f44cb0SIgor Mitsyanko out: 16409ef75095SSergey Matyukevich qtnf_bus_unlock(mac->bus); 164198f44cb0SIgor Mitsyanko consume_skb(resp_skb); 164298f44cb0SIgor Mitsyanko 164398f44cb0SIgor Mitsyanko return ret; 164498f44cb0SIgor Mitsyanko } 164598f44cb0SIgor Mitsyanko 164698f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) 164798f44cb0SIgor Mitsyanko { 164898f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 164998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 165098f44cb0SIgor Mitsyanko int ret = 0; 165198f44cb0SIgor Mitsyanko 165298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 165398f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_SET, 165498f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 165598f44cb0SIgor Mitsyanko if (!cmd_skb) 165698f44cb0SIgor Mitsyanko return -ENOMEM; 165798f44cb0SIgor Mitsyanko 165898f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 165998f44cb0SIgor Mitsyanko 166098f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_FRAG_THRESHOLD) 16619fe504a1SSergey Matyukevich qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_FRAG_THRESH, 166298f44cb0SIgor Mitsyanko wiphy->frag_threshold); 166398f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_RTS_THRESHOLD) 16649fe504a1SSergey Matyukevich qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH, 166598f44cb0SIgor Mitsyanko wiphy->rts_threshold); 166698f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_COVERAGE_CLASS) 16678b0b5f1bSIgor Mitsyanko qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, 166898f44cb0SIgor Mitsyanko wiphy->coverage_class); 166998f44cb0SIgor Mitsyanko 1670f3c8bd46SSergey Matyukevich if (changed & WIPHY_PARAM_RETRY_LONG) 16718b0b5f1bSIgor Mitsyanko qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT, 1672f3c8bd46SSergey Matyukevich wiphy->retry_long); 1673f3c8bd46SSergey Matyukevich 1674f3c8bd46SSergey Matyukevich if (changed & WIPHY_PARAM_RETRY_SHORT) 16758b0b5f1bSIgor Mitsyanko qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT, 1676f3c8bd46SSergey Matyukevich wiphy->retry_short); 1677f3c8bd46SSergey Matyukevich 1678c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 1679c6ed298fSSergey Matyukevich if (ret) 168098f44cb0SIgor Mitsyanko goto out; 168198f44cb0SIgor Mitsyanko 168298f44cb0SIgor Mitsyanko out: 168398f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 1684c6ed298fSSergey Matyukevich 168598f44cb0SIgor Mitsyanko return ret; 168698f44cb0SIgor Mitsyanko } 168798f44cb0SIgor Mitsyanko 168898f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus) 168998f44cb0SIgor Mitsyanko { 1690a3ebb033SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 1691a3ebb033SIgor Mitsyanko struct qlink_resp_init_fw *resp; 1692a3ebb033SIgor Mitsyanko struct qlink_cmd_init_fw *cmd; 169398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 1694a3ebb033SIgor Mitsyanko size_t info_len = 0; 1695a3ebb033SIgor Mitsyanko int ret; 169698f44cb0SIgor Mitsyanko 169798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 169898f44cb0SIgor Mitsyanko QLINK_CMD_FW_INIT, 1699a3ebb033SIgor Mitsyanko sizeof(*cmd)); 1700c93fe71cSSergey Matyukevich if (!cmd_skb) 170198f44cb0SIgor Mitsyanko return -ENOMEM; 170298f44cb0SIgor Mitsyanko 1703a3ebb033SIgor Mitsyanko cmd = (struct qlink_cmd_init_fw *)cmd_skb->data; 1704a3ebb033SIgor Mitsyanko cmd->qlink_proto_ver = cpu_to_le32(QLINK_PROTO_VER); 1705a3ebb033SIgor Mitsyanko 170698f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1707a3ebb033SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 1708a3ebb033SIgor Mitsyanko sizeof(*resp), &info_len); 1709a3ebb033SIgor Mitsyanko qtnf_bus_unlock(bus); 1710a3ebb033SIgor Mitsyanko 1711c6ed298fSSergey Matyukevich if (ret) 171298f44cb0SIgor Mitsyanko goto out; 171398f44cb0SIgor Mitsyanko 1714a3ebb033SIgor Mitsyanko resp = (struct qlink_resp_init_fw *)resp_skb->data; 1715a3ebb033SIgor Mitsyanko bus->hw_info.ql_proto_ver = le32_to_cpu(resp->qlink_proto_ver); 1716c6ed298fSSergey Matyukevich 1717a3ebb033SIgor Mitsyanko out: 1718a3ebb033SIgor Mitsyanko consume_skb(resp_skb); 171998f44cb0SIgor Mitsyanko return ret; 172098f44cb0SIgor Mitsyanko } 172198f44cb0SIgor Mitsyanko 172298f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus) 172398f44cb0SIgor Mitsyanko { 172498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 172598f44cb0SIgor Mitsyanko 172698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 172798f44cb0SIgor Mitsyanko QLINK_CMD_FW_DEINIT, 172898f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 172998f44cb0SIgor Mitsyanko if (!cmd_skb) 173098f44cb0SIgor Mitsyanko return; 173198f44cb0SIgor Mitsyanko 173298f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1733c6ed298fSSergey Matyukevich qtnf_cmd_send(bus, cmd_skb); 173498f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 173598f44cb0SIgor Mitsyanko } 173698f44cb0SIgor Mitsyanko 173798f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 173898f44cb0SIgor Mitsyanko const u8 *mac_addr, struct key_params *params) 173998f44cb0SIgor Mitsyanko { 174098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 174198f44cb0SIgor Mitsyanko struct qlink_cmd_add_key *cmd; 174298f44cb0SIgor Mitsyanko int ret = 0; 174398f44cb0SIgor Mitsyanko 174498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 174598f44cb0SIgor Mitsyanko QLINK_CMD_ADD_KEY, 174698f44cb0SIgor Mitsyanko sizeof(*cmd)); 1747c93fe71cSSergey Matyukevich if (!cmd_skb) 174898f44cb0SIgor Mitsyanko return -ENOMEM; 174998f44cb0SIgor Mitsyanko 175098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 175198f44cb0SIgor Mitsyanko 175298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_add_key *)cmd_skb->data; 175398f44cb0SIgor Mitsyanko 175498f44cb0SIgor Mitsyanko if (mac_addr) 175598f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 175698f44cb0SIgor Mitsyanko else 175798f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 175898f44cb0SIgor Mitsyanko 175998f44cb0SIgor Mitsyanko cmd->cipher = cpu_to_le32(params->cipher); 176098f44cb0SIgor Mitsyanko cmd->key_index = key_index; 176198f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 176298f44cb0SIgor Mitsyanko 176398f44cb0SIgor Mitsyanko if (params->key && params->key_len > 0) 176498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY, 176598f44cb0SIgor Mitsyanko params->key, 176698f44cb0SIgor Mitsyanko params->key_len); 176798f44cb0SIgor Mitsyanko 176898f44cb0SIgor Mitsyanko if (params->seq && params->seq_len > 0) 176998f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ, 177098f44cb0SIgor Mitsyanko params->seq, 177198f44cb0SIgor Mitsyanko params->seq_len); 177298f44cb0SIgor Mitsyanko 1773c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1774c6ed298fSSergey Matyukevich if (ret) 177598f44cb0SIgor Mitsyanko goto out; 177698f44cb0SIgor Mitsyanko 177798f44cb0SIgor Mitsyanko out: 177898f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1779c6ed298fSSergey Matyukevich 178098f44cb0SIgor Mitsyanko return ret; 178198f44cb0SIgor Mitsyanko } 178298f44cb0SIgor Mitsyanko 178398f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 178498f44cb0SIgor Mitsyanko const u8 *mac_addr) 178598f44cb0SIgor Mitsyanko { 178698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 178798f44cb0SIgor Mitsyanko struct qlink_cmd_del_key *cmd; 178898f44cb0SIgor Mitsyanko int ret = 0; 178998f44cb0SIgor Mitsyanko 179098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 179198f44cb0SIgor Mitsyanko QLINK_CMD_DEL_KEY, 179298f44cb0SIgor Mitsyanko sizeof(*cmd)); 1793c93fe71cSSergey Matyukevich if (!cmd_skb) 179498f44cb0SIgor Mitsyanko return -ENOMEM; 179598f44cb0SIgor Mitsyanko 179698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 179798f44cb0SIgor Mitsyanko 179898f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_key *)cmd_skb->data; 179998f44cb0SIgor Mitsyanko 180098f44cb0SIgor Mitsyanko if (mac_addr) 180198f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 180298f44cb0SIgor Mitsyanko else 180398f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 180498f44cb0SIgor Mitsyanko 180598f44cb0SIgor Mitsyanko cmd->key_index = key_index; 180698f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 180798f44cb0SIgor Mitsyanko 1808c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1809c6ed298fSSergey Matyukevich if (ret) 181098f44cb0SIgor Mitsyanko goto out; 181198f44cb0SIgor Mitsyanko 181298f44cb0SIgor Mitsyanko out: 181398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1814c6ed298fSSergey Matyukevich 181598f44cb0SIgor Mitsyanko return ret; 181698f44cb0SIgor Mitsyanko } 181798f44cb0SIgor Mitsyanko 181898f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index, 181998f44cb0SIgor Mitsyanko bool unicast, bool multicast) 182098f44cb0SIgor Mitsyanko { 182198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 182298f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_key *cmd; 182398f44cb0SIgor Mitsyanko int ret = 0; 182498f44cb0SIgor Mitsyanko 182598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 182698f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_KEY, 182798f44cb0SIgor Mitsyanko sizeof(*cmd)); 1828c93fe71cSSergey Matyukevich if (!cmd_skb) 182998f44cb0SIgor Mitsyanko return -ENOMEM; 183098f44cb0SIgor Mitsyanko 183198f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 183298f44cb0SIgor Mitsyanko 183398f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data; 183498f44cb0SIgor Mitsyanko cmd->key_index = key_index; 183598f44cb0SIgor Mitsyanko cmd->unicast = unicast; 183698f44cb0SIgor Mitsyanko cmd->multicast = multicast; 183798f44cb0SIgor Mitsyanko 1838c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1839c6ed298fSSergey Matyukevich if (ret) 184098f44cb0SIgor Mitsyanko goto out; 184198f44cb0SIgor Mitsyanko 184298f44cb0SIgor Mitsyanko out: 184398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1844c6ed298fSSergey Matyukevich 184598f44cb0SIgor Mitsyanko return ret; 184698f44cb0SIgor Mitsyanko } 184798f44cb0SIgor Mitsyanko 184898f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index) 184998f44cb0SIgor Mitsyanko { 185098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 185198f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_mgmt_key *cmd; 185298f44cb0SIgor Mitsyanko int ret = 0; 185398f44cb0SIgor Mitsyanko 185498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 185598f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_MGMT_KEY, 185698f44cb0SIgor Mitsyanko sizeof(*cmd)); 1857c93fe71cSSergey Matyukevich if (!cmd_skb) 185898f44cb0SIgor Mitsyanko return -ENOMEM; 185998f44cb0SIgor Mitsyanko 186098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 186198f44cb0SIgor Mitsyanko 186298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data; 186398f44cb0SIgor Mitsyanko cmd->key_index = key_index; 186498f44cb0SIgor Mitsyanko 1865c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1866c6ed298fSSergey Matyukevich if (ret) 186798f44cb0SIgor Mitsyanko goto out; 186898f44cb0SIgor Mitsyanko 186998f44cb0SIgor Mitsyanko out: 187098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1871c6ed298fSSergey Matyukevich 187298f44cb0SIgor Mitsyanko return ret; 187398f44cb0SIgor Mitsyanko } 187498f44cb0SIgor Mitsyanko 187598f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags) 187698f44cb0SIgor Mitsyanko { 187798f44cb0SIgor Mitsyanko u32 code = 0; 187898f44cb0SIgor Mitsyanko 187998f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED)) 188098f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHORIZED; 188198f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) 188298f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_SHORT_PREAMBLE; 188398f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_WME)) 188498f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_WME; 188598f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_MFP)) 188698f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_MFP; 188798f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED)) 188898f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHENTICATED; 188998f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER)) 189098f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_TDLS_PEER; 189198f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED)) 189298f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_ASSOCIATED; 189398f44cb0SIgor Mitsyanko return code; 189498f44cb0SIgor Mitsyanko } 189598f44cb0SIgor Mitsyanko 189698f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, 189798f44cb0SIgor Mitsyanko struct station_parameters *params) 189898f44cb0SIgor Mitsyanko { 189998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 190098f44cb0SIgor Mitsyanko struct qlink_cmd_change_sta *cmd; 190198f44cb0SIgor Mitsyanko int ret = 0; 190298f44cb0SIgor Mitsyanko 190398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 190498f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_STA, 190598f44cb0SIgor Mitsyanko sizeof(*cmd)); 1906c93fe71cSSergey Matyukevich if (!cmd_skb) 190798f44cb0SIgor Mitsyanko return -ENOMEM; 190898f44cb0SIgor Mitsyanko 190998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 191098f44cb0SIgor Mitsyanko 191198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; 191298f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, mac); 19134d2a7a1cSIgor Mitsyanko cmd->flag_update.mask = 19144d2a7a1cSIgor Mitsyanko cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_mask)); 19154d2a7a1cSIgor Mitsyanko cmd->flag_update.value = 19164d2a7a1cSIgor Mitsyanko cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_set)); 1917805b28c0SSergey Matyukevich 1918805b28c0SSergey Matyukevich switch (vif->wdev.iftype) { 1919805b28c0SSergey Matyukevich case NL80211_IFTYPE_AP: 1920805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); 1921805b28c0SSergey Matyukevich break; 1922805b28c0SSergey Matyukevich case NL80211_IFTYPE_STATION: 1923805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 1924805b28c0SSergey Matyukevich break; 1925805b28c0SSergey Matyukevich default: 1926805b28c0SSergey Matyukevich pr_err("unsupported iftype %d\n", vif->wdev.iftype); 1927805b28c0SSergey Matyukevich ret = -EINVAL; 1928805b28c0SSergey Matyukevich goto out; 1929805b28c0SSergey Matyukevich } 193098f44cb0SIgor Mitsyanko 1931c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1932c6ed298fSSergey Matyukevich if (ret) 193398f44cb0SIgor Mitsyanko goto out; 193498f44cb0SIgor Mitsyanko 193598f44cb0SIgor Mitsyanko out: 193698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1937c6ed298fSSergey Matyukevich 193898f44cb0SIgor Mitsyanko return ret; 193998f44cb0SIgor Mitsyanko } 194098f44cb0SIgor Mitsyanko 194198f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif, 194298f44cb0SIgor Mitsyanko struct station_del_parameters *params) 194398f44cb0SIgor Mitsyanko { 194498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 194598f44cb0SIgor Mitsyanko struct qlink_cmd_del_sta *cmd; 194698f44cb0SIgor Mitsyanko int ret = 0; 194798f44cb0SIgor Mitsyanko 194898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 194998f44cb0SIgor Mitsyanko QLINK_CMD_DEL_STA, 195098f44cb0SIgor Mitsyanko sizeof(*cmd)); 1951c93fe71cSSergey Matyukevich if (!cmd_skb) 195298f44cb0SIgor Mitsyanko return -ENOMEM; 195398f44cb0SIgor Mitsyanko 195498f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 195598f44cb0SIgor Mitsyanko 195698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_sta *)cmd_skb->data; 195798f44cb0SIgor Mitsyanko 195898f44cb0SIgor Mitsyanko if (params->mac) 195998f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, params->mac); 196098f44cb0SIgor Mitsyanko else 196198f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->sta_addr); /* flush all stations */ 196298f44cb0SIgor Mitsyanko 196398f44cb0SIgor Mitsyanko cmd->subtype = params->subtype; 196498f44cb0SIgor Mitsyanko cmd->reason_code = cpu_to_le16(params->reason_code); 196598f44cb0SIgor Mitsyanko 1966c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1967c6ed298fSSergey Matyukevich if (ret) 196898f44cb0SIgor Mitsyanko goto out; 196998f44cb0SIgor Mitsyanko 197098f44cb0SIgor Mitsyanko out: 197198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1972c6ed298fSSergey Matyukevich 197398f44cb0SIgor Mitsyanko return ret; 197498f44cb0SIgor Mitsyanko } 197598f44cb0SIgor Mitsyanko 1976c9889671SIgor Mitsyanko static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb, 1977c9889671SIgor Mitsyanko const struct ieee80211_channel *sc) 1978c9889671SIgor Mitsyanko { 19792c31129fSIgor Mitsyanko struct qlink_tlv_channel *tlv; 19802c31129fSIgor Mitsyanko struct qlink_channel *qch; 1981c9889671SIgor Mitsyanko 19822c31129fSIgor Mitsyanko tlv = skb_put_zero(cmd_skb, sizeof(*tlv)); 19832c31129fSIgor Mitsyanko qch = &tlv->chan; 19842c31129fSIgor Mitsyanko tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); 19852c31129fSIgor Mitsyanko tlv->hdr.len = cpu_to_le16(sizeof(*qch)); 1986c9889671SIgor Mitsyanko 19872c31129fSIgor Mitsyanko qch->center_freq = cpu_to_le16(sc->center_freq); 19882c31129fSIgor Mitsyanko qch->hw_value = cpu_to_le16(sc->hw_value); 19892c31129fSIgor Mitsyanko qch->band = qlink_utils_band_cfg2q(sc->band); 19902c31129fSIgor Mitsyanko qch->max_power = sc->max_power; 19912c31129fSIgor Mitsyanko qch->max_reg_power = sc->max_reg_power; 19922c31129fSIgor Mitsyanko qch->max_antenna_gain = sc->max_antenna_gain; 19932c31129fSIgor Mitsyanko qch->beacon_found = sc->beacon_found; 19942c31129fSIgor Mitsyanko qch->dfs_state = qlink_utils_dfs_state_cfg2q(sc->dfs_state); 19952c31129fSIgor Mitsyanko qch->flags = cpu_to_le32(qlink_utils_chflags_cfg2q(sc->flags)); 1996c9889671SIgor Mitsyanko } 1997c9889671SIgor Mitsyanko 19986fbef954SAndrey Shevchenko static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb, 19996fbef954SAndrey Shevchenko const u8 *mac_addr, 20006fbef954SAndrey Shevchenko const u8 *mac_addr_mask) 20016fbef954SAndrey Shevchenko { 20026fbef954SAndrey Shevchenko struct qlink_random_mac_addr *randmac; 20036fbef954SAndrey Shevchenko struct qlink_tlv_hdr *hdr = 20046fbef954SAndrey Shevchenko skb_put(cmd_skb, sizeof(*hdr) + sizeof(*randmac)); 20056fbef954SAndrey Shevchenko 20066fbef954SAndrey Shevchenko hdr->type = cpu_to_le16(QTN_TLV_ID_RANDOM_MAC_ADDR); 20076fbef954SAndrey Shevchenko hdr->len = cpu_to_le16(sizeof(*randmac)); 20086fbef954SAndrey Shevchenko randmac = (struct qlink_random_mac_addr *)hdr->val; 20096fbef954SAndrey Shevchenko 20106fbef954SAndrey Shevchenko memcpy(randmac->mac_addr, mac_addr, ETH_ALEN); 20116fbef954SAndrey Shevchenko memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN); 20126fbef954SAndrey Shevchenko } 20136fbef954SAndrey Shevchenko 201498f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac) 201598f44cb0SIgor Mitsyanko { 201698f44cb0SIgor Mitsyanko struct cfg80211_scan_request *scan_req = mac->scan_req; 2017501c3be1SIgor Mitsyanko u16 dwell_passive = QTNF_SCAN_DWELL_PASSIVE_DEFAULT; 2018501c3be1SIgor Mitsyanko u16 dwell_active = QTNF_SCAN_DWELL_ACTIVE_DEFAULT; 2019501c3be1SIgor Mitsyanko struct wireless_dev *wdev = scan_req->wdev; 2020501c3be1SIgor Mitsyanko struct ieee80211_channel *sc; 2021501c3be1SIgor Mitsyanko struct qlink_cmd_scan *cmd; 2022501c3be1SIgor Mitsyanko struct sk_buff *cmd_skb; 2023501c3be1SIgor Mitsyanko int n_channels = 0; 2024501c3be1SIgor Mitsyanko u64 flags = 0; 2025501c3be1SIgor Mitsyanko int count; 202698f44cb0SIgor Mitsyanko int ret; 202798f44cb0SIgor Mitsyanko 202898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 202998f44cb0SIgor Mitsyanko QLINK_CMD_SCAN, 2030501c3be1SIgor Mitsyanko sizeof(*cmd)); 2031c93fe71cSSergey Matyukevich if (!cmd_skb) 203298f44cb0SIgor Mitsyanko return -ENOMEM; 203398f44cb0SIgor Mitsyanko 2034501c3be1SIgor Mitsyanko cmd = (struct qlink_cmd_scan *)cmd_skb->data; 203598f44cb0SIgor Mitsyanko 2036501c3be1SIgor Mitsyanko if (scan_req->duration) { 2037501c3be1SIgor Mitsyanko dwell_active = scan_req->duration; 2038501c3be1SIgor Mitsyanko dwell_passive = scan_req->duration; 2039501c3be1SIgor Mitsyanko } else if (wdev->iftype == NL80211_IFTYPE_STATION && 2040501c3be1SIgor Mitsyanko wdev->current_bss) { 2041501c3be1SIgor Mitsyanko /* let device select dwell based on traffic conditions */ 2042501c3be1SIgor Mitsyanko dwell_active = QTNF_SCAN_TIME_AUTO; 2043501c3be1SIgor Mitsyanko dwell_passive = QTNF_SCAN_TIME_AUTO; 2044501c3be1SIgor Mitsyanko } 2045501c3be1SIgor Mitsyanko 2046501c3be1SIgor Mitsyanko cmd->n_ssids = cpu_to_le16(scan_req->n_ssids); 2047501c3be1SIgor Mitsyanko for (count = 0; count < scan_req->n_ssids; ++count) { 204898f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, 204998f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid, 205098f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid_len); 205198f44cb0SIgor Mitsyanko } 205298f44cb0SIgor Mitsyanko 205398f44cb0SIgor Mitsyanko if (scan_req->ie_len != 0) 205418b7470fSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ, 205518b7470fSIgor Mitsyanko scan_req->ie, scan_req->ie_len); 205698f44cb0SIgor Mitsyanko 2057501c3be1SIgor Mitsyanko for (count = 0; count < scan_req->n_channels; ++count) { 205898f44cb0SIgor Mitsyanko sc = scan_req->channels[count]; 2059501c3be1SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_DISABLED) 206098f44cb0SIgor Mitsyanko continue; 206198f44cb0SIgor Mitsyanko 2062501c3be1SIgor Mitsyanko pr_debug("[MAC%u] scan chan=%d, freq=%d, flags=%#x\n", 206398f44cb0SIgor Mitsyanko mac->macid, sc->hw_value, sc->center_freq, 206498f44cb0SIgor Mitsyanko sc->flags); 206598f44cb0SIgor Mitsyanko 2066c9889671SIgor Mitsyanko qtnf_cmd_channel_tlv_add(cmd_skb, sc); 2067501c3be1SIgor Mitsyanko ++n_channels; 206898f44cb0SIgor Mitsyanko } 206998f44cb0SIgor Mitsyanko 2070501c3be1SIgor Mitsyanko if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH) 2071501c3be1SIgor Mitsyanko flags |= QLINK_SCAN_FLAG_FLUSH; 2072501c3be1SIgor Mitsyanko 2073501c3be1SIgor Mitsyanko if (scan_req->duration_mandatory) 2074501c3be1SIgor Mitsyanko flags |= QLINK_SCAN_FLAG_DURATION_MANDATORY; 2075501c3be1SIgor Mitsyanko 2076501c3be1SIgor Mitsyanko cmd->n_channels = cpu_to_le16(n_channels); 2077501c3be1SIgor Mitsyanko cmd->active_dwell = cpu_to_le16(dwell_active); 2078501c3be1SIgor Mitsyanko cmd->passive_dwell = cpu_to_le16(dwell_passive); 2079501c3be1SIgor Mitsyanko cmd->sample_duration = cpu_to_le16(QTNF_SCAN_SAMPLE_DURATION_DEFAULT); 2080501c3be1SIgor Mitsyanko cmd->flags = cpu_to_le64(flags); 2081501c3be1SIgor Mitsyanko 2082501c3be1SIgor Mitsyanko pr_debug("[MAC%u] %s scan dwell active=%u passive=%u duration=%u\n", 2083501c3be1SIgor Mitsyanko mac->macid, 2084501c3be1SIgor Mitsyanko scan_req->duration_mandatory ? "mandatory" : "max", 2085501c3be1SIgor Mitsyanko dwell_active, dwell_passive, 2086501c3be1SIgor Mitsyanko QTNF_SCAN_SAMPLE_DURATION_DEFAULT); 2087b63967caSIgor Mitsyanko 20886fbef954SAndrey Shevchenko if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 2089501c3be1SIgor Mitsyanko pr_debug("[MAC%u] scan with random addr=%pM, mask=%pM\n", 20906fbef954SAndrey Shevchenko mac->macid, 20916fbef954SAndrey Shevchenko scan_req->mac_addr, scan_req->mac_addr_mask); 20926fbef954SAndrey Shevchenko qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr, 20936fbef954SAndrey Shevchenko scan_req->mac_addr_mask); 20946fbef954SAndrey Shevchenko } 20956fbef954SAndrey Shevchenko 2096501c3be1SIgor Mitsyanko qtnf_bus_lock(mac->bus); 2097c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 209898f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 2099c6ed298fSSergey Matyukevich 210098f44cb0SIgor Mitsyanko return ret; 210198f44cb0SIgor Mitsyanko } 210298f44cb0SIgor Mitsyanko 210398f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif, 210498f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 210598f44cb0SIgor Mitsyanko { 210698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 210798f44cb0SIgor Mitsyanko struct qlink_cmd_connect *cmd; 2108d23d1361SIgor Mitsyanko struct qlink_auth_encr *aen; 210998f44cb0SIgor Mitsyanko int ret; 211098f44cb0SIgor Mitsyanko int i; 21119766d1ddSIgor Mitsyanko u32 connect_flags = 0; 211298f44cb0SIgor Mitsyanko 211398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 211498f44cb0SIgor Mitsyanko QLINK_CMD_CONNECT, 211598f44cb0SIgor Mitsyanko sizeof(*cmd)); 2116c93fe71cSSergey Matyukevich if (!cmd_skb) 211798f44cb0SIgor Mitsyanko return -ENOMEM; 211898f44cb0SIgor Mitsyanko 211998f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_connect *)cmd_skb->data; 212098f44cb0SIgor Mitsyanko 21219766d1ddSIgor Mitsyanko ether_addr_copy(cmd->bssid, vif->bssid); 212298f44cb0SIgor Mitsyanko 2123c9889671SIgor Mitsyanko if (sme->bssid_hint) 2124c9889671SIgor Mitsyanko ether_addr_copy(cmd->bssid_hint, sme->bssid_hint); 212596d4eaf2SIgor Mitsyanko else 2126c9889671SIgor Mitsyanko eth_zero_addr(cmd->bssid_hint); 2127c9889671SIgor Mitsyanko 2128c9889671SIgor Mitsyanko if (sme->prev_bssid) 2129c9889671SIgor Mitsyanko ether_addr_copy(cmd->prev_bssid, sme->prev_bssid); 2130c9889671SIgor Mitsyanko else 2131c9889671SIgor Mitsyanko eth_zero_addr(cmd->prev_bssid); 213298f44cb0SIgor Mitsyanko 2133f5d2ff43SAndrey Shevchenko if ((sme->bg_scan_period >= 0) && 2134f5d2ff43SAndrey Shevchenko (sme->bg_scan_period <= SHRT_MAX)) 21359766d1ddSIgor Mitsyanko cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period); 21369766d1ddSIgor Mitsyanko else 2137f5d2ff43SAndrey Shevchenko cmd->bg_scan_period = cpu_to_le16(-1); /* use default value */ 21389766d1ddSIgor Mitsyanko 21399766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_HT) 21409766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; 21419766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_VHT) 21429766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; 21439766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_USE_RRM) 21449766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_USE_RRM; 21459766d1ddSIgor Mitsyanko 21469766d1ddSIgor Mitsyanko cmd->flags = cpu_to_le32(connect_flags); 2147c9889671SIgor Mitsyanko memcpy(&cmd->ht_capa, &sme->ht_capa, sizeof(cmd->ht_capa)); 2148c9889671SIgor Mitsyanko memcpy(&cmd->ht_capa_mask, &sme->ht_capa_mask, 2149c9889671SIgor Mitsyanko sizeof(cmd->ht_capa_mask)); 2150c9889671SIgor Mitsyanko memcpy(&cmd->vht_capa, &sme->vht_capa, sizeof(cmd->vht_capa)); 2151c9889671SIgor Mitsyanko memcpy(&cmd->vht_capa_mask, &sme->vht_capa_mask, 2152c9889671SIgor Mitsyanko sizeof(cmd->vht_capa_mask)); 2153c9889671SIgor Mitsyanko cmd->pbss = sme->pbss; 215498f44cb0SIgor Mitsyanko 2155d23d1361SIgor Mitsyanko aen = &cmd->aen; 2156d23d1361SIgor Mitsyanko aen->auth_type = sme->auth_type; 2157d23d1361SIgor Mitsyanko aen->privacy = !!sme->privacy; 2158c9889671SIgor Mitsyanko cmd->mfp = sme->mfp; 2159d23d1361SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions); 2160d23d1361SIgor Mitsyanko aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group); 2161d23d1361SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise); 216298f44cb0SIgor Mitsyanko 216398f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2164d23d1361SIgor Mitsyanko aen->ciphers_pairwise[i] = 2165d23d1361SIgor Mitsyanko cpu_to_le32(sme->crypto.ciphers_pairwise[i]); 216698f44cb0SIgor Mitsyanko 2167d23d1361SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites); 216898f44cb0SIgor Mitsyanko 216998f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2170d23d1361SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]); 217198f44cb0SIgor Mitsyanko 2172d23d1361SIgor Mitsyanko aen->control_port = sme->crypto.control_port; 2173d23d1361SIgor Mitsyanko aen->control_port_no_encrypt = 21749766d1ddSIgor Mitsyanko sme->crypto.control_port_no_encrypt; 2175d23d1361SIgor Mitsyanko aen->control_port_ethertype = 2176d23d1361SIgor Mitsyanko cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype)); 217798f44cb0SIgor Mitsyanko 21789766d1ddSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid, 21799766d1ddSIgor Mitsyanko sme->ssid_len); 218098f44cb0SIgor Mitsyanko 218198f44cb0SIgor Mitsyanko if (sme->ie_len != 0) 218218b7470fSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_REQ, 218318b7470fSIgor Mitsyanko sme->ie, sme->ie_len); 218498f44cb0SIgor Mitsyanko 2185c9889671SIgor Mitsyanko if (sme->channel) 2186c9889671SIgor Mitsyanko qtnf_cmd_channel_tlv_add(cmd_skb, sme->channel); 2187c9889671SIgor Mitsyanko 2188d23d1361SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 2189c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2190c6ed298fSSergey Matyukevich if (ret) 219198f44cb0SIgor Mitsyanko goto out; 219298f44cb0SIgor Mitsyanko 219398f44cb0SIgor Mitsyanko out: 219498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2195c6ed298fSSergey Matyukevich 219698f44cb0SIgor Mitsyanko return ret; 219798f44cb0SIgor Mitsyanko } 219898f44cb0SIgor Mitsyanko 219947b08e75SSergey Matyukevich int qtnf_cmd_send_external_auth(struct qtnf_vif *vif, 220047b08e75SSergey Matyukevich struct cfg80211_external_auth_params *auth) 220147b08e75SSergey Matyukevich { 220247b08e75SSergey Matyukevich struct sk_buff *cmd_skb; 220347b08e75SSergey Matyukevich struct qlink_cmd_external_auth *cmd; 220447b08e75SSergey Matyukevich int ret; 220547b08e75SSergey Matyukevich 220647b08e75SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 220747b08e75SSergey Matyukevich QLINK_CMD_EXTERNAL_AUTH, 220847b08e75SSergey Matyukevich sizeof(*cmd)); 220947b08e75SSergey Matyukevich if (!cmd_skb) 221047b08e75SSergey Matyukevich return -ENOMEM; 221147b08e75SSergey Matyukevich 221247b08e75SSergey Matyukevich cmd = (struct qlink_cmd_external_auth *)cmd_skb->data; 221347b08e75SSergey Matyukevich 2214*b3860e7aSSergey Matyukevich ether_addr_copy(cmd->peer, auth->bssid); 221547b08e75SSergey Matyukevich cmd->status = cpu_to_le16(auth->status); 221647b08e75SSergey Matyukevich 221747b08e75SSergey Matyukevich qtnf_bus_lock(vif->mac->bus); 221847b08e75SSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 221947b08e75SSergey Matyukevich if (ret) 222047b08e75SSergey Matyukevich goto out; 222147b08e75SSergey Matyukevich 222247b08e75SSergey Matyukevich out: 222347b08e75SSergey Matyukevich qtnf_bus_unlock(vif->mac->bus); 222447b08e75SSergey Matyukevich 222547b08e75SSergey Matyukevich return ret; 222647b08e75SSergey Matyukevich } 222747b08e75SSergey Matyukevich 222898f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code) 222998f44cb0SIgor Mitsyanko { 223098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 223198f44cb0SIgor Mitsyanko struct qlink_cmd_disconnect *cmd; 223298f44cb0SIgor Mitsyanko int ret; 223398f44cb0SIgor Mitsyanko 223498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 223598f44cb0SIgor Mitsyanko QLINK_CMD_DISCONNECT, 223698f44cb0SIgor Mitsyanko sizeof(*cmd)); 2237c93fe71cSSergey Matyukevich if (!cmd_skb) 223898f44cb0SIgor Mitsyanko return -ENOMEM; 223998f44cb0SIgor Mitsyanko 224098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 224198f44cb0SIgor Mitsyanko 224298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_disconnect *)cmd_skb->data; 224398f44cb0SIgor Mitsyanko cmd->reason = cpu_to_le16(reason_code); 224498f44cb0SIgor Mitsyanko 2245c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2246c6ed298fSSergey Matyukevich if (ret) 224798f44cb0SIgor Mitsyanko goto out; 224898f44cb0SIgor Mitsyanko 224998f44cb0SIgor Mitsyanko out: 225098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2251c6ed298fSSergey Matyukevich 225298f44cb0SIgor Mitsyanko return ret; 225398f44cb0SIgor Mitsyanko } 225498f44cb0SIgor Mitsyanko 225598f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up) 225698f44cb0SIgor Mitsyanko { 225798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 225898f44cb0SIgor Mitsyanko struct qlink_cmd_updown *cmd; 225998f44cb0SIgor Mitsyanko int ret; 226098f44cb0SIgor Mitsyanko 226198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 226298f44cb0SIgor Mitsyanko QLINK_CMD_UPDOWN_INTF, 226398f44cb0SIgor Mitsyanko sizeof(*cmd)); 2264c93fe71cSSergey Matyukevich if (!cmd_skb) 226598f44cb0SIgor Mitsyanko return -ENOMEM; 226698f44cb0SIgor Mitsyanko 226798f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_updown *)cmd_skb->data; 226898f44cb0SIgor Mitsyanko cmd->if_up = !!up; 226998f44cb0SIgor Mitsyanko 227098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 2271c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2272c6ed298fSSergey Matyukevich if (ret) 227398f44cb0SIgor Mitsyanko goto out; 227498f44cb0SIgor Mitsyanko 227598f44cb0SIgor Mitsyanko out: 227698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2277c6ed298fSSergey Matyukevich 227898f44cb0SIgor Mitsyanko return ret; 227998f44cb0SIgor Mitsyanko } 22804dd07d2bSSergey Matyukevich 2281888f1564SIgor Mitsyanko int qtnf_cmd_reg_notify(struct qtnf_wmac *mac, struct regulatory_request *req, 2282155b424cSSergey Matyukevich bool slave_radar, bool dfs_offload) 22834dd07d2bSSergey Matyukevich { 2284a2fbaaf7SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 2285d1231721SIgor Mitsyanko struct qtnf_bus *bus = mac->bus; 22864dd07d2bSSergey Matyukevich struct sk_buff *cmd_skb; 22874dd07d2bSSergey Matyukevich int ret; 22884dd07d2bSSergey Matyukevich struct qlink_cmd_reg_notify *cmd; 2289a2fbaaf7SIgor Mitsyanko enum nl80211_band band; 2290a2fbaaf7SIgor Mitsyanko const struct ieee80211_supported_band *cfg_band; 22914dd07d2bSSergey Matyukevich 2292d1231721SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 22934dd07d2bSSergey Matyukevich QLINK_CMD_REG_NOTIFY, 22944dd07d2bSSergey Matyukevich sizeof(*cmd)); 22954dd07d2bSSergey Matyukevich if (!cmd_skb) 22964dd07d2bSSergey Matyukevich return -ENOMEM; 22974dd07d2bSSergey Matyukevich 22984dd07d2bSSergey Matyukevich cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; 22994dd07d2bSSergey Matyukevich cmd->alpha2[0] = req->alpha2[0]; 23004dd07d2bSSergey Matyukevich cmd->alpha2[1] = req->alpha2[1]; 23014dd07d2bSSergey Matyukevich 23024dd07d2bSSergey Matyukevich switch (req->initiator) { 23034dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_CORE: 23044dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_CORE; 23054dd07d2bSSergey Matyukevich break; 23064dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_USER: 23074dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_USER; 23084dd07d2bSSergey Matyukevich break; 23094dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_DRIVER: 23104dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; 23114dd07d2bSSergey Matyukevich break; 23124dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_COUNTRY_IE: 23134dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; 23144dd07d2bSSergey Matyukevich break; 23154dd07d2bSSergey Matyukevich } 23164dd07d2bSSergey Matyukevich 23174dd07d2bSSergey Matyukevich switch (req->user_reg_hint_type) { 23184dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_USER: 23194dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; 23204dd07d2bSSergey Matyukevich break; 23214dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_CELL_BASE: 23224dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; 23234dd07d2bSSergey Matyukevich break; 23244dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_INDOOR: 23254dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; 23264dd07d2bSSergey Matyukevich break; 23274dd07d2bSSergey Matyukevich } 23284dd07d2bSSergey Matyukevich 2329438fb43bSIgor Mitsyanko switch (req->dfs_region) { 2330438fb43bSIgor Mitsyanko case NL80211_DFS_FCC: 2331438fb43bSIgor Mitsyanko cmd->dfs_region = QLINK_DFS_FCC; 2332438fb43bSIgor Mitsyanko break; 2333438fb43bSIgor Mitsyanko case NL80211_DFS_ETSI: 2334438fb43bSIgor Mitsyanko cmd->dfs_region = QLINK_DFS_ETSI; 2335438fb43bSIgor Mitsyanko break; 2336438fb43bSIgor Mitsyanko case NL80211_DFS_JP: 2337438fb43bSIgor Mitsyanko cmd->dfs_region = QLINK_DFS_JP; 2338438fb43bSIgor Mitsyanko break; 2339438fb43bSIgor Mitsyanko default: 2340438fb43bSIgor Mitsyanko cmd->dfs_region = QLINK_DFS_UNSET; 2341438fb43bSIgor Mitsyanko break; 2342438fb43bSIgor Mitsyanko } 2343438fb43bSIgor Mitsyanko 2344888f1564SIgor Mitsyanko cmd->slave_radar = slave_radar; 2345155b424cSSergey Matyukevich cmd->dfs_offload = dfs_offload; 2346a2fbaaf7SIgor Mitsyanko cmd->num_channels = 0; 2347a2fbaaf7SIgor Mitsyanko 2348a2fbaaf7SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; band++) { 2349a2fbaaf7SIgor Mitsyanko unsigned int i; 2350a2fbaaf7SIgor Mitsyanko 2351a2fbaaf7SIgor Mitsyanko cfg_band = wiphy->bands[band]; 2352a2fbaaf7SIgor Mitsyanko if (!cfg_band) 2353a2fbaaf7SIgor Mitsyanko continue; 2354a2fbaaf7SIgor Mitsyanko 2355a2fbaaf7SIgor Mitsyanko cmd->num_channels += cfg_band->n_channels; 2356a2fbaaf7SIgor Mitsyanko 2357a2fbaaf7SIgor Mitsyanko for (i = 0; i < cfg_band->n_channels; ++i) { 2358a2fbaaf7SIgor Mitsyanko qtnf_cmd_channel_tlv_add(cmd_skb, 2359a2fbaaf7SIgor Mitsyanko &cfg_band->channels[i]); 2360a2fbaaf7SIgor Mitsyanko } 2361a2fbaaf7SIgor Mitsyanko } 2362a2fbaaf7SIgor Mitsyanko 23634dd07d2bSSergey Matyukevich qtnf_bus_lock(bus); 2364c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 23654dd07d2bSSergey Matyukevich qtnf_bus_unlock(bus); 23664dd07d2bSSergey Matyukevich 23674dd07d2bSSergey Matyukevich return ret; 23684dd07d2bSSergey Matyukevich } 23697c04b439SSergey Matyukevich 2370601ce21fSIgor Mitsyanko static int 2371601ce21fSIgor Mitsyanko qtnf_cmd_resp_proc_chan_stat_info(struct survey_info *survey, 2372601ce21fSIgor Mitsyanko const u8 *payload, size_t payload_len) 2373601ce21fSIgor Mitsyanko { 2374601ce21fSIgor Mitsyanko const struct qlink_chan_stats *stats = NULL; 2375601ce21fSIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 2376601ce21fSIgor Mitsyanko u16 tlv_value_len; 2377601ce21fSIgor Mitsyanko u16 tlv_type; 2378601ce21fSIgor Mitsyanko const u8 *map = NULL; 2379601ce21fSIgor Mitsyanko unsigned int map_len = 0; 2380601ce21fSIgor Mitsyanko unsigned int stats_len = 0; 2381601ce21fSIgor Mitsyanko 23828b0b5f1bSIgor Mitsyanko qlink_for_each_tlv(tlv, payload, payload_len) { 2383601ce21fSIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 2384601ce21fSIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 2385601ce21fSIgor Mitsyanko 2386601ce21fSIgor Mitsyanko switch (tlv_type) { 2387601ce21fSIgor Mitsyanko case QTN_TLV_ID_BITMAP: 2388601ce21fSIgor Mitsyanko map = tlv->val; 2389601ce21fSIgor Mitsyanko map_len = tlv_value_len; 2390601ce21fSIgor Mitsyanko break; 2391601ce21fSIgor Mitsyanko case QTN_TLV_ID_CHANNEL_STATS: 2392601ce21fSIgor Mitsyanko stats = (struct qlink_chan_stats *)tlv->val; 2393601ce21fSIgor Mitsyanko stats_len = tlv_value_len; 2394601ce21fSIgor Mitsyanko break; 2395601ce21fSIgor Mitsyanko default: 2396601ce21fSIgor Mitsyanko pr_info("Unknown TLV type: %#x\n", tlv_type); 2397601ce21fSIgor Mitsyanko break; 2398601ce21fSIgor Mitsyanko } 2399601ce21fSIgor Mitsyanko } 2400601ce21fSIgor Mitsyanko 24018b0b5f1bSIgor Mitsyanko if (!qlink_tlv_parsing_ok(tlv, payload, payload_len)) { 24028b0b5f1bSIgor Mitsyanko pr_err("Malformed TLV buffer\n"); 2403601ce21fSIgor Mitsyanko return -EINVAL; 2404601ce21fSIgor Mitsyanko } 2405601ce21fSIgor Mitsyanko 2406601ce21fSIgor Mitsyanko if (!map || !stats) 2407601ce21fSIgor Mitsyanko return 0; 2408601ce21fSIgor Mitsyanko 2409601ce21fSIgor Mitsyanko #define qtnf_chan_stat_avail(stat_name, bitn) \ 2410601ce21fSIgor Mitsyanko (qtnf_utils_is_bit_set(map, bitn, map_len) && \ 2411601ce21fSIgor Mitsyanko (offsetofend(struct qlink_chan_stats, stat_name) <= stats_len)) 2412601ce21fSIgor Mitsyanko 2413601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(time_on, QLINK_CHAN_STAT_TIME_ON)) { 2414601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME; 2415601ce21fSIgor Mitsyanko survey->time = le64_to_cpu(stats->time_on); 2416601ce21fSIgor Mitsyanko } 2417601ce21fSIgor Mitsyanko 2418601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(time_tx, QLINK_CHAN_STAT_TIME_TX)) { 2419601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME_TX; 2420601ce21fSIgor Mitsyanko survey->time_tx = le64_to_cpu(stats->time_tx); 2421601ce21fSIgor Mitsyanko } 2422601ce21fSIgor Mitsyanko 2423601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(time_rx, QLINK_CHAN_STAT_TIME_RX)) { 2424601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME_RX; 2425601ce21fSIgor Mitsyanko survey->time_rx = le64_to_cpu(stats->time_rx); 2426601ce21fSIgor Mitsyanko } 2427601ce21fSIgor Mitsyanko 2428601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(cca_busy, QLINK_CHAN_STAT_CCA_BUSY)) { 2429601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME_BUSY; 2430601ce21fSIgor Mitsyanko survey->time_busy = le64_to_cpu(stats->cca_busy); 2431601ce21fSIgor Mitsyanko } 2432601ce21fSIgor Mitsyanko 2433601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(cca_busy_ext, QLINK_CHAN_STAT_CCA_BUSY_EXT)) { 2434601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME_EXT_BUSY; 2435601ce21fSIgor Mitsyanko survey->time_ext_busy = le64_to_cpu(stats->cca_busy_ext); 2436601ce21fSIgor Mitsyanko } 2437601ce21fSIgor Mitsyanko 2438601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(time_scan, QLINK_CHAN_STAT_TIME_SCAN)) { 2439601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_TIME_SCAN; 2440601ce21fSIgor Mitsyanko survey->time_scan = le64_to_cpu(stats->time_scan); 2441601ce21fSIgor Mitsyanko } 2442601ce21fSIgor Mitsyanko 2443601ce21fSIgor Mitsyanko if (qtnf_chan_stat_avail(chan_noise, QLINK_CHAN_STAT_CHAN_NOISE)) { 2444601ce21fSIgor Mitsyanko survey->filled |= SURVEY_INFO_NOISE_DBM; 2445601ce21fSIgor Mitsyanko survey->noise = stats->chan_noise; 2446601ce21fSIgor Mitsyanko } 2447601ce21fSIgor Mitsyanko 2448601ce21fSIgor Mitsyanko #undef qtnf_chan_stat_avail 2449601ce21fSIgor Mitsyanko 2450601ce21fSIgor Mitsyanko return 0; 2451601ce21fSIgor Mitsyanko } 2452601ce21fSIgor Mitsyanko 2453601ce21fSIgor Mitsyanko int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u32 chan_freq, 2454601ce21fSIgor Mitsyanko struct survey_info *survey) 24557c04b439SSergey Matyukevich { 24567c04b439SSergey Matyukevich struct sk_buff *cmd_skb, *resp_skb = NULL; 24577c04b439SSergey Matyukevich struct qlink_cmd_get_chan_stats *cmd; 24587c04b439SSergey Matyukevich struct qlink_resp_get_chan_stats *resp; 24591066bd19SSergey Matyukevich size_t var_data_len = 0; 24607c04b439SSergey Matyukevich int ret = 0; 24617c04b439SSergey Matyukevich 24627c04b439SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 24637c04b439SSergey Matyukevich QLINK_CMD_CHAN_STATS, 24647c04b439SSergey Matyukevich sizeof(*cmd)); 24657c04b439SSergey Matyukevich if (!cmd_skb) 24667c04b439SSergey Matyukevich return -ENOMEM; 24677c04b439SSergey Matyukevich 24687c04b439SSergey Matyukevich cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; 2469601ce21fSIgor Mitsyanko cmd->channel_freq = cpu_to_le32(chan_freq); 24707c04b439SSergey Matyukevich 2471601ce21fSIgor Mitsyanko qtnf_bus_lock(mac->bus); 2472c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 24737c04b439SSergey Matyukevich sizeof(*resp), &var_data_len); 2474601ce21fSIgor Mitsyanko qtnf_bus_unlock(mac->bus); 2475601ce21fSIgor Mitsyanko 2476c6ed298fSSergey Matyukevich if (ret) 24777c04b439SSergey Matyukevich goto out; 24787c04b439SSergey Matyukevich 24797c04b439SSergey Matyukevich resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; 2480601ce21fSIgor Mitsyanko 2481601ce21fSIgor Mitsyanko if (le32_to_cpu(resp->chan_freq) != chan_freq) { 2482601ce21fSIgor Mitsyanko pr_err("[MAC%u] channel stats freq %u != requested %u\n", 2483601ce21fSIgor Mitsyanko mac->macid, le32_to_cpu(resp->chan_freq), chan_freq); 2484601ce21fSIgor Mitsyanko ret = -EINVAL; 2485601ce21fSIgor Mitsyanko goto out; 2486601ce21fSIgor Mitsyanko } 2487601ce21fSIgor Mitsyanko 2488601ce21fSIgor Mitsyanko ret = qtnf_cmd_resp_proc_chan_stat_info(survey, resp->info, 24897c04b439SSergey Matyukevich var_data_len); 24907c04b439SSergey Matyukevich 24917c04b439SSergey Matyukevich out: 24927c04b439SSergey Matyukevich consume_skb(resp_skb); 2493c6ed298fSSergey Matyukevich 24947c04b439SSergey Matyukevich return ret; 24957c04b439SSergey Matyukevich } 249697883695SSergey Matyukevich 24978c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, 249897883695SSergey Matyukevich struct cfg80211_csa_settings *params) 249997883695SSergey Matyukevich { 25008c015b90SIgor Mitsyanko struct qtnf_wmac *mac = vif->mac; 250197883695SSergey Matyukevich struct qlink_cmd_chan_switch *cmd; 250297883695SSergey Matyukevich struct sk_buff *cmd_skb; 250397883695SSergey Matyukevich int ret; 25045edadc5aSIgor Mitsyanko u64 flags = 0; 250597883695SSergey Matyukevich 25068c015b90SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, 250797883695SSergey Matyukevich QLINK_CMD_CHAN_SWITCH, 250897883695SSergey Matyukevich sizeof(*cmd)); 2509c93fe71cSSergey Matyukevich if (!cmd_skb) 251097883695SSergey Matyukevich return -ENOMEM; 251197883695SSergey Matyukevich 25125edadc5aSIgor Mitsyanko if (params->radar_required) 25135edadc5aSIgor Mitsyanko flags |= QLINK_CHAN_SW_RADAR_REQUIRED; 25145edadc5aSIgor Mitsyanko 25155edadc5aSIgor Mitsyanko if (params->block_tx) 25165edadc5aSIgor Mitsyanko flags |= QLINK_CHAN_SW_BLOCK_TX; 251797883695SSergey Matyukevich 251897883695SSergey Matyukevich cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; 25195edadc5aSIgor Mitsyanko qlink_chandef_cfg2q(¶ms->chandef, &cmd->channel); 25205edadc5aSIgor Mitsyanko cmd->flags = cpu_to_le64(flags); 252197883695SSergey Matyukevich cmd->beacon_count = params->count; 252297883695SSergey Matyukevich 25235edadc5aSIgor Mitsyanko qtnf_bus_lock(mac->bus); 2524c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 252597883695SSergey Matyukevich qtnf_bus_unlock(mac->bus); 2526c6ed298fSSergey Matyukevich 252797883695SSergey Matyukevich return ret; 252897883695SSergey Matyukevich } 25299e5478b6SIgor Mitsyanko 25309e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef) 25319e5478b6SIgor Mitsyanko { 25329e5478b6SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 25339e5478b6SIgor Mitsyanko const struct qlink_resp_channel_get *resp; 25349e5478b6SIgor Mitsyanko struct sk_buff *cmd_skb; 25359e5478b6SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 25369e5478b6SIgor Mitsyanko int ret; 25379e5478b6SIgor Mitsyanko 25389e5478b6SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 25399e5478b6SIgor Mitsyanko QLINK_CMD_CHAN_GET, 25409e5478b6SIgor Mitsyanko sizeof(struct qlink_cmd)); 2541c93fe71cSSergey Matyukevich if (!cmd_skb) 25429e5478b6SIgor Mitsyanko return -ENOMEM; 25439e5478b6SIgor Mitsyanko 25449e5478b6SIgor Mitsyanko qtnf_bus_lock(bus); 2545c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 25469e5478b6SIgor Mitsyanko sizeof(*resp), NULL); 2547c6ed298fSSergey Matyukevich if (ret) 25489e5478b6SIgor Mitsyanko goto out; 25499e5478b6SIgor Mitsyanko 25509e5478b6SIgor Mitsyanko resp = (const struct qlink_resp_channel_get *)resp_skb->data; 25519e5478b6SIgor Mitsyanko qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef); 25529e5478b6SIgor Mitsyanko 25539e5478b6SIgor Mitsyanko out: 2554c6ed298fSSergey Matyukevich qtnf_bus_unlock(bus); 25559e5478b6SIgor Mitsyanko consume_skb(resp_skb); 2556c6ed298fSSergey Matyukevich 25579e5478b6SIgor Mitsyanko return ret; 25589e5478b6SIgor Mitsyanko } 2559b05ee456SIgor Mitsyanko 2560b05ee456SIgor Mitsyanko int qtnf_cmd_start_cac(const struct qtnf_vif *vif, 2561b05ee456SIgor Mitsyanko const struct cfg80211_chan_def *chdef, 2562b05ee456SIgor Mitsyanko u32 cac_time_ms) 2563b05ee456SIgor Mitsyanko { 2564b05ee456SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 2565b05ee456SIgor Mitsyanko struct sk_buff *cmd_skb; 2566b05ee456SIgor Mitsyanko struct qlink_cmd_start_cac *cmd; 2567b05ee456SIgor Mitsyanko int ret; 2568b05ee456SIgor Mitsyanko 2569b05ee456SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 2570b05ee456SIgor Mitsyanko QLINK_CMD_START_CAC, 2571b05ee456SIgor Mitsyanko sizeof(*cmd)); 2572c93fe71cSSergey Matyukevich if (!cmd_skb) 2573b05ee456SIgor Mitsyanko return -ENOMEM; 2574b05ee456SIgor Mitsyanko 2575b05ee456SIgor Mitsyanko cmd = (struct qlink_cmd_start_cac *)cmd_skb->data; 2576b05ee456SIgor Mitsyanko cmd->cac_time_ms = cpu_to_le32(cac_time_ms); 2577b05ee456SIgor Mitsyanko qlink_chandef_cfg2q(chdef, &cmd->chan); 2578b05ee456SIgor Mitsyanko 2579b05ee456SIgor Mitsyanko qtnf_bus_lock(bus); 2580c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2581b05ee456SIgor Mitsyanko if (ret) 2582c6ed298fSSergey Matyukevich goto out; 2583b05ee456SIgor Mitsyanko 2584c6ed298fSSergey Matyukevich out: 2585c6ed298fSSergey Matyukevich qtnf_bus_unlock(bus); 2586b05ee456SIgor Mitsyanko 2587b05ee456SIgor Mitsyanko return ret; 2588b05ee456SIgor Mitsyanko } 2589f1398fd2SVasily Ulyanov 2590f1398fd2SVasily Ulyanov int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif, 2591f1398fd2SVasily Ulyanov const struct cfg80211_acl_data *params) 2592f1398fd2SVasily Ulyanov { 2593f1398fd2SVasily Ulyanov struct qtnf_bus *bus = vif->mac->bus; 2594f1398fd2SVasily Ulyanov struct sk_buff *cmd_skb; 259533f98992SVasily Ulyanov struct qlink_tlv_hdr *tlv; 259695336d4cSGustavo A. R. Silva size_t acl_size = struct_size(params, mac_addrs, params->n_acl_entries); 2597f1398fd2SVasily Ulyanov int ret; 2598f1398fd2SVasily Ulyanov 2599f1398fd2SVasily Ulyanov cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 2600f1398fd2SVasily Ulyanov QLINK_CMD_SET_MAC_ACL, 260133f98992SVasily Ulyanov sizeof(struct qlink_cmd)); 2602c93fe71cSSergey Matyukevich if (!cmd_skb) 2603f1398fd2SVasily Ulyanov return -ENOMEM; 2604f1398fd2SVasily Ulyanov 26058b0b5f1bSIgor Mitsyanko tlv = skb_put(cmd_skb, sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN)); 260633f98992SVasily Ulyanov tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); 260733f98992SVasily Ulyanov tlv->len = cpu_to_le16(acl_size); 260833f98992SVasily Ulyanov qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val); 2609f1398fd2SVasily Ulyanov 2610f1398fd2SVasily Ulyanov qtnf_bus_lock(bus); 2611c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2612c6ed298fSSergey Matyukevich if (ret) 2613c6ed298fSSergey Matyukevich goto out; 2614c6ed298fSSergey Matyukevich 2615c6ed298fSSergey Matyukevich out: 2616f1398fd2SVasily Ulyanov qtnf_bus_unlock(bus); 2617f1398fd2SVasily Ulyanov 2618f1398fd2SVasily Ulyanov return ret; 2619f1398fd2SVasily Ulyanov } 26204775ad06SSergei Maksimenko 26214775ad06SSergei Maksimenko int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout) 26224775ad06SSergei Maksimenko { 26234775ad06SSergei Maksimenko struct qtnf_bus *bus = vif->mac->bus; 26244775ad06SSergei Maksimenko struct sk_buff *cmd_skb; 26254775ad06SSergei Maksimenko struct qlink_cmd_pm_set *cmd; 26264775ad06SSergei Maksimenko int ret = 0; 26274775ad06SSergei Maksimenko 26284775ad06SSergei Maksimenko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 26294775ad06SSergei Maksimenko QLINK_CMD_PM_SET, sizeof(*cmd)); 26304775ad06SSergei Maksimenko if (!cmd_skb) 26314775ad06SSergei Maksimenko return -ENOMEM; 26324775ad06SSergei Maksimenko 26334775ad06SSergei Maksimenko cmd = (struct qlink_cmd_pm_set *)cmd_skb->data; 26344775ad06SSergei Maksimenko cmd->pm_mode = pm_mode; 26354775ad06SSergei Maksimenko cmd->pm_standby_timer = cpu_to_le32(timeout); 26364775ad06SSergei Maksimenko 26374775ad06SSergei Maksimenko qtnf_bus_lock(bus); 26384775ad06SSergei Maksimenko 2639c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2640c6ed298fSSergey Matyukevich if (ret) 26414775ad06SSergei Maksimenko goto out; 26424775ad06SSergei Maksimenko 26434775ad06SSergei Maksimenko out: 26444775ad06SSergei Maksimenko qtnf_bus_unlock(bus); 2645c6ed298fSSergey Matyukevich 26464775ad06SSergei Maksimenko return ret; 26474775ad06SSergei Maksimenko } 264828b91884SSergey Matyukevich 26490756e913SMikhail Karpenko int qtnf_cmd_get_tx_power(const struct qtnf_vif *vif, int *dbm) 26500756e913SMikhail Karpenko { 26510756e913SMikhail Karpenko struct qtnf_bus *bus = vif->mac->bus; 26520756e913SMikhail Karpenko const struct qlink_resp_txpwr *resp; 26530756e913SMikhail Karpenko struct sk_buff *resp_skb = NULL; 26540756e913SMikhail Karpenko struct qlink_cmd_txpwr *cmd; 26550756e913SMikhail Karpenko struct sk_buff *cmd_skb; 26560756e913SMikhail Karpenko int ret = 0; 26570756e913SMikhail Karpenko 26580756e913SMikhail Karpenko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 26590756e913SMikhail Karpenko QLINK_CMD_TXPWR, sizeof(*cmd)); 26600756e913SMikhail Karpenko if (!cmd_skb) 26610756e913SMikhail Karpenko return -ENOMEM; 26620756e913SMikhail Karpenko 26630756e913SMikhail Karpenko cmd = (struct qlink_cmd_txpwr *)cmd_skb->data; 26640756e913SMikhail Karpenko cmd->op_type = QLINK_TXPWR_GET; 26650756e913SMikhail Karpenko 26660756e913SMikhail Karpenko qtnf_bus_lock(bus); 26670756e913SMikhail Karpenko 26680756e913SMikhail Karpenko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 26690756e913SMikhail Karpenko sizeof(*resp), NULL); 26700756e913SMikhail Karpenko if (ret) 26710756e913SMikhail Karpenko goto out; 26720756e913SMikhail Karpenko 26730756e913SMikhail Karpenko resp = (const struct qlink_resp_txpwr *)resp_skb->data; 26740756e913SMikhail Karpenko *dbm = MBM_TO_DBM(le32_to_cpu(resp->txpwr)); 26750756e913SMikhail Karpenko 26760756e913SMikhail Karpenko out: 26770756e913SMikhail Karpenko qtnf_bus_unlock(bus); 26780756e913SMikhail Karpenko consume_skb(resp_skb); 26790756e913SMikhail Karpenko 26800756e913SMikhail Karpenko return ret; 26810756e913SMikhail Karpenko } 26820756e913SMikhail Karpenko 26830756e913SMikhail Karpenko int qtnf_cmd_set_tx_power(const struct qtnf_vif *vif, 26840756e913SMikhail Karpenko enum nl80211_tx_power_setting type, int mbm) 26850756e913SMikhail Karpenko { 26860756e913SMikhail Karpenko struct qtnf_bus *bus = vif->mac->bus; 26870756e913SMikhail Karpenko const struct qlink_resp_txpwr *resp; 26880756e913SMikhail Karpenko struct sk_buff *resp_skb = NULL; 26890756e913SMikhail Karpenko struct qlink_cmd_txpwr *cmd; 26900756e913SMikhail Karpenko struct sk_buff *cmd_skb; 26910756e913SMikhail Karpenko int ret = 0; 26920756e913SMikhail Karpenko 26930756e913SMikhail Karpenko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 26940756e913SMikhail Karpenko QLINK_CMD_TXPWR, sizeof(*cmd)); 26950756e913SMikhail Karpenko if (!cmd_skb) 26960756e913SMikhail Karpenko return -ENOMEM; 26970756e913SMikhail Karpenko 26980756e913SMikhail Karpenko cmd = (struct qlink_cmd_txpwr *)cmd_skb->data; 26990756e913SMikhail Karpenko cmd->op_type = QLINK_TXPWR_SET; 27000756e913SMikhail Karpenko cmd->txpwr_setting = type; 27010756e913SMikhail Karpenko cmd->txpwr = cpu_to_le32(mbm); 27020756e913SMikhail Karpenko 27030756e913SMikhail Karpenko qtnf_bus_lock(bus); 27040756e913SMikhail Karpenko 27050756e913SMikhail Karpenko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 27060756e913SMikhail Karpenko sizeof(*resp), NULL); 27070756e913SMikhail Karpenko 27080756e913SMikhail Karpenko qtnf_bus_unlock(bus); 27090756e913SMikhail Karpenko consume_skb(resp_skb); 27100756e913SMikhail Karpenko 27110756e913SMikhail Karpenko return ret; 27120756e913SMikhail Karpenko } 27130756e913SMikhail Karpenko 271428b91884SSergey Matyukevich int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, 271528b91884SSergey Matyukevich const struct cfg80211_wowlan *wowl) 271628b91884SSergey Matyukevich { 271728b91884SSergey Matyukevich struct qtnf_bus *bus = vif->mac->bus; 271828b91884SSergey Matyukevich struct sk_buff *cmd_skb; 271928b91884SSergey Matyukevich struct qlink_cmd_wowlan_set *cmd; 272028b91884SSergey Matyukevich u32 triggers = 0; 272128b91884SSergey Matyukevich int count = 0; 272228b91884SSergey Matyukevich int ret = 0; 272328b91884SSergey Matyukevich 272428b91884SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 272528b91884SSergey Matyukevich QLINK_CMD_WOWLAN_SET, sizeof(*cmd)); 272628b91884SSergey Matyukevich if (!cmd_skb) 272728b91884SSergey Matyukevich return -ENOMEM; 272828b91884SSergey Matyukevich 272928b91884SSergey Matyukevich qtnf_bus_lock(bus); 273028b91884SSergey Matyukevich 273128b91884SSergey Matyukevich cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data; 273228b91884SSergey Matyukevich 273328b91884SSergey Matyukevich if (wowl) { 273428b91884SSergey Matyukevich if (wowl->disconnect) 273528b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_DISCONNECT; 273628b91884SSergey Matyukevich 273728b91884SSergey Matyukevich if (wowl->magic_pkt) 273828b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT; 273928b91884SSergey Matyukevich 274028b91884SSergey Matyukevich if (wowl->n_patterns && wowl->patterns) { 274128b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT; 274228b91884SSergey Matyukevich while (count < wowl->n_patterns) { 274328b91884SSergey Matyukevich qtnf_cmd_skb_put_tlv_arr(cmd_skb, 274428b91884SSergey Matyukevich QTN_TLV_ID_WOWLAN_PATTERN, 274528b91884SSergey Matyukevich wowl->patterns[count].pattern, 274628b91884SSergey Matyukevich wowl->patterns[count].pattern_len); 274728b91884SSergey Matyukevich count++; 274828b91884SSergey Matyukevich } 274928b91884SSergey Matyukevich } 275028b91884SSergey Matyukevich } 275128b91884SSergey Matyukevich 275228b91884SSergey Matyukevich cmd->triggers = cpu_to_le32(triggers); 275328b91884SSergey Matyukevich 2754c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2755c6ed298fSSergey Matyukevich if (ret) 275628b91884SSergey Matyukevich goto out; 275728b91884SSergey Matyukevich 275828b91884SSergey Matyukevich out: 275928b91884SSergey Matyukevich qtnf_bus_unlock(bus); 276028b91884SSergey Matyukevich return ret; 276128b91884SSergey Matyukevich } 2762decfc5c7SIgor Mitsyanko 2763decfc5c7SIgor Mitsyanko int qtnf_cmd_netdev_changeupper(const struct qtnf_vif *vif, int br_domain) 2764decfc5c7SIgor Mitsyanko { 2765decfc5c7SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 2766decfc5c7SIgor Mitsyanko struct sk_buff *cmd_skb; 2767decfc5c7SIgor Mitsyanko struct qlink_cmd_ndev_changeupper *cmd; 2768decfc5c7SIgor Mitsyanko int ret; 2769decfc5c7SIgor Mitsyanko 2770decfc5c7SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 2771decfc5c7SIgor Mitsyanko QLINK_CMD_NDEV_EVENT, 2772decfc5c7SIgor Mitsyanko sizeof(*cmd)); 2773decfc5c7SIgor Mitsyanko if (!cmd_skb) 2774decfc5c7SIgor Mitsyanko return -ENOMEM; 2775decfc5c7SIgor Mitsyanko 2776decfc5c7SIgor Mitsyanko pr_debug("[VIF%u.%u] set broadcast domain to %d\n", 2777decfc5c7SIgor Mitsyanko vif->mac->macid, vif->vifid, br_domain); 2778decfc5c7SIgor Mitsyanko 2779decfc5c7SIgor Mitsyanko cmd = (struct qlink_cmd_ndev_changeupper *)cmd_skb->data; 2780decfc5c7SIgor Mitsyanko cmd->nehdr.event = cpu_to_le16(QLINK_NDEV_EVENT_CHANGEUPPER); 2781decfc5c7SIgor Mitsyanko cmd->upper_type = QLINK_NDEV_UPPER_TYPE_BRIDGE; 2782decfc5c7SIgor Mitsyanko cmd->br_domain = cpu_to_le32(br_domain); 2783decfc5c7SIgor Mitsyanko 2784decfc5c7SIgor Mitsyanko qtnf_bus_lock(bus); 2785decfc5c7SIgor Mitsyanko ret = qtnf_cmd_send(bus, cmd_skb); 2786decfc5c7SIgor Mitsyanko qtnf_bus_unlock(bus); 2787decfc5c7SIgor Mitsyanko 2788decfc5c7SIgor Mitsyanko if (ret) 2789decfc5c7SIgor Mitsyanko pr_err("[VIF%u.%u] failed to set broadcast domain\n", 2790decfc5c7SIgor Mitsyanko vif->mac->macid, vif->vifid); 2791decfc5c7SIgor Mitsyanko 2792decfc5c7SIgor Mitsyanko return ret; 2793decfc5c7SIgor Mitsyanko } 2794