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