198f44cb0SIgor Mitsyanko /* 298f44cb0SIgor Mitsyanko * Copyright (c) 2015-2016 Quantenna Communications, Inc. 398f44cb0SIgor Mitsyanko * 498f44cb0SIgor Mitsyanko * This program is free software; you can redistribute it and/or 598f44cb0SIgor Mitsyanko * modify it under the terms of the GNU General Public License 698f44cb0SIgor Mitsyanko * as published by the Free Software Foundation; either version 2 798f44cb0SIgor Mitsyanko * of the License, or (at your option) any later version. 898f44cb0SIgor Mitsyanko * 998f44cb0SIgor Mitsyanko * This program is distributed in the hope that it will be useful, 1098f44cb0SIgor Mitsyanko * but WITHOUT ANY WARRANTY; without even the implied warranty of 1198f44cb0SIgor Mitsyanko * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1298f44cb0SIgor Mitsyanko * GNU General Public License for more details. 1398f44cb0SIgor Mitsyanko * 1498f44cb0SIgor Mitsyanko */ 1598f44cb0SIgor Mitsyanko 1698f44cb0SIgor Mitsyanko #include <linux/types.h> 1798f44cb0SIgor Mitsyanko #include <linux/skbuff.h> 1898f44cb0SIgor Mitsyanko 1998f44cb0SIgor Mitsyanko #include "cfg80211.h" 2098f44cb0SIgor Mitsyanko #include "core.h" 2198f44cb0SIgor Mitsyanko #include "qlink.h" 2298f44cb0SIgor Mitsyanko #include "qlink_util.h" 2398f44cb0SIgor Mitsyanko #include "bus.h" 2498f44cb0SIgor Mitsyanko #include "commands.h" 2598f44cb0SIgor Mitsyanko 2698f44cb0SIgor Mitsyanko static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp, 2798f44cb0SIgor Mitsyanko u16 cmd_id, u8 mac_id, u8 vif_id, 2898f44cb0SIgor Mitsyanko size_t resp_size) 2998f44cb0SIgor Mitsyanko { 3098f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->cmd_id) != cmd_id)) { 3198f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad cmd_id in response: 0x%.4X\n", 3298f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, le16_to_cpu(resp->cmd_id)); 3398f44cb0SIgor Mitsyanko return -EINVAL; 3498f44cb0SIgor Mitsyanko } 3598f44cb0SIgor Mitsyanko 3698f44cb0SIgor Mitsyanko if (unlikely(resp->macid != mac_id)) { 3798f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad MAC in response: %u\n", 3898f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->macid); 3998f44cb0SIgor Mitsyanko return -EINVAL; 4098f44cb0SIgor Mitsyanko } 4198f44cb0SIgor Mitsyanko 4298f44cb0SIgor Mitsyanko if (unlikely(resp->vifid != vif_id)) { 4398f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad VIF in response: %u\n", 4498f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->vifid); 4598f44cb0SIgor Mitsyanko return -EINVAL; 4698f44cb0SIgor Mitsyanko } 4798f44cb0SIgor Mitsyanko 4898f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->mhdr.len) < resp_size)) { 4998f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad response size %u < %zu\n", 5098f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, 5198f44cb0SIgor Mitsyanko le16_to_cpu(resp->mhdr.len), resp_size); 5298f44cb0SIgor Mitsyanko return -ENOSPC; 5398f44cb0SIgor Mitsyanko } 5498f44cb0SIgor Mitsyanko 5598f44cb0SIgor Mitsyanko return 0; 5698f44cb0SIgor Mitsyanko } 5798f44cb0SIgor Mitsyanko 5898f44cb0SIgor Mitsyanko static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus, 5998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, 6098f44cb0SIgor Mitsyanko struct sk_buff **response_skb, 6198f44cb0SIgor Mitsyanko u16 *result_code, 6298f44cb0SIgor Mitsyanko size_t const_resp_size, 6398f44cb0SIgor Mitsyanko size_t *var_resp_size) 6498f44cb0SIgor Mitsyanko { 6598f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 6698f44cb0SIgor Mitsyanko const struct qlink_resp *resp; 6798f44cb0SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 6898f44cb0SIgor Mitsyanko u16 cmd_id; 6998f44cb0SIgor Mitsyanko u8 mac_id, vif_id; 7098f44cb0SIgor Mitsyanko int ret; 7198f44cb0SIgor Mitsyanko 7298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 7398f44cb0SIgor Mitsyanko cmd_id = le16_to_cpu(cmd->cmd_id); 7498f44cb0SIgor Mitsyanko mac_id = cmd->macid; 7598f44cb0SIgor Mitsyanko vif_id = cmd->vifid; 7698f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 7798f44cb0SIgor Mitsyanko 7898f44cb0SIgor Mitsyanko if (unlikely(bus->fw_state != QTNF_FW_STATE_ACTIVE && 7998f44cb0SIgor Mitsyanko le16_to_cpu(cmd->cmd_id) != QLINK_CMD_FW_INIT)) { 8098f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n", 8198f44cb0SIgor Mitsyanko mac_id, vif_id, le16_to_cpu(cmd->cmd_id), 8298f44cb0SIgor Mitsyanko bus->fw_state); 8398f44cb0SIgor Mitsyanko return -ENODEV; 8498f44cb0SIgor Mitsyanko } 8598f44cb0SIgor Mitsyanko 8698f44cb0SIgor Mitsyanko pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id, 8798f44cb0SIgor Mitsyanko le16_to_cpu(cmd->cmd_id)); 8898f44cb0SIgor Mitsyanko 8998f44cb0SIgor Mitsyanko ret = qtnf_trans_send_cmd_with_resp(bus, cmd_skb, &resp_skb); 9098f44cb0SIgor Mitsyanko 9198f44cb0SIgor Mitsyanko if (unlikely(ret)) 9298f44cb0SIgor Mitsyanko goto out; 9398f44cb0SIgor Mitsyanko 9498f44cb0SIgor Mitsyanko resp = (const struct qlink_resp *)resp_skb->data; 9598f44cb0SIgor Mitsyanko ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id, 9698f44cb0SIgor Mitsyanko const_resp_size); 9798f44cb0SIgor Mitsyanko 9898f44cb0SIgor Mitsyanko if (unlikely(ret)) 9998f44cb0SIgor Mitsyanko goto out; 10098f44cb0SIgor Mitsyanko 10198f44cb0SIgor Mitsyanko if (likely(result_code)) 10298f44cb0SIgor Mitsyanko *result_code = le16_to_cpu(resp->result); 10398f44cb0SIgor Mitsyanko 10498f44cb0SIgor Mitsyanko /* Return length of variable part of response */ 10598f44cb0SIgor Mitsyanko if (response_skb && var_resp_size) 10698f44cb0SIgor Mitsyanko *var_resp_size = le16_to_cpu(resp->mhdr.len) - const_resp_size; 10798f44cb0SIgor Mitsyanko 10898f44cb0SIgor Mitsyanko out: 10998f44cb0SIgor Mitsyanko if (response_skb) 11098f44cb0SIgor Mitsyanko *response_skb = resp_skb; 11198f44cb0SIgor Mitsyanko else 11298f44cb0SIgor Mitsyanko consume_skb(resp_skb); 11398f44cb0SIgor Mitsyanko 11498f44cb0SIgor Mitsyanko return ret; 11598f44cb0SIgor Mitsyanko } 11698f44cb0SIgor Mitsyanko 11798f44cb0SIgor Mitsyanko static inline int qtnf_cmd_send(struct qtnf_bus *bus, 11898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, 11998f44cb0SIgor Mitsyanko u16 *result_code) 12098f44cb0SIgor Mitsyanko { 12198f44cb0SIgor Mitsyanko return qtnf_cmd_send_with_reply(bus, cmd_skb, NULL, result_code, 12298f44cb0SIgor Mitsyanko sizeof(struct qlink_resp), NULL); 12398f44cb0SIgor Mitsyanko } 12498f44cb0SIgor Mitsyanko 12598f44cb0SIgor Mitsyanko static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no, 12698f44cb0SIgor Mitsyanko size_t cmd_size) 12798f44cb0SIgor Mitsyanko { 12898f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 12998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 13098f44cb0SIgor Mitsyanko 13198f44cb0SIgor Mitsyanko cmd_skb = __dev_alloc_skb(sizeof(*cmd) + 13298f44cb0SIgor Mitsyanko QTNF_MAX_CMD_BUF_SIZE, GFP_KERNEL); 13398f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) { 13498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u CMD %u: alloc failed\n", macid, vifid, cmd_no); 13598f44cb0SIgor Mitsyanko return NULL; 13698f44cb0SIgor Mitsyanko } 13798f44cb0SIgor Mitsyanko 138b080db58SJohannes Berg skb_put_zero(cmd_skb, cmd_size); 13998f44cb0SIgor Mitsyanko 14098f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 14198f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 14298f44cb0SIgor Mitsyanko cmd->mhdr.type = cpu_to_le16(QLINK_MSG_TYPE_CMD); 14398f44cb0SIgor Mitsyanko cmd->cmd_id = cpu_to_le16(cmd_no); 14498f44cb0SIgor Mitsyanko cmd->macid = macid; 14598f44cb0SIgor Mitsyanko cmd->vifid = vifid; 14698f44cb0SIgor Mitsyanko 14798f44cb0SIgor Mitsyanko return cmd_skb; 14898f44cb0SIgor Mitsyanko } 14998f44cb0SIgor Mitsyanko 15098f44cb0SIgor Mitsyanko int qtnf_cmd_send_start_ap(struct qtnf_vif *vif) 15198f44cb0SIgor Mitsyanko { 15298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 15398f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 15498f44cb0SIgor Mitsyanko int ret; 15598f44cb0SIgor Mitsyanko 15698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 15798f44cb0SIgor Mitsyanko QLINK_CMD_START_AP, 15898f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 15998f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 16098f44cb0SIgor Mitsyanko return -ENOMEM; 16198f44cb0SIgor Mitsyanko 16298f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 16398f44cb0SIgor Mitsyanko 16498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 16598f44cb0SIgor Mitsyanko 16698f44cb0SIgor Mitsyanko if (unlikely(ret)) 16798f44cb0SIgor Mitsyanko goto out; 16898f44cb0SIgor Mitsyanko 16998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 17098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 17198f44cb0SIgor Mitsyanko vif->vifid, res_code); 17298f44cb0SIgor Mitsyanko ret = -EFAULT; 17398f44cb0SIgor Mitsyanko goto out; 17498f44cb0SIgor Mitsyanko } 17598f44cb0SIgor Mitsyanko 17698f44cb0SIgor Mitsyanko vif->bss_status |= QTNF_STATE_AP_START; 17798f44cb0SIgor Mitsyanko netif_carrier_on(vif->netdev); 17898f44cb0SIgor Mitsyanko 17998f44cb0SIgor Mitsyanko out: 18098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 18198f44cb0SIgor Mitsyanko return ret; 18298f44cb0SIgor Mitsyanko } 18398f44cb0SIgor Mitsyanko 1849b692df1SIgor Mitsyanko int qtnf_cmd_send_config_ap(struct qtnf_vif *vif, 1859b692df1SIgor Mitsyanko const struct cfg80211_ap_settings *s) 18698f44cb0SIgor Mitsyanko { 18798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 18834f1145bSSergey Matyukevich struct cfg80211_chan_def *chandef = &vif->mac->chandef; 18998f44cb0SIgor Mitsyanko struct qlink_tlv_channel *qchan; 190*8b5f4aa7SIgor Mitsyanko struct qlink_cmd_config_ap *cmd; 191*8b5f4aa7SIgor Mitsyanko struct qlink_auth_encr *aen; 19298f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 19398f44cb0SIgor Mitsyanko int ret; 19498f44cb0SIgor Mitsyanko int i; 19598f44cb0SIgor Mitsyanko 19698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 19798f44cb0SIgor Mitsyanko QLINK_CMD_CONFIG_AP, 198*8b5f4aa7SIgor Mitsyanko sizeof(*cmd)); 19998f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 20098f44cb0SIgor Mitsyanko return -ENOMEM; 20198f44cb0SIgor Mitsyanko 202*8b5f4aa7SIgor Mitsyanko cmd = (struct qlink_cmd_config_ap *)cmd_skb->data; 203*8b5f4aa7SIgor Mitsyanko cmd->dtim_period = s->dtim_period; 204*8b5f4aa7SIgor Mitsyanko cmd->beacon_interval = cpu_to_le16(s->beacon_interval); 205*8b5f4aa7SIgor Mitsyanko cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid); 206*8b5f4aa7SIgor Mitsyanko cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout); 207*8b5f4aa7SIgor Mitsyanko cmd->smps_mode = s->smps_mode; 208*8b5f4aa7SIgor Mitsyanko cmd->p2p_ctwindow = s->p2p_ctwindow; 209*8b5f4aa7SIgor Mitsyanko cmd->p2p_opp_ps = s->p2p_opp_ps; 210*8b5f4aa7SIgor Mitsyanko cmd->pbss = s->pbss; 211*8b5f4aa7SIgor Mitsyanko cmd->ht_required = s->ht_required; 212*8b5f4aa7SIgor Mitsyanko cmd->vht_required = s->vht_required; 21398f44cb0SIgor Mitsyanko 2149b692df1SIgor Mitsyanko if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN) 2159b692df1SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid, 2169b692df1SIgor Mitsyanko s->ssid_len); 2179b692df1SIgor Mitsyanko 218b080db58SJohannes Berg qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); 21998f44cb0SIgor Mitsyanko qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); 22098f44cb0SIgor Mitsyanko qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - 22198f44cb0SIgor Mitsyanko sizeof(struct qlink_tlv_hdr)); 22298f44cb0SIgor Mitsyanko qchan->hw_value = cpu_to_le16( 22398f44cb0SIgor Mitsyanko ieee80211_frequency_to_channel(chandef->chan->center_freq)); 22498f44cb0SIgor Mitsyanko 225*8b5f4aa7SIgor Mitsyanko aen = &cmd->aen; 226*8b5f4aa7SIgor Mitsyanko aen->auth_type = s->auth_type; 227*8b5f4aa7SIgor Mitsyanko aen->privacy = !!s->privacy; 228*8b5f4aa7SIgor Mitsyanko aen->mfp = 0; 229*8b5f4aa7SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions); 230*8b5f4aa7SIgor Mitsyanko aen->cipher_group = cpu_to_le32(s->crypto.cipher_group); 231*8b5f4aa7SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise); 23298f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 233*8b5f4aa7SIgor Mitsyanko aen->ciphers_pairwise[i] = 2349b692df1SIgor Mitsyanko cpu_to_le32(s->crypto.ciphers_pairwise[i]); 235*8b5f4aa7SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); 23698f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 237*8b5f4aa7SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); 238*8b5f4aa7SIgor Mitsyanko aen->control_port = s->crypto.control_port; 239*8b5f4aa7SIgor Mitsyanko aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; 240*8b5f4aa7SIgor Mitsyanko aen->control_port_ethertype = 2419b692df1SIgor Mitsyanko cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype)); 24298f44cb0SIgor Mitsyanko 243*8b5f4aa7SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 24498f44cb0SIgor Mitsyanko 24598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 24698f44cb0SIgor Mitsyanko 24798f44cb0SIgor Mitsyanko if (unlikely(ret)) 24898f44cb0SIgor Mitsyanko goto out; 24998f44cb0SIgor Mitsyanko 25098f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 25198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 25298f44cb0SIgor Mitsyanko vif->vifid, res_code); 25398f44cb0SIgor Mitsyanko ret = -EFAULT; 25498f44cb0SIgor Mitsyanko goto out; 25598f44cb0SIgor Mitsyanko } 25698f44cb0SIgor Mitsyanko 25798f44cb0SIgor Mitsyanko vif->bss_status |= QTNF_STATE_AP_CONFIG; 25898f44cb0SIgor Mitsyanko 25998f44cb0SIgor Mitsyanko out: 26098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 26198f44cb0SIgor Mitsyanko return ret; 26298f44cb0SIgor Mitsyanko } 26398f44cb0SIgor Mitsyanko 26498f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif) 26598f44cb0SIgor Mitsyanko { 26698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 26798f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 26898f44cb0SIgor Mitsyanko int ret; 26998f44cb0SIgor Mitsyanko 27098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 27198f44cb0SIgor Mitsyanko QLINK_CMD_STOP_AP, 27298f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 27398f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 27498f44cb0SIgor Mitsyanko return -ENOMEM; 27598f44cb0SIgor Mitsyanko 27698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 27798f44cb0SIgor Mitsyanko 27898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 27998f44cb0SIgor Mitsyanko 28098f44cb0SIgor Mitsyanko if (unlikely(ret)) 28198f44cb0SIgor Mitsyanko goto out; 28298f44cb0SIgor Mitsyanko 28398f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 28498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 28598f44cb0SIgor Mitsyanko vif->vifid, res_code); 28698f44cb0SIgor Mitsyanko ret = -EFAULT; 28798f44cb0SIgor Mitsyanko goto out; 28898f44cb0SIgor Mitsyanko } 28998f44cb0SIgor Mitsyanko 29098f44cb0SIgor Mitsyanko vif->bss_status &= ~QTNF_STATE_AP_START; 29198f44cb0SIgor Mitsyanko vif->bss_status &= ~QTNF_STATE_AP_CONFIG; 29298f44cb0SIgor Mitsyanko 29398f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 29498f44cb0SIgor Mitsyanko 29598f44cb0SIgor Mitsyanko out: 29698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 29798f44cb0SIgor Mitsyanko return ret; 29898f44cb0SIgor Mitsyanko } 29998f44cb0SIgor Mitsyanko 30098f44cb0SIgor Mitsyanko int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg) 30198f44cb0SIgor Mitsyanko { 30298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 30398f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_frame_register *cmd; 30498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 30598f44cb0SIgor Mitsyanko int ret; 30698f44cb0SIgor Mitsyanko 30798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 30898f44cb0SIgor Mitsyanko QLINK_CMD_REGISTER_MGMT, 30998f44cb0SIgor Mitsyanko sizeof(*cmd)); 31098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 31198f44cb0SIgor Mitsyanko return -ENOMEM; 31298f44cb0SIgor Mitsyanko 31398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 31498f44cb0SIgor Mitsyanko 31598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_mgmt_frame_register *)cmd_skb->data; 31698f44cb0SIgor Mitsyanko cmd->frame_type = cpu_to_le16(frame_type); 31798f44cb0SIgor Mitsyanko cmd->do_register = reg; 31898f44cb0SIgor Mitsyanko 31998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 32098f44cb0SIgor Mitsyanko 32198f44cb0SIgor Mitsyanko if (unlikely(ret)) 32298f44cb0SIgor Mitsyanko goto out; 32398f44cb0SIgor Mitsyanko 32498f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 32598f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 32698f44cb0SIgor Mitsyanko vif->vifid, res_code); 32798f44cb0SIgor Mitsyanko ret = -EFAULT; 32898f44cb0SIgor Mitsyanko goto out; 32998f44cb0SIgor Mitsyanko } 33098f44cb0SIgor Mitsyanko 33198f44cb0SIgor Mitsyanko out: 33298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 33398f44cb0SIgor Mitsyanko return ret; 33498f44cb0SIgor Mitsyanko } 33598f44cb0SIgor Mitsyanko 33698f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags, 33798f44cb0SIgor Mitsyanko u16 freq, const u8 *buf, size_t len) 33898f44cb0SIgor Mitsyanko { 33998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 34098f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_frame_tx *cmd; 34198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 34298f44cb0SIgor Mitsyanko int ret; 34398f44cb0SIgor Mitsyanko 34498f44cb0SIgor Mitsyanko if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) { 34598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: frame is too big: %zu\n", vif->mac->macid, 34698f44cb0SIgor Mitsyanko vif->vifid, len); 34798f44cb0SIgor Mitsyanko return -E2BIG; 34898f44cb0SIgor Mitsyanko } 34998f44cb0SIgor Mitsyanko 35098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 35198f44cb0SIgor Mitsyanko QLINK_CMD_SEND_MGMT_FRAME, 35298f44cb0SIgor Mitsyanko sizeof(*cmd)); 35398f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 35498f44cb0SIgor Mitsyanko return -ENOMEM; 35598f44cb0SIgor Mitsyanko 35698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 35798f44cb0SIgor Mitsyanko 35898f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_mgmt_frame_tx *)cmd_skb->data; 35998f44cb0SIgor Mitsyanko cmd->cookie = cpu_to_le32(cookie); 36098f44cb0SIgor Mitsyanko cmd->freq = cpu_to_le16(freq); 36198f44cb0SIgor Mitsyanko cmd->flags = cpu_to_le16(flags); 36298f44cb0SIgor Mitsyanko 36398f44cb0SIgor Mitsyanko if (len && buf) 36498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_buffer(cmd_skb, buf, len); 36598f44cb0SIgor Mitsyanko 36698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 36798f44cb0SIgor Mitsyanko 36898f44cb0SIgor Mitsyanko if (unlikely(ret)) 36998f44cb0SIgor Mitsyanko goto out; 37098f44cb0SIgor Mitsyanko 37198f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 37298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 37398f44cb0SIgor Mitsyanko vif->vifid, res_code); 37498f44cb0SIgor Mitsyanko ret = -EFAULT; 37598f44cb0SIgor Mitsyanko goto out; 37698f44cb0SIgor Mitsyanko } 37798f44cb0SIgor Mitsyanko 37898f44cb0SIgor Mitsyanko out: 37998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 38098f44cb0SIgor Mitsyanko return ret; 38198f44cb0SIgor Mitsyanko } 38298f44cb0SIgor Mitsyanko 38398f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, 38498f44cb0SIgor Mitsyanko const u8 *buf, size_t len) 38598f44cb0SIgor Mitsyanko { 38698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 38798f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_append_ie *cmd; 38898f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 38998f44cb0SIgor Mitsyanko int ret; 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) { 39298f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid, 39398f44cb0SIgor Mitsyanko vif->vifid, frame_type, len); 39498f44cb0SIgor Mitsyanko return -E2BIG; 39598f44cb0SIgor Mitsyanko } 39698f44cb0SIgor Mitsyanko 39798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 39898f44cb0SIgor Mitsyanko QLINK_CMD_MGMT_SET_APPIE, 39998f44cb0SIgor Mitsyanko sizeof(*cmd)); 40098f44cb0SIgor Mitsyanko if (unlikely(!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_append_ie *)cmd_skb->data; 40698f44cb0SIgor Mitsyanko cmd->type = frame_type; 40798f44cb0SIgor Mitsyanko cmd->flags = 0; 40898f44cb0SIgor Mitsyanko 40998f44cb0SIgor Mitsyanko /* If len == 0 then IE buf for specified frame type 41098f44cb0SIgor Mitsyanko * should be cleared on EP. 41198f44cb0SIgor Mitsyanko */ 41298f44cb0SIgor Mitsyanko if (len && buf) 41398f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_buffer(cmd_skb, buf, len); 41498f44cb0SIgor Mitsyanko 41598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 41698f44cb0SIgor Mitsyanko 41798f44cb0SIgor Mitsyanko if (unlikely(ret)) 41898f44cb0SIgor Mitsyanko goto out; 41998f44cb0SIgor Mitsyanko 42098f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 42198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u frame %u: CMD failed: %u\n", vif->mac->macid, 42298f44cb0SIgor Mitsyanko vif->vifid, frame_type, res_code); 42398f44cb0SIgor Mitsyanko ret = -EFAULT; 42498f44cb0SIgor Mitsyanko goto out; 42598f44cb0SIgor Mitsyanko } 42698f44cb0SIgor Mitsyanko 42798f44cb0SIgor Mitsyanko out: 42898f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 42998f44cb0SIgor Mitsyanko return ret; 43098f44cb0SIgor Mitsyanko } 43198f44cb0SIgor Mitsyanko 43298f44cb0SIgor Mitsyanko static void 43398f44cb0SIgor Mitsyanko qtnf_sta_info_parse_basic_counters(struct station_info *sinfo, 43498f44cb0SIgor Mitsyanko const struct qlink_sta_stat_basic_counters *counters) 43598f44cb0SIgor Mitsyanko { 43698f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES) | 43798f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_TX_BYTES); 43898f44cb0SIgor Mitsyanko sinfo->rx_bytes = get_unaligned_le64(&counters->rx_bytes); 43998f44cb0SIgor Mitsyanko sinfo->tx_bytes = get_unaligned_le64(&counters->tx_bytes); 44098f44cb0SIgor Mitsyanko 44198f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) | 44298f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_TX_PACKETS) | 44398f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_BEACON_RX); 44498f44cb0SIgor Mitsyanko sinfo->rx_packets = get_unaligned_le32(&counters->rx_packets); 44598f44cb0SIgor Mitsyanko sinfo->tx_packets = get_unaligned_le32(&counters->tx_packets); 44698f44cb0SIgor Mitsyanko sinfo->rx_beacon = get_unaligned_le64(&counters->rx_beacons); 44798f44cb0SIgor Mitsyanko 44898f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_RX_DROP_MISC) | 44998f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_TX_FAILED); 45098f44cb0SIgor Mitsyanko sinfo->rx_dropped_misc = get_unaligned_le32(&counters->rx_dropped); 45198f44cb0SIgor Mitsyanko sinfo->tx_failed = get_unaligned_le32(&counters->tx_failed); 45298f44cb0SIgor Mitsyanko } 45398f44cb0SIgor Mitsyanko 45498f44cb0SIgor Mitsyanko static void 45598f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(struct rate_info *rate_dst, 45698f44cb0SIgor Mitsyanko const struct qlink_sta_info_rate *rate_src) 45798f44cb0SIgor Mitsyanko { 45898f44cb0SIgor Mitsyanko rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10; 45998f44cb0SIgor Mitsyanko 46098f44cb0SIgor Mitsyanko rate_dst->mcs = rate_src->mcs; 46198f44cb0SIgor Mitsyanko rate_dst->nss = rate_src->nss; 46298f44cb0SIgor Mitsyanko rate_dst->flags = 0; 46398f44cb0SIgor Mitsyanko 46498f44cb0SIgor Mitsyanko switch (rate_src->bw) { 46598f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_5: 46698f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_5; 46798f44cb0SIgor Mitsyanko break; 46898f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_10: 46998f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_10; 47098f44cb0SIgor Mitsyanko break; 47198f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_20: 47298f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_20; 47398f44cb0SIgor Mitsyanko break; 47498f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_40: 47598f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_40; 47698f44cb0SIgor Mitsyanko break; 47798f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_80: 47898f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_80; 47998f44cb0SIgor Mitsyanko break; 48098f44cb0SIgor Mitsyanko case QLINK_STA_INFO_RATE_BW_160: 48198f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_160; 48298f44cb0SIgor Mitsyanko break; 48398f44cb0SIgor Mitsyanko default: 48498f44cb0SIgor Mitsyanko rate_dst->bw = 0; 48598f44cb0SIgor Mitsyanko break; 48698f44cb0SIgor Mitsyanko } 48798f44cb0SIgor Mitsyanko 48898f44cb0SIgor Mitsyanko if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HT_MCS) 48998f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_MCS; 49098f44cb0SIgor Mitsyanko else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS) 49198f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS; 49298f44cb0SIgor Mitsyanko } 49398f44cb0SIgor Mitsyanko 49498f44cb0SIgor Mitsyanko static void 49598f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst, 49698f44cb0SIgor Mitsyanko const struct qlink_sta_info_state *src) 49798f44cb0SIgor Mitsyanko { 49898f44cb0SIgor Mitsyanko u32 mask, value; 49998f44cb0SIgor Mitsyanko 50098f44cb0SIgor Mitsyanko dst->mask = 0; 50198f44cb0SIgor Mitsyanko dst->set = 0; 50298f44cb0SIgor Mitsyanko 50398f44cb0SIgor Mitsyanko mask = le32_to_cpu(src->mask); 50498f44cb0SIgor Mitsyanko value = le32_to_cpu(src->value); 50598f44cb0SIgor Mitsyanko 50698f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHORIZED) { 50798f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHORIZED); 50898f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHORIZED) 50998f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); 51098f44cb0SIgor Mitsyanko } 51198f44cb0SIgor Mitsyanko 51298f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_SHORT_PREAMBLE) { 51398f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 51498f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_SHORT_PREAMBLE) 51598f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 51698f44cb0SIgor Mitsyanko } 51798f44cb0SIgor Mitsyanko 51898f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_WME) { 51998f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_WME); 52098f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_WME) 52198f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_WME); 52298f44cb0SIgor Mitsyanko } 52398f44cb0SIgor Mitsyanko 52498f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_MFP) { 52598f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_MFP); 52698f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_MFP) 52798f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_MFP); 52898f44cb0SIgor Mitsyanko } 52998f44cb0SIgor Mitsyanko 53098f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHENTICATED) { 53198f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 53298f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHENTICATED) 53398f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 53498f44cb0SIgor Mitsyanko } 53598f44cb0SIgor Mitsyanko 53698f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_TDLS_PEER) { 53798f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); 53898f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_TDLS_PEER) 53998f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_TDLS_PEER); 54098f44cb0SIgor Mitsyanko } 54198f44cb0SIgor Mitsyanko 54298f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_ASSOCIATED) { 54398f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); 54498f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_ASSOCIATED) 54598f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); 54698f44cb0SIgor Mitsyanko } 54798f44cb0SIgor Mitsyanko } 54898f44cb0SIgor Mitsyanko 54998f44cb0SIgor Mitsyanko static void 55098f44cb0SIgor Mitsyanko qtnf_sta_info_parse_generic_info(struct station_info *sinfo, 55198f44cb0SIgor Mitsyanko const struct qlink_sta_info_generic *info) 55298f44cb0SIgor Mitsyanko { 55398f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME) | 55498f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_INACTIVE_TIME); 55598f44cb0SIgor Mitsyanko sinfo->connected_time = get_unaligned_le32(&info->connected_time); 55698f44cb0SIgor Mitsyanko sinfo->inactive_time = get_unaligned_le32(&info->inactive_time); 55798f44cb0SIgor Mitsyanko 55898f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) | 55998f44cb0SIgor Mitsyanko BIT(NL80211_STA_INFO_SIGNAL_AVG); 56098f44cb0SIgor Mitsyanko sinfo->signal = info->rssi - 120; 56198f44cb0SIgor Mitsyanko sinfo->signal_avg = info->rssi_avg - QLINK_RSSI_OFFSET; 56298f44cb0SIgor Mitsyanko 56398f44cb0SIgor Mitsyanko if (info->rx_rate.rate) { 56498f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); 56598f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->rxrate, &info->rx_rate); 56698f44cb0SIgor Mitsyanko } 56798f44cb0SIgor Mitsyanko 56898f44cb0SIgor Mitsyanko if (info->tx_rate.rate) { 56998f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); 57098f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->txrate, &info->tx_rate); 57198f44cb0SIgor Mitsyanko } 57298f44cb0SIgor Mitsyanko 57398f44cb0SIgor Mitsyanko sinfo->filled |= BIT(NL80211_STA_INFO_STA_FLAGS); 57498f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(&sinfo->sta_flags, &info->state); 57598f44cb0SIgor Mitsyanko } 57698f44cb0SIgor Mitsyanko 57798f44cb0SIgor Mitsyanko static int qtnf_cmd_sta_info_parse(struct station_info *sinfo, 57898f44cb0SIgor Mitsyanko const u8 *payload, size_t payload_size) 57998f44cb0SIgor Mitsyanko { 58098f44cb0SIgor Mitsyanko const struct qlink_sta_stat_basic_counters *counters; 58198f44cb0SIgor Mitsyanko const struct qlink_sta_info_generic *sta_info; 58298f44cb0SIgor Mitsyanko u16 tlv_type; 58398f44cb0SIgor Mitsyanko u16 tlv_value_len; 58498f44cb0SIgor Mitsyanko size_t tlv_full_len; 58598f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 58698f44cb0SIgor Mitsyanko 58798f44cb0SIgor Mitsyanko sinfo->filled = 0; 58898f44cb0SIgor Mitsyanko 58998f44cb0SIgor Mitsyanko tlv = (const struct qlink_tlv_hdr *)payload; 59098f44cb0SIgor Mitsyanko while (payload_size >= sizeof(struct qlink_tlv_hdr)) { 59198f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 59298f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 59398f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 59498f44cb0SIgor Mitsyanko if (tlv_full_len > payload_size) { 59598f44cb0SIgor Mitsyanko pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 59698f44cb0SIgor Mitsyanko tlv_type, tlv_value_len); 59798f44cb0SIgor Mitsyanko return -EINVAL; 59898f44cb0SIgor Mitsyanko } 59998f44cb0SIgor Mitsyanko switch (tlv_type) { 60098f44cb0SIgor Mitsyanko case QTN_TLV_ID_STA_BASIC_COUNTERS: 60198f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len < sizeof(*counters))) { 60298f44cb0SIgor Mitsyanko pr_err("invalid TLV size %.4X: %u\n", 60398f44cb0SIgor Mitsyanko tlv_type, tlv_value_len); 60498f44cb0SIgor Mitsyanko break; 60598f44cb0SIgor Mitsyanko } 60698f44cb0SIgor Mitsyanko 60798f44cb0SIgor Mitsyanko counters = (void *)tlv->val; 60898f44cb0SIgor Mitsyanko qtnf_sta_info_parse_basic_counters(sinfo, counters); 60998f44cb0SIgor Mitsyanko break; 61098f44cb0SIgor Mitsyanko case QTN_TLV_ID_STA_GENERIC_INFO: 61198f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len < sizeof(*sta_info))) 61298f44cb0SIgor Mitsyanko break; 61398f44cb0SIgor Mitsyanko 61498f44cb0SIgor Mitsyanko sta_info = (void *)tlv->val; 61598f44cb0SIgor Mitsyanko qtnf_sta_info_parse_generic_info(sinfo, sta_info); 61698f44cb0SIgor Mitsyanko break; 61798f44cb0SIgor Mitsyanko default: 61898f44cb0SIgor Mitsyanko pr_warn("unexpected TLV type: %.4X\n", tlv_type); 61998f44cb0SIgor Mitsyanko break; 62098f44cb0SIgor Mitsyanko } 62198f44cb0SIgor Mitsyanko payload_size -= tlv_full_len; 62298f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 62398f44cb0SIgor Mitsyanko } 62498f44cb0SIgor Mitsyanko 62598f44cb0SIgor Mitsyanko if (payload_size) { 62698f44cb0SIgor Mitsyanko pr_warn("malformed TLV buf; bytes left: %zu\n", payload_size); 62798f44cb0SIgor Mitsyanko return -EINVAL; 62898f44cb0SIgor Mitsyanko } 62998f44cb0SIgor Mitsyanko 63098f44cb0SIgor Mitsyanko return 0; 63198f44cb0SIgor Mitsyanko } 63298f44cb0SIgor Mitsyanko 63398f44cb0SIgor Mitsyanko int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac, 63498f44cb0SIgor Mitsyanko struct station_info *sinfo) 63598f44cb0SIgor Mitsyanko { 63698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 63798f44cb0SIgor Mitsyanko struct qlink_cmd_get_sta_info *cmd; 63898f44cb0SIgor Mitsyanko const struct qlink_resp_get_sta_info *resp; 63998f44cb0SIgor Mitsyanko size_t var_resp_len; 64098f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 64198f44cb0SIgor Mitsyanko int ret = 0; 64298f44cb0SIgor Mitsyanko 64398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 64498f44cb0SIgor Mitsyanko QLINK_CMD_GET_STA_INFO, 64598f44cb0SIgor Mitsyanko sizeof(*cmd)); 64698f44cb0SIgor Mitsyanko 64798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 64898f44cb0SIgor Mitsyanko return -ENOMEM; 64998f44cb0SIgor Mitsyanko 65098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 65198f44cb0SIgor Mitsyanko 65298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_get_sta_info *)cmd_skb->data; 65398f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, sta_mac); 65498f44cb0SIgor Mitsyanko 65598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 65698f44cb0SIgor Mitsyanko &res_code, sizeof(*resp), 65798f44cb0SIgor Mitsyanko &var_resp_len); 65898f44cb0SIgor Mitsyanko 65998f44cb0SIgor Mitsyanko if (unlikely(ret)) 66098f44cb0SIgor Mitsyanko goto out; 66198f44cb0SIgor Mitsyanko 66298f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 66398f44cb0SIgor Mitsyanko switch (res_code) { 66498f44cb0SIgor Mitsyanko case QLINK_CMD_RESULT_ENOTFOUND: 66598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: %pM STA not found\n", 66698f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, sta_mac); 66798f44cb0SIgor Mitsyanko ret = -ENOENT; 66898f44cb0SIgor Mitsyanko break; 66998f44cb0SIgor Mitsyanko default: 67098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: can't get info for %pM: %u\n", 67198f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, sta_mac, res_code); 67298f44cb0SIgor Mitsyanko ret = -EFAULT; 67398f44cb0SIgor Mitsyanko break; 67498f44cb0SIgor Mitsyanko } 67598f44cb0SIgor Mitsyanko goto out; 67698f44cb0SIgor Mitsyanko } 67798f44cb0SIgor Mitsyanko 67898f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_sta_info *)resp_skb->data; 67998f44cb0SIgor Mitsyanko 68098f44cb0SIgor Mitsyanko if (unlikely(!ether_addr_equal(sta_mac, resp->sta_addr))) { 68198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: wrong mac in reply: %pM != %pM\n", 68298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, resp->sta_addr, sta_mac); 68398f44cb0SIgor Mitsyanko ret = -EINVAL; 68498f44cb0SIgor Mitsyanko goto out; 68598f44cb0SIgor Mitsyanko } 68698f44cb0SIgor Mitsyanko 68798f44cb0SIgor Mitsyanko ret = qtnf_cmd_sta_info_parse(sinfo, resp->info, var_resp_len); 68898f44cb0SIgor Mitsyanko 68998f44cb0SIgor Mitsyanko out: 69098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 69198f44cb0SIgor Mitsyanko consume_skb(resp_skb); 69298f44cb0SIgor Mitsyanko 69398f44cb0SIgor Mitsyanko return ret; 69498f44cb0SIgor Mitsyanko } 69598f44cb0SIgor Mitsyanko 69698f44cb0SIgor Mitsyanko static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif, 69798f44cb0SIgor Mitsyanko enum nl80211_iftype iftype, 69898f44cb0SIgor Mitsyanko u8 *mac_addr, 69998f44cb0SIgor Mitsyanko enum qlink_cmd_type cmd_type) 70098f44cb0SIgor Mitsyanko { 70198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 70298f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 70398f44cb0SIgor Mitsyanko const struct qlink_resp_manage_intf *resp; 70498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 70598f44cb0SIgor Mitsyanko int ret = 0; 70698f44cb0SIgor Mitsyanko 70798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 70898f44cb0SIgor Mitsyanko cmd_type, 70998f44cb0SIgor Mitsyanko sizeof(*cmd)); 71098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 71198f44cb0SIgor Mitsyanko return -ENOMEM; 71298f44cb0SIgor Mitsyanko 71398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 71498f44cb0SIgor Mitsyanko 71598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 71698f44cb0SIgor Mitsyanko 71798f44cb0SIgor Mitsyanko switch (iftype) { 71898f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 71998f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 72098f44cb0SIgor Mitsyanko break; 72198f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 72298f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 72398f44cb0SIgor Mitsyanko break; 72498f44cb0SIgor Mitsyanko default: 72598f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: unsupported type %d\n", vif->mac->macid, 72698f44cb0SIgor Mitsyanko vif->vifid, iftype); 72798f44cb0SIgor Mitsyanko ret = -EINVAL; 72898f44cb0SIgor Mitsyanko goto out; 72998f44cb0SIgor Mitsyanko } 73098f44cb0SIgor Mitsyanko 73198f44cb0SIgor Mitsyanko if (mac_addr) 73298f44cb0SIgor Mitsyanko ether_addr_copy(cmd->intf_info.mac_addr, mac_addr); 73398f44cb0SIgor Mitsyanko else 73498f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 73598f44cb0SIgor Mitsyanko 73698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 73798f44cb0SIgor Mitsyanko &res_code, sizeof(*resp), NULL); 73898f44cb0SIgor Mitsyanko 73998f44cb0SIgor Mitsyanko if (unlikely(ret)) 74098f44cb0SIgor Mitsyanko goto out; 74198f44cb0SIgor Mitsyanko 74298f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 74398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD %d failed: %u\n", vif->mac->macid, 74498f44cb0SIgor Mitsyanko vif->vifid, cmd_type, res_code); 74598f44cb0SIgor Mitsyanko ret = -EFAULT; 74698f44cb0SIgor Mitsyanko goto out; 74798f44cb0SIgor Mitsyanko } 74898f44cb0SIgor Mitsyanko 74998f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_manage_intf *)resp_skb->data; 75098f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, resp->intf_info.mac_addr); 75198f44cb0SIgor Mitsyanko 75298f44cb0SIgor Mitsyanko out: 75398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 75498f44cb0SIgor Mitsyanko consume_skb(resp_skb); 75598f44cb0SIgor Mitsyanko 75698f44cb0SIgor Mitsyanko return ret; 75798f44cb0SIgor Mitsyanko } 75898f44cb0SIgor Mitsyanko 75998f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, 76098f44cb0SIgor Mitsyanko enum nl80211_iftype iftype, u8 *mac_addr) 76198f44cb0SIgor Mitsyanko { 76298f44cb0SIgor Mitsyanko return qtnf_cmd_send_add_change_intf(vif, iftype, mac_addr, 76398f44cb0SIgor Mitsyanko QLINK_CMD_ADD_INTF); 76498f44cb0SIgor Mitsyanko } 76598f44cb0SIgor Mitsyanko 76698f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif, 76798f44cb0SIgor Mitsyanko enum nl80211_iftype iftype, u8 *mac_addr) 76898f44cb0SIgor Mitsyanko { 76998f44cb0SIgor Mitsyanko return qtnf_cmd_send_add_change_intf(vif, iftype, mac_addr, 77098f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_INTF); 77198f44cb0SIgor Mitsyanko } 77298f44cb0SIgor Mitsyanko 77398f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_intf(struct qtnf_vif *vif) 77498f44cb0SIgor Mitsyanko { 77598f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 77698f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 77798f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 77898f44cb0SIgor Mitsyanko int ret = 0; 77998f44cb0SIgor Mitsyanko 78098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 78198f44cb0SIgor Mitsyanko QLINK_CMD_DEL_INTF, 78298f44cb0SIgor Mitsyanko sizeof(*cmd)); 78398f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 78498f44cb0SIgor Mitsyanko return -ENOMEM; 78598f44cb0SIgor Mitsyanko 78698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 78798f44cb0SIgor Mitsyanko 78898f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 78998f44cb0SIgor Mitsyanko 79098f44cb0SIgor Mitsyanko switch (vif->wdev.iftype) { 79198f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 79298f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 79398f44cb0SIgor Mitsyanko break; 79498f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 79598f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 79698f44cb0SIgor Mitsyanko break; 79798f44cb0SIgor Mitsyanko default: 79898f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported iftype %d\n", vif->mac->macid, 79998f44cb0SIgor Mitsyanko vif->vifid, vif->wdev.iftype); 80098f44cb0SIgor Mitsyanko ret = -EINVAL; 80198f44cb0SIgor Mitsyanko goto out; 80298f44cb0SIgor Mitsyanko } 80398f44cb0SIgor Mitsyanko 80498f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 80598f44cb0SIgor Mitsyanko 80698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 80798f44cb0SIgor Mitsyanko 80898f44cb0SIgor Mitsyanko if (unlikely(ret)) 80998f44cb0SIgor Mitsyanko goto out; 81098f44cb0SIgor Mitsyanko 81198f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 81298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 81398f44cb0SIgor Mitsyanko vif->vifid, res_code); 81498f44cb0SIgor Mitsyanko ret = -EFAULT; 81598f44cb0SIgor Mitsyanko goto out; 81698f44cb0SIgor Mitsyanko } 81798f44cb0SIgor Mitsyanko 81898f44cb0SIgor Mitsyanko out: 81998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 82098f44cb0SIgor Mitsyanko return ret; 82198f44cb0SIgor Mitsyanko } 82298f44cb0SIgor Mitsyanko 8234dd07d2bSSergey Matyukevich static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags) 8244dd07d2bSSergey Matyukevich { 8254dd07d2bSSergey Matyukevich u32 flags = 0; 8264dd07d2bSSergey Matyukevich 8274dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_OFDM) 8284dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_OFDM; 8294dd07d2bSSergey Matyukevich 8304dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_CCK) 8314dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_CCK; 8324dd07d2bSSergey Matyukevich 8334dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_INDOOR) 8344dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_INDOOR; 8354dd07d2bSSergey Matyukevich 8364dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_OUTDOOR) 8374dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_OUTDOOR; 8384dd07d2bSSergey Matyukevich 8394dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_DFS) 8404dd07d2bSSergey Matyukevich flags |= NL80211_RRF_DFS; 8414dd07d2bSSergey Matyukevich 8424dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_PTP_ONLY) 8434dd07d2bSSergey Matyukevich flags |= NL80211_RRF_PTP_ONLY; 8444dd07d2bSSergey Matyukevich 8454dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_PTMP_ONLY) 8464dd07d2bSSergey Matyukevich flags |= NL80211_RRF_PTMP_ONLY; 8474dd07d2bSSergey Matyukevich 8484dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_IR) 8494dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_IR; 8504dd07d2bSSergey Matyukevich 8514dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_AUTO_BW) 8524dd07d2bSSergey Matyukevich flags |= NL80211_RRF_AUTO_BW; 8534dd07d2bSSergey Matyukevich 8544dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_IR_CONCURRENT) 8554dd07d2bSSergey Matyukevich flags |= NL80211_RRF_IR_CONCURRENT; 8564dd07d2bSSergey Matyukevich 8574dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_HT40MINUS) 8584dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_HT40MINUS; 8594dd07d2bSSergey Matyukevich 8604dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_HT40PLUS) 8614dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_HT40PLUS; 8624dd07d2bSSergey Matyukevich 8634dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_80MHZ) 8644dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_80MHZ; 8654dd07d2bSSergey Matyukevich 8664dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_160MHZ) 8674dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_160MHZ; 8684dd07d2bSSergey Matyukevich 8694dd07d2bSSergey Matyukevich return flags; 8704dd07d2bSSergey Matyukevich } 8714dd07d2bSSergey Matyukevich 87298f44cb0SIgor Mitsyanko static int 87398f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, 8744dd07d2bSSergey Matyukevich const struct qlink_resp_get_hw_info *resp, 8754dd07d2bSSergey Matyukevich size_t info_len) 87698f44cb0SIgor Mitsyanko { 87798f44cb0SIgor Mitsyanko struct qtnf_hw_info *hwinfo = &bus->hw_info; 8784dd07d2bSSergey Matyukevich const struct qlink_tlv_hdr *tlv; 8794dd07d2bSSergey Matyukevich const struct qlink_tlv_reg_rule *tlv_rule; 8804dd07d2bSSergey Matyukevich struct ieee80211_reg_rule *rule; 8814dd07d2bSSergey Matyukevich u16 tlv_type; 8824dd07d2bSSergey Matyukevich u16 tlv_value_len; 8834dd07d2bSSergey Matyukevich unsigned int rule_idx = 0; 8844dd07d2bSSergey Matyukevich 8854dd07d2bSSergey Matyukevich if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) 8864dd07d2bSSergey Matyukevich return -E2BIG; 8874dd07d2bSSergey Matyukevich 8884dd07d2bSSergey Matyukevich hwinfo->rd = kzalloc(sizeof(*hwinfo->rd) 8894dd07d2bSSergey Matyukevich + sizeof(struct ieee80211_reg_rule) 8904dd07d2bSSergey Matyukevich * resp->n_reg_rules, GFP_KERNEL); 8914dd07d2bSSergey Matyukevich 8924dd07d2bSSergey Matyukevich if (!hwinfo->rd) 8934dd07d2bSSergey Matyukevich return -ENOMEM; 89498f44cb0SIgor Mitsyanko 89598f44cb0SIgor Mitsyanko hwinfo->num_mac = resp->num_mac; 89698f44cb0SIgor Mitsyanko hwinfo->mac_bitmap = resp->mac_bitmap; 89798f44cb0SIgor Mitsyanko hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); 89898f44cb0SIgor Mitsyanko hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver); 89998f44cb0SIgor Mitsyanko hwinfo->total_tx_chain = resp->total_tx_chain; 90098f44cb0SIgor Mitsyanko hwinfo->total_rx_chain = resp->total_rx_chain; 90198f44cb0SIgor Mitsyanko hwinfo->hw_capab = le32_to_cpu(resp->hw_capab); 9024dd07d2bSSergey Matyukevich hwinfo->rd->n_reg_rules = resp->n_reg_rules; 9034dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[0] = resp->alpha2[0]; 9044dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[1] = resp->alpha2[1]; 9054dd07d2bSSergey Matyukevich 9064dd07d2bSSergey Matyukevich switch (resp->dfs_region) { 9074dd07d2bSSergey Matyukevich case QLINK_DFS_FCC: 9084dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_FCC; 9094dd07d2bSSergey Matyukevich break; 9104dd07d2bSSergey Matyukevich case QLINK_DFS_ETSI: 9114dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_ETSI; 9124dd07d2bSSergey Matyukevich break; 9134dd07d2bSSergey Matyukevich case QLINK_DFS_JP: 9144dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_JP; 9154dd07d2bSSergey Matyukevich break; 9164dd07d2bSSergey Matyukevich case QLINK_DFS_UNSET: 9174dd07d2bSSergey Matyukevich default: 9184dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_UNSET; 9194dd07d2bSSergey Matyukevich break; 9204dd07d2bSSergey Matyukevich } 9214dd07d2bSSergey Matyukevich 9224dd07d2bSSergey Matyukevich tlv = (const struct qlink_tlv_hdr *)resp->info; 9234dd07d2bSSergey Matyukevich 9244dd07d2bSSergey Matyukevich while (info_len >= sizeof(*tlv)) { 9254dd07d2bSSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 9264dd07d2bSSergey Matyukevich tlv_value_len = le16_to_cpu(tlv->len); 9274dd07d2bSSergey Matyukevich 9284dd07d2bSSergey Matyukevich if (tlv_value_len + sizeof(*tlv) > info_len) { 9294dd07d2bSSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 9304dd07d2bSSergey Matyukevich tlv_type, tlv_value_len); 9314dd07d2bSSergey Matyukevich return -EINVAL; 9324dd07d2bSSergey Matyukevich } 9334dd07d2bSSergey Matyukevich 9344dd07d2bSSergey Matyukevich switch (tlv_type) { 9354dd07d2bSSergey Matyukevich case QTN_TLV_ID_REG_RULE: 9364dd07d2bSSergey Matyukevich if (rule_idx >= resp->n_reg_rules) { 9374dd07d2bSSergey Matyukevich pr_warn("unexpected number of rules: %u\n", 9384dd07d2bSSergey Matyukevich resp->n_reg_rules); 9394dd07d2bSSergey Matyukevich return -EINVAL; 9404dd07d2bSSergey Matyukevich } 9414dd07d2bSSergey Matyukevich 9424dd07d2bSSergey Matyukevich if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) { 9434dd07d2bSSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 9444dd07d2bSSergey Matyukevich tlv_type, tlv_value_len); 9454dd07d2bSSergey Matyukevich return -EINVAL; 9464dd07d2bSSergey Matyukevich } 9474dd07d2bSSergey Matyukevich 9484dd07d2bSSergey Matyukevich tlv_rule = (const struct qlink_tlv_reg_rule *)tlv; 9494dd07d2bSSergey Matyukevich rule = &hwinfo->rd->reg_rules[rule_idx++]; 9504dd07d2bSSergey Matyukevich 9514dd07d2bSSergey Matyukevich rule->freq_range.start_freq_khz = 9524dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->start_freq_khz); 9534dd07d2bSSergey Matyukevich rule->freq_range.end_freq_khz = 9544dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->end_freq_khz); 9554dd07d2bSSergey Matyukevich rule->freq_range.max_bandwidth_khz = 9564dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_bandwidth_khz); 9574dd07d2bSSergey Matyukevich rule->power_rule.max_antenna_gain = 9584dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_antenna_gain); 9594dd07d2bSSergey Matyukevich rule->power_rule.max_eirp = 9604dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_eirp); 9614dd07d2bSSergey Matyukevich rule->dfs_cac_ms = 9624dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->dfs_cac_ms); 9634dd07d2bSSergey Matyukevich rule->flags = qtnf_cmd_resp_reg_rule_flags_parse( 9644dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->flags)); 9654dd07d2bSSergey Matyukevich break; 9664dd07d2bSSergey Matyukevich default: 9674dd07d2bSSergey Matyukevich break; 9684dd07d2bSSergey Matyukevich } 9694dd07d2bSSergey Matyukevich 9704dd07d2bSSergey Matyukevich info_len -= tlv_value_len + sizeof(*tlv); 9714dd07d2bSSergey Matyukevich tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 9724dd07d2bSSergey Matyukevich } 9734dd07d2bSSergey Matyukevich 9744dd07d2bSSergey Matyukevich if (rule_idx != resp->n_reg_rules) { 9754dd07d2bSSergey Matyukevich pr_warn("unexpected number of rules: expected %u got %u\n", 9764dd07d2bSSergey Matyukevich resp->n_reg_rules, rule_idx); 9774dd07d2bSSergey Matyukevich kfree(hwinfo->rd); 9784dd07d2bSSergey Matyukevich hwinfo->rd = NULL; 9794dd07d2bSSergey Matyukevich return -EINVAL; 9804dd07d2bSSergey Matyukevich } 98198f44cb0SIgor Mitsyanko 98298f44cb0SIgor Mitsyanko pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u\n", 98398f44cb0SIgor Mitsyanko hwinfo->fw_ver, hwinfo->mac_bitmap, 9844dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1], 98598f44cb0SIgor Mitsyanko hwinfo->total_tx_chain, hwinfo->total_rx_chain); 98698f44cb0SIgor Mitsyanko 98798f44cb0SIgor Mitsyanko return 0; 98898f44cb0SIgor Mitsyanko } 98998f44cb0SIgor Mitsyanko 99098f44cb0SIgor Mitsyanko static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, 99198f44cb0SIgor Mitsyanko const u8 *tlv_buf, size_t tlv_buf_size) 99298f44cb0SIgor Mitsyanko { 99398f44cb0SIgor Mitsyanko struct ieee80211_iface_limit *limits = NULL; 99498f44cb0SIgor Mitsyanko const struct qlink_iface_limit *limit_record; 99598f44cb0SIgor Mitsyanko size_t record_count = 0, rec = 0; 99641c8fa0cSSergey Matyukevich u16 tlv_type, tlv_value_len; 99798f44cb0SIgor Mitsyanko struct qlink_iface_comb_num *comb; 99898f44cb0SIgor Mitsyanko size_t tlv_full_len; 99998f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 100098f44cb0SIgor Mitsyanko 100198f44cb0SIgor Mitsyanko mac->macinfo.n_limits = 0; 100298f44cb0SIgor Mitsyanko 100398f44cb0SIgor Mitsyanko tlv = (const struct qlink_tlv_hdr *)tlv_buf; 100498f44cb0SIgor Mitsyanko while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) { 100598f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 100698f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 100798f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 100898f44cb0SIgor Mitsyanko if (tlv_full_len > tlv_buf_size) { 100998f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 101098f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 101198f44cb0SIgor Mitsyanko return -EINVAL; 101298f44cb0SIgor Mitsyanko } 101398f44cb0SIgor Mitsyanko 101498f44cb0SIgor Mitsyanko switch (tlv_type) { 101598f44cb0SIgor Mitsyanko case QTN_TLV_ID_NUM_IFACE_COMB: 101698f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len != sizeof(*comb))) 101798f44cb0SIgor Mitsyanko return -EINVAL; 101898f44cb0SIgor Mitsyanko 101998f44cb0SIgor Mitsyanko comb = (void *)tlv->val; 102098f44cb0SIgor Mitsyanko record_count = le16_to_cpu(comb->iface_comb_num); 102198f44cb0SIgor Mitsyanko 102298f44cb0SIgor Mitsyanko mac->macinfo.n_limits = record_count; 102398f44cb0SIgor Mitsyanko /* free earlier iface limits memory */ 102498f44cb0SIgor Mitsyanko kfree(mac->macinfo.limits); 102598f44cb0SIgor Mitsyanko mac->macinfo.limits = 102698f44cb0SIgor Mitsyanko kzalloc(sizeof(*mac->macinfo.limits) * 102798f44cb0SIgor Mitsyanko record_count, GFP_KERNEL); 102898f44cb0SIgor Mitsyanko 102998f44cb0SIgor Mitsyanko if (unlikely(!mac->macinfo.limits)) 103098f44cb0SIgor Mitsyanko return -ENOMEM; 103198f44cb0SIgor Mitsyanko 103298f44cb0SIgor Mitsyanko limits = mac->macinfo.limits; 103398f44cb0SIgor Mitsyanko break; 103498f44cb0SIgor Mitsyanko case QTN_TLV_ID_IFACE_LIMIT: 103598f44cb0SIgor Mitsyanko if (unlikely(!limits)) { 103698f44cb0SIgor Mitsyanko pr_warn("MAC%u: limits are not inited\n", 103798f44cb0SIgor Mitsyanko mac->macid); 103898f44cb0SIgor Mitsyanko return -EINVAL; 103998f44cb0SIgor Mitsyanko } 104098f44cb0SIgor Mitsyanko 104198f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len != sizeof(*limit_record))) { 104298f44cb0SIgor Mitsyanko pr_warn("MAC%u: record size mismatch\n", 104398f44cb0SIgor Mitsyanko mac->macid); 104498f44cb0SIgor Mitsyanko return -EINVAL; 104598f44cb0SIgor Mitsyanko } 104698f44cb0SIgor Mitsyanko 104798f44cb0SIgor Mitsyanko limit_record = (void *)tlv->val; 104898f44cb0SIgor Mitsyanko limits[rec].max = le16_to_cpu(limit_record->max_num); 104941c8fa0cSSergey Matyukevich limits[rec].types = qlink_iface_type_to_nl_mask( 105041c8fa0cSSergey Matyukevich le16_to_cpu(limit_record->type)); 105141c8fa0cSSergey Matyukevich 105241c8fa0cSSergey Matyukevich /* supported modes: STA, AP */ 105398f44cb0SIgor Mitsyanko limits[rec].types &= BIT(NL80211_IFTYPE_AP) | 1054805b28c0SSergey Matyukevich BIT(NL80211_IFTYPE_AP_VLAN) | 105598f44cb0SIgor Mitsyanko BIT(NL80211_IFTYPE_STATION); 105698f44cb0SIgor Mitsyanko 105798f44cb0SIgor Mitsyanko pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid, 105898f44cb0SIgor Mitsyanko limits[rec].max, limits[rec].types); 105998f44cb0SIgor Mitsyanko 106098f44cb0SIgor Mitsyanko if (limits[rec].types) 106198f44cb0SIgor Mitsyanko rec++; 106298f44cb0SIgor Mitsyanko break; 106398f44cb0SIgor Mitsyanko default: 106498f44cb0SIgor Mitsyanko break; 106598f44cb0SIgor Mitsyanko } 1066805b28c0SSergey Matyukevich 106798f44cb0SIgor Mitsyanko tlv_buf_size -= tlv_full_len; 106898f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 106998f44cb0SIgor Mitsyanko } 107098f44cb0SIgor Mitsyanko 107198f44cb0SIgor Mitsyanko if (tlv_buf_size) { 107298f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 107398f44cb0SIgor Mitsyanko mac->macid, tlv_buf_size); 107498f44cb0SIgor Mitsyanko return -EINVAL; 107598f44cb0SIgor Mitsyanko } 107698f44cb0SIgor Mitsyanko 107798f44cb0SIgor Mitsyanko if (mac->macinfo.n_limits != rec) { 107898f44cb0SIgor Mitsyanko pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n", 107998f44cb0SIgor Mitsyanko mac->macid, mac->macinfo.n_limits, rec); 108098f44cb0SIgor Mitsyanko return -EINVAL; 108198f44cb0SIgor Mitsyanko } 108298f44cb0SIgor Mitsyanko 108398f44cb0SIgor Mitsyanko return 0; 108498f44cb0SIgor Mitsyanko } 108598f44cb0SIgor Mitsyanko 108698f44cb0SIgor Mitsyanko static void 108798f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, 108898f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp_info) 108998f44cb0SIgor Mitsyanko { 109098f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 109198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 109298f44cb0SIgor Mitsyanko 109398f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 109498f44cb0SIgor Mitsyanko 109598f44cb0SIgor Mitsyanko mac_info->bands_cap = resp_info->bands_cap; 109698f44cb0SIgor Mitsyanko mac_info->phymode_cap = resp_info->phymode_cap; 109798f44cb0SIgor Mitsyanko memcpy(&mac_info->dev_mac, &resp_info->dev_mac, 109898f44cb0SIgor Mitsyanko sizeof(mac_info->dev_mac)); 109998f44cb0SIgor Mitsyanko 110098f44cb0SIgor Mitsyanko ether_addr_copy(mac->macaddr, mac_info->dev_mac); 110198f44cb0SIgor Mitsyanko 110298f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 110398f44cb0SIgor Mitsyanko if (vif) 110498f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, mac->macaddr); 110598f44cb0SIgor Mitsyanko else 110698f44cb0SIgor Mitsyanko pr_err("could not get valid base vif\n"); 110798f44cb0SIgor Mitsyanko 110898f44cb0SIgor Mitsyanko mac_info->num_tx_chain = resp_info->num_tx_chain; 110998f44cb0SIgor Mitsyanko mac_info->num_rx_chain = resp_info->num_rx_chain; 111098f44cb0SIgor Mitsyanko 111198f44cb0SIgor Mitsyanko mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta); 111298f44cb0SIgor Mitsyanko mac_info->radar_detect_widths = 111398f44cb0SIgor Mitsyanko qlink_chan_width_mask_to_nl(le16_to_cpu( 111498f44cb0SIgor Mitsyanko resp_info->radar_detect_widths)); 111598f44cb0SIgor Mitsyanko 111698f44cb0SIgor Mitsyanko memcpy(&mac_info->ht_cap, &resp_info->ht_cap, sizeof(mac_info->ht_cap)); 111798f44cb0SIgor Mitsyanko memcpy(&mac_info->vht_cap, &resp_info->vht_cap, 111898f44cb0SIgor Mitsyanko sizeof(mac_info->vht_cap)); 111998f44cb0SIgor Mitsyanko } 112098f44cb0SIgor Mitsyanko 112198f44cb0SIgor Mitsyanko static int 112298f44cb0SIgor Mitsyanko qtnf_cmd_resp_fill_channels_info(struct ieee80211_supported_band *band, 112398f44cb0SIgor Mitsyanko struct qlink_resp_get_chan_info *resp, 112498f44cb0SIgor Mitsyanko size_t payload_len) 112598f44cb0SIgor Mitsyanko { 112698f44cb0SIgor Mitsyanko u16 tlv_type; 112798f44cb0SIgor Mitsyanko size_t tlv_len; 112898f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 112998f44cb0SIgor Mitsyanko const struct qlink_tlv_channel *qchan; 113098f44cb0SIgor Mitsyanko struct ieee80211_channel *chan; 113198f44cb0SIgor Mitsyanko unsigned int chidx = 0; 113298f44cb0SIgor Mitsyanko u32 qflags; 113398f44cb0SIgor Mitsyanko 11344dd07d2bSSergey Matyukevich if (band->channels) { 11354dd07d2bSSergey Matyukevich if (band->n_channels == resp->num_chans) { 11364dd07d2bSSergey Matyukevich memset(band->channels, 0, 11374dd07d2bSSergey Matyukevich sizeof(*band->channels) * band->n_channels); 11384dd07d2bSSergey Matyukevich } else { 113998f44cb0SIgor Mitsyanko kfree(band->channels); 11404dd07d2bSSergey Matyukevich band->n_channels = 0; 114198f44cb0SIgor Mitsyanko band->channels = NULL; 11424dd07d2bSSergey Matyukevich } 11434dd07d2bSSergey Matyukevich } 114498f44cb0SIgor Mitsyanko 114598f44cb0SIgor Mitsyanko band->n_channels = resp->num_chans; 114698f44cb0SIgor Mitsyanko if (band->n_channels == 0) 114798f44cb0SIgor Mitsyanko return 0; 114898f44cb0SIgor Mitsyanko 11494dd07d2bSSergey Matyukevich if (!band->channels) 11504dd07d2bSSergey Matyukevich band->channels = kcalloc(band->n_channels, sizeof(*chan), 11514dd07d2bSSergey Matyukevich GFP_KERNEL); 115298f44cb0SIgor Mitsyanko if (!band->channels) { 115398f44cb0SIgor Mitsyanko band->n_channels = 0; 115498f44cb0SIgor Mitsyanko return -ENOMEM; 115598f44cb0SIgor Mitsyanko } 115698f44cb0SIgor Mitsyanko 115798f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)resp->info; 115898f44cb0SIgor Mitsyanko 115998f44cb0SIgor Mitsyanko while (payload_len >= sizeof(*tlv)) { 116098f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 116198f44cb0SIgor Mitsyanko tlv_len = le16_to_cpu(tlv->len) + sizeof(*tlv); 116298f44cb0SIgor Mitsyanko 116398f44cb0SIgor Mitsyanko if (tlv_len > payload_len) { 116498f44cb0SIgor Mitsyanko pr_warn("malformed TLV 0x%.2X; LEN: %zu\n", 116598f44cb0SIgor Mitsyanko tlv_type, tlv_len); 116698f44cb0SIgor Mitsyanko goto error_ret; 116798f44cb0SIgor Mitsyanko } 116898f44cb0SIgor Mitsyanko 116998f44cb0SIgor Mitsyanko switch (tlv_type) { 117098f44cb0SIgor Mitsyanko case QTN_TLV_ID_CHANNEL: 117198f44cb0SIgor Mitsyanko if (unlikely(tlv_len != sizeof(*qchan))) { 117298f44cb0SIgor Mitsyanko pr_err("invalid channel TLV len %zu\n", 117398f44cb0SIgor Mitsyanko tlv_len); 117498f44cb0SIgor Mitsyanko goto error_ret; 117598f44cb0SIgor Mitsyanko } 117698f44cb0SIgor Mitsyanko 117798f44cb0SIgor Mitsyanko if (chidx == band->n_channels) { 117898f44cb0SIgor Mitsyanko pr_err("too many channel TLVs\n"); 117998f44cb0SIgor Mitsyanko goto error_ret; 118098f44cb0SIgor Mitsyanko } 118198f44cb0SIgor Mitsyanko 118298f44cb0SIgor Mitsyanko qchan = (const struct qlink_tlv_channel *)tlv; 118398f44cb0SIgor Mitsyanko chan = &band->channels[chidx++]; 118498f44cb0SIgor Mitsyanko qflags = le32_to_cpu(qchan->flags); 118598f44cb0SIgor Mitsyanko 118698f44cb0SIgor Mitsyanko chan->hw_value = le16_to_cpu(qchan->hw_value); 118798f44cb0SIgor Mitsyanko chan->band = band->band; 118898f44cb0SIgor Mitsyanko chan->center_freq = le16_to_cpu(qchan->center_freq); 118998f44cb0SIgor Mitsyanko chan->max_antenna_gain = (int)qchan->max_antenna_gain; 119098f44cb0SIgor Mitsyanko chan->max_power = (int)qchan->max_power; 119198f44cb0SIgor Mitsyanko chan->max_reg_power = (int)qchan->max_reg_power; 119298f44cb0SIgor Mitsyanko chan->beacon_found = qchan->beacon_found; 119398f44cb0SIgor Mitsyanko chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms); 119498f44cb0SIgor Mitsyanko chan->flags = 0; 119598f44cb0SIgor Mitsyanko 119698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_DISABLED) 119798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_DISABLED; 119898f44cb0SIgor Mitsyanko 119998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_IR) 120098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_IR; 120198f44cb0SIgor Mitsyanko 120298f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40PLUS) 120398f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40PLUS; 120498f44cb0SIgor Mitsyanko 120598f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40MINUS) 120698f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40MINUS; 120798f44cb0SIgor Mitsyanko 120898f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_OFDM) 120998f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_OFDM; 121098f44cb0SIgor Mitsyanko 121198f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_80MHZ) 121298f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_80MHZ; 121398f44cb0SIgor Mitsyanko 121498f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_160MHZ) 121598f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_160MHZ; 121698f44cb0SIgor Mitsyanko 121798f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_INDOOR_ONLY) 121898f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_INDOOR_ONLY; 121998f44cb0SIgor Mitsyanko 122098f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_IR_CONCURRENT) 122198f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_IR_CONCURRENT; 122298f44cb0SIgor Mitsyanko 122398f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_20MHZ) 122498f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_20MHZ; 122598f44cb0SIgor Mitsyanko 122698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_10MHZ) 122798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_10MHZ; 122898f44cb0SIgor Mitsyanko 122998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_RADAR) { 123098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_RADAR; 123198f44cb0SIgor Mitsyanko chan->dfs_state_entered = jiffies; 123298f44cb0SIgor Mitsyanko 123398f44cb0SIgor Mitsyanko if (qchan->dfs_state == QLINK_DFS_USABLE) 123498f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_USABLE; 123598f44cb0SIgor Mitsyanko else if (qchan->dfs_state == 123698f44cb0SIgor Mitsyanko QLINK_DFS_AVAILABLE) 123798f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_AVAILABLE; 123898f44cb0SIgor Mitsyanko else 123998f44cb0SIgor Mitsyanko chan->dfs_state = 124098f44cb0SIgor Mitsyanko NL80211_DFS_UNAVAILABLE; 124198f44cb0SIgor Mitsyanko } 124298f44cb0SIgor Mitsyanko 124398f44cb0SIgor Mitsyanko pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n", 124498f44cb0SIgor Mitsyanko chan->hw_value, chan->flags, chan->max_power, 124598f44cb0SIgor Mitsyanko chan->max_reg_power); 124698f44cb0SIgor Mitsyanko break; 124798f44cb0SIgor Mitsyanko default: 124898f44cb0SIgor Mitsyanko pr_warn("unknown TLV type: %#x\n", tlv_type); 124998f44cb0SIgor Mitsyanko break; 125098f44cb0SIgor Mitsyanko } 125198f44cb0SIgor Mitsyanko 125298f44cb0SIgor Mitsyanko payload_len -= tlv_len; 125398f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)((u8 *)tlv + tlv_len); 125498f44cb0SIgor Mitsyanko } 125598f44cb0SIgor Mitsyanko 125698f44cb0SIgor Mitsyanko if (payload_len) { 125798f44cb0SIgor Mitsyanko pr_err("malformed TLV buf; bytes left: %zu\n", payload_len); 125898f44cb0SIgor Mitsyanko goto error_ret; 125998f44cb0SIgor Mitsyanko } 126098f44cb0SIgor Mitsyanko 126198f44cb0SIgor Mitsyanko if (band->n_channels != chidx) { 126298f44cb0SIgor Mitsyanko pr_err("channel count mismatch: reported=%d, parsed=%d\n", 126398f44cb0SIgor Mitsyanko band->n_channels, chidx); 126498f44cb0SIgor Mitsyanko goto error_ret; 126598f44cb0SIgor Mitsyanko } 126698f44cb0SIgor Mitsyanko 126798f44cb0SIgor Mitsyanko return 0; 126898f44cb0SIgor Mitsyanko 126998f44cb0SIgor Mitsyanko error_ret: 127098f44cb0SIgor Mitsyanko kfree(band->channels); 127198f44cb0SIgor Mitsyanko band->channels = NULL; 127298f44cb0SIgor Mitsyanko band->n_channels = 0; 127398f44cb0SIgor Mitsyanko 127498f44cb0SIgor Mitsyanko return -EINVAL; 127598f44cb0SIgor Mitsyanko } 127698f44cb0SIgor Mitsyanko 127798f44cb0SIgor Mitsyanko static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, 127898f44cb0SIgor Mitsyanko const u8 *payload, size_t payload_len) 127998f44cb0SIgor Mitsyanko { 128098f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 128198f44cb0SIgor Mitsyanko struct qlink_tlv_frag_rts_thr *phy_thr; 128298f44cb0SIgor Mitsyanko struct qlink_tlv_rlimit *limit; 128398f44cb0SIgor Mitsyanko struct qlink_tlv_cclass *class; 128498f44cb0SIgor Mitsyanko u16 tlv_type; 128598f44cb0SIgor Mitsyanko u16 tlv_value_len; 128698f44cb0SIgor Mitsyanko size_t tlv_full_len; 128798f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 128898f44cb0SIgor Mitsyanko 128998f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 129098f44cb0SIgor Mitsyanko 129198f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)payload; 129298f44cb0SIgor Mitsyanko while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 129398f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 129498f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 129598f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 129698f44cb0SIgor Mitsyanko 129798f44cb0SIgor Mitsyanko if (tlv_full_len > payload_len) { 129898f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 129998f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 130098f44cb0SIgor Mitsyanko return -EINVAL; 130198f44cb0SIgor Mitsyanko } 130298f44cb0SIgor Mitsyanko 130398f44cb0SIgor Mitsyanko switch (tlv_type) { 130498f44cb0SIgor Mitsyanko case QTN_TLV_ID_FRAG_THRESH: 130598f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 130698f44cb0SIgor Mitsyanko mac_info->frag_thr = (u32)le16_to_cpu(phy_thr->thr); 130798f44cb0SIgor Mitsyanko break; 130898f44cb0SIgor Mitsyanko case QTN_TLV_ID_RTS_THRESH: 130998f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 131098f44cb0SIgor Mitsyanko mac_info->rts_thr = (u32)le16_to_cpu(phy_thr->thr); 131198f44cb0SIgor Mitsyanko break; 131298f44cb0SIgor Mitsyanko case QTN_TLV_ID_SRETRY_LIMIT: 131398f44cb0SIgor Mitsyanko limit = (void *)tlv; 131498f44cb0SIgor Mitsyanko mac_info->sretry_limit = limit->rlimit; 131598f44cb0SIgor Mitsyanko break; 131698f44cb0SIgor Mitsyanko case QTN_TLV_ID_LRETRY_LIMIT: 131798f44cb0SIgor Mitsyanko limit = (void *)tlv; 131898f44cb0SIgor Mitsyanko mac_info->lretry_limit = limit->rlimit; 131998f44cb0SIgor Mitsyanko break; 132098f44cb0SIgor Mitsyanko case QTN_TLV_ID_COVERAGE_CLASS: 132198f44cb0SIgor Mitsyanko class = (void *)tlv; 132298f44cb0SIgor Mitsyanko mac_info->coverage_class = class->cclass; 132398f44cb0SIgor Mitsyanko break; 132498f44cb0SIgor Mitsyanko default: 132598f44cb0SIgor Mitsyanko pr_err("MAC%u: Unknown TLV type: %#x\n", mac->macid, 132698f44cb0SIgor Mitsyanko le16_to_cpu(tlv->type)); 132798f44cb0SIgor Mitsyanko break; 132898f44cb0SIgor Mitsyanko } 132998f44cb0SIgor Mitsyanko 133098f44cb0SIgor Mitsyanko payload_len -= tlv_full_len; 133198f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 133298f44cb0SIgor Mitsyanko } 133398f44cb0SIgor Mitsyanko 133498f44cb0SIgor Mitsyanko if (payload_len) { 133598f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 133698f44cb0SIgor Mitsyanko mac->macid, payload_len); 133798f44cb0SIgor Mitsyanko return -EINVAL; 133898f44cb0SIgor Mitsyanko } 133998f44cb0SIgor Mitsyanko 134098f44cb0SIgor Mitsyanko return 0; 134198f44cb0SIgor Mitsyanko } 134298f44cb0SIgor Mitsyanko 13437c04b439SSergey Matyukevich static int 13447c04b439SSergey Matyukevich qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, 13457c04b439SSergey Matyukevich const u8 *payload, size_t payload_len) 13467c04b439SSergey Matyukevich { 13477c04b439SSergey Matyukevich struct qlink_chan_stats *qlink_stats; 13487c04b439SSergey Matyukevich const struct qlink_tlv_hdr *tlv; 13497c04b439SSergey Matyukevich size_t tlv_full_len; 13507c04b439SSergey Matyukevich u16 tlv_value_len; 13517c04b439SSergey Matyukevich u16 tlv_type; 13527c04b439SSergey Matyukevich 13537c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)payload; 13547c04b439SSergey Matyukevich while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 13557c04b439SSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 13567c04b439SSergey Matyukevich tlv_value_len = le16_to_cpu(tlv->len); 13577c04b439SSergey Matyukevich tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 13587c04b439SSergey Matyukevich if (tlv_full_len > payload_len) { 13597c04b439SSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 13607c04b439SSergey Matyukevich tlv_type, tlv_value_len); 13617c04b439SSergey Matyukevich return -EINVAL; 13627c04b439SSergey Matyukevich } 13637c04b439SSergey Matyukevich switch (tlv_type) { 13647c04b439SSergey Matyukevich case QTN_TLV_ID_CHANNEL_STATS: 13657c04b439SSergey Matyukevich if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { 13667c04b439SSergey Matyukevich pr_err("invalid CHANNEL_STATS entry size\n"); 13677c04b439SSergey Matyukevich return -EINVAL; 13687c04b439SSergey Matyukevich } 13697c04b439SSergey Matyukevich 13707c04b439SSergey Matyukevich qlink_stats = (void *)tlv->val; 13717c04b439SSergey Matyukevich 13727c04b439SSergey Matyukevich stats->chan_num = le32_to_cpu(qlink_stats->chan_num); 13737c04b439SSergey Matyukevich stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); 13747c04b439SSergey Matyukevich stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); 13757c04b439SSergey Matyukevich stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); 13767c04b439SSergey Matyukevich stats->cca_try = le32_to_cpu(qlink_stats->cca_try); 13777c04b439SSergey Matyukevich stats->chan_noise = qlink_stats->chan_noise; 13787c04b439SSergey Matyukevich 13797c04b439SSergey Matyukevich pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", 13807c04b439SSergey Matyukevich stats->chan_num, stats->cca_try, 13817c04b439SSergey Matyukevich stats->cca_busy, stats->chan_noise); 13827c04b439SSergey Matyukevich break; 13837c04b439SSergey Matyukevich default: 13847c04b439SSergey Matyukevich pr_warn("Unknown TLV type: %#x\n", 13857c04b439SSergey Matyukevich le16_to_cpu(tlv->type)); 13867c04b439SSergey Matyukevich } 13877c04b439SSergey Matyukevich payload_len -= tlv_full_len; 13887c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 13897c04b439SSergey Matyukevich } 13907c04b439SSergey Matyukevich 13917c04b439SSergey Matyukevich if (payload_len) { 13927c04b439SSergey Matyukevich pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); 13937c04b439SSergey Matyukevich return -EINVAL; 13947c04b439SSergey Matyukevich } 13957c04b439SSergey Matyukevich 13967c04b439SSergey Matyukevich return 0; 13977c04b439SSergey Matyukevich } 13987c04b439SSergey Matyukevich 139998f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) 140098f44cb0SIgor Mitsyanko { 140198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 140298f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp; 140398f44cb0SIgor Mitsyanko size_t var_data_len; 140498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 140598f44cb0SIgor Mitsyanko int ret = 0; 140698f44cb0SIgor Mitsyanko 140798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 140898f44cb0SIgor Mitsyanko QLINK_CMD_MAC_INFO, 140998f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 141098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 141198f44cb0SIgor Mitsyanko return -ENOMEM; 141298f44cb0SIgor Mitsyanko 141398f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 141498f44cb0SIgor Mitsyanko 141598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 141698f44cb0SIgor Mitsyanko sizeof(*resp), &var_data_len); 141798f44cb0SIgor Mitsyanko if (unlikely(ret)) 141898f44cb0SIgor Mitsyanko goto out; 141998f44cb0SIgor Mitsyanko 142098f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 142198f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 142298f44cb0SIgor Mitsyanko ret = -EFAULT; 142398f44cb0SIgor Mitsyanko goto out; 142498f44cb0SIgor Mitsyanko } 142598f44cb0SIgor Mitsyanko 142698f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_mac_info *)resp_skb->data; 142798f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(mac, resp); 142898f44cb0SIgor Mitsyanko ret = qtnf_parse_variable_mac_info(mac, resp->var_info, var_data_len); 142998f44cb0SIgor Mitsyanko 143098f44cb0SIgor Mitsyanko out: 143198f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 143298f44cb0SIgor Mitsyanko consume_skb(resp_skb); 143398f44cb0SIgor Mitsyanko 143498f44cb0SIgor Mitsyanko return ret; 143598f44cb0SIgor Mitsyanko } 143698f44cb0SIgor Mitsyanko 143798f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) 143898f44cb0SIgor Mitsyanko { 143998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 144098f44cb0SIgor Mitsyanko const struct qlink_resp_get_hw_info *resp; 144198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 144298f44cb0SIgor Mitsyanko int ret = 0; 14434dd07d2bSSergey Matyukevich size_t info_len; 144498f44cb0SIgor Mitsyanko 144598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 144698f44cb0SIgor Mitsyanko QLINK_CMD_GET_HW_INFO, 144798f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 144898f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 144998f44cb0SIgor Mitsyanko return -ENOMEM; 145098f44cb0SIgor Mitsyanko 145198f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 145298f44cb0SIgor Mitsyanko 145398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, 14544dd07d2bSSergey Matyukevich sizeof(*resp), &info_len); 145598f44cb0SIgor Mitsyanko 145698f44cb0SIgor Mitsyanko if (unlikely(ret)) 145798f44cb0SIgor Mitsyanko goto out; 145898f44cb0SIgor Mitsyanko 145998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 146098f44cb0SIgor Mitsyanko pr_err("cmd exec failed: 0x%.4X\n", res_code); 146198f44cb0SIgor Mitsyanko ret = -EFAULT; 146298f44cb0SIgor Mitsyanko goto out; 146398f44cb0SIgor Mitsyanko } 146498f44cb0SIgor Mitsyanko 146598f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; 14664dd07d2bSSergey Matyukevich ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); 146798f44cb0SIgor Mitsyanko 146898f44cb0SIgor Mitsyanko out: 146998f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 147098f44cb0SIgor Mitsyanko consume_skb(resp_skb); 147198f44cb0SIgor Mitsyanko 147298f44cb0SIgor Mitsyanko return ret; 147398f44cb0SIgor Mitsyanko } 147498f44cb0SIgor Mitsyanko 147598f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, 147698f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 147798f44cb0SIgor Mitsyanko { 147898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 147998f44cb0SIgor Mitsyanko size_t info_len; 148098f44cb0SIgor Mitsyanko struct qlink_cmd_chans_info_get *cmd; 148198f44cb0SIgor Mitsyanko struct qlink_resp_get_chan_info *resp; 148298f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 148398f44cb0SIgor Mitsyanko int ret = 0; 148498f44cb0SIgor Mitsyanko u8 qband; 148598f44cb0SIgor Mitsyanko 148698f44cb0SIgor Mitsyanko switch (band->band) { 148798f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 148898f44cb0SIgor Mitsyanko qband = QLINK_BAND_2GHZ; 148998f44cb0SIgor Mitsyanko break; 149098f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 149198f44cb0SIgor Mitsyanko qband = QLINK_BAND_5GHZ; 149298f44cb0SIgor Mitsyanko break; 149398f44cb0SIgor Mitsyanko case NL80211_BAND_60GHZ: 149498f44cb0SIgor Mitsyanko qband = QLINK_BAND_60GHZ; 149598f44cb0SIgor Mitsyanko break; 149698f44cb0SIgor Mitsyanko default: 149798f44cb0SIgor Mitsyanko return -EINVAL; 149898f44cb0SIgor Mitsyanko } 149998f44cb0SIgor Mitsyanko 1500bc0384eeSColin Ian King cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 1501bc0384eeSColin Ian King QLINK_CMD_CHANS_INFO_GET, 1502bc0384eeSColin Ian King sizeof(*cmd)); 1503bc0384eeSColin Ian King if (!cmd_skb) 1504bc0384eeSColin Ian King return -ENOMEM; 1505bc0384eeSColin Ian King 150698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_chans_info_get *)cmd_skb->data; 150798f44cb0SIgor Mitsyanko cmd->band = qband; 15089ef75095SSergey Matyukevich 15099ef75095SSergey Matyukevich qtnf_bus_lock(mac->bus); 15109ef75095SSergey Matyukevich 151198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 151298f44cb0SIgor Mitsyanko sizeof(*resp), &info_len); 151398f44cb0SIgor Mitsyanko 151498f44cb0SIgor Mitsyanko if (unlikely(ret)) 151598f44cb0SIgor Mitsyanko goto out; 151698f44cb0SIgor Mitsyanko 151798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 151898f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 151998f44cb0SIgor Mitsyanko ret = -EFAULT; 152098f44cb0SIgor Mitsyanko goto out; 152198f44cb0SIgor Mitsyanko } 152298f44cb0SIgor Mitsyanko 152398f44cb0SIgor Mitsyanko resp = (struct qlink_resp_get_chan_info *)resp_skb->data; 152498f44cb0SIgor Mitsyanko if (resp->band != qband) { 152598f44cb0SIgor Mitsyanko pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid, 152698f44cb0SIgor Mitsyanko resp->band, qband); 152798f44cb0SIgor Mitsyanko ret = -EINVAL; 152898f44cb0SIgor Mitsyanko goto out; 152998f44cb0SIgor Mitsyanko } 153098f44cb0SIgor Mitsyanko 153198f44cb0SIgor Mitsyanko ret = qtnf_cmd_resp_fill_channels_info(band, resp, info_len); 153298f44cb0SIgor Mitsyanko 153398f44cb0SIgor Mitsyanko out: 15349ef75095SSergey Matyukevich qtnf_bus_unlock(mac->bus); 153598f44cb0SIgor Mitsyanko consume_skb(resp_skb); 153698f44cb0SIgor Mitsyanko 153798f44cb0SIgor Mitsyanko return ret; 153898f44cb0SIgor Mitsyanko } 153998f44cb0SIgor Mitsyanko 154098f44cb0SIgor Mitsyanko int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac) 154198f44cb0SIgor Mitsyanko { 154298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 154398f44cb0SIgor Mitsyanko size_t response_size; 154498f44cb0SIgor Mitsyanko struct qlink_resp_phy_params *resp; 154598f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 154698f44cb0SIgor Mitsyanko int ret = 0; 154798f44cb0SIgor Mitsyanko 154898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 154998f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_GET, 155098f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 155198f44cb0SIgor Mitsyanko if (!cmd_skb) 155298f44cb0SIgor Mitsyanko return -ENOMEM; 155398f44cb0SIgor Mitsyanko 155498f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 155598f44cb0SIgor Mitsyanko 155698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 155798f44cb0SIgor Mitsyanko sizeof(*resp), &response_size); 155898f44cb0SIgor Mitsyanko 155998f44cb0SIgor Mitsyanko if (unlikely(ret)) 156098f44cb0SIgor Mitsyanko goto out; 156198f44cb0SIgor Mitsyanko 156298f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 156398f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 156498f44cb0SIgor Mitsyanko ret = -EFAULT; 156598f44cb0SIgor Mitsyanko goto out; 156698f44cb0SIgor Mitsyanko } 156798f44cb0SIgor Mitsyanko 156898f44cb0SIgor Mitsyanko resp = (struct qlink_resp_phy_params *)resp_skb->data; 156998f44cb0SIgor Mitsyanko ret = qtnf_cmd_resp_proc_phy_params(mac, resp->info, response_size); 157098f44cb0SIgor Mitsyanko 157198f44cb0SIgor Mitsyanko out: 157298f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 157398f44cb0SIgor Mitsyanko consume_skb(resp_skb); 157498f44cb0SIgor Mitsyanko 157598f44cb0SIgor Mitsyanko return ret; 157698f44cb0SIgor Mitsyanko } 157798f44cb0SIgor Mitsyanko 157898f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) 157998f44cb0SIgor Mitsyanko { 158098f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 158198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 158298f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 158398f44cb0SIgor Mitsyanko int ret = 0; 158498f44cb0SIgor Mitsyanko 158598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 158698f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_SET, 158798f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 158898f44cb0SIgor Mitsyanko if (!cmd_skb) 158998f44cb0SIgor Mitsyanko return -ENOMEM; 159098f44cb0SIgor Mitsyanko 159198f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 159298f44cb0SIgor Mitsyanko 159398f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_FRAG_THRESHOLD) 159498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_FRAG_THRESH, 159598f44cb0SIgor Mitsyanko wiphy->frag_threshold); 159698f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_RTS_THRESHOLD) 159798f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_RTS_THRESH, 159898f44cb0SIgor Mitsyanko wiphy->rts_threshold); 159998f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_COVERAGE_CLASS) 160098f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, 160198f44cb0SIgor Mitsyanko wiphy->coverage_class); 160298f44cb0SIgor Mitsyanko 160398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 160498f44cb0SIgor Mitsyanko 160598f44cb0SIgor Mitsyanko if (unlikely(ret)) 160698f44cb0SIgor Mitsyanko goto out; 160798f44cb0SIgor Mitsyanko 160898f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 160998f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 161098f44cb0SIgor Mitsyanko ret = -EFAULT; 161198f44cb0SIgor Mitsyanko goto out; 161298f44cb0SIgor Mitsyanko } 161398f44cb0SIgor Mitsyanko 161498f44cb0SIgor Mitsyanko out: 161598f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 161698f44cb0SIgor Mitsyanko return ret; 161798f44cb0SIgor Mitsyanko } 161898f44cb0SIgor Mitsyanko 161998f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus) 162098f44cb0SIgor Mitsyanko { 162198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 162298f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 162398f44cb0SIgor Mitsyanko int ret = 0; 162498f44cb0SIgor Mitsyanko 162598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 162698f44cb0SIgor Mitsyanko QLINK_CMD_FW_INIT, 162798f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 162898f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 162998f44cb0SIgor Mitsyanko return -ENOMEM; 163098f44cb0SIgor Mitsyanko 163198f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 163298f44cb0SIgor Mitsyanko 163398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(bus, cmd_skb, &res_code); 163498f44cb0SIgor Mitsyanko 163598f44cb0SIgor Mitsyanko if (unlikely(ret)) 163698f44cb0SIgor Mitsyanko goto out; 163798f44cb0SIgor Mitsyanko 163898f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 163998f44cb0SIgor Mitsyanko pr_err("cmd exec failed: 0x%.4X\n", res_code); 164098f44cb0SIgor Mitsyanko ret = -EFAULT; 164198f44cb0SIgor Mitsyanko goto out; 164298f44cb0SIgor Mitsyanko } 164398f44cb0SIgor Mitsyanko 164498f44cb0SIgor Mitsyanko out: 164598f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 164698f44cb0SIgor Mitsyanko return ret; 164798f44cb0SIgor Mitsyanko } 164898f44cb0SIgor Mitsyanko 164998f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus) 165098f44cb0SIgor Mitsyanko { 165198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 165298f44cb0SIgor Mitsyanko 165398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 165498f44cb0SIgor Mitsyanko QLINK_CMD_FW_DEINIT, 165598f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 165698f44cb0SIgor Mitsyanko if (!cmd_skb) 165798f44cb0SIgor Mitsyanko return; 165898f44cb0SIgor Mitsyanko 165998f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 166098f44cb0SIgor Mitsyanko 166198f44cb0SIgor Mitsyanko qtnf_cmd_send(bus, cmd_skb, NULL); 166298f44cb0SIgor Mitsyanko 166398f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 166498f44cb0SIgor Mitsyanko } 166598f44cb0SIgor Mitsyanko 166698f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 166798f44cb0SIgor Mitsyanko const u8 *mac_addr, struct key_params *params) 166898f44cb0SIgor Mitsyanko { 166998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 167098f44cb0SIgor Mitsyanko struct qlink_cmd_add_key *cmd; 167198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 167298f44cb0SIgor Mitsyanko int ret = 0; 167398f44cb0SIgor Mitsyanko 167498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 167598f44cb0SIgor Mitsyanko QLINK_CMD_ADD_KEY, 167698f44cb0SIgor Mitsyanko sizeof(*cmd)); 167798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 167898f44cb0SIgor Mitsyanko return -ENOMEM; 167998f44cb0SIgor Mitsyanko 168098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 168198f44cb0SIgor Mitsyanko 168298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_add_key *)cmd_skb->data; 168398f44cb0SIgor Mitsyanko 168498f44cb0SIgor Mitsyanko if (mac_addr) 168598f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 168698f44cb0SIgor Mitsyanko else 168798f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 168898f44cb0SIgor Mitsyanko 168998f44cb0SIgor Mitsyanko cmd->cipher = cpu_to_le32(params->cipher); 169098f44cb0SIgor Mitsyanko cmd->key_index = key_index; 169198f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 169298f44cb0SIgor Mitsyanko 169398f44cb0SIgor Mitsyanko if (params->key && params->key_len > 0) 169498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY, 169598f44cb0SIgor Mitsyanko params->key, 169698f44cb0SIgor Mitsyanko params->key_len); 169798f44cb0SIgor Mitsyanko 169898f44cb0SIgor Mitsyanko if (params->seq && params->seq_len > 0) 169998f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ, 170098f44cb0SIgor Mitsyanko params->seq, 170198f44cb0SIgor Mitsyanko params->seq_len); 170298f44cb0SIgor Mitsyanko 170398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 170498f44cb0SIgor Mitsyanko if (unlikely(ret)) 170598f44cb0SIgor Mitsyanko goto out; 170698f44cb0SIgor Mitsyanko 170798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 170898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", 170998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, res_code); 171098f44cb0SIgor Mitsyanko ret = -EFAULT; 171198f44cb0SIgor Mitsyanko goto out; 171298f44cb0SIgor Mitsyanko } 171398f44cb0SIgor Mitsyanko 171498f44cb0SIgor Mitsyanko out: 171598f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 171698f44cb0SIgor Mitsyanko return ret; 171798f44cb0SIgor Mitsyanko } 171898f44cb0SIgor Mitsyanko 171998f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 172098f44cb0SIgor Mitsyanko const u8 *mac_addr) 172198f44cb0SIgor Mitsyanko { 172298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 172398f44cb0SIgor Mitsyanko struct qlink_cmd_del_key *cmd; 172498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 172598f44cb0SIgor Mitsyanko int ret = 0; 172698f44cb0SIgor Mitsyanko 172798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 172898f44cb0SIgor Mitsyanko QLINK_CMD_DEL_KEY, 172998f44cb0SIgor Mitsyanko sizeof(*cmd)); 173098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 173198f44cb0SIgor Mitsyanko return -ENOMEM; 173298f44cb0SIgor Mitsyanko 173398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 173498f44cb0SIgor Mitsyanko 173598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_key *)cmd_skb->data; 173698f44cb0SIgor Mitsyanko 173798f44cb0SIgor Mitsyanko if (mac_addr) 173898f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 173998f44cb0SIgor Mitsyanko else 174098f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 174198f44cb0SIgor Mitsyanko 174298f44cb0SIgor Mitsyanko cmd->key_index = key_index; 174398f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 174498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 174598f44cb0SIgor Mitsyanko if (unlikely(ret)) 174698f44cb0SIgor Mitsyanko goto out; 174798f44cb0SIgor Mitsyanko 174898f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 174998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", 175098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, res_code); 175198f44cb0SIgor Mitsyanko ret = -EFAULT; 175298f44cb0SIgor Mitsyanko goto out; 175398f44cb0SIgor Mitsyanko } 175498f44cb0SIgor Mitsyanko 175598f44cb0SIgor Mitsyanko out: 175698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 175798f44cb0SIgor Mitsyanko return ret; 175898f44cb0SIgor Mitsyanko } 175998f44cb0SIgor Mitsyanko 176098f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index, 176198f44cb0SIgor Mitsyanko bool unicast, bool multicast) 176298f44cb0SIgor Mitsyanko { 176398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 176498f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_key *cmd; 176598f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 176698f44cb0SIgor Mitsyanko int ret = 0; 176798f44cb0SIgor Mitsyanko 176898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 176998f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_KEY, 177098f44cb0SIgor Mitsyanko sizeof(*cmd)); 177198f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 177298f44cb0SIgor Mitsyanko return -ENOMEM; 177398f44cb0SIgor Mitsyanko 177498f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 177598f44cb0SIgor Mitsyanko 177698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data; 177798f44cb0SIgor Mitsyanko cmd->key_index = key_index; 177898f44cb0SIgor Mitsyanko cmd->unicast = unicast; 177998f44cb0SIgor Mitsyanko cmd->multicast = multicast; 178098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 178198f44cb0SIgor Mitsyanko if (unlikely(ret)) 178298f44cb0SIgor Mitsyanko goto out; 178398f44cb0SIgor Mitsyanko 178498f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 178598f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 178698f44cb0SIgor Mitsyanko vif->vifid, res_code); 178798f44cb0SIgor Mitsyanko ret = -EFAULT; 178898f44cb0SIgor Mitsyanko goto out; 178998f44cb0SIgor Mitsyanko } 179098f44cb0SIgor Mitsyanko 179198f44cb0SIgor Mitsyanko out: 179298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 179398f44cb0SIgor Mitsyanko return ret; 179498f44cb0SIgor Mitsyanko } 179598f44cb0SIgor Mitsyanko 179698f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index) 179798f44cb0SIgor Mitsyanko { 179898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 179998f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_mgmt_key *cmd; 180098f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 180198f44cb0SIgor Mitsyanko int ret = 0; 180298f44cb0SIgor Mitsyanko 180398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 180498f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_MGMT_KEY, 180598f44cb0SIgor Mitsyanko sizeof(*cmd)); 180698f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 180798f44cb0SIgor Mitsyanko return -ENOMEM; 180898f44cb0SIgor Mitsyanko 180998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 181098f44cb0SIgor Mitsyanko 181198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data; 181298f44cb0SIgor Mitsyanko cmd->key_index = key_index; 181398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 181498f44cb0SIgor Mitsyanko if (unlikely(ret)) 181598f44cb0SIgor Mitsyanko goto out; 181698f44cb0SIgor Mitsyanko 181798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 181898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 181998f44cb0SIgor Mitsyanko vif->vifid, res_code); 182098f44cb0SIgor Mitsyanko ret = -EFAULT; 182198f44cb0SIgor Mitsyanko goto out; 182298f44cb0SIgor Mitsyanko } 182398f44cb0SIgor Mitsyanko 182498f44cb0SIgor Mitsyanko out: 182598f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 182698f44cb0SIgor Mitsyanko return ret; 182798f44cb0SIgor Mitsyanko } 182898f44cb0SIgor Mitsyanko 182998f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags) 183098f44cb0SIgor Mitsyanko { 183198f44cb0SIgor Mitsyanko u32 code = 0; 183298f44cb0SIgor Mitsyanko 183398f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED)) 183498f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHORIZED; 183598f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) 183698f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_SHORT_PREAMBLE; 183798f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_WME)) 183898f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_WME; 183998f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_MFP)) 184098f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_MFP; 184198f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED)) 184298f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHENTICATED; 184398f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER)) 184498f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_TDLS_PEER; 184598f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED)) 184698f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_ASSOCIATED; 184798f44cb0SIgor Mitsyanko return code; 184898f44cb0SIgor Mitsyanko } 184998f44cb0SIgor Mitsyanko 185098f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, 185198f44cb0SIgor Mitsyanko struct station_parameters *params) 185298f44cb0SIgor Mitsyanko { 185398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 185498f44cb0SIgor Mitsyanko struct qlink_cmd_change_sta *cmd; 185598f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 185698f44cb0SIgor Mitsyanko int ret = 0; 185798f44cb0SIgor Mitsyanko 185898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 185998f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_STA, 186098f44cb0SIgor Mitsyanko sizeof(*cmd)); 186198f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 186298f44cb0SIgor Mitsyanko return -ENOMEM; 186398f44cb0SIgor Mitsyanko 186498f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 186598f44cb0SIgor Mitsyanko 186698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; 186798f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, mac); 1868805b28c0SSergey Matyukevich 1869805b28c0SSergey Matyukevich switch (vif->wdev.iftype) { 1870805b28c0SSergey Matyukevich case NL80211_IFTYPE_AP: 1871805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); 187298f44cb0SIgor Mitsyanko cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( 187398f44cb0SIgor Mitsyanko params->sta_flags_mask)); 187498f44cb0SIgor Mitsyanko cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( 187598f44cb0SIgor Mitsyanko params->sta_flags_set)); 1876805b28c0SSergey Matyukevich break; 1877805b28c0SSergey Matyukevich case NL80211_IFTYPE_STATION: 1878805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 1879805b28c0SSergey Matyukevich cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( 1880805b28c0SSergey Matyukevich params->sta_flags_mask)); 1881805b28c0SSergey Matyukevich cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( 1882805b28c0SSergey Matyukevich params->sta_flags_set)); 1883805b28c0SSergey Matyukevich break; 1884805b28c0SSergey Matyukevich default: 1885805b28c0SSergey Matyukevich pr_err("unsupported iftype %d\n", vif->wdev.iftype); 1886805b28c0SSergey Matyukevich ret = -EINVAL; 1887805b28c0SSergey Matyukevich goto out; 1888805b28c0SSergey Matyukevich } 188998f44cb0SIgor Mitsyanko 189098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 189198f44cb0SIgor Mitsyanko if (unlikely(ret)) 189298f44cb0SIgor Mitsyanko goto out; 189398f44cb0SIgor Mitsyanko 189498f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 189598f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 189698f44cb0SIgor Mitsyanko vif->vifid, res_code); 189798f44cb0SIgor Mitsyanko ret = -EFAULT; 189898f44cb0SIgor Mitsyanko goto out; 189998f44cb0SIgor Mitsyanko } 190098f44cb0SIgor Mitsyanko 190198f44cb0SIgor Mitsyanko out: 190298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 190398f44cb0SIgor Mitsyanko return ret; 190498f44cb0SIgor Mitsyanko } 190598f44cb0SIgor Mitsyanko 190698f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif, 190798f44cb0SIgor Mitsyanko struct station_del_parameters *params) 190898f44cb0SIgor Mitsyanko { 190998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 191098f44cb0SIgor Mitsyanko struct qlink_cmd_del_sta *cmd; 191198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 191298f44cb0SIgor Mitsyanko int ret = 0; 191398f44cb0SIgor Mitsyanko 191498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 191598f44cb0SIgor Mitsyanko QLINK_CMD_DEL_STA, 191698f44cb0SIgor Mitsyanko sizeof(*cmd)); 191798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 191898f44cb0SIgor Mitsyanko return -ENOMEM; 191998f44cb0SIgor Mitsyanko 192098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 192198f44cb0SIgor Mitsyanko 192298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_sta *)cmd_skb->data; 192398f44cb0SIgor Mitsyanko 192498f44cb0SIgor Mitsyanko if (params->mac) 192598f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, params->mac); 192698f44cb0SIgor Mitsyanko else 192798f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->sta_addr); /* flush all stations */ 192898f44cb0SIgor Mitsyanko 192998f44cb0SIgor Mitsyanko cmd->subtype = params->subtype; 193098f44cb0SIgor Mitsyanko cmd->reason_code = cpu_to_le16(params->reason_code); 193198f44cb0SIgor Mitsyanko 193298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 193398f44cb0SIgor Mitsyanko if (unlikely(ret)) 193498f44cb0SIgor Mitsyanko goto out; 193598f44cb0SIgor Mitsyanko 193698f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 193798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 193898f44cb0SIgor Mitsyanko vif->vifid, res_code); 193998f44cb0SIgor Mitsyanko ret = -EFAULT; 194098f44cb0SIgor Mitsyanko goto out; 194198f44cb0SIgor Mitsyanko } 194298f44cb0SIgor Mitsyanko 194398f44cb0SIgor Mitsyanko out: 194498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 194598f44cb0SIgor Mitsyanko return ret; 194698f44cb0SIgor Mitsyanko } 194798f44cb0SIgor Mitsyanko 194898f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac) 194998f44cb0SIgor Mitsyanko { 195098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 195198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 195298f44cb0SIgor Mitsyanko struct ieee80211_channel *sc; 195398f44cb0SIgor Mitsyanko struct cfg80211_scan_request *scan_req = mac->scan_req; 195498f44cb0SIgor Mitsyanko struct qlink_tlv_channel *qchan; 195598f44cb0SIgor Mitsyanko int n_channels; 195698f44cb0SIgor Mitsyanko int count = 0; 195798f44cb0SIgor Mitsyanko int ret; 195898f44cb0SIgor Mitsyanko u32 flags; 195998f44cb0SIgor Mitsyanko 196098f44cb0SIgor Mitsyanko if (scan_req->n_ssids > QTNF_MAX_SSID_LIST_LENGTH) { 196198f44cb0SIgor Mitsyanko pr_err("MAC%u: too many SSIDs in scan request\n", mac->macid); 196298f44cb0SIgor Mitsyanko return -EINVAL; 196398f44cb0SIgor Mitsyanko } 196498f44cb0SIgor Mitsyanko 196598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 196698f44cb0SIgor Mitsyanko QLINK_CMD_SCAN, 196798f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 196898f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 196998f44cb0SIgor Mitsyanko return -ENOMEM; 197098f44cb0SIgor Mitsyanko 197198f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 197298f44cb0SIgor Mitsyanko 197398f44cb0SIgor Mitsyanko if (scan_req->n_ssids != 0) { 197498f44cb0SIgor Mitsyanko while (count < scan_req->n_ssids) { 197598f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, 197698f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid, 197798f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid_len); 197898f44cb0SIgor Mitsyanko count++; 197998f44cb0SIgor Mitsyanko } 198098f44cb0SIgor Mitsyanko } 198198f44cb0SIgor Mitsyanko 198298f44cb0SIgor Mitsyanko if (scan_req->ie_len != 0) 198398f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, 198498f44cb0SIgor Mitsyanko scan_req->ie, 198598f44cb0SIgor Mitsyanko scan_req->ie_len); 198698f44cb0SIgor Mitsyanko 198798f44cb0SIgor Mitsyanko if (scan_req->n_channels) { 198898f44cb0SIgor Mitsyanko n_channels = scan_req->n_channels; 198998f44cb0SIgor Mitsyanko count = 0; 199098f44cb0SIgor Mitsyanko 199198f44cb0SIgor Mitsyanko while (n_channels != 0) { 199298f44cb0SIgor Mitsyanko sc = scan_req->channels[count]; 199398f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_DISABLED) { 199498f44cb0SIgor Mitsyanko n_channels--; 199598f44cb0SIgor Mitsyanko continue; 199698f44cb0SIgor Mitsyanko } 199798f44cb0SIgor Mitsyanko 199898f44cb0SIgor Mitsyanko pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n", 199998f44cb0SIgor Mitsyanko mac->macid, sc->hw_value, sc->center_freq, 200098f44cb0SIgor Mitsyanko sc->flags); 2001b080db58SJohannes Berg qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); 200298f44cb0SIgor Mitsyanko flags = 0; 200398f44cb0SIgor Mitsyanko 200498f44cb0SIgor Mitsyanko qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); 200598f44cb0SIgor Mitsyanko qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - 200698f44cb0SIgor Mitsyanko sizeof(struct qlink_tlv_hdr)); 200798f44cb0SIgor Mitsyanko qchan->center_freq = cpu_to_le16(sc->center_freq); 200898f44cb0SIgor Mitsyanko qchan->hw_value = cpu_to_le16(sc->hw_value); 200998f44cb0SIgor Mitsyanko 201098f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_NO_IR) 201198f44cb0SIgor Mitsyanko flags |= QLINK_CHAN_NO_IR; 201298f44cb0SIgor Mitsyanko 201398f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_RADAR) 201498f44cb0SIgor Mitsyanko flags |= QLINK_CHAN_RADAR; 201598f44cb0SIgor Mitsyanko 201698f44cb0SIgor Mitsyanko qchan->flags = cpu_to_le32(flags); 201798f44cb0SIgor Mitsyanko n_channels--; 201898f44cb0SIgor Mitsyanko count++; 201998f44cb0SIgor Mitsyanko } 202098f44cb0SIgor Mitsyanko } 202198f44cb0SIgor Mitsyanko 202298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 202398f44cb0SIgor Mitsyanko 202498f44cb0SIgor Mitsyanko if (unlikely(ret)) 202598f44cb0SIgor Mitsyanko goto out; 202698f44cb0SIgor Mitsyanko 202798f44cb0SIgor Mitsyanko pr_debug("MAC%u: scan started\n", mac->macid); 202898f44cb0SIgor Mitsyanko 202998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 203098f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 203198f44cb0SIgor Mitsyanko ret = -EFAULT; 203298f44cb0SIgor Mitsyanko goto out; 203398f44cb0SIgor Mitsyanko } 203498f44cb0SIgor Mitsyanko out: 203598f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 203698f44cb0SIgor Mitsyanko return ret; 203798f44cb0SIgor Mitsyanko } 203898f44cb0SIgor Mitsyanko 203998f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif, 204098f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 204198f44cb0SIgor Mitsyanko { 204298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 204398f44cb0SIgor Mitsyanko struct qlink_cmd_connect *cmd; 204498f44cb0SIgor Mitsyanko struct qtnf_bss_config *bss_cfg = &vif->bss_cfg; 204598f44cb0SIgor Mitsyanko struct qlink_auth_encr aen; 204698f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 204798f44cb0SIgor Mitsyanko int ret; 204898f44cb0SIgor Mitsyanko int i; 204998f44cb0SIgor Mitsyanko 205098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 205198f44cb0SIgor Mitsyanko QLINK_CMD_CONNECT, 205298f44cb0SIgor Mitsyanko sizeof(*cmd)); 205398f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 205498f44cb0SIgor Mitsyanko return -ENOMEM; 205598f44cb0SIgor Mitsyanko 205698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 205798f44cb0SIgor Mitsyanko 205898f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_connect *)cmd_skb->data; 205998f44cb0SIgor Mitsyanko 206098f44cb0SIgor Mitsyanko ether_addr_copy(cmd->bssid, bss_cfg->bssid); 206198f44cb0SIgor Mitsyanko 206296d4eaf2SIgor Mitsyanko if (sme->channel) 206396d4eaf2SIgor Mitsyanko cmd->channel = cpu_to_le16(sme->channel->hw_value); 206496d4eaf2SIgor Mitsyanko else 206596d4eaf2SIgor Mitsyanko cmd->channel = 0; 206698f44cb0SIgor Mitsyanko 206798f44cb0SIgor Mitsyanko cmd->bg_scan_period = cpu_to_le16(bss_cfg->bg_scan_period); 206898f44cb0SIgor Mitsyanko 206998f44cb0SIgor Mitsyanko memset(&aen, 0, sizeof(aen)); 207098f44cb0SIgor Mitsyanko aen.auth_type = bss_cfg->auth_type; 207198f44cb0SIgor Mitsyanko aen.privacy = !!bss_cfg->privacy; 207298f44cb0SIgor Mitsyanko aen.mfp = bss_cfg->mfp; 207398f44cb0SIgor Mitsyanko aen.wpa_versions = cpu_to_le32(bss_cfg->crypto.wpa_versions); 207498f44cb0SIgor Mitsyanko aen.cipher_group = cpu_to_le32(bss_cfg->crypto.cipher_group); 207598f44cb0SIgor Mitsyanko aen.n_ciphers_pairwise = cpu_to_le32( 207698f44cb0SIgor Mitsyanko bss_cfg->crypto.n_ciphers_pairwise); 207798f44cb0SIgor Mitsyanko 207898f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 207998f44cb0SIgor Mitsyanko aen.ciphers_pairwise[i] = cpu_to_le32( 208098f44cb0SIgor Mitsyanko bss_cfg->crypto.ciphers_pairwise[i]); 208198f44cb0SIgor Mitsyanko 208298f44cb0SIgor Mitsyanko aen.n_akm_suites = cpu_to_le32(bss_cfg->crypto.n_akm_suites); 208398f44cb0SIgor Mitsyanko 208498f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 208598f44cb0SIgor Mitsyanko aen.akm_suites[i] = cpu_to_le32( 208698f44cb0SIgor Mitsyanko bss_cfg->crypto.akm_suites[i]); 208798f44cb0SIgor Mitsyanko 208898f44cb0SIgor Mitsyanko aen.control_port = bss_cfg->crypto.control_port; 208998f44cb0SIgor Mitsyanko aen.control_port_no_encrypt = 209098f44cb0SIgor Mitsyanko bss_cfg->crypto.control_port_no_encrypt; 209198f44cb0SIgor Mitsyanko aen.control_port_ethertype = cpu_to_le16(be16_to_cpu( 209298f44cb0SIgor Mitsyanko bss_cfg->crypto.control_port_ethertype)); 209398f44cb0SIgor Mitsyanko 209498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, bss_cfg->ssid, 209598f44cb0SIgor Mitsyanko bss_cfg->ssid_len); 209698f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_CRYPTO, (u8 *)&aen, 209798f44cb0SIgor Mitsyanko sizeof(aen)); 209898f44cb0SIgor Mitsyanko 209998f44cb0SIgor Mitsyanko if (sme->ie_len != 0) 210098f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, 210198f44cb0SIgor Mitsyanko sme->ie, 210298f44cb0SIgor Mitsyanko sme->ie_len); 210398f44cb0SIgor Mitsyanko 210498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 210598f44cb0SIgor Mitsyanko 210698f44cb0SIgor Mitsyanko if (unlikely(ret)) 210798f44cb0SIgor Mitsyanko goto out; 210898f44cb0SIgor Mitsyanko 210998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 211098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 211198f44cb0SIgor Mitsyanko vif->vifid, res_code); 211298f44cb0SIgor Mitsyanko ret = -EFAULT; 211398f44cb0SIgor Mitsyanko goto out; 211498f44cb0SIgor Mitsyanko } 211598f44cb0SIgor Mitsyanko out: 211698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 211798f44cb0SIgor Mitsyanko return ret; 211898f44cb0SIgor Mitsyanko } 211998f44cb0SIgor Mitsyanko 212098f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code) 212198f44cb0SIgor Mitsyanko { 212298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 212398f44cb0SIgor Mitsyanko struct qlink_cmd_disconnect *cmd; 212498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 212598f44cb0SIgor Mitsyanko int ret; 212698f44cb0SIgor Mitsyanko 212798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 212898f44cb0SIgor Mitsyanko QLINK_CMD_DISCONNECT, 212998f44cb0SIgor Mitsyanko sizeof(*cmd)); 213098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 213198f44cb0SIgor Mitsyanko return -ENOMEM; 213298f44cb0SIgor Mitsyanko 213398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 213498f44cb0SIgor Mitsyanko 213598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_disconnect *)cmd_skb->data; 213698f44cb0SIgor Mitsyanko cmd->reason = cpu_to_le16(reason_code); 213798f44cb0SIgor Mitsyanko 213898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 213998f44cb0SIgor Mitsyanko 214098f44cb0SIgor Mitsyanko if (unlikely(ret)) 214198f44cb0SIgor Mitsyanko goto out; 214298f44cb0SIgor Mitsyanko 214398f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 214498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 214598f44cb0SIgor Mitsyanko vif->vifid, res_code); 214698f44cb0SIgor Mitsyanko ret = -EFAULT; 214798f44cb0SIgor Mitsyanko goto out; 214898f44cb0SIgor Mitsyanko } 214998f44cb0SIgor Mitsyanko out: 215098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 215198f44cb0SIgor Mitsyanko return ret; 215298f44cb0SIgor Mitsyanko } 215398f44cb0SIgor Mitsyanko 215498f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up) 215598f44cb0SIgor Mitsyanko { 215698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 215798f44cb0SIgor Mitsyanko struct qlink_cmd_updown *cmd; 215898f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 215998f44cb0SIgor Mitsyanko int ret; 216098f44cb0SIgor Mitsyanko 216198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 216298f44cb0SIgor Mitsyanko QLINK_CMD_UPDOWN_INTF, 216398f44cb0SIgor Mitsyanko sizeof(*cmd)); 216498f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 216598f44cb0SIgor Mitsyanko return -ENOMEM; 216698f44cb0SIgor Mitsyanko 216798f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_updown *)cmd_skb->data; 216898f44cb0SIgor Mitsyanko cmd->if_up = !!up; 216998f44cb0SIgor Mitsyanko 217098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 217198f44cb0SIgor Mitsyanko 217298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 217398f44cb0SIgor Mitsyanko 217498f44cb0SIgor Mitsyanko if (unlikely(ret)) 217598f44cb0SIgor Mitsyanko goto out; 217698f44cb0SIgor Mitsyanko 217798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 217898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 217998f44cb0SIgor Mitsyanko vif->vifid, res_code); 218098f44cb0SIgor Mitsyanko ret = -EFAULT; 218198f44cb0SIgor Mitsyanko goto out; 218298f44cb0SIgor Mitsyanko } 218398f44cb0SIgor Mitsyanko out: 218498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 218598f44cb0SIgor Mitsyanko return ret; 218698f44cb0SIgor Mitsyanko } 21874dd07d2bSSergey Matyukevich 21884dd07d2bSSergey Matyukevich int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req) 21894dd07d2bSSergey Matyukevich { 21904dd07d2bSSergey Matyukevich struct sk_buff *cmd_skb; 21914dd07d2bSSergey Matyukevich int ret; 21924dd07d2bSSergey Matyukevich u16 res_code; 21934dd07d2bSSergey Matyukevich struct qlink_cmd_reg_notify *cmd; 21944dd07d2bSSergey Matyukevich 21954dd07d2bSSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 21964dd07d2bSSergey Matyukevich QLINK_CMD_REG_NOTIFY, 21974dd07d2bSSergey Matyukevich sizeof(*cmd)); 21984dd07d2bSSergey Matyukevich if (!cmd_skb) 21994dd07d2bSSergey Matyukevich return -ENOMEM; 22004dd07d2bSSergey Matyukevich 22014dd07d2bSSergey Matyukevich cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; 22024dd07d2bSSergey Matyukevich cmd->alpha2[0] = req->alpha2[0]; 22034dd07d2bSSergey Matyukevich cmd->alpha2[1] = req->alpha2[1]; 22044dd07d2bSSergey Matyukevich 22054dd07d2bSSergey Matyukevich switch (req->initiator) { 22064dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_CORE: 22074dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_CORE; 22084dd07d2bSSergey Matyukevich break; 22094dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_USER: 22104dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_USER; 22114dd07d2bSSergey Matyukevich break; 22124dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_DRIVER: 22134dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; 22144dd07d2bSSergey Matyukevich break; 22154dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_COUNTRY_IE: 22164dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; 22174dd07d2bSSergey Matyukevich break; 22184dd07d2bSSergey Matyukevich } 22194dd07d2bSSergey Matyukevich 22204dd07d2bSSergey Matyukevich switch (req->user_reg_hint_type) { 22214dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_USER: 22224dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; 22234dd07d2bSSergey Matyukevich break; 22244dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_CELL_BASE: 22254dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; 22264dd07d2bSSergey Matyukevich break; 22274dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_INDOOR: 22284dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; 22294dd07d2bSSergey Matyukevich break; 22304dd07d2bSSergey Matyukevich } 22314dd07d2bSSergey Matyukevich 22324dd07d2bSSergey Matyukevich qtnf_bus_lock(bus); 22334dd07d2bSSergey Matyukevich 22344dd07d2bSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb, &res_code); 22354dd07d2bSSergey Matyukevich if (ret) 22364dd07d2bSSergey Matyukevich goto out; 22374dd07d2bSSergey Matyukevich 22384dd07d2bSSergey Matyukevich switch (res_code) { 22394dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_ENOTSUPP: 22404dd07d2bSSergey Matyukevich pr_warn("reg update not supported\n"); 22414dd07d2bSSergey Matyukevich ret = -EOPNOTSUPP; 22424dd07d2bSSergey Matyukevich break; 22434dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_EALREADY: 22444dd07d2bSSergey Matyukevich pr_info("regulatory domain is already set to %c%c", 22454dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 22464dd07d2bSSergey Matyukevich ret = -EALREADY; 22474dd07d2bSSergey Matyukevich break; 22484dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_OK: 22494dd07d2bSSergey Matyukevich ret = 0; 22504dd07d2bSSergey Matyukevich break; 22514dd07d2bSSergey Matyukevich default: 22524dd07d2bSSergey Matyukevich ret = -EFAULT; 22534dd07d2bSSergey Matyukevich break; 22544dd07d2bSSergey Matyukevich } 22554dd07d2bSSergey Matyukevich 22564dd07d2bSSergey Matyukevich out: 22574dd07d2bSSergey Matyukevich qtnf_bus_unlock(bus); 22584dd07d2bSSergey Matyukevich 22594dd07d2bSSergey Matyukevich return ret; 22604dd07d2bSSergey Matyukevich } 22617c04b439SSergey Matyukevich 22627c04b439SSergey Matyukevich int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, 22637c04b439SSergey Matyukevich struct qtnf_chan_stats *stats) 22647c04b439SSergey Matyukevich { 22657c04b439SSergey Matyukevich struct sk_buff *cmd_skb, *resp_skb = NULL; 22667c04b439SSergey Matyukevich struct qlink_cmd_get_chan_stats *cmd; 22677c04b439SSergey Matyukevich struct qlink_resp_get_chan_stats *resp; 22687c04b439SSergey Matyukevich size_t var_data_len; 22697c04b439SSergey Matyukevich u16 res_code = QLINK_CMD_RESULT_OK; 22707c04b439SSergey Matyukevich int ret = 0; 22717c04b439SSergey Matyukevich 22727c04b439SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 22737c04b439SSergey Matyukevich QLINK_CMD_CHAN_STATS, 22747c04b439SSergey Matyukevich sizeof(*cmd)); 22757c04b439SSergey Matyukevich if (!cmd_skb) 22767c04b439SSergey Matyukevich return -ENOMEM; 22777c04b439SSergey Matyukevich 22787c04b439SSergey Matyukevich qtnf_bus_lock(mac->bus); 22797c04b439SSergey Matyukevich 22807c04b439SSergey Matyukevich cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; 22817c04b439SSergey Matyukevich cmd->channel = cpu_to_le16(channel); 22827c04b439SSergey Matyukevich 22837c04b439SSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 22847c04b439SSergey Matyukevich sizeof(*resp), &var_data_len); 22857c04b439SSergey Matyukevich if (unlikely(ret)) { 22867c04b439SSergey Matyukevich qtnf_bus_unlock(mac->bus); 22877c04b439SSergey Matyukevich return ret; 22887c04b439SSergey Matyukevich } 22897c04b439SSergey Matyukevich 22907c04b439SSergey Matyukevich if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 22917c04b439SSergey Matyukevich switch (res_code) { 22927c04b439SSergey Matyukevich case QLINK_CMD_RESULT_ENOTFOUND: 22937c04b439SSergey Matyukevich ret = -ENOENT; 22947c04b439SSergey Matyukevich break; 22957c04b439SSergey Matyukevich default: 22967c04b439SSergey Matyukevich pr_err("cmd exec failed: 0x%.4X\n", res_code); 22977c04b439SSergey Matyukevich ret = -EFAULT; 22987c04b439SSergey Matyukevich break; 22997c04b439SSergey Matyukevich } 23007c04b439SSergey Matyukevich goto out; 23017c04b439SSergey Matyukevich } 23027c04b439SSergey Matyukevich 23037c04b439SSergey Matyukevich resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; 23047c04b439SSergey Matyukevich ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, 23057c04b439SSergey Matyukevich var_data_len); 23067c04b439SSergey Matyukevich 23077c04b439SSergey Matyukevich out: 23087c04b439SSergey Matyukevich qtnf_bus_unlock(mac->bus); 23097c04b439SSergey Matyukevich consume_skb(resp_skb); 23107c04b439SSergey Matyukevich return ret; 23117c04b439SSergey Matyukevich } 231297883695SSergey Matyukevich 23138c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, 231497883695SSergey Matyukevich struct cfg80211_csa_settings *params) 231597883695SSergey Matyukevich { 23168c015b90SIgor Mitsyanko struct qtnf_wmac *mac = vif->mac; 231797883695SSergey Matyukevich struct qlink_cmd_chan_switch *cmd; 231897883695SSergey Matyukevich struct sk_buff *cmd_skb; 231997883695SSergey Matyukevich u16 res_code = QLINK_CMD_RESULT_OK; 232097883695SSergey Matyukevich int ret; 232197883695SSergey Matyukevich 23228c015b90SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, 232397883695SSergey Matyukevich QLINK_CMD_CHAN_SWITCH, 232497883695SSergey Matyukevich sizeof(*cmd)); 232597883695SSergey Matyukevich 232697883695SSergey Matyukevich if (unlikely(!cmd_skb)) 232797883695SSergey Matyukevich return -ENOMEM; 232897883695SSergey Matyukevich 232997883695SSergey Matyukevich qtnf_bus_lock(mac->bus); 233097883695SSergey Matyukevich 233197883695SSergey Matyukevich cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; 233297883695SSergey Matyukevich cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); 233397883695SSergey Matyukevich cmd->radar_required = params->radar_required; 233497883695SSergey Matyukevich cmd->block_tx = params->block_tx; 233597883695SSergey Matyukevich cmd->beacon_count = params->count; 233697883695SSergey Matyukevich 233797883695SSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 233897883695SSergey Matyukevich 233997883695SSergey Matyukevich if (unlikely(ret)) 234097883695SSergey Matyukevich goto out; 234197883695SSergey Matyukevich 234297883695SSergey Matyukevich switch (res_code) { 234397883695SSergey Matyukevich case QLINK_CMD_RESULT_OK: 234497883695SSergey Matyukevich ret = 0; 234597883695SSergey Matyukevich break; 234697883695SSergey Matyukevich case QLINK_CMD_RESULT_ENOTFOUND: 234797883695SSergey Matyukevich ret = -ENOENT; 234897883695SSergey Matyukevich break; 234997883695SSergey Matyukevich case QLINK_CMD_RESULT_ENOTSUPP: 235097883695SSergey Matyukevich ret = -EOPNOTSUPP; 235197883695SSergey Matyukevich break; 235297883695SSergey Matyukevich case QLINK_CMD_RESULT_EALREADY: 235397883695SSergey Matyukevich ret = -EALREADY; 235497883695SSergey Matyukevich break; 235597883695SSergey Matyukevich case QLINK_CMD_RESULT_INVALID: 235697883695SSergey Matyukevich default: 235797883695SSergey Matyukevich ret = -EFAULT; 235897883695SSergey Matyukevich break; 235997883695SSergey Matyukevich } 236097883695SSergey Matyukevich 236197883695SSergey Matyukevich out: 236297883695SSergey Matyukevich qtnf_bus_unlock(mac->bus); 236397883695SSergey Matyukevich return ret; 236497883695SSergey Matyukevich } 23659e5478b6SIgor Mitsyanko 23669e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef) 23679e5478b6SIgor Mitsyanko { 23689e5478b6SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 23699e5478b6SIgor Mitsyanko const struct qlink_resp_channel_get *resp; 23709e5478b6SIgor Mitsyanko struct sk_buff *cmd_skb; 23719e5478b6SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 23729e5478b6SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 23739e5478b6SIgor Mitsyanko int ret; 23749e5478b6SIgor Mitsyanko 23759e5478b6SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 23769e5478b6SIgor Mitsyanko QLINK_CMD_CHAN_GET, 23779e5478b6SIgor Mitsyanko sizeof(struct qlink_cmd)); 23789e5478b6SIgor Mitsyanko if (unlikely(!cmd_skb)) 23799e5478b6SIgor Mitsyanko return -ENOMEM; 23809e5478b6SIgor Mitsyanko 23819e5478b6SIgor Mitsyanko qtnf_bus_lock(bus); 23829e5478b6SIgor Mitsyanko 23839e5478b6SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, 23849e5478b6SIgor Mitsyanko sizeof(*resp), NULL); 23859e5478b6SIgor Mitsyanko 23869e5478b6SIgor Mitsyanko qtnf_bus_unlock(bus); 23879e5478b6SIgor Mitsyanko 23889e5478b6SIgor Mitsyanko if (unlikely(ret)) 23899e5478b6SIgor Mitsyanko goto out; 23909e5478b6SIgor Mitsyanko 23919e5478b6SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 23929e5478b6SIgor Mitsyanko ret = -ENODATA; 23939e5478b6SIgor Mitsyanko goto out; 23949e5478b6SIgor Mitsyanko } 23959e5478b6SIgor Mitsyanko 23969e5478b6SIgor Mitsyanko resp = (const struct qlink_resp_channel_get *)resp_skb->data; 23979e5478b6SIgor Mitsyanko qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef); 23989e5478b6SIgor Mitsyanko 23999e5478b6SIgor Mitsyanko out: 24009e5478b6SIgor Mitsyanko consume_skb(resp_skb); 24019e5478b6SIgor Mitsyanko return ret; 24029e5478b6SIgor Mitsyanko } 2403