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 netif_carrier_on(vif->netdev); 17798f44cb0SIgor Mitsyanko 17898f44cb0SIgor Mitsyanko out: 17998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 18098f44cb0SIgor Mitsyanko return ret; 18198f44cb0SIgor Mitsyanko } 18298f44cb0SIgor Mitsyanko 1839b692df1SIgor Mitsyanko int qtnf_cmd_send_config_ap(struct qtnf_vif *vif, 1849b692df1SIgor Mitsyanko const struct cfg80211_ap_settings *s) 18598f44cb0SIgor Mitsyanko { 18698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 1878b5f4aa7SIgor Mitsyanko struct qlink_cmd_config_ap *cmd; 1888b5f4aa7SIgor Mitsyanko struct qlink_auth_encr *aen; 18998f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 19098f44cb0SIgor Mitsyanko int ret; 19198f44cb0SIgor Mitsyanko int i; 19298f44cb0SIgor Mitsyanko 19398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 19498f44cb0SIgor Mitsyanko QLINK_CMD_CONFIG_AP, 1958b5f4aa7SIgor Mitsyanko sizeof(*cmd)); 19698f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 19798f44cb0SIgor Mitsyanko return -ENOMEM; 19898f44cb0SIgor Mitsyanko 1998b5f4aa7SIgor Mitsyanko cmd = (struct qlink_cmd_config_ap *)cmd_skb->data; 2008b5f4aa7SIgor Mitsyanko cmd->dtim_period = s->dtim_period; 2018b5f4aa7SIgor Mitsyanko cmd->beacon_interval = cpu_to_le16(s->beacon_interval); 2028b5f4aa7SIgor Mitsyanko cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid); 2038b5f4aa7SIgor Mitsyanko cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout); 2048b5f4aa7SIgor Mitsyanko cmd->smps_mode = s->smps_mode; 2058b5f4aa7SIgor Mitsyanko cmd->p2p_ctwindow = s->p2p_ctwindow; 2068b5f4aa7SIgor Mitsyanko cmd->p2p_opp_ps = s->p2p_opp_ps; 2078b5f4aa7SIgor Mitsyanko cmd->pbss = s->pbss; 2088b5f4aa7SIgor Mitsyanko cmd->ht_required = s->ht_required; 2098b5f4aa7SIgor Mitsyanko cmd->vht_required = s->vht_required; 21098f44cb0SIgor Mitsyanko 2118b5f4aa7SIgor Mitsyanko aen = &cmd->aen; 2128b5f4aa7SIgor Mitsyanko aen->auth_type = s->auth_type; 2138b5f4aa7SIgor Mitsyanko aen->privacy = !!s->privacy; 2148b5f4aa7SIgor Mitsyanko aen->mfp = 0; 2158b5f4aa7SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions); 2168b5f4aa7SIgor Mitsyanko aen->cipher_group = cpu_to_le32(s->crypto.cipher_group); 2178b5f4aa7SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise); 21898f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2198b5f4aa7SIgor Mitsyanko aen->ciphers_pairwise[i] = 2209b692df1SIgor Mitsyanko cpu_to_le32(s->crypto.ciphers_pairwise[i]); 2218b5f4aa7SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); 22298f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2238b5f4aa7SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); 2248b5f4aa7SIgor Mitsyanko aen->control_port = s->crypto.control_port; 2258b5f4aa7SIgor Mitsyanko aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; 2268b5f4aa7SIgor Mitsyanko aen->control_port_ethertype = 2279b692df1SIgor Mitsyanko cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype)); 22898f44cb0SIgor Mitsyanko 229f99201cbSIgor Mitsyanko if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN) 230f99201cbSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid, 231f99201cbSIgor Mitsyanko s->ssid_len); 232f99201cbSIgor Mitsyanko 233f99201cbSIgor Mitsyanko if (cfg80211_chandef_valid(&s->chandef)) { 234f99201cbSIgor Mitsyanko struct qlink_tlv_chandef *chtlv = 235f99201cbSIgor Mitsyanko (struct qlink_tlv_chandef *)skb_put(cmd_skb, 236f99201cbSIgor Mitsyanko sizeof(*chtlv)); 237f99201cbSIgor Mitsyanko 238f99201cbSIgor Mitsyanko chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF); 239f99201cbSIgor Mitsyanko chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) - 240f99201cbSIgor Mitsyanko sizeof(chtlv->hdr)); 241f99201cbSIgor Mitsyanko qlink_chandef_cfg2q(&s->chandef, &chtlv->chan); 242f99201cbSIgor Mitsyanko } 243f99201cbSIgor Mitsyanko 2448b5f4aa7SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 24598f44cb0SIgor Mitsyanko 24698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 24798f44cb0SIgor Mitsyanko 24898f44cb0SIgor Mitsyanko if (unlikely(ret)) 24998f44cb0SIgor Mitsyanko goto out; 25098f44cb0SIgor Mitsyanko 25198f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 25298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 25398f44cb0SIgor Mitsyanko vif->vifid, res_code); 25498f44cb0SIgor Mitsyanko ret = -EFAULT; 25598f44cb0SIgor Mitsyanko goto out; 25698f44cb0SIgor Mitsyanko } 25798f44cb0SIgor Mitsyanko 25898f44cb0SIgor Mitsyanko out: 25998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 26098f44cb0SIgor Mitsyanko return ret; 26198f44cb0SIgor Mitsyanko } 26298f44cb0SIgor Mitsyanko 26398f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif) 26498f44cb0SIgor Mitsyanko { 26598f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 26698f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 26798f44cb0SIgor Mitsyanko int ret; 26898f44cb0SIgor Mitsyanko 26998f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 27098f44cb0SIgor Mitsyanko QLINK_CMD_STOP_AP, 27198f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 27298f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 27398f44cb0SIgor Mitsyanko return -ENOMEM; 27498f44cb0SIgor Mitsyanko 27598f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 27698f44cb0SIgor Mitsyanko 27798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 27898f44cb0SIgor Mitsyanko 27998f44cb0SIgor Mitsyanko if (unlikely(ret)) 28098f44cb0SIgor Mitsyanko goto out; 28198f44cb0SIgor Mitsyanko 28298f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 28398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 28498f44cb0SIgor Mitsyanko vif->vifid, res_code); 28598f44cb0SIgor Mitsyanko ret = -EFAULT; 28698f44cb0SIgor Mitsyanko goto out; 28798f44cb0SIgor Mitsyanko } 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 978db5c6d4aSIgor Mitsyanko pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n", 97998f44cb0SIgor Mitsyanko hwinfo->fw_ver, hwinfo->mac_bitmap, 9804dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1], 981db5c6d4aSIgor Mitsyanko hwinfo->total_tx_chain, hwinfo->total_rx_chain, 982db5c6d4aSIgor Mitsyanko hwinfo->hw_capab); 98398f44cb0SIgor Mitsyanko 98498f44cb0SIgor Mitsyanko return 0; 98598f44cb0SIgor Mitsyanko } 98698f44cb0SIgor Mitsyanko 98798f44cb0SIgor Mitsyanko static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, 98898f44cb0SIgor Mitsyanko const u8 *tlv_buf, size_t tlv_buf_size) 98998f44cb0SIgor Mitsyanko { 99098f44cb0SIgor Mitsyanko struct ieee80211_iface_limit *limits = NULL; 99198f44cb0SIgor Mitsyanko const struct qlink_iface_limit *limit_record; 99298f44cb0SIgor Mitsyanko size_t record_count = 0, rec = 0; 99341c8fa0cSSergey Matyukevich u16 tlv_type, tlv_value_len; 99498f44cb0SIgor Mitsyanko struct qlink_iface_comb_num *comb; 99598f44cb0SIgor Mitsyanko size_t tlv_full_len; 99698f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 99798f44cb0SIgor Mitsyanko 99898f44cb0SIgor Mitsyanko mac->macinfo.n_limits = 0; 99998f44cb0SIgor Mitsyanko 100098f44cb0SIgor Mitsyanko tlv = (const struct qlink_tlv_hdr *)tlv_buf; 100198f44cb0SIgor Mitsyanko while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) { 100298f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 100398f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 100498f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 100598f44cb0SIgor Mitsyanko if (tlv_full_len > tlv_buf_size) { 100698f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 100798f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 100898f44cb0SIgor Mitsyanko return -EINVAL; 100998f44cb0SIgor Mitsyanko } 101098f44cb0SIgor Mitsyanko 101198f44cb0SIgor Mitsyanko switch (tlv_type) { 101298f44cb0SIgor Mitsyanko case QTN_TLV_ID_NUM_IFACE_COMB: 101398f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len != sizeof(*comb))) 101498f44cb0SIgor Mitsyanko return -EINVAL; 101598f44cb0SIgor Mitsyanko 101698f44cb0SIgor Mitsyanko comb = (void *)tlv->val; 101798f44cb0SIgor Mitsyanko record_count = le16_to_cpu(comb->iface_comb_num); 101898f44cb0SIgor Mitsyanko 101998f44cb0SIgor Mitsyanko mac->macinfo.n_limits = record_count; 102098f44cb0SIgor Mitsyanko /* free earlier iface limits memory */ 102198f44cb0SIgor Mitsyanko kfree(mac->macinfo.limits); 102298f44cb0SIgor Mitsyanko mac->macinfo.limits = 102398f44cb0SIgor Mitsyanko kzalloc(sizeof(*mac->macinfo.limits) * 102498f44cb0SIgor Mitsyanko record_count, GFP_KERNEL); 102598f44cb0SIgor Mitsyanko 102698f44cb0SIgor Mitsyanko if (unlikely(!mac->macinfo.limits)) 102798f44cb0SIgor Mitsyanko return -ENOMEM; 102898f44cb0SIgor Mitsyanko 102998f44cb0SIgor Mitsyanko limits = mac->macinfo.limits; 103098f44cb0SIgor Mitsyanko break; 103198f44cb0SIgor Mitsyanko case QTN_TLV_ID_IFACE_LIMIT: 103298f44cb0SIgor Mitsyanko if (unlikely(!limits)) { 103398f44cb0SIgor Mitsyanko pr_warn("MAC%u: limits are not inited\n", 103498f44cb0SIgor Mitsyanko mac->macid); 103598f44cb0SIgor Mitsyanko return -EINVAL; 103698f44cb0SIgor Mitsyanko } 103798f44cb0SIgor Mitsyanko 103898f44cb0SIgor Mitsyanko if (unlikely(tlv_value_len != sizeof(*limit_record))) { 103998f44cb0SIgor Mitsyanko pr_warn("MAC%u: record size mismatch\n", 104098f44cb0SIgor Mitsyanko mac->macid); 104198f44cb0SIgor Mitsyanko return -EINVAL; 104298f44cb0SIgor Mitsyanko } 104398f44cb0SIgor Mitsyanko 104498f44cb0SIgor Mitsyanko limit_record = (void *)tlv->val; 104598f44cb0SIgor Mitsyanko limits[rec].max = le16_to_cpu(limit_record->max_num); 104641c8fa0cSSergey Matyukevich limits[rec].types = qlink_iface_type_to_nl_mask( 104741c8fa0cSSergey Matyukevich le16_to_cpu(limit_record->type)); 104841c8fa0cSSergey Matyukevich 104941c8fa0cSSergey Matyukevich /* supported modes: STA, AP */ 105098f44cb0SIgor Mitsyanko limits[rec].types &= BIT(NL80211_IFTYPE_AP) | 1051805b28c0SSergey Matyukevich BIT(NL80211_IFTYPE_AP_VLAN) | 105298f44cb0SIgor Mitsyanko BIT(NL80211_IFTYPE_STATION); 105398f44cb0SIgor Mitsyanko 105498f44cb0SIgor Mitsyanko pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid, 105598f44cb0SIgor Mitsyanko limits[rec].max, limits[rec].types); 105698f44cb0SIgor Mitsyanko 105798f44cb0SIgor Mitsyanko if (limits[rec].types) 105898f44cb0SIgor Mitsyanko rec++; 105998f44cb0SIgor Mitsyanko break; 106098f44cb0SIgor Mitsyanko default: 106198f44cb0SIgor Mitsyanko break; 106298f44cb0SIgor Mitsyanko } 1063805b28c0SSergey Matyukevich 106498f44cb0SIgor Mitsyanko tlv_buf_size -= tlv_full_len; 106598f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 106698f44cb0SIgor Mitsyanko } 106798f44cb0SIgor Mitsyanko 106898f44cb0SIgor Mitsyanko if (tlv_buf_size) { 106998f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 107098f44cb0SIgor Mitsyanko mac->macid, tlv_buf_size); 107198f44cb0SIgor Mitsyanko return -EINVAL; 107298f44cb0SIgor Mitsyanko } 107398f44cb0SIgor Mitsyanko 107498f44cb0SIgor Mitsyanko if (mac->macinfo.n_limits != rec) { 107598f44cb0SIgor Mitsyanko pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n", 107698f44cb0SIgor Mitsyanko mac->macid, mac->macinfo.n_limits, rec); 107798f44cb0SIgor Mitsyanko return -EINVAL; 107898f44cb0SIgor Mitsyanko } 107998f44cb0SIgor Mitsyanko 108098f44cb0SIgor Mitsyanko return 0; 108198f44cb0SIgor Mitsyanko } 108298f44cb0SIgor Mitsyanko 108398f44cb0SIgor Mitsyanko static void 108498f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, 108598f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp_info) 108698f44cb0SIgor Mitsyanko { 108798f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 108898f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 108998f44cb0SIgor Mitsyanko 109098f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 109198f44cb0SIgor Mitsyanko 109298f44cb0SIgor Mitsyanko mac_info->bands_cap = resp_info->bands_cap; 109398f44cb0SIgor Mitsyanko mac_info->phymode_cap = resp_info->phymode_cap; 109498f44cb0SIgor Mitsyanko memcpy(&mac_info->dev_mac, &resp_info->dev_mac, 109598f44cb0SIgor Mitsyanko sizeof(mac_info->dev_mac)); 109698f44cb0SIgor Mitsyanko 109798f44cb0SIgor Mitsyanko ether_addr_copy(mac->macaddr, mac_info->dev_mac); 109898f44cb0SIgor Mitsyanko 109998f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 110098f44cb0SIgor Mitsyanko if (vif) 110198f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, mac->macaddr); 110298f44cb0SIgor Mitsyanko else 110398f44cb0SIgor Mitsyanko pr_err("could not get valid base vif\n"); 110498f44cb0SIgor Mitsyanko 110598f44cb0SIgor Mitsyanko mac_info->num_tx_chain = resp_info->num_tx_chain; 110698f44cb0SIgor Mitsyanko mac_info->num_rx_chain = resp_info->num_rx_chain; 110798f44cb0SIgor Mitsyanko 110898f44cb0SIgor Mitsyanko mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta); 110998f44cb0SIgor Mitsyanko mac_info->radar_detect_widths = 111098f44cb0SIgor Mitsyanko qlink_chan_width_mask_to_nl(le16_to_cpu( 111198f44cb0SIgor Mitsyanko resp_info->radar_detect_widths)); 111298f44cb0SIgor Mitsyanko 1113*d42df85fSIgor Mitsyanko memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask, 1114*d42df85fSIgor Mitsyanko sizeof(mac_info->ht_cap_mod_mask)); 1115*d42df85fSIgor Mitsyanko memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask, 1116*d42df85fSIgor Mitsyanko sizeof(mac_info->vht_cap_mod_mask)); 111798f44cb0SIgor Mitsyanko } 111898f44cb0SIgor Mitsyanko 1119e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_htcap(const u8 *info, 1120e294cbfdSIgor Mitsyanko struct ieee80211_sta_ht_cap *bcap) 1121e294cbfdSIgor Mitsyanko { 1122e294cbfdSIgor Mitsyanko const struct ieee80211_ht_cap *ht_cap = 1123e294cbfdSIgor Mitsyanko (const struct ieee80211_ht_cap *)info; 1124e294cbfdSIgor Mitsyanko 1125e294cbfdSIgor Mitsyanko bcap->ht_supported = true; 1126e294cbfdSIgor Mitsyanko bcap->cap = le16_to_cpu(ht_cap->cap_info); 1127e294cbfdSIgor Mitsyanko bcap->ampdu_factor = 1128e294cbfdSIgor Mitsyanko ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 1129e294cbfdSIgor Mitsyanko bcap->ampdu_density = 1130e294cbfdSIgor Mitsyanko (ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 1131e294cbfdSIgor Mitsyanko IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 1132e294cbfdSIgor Mitsyanko memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs)); 1133e294cbfdSIgor Mitsyanko } 1134e294cbfdSIgor Mitsyanko 1135e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info, 1136e294cbfdSIgor Mitsyanko struct ieee80211_sta_vht_cap *bcap) 1137e294cbfdSIgor Mitsyanko { 1138e294cbfdSIgor Mitsyanko const struct ieee80211_vht_cap *vht_cap = 1139e294cbfdSIgor Mitsyanko (const struct ieee80211_vht_cap *)info; 1140e294cbfdSIgor Mitsyanko 1141e294cbfdSIgor Mitsyanko bcap->vht_supported = true; 1142e294cbfdSIgor Mitsyanko bcap->cap = le32_to_cpu(vht_cap->vht_cap_info); 1143e294cbfdSIgor Mitsyanko memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs)); 1144e294cbfdSIgor Mitsyanko } 1145e294cbfdSIgor Mitsyanko 114698f44cb0SIgor Mitsyanko static int 1147e294cbfdSIgor Mitsyanko qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, 1148e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp, 114998f44cb0SIgor Mitsyanko size_t payload_len) 115098f44cb0SIgor Mitsyanko { 115198f44cb0SIgor Mitsyanko u16 tlv_type; 115298f44cb0SIgor Mitsyanko size_t tlv_len; 1153e294cbfdSIgor Mitsyanko size_t tlv_dlen; 115498f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 115598f44cb0SIgor Mitsyanko const struct qlink_tlv_channel *qchan; 115698f44cb0SIgor Mitsyanko struct ieee80211_channel *chan; 115798f44cb0SIgor Mitsyanko unsigned int chidx = 0; 115898f44cb0SIgor Mitsyanko u32 qflags; 115998f44cb0SIgor Mitsyanko 1160e294cbfdSIgor Mitsyanko memset(&band->ht_cap, 0, sizeof(band->ht_cap)); 1161e294cbfdSIgor Mitsyanko memset(&band->vht_cap, 0, sizeof(band->vht_cap)); 1162e294cbfdSIgor Mitsyanko 11634dd07d2bSSergey Matyukevich if (band->channels) { 11644dd07d2bSSergey Matyukevich if (band->n_channels == resp->num_chans) { 11654dd07d2bSSergey Matyukevich memset(band->channels, 0, 11664dd07d2bSSergey Matyukevich sizeof(*band->channels) * band->n_channels); 11674dd07d2bSSergey Matyukevich } else { 116898f44cb0SIgor Mitsyanko kfree(band->channels); 11694dd07d2bSSergey Matyukevich band->n_channels = 0; 117098f44cb0SIgor Mitsyanko band->channels = NULL; 11714dd07d2bSSergey Matyukevich } 11724dd07d2bSSergey Matyukevich } 117398f44cb0SIgor Mitsyanko 117498f44cb0SIgor Mitsyanko band->n_channels = resp->num_chans; 117598f44cb0SIgor Mitsyanko if (band->n_channels == 0) 117698f44cb0SIgor Mitsyanko return 0; 117798f44cb0SIgor Mitsyanko 11784dd07d2bSSergey Matyukevich if (!band->channels) 11794dd07d2bSSergey Matyukevich band->channels = kcalloc(band->n_channels, sizeof(*chan), 11804dd07d2bSSergey Matyukevich GFP_KERNEL); 118198f44cb0SIgor Mitsyanko if (!band->channels) { 118298f44cb0SIgor Mitsyanko band->n_channels = 0; 118398f44cb0SIgor Mitsyanko return -ENOMEM; 118498f44cb0SIgor Mitsyanko } 118598f44cb0SIgor Mitsyanko 118698f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)resp->info; 118798f44cb0SIgor Mitsyanko 118898f44cb0SIgor Mitsyanko while (payload_len >= sizeof(*tlv)) { 118998f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 1190e294cbfdSIgor Mitsyanko tlv_dlen = le16_to_cpu(tlv->len); 1191e294cbfdSIgor Mitsyanko tlv_len = tlv_dlen + sizeof(*tlv); 119298f44cb0SIgor Mitsyanko 119398f44cb0SIgor Mitsyanko if (tlv_len > payload_len) { 119498f44cb0SIgor Mitsyanko pr_warn("malformed TLV 0x%.2X; LEN: %zu\n", 119598f44cb0SIgor Mitsyanko tlv_type, tlv_len); 119698f44cb0SIgor Mitsyanko goto error_ret; 119798f44cb0SIgor Mitsyanko } 119898f44cb0SIgor Mitsyanko 119998f44cb0SIgor Mitsyanko switch (tlv_type) { 120098f44cb0SIgor Mitsyanko case QTN_TLV_ID_CHANNEL: 120198f44cb0SIgor Mitsyanko if (unlikely(tlv_len != sizeof(*qchan))) { 120298f44cb0SIgor Mitsyanko pr_err("invalid channel TLV len %zu\n", 120398f44cb0SIgor Mitsyanko tlv_len); 120498f44cb0SIgor Mitsyanko goto error_ret; 120598f44cb0SIgor Mitsyanko } 120698f44cb0SIgor Mitsyanko 120798f44cb0SIgor Mitsyanko if (chidx == band->n_channels) { 120898f44cb0SIgor Mitsyanko pr_err("too many channel TLVs\n"); 120998f44cb0SIgor Mitsyanko goto error_ret; 121098f44cb0SIgor Mitsyanko } 121198f44cb0SIgor Mitsyanko 121298f44cb0SIgor Mitsyanko qchan = (const struct qlink_tlv_channel *)tlv; 121398f44cb0SIgor Mitsyanko chan = &band->channels[chidx++]; 121498f44cb0SIgor Mitsyanko qflags = le32_to_cpu(qchan->flags); 121598f44cb0SIgor Mitsyanko 121698f44cb0SIgor Mitsyanko chan->hw_value = le16_to_cpu(qchan->hw_value); 121798f44cb0SIgor Mitsyanko chan->band = band->band; 121898f44cb0SIgor Mitsyanko chan->center_freq = le16_to_cpu(qchan->center_freq); 121998f44cb0SIgor Mitsyanko chan->max_antenna_gain = (int)qchan->max_antenna_gain; 122098f44cb0SIgor Mitsyanko chan->max_power = (int)qchan->max_power; 122198f44cb0SIgor Mitsyanko chan->max_reg_power = (int)qchan->max_reg_power; 122298f44cb0SIgor Mitsyanko chan->beacon_found = qchan->beacon_found; 122398f44cb0SIgor Mitsyanko chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms); 122498f44cb0SIgor Mitsyanko chan->flags = 0; 122598f44cb0SIgor Mitsyanko 122698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_DISABLED) 122798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_DISABLED; 122898f44cb0SIgor Mitsyanko 122998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_IR) 123098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_IR; 123198f44cb0SIgor Mitsyanko 123298f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40PLUS) 123398f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40PLUS; 123498f44cb0SIgor Mitsyanko 123598f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40MINUS) 123698f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40MINUS; 123798f44cb0SIgor Mitsyanko 123898f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_OFDM) 123998f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_OFDM; 124098f44cb0SIgor Mitsyanko 124198f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_80MHZ) 124298f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_80MHZ; 124398f44cb0SIgor Mitsyanko 124498f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_160MHZ) 124598f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_160MHZ; 124698f44cb0SIgor Mitsyanko 124798f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_INDOOR_ONLY) 124898f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_INDOOR_ONLY; 124998f44cb0SIgor Mitsyanko 125098f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_IR_CONCURRENT) 125198f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_IR_CONCURRENT; 125298f44cb0SIgor Mitsyanko 125398f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_20MHZ) 125498f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_20MHZ; 125598f44cb0SIgor Mitsyanko 125698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_10MHZ) 125798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_10MHZ; 125898f44cb0SIgor Mitsyanko 125998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_RADAR) { 126098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_RADAR; 126198f44cb0SIgor Mitsyanko chan->dfs_state_entered = jiffies; 126298f44cb0SIgor Mitsyanko 126398f44cb0SIgor Mitsyanko if (qchan->dfs_state == QLINK_DFS_USABLE) 126498f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_USABLE; 126598f44cb0SIgor Mitsyanko else if (qchan->dfs_state == 126698f44cb0SIgor Mitsyanko QLINK_DFS_AVAILABLE) 126798f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_AVAILABLE; 126898f44cb0SIgor Mitsyanko else 126998f44cb0SIgor Mitsyanko chan->dfs_state = 127098f44cb0SIgor Mitsyanko NL80211_DFS_UNAVAILABLE; 127198f44cb0SIgor Mitsyanko } 127298f44cb0SIgor Mitsyanko 127398f44cb0SIgor Mitsyanko pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n", 127498f44cb0SIgor Mitsyanko chan->hw_value, chan->flags, chan->max_power, 127598f44cb0SIgor Mitsyanko chan->max_reg_power); 127698f44cb0SIgor Mitsyanko break; 1277e294cbfdSIgor Mitsyanko case WLAN_EID_HT_CAPABILITY: 1278e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1279e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_ht_cap))) { 1280e294cbfdSIgor Mitsyanko pr_err("bad HTCAP TLV len %zu\n", tlv_dlen); 1281e294cbfdSIgor Mitsyanko goto error_ret; 1282e294cbfdSIgor Mitsyanko } 1283e294cbfdSIgor Mitsyanko 1284e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap); 1285e294cbfdSIgor Mitsyanko break; 1286e294cbfdSIgor Mitsyanko case WLAN_EID_VHT_CAPABILITY: 1287e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1288e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_vht_cap))) { 1289e294cbfdSIgor Mitsyanko pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen); 1290e294cbfdSIgor Mitsyanko goto error_ret; 1291e294cbfdSIgor Mitsyanko } 1292e294cbfdSIgor Mitsyanko 1293e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_vhtcap(tlv->val, 1294e294cbfdSIgor Mitsyanko &band->vht_cap); 1295e294cbfdSIgor Mitsyanko break; 129698f44cb0SIgor Mitsyanko default: 129798f44cb0SIgor Mitsyanko pr_warn("unknown TLV type: %#x\n", tlv_type); 129898f44cb0SIgor Mitsyanko break; 129998f44cb0SIgor Mitsyanko } 130098f44cb0SIgor Mitsyanko 130198f44cb0SIgor Mitsyanko payload_len -= tlv_len; 1302e294cbfdSIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_dlen); 130398f44cb0SIgor Mitsyanko } 130498f44cb0SIgor Mitsyanko 130598f44cb0SIgor Mitsyanko if (payload_len) { 130698f44cb0SIgor Mitsyanko pr_err("malformed TLV buf; bytes left: %zu\n", payload_len); 130798f44cb0SIgor Mitsyanko goto error_ret; 130898f44cb0SIgor Mitsyanko } 130998f44cb0SIgor Mitsyanko 131098f44cb0SIgor Mitsyanko if (band->n_channels != chidx) { 131198f44cb0SIgor Mitsyanko pr_err("channel count mismatch: reported=%d, parsed=%d\n", 131298f44cb0SIgor Mitsyanko band->n_channels, chidx); 131398f44cb0SIgor Mitsyanko goto error_ret; 131498f44cb0SIgor Mitsyanko } 131598f44cb0SIgor Mitsyanko 131698f44cb0SIgor Mitsyanko return 0; 131798f44cb0SIgor Mitsyanko 131898f44cb0SIgor Mitsyanko error_ret: 131998f44cb0SIgor Mitsyanko kfree(band->channels); 132098f44cb0SIgor Mitsyanko band->channels = NULL; 132198f44cb0SIgor Mitsyanko band->n_channels = 0; 132298f44cb0SIgor Mitsyanko 132398f44cb0SIgor Mitsyanko return -EINVAL; 132498f44cb0SIgor Mitsyanko } 132598f44cb0SIgor Mitsyanko 132698f44cb0SIgor Mitsyanko static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, 132798f44cb0SIgor Mitsyanko const u8 *payload, size_t payload_len) 132898f44cb0SIgor Mitsyanko { 132998f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 133098f44cb0SIgor Mitsyanko struct qlink_tlv_frag_rts_thr *phy_thr; 133198f44cb0SIgor Mitsyanko struct qlink_tlv_rlimit *limit; 133298f44cb0SIgor Mitsyanko struct qlink_tlv_cclass *class; 133398f44cb0SIgor Mitsyanko u16 tlv_type; 133498f44cb0SIgor Mitsyanko u16 tlv_value_len; 133598f44cb0SIgor Mitsyanko size_t tlv_full_len; 133698f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 133798f44cb0SIgor Mitsyanko 133898f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 133998f44cb0SIgor Mitsyanko 134098f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)payload; 134198f44cb0SIgor Mitsyanko while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 134298f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 134398f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 134498f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 134598f44cb0SIgor Mitsyanko 134698f44cb0SIgor Mitsyanko if (tlv_full_len > payload_len) { 134798f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 134898f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 134998f44cb0SIgor Mitsyanko return -EINVAL; 135098f44cb0SIgor Mitsyanko } 135198f44cb0SIgor Mitsyanko 135298f44cb0SIgor Mitsyanko switch (tlv_type) { 135398f44cb0SIgor Mitsyanko case QTN_TLV_ID_FRAG_THRESH: 135498f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 135598f44cb0SIgor Mitsyanko mac_info->frag_thr = (u32)le16_to_cpu(phy_thr->thr); 135698f44cb0SIgor Mitsyanko break; 135798f44cb0SIgor Mitsyanko case QTN_TLV_ID_RTS_THRESH: 135898f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 135998f44cb0SIgor Mitsyanko mac_info->rts_thr = (u32)le16_to_cpu(phy_thr->thr); 136098f44cb0SIgor Mitsyanko break; 136198f44cb0SIgor Mitsyanko case QTN_TLV_ID_SRETRY_LIMIT: 136298f44cb0SIgor Mitsyanko limit = (void *)tlv; 136398f44cb0SIgor Mitsyanko mac_info->sretry_limit = limit->rlimit; 136498f44cb0SIgor Mitsyanko break; 136598f44cb0SIgor Mitsyanko case QTN_TLV_ID_LRETRY_LIMIT: 136698f44cb0SIgor Mitsyanko limit = (void *)tlv; 136798f44cb0SIgor Mitsyanko mac_info->lretry_limit = limit->rlimit; 136898f44cb0SIgor Mitsyanko break; 136998f44cb0SIgor Mitsyanko case QTN_TLV_ID_COVERAGE_CLASS: 137098f44cb0SIgor Mitsyanko class = (void *)tlv; 137198f44cb0SIgor Mitsyanko mac_info->coverage_class = class->cclass; 137298f44cb0SIgor Mitsyanko break; 137398f44cb0SIgor Mitsyanko default: 137498f44cb0SIgor Mitsyanko pr_err("MAC%u: Unknown TLV type: %#x\n", mac->macid, 137598f44cb0SIgor Mitsyanko le16_to_cpu(tlv->type)); 137698f44cb0SIgor Mitsyanko break; 137798f44cb0SIgor Mitsyanko } 137898f44cb0SIgor Mitsyanko 137998f44cb0SIgor Mitsyanko payload_len -= tlv_full_len; 138098f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 138198f44cb0SIgor Mitsyanko } 138298f44cb0SIgor Mitsyanko 138398f44cb0SIgor Mitsyanko if (payload_len) { 138498f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 138598f44cb0SIgor Mitsyanko mac->macid, payload_len); 138698f44cb0SIgor Mitsyanko return -EINVAL; 138798f44cb0SIgor Mitsyanko } 138898f44cb0SIgor Mitsyanko 138998f44cb0SIgor Mitsyanko return 0; 139098f44cb0SIgor Mitsyanko } 139198f44cb0SIgor Mitsyanko 13927c04b439SSergey Matyukevich static int 13937c04b439SSergey Matyukevich qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, 13947c04b439SSergey Matyukevich const u8 *payload, size_t payload_len) 13957c04b439SSergey Matyukevich { 13967c04b439SSergey Matyukevich struct qlink_chan_stats *qlink_stats; 13977c04b439SSergey Matyukevich const struct qlink_tlv_hdr *tlv; 13987c04b439SSergey Matyukevich size_t tlv_full_len; 13997c04b439SSergey Matyukevich u16 tlv_value_len; 14007c04b439SSergey Matyukevich u16 tlv_type; 14017c04b439SSergey Matyukevich 14027c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)payload; 14037c04b439SSergey Matyukevich while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 14047c04b439SSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 14057c04b439SSergey Matyukevich tlv_value_len = le16_to_cpu(tlv->len); 14067c04b439SSergey Matyukevich tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 14077c04b439SSergey Matyukevich if (tlv_full_len > payload_len) { 14087c04b439SSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 14097c04b439SSergey Matyukevich tlv_type, tlv_value_len); 14107c04b439SSergey Matyukevich return -EINVAL; 14117c04b439SSergey Matyukevich } 14127c04b439SSergey Matyukevich switch (tlv_type) { 14137c04b439SSergey Matyukevich case QTN_TLV_ID_CHANNEL_STATS: 14147c04b439SSergey Matyukevich if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { 14157c04b439SSergey Matyukevich pr_err("invalid CHANNEL_STATS entry size\n"); 14167c04b439SSergey Matyukevich return -EINVAL; 14177c04b439SSergey Matyukevich } 14187c04b439SSergey Matyukevich 14197c04b439SSergey Matyukevich qlink_stats = (void *)tlv->val; 14207c04b439SSergey Matyukevich 14217c04b439SSergey Matyukevich stats->chan_num = le32_to_cpu(qlink_stats->chan_num); 14227c04b439SSergey Matyukevich stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); 14237c04b439SSergey Matyukevich stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); 14247c04b439SSergey Matyukevich stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); 14257c04b439SSergey Matyukevich stats->cca_try = le32_to_cpu(qlink_stats->cca_try); 14267c04b439SSergey Matyukevich stats->chan_noise = qlink_stats->chan_noise; 14277c04b439SSergey Matyukevich 14287c04b439SSergey Matyukevich pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", 14297c04b439SSergey Matyukevich stats->chan_num, stats->cca_try, 14307c04b439SSergey Matyukevich stats->cca_busy, stats->chan_noise); 14317c04b439SSergey Matyukevich break; 14327c04b439SSergey Matyukevich default: 14337c04b439SSergey Matyukevich pr_warn("Unknown TLV type: %#x\n", 14347c04b439SSergey Matyukevich le16_to_cpu(tlv->type)); 14357c04b439SSergey Matyukevich } 14367c04b439SSergey Matyukevich payload_len -= tlv_full_len; 14377c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 14387c04b439SSergey Matyukevich } 14397c04b439SSergey Matyukevich 14407c04b439SSergey Matyukevich if (payload_len) { 14417c04b439SSergey Matyukevich pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); 14427c04b439SSergey Matyukevich return -EINVAL; 14437c04b439SSergey Matyukevich } 14447c04b439SSergey Matyukevich 14457c04b439SSergey Matyukevich return 0; 14467c04b439SSergey Matyukevich } 14477c04b439SSergey Matyukevich 144898f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) 144998f44cb0SIgor Mitsyanko { 145098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 145198f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp; 145298f44cb0SIgor Mitsyanko size_t var_data_len; 145398f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 145498f44cb0SIgor Mitsyanko int ret = 0; 145598f44cb0SIgor Mitsyanko 145698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 145798f44cb0SIgor Mitsyanko QLINK_CMD_MAC_INFO, 145898f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 145998f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 146098f44cb0SIgor Mitsyanko return -ENOMEM; 146198f44cb0SIgor Mitsyanko 146298f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 146398f44cb0SIgor Mitsyanko 146498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 146598f44cb0SIgor Mitsyanko sizeof(*resp), &var_data_len); 146698f44cb0SIgor Mitsyanko if (unlikely(ret)) 146798f44cb0SIgor Mitsyanko goto out; 146898f44cb0SIgor Mitsyanko 146998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 147098f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 147198f44cb0SIgor Mitsyanko ret = -EFAULT; 147298f44cb0SIgor Mitsyanko goto out; 147398f44cb0SIgor Mitsyanko } 147498f44cb0SIgor Mitsyanko 147598f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_mac_info *)resp_skb->data; 147698f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(mac, resp); 147798f44cb0SIgor Mitsyanko ret = qtnf_parse_variable_mac_info(mac, resp->var_info, var_data_len); 147898f44cb0SIgor Mitsyanko 147998f44cb0SIgor Mitsyanko out: 148098f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 148198f44cb0SIgor Mitsyanko consume_skb(resp_skb); 148298f44cb0SIgor Mitsyanko 148398f44cb0SIgor Mitsyanko return ret; 148498f44cb0SIgor Mitsyanko } 148598f44cb0SIgor Mitsyanko 148698f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) 148798f44cb0SIgor Mitsyanko { 148898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 148998f44cb0SIgor Mitsyanko const struct qlink_resp_get_hw_info *resp; 149098f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 149198f44cb0SIgor Mitsyanko int ret = 0; 14924dd07d2bSSergey Matyukevich size_t info_len; 149398f44cb0SIgor Mitsyanko 149498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 149598f44cb0SIgor Mitsyanko QLINK_CMD_GET_HW_INFO, 149698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 149798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 149898f44cb0SIgor Mitsyanko return -ENOMEM; 149998f44cb0SIgor Mitsyanko 150098f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 150198f44cb0SIgor Mitsyanko 150298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, 15034dd07d2bSSergey Matyukevich sizeof(*resp), &info_len); 150498f44cb0SIgor Mitsyanko 150598f44cb0SIgor Mitsyanko if (unlikely(ret)) 150698f44cb0SIgor Mitsyanko goto out; 150798f44cb0SIgor Mitsyanko 150898f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 150998f44cb0SIgor Mitsyanko pr_err("cmd exec failed: 0x%.4X\n", res_code); 151098f44cb0SIgor Mitsyanko ret = -EFAULT; 151198f44cb0SIgor Mitsyanko goto out; 151298f44cb0SIgor Mitsyanko } 151398f44cb0SIgor Mitsyanko 151498f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; 15154dd07d2bSSergey Matyukevich ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); 151698f44cb0SIgor Mitsyanko 151798f44cb0SIgor Mitsyanko out: 151898f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 151998f44cb0SIgor Mitsyanko consume_skb(resp_skb); 152098f44cb0SIgor Mitsyanko 152198f44cb0SIgor Mitsyanko return ret; 152298f44cb0SIgor Mitsyanko } 152398f44cb0SIgor Mitsyanko 1524e294cbfdSIgor Mitsyanko int qtnf_cmd_band_info_get(struct qtnf_wmac *mac, 152598f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 152698f44cb0SIgor Mitsyanko { 152798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 152898f44cb0SIgor Mitsyanko size_t info_len; 1529e294cbfdSIgor Mitsyanko struct qlink_cmd_band_info_get *cmd; 1530e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp; 153198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 153298f44cb0SIgor Mitsyanko int ret = 0; 153398f44cb0SIgor Mitsyanko u8 qband; 153498f44cb0SIgor Mitsyanko 153598f44cb0SIgor Mitsyanko switch (band->band) { 153698f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 153798f44cb0SIgor Mitsyanko qband = QLINK_BAND_2GHZ; 153898f44cb0SIgor Mitsyanko break; 153998f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 154098f44cb0SIgor Mitsyanko qband = QLINK_BAND_5GHZ; 154198f44cb0SIgor Mitsyanko break; 154298f44cb0SIgor Mitsyanko case NL80211_BAND_60GHZ: 154398f44cb0SIgor Mitsyanko qband = QLINK_BAND_60GHZ; 154498f44cb0SIgor Mitsyanko break; 154598f44cb0SIgor Mitsyanko default: 154698f44cb0SIgor Mitsyanko return -EINVAL; 154798f44cb0SIgor Mitsyanko } 154898f44cb0SIgor Mitsyanko 1549bc0384eeSColin Ian King cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 1550e294cbfdSIgor Mitsyanko QLINK_CMD_BAND_INFO_GET, 1551bc0384eeSColin Ian King sizeof(*cmd)); 1552bc0384eeSColin Ian King if (!cmd_skb) 1553bc0384eeSColin Ian King return -ENOMEM; 1554bc0384eeSColin Ian King 1555e294cbfdSIgor Mitsyanko cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data; 155698f44cb0SIgor Mitsyanko cmd->band = qband; 15579ef75095SSergey Matyukevich 15589ef75095SSergey Matyukevich qtnf_bus_lock(mac->bus); 15599ef75095SSergey Matyukevich 156098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 156198f44cb0SIgor Mitsyanko sizeof(*resp), &info_len); 156298f44cb0SIgor Mitsyanko 156398f44cb0SIgor Mitsyanko if (unlikely(ret)) 156498f44cb0SIgor Mitsyanko goto out; 156598f44cb0SIgor Mitsyanko 156698f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 156798f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 156898f44cb0SIgor Mitsyanko ret = -EFAULT; 156998f44cb0SIgor Mitsyanko goto out; 157098f44cb0SIgor Mitsyanko } 157198f44cb0SIgor Mitsyanko 1572e294cbfdSIgor Mitsyanko resp = (struct qlink_resp_band_info_get *)resp_skb->data; 157398f44cb0SIgor Mitsyanko if (resp->band != qband) { 157498f44cb0SIgor Mitsyanko pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid, 157598f44cb0SIgor Mitsyanko resp->band, qband); 157698f44cb0SIgor Mitsyanko ret = -EINVAL; 157798f44cb0SIgor Mitsyanko goto out; 157898f44cb0SIgor Mitsyanko } 157998f44cb0SIgor Mitsyanko 1580e294cbfdSIgor Mitsyanko ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len); 158198f44cb0SIgor Mitsyanko 158298f44cb0SIgor Mitsyanko out: 15839ef75095SSergey Matyukevich qtnf_bus_unlock(mac->bus); 158498f44cb0SIgor Mitsyanko consume_skb(resp_skb); 158598f44cb0SIgor Mitsyanko 158698f44cb0SIgor Mitsyanko return ret; 158798f44cb0SIgor Mitsyanko } 158898f44cb0SIgor Mitsyanko 158998f44cb0SIgor Mitsyanko int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac) 159098f44cb0SIgor Mitsyanko { 159198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 159298f44cb0SIgor Mitsyanko size_t response_size; 159398f44cb0SIgor Mitsyanko struct qlink_resp_phy_params *resp; 159498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 159598f44cb0SIgor Mitsyanko int ret = 0; 159698f44cb0SIgor Mitsyanko 159798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 159898f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_GET, 159998f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 160098f44cb0SIgor Mitsyanko if (!cmd_skb) 160198f44cb0SIgor Mitsyanko return -ENOMEM; 160298f44cb0SIgor Mitsyanko 160398f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 160498f44cb0SIgor Mitsyanko 160598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 160698f44cb0SIgor Mitsyanko sizeof(*resp), &response_size); 160798f44cb0SIgor Mitsyanko 160898f44cb0SIgor Mitsyanko if (unlikely(ret)) 160998f44cb0SIgor Mitsyanko goto out; 161098f44cb0SIgor Mitsyanko 161198f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 161298f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 161398f44cb0SIgor Mitsyanko ret = -EFAULT; 161498f44cb0SIgor Mitsyanko goto out; 161598f44cb0SIgor Mitsyanko } 161698f44cb0SIgor Mitsyanko 161798f44cb0SIgor Mitsyanko resp = (struct qlink_resp_phy_params *)resp_skb->data; 161898f44cb0SIgor Mitsyanko ret = qtnf_cmd_resp_proc_phy_params(mac, resp->info, response_size); 161998f44cb0SIgor Mitsyanko 162098f44cb0SIgor Mitsyanko out: 162198f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 162298f44cb0SIgor Mitsyanko consume_skb(resp_skb); 162398f44cb0SIgor Mitsyanko 162498f44cb0SIgor Mitsyanko return ret; 162598f44cb0SIgor Mitsyanko } 162698f44cb0SIgor Mitsyanko 162798f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) 162898f44cb0SIgor Mitsyanko { 162998f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 163098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 163198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 163298f44cb0SIgor Mitsyanko int ret = 0; 163398f44cb0SIgor Mitsyanko 163498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 163598f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_SET, 163698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 163798f44cb0SIgor Mitsyanko if (!cmd_skb) 163898f44cb0SIgor Mitsyanko return -ENOMEM; 163998f44cb0SIgor Mitsyanko 164098f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 164198f44cb0SIgor Mitsyanko 164298f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_FRAG_THRESHOLD) 164398f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_FRAG_THRESH, 164498f44cb0SIgor Mitsyanko wiphy->frag_threshold); 164598f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_RTS_THRESHOLD) 164698f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_RTS_THRESH, 164798f44cb0SIgor Mitsyanko wiphy->rts_threshold); 164898f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_COVERAGE_CLASS) 164998f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, 165098f44cb0SIgor Mitsyanko wiphy->coverage_class); 165198f44cb0SIgor Mitsyanko 165298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 165398f44cb0SIgor Mitsyanko 165498f44cb0SIgor Mitsyanko if (unlikely(ret)) 165598f44cb0SIgor Mitsyanko goto out; 165698f44cb0SIgor Mitsyanko 165798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 165898f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 165998f44cb0SIgor Mitsyanko ret = -EFAULT; 166098f44cb0SIgor Mitsyanko goto out; 166198f44cb0SIgor Mitsyanko } 166298f44cb0SIgor Mitsyanko 166398f44cb0SIgor Mitsyanko out: 166498f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 166598f44cb0SIgor Mitsyanko return ret; 166698f44cb0SIgor Mitsyanko } 166798f44cb0SIgor Mitsyanko 166898f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus) 166998f44cb0SIgor Mitsyanko { 167098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 167198f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 167298f44cb0SIgor Mitsyanko int ret = 0; 167398f44cb0SIgor Mitsyanko 167498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 167598f44cb0SIgor Mitsyanko QLINK_CMD_FW_INIT, 167698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 167798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 167898f44cb0SIgor Mitsyanko return -ENOMEM; 167998f44cb0SIgor Mitsyanko 168098f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 168198f44cb0SIgor Mitsyanko 168298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(bus, cmd_skb, &res_code); 168398f44cb0SIgor Mitsyanko 168498f44cb0SIgor Mitsyanko if (unlikely(ret)) 168598f44cb0SIgor Mitsyanko goto out; 168698f44cb0SIgor Mitsyanko 168798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 168898f44cb0SIgor Mitsyanko pr_err("cmd exec failed: 0x%.4X\n", res_code); 168998f44cb0SIgor Mitsyanko ret = -EFAULT; 169098f44cb0SIgor Mitsyanko goto out; 169198f44cb0SIgor Mitsyanko } 169298f44cb0SIgor Mitsyanko 169398f44cb0SIgor Mitsyanko out: 169498f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 169598f44cb0SIgor Mitsyanko return ret; 169698f44cb0SIgor Mitsyanko } 169798f44cb0SIgor Mitsyanko 169898f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus) 169998f44cb0SIgor Mitsyanko { 170098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 170198f44cb0SIgor Mitsyanko 170298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 170398f44cb0SIgor Mitsyanko QLINK_CMD_FW_DEINIT, 170498f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 170598f44cb0SIgor Mitsyanko if (!cmd_skb) 170698f44cb0SIgor Mitsyanko return; 170798f44cb0SIgor Mitsyanko 170898f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 170998f44cb0SIgor Mitsyanko 171098f44cb0SIgor Mitsyanko qtnf_cmd_send(bus, cmd_skb, NULL); 171198f44cb0SIgor Mitsyanko 171298f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 171398f44cb0SIgor Mitsyanko } 171498f44cb0SIgor Mitsyanko 171598f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 171698f44cb0SIgor Mitsyanko const u8 *mac_addr, struct key_params *params) 171798f44cb0SIgor Mitsyanko { 171898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 171998f44cb0SIgor Mitsyanko struct qlink_cmd_add_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_ADD_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_add_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->cipher = cpu_to_le32(params->cipher); 173998f44cb0SIgor Mitsyanko cmd->key_index = key_index; 174098f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 174198f44cb0SIgor Mitsyanko 174298f44cb0SIgor Mitsyanko if (params->key && params->key_len > 0) 174398f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY, 174498f44cb0SIgor Mitsyanko params->key, 174598f44cb0SIgor Mitsyanko params->key_len); 174698f44cb0SIgor Mitsyanko 174798f44cb0SIgor Mitsyanko if (params->seq && params->seq_len > 0) 174898f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ, 174998f44cb0SIgor Mitsyanko params->seq, 175098f44cb0SIgor Mitsyanko params->seq_len); 175198f44cb0SIgor Mitsyanko 175298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 175398f44cb0SIgor Mitsyanko if (unlikely(ret)) 175498f44cb0SIgor Mitsyanko goto out; 175598f44cb0SIgor Mitsyanko 175698f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 175798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", 175898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, res_code); 175998f44cb0SIgor Mitsyanko ret = -EFAULT; 176098f44cb0SIgor Mitsyanko goto out; 176198f44cb0SIgor Mitsyanko } 176298f44cb0SIgor Mitsyanko 176398f44cb0SIgor Mitsyanko out: 176498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 176598f44cb0SIgor Mitsyanko return ret; 176698f44cb0SIgor Mitsyanko } 176798f44cb0SIgor Mitsyanko 176898f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 176998f44cb0SIgor Mitsyanko const u8 *mac_addr) 177098f44cb0SIgor Mitsyanko { 177198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 177298f44cb0SIgor Mitsyanko struct qlink_cmd_del_key *cmd; 177398f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 177498f44cb0SIgor Mitsyanko int ret = 0; 177598f44cb0SIgor Mitsyanko 177698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 177798f44cb0SIgor Mitsyanko QLINK_CMD_DEL_KEY, 177898f44cb0SIgor Mitsyanko sizeof(*cmd)); 177998f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 178098f44cb0SIgor Mitsyanko return -ENOMEM; 178198f44cb0SIgor Mitsyanko 178298f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 178398f44cb0SIgor Mitsyanko 178498f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_key *)cmd_skb->data; 178598f44cb0SIgor Mitsyanko 178698f44cb0SIgor Mitsyanko if (mac_addr) 178798f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 178898f44cb0SIgor Mitsyanko else 178998f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 179098f44cb0SIgor Mitsyanko 179198f44cb0SIgor Mitsyanko cmd->key_index = key_index; 179298f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 179398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 179498f44cb0SIgor Mitsyanko if (unlikely(ret)) 179598f44cb0SIgor Mitsyanko goto out; 179698f44cb0SIgor Mitsyanko 179798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 179898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", 179998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, res_code); 180098f44cb0SIgor Mitsyanko ret = -EFAULT; 180198f44cb0SIgor Mitsyanko goto out; 180298f44cb0SIgor Mitsyanko } 180398f44cb0SIgor Mitsyanko 180498f44cb0SIgor Mitsyanko out: 180598f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 180698f44cb0SIgor Mitsyanko return ret; 180798f44cb0SIgor Mitsyanko } 180898f44cb0SIgor Mitsyanko 180998f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index, 181098f44cb0SIgor Mitsyanko bool unicast, bool multicast) 181198f44cb0SIgor Mitsyanko { 181298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 181398f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_key *cmd; 181498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 181598f44cb0SIgor Mitsyanko int ret = 0; 181698f44cb0SIgor Mitsyanko 181798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 181898f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_KEY, 181998f44cb0SIgor Mitsyanko sizeof(*cmd)); 182098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 182198f44cb0SIgor Mitsyanko return -ENOMEM; 182298f44cb0SIgor Mitsyanko 182398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 182498f44cb0SIgor Mitsyanko 182598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data; 182698f44cb0SIgor Mitsyanko cmd->key_index = key_index; 182798f44cb0SIgor Mitsyanko cmd->unicast = unicast; 182898f44cb0SIgor Mitsyanko cmd->multicast = multicast; 182998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 183098f44cb0SIgor Mitsyanko if (unlikely(ret)) 183198f44cb0SIgor Mitsyanko goto out; 183298f44cb0SIgor Mitsyanko 183398f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 183498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 183598f44cb0SIgor Mitsyanko vif->vifid, res_code); 183698f44cb0SIgor Mitsyanko ret = -EFAULT; 183798f44cb0SIgor Mitsyanko goto out; 183898f44cb0SIgor Mitsyanko } 183998f44cb0SIgor Mitsyanko 184098f44cb0SIgor Mitsyanko out: 184198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 184298f44cb0SIgor Mitsyanko return ret; 184398f44cb0SIgor Mitsyanko } 184498f44cb0SIgor Mitsyanko 184598f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index) 184698f44cb0SIgor Mitsyanko { 184798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 184898f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_mgmt_key *cmd; 184998f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 185098f44cb0SIgor Mitsyanko int ret = 0; 185198f44cb0SIgor Mitsyanko 185298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 185398f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_MGMT_KEY, 185498f44cb0SIgor Mitsyanko sizeof(*cmd)); 185598f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 185698f44cb0SIgor Mitsyanko return -ENOMEM; 185798f44cb0SIgor Mitsyanko 185898f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 185998f44cb0SIgor Mitsyanko 186098f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data; 186198f44cb0SIgor Mitsyanko cmd->key_index = key_index; 186298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 186398f44cb0SIgor Mitsyanko if (unlikely(ret)) 186498f44cb0SIgor Mitsyanko goto out; 186598f44cb0SIgor Mitsyanko 186698f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 186798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 186898f44cb0SIgor Mitsyanko vif->vifid, res_code); 186998f44cb0SIgor Mitsyanko ret = -EFAULT; 187098f44cb0SIgor Mitsyanko goto out; 187198f44cb0SIgor Mitsyanko } 187298f44cb0SIgor Mitsyanko 187398f44cb0SIgor Mitsyanko out: 187498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 187598f44cb0SIgor Mitsyanko return ret; 187698f44cb0SIgor Mitsyanko } 187798f44cb0SIgor Mitsyanko 187898f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags) 187998f44cb0SIgor Mitsyanko { 188098f44cb0SIgor Mitsyanko u32 code = 0; 188198f44cb0SIgor Mitsyanko 188298f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED)) 188398f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHORIZED; 188498f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) 188598f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_SHORT_PREAMBLE; 188698f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_WME)) 188798f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_WME; 188898f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_MFP)) 188998f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_MFP; 189098f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED)) 189198f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHENTICATED; 189298f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER)) 189398f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_TDLS_PEER; 189498f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED)) 189598f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_ASSOCIATED; 189698f44cb0SIgor Mitsyanko return code; 189798f44cb0SIgor Mitsyanko } 189898f44cb0SIgor Mitsyanko 189998f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, 190098f44cb0SIgor Mitsyanko struct station_parameters *params) 190198f44cb0SIgor Mitsyanko { 190298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 190398f44cb0SIgor Mitsyanko struct qlink_cmd_change_sta *cmd; 190498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 190598f44cb0SIgor Mitsyanko int ret = 0; 190698f44cb0SIgor Mitsyanko 190798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 190898f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_STA, 190998f44cb0SIgor Mitsyanko sizeof(*cmd)); 191098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 191198f44cb0SIgor Mitsyanko return -ENOMEM; 191298f44cb0SIgor Mitsyanko 191398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 191498f44cb0SIgor Mitsyanko 191598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; 191698f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, mac); 1917805b28c0SSergey Matyukevich 1918805b28c0SSergey Matyukevich switch (vif->wdev.iftype) { 1919805b28c0SSergey Matyukevich case NL80211_IFTYPE_AP: 1920805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); 192198f44cb0SIgor Mitsyanko cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( 192298f44cb0SIgor Mitsyanko params->sta_flags_mask)); 192398f44cb0SIgor Mitsyanko cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( 192498f44cb0SIgor Mitsyanko params->sta_flags_set)); 1925805b28c0SSergey Matyukevich break; 1926805b28c0SSergey Matyukevich case NL80211_IFTYPE_STATION: 1927805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 1928805b28c0SSergey Matyukevich cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( 1929805b28c0SSergey Matyukevich params->sta_flags_mask)); 1930805b28c0SSergey Matyukevich cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( 1931805b28c0SSergey Matyukevich params->sta_flags_set)); 1932805b28c0SSergey Matyukevich break; 1933805b28c0SSergey Matyukevich default: 1934805b28c0SSergey Matyukevich pr_err("unsupported iftype %d\n", vif->wdev.iftype); 1935805b28c0SSergey Matyukevich ret = -EINVAL; 1936805b28c0SSergey Matyukevich goto out; 1937805b28c0SSergey Matyukevich } 193898f44cb0SIgor Mitsyanko 193998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 194098f44cb0SIgor Mitsyanko if (unlikely(ret)) 194198f44cb0SIgor Mitsyanko goto out; 194298f44cb0SIgor Mitsyanko 194398f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 194498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 194598f44cb0SIgor Mitsyanko vif->vifid, res_code); 194698f44cb0SIgor Mitsyanko ret = -EFAULT; 194798f44cb0SIgor Mitsyanko goto out; 194898f44cb0SIgor Mitsyanko } 194998f44cb0SIgor Mitsyanko 195098f44cb0SIgor Mitsyanko out: 195198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 195298f44cb0SIgor Mitsyanko return ret; 195398f44cb0SIgor Mitsyanko } 195498f44cb0SIgor Mitsyanko 195598f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif, 195698f44cb0SIgor Mitsyanko struct station_del_parameters *params) 195798f44cb0SIgor Mitsyanko { 195898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 195998f44cb0SIgor Mitsyanko struct qlink_cmd_del_sta *cmd; 196098f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 196198f44cb0SIgor Mitsyanko int ret = 0; 196298f44cb0SIgor Mitsyanko 196398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 196498f44cb0SIgor Mitsyanko QLINK_CMD_DEL_STA, 196598f44cb0SIgor Mitsyanko sizeof(*cmd)); 196698f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 196798f44cb0SIgor Mitsyanko return -ENOMEM; 196898f44cb0SIgor Mitsyanko 196998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 197098f44cb0SIgor Mitsyanko 197198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_sta *)cmd_skb->data; 197298f44cb0SIgor Mitsyanko 197398f44cb0SIgor Mitsyanko if (params->mac) 197498f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, params->mac); 197598f44cb0SIgor Mitsyanko else 197698f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->sta_addr); /* flush all stations */ 197798f44cb0SIgor Mitsyanko 197898f44cb0SIgor Mitsyanko cmd->subtype = params->subtype; 197998f44cb0SIgor Mitsyanko cmd->reason_code = cpu_to_le16(params->reason_code); 198098f44cb0SIgor Mitsyanko 198198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 198298f44cb0SIgor Mitsyanko if (unlikely(ret)) 198398f44cb0SIgor Mitsyanko goto out; 198498f44cb0SIgor Mitsyanko 198598f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 198698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 198798f44cb0SIgor Mitsyanko vif->vifid, res_code); 198898f44cb0SIgor Mitsyanko ret = -EFAULT; 198998f44cb0SIgor Mitsyanko goto out; 199098f44cb0SIgor Mitsyanko } 199198f44cb0SIgor Mitsyanko 199298f44cb0SIgor Mitsyanko out: 199398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 199498f44cb0SIgor Mitsyanko return ret; 199598f44cb0SIgor Mitsyanko } 199698f44cb0SIgor Mitsyanko 199798f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac) 199898f44cb0SIgor Mitsyanko { 199998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 200098f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 200198f44cb0SIgor Mitsyanko struct ieee80211_channel *sc; 200298f44cb0SIgor Mitsyanko struct cfg80211_scan_request *scan_req = mac->scan_req; 200398f44cb0SIgor Mitsyanko struct qlink_tlv_channel *qchan; 200498f44cb0SIgor Mitsyanko int n_channels; 200598f44cb0SIgor Mitsyanko int count = 0; 200698f44cb0SIgor Mitsyanko int ret; 200798f44cb0SIgor Mitsyanko u32 flags; 200898f44cb0SIgor Mitsyanko 200998f44cb0SIgor Mitsyanko if (scan_req->n_ssids > QTNF_MAX_SSID_LIST_LENGTH) { 201098f44cb0SIgor Mitsyanko pr_err("MAC%u: too many SSIDs in scan request\n", mac->macid); 201198f44cb0SIgor Mitsyanko return -EINVAL; 201298f44cb0SIgor Mitsyanko } 201398f44cb0SIgor Mitsyanko 201498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 201598f44cb0SIgor Mitsyanko QLINK_CMD_SCAN, 201698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 201798f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 201898f44cb0SIgor Mitsyanko return -ENOMEM; 201998f44cb0SIgor Mitsyanko 202098f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 202198f44cb0SIgor Mitsyanko 202298f44cb0SIgor Mitsyanko if (scan_req->n_ssids != 0) { 202398f44cb0SIgor Mitsyanko while (count < scan_req->n_ssids) { 202498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, 202598f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid, 202698f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid_len); 202798f44cb0SIgor Mitsyanko count++; 202898f44cb0SIgor Mitsyanko } 202998f44cb0SIgor Mitsyanko } 203098f44cb0SIgor Mitsyanko 203198f44cb0SIgor Mitsyanko if (scan_req->ie_len != 0) 203298f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, 203398f44cb0SIgor Mitsyanko scan_req->ie, 203498f44cb0SIgor Mitsyanko scan_req->ie_len); 203598f44cb0SIgor Mitsyanko 203698f44cb0SIgor Mitsyanko if (scan_req->n_channels) { 203798f44cb0SIgor Mitsyanko n_channels = scan_req->n_channels; 203898f44cb0SIgor Mitsyanko count = 0; 203998f44cb0SIgor Mitsyanko 204098f44cb0SIgor Mitsyanko while (n_channels != 0) { 204198f44cb0SIgor Mitsyanko sc = scan_req->channels[count]; 204298f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_DISABLED) { 204398f44cb0SIgor Mitsyanko n_channels--; 204498f44cb0SIgor Mitsyanko continue; 204598f44cb0SIgor Mitsyanko } 204698f44cb0SIgor Mitsyanko 204798f44cb0SIgor Mitsyanko pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n", 204898f44cb0SIgor Mitsyanko mac->macid, sc->hw_value, sc->center_freq, 204998f44cb0SIgor Mitsyanko sc->flags); 2050b080db58SJohannes Berg qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); 205198f44cb0SIgor Mitsyanko flags = 0; 205298f44cb0SIgor Mitsyanko 205398f44cb0SIgor Mitsyanko qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); 205498f44cb0SIgor Mitsyanko qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - 205598f44cb0SIgor Mitsyanko sizeof(struct qlink_tlv_hdr)); 205698f44cb0SIgor Mitsyanko qchan->center_freq = cpu_to_le16(sc->center_freq); 205798f44cb0SIgor Mitsyanko qchan->hw_value = cpu_to_le16(sc->hw_value); 205898f44cb0SIgor Mitsyanko 205998f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_NO_IR) 206098f44cb0SIgor Mitsyanko flags |= QLINK_CHAN_NO_IR; 206198f44cb0SIgor Mitsyanko 206298f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_RADAR) 206398f44cb0SIgor Mitsyanko flags |= QLINK_CHAN_RADAR; 206498f44cb0SIgor Mitsyanko 206598f44cb0SIgor Mitsyanko qchan->flags = cpu_to_le32(flags); 206698f44cb0SIgor Mitsyanko n_channels--; 206798f44cb0SIgor Mitsyanko count++; 206898f44cb0SIgor Mitsyanko } 206998f44cb0SIgor Mitsyanko } 207098f44cb0SIgor Mitsyanko 207198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 207298f44cb0SIgor Mitsyanko 207398f44cb0SIgor Mitsyanko if (unlikely(ret)) 207498f44cb0SIgor Mitsyanko goto out; 207598f44cb0SIgor Mitsyanko 207698f44cb0SIgor Mitsyanko pr_debug("MAC%u: scan started\n", mac->macid); 207798f44cb0SIgor Mitsyanko 207898f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 207998f44cb0SIgor Mitsyanko pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); 208098f44cb0SIgor Mitsyanko ret = -EFAULT; 208198f44cb0SIgor Mitsyanko goto out; 208298f44cb0SIgor Mitsyanko } 208398f44cb0SIgor Mitsyanko out: 208498f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 208598f44cb0SIgor Mitsyanko return ret; 208698f44cb0SIgor Mitsyanko } 208798f44cb0SIgor Mitsyanko 208898f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif, 208998f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 209098f44cb0SIgor Mitsyanko { 209198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 209298f44cb0SIgor Mitsyanko struct qlink_cmd_connect *cmd; 2093d23d1361SIgor Mitsyanko struct qlink_auth_encr *aen; 209498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 209598f44cb0SIgor Mitsyanko int ret; 209698f44cb0SIgor Mitsyanko int i; 20979766d1ddSIgor Mitsyanko u32 connect_flags = 0; 209898f44cb0SIgor Mitsyanko 209998f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 210098f44cb0SIgor Mitsyanko QLINK_CMD_CONNECT, 210198f44cb0SIgor Mitsyanko sizeof(*cmd)); 210298f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 210398f44cb0SIgor Mitsyanko return -ENOMEM; 210498f44cb0SIgor Mitsyanko 210598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_connect *)cmd_skb->data; 210698f44cb0SIgor Mitsyanko 21079766d1ddSIgor Mitsyanko ether_addr_copy(cmd->bssid, vif->bssid); 210898f44cb0SIgor Mitsyanko 210996d4eaf2SIgor Mitsyanko if (sme->channel) 211096d4eaf2SIgor Mitsyanko cmd->channel = cpu_to_le16(sme->channel->hw_value); 211196d4eaf2SIgor Mitsyanko else 211296d4eaf2SIgor Mitsyanko cmd->channel = 0; 211398f44cb0SIgor Mitsyanko 21149766d1ddSIgor Mitsyanko if ((sme->bg_scan_period > 0) && 21159766d1ddSIgor Mitsyanko (sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) 21169766d1ddSIgor Mitsyanko cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period); 21179766d1ddSIgor Mitsyanko else if (sme->bg_scan_period == -1) 21189766d1ddSIgor Mitsyanko cmd->bg_scan_period = cpu_to_le16(QTNF_DEFAULT_BG_SCAN_PERIOD); 21199766d1ddSIgor Mitsyanko else 21209766d1ddSIgor Mitsyanko cmd->bg_scan_period = 0; /* disabled */ 21219766d1ddSIgor Mitsyanko 21229766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_HT) 21239766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; 21249766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_VHT) 21259766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; 21269766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_USE_RRM) 21279766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_USE_RRM; 21289766d1ddSIgor Mitsyanko 21299766d1ddSIgor Mitsyanko cmd->flags = cpu_to_le32(connect_flags); 213098f44cb0SIgor Mitsyanko 2131d23d1361SIgor Mitsyanko aen = &cmd->aen; 2132d23d1361SIgor Mitsyanko aen->auth_type = sme->auth_type; 2133d23d1361SIgor Mitsyanko aen->privacy = !!sme->privacy; 2134d23d1361SIgor Mitsyanko aen->mfp = sme->mfp; 2135d23d1361SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions); 2136d23d1361SIgor Mitsyanko aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group); 2137d23d1361SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise); 213898f44cb0SIgor Mitsyanko 213998f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2140d23d1361SIgor Mitsyanko aen->ciphers_pairwise[i] = 2141d23d1361SIgor Mitsyanko cpu_to_le32(sme->crypto.ciphers_pairwise[i]); 214298f44cb0SIgor Mitsyanko 2143d23d1361SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites); 214498f44cb0SIgor Mitsyanko 214598f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2146d23d1361SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]); 214798f44cb0SIgor Mitsyanko 2148d23d1361SIgor Mitsyanko aen->control_port = sme->crypto.control_port; 2149d23d1361SIgor Mitsyanko aen->control_port_no_encrypt = 21509766d1ddSIgor Mitsyanko sme->crypto.control_port_no_encrypt; 2151d23d1361SIgor Mitsyanko aen->control_port_ethertype = 2152d23d1361SIgor Mitsyanko cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype)); 215398f44cb0SIgor Mitsyanko 21549766d1ddSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid, 21559766d1ddSIgor Mitsyanko sme->ssid_len); 215698f44cb0SIgor Mitsyanko 215798f44cb0SIgor Mitsyanko if (sme->ie_len != 0) 215898f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, 215998f44cb0SIgor Mitsyanko sme->ie, 216098f44cb0SIgor Mitsyanko sme->ie_len); 216198f44cb0SIgor Mitsyanko 2162d23d1361SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 2163d23d1361SIgor Mitsyanko 216498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 216598f44cb0SIgor Mitsyanko 216698f44cb0SIgor Mitsyanko if (unlikely(ret)) 216798f44cb0SIgor Mitsyanko goto out; 216898f44cb0SIgor Mitsyanko 216998f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 217098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 217198f44cb0SIgor Mitsyanko vif->vifid, res_code); 217298f44cb0SIgor Mitsyanko ret = -EFAULT; 217398f44cb0SIgor Mitsyanko goto out; 217498f44cb0SIgor Mitsyanko } 217598f44cb0SIgor Mitsyanko out: 217698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 217798f44cb0SIgor Mitsyanko return ret; 217898f44cb0SIgor Mitsyanko } 217998f44cb0SIgor Mitsyanko 218098f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code) 218198f44cb0SIgor Mitsyanko { 218298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 218398f44cb0SIgor Mitsyanko struct qlink_cmd_disconnect *cmd; 218498f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 218598f44cb0SIgor Mitsyanko int ret; 218698f44cb0SIgor Mitsyanko 218798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 218898f44cb0SIgor Mitsyanko QLINK_CMD_DISCONNECT, 218998f44cb0SIgor Mitsyanko sizeof(*cmd)); 219098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 219198f44cb0SIgor Mitsyanko return -ENOMEM; 219298f44cb0SIgor Mitsyanko 219398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 219498f44cb0SIgor Mitsyanko 219598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_disconnect *)cmd_skb->data; 219698f44cb0SIgor Mitsyanko cmd->reason = cpu_to_le16(reason_code); 219798f44cb0SIgor Mitsyanko 219898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 219998f44cb0SIgor Mitsyanko 220098f44cb0SIgor Mitsyanko if (unlikely(ret)) 220198f44cb0SIgor Mitsyanko goto out; 220298f44cb0SIgor Mitsyanko 220398f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 220498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 220598f44cb0SIgor Mitsyanko vif->vifid, res_code); 220698f44cb0SIgor Mitsyanko ret = -EFAULT; 220798f44cb0SIgor Mitsyanko goto out; 220898f44cb0SIgor Mitsyanko } 220998f44cb0SIgor Mitsyanko out: 221098f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 221198f44cb0SIgor Mitsyanko return ret; 221298f44cb0SIgor Mitsyanko } 221398f44cb0SIgor Mitsyanko 221498f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up) 221598f44cb0SIgor Mitsyanko { 221698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 221798f44cb0SIgor Mitsyanko struct qlink_cmd_updown *cmd; 221898f44cb0SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 221998f44cb0SIgor Mitsyanko int ret; 222098f44cb0SIgor Mitsyanko 222198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 222298f44cb0SIgor Mitsyanko QLINK_CMD_UPDOWN_INTF, 222398f44cb0SIgor Mitsyanko sizeof(*cmd)); 222498f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) 222598f44cb0SIgor Mitsyanko return -ENOMEM; 222698f44cb0SIgor Mitsyanko 222798f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_updown *)cmd_skb->data; 222898f44cb0SIgor Mitsyanko cmd->if_up = !!up; 222998f44cb0SIgor Mitsyanko 223098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 223198f44cb0SIgor Mitsyanko 223298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); 223398f44cb0SIgor Mitsyanko 223498f44cb0SIgor Mitsyanko if (unlikely(ret)) 223598f44cb0SIgor Mitsyanko goto out; 223698f44cb0SIgor Mitsyanko 223798f44cb0SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 223898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, 223998f44cb0SIgor Mitsyanko vif->vifid, res_code); 224098f44cb0SIgor Mitsyanko ret = -EFAULT; 224198f44cb0SIgor Mitsyanko goto out; 224298f44cb0SIgor Mitsyanko } 224398f44cb0SIgor Mitsyanko out: 224498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 224598f44cb0SIgor Mitsyanko return ret; 224698f44cb0SIgor Mitsyanko } 22474dd07d2bSSergey Matyukevich 22484dd07d2bSSergey Matyukevich int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req) 22494dd07d2bSSergey Matyukevich { 22504dd07d2bSSergey Matyukevich struct sk_buff *cmd_skb; 22514dd07d2bSSergey Matyukevich int ret; 22524dd07d2bSSergey Matyukevich u16 res_code; 22534dd07d2bSSergey Matyukevich struct qlink_cmd_reg_notify *cmd; 22544dd07d2bSSergey Matyukevich 22554dd07d2bSSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 22564dd07d2bSSergey Matyukevich QLINK_CMD_REG_NOTIFY, 22574dd07d2bSSergey Matyukevich sizeof(*cmd)); 22584dd07d2bSSergey Matyukevich if (!cmd_skb) 22594dd07d2bSSergey Matyukevich return -ENOMEM; 22604dd07d2bSSergey Matyukevich 22614dd07d2bSSergey Matyukevich cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; 22624dd07d2bSSergey Matyukevich cmd->alpha2[0] = req->alpha2[0]; 22634dd07d2bSSergey Matyukevich cmd->alpha2[1] = req->alpha2[1]; 22644dd07d2bSSergey Matyukevich 22654dd07d2bSSergey Matyukevich switch (req->initiator) { 22664dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_CORE: 22674dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_CORE; 22684dd07d2bSSergey Matyukevich break; 22694dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_USER: 22704dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_USER; 22714dd07d2bSSergey Matyukevich break; 22724dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_DRIVER: 22734dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; 22744dd07d2bSSergey Matyukevich break; 22754dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_COUNTRY_IE: 22764dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; 22774dd07d2bSSergey Matyukevich break; 22784dd07d2bSSergey Matyukevich } 22794dd07d2bSSergey Matyukevich 22804dd07d2bSSergey Matyukevich switch (req->user_reg_hint_type) { 22814dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_USER: 22824dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; 22834dd07d2bSSergey Matyukevich break; 22844dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_CELL_BASE: 22854dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; 22864dd07d2bSSergey Matyukevich break; 22874dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_INDOOR: 22884dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; 22894dd07d2bSSergey Matyukevich break; 22904dd07d2bSSergey Matyukevich } 22914dd07d2bSSergey Matyukevich 22924dd07d2bSSergey Matyukevich qtnf_bus_lock(bus); 22934dd07d2bSSergey Matyukevich 22944dd07d2bSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb, &res_code); 22954dd07d2bSSergey Matyukevich if (ret) 22964dd07d2bSSergey Matyukevich goto out; 22974dd07d2bSSergey Matyukevich 22984dd07d2bSSergey Matyukevich switch (res_code) { 22994dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_ENOTSUPP: 23004dd07d2bSSergey Matyukevich pr_warn("reg update not supported\n"); 23014dd07d2bSSergey Matyukevich ret = -EOPNOTSUPP; 23024dd07d2bSSergey Matyukevich break; 23034dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_EALREADY: 23044dd07d2bSSergey Matyukevich pr_info("regulatory domain is already set to %c%c", 23054dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 23064dd07d2bSSergey Matyukevich ret = -EALREADY; 23074dd07d2bSSergey Matyukevich break; 23084dd07d2bSSergey Matyukevich case QLINK_CMD_RESULT_OK: 23094dd07d2bSSergey Matyukevich ret = 0; 23104dd07d2bSSergey Matyukevich break; 23114dd07d2bSSergey Matyukevich default: 23124dd07d2bSSergey Matyukevich ret = -EFAULT; 23134dd07d2bSSergey Matyukevich break; 23144dd07d2bSSergey Matyukevich } 23154dd07d2bSSergey Matyukevich 23164dd07d2bSSergey Matyukevich out: 23174dd07d2bSSergey Matyukevich qtnf_bus_unlock(bus); 23184dd07d2bSSergey Matyukevich 23194dd07d2bSSergey Matyukevich return ret; 23204dd07d2bSSergey Matyukevich } 23217c04b439SSergey Matyukevich 23227c04b439SSergey Matyukevich int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, 23237c04b439SSergey Matyukevich struct qtnf_chan_stats *stats) 23247c04b439SSergey Matyukevich { 23257c04b439SSergey Matyukevich struct sk_buff *cmd_skb, *resp_skb = NULL; 23267c04b439SSergey Matyukevich struct qlink_cmd_get_chan_stats *cmd; 23277c04b439SSergey Matyukevich struct qlink_resp_get_chan_stats *resp; 23287c04b439SSergey Matyukevich size_t var_data_len; 23297c04b439SSergey Matyukevich u16 res_code = QLINK_CMD_RESULT_OK; 23307c04b439SSergey Matyukevich int ret = 0; 23317c04b439SSergey Matyukevich 23327c04b439SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 23337c04b439SSergey Matyukevich QLINK_CMD_CHAN_STATS, 23347c04b439SSergey Matyukevich sizeof(*cmd)); 23357c04b439SSergey Matyukevich if (!cmd_skb) 23367c04b439SSergey Matyukevich return -ENOMEM; 23377c04b439SSergey Matyukevich 23387c04b439SSergey Matyukevich qtnf_bus_lock(mac->bus); 23397c04b439SSergey Matyukevich 23407c04b439SSergey Matyukevich cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; 23417c04b439SSergey Matyukevich cmd->channel = cpu_to_le16(channel); 23427c04b439SSergey Matyukevich 23437c04b439SSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, 23447c04b439SSergey Matyukevich sizeof(*resp), &var_data_len); 23457c04b439SSergey Matyukevich if (unlikely(ret)) { 23467c04b439SSergey Matyukevich qtnf_bus_unlock(mac->bus); 23477c04b439SSergey Matyukevich return ret; 23487c04b439SSergey Matyukevich } 23497c04b439SSergey Matyukevich 23507c04b439SSergey Matyukevich if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 23517c04b439SSergey Matyukevich switch (res_code) { 23527c04b439SSergey Matyukevich case QLINK_CMD_RESULT_ENOTFOUND: 23537c04b439SSergey Matyukevich ret = -ENOENT; 23547c04b439SSergey Matyukevich break; 23557c04b439SSergey Matyukevich default: 23567c04b439SSergey Matyukevich pr_err("cmd exec failed: 0x%.4X\n", res_code); 23577c04b439SSergey Matyukevich ret = -EFAULT; 23587c04b439SSergey Matyukevich break; 23597c04b439SSergey Matyukevich } 23607c04b439SSergey Matyukevich goto out; 23617c04b439SSergey Matyukevich } 23627c04b439SSergey Matyukevich 23637c04b439SSergey Matyukevich resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; 23647c04b439SSergey Matyukevich ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, 23657c04b439SSergey Matyukevich var_data_len); 23667c04b439SSergey Matyukevich 23677c04b439SSergey Matyukevich out: 23687c04b439SSergey Matyukevich qtnf_bus_unlock(mac->bus); 23697c04b439SSergey Matyukevich consume_skb(resp_skb); 23707c04b439SSergey Matyukevich return ret; 23717c04b439SSergey Matyukevich } 237297883695SSergey Matyukevich 23738c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, 237497883695SSergey Matyukevich struct cfg80211_csa_settings *params) 237597883695SSergey Matyukevich { 23768c015b90SIgor Mitsyanko struct qtnf_wmac *mac = vif->mac; 237797883695SSergey Matyukevich struct qlink_cmd_chan_switch *cmd; 237897883695SSergey Matyukevich struct sk_buff *cmd_skb; 237997883695SSergey Matyukevich u16 res_code = QLINK_CMD_RESULT_OK; 238097883695SSergey Matyukevich int ret; 238197883695SSergey Matyukevich 23828c015b90SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, 238397883695SSergey Matyukevich QLINK_CMD_CHAN_SWITCH, 238497883695SSergey Matyukevich sizeof(*cmd)); 238597883695SSergey Matyukevich 238697883695SSergey Matyukevich if (unlikely(!cmd_skb)) 238797883695SSergey Matyukevich return -ENOMEM; 238897883695SSergey Matyukevich 238997883695SSergey Matyukevich qtnf_bus_lock(mac->bus); 239097883695SSergey Matyukevich 239197883695SSergey Matyukevich cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; 239297883695SSergey Matyukevich cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); 239397883695SSergey Matyukevich cmd->radar_required = params->radar_required; 239497883695SSergey Matyukevich cmd->block_tx = params->block_tx; 239597883695SSergey Matyukevich cmd->beacon_count = params->count; 239697883695SSergey Matyukevich 239797883695SSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); 239897883695SSergey Matyukevich 239997883695SSergey Matyukevich if (unlikely(ret)) 240097883695SSergey Matyukevich goto out; 240197883695SSergey Matyukevich 240297883695SSergey Matyukevich switch (res_code) { 240397883695SSergey Matyukevich case QLINK_CMD_RESULT_OK: 240497883695SSergey Matyukevich ret = 0; 240597883695SSergey Matyukevich break; 240697883695SSergey Matyukevich case QLINK_CMD_RESULT_ENOTFOUND: 240797883695SSergey Matyukevich ret = -ENOENT; 240897883695SSergey Matyukevich break; 240997883695SSergey Matyukevich case QLINK_CMD_RESULT_ENOTSUPP: 241097883695SSergey Matyukevich ret = -EOPNOTSUPP; 241197883695SSergey Matyukevich break; 241297883695SSergey Matyukevich case QLINK_CMD_RESULT_EALREADY: 241397883695SSergey Matyukevich ret = -EALREADY; 241497883695SSergey Matyukevich break; 241597883695SSergey Matyukevich case QLINK_CMD_RESULT_INVALID: 241697883695SSergey Matyukevich default: 241797883695SSergey Matyukevich ret = -EFAULT; 241897883695SSergey Matyukevich break; 241997883695SSergey Matyukevich } 242097883695SSergey Matyukevich 242197883695SSergey Matyukevich out: 242297883695SSergey Matyukevich qtnf_bus_unlock(mac->bus); 242397883695SSergey Matyukevich return ret; 242497883695SSergey Matyukevich } 24259e5478b6SIgor Mitsyanko 24269e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef) 24279e5478b6SIgor Mitsyanko { 24289e5478b6SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 24299e5478b6SIgor Mitsyanko const struct qlink_resp_channel_get *resp; 24309e5478b6SIgor Mitsyanko struct sk_buff *cmd_skb; 24319e5478b6SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 24329e5478b6SIgor Mitsyanko u16 res_code = QLINK_CMD_RESULT_OK; 24339e5478b6SIgor Mitsyanko int ret; 24349e5478b6SIgor Mitsyanko 24359e5478b6SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 24369e5478b6SIgor Mitsyanko QLINK_CMD_CHAN_GET, 24379e5478b6SIgor Mitsyanko sizeof(struct qlink_cmd)); 24389e5478b6SIgor Mitsyanko if (unlikely(!cmd_skb)) 24399e5478b6SIgor Mitsyanko return -ENOMEM; 24409e5478b6SIgor Mitsyanko 24419e5478b6SIgor Mitsyanko qtnf_bus_lock(bus); 24429e5478b6SIgor Mitsyanko 24439e5478b6SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, 24449e5478b6SIgor Mitsyanko sizeof(*resp), NULL); 24459e5478b6SIgor Mitsyanko 24469e5478b6SIgor Mitsyanko qtnf_bus_unlock(bus); 24479e5478b6SIgor Mitsyanko 24489e5478b6SIgor Mitsyanko if (unlikely(ret)) 24499e5478b6SIgor Mitsyanko goto out; 24509e5478b6SIgor Mitsyanko 24519e5478b6SIgor Mitsyanko if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { 24529e5478b6SIgor Mitsyanko ret = -ENODATA; 24539e5478b6SIgor Mitsyanko goto out; 24549e5478b6SIgor Mitsyanko } 24559e5478b6SIgor Mitsyanko 24569e5478b6SIgor Mitsyanko resp = (const struct qlink_resp_channel_get *)resp_skb->data; 24579e5478b6SIgor Mitsyanko qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef); 24589e5478b6SIgor Mitsyanko 24599e5478b6SIgor Mitsyanko out: 24609e5478b6SIgor Mitsyanko consume_skb(resp_skb); 24619e5478b6SIgor Mitsyanko return ret; 24629e5478b6SIgor Mitsyanko } 2463