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