xref: /openbmc/linux/drivers/net/wireless/quantenna/qtnfmac/commands.c (revision d42df85f7d853ae155d7708cc57118a0d6925d4f)
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