xref: /openbmc/linux/drivers/net/wireless/quantenna/qtnfmac/commands.c (revision ff233cb515031d95550958c5797a70222749e9a3)
1*ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+
2*ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
398f44cb0SIgor Mitsyanko 
498f44cb0SIgor Mitsyanko #include <linux/types.h>
598f44cb0SIgor Mitsyanko #include <linux/skbuff.h>
698f44cb0SIgor Mitsyanko 
798f44cb0SIgor Mitsyanko #include "cfg80211.h"
898f44cb0SIgor Mitsyanko #include "core.h"
998f44cb0SIgor Mitsyanko #include "qlink.h"
1098f44cb0SIgor Mitsyanko #include "qlink_util.h"
1198f44cb0SIgor Mitsyanko #include "bus.h"
1298f44cb0SIgor Mitsyanko #include "commands.h"
1398f44cb0SIgor Mitsyanko 
1498f44cb0SIgor Mitsyanko static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp,
1598f44cb0SIgor Mitsyanko 				       u16 cmd_id, u8 mac_id, u8 vif_id,
1698f44cb0SIgor Mitsyanko 				       size_t resp_size)
1798f44cb0SIgor Mitsyanko {
1898f44cb0SIgor Mitsyanko 	if (unlikely(le16_to_cpu(resp->cmd_id) != cmd_id)) {
1998f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad cmd_id in response: 0x%.4X\n",
2098f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, le16_to_cpu(resp->cmd_id));
2198f44cb0SIgor Mitsyanko 		return -EINVAL;
2298f44cb0SIgor Mitsyanko 	}
2398f44cb0SIgor Mitsyanko 
2498f44cb0SIgor Mitsyanko 	if (unlikely(resp->macid != mac_id)) {
2598f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad MAC in response: %u\n",
2698f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, resp->macid);
2798f44cb0SIgor Mitsyanko 		return -EINVAL;
2898f44cb0SIgor Mitsyanko 	}
2998f44cb0SIgor Mitsyanko 
3098f44cb0SIgor Mitsyanko 	if (unlikely(resp->vifid != vif_id)) {
3198f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad VIF in response: %u\n",
3298f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, resp->vifid);
3398f44cb0SIgor Mitsyanko 		return -EINVAL;
3498f44cb0SIgor Mitsyanko 	}
3598f44cb0SIgor Mitsyanko 
3698f44cb0SIgor Mitsyanko 	if (unlikely(le16_to_cpu(resp->mhdr.len) < resp_size)) {
3798f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad response size %u < %zu\n",
3898f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id,
3998f44cb0SIgor Mitsyanko 			le16_to_cpu(resp->mhdr.len), resp_size);
4098f44cb0SIgor Mitsyanko 		return -ENOSPC;
4198f44cb0SIgor Mitsyanko 	}
4298f44cb0SIgor Mitsyanko 
4398f44cb0SIgor Mitsyanko 	return 0;
4498f44cb0SIgor Mitsyanko }
4598f44cb0SIgor Mitsyanko 
4636e8c538SIgor Mitsyanko static int qtnf_cmd_resp_result_decode(enum qlink_cmd_result qcode)
4736e8c538SIgor Mitsyanko {
4836e8c538SIgor Mitsyanko 	switch (qcode) {
4936e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_OK:
5036e8c538SIgor Mitsyanko 		return 0;
5136e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_INVALID:
5236e8c538SIgor Mitsyanko 		return -EINVAL;
5336e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_ENOTSUPP:
5436e8c538SIgor Mitsyanko 		return -ENOTSUPP;
5536e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_ENOTFOUND:
5636e8c538SIgor Mitsyanko 		return -ENOENT;
5736e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EALREADY:
5836e8c538SIgor Mitsyanko 		return -EALREADY;
5936e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EADDRINUSE:
6036e8c538SIgor Mitsyanko 		return -EADDRINUSE;
6136e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EADDRNOTAVAIL:
6236e8c538SIgor Mitsyanko 		return -EADDRNOTAVAIL;
63126824f5SAndrey Shevchenko 	case QLINK_CMD_RESULT_EBUSY:
64126824f5SAndrey Shevchenko 		return -EBUSY;
6536e8c538SIgor Mitsyanko 	default:
6636e8c538SIgor Mitsyanko 		return -EFAULT;
6736e8c538SIgor Mitsyanko 	}
6836e8c538SIgor Mitsyanko }
6936e8c538SIgor Mitsyanko 
7098f44cb0SIgor Mitsyanko static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
7198f44cb0SIgor Mitsyanko 				    struct sk_buff *cmd_skb,
7298f44cb0SIgor Mitsyanko 				    struct sk_buff **response_skb,
7398f44cb0SIgor Mitsyanko 				    size_t const_resp_size,
7498f44cb0SIgor Mitsyanko 				    size_t *var_resp_size)
7598f44cb0SIgor Mitsyanko {
7698f44cb0SIgor Mitsyanko 	struct qlink_cmd *cmd;
771066bd19SSergey Matyukevich 	struct qlink_resp *resp = NULL;
7898f44cb0SIgor Mitsyanko 	struct sk_buff *resp_skb = NULL;
7998f44cb0SIgor Mitsyanko 	u16 cmd_id;
80c6ed298fSSergey Matyukevich 	u8 mac_id;
81c6ed298fSSergey Matyukevich 	u8 vif_id;
8298f44cb0SIgor Mitsyanko 	int ret;
8398f44cb0SIgor Mitsyanko 
8498f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd *)cmd_skb->data;
8598f44cb0SIgor Mitsyanko 	cmd_id = le16_to_cpu(cmd->cmd_id);
8698f44cb0SIgor Mitsyanko 	mac_id = cmd->macid;
8798f44cb0SIgor Mitsyanko 	vif_id = cmd->vifid;
8898f44cb0SIgor Mitsyanko 	cmd->mhdr.len = cpu_to_le16(cmd_skb->len);
8998f44cb0SIgor Mitsyanko 
90c6ed298fSSergey Matyukevich 	pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id,
91c6ed298fSSergey Matyukevich 		 le16_to_cpu(cmd->cmd_id));
92c6ed298fSSergey Matyukevich 
93c6ed298fSSergey Matyukevich 	if (bus->fw_state != QTNF_FW_STATE_ACTIVE &&
94c6ed298fSSergey Matyukevich 	    le16_to_cpu(cmd->cmd_id) != QLINK_CMD_FW_INIT) {
9598f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n",
9698f44cb0SIgor Mitsyanko 			mac_id, vif_id, le16_to_cpu(cmd->cmd_id),
9798f44cb0SIgor Mitsyanko 			bus->fw_state);
98b60769e2SDmitry Lebed 		dev_kfree_skb(cmd_skb);
9998f44cb0SIgor Mitsyanko 		return -ENODEV;
10098f44cb0SIgor Mitsyanko 	}
10198f44cb0SIgor Mitsyanko 
10298f44cb0SIgor Mitsyanko 	ret = qtnf_trans_send_cmd_with_resp(bus, cmd_skb, &resp_skb);
103c6ed298fSSergey Matyukevich 	if (ret)
10498f44cb0SIgor Mitsyanko 		goto out;
10598f44cb0SIgor Mitsyanko 
1061066bd19SSergey Matyukevich 	if (WARN_ON(!resp_skb || !resp_skb->data)) {
1071066bd19SSergey Matyukevich 		ret = -EFAULT;
1081066bd19SSergey Matyukevich 		goto out;
1091066bd19SSergey Matyukevich 	}
1101066bd19SSergey Matyukevich 
1111066bd19SSergey Matyukevich 	resp = (struct qlink_resp *)resp_skb->data;
11298f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id,
11398f44cb0SIgor Mitsyanko 					  const_resp_size);
114c6ed298fSSergey Matyukevich 	if (ret)
11598f44cb0SIgor Mitsyanko 		goto out;
11698f44cb0SIgor Mitsyanko 
11798f44cb0SIgor Mitsyanko 	/* Return length of variable part of response */
11898f44cb0SIgor Mitsyanko 	if (response_skb && var_resp_size)
11998f44cb0SIgor Mitsyanko 		*var_resp_size = le16_to_cpu(resp->mhdr.len) - const_resp_size;
12098f44cb0SIgor Mitsyanko 
12198f44cb0SIgor Mitsyanko out:
12298f44cb0SIgor Mitsyanko 	if (response_skb)
12398f44cb0SIgor Mitsyanko 		*response_skb = resp_skb;
12498f44cb0SIgor Mitsyanko 	else
12598f44cb0SIgor Mitsyanko 		consume_skb(resp_skb);
12698f44cb0SIgor Mitsyanko 
127c6ed298fSSergey Matyukevich 	if (!ret && resp)
128c6ed298fSSergey Matyukevich 		return qtnf_cmd_resp_result_decode(le16_to_cpu(resp->result));
129c6ed298fSSergey Matyukevich 
130c6ed298fSSergey Matyukevich 	pr_warn("VIF%u.%u: cmd 0x%.4X failed: %d\n",
131c6ed298fSSergey Matyukevich 		mac_id, vif_id, le16_to_cpu(cmd->cmd_id), ret);
132c6ed298fSSergey Matyukevich 
13398f44cb0SIgor Mitsyanko 	return ret;
13498f44cb0SIgor Mitsyanko }
13598f44cb0SIgor Mitsyanko 
136c6ed298fSSergey Matyukevich static inline int qtnf_cmd_send(struct qtnf_bus *bus, struct sk_buff *cmd_skb)
13798f44cb0SIgor Mitsyanko {
138c6ed298fSSergey Matyukevich 	return qtnf_cmd_send_with_reply(bus, cmd_skb, NULL,
13998f44cb0SIgor Mitsyanko 					sizeof(struct qlink_resp), NULL);
14098f44cb0SIgor Mitsyanko }
14198f44cb0SIgor Mitsyanko 
14298f44cb0SIgor Mitsyanko static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no,
14398f44cb0SIgor Mitsyanko 						 size_t cmd_size)
14498f44cb0SIgor Mitsyanko {
14598f44cb0SIgor Mitsyanko 	struct qlink_cmd *cmd;
14698f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
14798f44cb0SIgor Mitsyanko 
14898f44cb0SIgor Mitsyanko 	cmd_skb = __dev_alloc_skb(sizeof(*cmd) +
14998f44cb0SIgor Mitsyanko 				  QTNF_MAX_CMD_BUF_SIZE, GFP_KERNEL);
15098f44cb0SIgor Mitsyanko 	if (unlikely(!cmd_skb)) {
15198f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u CMD %u: alloc failed\n", macid, vifid, cmd_no);
15298f44cb0SIgor Mitsyanko 		return NULL;
15398f44cb0SIgor Mitsyanko 	}
15498f44cb0SIgor Mitsyanko 
155b080db58SJohannes Berg 	skb_put_zero(cmd_skb, cmd_size);
15698f44cb0SIgor Mitsyanko 
15798f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd *)cmd_skb->data;
15898f44cb0SIgor Mitsyanko 	cmd->mhdr.len = cpu_to_le16(cmd_skb->len);
15998f44cb0SIgor Mitsyanko 	cmd->mhdr.type = cpu_to_le16(QLINK_MSG_TYPE_CMD);
16098f44cb0SIgor Mitsyanko 	cmd->cmd_id = cpu_to_le16(cmd_no);
16198f44cb0SIgor Mitsyanko 	cmd->macid = macid;
16298f44cb0SIgor Mitsyanko 	cmd->vifid = vifid;
16398f44cb0SIgor Mitsyanko 
16498f44cb0SIgor Mitsyanko 	return cmd_skb;
16598f44cb0SIgor Mitsyanko }
16698f44cb0SIgor Mitsyanko 
16718b7470fSIgor Mitsyanko static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type,
16818b7470fSIgor Mitsyanko 				    const u8 *buf, size_t len)
16918b7470fSIgor Mitsyanko {
17018b7470fSIgor Mitsyanko 	struct qlink_tlv_ie_set *tlv;
17118b7470fSIgor Mitsyanko 
17218b7470fSIgor Mitsyanko 	tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + len);
17318b7470fSIgor Mitsyanko 	tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET);
17418b7470fSIgor Mitsyanko 	tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr));
17518b7470fSIgor Mitsyanko 	tlv->type = frame_type;
17618b7470fSIgor Mitsyanko 	tlv->flags = 0;
17718b7470fSIgor Mitsyanko 
17818b7470fSIgor Mitsyanko 	if (len && buf)
17918b7470fSIgor Mitsyanko 		memcpy(tlv->ie_data, buf, len);
18018b7470fSIgor Mitsyanko }
18118b7470fSIgor Mitsyanko 
182f1398fd2SVasily Ulyanov static inline size_t qtnf_cmd_acl_data_size(const struct cfg80211_acl_data *acl)
183f1398fd2SVasily Ulyanov {
184f1398fd2SVasily Ulyanov 	size_t size = sizeof(struct qlink_acl_data) +
185f1398fd2SVasily Ulyanov 		      acl->n_acl_entries * sizeof(struct qlink_mac_address);
186f1398fd2SVasily Ulyanov 
187f1398fd2SVasily Ulyanov 	return size;
188f1398fd2SVasily Ulyanov }
189f1398fd2SVasily Ulyanov 
19017011da0SIgor Mitsyanko static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
19117011da0SIgor Mitsyanko 				      const struct cfg80211_ap_settings *s)
19298f44cb0SIgor Mitsyanko {
19317011da0SIgor Mitsyanko 	unsigned int len = sizeof(struct qlink_cmd_start_ap);
19498f44cb0SIgor Mitsyanko 
19517011da0SIgor Mitsyanko 	len += s->ssid_len;
19617011da0SIgor Mitsyanko 	len += s->beacon.head_len;
19717011da0SIgor Mitsyanko 	len += s->beacon.tail_len;
19817011da0SIgor Mitsyanko 	len += s->beacon.beacon_ies_len;
19917011da0SIgor Mitsyanko 	len += s->beacon.proberesp_ies_len;
20017011da0SIgor Mitsyanko 	len += s->beacon.assocresp_ies_len;
20117011da0SIgor Mitsyanko 	len += s->beacon.probe_resp_len;
20298f44cb0SIgor Mitsyanko 
20317011da0SIgor Mitsyanko 	if (cfg80211_chandef_valid(&s->chandef))
20417011da0SIgor Mitsyanko 		len += sizeof(struct qlink_tlv_chandef);
20598f44cb0SIgor Mitsyanko 
206f1398fd2SVasily Ulyanov 	if (s->acl)
20733f98992SVasily Ulyanov 		len += sizeof(struct qlink_tlv_hdr) +
20833f98992SVasily Ulyanov 		       qtnf_cmd_acl_data_size(s->acl);
209f1398fd2SVasily Ulyanov 
21017011da0SIgor Mitsyanko 	if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
21117011da0SIgor Mitsyanko 		pr_err("VIF%u.%u: can not fit AP settings: %u\n",
21217011da0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, len);
21317011da0SIgor Mitsyanko 		return false;
21498f44cb0SIgor Mitsyanko 	}
21598f44cb0SIgor Mitsyanko 
21617011da0SIgor Mitsyanko 	return true;
21798f44cb0SIgor Mitsyanko }
21898f44cb0SIgor Mitsyanko 
21917011da0SIgor Mitsyanko int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
2209b692df1SIgor Mitsyanko 			   const struct cfg80211_ap_settings *s)
22198f44cb0SIgor Mitsyanko {
22298f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
22317011da0SIgor Mitsyanko 	struct qlink_cmd_start_ap *cmd;
2248b5f4aa7SIgor Mitsyanko 	struct qlink_auth_encr *aen;
22598f44cb0SIgor Mitsyanko 	int ret;
22698f44cb0SIgor Mitsyanko 	int i;
22798f44cb0SIgor Mitsyanko 
22817011da0SIgor Mitsyanko 	if (!qtnf_cmd_start_ap_can_fit(vif, s))
22917011da0SIgor Mitsyanko 		return -E2BIG;
23017011da0SIgor Mitsyanko 
23198f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
23217011da0SIgor Mitsyanko 					    QLINK_CMD_START_AP,
2338b5f4aa7SIgor Mitsyanko 					    sizeof(*cmd));
234c93fe71cSSergey Matyukevich 	if (!cmd_skb)
23598f44cb0SIgor Mitsyanko 		return -ENOMEM;
23698f44cb0SIgor Mitsyanko 
23717011da0SIgor Mitsyanko 	cmd = (struct qlink_cmd_start_ap *)cmd_skb->data;
2388b5f4aa7SIgor Mitsyanko 	cmd->dtim_period = s->dtim_period;
2398b5f4aa7SIgor Mitsyanko 	cmd->beacon_interval = cpu_to_le16(s->beacon_interval);
2408b5f4aa7SIgor Mitsyanko 	cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid);
2418b5f4aa7SIgor Mitsyanko 	cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout);
2428b5f4aa7SIgor Mitsyanko 	cmd->smps_mode = s->smps_mode;
2438b5f4aa7SIgor Mitsyanko 	cmd->p2p_ctwindow = s->p2p_ctwindow;
2448b5f4aa7SIgor Mitsyanko 	cmd->p2p_opp_ps = s->p2p_opp_ps;
2458b5f4aa7SIgor Mitsyanko 	cmd->pbss = s->pbss;
2468b5f4aa7SIgor Mitsyanko 	cmd->ht_required = s->ht_required;
2478b5f4aa7SIgor Mitsyanko 	cmd->vht_required = s->vht_required;
24898f44cb0SIgor Mitsyanko 
2498b5f4aa7SIgor Mitsyanko 	aen = &cmd->aen;
2508b5f4aa7SIgor Mitsyanko 	aen->auth_type = s->auth_type;
2518b5f4aa7SIgor Mitsyanko 	aen->privacy = !!s->privacy;
2528b5f4aa7SIgor Mitsyanko 	aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions);
2538b5f4aa7SIgor Mitsyanko 	aen->cipher_group = cpu_to_le32(s->crypto.cipher_group);
2548b5f4aa7SIgor Mitsyanko 	aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise);
25598f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++)
2568b5f4aa7SIgor Mitsyanko 		aen->ciphers_pairwise[i] =
2579b692df1SIgor Mitsyanko 				cpu_to_le32(s->crypto.ciphers_pairwise[i]);
2588b5f4aa7SIgor Mitsyanko 	aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites);
25998f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++)
2608b5f4aa7SIgor Mitsyanko 		aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]);
2618b5f4aa7SIgor Mitsyanko 	aen->control_port = s->crypto.control_port;
2628b5f4aa7SIgor Mitsyanko 	aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt;
2638b5f4aa7SIgor Mitsyanko 	aen->control_port_ethertype =
2649b692df1SIgor Mitsyanko 		cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype));
26598f44cb0SIgor Mitsyanko 
266f99201cbSIgor Mitsyanko 	if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN)
267f99201cbSIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid,
268f99201cbSIgor Mitsyanko 					 s->ssid_len);
269f99201cbSIgor Mitsyanko 
270f99201cbSIgor Mitsyanko 	if (cfg80211_chandef_valid(&s->chandef)) {
271f99201cbSIgor Mitsyanko 		struct qlink_tlv_chandef *chtlv =
272f99201cbSIgor Mitsyanko 			(struct qlink_tlv_chandef *)skb_put(cmd_skb,
273f99201cbSIgor Mitsyanko 							    sizeof(*chtlv));
274f99201cbSIgor Mitsyanko 
275f99201cbSIgor Mitsyanko 		chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF);
276f99201cbSIgor Mitsyanko 		chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) -
277f99201cbSIgor Mitsyanko 					     sizeof(chtlv->hdr));
2785bf374abSSergey Matyukevich 		qlink_chandef_cfg2q(&s->chandef, &chtlv->chdef);
279f99201cbSIgor Mitsyanko 	}
280f99201cbSIgor Mitsyanko 
28117011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD,
28217011da0SIgor Mitsyanko 				s->beacon.head, s->beacon.head_len);
28317011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_TAIL,
28417011da0SIgor Mitsyanko 				s->beacon.tail, s->beacon.tail_len);
28517011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_IES,
28617011da0SIgor Mitsyanko 				s->beacon.beacon_ies, s->beacon.beacon_ies_len);
28717011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP,
28817011da0SIgor Mitsyanko 				s->beacon.probe_resp, s->beacon.probe_resp_len);
28917011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP_IES,
29017011da0SIgor Mitsyanko 				s->beacon.proberesp_ies,
29117011da0SIgor Mitsyanko 				s->beacon.proberesp_ies_len);
29217011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_RESP,
29317011da0SIgor Mitsyanko 				s->beacon.assocresp_ies,
29417011da0SIgor Mitsyanko 				s->beacon.assocresp_ies_len);
29517011da0SIgor Mitsyanko 
296a3945f43SIgor Mitsyanko 	if (s->ht_cap) {
297a3945f43SIgor Mitsyanko 		struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *)
298a3945f43SIgor Mitsyanko 			skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->ht_cap));
299a3945f43SIgor Mitsyanko 
300a3945f43SIgor Mitsyanko 		tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
301a3945f43SIgor Mitsyanko 		tlv->len = cpu_to_le16(sizeof(*s->ht_cap));
302a3945f43SIgor Mitsyanko 		memcpy(tlv->val, s->ht_cap, sizeof(*s->ht_cap));
303a3945f43SIgor Mitsyanko 	}
304a3945f43SIgor Mitsyanko 
305a3945f43SIgor Mitsyanko 	if (s->vht_cap) {
306a3945f43SIgor Mitsyanko 		struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *)
307a3945f43SIgor Mitsyanko 			skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->vht_cap));
308a3945f43SIgor Mitsyanko 
309a3945f43SIgor Mitsyanko 		tlv->type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
310a3945f43SIgor Mitsyanko 		tlv->len = cpu_to_le16(sizeof(*s->vht_cap));
311a3945f43SIgor Mitsyanko 		memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
312a3945f43SIgor Mitsyanko 	}
313a3945f43SIgor Mitsyanko 
314f1398fd2SVasily Ulyanov 	if (s->acl) {
315f1398fd2SVasily Ulyanov 		size_t acl_size = qtnf_cmd_acl_data_size(s->acl);
316f1398fd2SVasily Ulyanov 		struct qlink_tlv_hdr *tlv =
317f1398fd2SVasily Ulyanov 			skb_put(cmd_skb, sizeof(*tlv) + acl_size);
318f1398fd2SVasily Ulyanov 
319f1398fd2SVasily Ulyanov 		tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
320f1398fd2SVasily Ulyanov 		tlv->len = cpu_to_le16(acl_size);
321f1398fd2SVasily Ulyanov 		qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val);
322f1398fd2SVasily Ulyanov 	}
323f1398fd2SVasily Ulyanov 
3248b5f4aa7SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
325c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
326c6ed298fSSergey Matyukevich 	if (ret)
32798f44cb0SIgor Mitsyanko 		goto out;
32898f44cb0SIgor Mitsyanko 
32917011da0SIgor Mitsyanko 	netif_carrier_on(vif->netdev);
33017011da0SIgor Mitsyanko 
33198f44cb0SIgor Mitsyanko out:
33298f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
333c6ed298fSSergey Matyukevich 
33498f44cb0SIgor Mitsyanko 	return ret;
33598f44cb0SIgor Mitsyanko }
33698f44cb0SIgor Mitsyanko 
33798f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif)
33898f44cb0SIgor Mitsyanko {
33998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
34098f44cb0SIgor Mitsyanko 	int ret;
34198f44cb0SIgor Mitsyanko 
34298f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
34398f44cb0SIgor Mitsyanko 					    QLINK_CMD_STOP_AP,
34498f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
345c93fe71cSSergey Matyukevich 	if (!cmd_skb)
34698f44cb0SIgor Mitsyanko 		return -ENOMEM;
34798f44cb0SIgor Mitsyanko 
34898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
349c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
350c6ed298fSSergey Matyukevich 	if (ret)
35198f44cb0SIgor Mitsyanko 		goto out;
35298f44cb0SIgor Mitsyanko 
35398f44cb0SIgor Mitsyanko out:
35498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
355c6ed298fSSergey Matyukevich 
35698f44cb0SIgor Mitsyanko 	return ret;
35798f44cb0SIgor Mitsyanko }
35898f44cb0SIgor Mitsyanko 
35998f44cb0SIgor Mitsyanko int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg)
36098f44cb0SIgor Mitsyanko {
36198f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
36298f44cb0SIgor Mitsyanko 	struct qlink_cmd_mgmt_frame_register *cmd;
36398f44cb0SIgor Mitsyanko 	int ret;
36498f44cb0SIgor Mitsyanko 
36598f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
36698f44cb0SIgor Mitsyanko 					    QLINK_CMD_REGISTER_MGMT,
36798f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
368c93fe71cSSergey Matyukevich 	if (!cmd_skb)
36998f44cb0SIgor Mitsyanko 		return -ENOMEM;
37098f44cb0SIgor Mitsyanko 
37198f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
37298f44cb0SIgor Mitsyanko 
37398f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_mgmt_frame_register *)cmd_skb->data;
37498f44cb0SIgor Mitsyanko 	cmd->frame_type = cpu_to_le16(frame_type);
37598f44cb0SIgor Mitsyanko 	cmd->do_register = reg;
37698f44cb0SIgor Mitsyanko 
377c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
378c6ed298fSSergey Matyukevich 	if (ret)
37998f44cb0SIgor Mitsyanko 		goto out;
38098f44cb0SIgor Mitsyanko 
38198f44cb0SIgor Mitsyanko out:
38298f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
383c6ed298fSSergey Matyukevich 
38498f44cb0SIgor Mitsyanko 	return ret;
38598f44cb0SIgor Mitsyanko }
38698f44cb0SIgor Mitsyanko 
38798f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
38898f44cb0SIgor Mitsyanko 			     u16 freq, const u8 *buf, size_t len)
38998f44cb0SIgor Mitsyanko {
39098f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
39198f44cb0SIgor Mitsyanko 	struct qlink_cmd_mgmt_frame_tx *cmd;
39298f44cb0SIgor Mitsyanko 	int ret;
39398f44cb0SIgor Mitsyanko 
39498f44cb0SIgor Mitsyanko 	if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) {
39598f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: frame is too big: %zu\n", vif->mac->macid,
39698f44cb0SIgor Mitsyanko 			vif->vifid, len);
39798f44cb0SIgor Mitsyanko 		return -E2BIG;
39898f44cb0SIgor Mitsyanko 	}
39998f44cb0SIgor Mitsyanko 
40098f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
40198f44cb0SIgor Mitsyanko 					    QLINK_CMD_SEND_MGMT_FRAME,
40298f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
403c93fe71cSSergey Matyukevich 	if (!cmd_skb)
40498f44cb0SIgor Mitsyanko 		return -ENOMEM;
40598f44cb0SIgor Mitsyanko 
40698f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
40798f44cb0SIgor Mitsyanko 
40898f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_mgmt_frame_tx *)cmd_skb->data;
40998f44cb0SIgor Mitsyanko 	cmd->cookie = cpu_to_le32(cookie);
41098f44cb0SIgor Mitsyanko 	cmd->freq = cpu_to_le16(freq);
41198f44cb0SIgor Mitsyanko 	cmd->flags = cpu_to_le16(flags);
41298f44cb0SIgor Mitsyanko 
41398f44cb0SIgor Mitsyanko 	if (len && buf)
41498f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_buffer(cmd_skb, buf, len);
41598f44cb0SIgor Mitsyanko 
416c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
417c6ed298fSSergey Matyukevich 	if (ret)
41898f44cb0SIgor Mitsyanko 		goto out;
41998f44cb0SIgor Mitsyanko 
42098f44cb0SIgor Mitsyanko out:
42198f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
422c6ed298fSSergey Matyukevich 
42398f44cb0SIgor Mitsyanko 	return ret;
42498f44cb0SIgor Mitsyanko }
42598f44cb0SIgor Mitsyanko 
42698f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
42798f44cb0SIgor Mitsyanko 				 const u8 *buf, size_t len)
42898f44cb0SIgor Mitsyanko {
42998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
43098f44cb0SIgor Mitsyanko 	int ret;
43198f44cb0SIgor Mitsyanko 
4324d1f0fabSIgor Mitsyanko 	if (len > QTNF_MAX_CMD_BUF_SIZE) {
43398f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid,
43498f44cb0SIgor Mitsyanko 			vif->vifid, frame_type, len);
43598f44cb0SIgor Mitsyanko 		return -E2BIG;
43698f44cb0SIgor Mitsyanko 	}
43798f44cb0SIgor Mitsyanko 
43898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
43998f44cb0SIgor Mitsyanko 					    QLINK_CMD_MGMT_SET_APPIE,
4404d1f0fabSIgor Mitsyanko 					    sizeof(struct qlink_cmd));
441c93fe71cSSergey Matyukevich 	if (!cmd_skb)
44298f44cb0SIgor Mitsyanko 		return -ENOMEM;
44398f44cb0SIgor Mitsyanko 
4444d1f0fabSIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len);
4454d1f0fabSIgor Mitsyanko 
44698f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
447c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
448c6ed298fSSergey Matyukevich 	if (ret)
44998f44cb0SIgor Mitsyanko 		goto out;
45098f44cb0SIgor Mitsyanko 
45198f44cb0SIgor Mitsyanko out:
45298f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
453c6ed298fSSergey Matyukevich 
45498f44cb0SIgor Mitsyanko 	return ret;
45598f44cb0SIgor Mitsyanko }
45698f44cb0SIgor Mitsyanko 
45798f44cb0SIgor Mitsyanko static void
45898f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(struct rate_info *rate_dst,
45998f44cb0SIgor Mitsyanko 			 const struct qlink_sta_info_rate *rate_src)
46098f44cb0SIgor Mitsyanko {
46198f44cb0SIgor Mitsyanko 	rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10;
46298f44cb0SIgor Mitsyanko 
46398f44cb0SIgor Mitsyanko 	rate_dst->mcs = rate_src->mcs;
46498f44cb0SIgor Mitsyanko 	rate_dst->nss = rate_src->nss;
46598f44cb0SIgor Mitsyanko 	rate_dst->flags = 0;
46698f44cb0SIgor Mitsyanko 
46798f44cb0SIgor Mitsyanko 	switch (rate_src->bw) {
4684d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_5:
46998f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_5;
47098f44cb0SIgor Mitsyanko 		break;
4714d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_10:
47298f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_10;
47398f44cb0SIgor Mitsyanko 		break;
4744d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_20:
4754d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_20_NOHT:
47698f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_20;
47798f44cb0SIgor Mitsyanko 		break;
4784d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_40:
47998f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_40;
48098f44cb0SIgor Mitsyanko 		break;
4814d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_80:
48298f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_80;
48398f44cb0SIgor Mitsyanko 		break;
4844d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_160:
48598f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_160;
48698f44cb0SIgor Mitsyanko 		break;
48798f44cb0SIgor Mitsyanko 	default:
48898f44cb0SIgor Mitsyanko 		rate_dst->bw = 0;
48998f44cb0SIgor Mitsyanko 		break;
49098f44cb0SIgor Mitsyanko 	}
49198f44cb0SIgor Mitsyanko 
49298f44cb0SIgor Mitsyanko 	if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HT_MCS)
49398f44cb0SIgor Mitsyanko 		rate_dst->flags |= RATE_INFO_FLAGS_MCS;
49498f44cb0SIgor Mitsyanko 	else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS)
49598f44cb0SIgor Mitsyanko 		rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS;
496d5657b70SSergey Matyukevich 
497d5657b70SSergey Matyukevich 	if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_SHORT_GI)
498d5657b70SSergey Matyukevich 		rate_dst->flags |= RATE_INFO_FLAGS_SHORT_GI;
49998f44cb0SIgor Mitsyanko }
50098f44cb0SIgor Mitsyanko 
50198f44cb0SIgor Mitsyanko static void
50298f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst,
50398f44cb0SIgor Mitsyanko 			  const struct qlink_sta_info_state *src)
50498f44cb0SIgor Mitsyanko {
50598f44cb0SIgor Mitsyanko 	u32 mask, value;
50698f44cb0SIgor Mitsyanko 
50798f44cb0SIgor Mitsyanko 	dst->mask = 0;
50898f44cb0SIgor Mitsyanko 	dst->set = 0;
50998f44cb0SIgor Mitsyanko 
51098f44cb0SIgor Mitsyanko 	mask = le32_to_cpu(src->mask);
51198f44cb0SIgor Mitsyanko 	value = le32_to_cpu(src->value);
51298f44cb0SIgor Mitsyanko 
51398f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_AUTHORIZED) {
51498f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_AUTHORIZED);
51598f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_AUTHORIZED)
51698f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
51798f44cb0SIgor Mitsyanko 	}
51898f44cb0SIgor Mitsyanko 
51998f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_SHORT_PREAMBLE) {
52098f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
52198f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_SHORT_PREAMBLE)
52298f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
52398f44cb0SIgor Mitsyanko 	}
52498f44cb0SIgor Mitsyanko 
52598f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_WME) {
52698f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_WME);
52798f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_WME)
52898f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_WME);
52998f44cb0SIgor Mitsyanko 	}
53098f44cb0SIgor Mitsyanko 
53198f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_MFP) {
53298f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_MFP);
53398f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_MFP)
53498f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_MFP);
53598f44cb0SIgor Mitsyanko 	}
53698f44cb0SIgor Mitsyanko 
53798f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_AUTHENTICATED) {
53898f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
53998f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_AUTHENTICATED)
54098f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
54198f44cb0SIgor Mitsyanko 	}
54298f44cb0SIgor Mitsyanko 
54398f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_TDLS_PEER) {
54498f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
54598f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_TDLS_PEER)
54698f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
54798f44cb0SIgor Mitsyanko 	}
54898f44cb0SIgor Mitsyanko 
54998f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_ASSOCIATED) {
55098f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
55198f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_ASSOCIATED)
55298f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
55398f44cb0SIgor Mitsyanko 	}
55498f44cb0SIgor Mitsyanko }
55598f44cb0SIgor Mitsyanko 
55698f44cb0SIgor Mitsyanko static void
5574d2a7a1cSIgor Mitsyanko qtnf_cmd_sta_info_parse(struct station_info *sinfo,
5584d2a7a1cSIgor Mitsyanko 			const struct qlink_tlv_hdr *tlv,
5594d2a7a1cSIgor Mitsyanko 			size_t resp_size)
56098f44cb0SIgor Mitsyanko {
5614d2a7a1cSIgor Mitsyanko 	const struct qlink_sta_stats *stats = NULL;
5624d2a7a1cSIgor Mitsyanko 	const u8 *map = NULL;
5634d2a7a1cSIgor Mitsyanko 	unsigned int map_len = 0;
5644d2a7a1cSIgor Mitsyanko 	unsigned int stats_len = 0;
5654d2a7a1cSIgor Mitsyanko 	u16 tlv_len;
56698f44cb0SIgor Mitsyanko 
5674d2a7a1cSIgor Mitsyanko #define qtnf_sta_stat_avail(stat_name, bitn)	\
5684d2a7a1cSIgor Mitsyanko 	(qtnf_utils_is_bit_set(map, bitn, map_len) && \
5694d2a7a1cSIgor Mitsyanko 	 (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len))
57098f44cb0SIgor Mitsyanko 
5714d2a7a1cSIgor Mitsyanko 	while (resp_size >= sizeof(*tlv)) {
5724d2a7a1cSIgor Mitsyanko 		tlv_len = le16_to_cpu(tlv->len);
57398f44cb0SIgor Mitsyanko 
5744d2a7a1cSIgor Mitsyanko 		switch (le16_to_cpu(tlv->type)) {
5754d2a7a1cSIgor Mitsyanko 		case QTN_TLV_ID_STA_STATS_MAP:
5764d2a7a1cSIgor Mitsyanko 			map_len = tlv_len;
5774d2a7a1cSIgor Mitsyanko 			map = tlv->val;
57898f44cb0SIgor Mitsyanko 			break;
5794d2a7a1cSIgor Mitsyanko 		case QTN_TLV_ID_STA_STATS:
5804d2a7a1cSIgor Mitsyanko 			stats_len = tlv_len;
5814d2a7a1cSIgor Mitsyanko 			stats = (const struct qlink_sta_stats *)tlv->val;
58298f44cb0SIgor Mitsyanko 			break;
58398f44cb0SIgor Mitsyanko 		default:
58498f44cb0SIgor Mitsyanko 			break;
58598f44cb0SIgor Mitsyanko 		}
5864d2a7a1cSIgor Mitsyanko 
5874d2a7a1cSIgor Mitsyanko 		resp_size -= tlv_len + sizeof(*tlv);
5884d2a7a1cSIgor Mitsyanko 		tlv = (const struct qlink_tlv_hdr *)(tlv->val + tlv_len);
58998f44cb0SIgor Mitsyanko 	}
59098f44cb0SIgor Mitsyanko 
5914d2a7a1cSIgor Mitsyanko 	if (!map || !stats)
5924d2a7a1cSIgor Mitsyanko 		return;
5934d2a7a1cSIgor Mitsyanko 
5944d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) {
59522d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
5964d2a7a1cSIgor Mitsyanko 		sinfo->inactive_time = le32_to_cpu(stats->inactive_time);
59798f44cb0SIgor Mitsyanko 	}
59898f44cb0SIgor Mitsyanko 
5994d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(connected_time,
6004d2a7a1cSIgor Mitsyanko 				QLINK_STA_INFO_CONNECTED_TIME)) {
60122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
6024d2a7a1cSIgor Mitsyanko 		sinfo->connected_time = le32_to_cpu(stats->connected_time);
6034d2a7a1cSIgor Mitsyanko 	}
6044d2a7a1cSIgor Mitsyanko 
6054d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) {
60622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
6074d2a7a1cSIgor Mitsyanko 		sinfo->signal = stats->signal - QLINK_RSSI_OFFSET;
6084d2a7a1cSIgor Mitsyanko 	}
6094d2a7a1cSIgor Mitsyanko 
6104d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) {
61122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
6124d2a7a1cSIgor Mitsyanko 		sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET;
6134d2a7a1cSIgor Mitsyanko 	}
6144d2a7a1cSIgor Mitsyanko 
6154d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) {
61622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
6174d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate);
6184d2a7a1cSIgor Mitsyanko 	}
6194d2a7a1cSIgor Mitsyanko 
6204d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) {
62122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
6224d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate);
6234d2a7a1cSIgor Mitsyanko 	}
6244d2a7a1cSIgor Mitsyanko 
6254d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) {
62622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
6274d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags);
6284d2a7a1cSIgor Mitsyanko 	}
6294d2a7a1cSIgor Mitsyanko 
6304d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) {
63122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
6324d2a7a1cSIgor Mitsyanko 		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
6334d2a7a1cSIgor Mitsyanko 	}
6344d2a7a1cSIgor Mitsyanko 
6354d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) {
63622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
6374d2a7a1cSIgor Mitsyanko 		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
6384d2a7a1cSIgor Mitsyanko 	}
6394d2a7a1cSIgor Mitsyanko 
6404d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) {
64122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
6424d2a7a1cSIgor Mitsyanko 		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
6434d2a7a1cSIgor Mitsyanko 	}
6444d2a7a1cSIgor Mitsyanko 
6454d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) {
64622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
6474d2a7a1cSIgor Mitsyanko 		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
6484d2a7a1cSIgor Mitsyanko 	}
6494d2a7a1cSIgor Mitsyanko 
6504d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) {
65122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
6524d2a7a1cSIgor Mitsyanko 		sinfo->rx_packets = le32_to_cpu(stats->rx_packets);
6534d2a7a1cSIgor Mitsyanko 	}
6544d2a7a1cSIgor Mitsyanko 
6554d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) {
65622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
6574d2a7a1cSIgor Mitsyanko 		sinfo->tx_packets = le32_to_cpu(stats->tx_packets);
6584d2a7a1cSIgor Mitsyanko 	}
6594d2a7a1cSIgor Mitsyanko 
6604d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) {
66122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);
6624d2a7a1cSIgor Mitsyanko 		sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon);
6634d2a7a1cSIgor Mitsyanko 	}
6644d2a7a1cSIgor Mitsyanko 
6654d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) {
66622d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC);
6674d2a7a1cSIgor Mitsyanko 		sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc);
6684d2a7a1cSIgor Mitsyanko 	}
6694d2a7a1cSIgor Mitsyanko 
6704d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) {
67122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
6724d2a7a1cSIgor Mitsyanko 		sinfo->tx_failed = le32_to_cpu(stats->tx_failed);
6734d2a7a1cSIgor Mitsyanko 	}
6744d2a7a1cSIgor Mitsyanko 
6754d2a7a1cSIgor Mitsyanko #undef qtnf_sta_stat_avail
67698f44cb0SIgor Mitsyanko }
67798f44cb0SIgor Mitsyanko 
67898f44cb0SIgor Mitsyanko int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
67998f44cb0SIgor Mitsyanko 			  struct station_info *sinfo)
68098f44cb0SIgor Mitsyanko {
68198f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
68298f44cb0SIgor Mitsyanko 	struct qlink_cmd_get_sta_info *cmd;
68398f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_sta_info *resp;
6841066bd19SSergey Matyukevich 	size_t var_resp_len = 0;
68598f44cb0SIgor Mitsyanko 	int ret = 0;
68698f44cb0SIgor Mitsyanko 
68798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
68898f44cb0SIgor Mitsyanko 					    QLINK_CMD_GET_STA_INFO,
68998f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
690c93fe71cSSergey Matyukevich 	if (!cmd_skb)
69198f44cb0SIgor Mitsyanko 		return -ENOMEM;
69298f44cb0SIgor Mitsyanko 
69398f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
69498f44cb0SIgor Mitsyanko 
69598f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_get_sta_info *)cmd_skb->data;
69698f44cb0SIgor Mitsyanko 	ether_addr_copy(cmd->sta_addr, sta_mac);
69798f44cb0SIgor Mitsyanko 
69898f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb,
699c6ed298fSSergey Matyukevich 				       sizeof(*resp), &var_resp_len);
700c6ed298fSSergey Matyukevich 	if (ret)
70198f44cb0SIgor Mitsyanko 		goto out;
70298f44cb0SIgor Mitsyanko 
70398f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_sta_info *)resp_skb->data;
70498f44cb0SIgor Mitsyanko 
705c6ed298fSSergey Matyukevich 	if (!ether_addr_equal(sta_mac, resp->sta_addr)) {
70698f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: wrong mac in reply: %pM != %pM\n",
70798f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, resp->sta_addr, sta_mac);
70898f44cb0SIgor Mitsyanko 		ret = -EINVAL;
70998f44cb0SIgor Mitsyanko 		goto out;
71098f44cb0SIgor Mitsyanko 	}
71198f44cb0SIgor Mitsyanko 
7124d2a7a1cSIgor Mitsyanko 	qtnf_cmd_sta_info_parse(sinfo,
7134d2a7a1cSIgor Mitsyanko 				(const struct qlink_tlv_hdr *)resp->info,
7144d2a7a1cSIgor Mitsyanko 				var_resp_len);
71598f44cb0SIgor Mitsyanko 
71698f44cb0SIgor Mitsyanko out:
71798f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
71898f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
71998f44cb0SIgor Mitsyanko 
72098f44cb0SIgor Mitsyanko 	return ret;
72198f44cb0SIgor Mitsyanko }
72298f44cb0SIgor Mitsyanko 
72398f44cb0SIgor Mitsyanko static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif,
72498f44cb0SIgor Mitsyanko 					 enum nl80211_iftype iftype,
725de624a35SSergey Matyukevich 					 int use4addr,
72698f44cb0SIgor Mitsyanko 					 u8 *mac_addr,
72798f44cb0SIgor Mitsyanko 					 enum qlink_cmd_type cmd_type)
72898f44cb0SIgor Mitsyanko {
72998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
73098f44cb0SIgor Mitsyanko 	struct qlink_cmd_manage_intf *cmd;
73198f44cb0SIgor Mitsyanko 	const struct qlink_resp_manage_intf *resp;
73298f44cb0SIgor Mitsyanko 	int ret = 0;
73398f44cb0SIgor Mitsyanko 
73498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
73598f44cb0SIgor Mitsyanko 					    cmd_type,
73698f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
737c93fe71cSSergey Matyukevich 	if (!cmd_skb)
73898f44cb0SIgor Mitsyanko 		return -ENOMEM;
73998f44cb0SIgor Mitsyanko 
74098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
74198f44cb0SIgor Mitsyanko 
74298f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data;
743de624a35SSergey Matyukevich 	cmd->intf_info.use4addr = use4addr;
74498f44cb0SIgor Mitsyanko 
74598f44cb0SIgor Mitsyanko 	switch (iftype) {
74698f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_AP:
74798f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP);
74898f44cb0SIgor Mitsyanko 		break;
74998f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_STATION:
75098f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
75198f44cb0SIgor Mitsyanko 		break;
75298f44cb0SIgor Mitsyanko 	default:
75398f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: unsupported type %d\n", vif->mac->macid,
75498f44cb0SIgor Mitsyanko 		       vif->vifid, iftype);
75598f44cb0SIgor Mitsyanko 		ret = -EINVAL;
75698f44cb0SIgor Mitsyanko 		goto out;
75798f44cb0SIgor Mitsyanko 	}
75898f44cb0SIgor Mitsyanko 
75998f44cb0SIgor Mitsyanko 	if (mac_addr)
76098f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->intf_info.mac_addr, mac_addr);
76198f44cb0SIgor Mitsyanko 	else
76298f44cb0SIgor Mitsyanko 		eth_zero_addr(cmd->intf_info.mac_addr);
76398f44cb0SIgor Mitsyanko 
76498f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb,
765c6ed298fSSergey Matyukevich 				       sizeof(*resp), NULL);
766c6ed298fSSergey Matyukevich 	if (ret)
76798f44cb0SIgor Mitsyanko 		goto out;
76898f44cb0SIgor Mitsyanko 
76998f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_manage_intf *)resp_skb->data;
77098f44cb0SIgor Mitsyanko 	ether_addr_copy(vif->mac_addr, resp->intf_info.mac_addr);
77198f44cb0SIgor Mitsyanko 
77298f44cb0SIgor Mitsyanko out:
77398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
77498f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
77598f44cb0SIgor Mitsyanko 
77698f44cb0SIgor Mitsyanko 	return ret;
77798f44cb0SIgor Mitsyanko }
77898f44cb0SIgor Mitsyanko 
779de624a35SSergey Matyukevich int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype,
780de624a35SSergey Matyukevich 			   int use4addr, u8 *mac_addr)
78198f44cb0SIgor Mitsyanko {
782de624a35SSergey Matyukevich 	return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
78398f44cb0SIgor Mitsyanko 			QLINK_CMD_ADD_INTF);
78498f44cb0SIgor Mitsyanko }
78598f44cb0SIgor Mitsyanko 
78698f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif,
787de624a35SSergey Matyukevich 				   enum nl80211_iftype iftype,
788de624a35SSergey Matyukevich 				   int use4addr,
789de624a35SSergey Matyukevich 				   u8 *mac_addr)
79098f44cb0SIgor Mitsyanko {
791de624a35SSergey Matyukevich 	return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
79298f44cb0SIgor Mitsyanko 					     QLINK_CMD_CHANGE_INTF);
79398f44cb0SIgor Mitsyanko }
79498f44cb0SIgor Mitsyanko 
79598f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_intf(struct qtnf_vif *vif)
79698f44cb0SIgor Mitsyanko {
79798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
79898f44cb0SIgor Mitsyanko 	struct qlink_cmd_manage_intf *cmd;
79998f44cb0SIgor Mitsyanko 	int ret = 0;
80098f44cb0SIgor Mitsyanko 
80198f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
80298f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_INTF,
80398f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
804c93fe71cSSergey Matyukevich 	if (!cmd_skb)
80598f44cb0SIgor Mitsyanko 		return -ENOMEM;
80698f44cb0SIgor Mitsyanko 
80798f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
80898f44cb0SIgor Mitsyanko 
80998f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data;
81098f44cb0SIgor Mitsyanko 
81198f44cb0SIgor Mitsyanko 	switch (vif->wdev.iftype) {
81298f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_AP:
81398f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP);
81498f44cb0SIgor Mitsyanko 		break;
81598f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_STATION:
81698f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
81798f44cb0SIgor Mitsyanko 		break;
81898f44cb0SIgor Mitsyanko 	default:
81998f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: unsupported iftype %d\n", vif->mac->macid,
82098f44cb0SIgor Mitsyanko 			vif->vifid, vif->wdev.iftype);
82198f44cb0SIgor Mitsyanko 		ret = -EINVAL;
82298f44cb0SIgor Mitsyanko 		goto out;
82398f44cb0SIgor Mitsyanko 	}
82498f44cb0SIgor Mitsyanko 
82598f44cb0SIgor Mitsyanko 	eth_zero_addr(cmd->intf_info.mac_addr);
82698f44cb0SIgor Mitsyanko 
827c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
828c6ed298fSSergey Matyukevich 	if (ret)
82998f44cb0SIgor Mitsyanko 		goto out;
83098f44cb0SIgor Mitsyanko 
83198f44cb0SIgor Mitsyanko out:
83298f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
83398f44cb0SIgor Mitsyanko 	return ret;
83498f44cb0SIgor Mitsyanko }
83598f44cb0SIgor Mitsyanko 
8364dd07d2bSSergey Matyukevich static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags)
8374dd07d2bSSergey Matyukevich {
8384dd07d2bSSergey Matyukevich 	u32 flags = 0;
8394dd07d2bSSergey Matyukevich 
8404dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_OFDM)
8414dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_OFDM;
8424dd07d2bSSergey Matyukevich 
8434dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_CCK)
8444dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_CCK;
8454dd07d2bSSergey Matyukevich 
8464dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_INDOOR)
8474dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_INDOOR;
8484dd07d2bSSergey Matyukevich 
8494dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_OUTDOOR)
8504dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_OUTDOOR;
8514dd07d2bSSergey Matyukevich 
8524dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_DFS)
8534dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_DFS;
8544dd07d2bSSergey Matyukevich 
8554dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_PTP_ONLY)
8564dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_PTP_ONLY;
8574dd07d2bSSergey Matyukevich 
8584dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_PTMP_ONLY)
8594dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_PTMP_ONLY;
8604dd07d2bSSergey Matyukevich 
8614dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_IR)
8624dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_IR;
8634dd07d2bSSergey Matyukevich 
8644dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_AUTO_BW)
8654dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_AUTO_BW;
8664dd07d2bSSergey Matyukevich 
8674dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_IR_CONCURRENT)
8684dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_IR_CONCURRENT;
8694dd07d2bSSergey Matyukevich 
8704dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_HT40MINUS)
8714dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_HT40MINUS;
8724dd07d2bSSergey Matyukevich 
8734dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_HT40PLUS)
8744dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_HT40PLUS;
8754dd07d2bSSergey Matyukevich 
8764dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_80MHZ)
8774dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_80MHZ;
8784dd07d2bSSergey Matyukevich 
8794dd07d2bSSergey Matyukevich 	if (qflags & QLINK_RRF_NO_160MHZ)
8804dd07d2bSSergey Matyukevich 		flags |= NL80211_RRF_NO_160MHZ;
8814dd07d2bSSergey Matyukevich 
8824dd07d2bSSergey Matyukevich 	return flags;
8834dd07d2bSSergey Matyukevich }
8844dd07d2bSSergey Matyukevich 
88598f44cb0SIgor Mitsyanko static int
88698f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
8874dd07d2bSSergey Matyukevich 			   const struct qlink_resp_get_hw_info *resp,
8884dd07d2bSSergey Matyukevich 			   size_t info_len)
88998f44cb0SIgor Mitsyanko {
89098f44cb0SIgor Mitsyanko 	struct qtnf_hw_info *hwinfo = &bus->hw_info;
8914dd07d2bSSergey Matyukevich 	const struct qlink_tlv_hdr *tlv;
8924dd07d2bSSergey Matyukevich 	const struct qlink_tlv_reg_rule *tlv_rule;
8935ec5b532SVasily Ulyanov 	const char *bld_name = NULL;
8945ec5b532SVasily Ulyanov 	const char *bld_rev = NULL;
8955ec5b532SVasily Ulyanov 	const char *bld_type = NULL;
8965ec5b532SVasily Ulyanov 	const char *bld_label = NULL;
8975ec5b532SVasily Ulyanov 	u32 bld_tmstamp = 0;
8985ec5b532SVasily Ulyanov 	u32 plat_id = 0;
8995ec5b532SVasily Ulyanov 	const char *hw_id = NULL;
9005ec5b532SVasily Ulyanov 	const char *calibration_ver = NULL;
9015ec5b532SVasily Ulyanov 	const char *uboot_ver = NULL;
9025ec5b532SVasily Ulyanov 	u32 hw_ver = 0;
9034dd07d2bSSergey Matyukevich 	struct ieee80211_reg_rule *rule;
9044dd07d2bSSergey Matyukevich 	u16 tlv_type;
9054dd07d2bSSergey Matyukevich 	u16 tlv_value_len;
9064dd07d2bSSergey Matyukevich 	unsigned int rule_idx = 0;
9074dd07d2bSSergey Matyukevich 
9084dd07d2bSSergey Matyukevich 	if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
9094dd07d2bSSergey Matyukevich 		return -E2BIG;
9104dd07d2bSSergey Matyukevich 
911db040dfaSGustavo A. R. Silva 	hwinfo->rd = kzalloc(struct_size(hwinfo->rd, reg_rules,
912db040dfaSGustavo A. R. Silva 					 resp->n_reg_rules), GFP_KERNEL);
9134dd07d2bSSergey Matyukevich 
9144dd07d2bSSergey Matyukevich 	if (!hwinfo->rd)
9154dd07d2bSSergey Matyukevich 		return -ENOMEM;
91698f44cb0SIgor Mitsyanko 
91798f44cb0SIgor Mitsyanko 	hwinfo->num_mac = resp->num_mac;
91898f44cb0SIgor Mitsyanko 	hwinfo->mac_bitmap = resp->mac_bitmap;
91998f44cb0SIgor Mitsyanko 	hwinfo->fw_ver = le32_to_cpu(resp->fw_ver);
92098f44cb0SIgor Mitsyanko 	hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver);
92198f44cb0SIgor Mitsyanko 	hwinfo->total_tx_chain = resp->total_tx_chain;
92298f44cb0SIgor Mitsyanko 	hwinfo->total_rx_chain = resp->total_rx_chain;
92398f44cb0SIgor Mitsyanko 	hwinfo->hw_capab = le32_to_cpu(resp->hw_capab);
9244dd07d2bSSergey Matyukevich 	hwinfo->rd->n_reg_rules = resp->n_reg_rules;
9254dd07d2bSSergey Matyukevich 	hwinfo->rd->alpha2[0] = resp->alpha2[0];
9264dd07d2bSSergey Matyukevich 	hwinfo->rd->alpha2[1] = resp->alpha2[1];
9274dd07d2bSSergey Matyukevich 
9285ec5b532SVasily Ulyanov 	bld_tmstamp = le32_to_cpu(resp->bld_tmstamp);
9295ec5b532SVasily Ulyanov 	plat_id = le32_to_cpu(resp->plat_id);
9305ec5b532SVasily Ulyanov 	hw_ver = le32_to_cpu(resp->hw_ver);
9315ec5b532SVasily Ulyanov 
9324dd07d2bSSergey Matyukevich 	switch (resp->dfs_region) {
9334dd07d2bSSergey Matyukevich 	case QLINK_DFS_FCC:
9344dd07d2bSSergey Matyukevich 		hwinfo->rd->dfs_region = NL80211_DFS_FCC;
9354dd07d2bSSergey Matyukevich 		break;
9364dd07d2bSSergey Matyukevich 	case QLINK_DFS_ETSI:
9374dd07d2bSSergey Matyukevich 		hwinfo->rd->dfs_region = NL80211_DFS_ETSI;
9384dd07d2bSSergey Matyukevich 		break;
9394dd07d2bSSergey Matyukevich 	case QLINK_DFS_JP:
9404dd07d2bSSergey Matyukevich 		hwinfo->rd->dfs_region = NL80211_DFS_JP;
9414dd07d2bSSergey Matyukevich 		break;
9424dd07d2bSSergey Matyukevich 	case QLINK_DFS_UNSET:
9434dd07d2bSSergey Matyukevich 	default:
9444dd07d2bSSergey Matyukevich 		hwinfo->rd->dfs_region = NL80211_DFS_UNSET;
9454dd07d2bSSergey Matyukevich 		break;
9464dd07d2bSSergey Matyukevich 	}
9474dd07d2bSSergey Matyukevich 
9484dd07d2bSSergey Matyukevich 	tlv = (const struct qlink_tlv_hdr *)resp->info;
9494dd07d2bSSergey Matyukevich 
9504dd07d2bSSergey Matyukevich 	while (info_len >= sizeof(*tlv)) {
9514dd07d2bSSergey Matyukevich 		tlv_type = le16_to_cpu(tlv->type);
9524dd07d2bSSergey Matyukevich 		tlv_value_len = le16_to_cpu(tlv->len);
9534dd07d2bSSergey Matyukevich 
9544dd07d2bSSergey Matyukevich 		if (tlv_value_len + sizeof(*tlv) > info_len) {
9554dd07d2bSSergey Matyukevich 			pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
9564dd07d2bSSergey Matyukevich 				tlv_type, tlv_value_len);
9574dd07d2bSSergey Matyukevich 			return -EINVAL;
9584dd07d2bSSergey Matyukevich 		}
9594dd07d2bSSergey Matyukevich 
9604dd07d2bSSergey Matyukevich 		switch (tlv_type) {
9614dd07d2bSSergey Matyukevich 		case QTN_TLV_ID_REG_RULE:
9624dd07d2bSSergey Matyukevich 			if (rule_idx >= resp->n_reg_rules) {
9634dd07d2bSSergey Matyukevich 				pr_warn("unexpected number of rules: %u\n",
9644dd07d2bSSergey Matyukevich 					resp->n_reg_rules);
9654dd07d2bSSergey Matyukevich 				return -EINVAL;
9664dd07d2bSSergey Matyukevich 			}
9674dd07d2bSSergey Matyukevich 
9684dd07d2bSSergey Matyukevich 			if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) {
9694dd07d2bSSergey Matyukevich 				pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
9704dd07d2bSSergey Matyukevich 					tlv_type, tlv_value_len);
9714dd07d2bSSergey Matyukevich 				return -EINVAL;
9724dd07d2bSSergey Matyukevich 			}
9734dd07d2bSSergey Matyukevich 
9744dd07d2bSSergey Matyukevich 			tlv_rule = (const struct qlink_tlv_reg_rule *)tlv;
9754dd07d2bSSergey Matyukevich 			rule = &hwinfo->rd->reg_rules[rule_idx++];
9764dd07d2bSSergey Matyukevich 
9774dd07d2bSSergey Matyukevich 			rule->freq_range.start_freq_khz =
9784dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->start_freq_khz);
9794dd07d2bSSergey Matyukevich 			rule->freq_range.end_freq_khz =
9804dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->end_freq_khz);
9814dd07d2bSSergey Matyukevich 			rule->freq_range.max_bandwidth_khz =
9824dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->max_bandwidth_khz);
9834dd07d2bSSergey Matyukevich 			rule->power_rule.max_antenna_gain =
9844dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->max_antenna_gain);
9854dd07d2bSSergey Matyukevich 			rule->power_rule.max_eirp =
9864dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->max_eirp);
9874dd07d2bSSergey Matyukevich 			rule->dfs_cac_ms =
9884dd07d2bSSergey Matyukevich 				le32_to_cpu(tlv_rule->dfs_cac_ms);
9894dd07d2bSSergey Matyukevich 			rule->flags = qtnf_cmd_resp_reg_rule_flags_parse(
9904dd07d2bSSergey Matyukevich 					le32_to_cpu(tlv_rule->flags));
9914dd07d2bSSergey Matyukevich 			break;
9925ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_NAME:
9935ec5b532SVasily Ulyanov 			bld_name = (const void *)tlv->val;
9945ec5b532SVasily Ulyanov 			break;
9955ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_REV:
9965ec5b532SVasily Ulyanov 			bld_rev = (const void *)tlv->val;
9975ec5b532SVasily Ulyanov 			break;
9985ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_TYPE:
9995ec5b532SVasily Ulyanov 			bld_type = (const void *)tlv->val;
10005ec5b532SVasily Ulyanov 			break;
10015ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_LABEL:
10025ec5b532SVasily Ulyanov 			bld_label = (const void *)tlv->val;
10035ec5b532SVasily Ulyanov 			break;
10045ec5b532SVasily Ulyanov 		case QTN_TLV_ID_HW_ID:
10055ec5b532SVasily Ulyanov 			hw_id = (const void *)tlv->val;
10065ec5b532SVasily Ulyanov 			break;
10075ec5b532SVasily Ulyanov 		case QTN_TLV_ID_CALIBRATION_VER:
10085ec5b532SVasily Ulyanov 			calibration_ver = (const void *)tlv->val;
10095ec5b532SVasily Ulyanov 			break;
10105ec5b532SVasily Ulyanov 		case QTN_TLV_ID_UBOOT_VER:
10115ec5b532SVasily Ulyanov 			uboot_ver = (const void *)tlv->val;
10125ec5b532SVasily Ulyanov 			break;
10138f1180e0SAndrey Shevchenko 		case QTN_TLV_ID_MAX_SCAN_SSIDS:
10148f1180e0SAndrey Shevchenko 			hwinfo->max_scan_ssids = *tlv->val;
10158f1180e0SAndrey Shevchenko 			break;
10164dd07d2bSSergey Matyukevich 		default:
10174dd07d2bSSergey Matyukevich 			break;
10184dd07d2bSSergey Matyukevich 		}
10194dd07d2bSSergey Matyukevich 
10204dd07d2bSSergey Matyukevich 		info_len -= tlv_value_len + sizeof(*tlv);
10214dd07d2bSSergey Matyukevich 		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
10224dd07d2bSSergey Matyukevich 	}
10234dd07d2bSSergey Matyukevich 
10244dd07d2bSSergey Matyukevich 	if (rule_idx != resp->n_reg_rules) {
10254dd07d2bSSergey Matyukevich 		pr_warn("unexpected number of rules: expected %u got %u\n",
10264dd07d2bSSergey Matyukevich 			resp->n_reg_rules, rule_idx);
10274dd07d2bSSergey Matyukevich 		kfree(hwinfo->rd);
10284dd07d2bSSergey Matyukevich 		hwinfo->rd = NULL;
10294dd07d2bSSergey Matyukevich 		return -EINVAL;
10304dd07d2bSSergey Matyukevich 	}
103198f44cb0SIgor Mitsyanko 
1032db5c6d4aSIgor Mitsyanko 	pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n",
103398f44cb0SIgor Mitsyanko 		hwinfo->fw_ver, hwinfo->mac_bitmap,
10344dd07d2bSSergey Matyukevich 		hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1],
1035db5c6d4aSIgor Mitsyanko 		hwinfo->total_tx_chain, hwinfo->total_rx_chain,
1036db5c6d4aSIgor Mitsyanko 		hwinfo->hw_capab);
103798f44cb0SIgor Mitsyanko 
10385ec5b532SVasily Ulyanov 	pr_info("\nBuild name:            %s"  \
10395ec5b532SVasily Ulyanov 		"\nBuild revision:        %s"  \
10405ec5b532SVasily Ulyanov 		"\nBuild type:            %s"  \
10415ec5b532SVasily Ulyanov 		"\nBuild label:           %s"  \
10425ec5b532SVasily Ulyanov 		"\nBuild timestamp:       %lu" \
10435ec5b532SVasily Ulyanov 		"\nPlatform ID:           %lu" \
10445ec5b532SVasily Ulyanov 		"\nHardware ID:           %s"  \
10455ec5b532SVasily Ulyanov 		"\nCalibration version:   %s"  \
10465ec5b532SVasily Ulyanov 		"\nU-Boot version:        %s"  \
10475ec5b532SVasily Ulyanov 		"\nHardware version:      0x%08x",
10485ec5b532SVasily Ulyanov 		bld_name, bld_rev, bld_type, bld_label,
10495ec5b532SVasily Ulyanov 		(unsigned long)bld_tmstamp,
10505ec5b532SVasily Ulyanov 		(unsigned long)plat_id,
10515ec5b532SVasily Ulyanov 		hw_id, calibration_ver, uboot_ver, hw_ver);
10525ec5b532SVasily Ulyanov 
10530b419d01SVasily Ulyanov 	strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version));
10540b419d01SVasily Ulyanov 	hwinfo->hw_version = hw_ver;
10550b419d01SVasily Ulyanov 
105698f44cb0SIgor Mitsyanko 	return 0;
105798f44cb0SIgor Mitsyanko }
105898f44cb0SIgor Mitsyanko 
105928b91884SSergey Matyukevich static void
106028b91884SSergey Matyukevich qtnf_parse_wowlan_info(struct qtnf_wmac *mac,
106128b91884SSergey Matyukevich 		       const struct qlink_wowlan_capab_data *wowlan)
106228b91884SSergey Matyukevich {
106328b91884SSergey Matyukevich 	struct qtnf_mac_info *mac_info = &mac->macinfo;
106428b91884SSergey Matyukevich 	const struct qlink_wowlan_support *data1;
106528b91884SSergey Matyukevich 	struct wiphy_wowlan_support *supp;
106628b91884SSergey Matyukevich 
106728b91884SSergey Matyukevich 	supp = kzalloc(sizeof(*supp), GFP_KERNEL);
106828b91884SSergey Matyukevich 	if (!supp)
106928b91884SSergey Matyukevich 		return;
107028b91884SSergey Matyukevich 
107128b91884SSergey Matyukevich 	switch (le16_to_cpu(wowlan->version)) {
107228b91884SSergey Matyukevich 	case 0x1:
107328b91884SSergey Matyukevich 		data1 = (struct qlink_wowlan_support *)wowlan->data;
107428b91884SSergey Matyukevich 
107528b91884SSergey Matyukevich 		supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT;
107628b91884SSergey Matyukevich 		supp->n_patterns = le32_to_cpu(data1->n_patterns);
107728b91884SSergey Matyukevich 		supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len);
107828b91884SSergey Matyukevich 		supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len);
107928b91884SSergey Matyukevich 
108028b91884SSergey Matyukevich 		mac_info->wowlan = supp;
108128b91884SSergey Matyukevich 		break;
108228b91884SSergey Matyukevich 	default:
108328b91884SSergey Matyukevich 		pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n",
108428b91884SSergey Matyukevich 			mac->macid, le16_to_cpu(wowlan->version));
108528b91884SSergey Matyukevich 		kfree(supp);
108628b91884SSergey Matyukevich 		break;
108728b91884SSergey Matyukevich 	}
108828b91884SSergey Matyukevich }
108928b91884SSergey Matyukevich 
109098f44cb0SIgor Mitsyanko static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
109198f44cb0SIgor Mitsyanko 					const u8 *tlv_buf, size_t tlv_buf_size)
109298f44cb0SIgor Mitsyanko {
1093537faf26SSergey Matyukevich 	struct ieee80211_iface_combination *comb = NULL;
1094537faf26SSergey Matyukevich 	size_t n_comb = 0;
1095537faf26SSergey Matyukevich 	struct ieee80211_iface_limit *limits;
1096537faf26SSergey Matyukevich 	const struct qlink_iface_comb_num *comb_num;
1097537faf26SSergey Matyukevich 	const struct qlink_iface_limit_record *rec;
1098537faf26SSergey Matyukevich 	const struct qlink_iface_limit *lim;
109928b91884SSergey Matyukevich 	const struct qlink_wowlan_capab_data *wowlan;
1100537faf26SSergey Matyukevich 	u16 rec_len;
1101537faf26SSergey Matyukevich 	u16 tlv_type;
1102537faf26SSergey Matyukevich 	u16 tlv_value_len;
110398f44cb0SIgor Mitsyanko 	size_t tlv_full_len;
110498f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
11059cbd5999SVasily Ulyanov 	u8 *ext_capa = NULL;
11069cbd5999SVasily Ulyanov 	u8 *ext_capa_mask = NULL;
11079cbd5999SVasily Ulyanov 	u8 ext_capa_len = 0;
11089cbd5999SVasily Ulyanov 	u8 ext_capa_mask_len = 0;
1109537faf26SSergey Matyukevich 	int i = 0;
111098f44cb0SIgor Mitsyanko 
111198f44cb0SIgor Mitsyanko 	tlv = (const struct qlink_tlv_hdr *)tlv_buf;
111298f44cb0SIgor Mitsyanko 	while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) {
111398f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
111498f44cb0SIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
111598f44cb0SIgor Mitsyanko 		tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
111698f44cb0SIgor Mitsyanko 		if (tlv_full_len > tlv_buf_size) {
111798f44cb0SIgor Mitsyanko 			pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n",
111898f44cb0SIgor Mitsyanko 				mac->macid, tlv_type, tlv_value_len);
111998f44cb0SIgor Mitsyanko 			return -EINVAL;
112098f44cb0SIgor Mitsyanko 		}
112198f44cb0SIgor Mitsyanko 
112298f44cb0SIgor Mitsyanko 		switch (tlv_type) {
112398f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_NUM_IFACE_COMB:
1124537faf26SSergey Matyukevich 			if (tlv_value_len != sizeof(*comb_num))
112598f44cb0SIgor Mitsyanko 				return -EINVAL;
112698f44cb0SIgor Mitsyanko 
1127537faf26SSergey Matyukevich 			comb_num = (void *)tlv->val;
112898f44cb0SIgor Mitsyanko 
1129537faf26SSergey Matyukevich 			/* free earlier iface comb memory */
1130537faf26SSergey Matyukevich 			qtnf_mac_iface_comb_free(mac);
113198f44cb0SIgor Mitsyanko 
1132537faf26SSergey Matyukevich 			mac->macinfo.n_if_comb =
1133537faf26SSergey Matyukevich 				le32_to_cpu(comb_num->iface_comb_num);
1134537faf26SSergey Matyukevich 
1135537faf26SSergey Matyukevich 			mac->macinfo.if_comb =
1136537faf26SSergey Matyukevich 				kcalloc(mac->macinfo.n_if_comb,
1137537faf26SSergey Matyukevich 					sizeof(*mac->macinfo.if_comb),
1138537faf26SSergey Matyukevich 					GFP_KERNEL);
1139537faf26SSergey Matyukevich 
1140537faf26SSergey Matyukevich 			if (!mac->macinfo.if_comb)
114198f44cb0SIgor Mitsyanko 				return -ENOMEM;
114298f44cb0SIgor Mitsyanko 
1143537faf26SSergey Matyukevich 			comb = mac->macinfo.if_comb;
1144537faf26SSergey Matyukevich 
1145537faf26SSergey Matyukevich 			pr_debug("MAC%u: %zu iface combinations\n",
1146537faf26SSergey Matyukevich 				 mac->macid, mac->macinfo.n_if_comb);
1147537faf26SSergey Matyukevich 
114898f44cb0SIgor Mitsyanko 			break;
114998f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_IFACE_LIMIT:
1150537faf26SSergey Matyukevich 			if (unlikely(!comb)) {
1151537faf26SSergey Matyukevich 				pr_warn("MAC%u: no combinations advertised\n",
115298f44cb0SIgor Mitsyanko 					mac->macid);
115398f44cb0SIgor Mitsyanko 				return -EINVAL;
115498f44cb0SIgor Mitsyanko 			}
115598f44cb0SIgor Mitsyanko 
1156537faf26SSergey Matyukevich 			if (n_comb >= mac->macinfo.n_if_comb) {
1157537faf26SSergey Matyukevich 				pr_warn("MAC%u: combinations count exceeded\n",
115898f44cb0SIgor Mitsyanko 					mac->macid);
1159537faf26SSergey Matyukevich 				n_comb++;
1160537faf26SSergey Matyukevich 				break;
1161537faf26SSergey Matyukevich 			}
1162537faf26SSergey Matyukevich 
1163537faf26SSergey Matyukevich 			rec = (void *)tlv->val;
1164537faf26SSergey Matyukevich 			rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim);
1165537faf26SSergey Matyukevich 
1166537faf26SSergey Matyukevich 			if (unlikely(tlv_value_len != rec_len)) {
1167537faf26SSergey Matyukevich 				pr_warn("MAC%u: record %zu size mismatch\n",
1168537faf26SSergey Matyukevich 					mac->macid, n_comb);
116998f44cb0SIgor Mitsyanko 				return -EINVAL;
117098f44cb0SIgor Mitsyanko 			}
117198f44cb0SIgor Mitsyanko 
11726396bb22SKees Cook 			limits = kcalloc(rec->n_limits, sizeof(*limits),
1173537faf26SSergey Matyukevich 					 GFP_KERNEL);
1174537faf26SSergey Matyukevich 			if (!limits)
1175537faf26SSergey Matyukevich 				return -ENOMEM;
117641c8fa0cSSergey Matyukevich 
1177537faf26SSergey Matyukevich 			comb[n_comb].num_different_channels =
1178537faf26SSergey Matyukevich 				rec->num_different_channels;
1179537faf26SSergey Matyukevich 			comb[n_comb].max_interfaces =
1180537faf26SSergey Matyukevich 				le16_to_cpu(rec->max_interfaces);
1181537faf26SSergey Matyukevich 			comb[n_comb].n_limits = rec->n_limits;
1182537faf26SSergey Matyukevich 			comb[n_comb].limits = limits;
118398f44cb0SIgor Mitsyanko 
1184537faf26SSergey Matyukevich 			for (i = 0; i < rec->n_limits; i++) {
1185537faf26SSergey Matyukevich 				lim = &rec->limits[i];
1186537faf26SSergey Matyukevich 				limits[i].max = le16_to_cpu(lim->max_num);
1187537faf26SSergey Matyukevich 				limits[i].types =
1188537faf26SSergey Matyukevich 					qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type));
1189537faf26SSergey Matyukevich 				pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n",
1190537faf26SSergey Matyukevich 					 mac->macid, n_comb,
1191537faf26SSergey Matyukevich 					 limits[i].max, limits[i].types);
1192537faf26SSergey Matyukevich 			}
119398f44cb0SIgor Mitsyanko 
1194537faf26SSergey Matyukevich 			n_comb++;
119598f44cb0SIgor Mitsyanko 			break;
11969cbd5999SVasily Ulyanov 		case WLAN_EID_EXT_CAPABILITY:
11979cbd5999SVasily Ulyanov 			if (unlikely(tlv_value_len > U8_MAX))
11989cbd5999SVasily Ulyanov 				return -EINVAL;
11999cbd5999SVasily Ulyanov 			ext_capa = (u8 *)tlv->val;
12009cbd5999SVasily Ulyanov 			ext_capa_len = tlv_value_len;
12019cbd5999SVasily Ulyanov 			break;
12029cbd5999SVasily Ulyanov 		case QTN_TLV_ID_EXT_CAPABILITY_MASK:
12039cbd5999SVasily Ulyanov 			if (unlikely(tlv_value_len > U8_MAX))
12049cbd5999SVasily Ulyanov 				return -EINVAL;
12059cbd5999SVasily Ulyanov 			ext_capa_mask = (u8 *)tlv->val;
12069cbd5999SVasily Ulyanov 			ext_capa_mask_len = tlv_value_len;
12079cbd5999SVasily Ulyanov 			break;
120828b91884SSergey Matyukevich 		case QTN_TLV_ID_WOWLAN_CAPAB:
120928b91884SSergey Matyukevich 			if (tlv_value_len < sizeof(*wowlan))
121028b91884SSergey Matyukevich 				return -EINVAL;
121128b91884SSergey Matyukevich 
121228b91884SSergey Matyukevich 			wowlan = (void *)tlv->val;
121328b91884SSergey Matyukevich 			if (!le16_to_cpu(wowlan->len)) {
121428b91884SSergey Matyukevich 				pr_warn("MAC%u: skip empty WoWLAN data\n",
121528b91884SSergey Matyukevich 					mac->macid);
121628b91884SSergey Matyukevich 				break;
121728b91884SSergey Matyukevich 			}
121828b91884SSergey Matyukevich 
121928b91884SSergey Matyukevich 			rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len);
122028b91884SSergey Matyukevich 			if (unlikely(tlv_value_len != rec_len)) {
122128b91884SSergey Matyukevich 				pr_warn("MAC%u: WoWLAN data size mismatch\n",
122228b91884SSergey Matyukevich 					mac->macid);
122328b91884SSergey Matyukevich 				return -EINVAL;
122428b91884SSergey Matyukevich 			}
122528b91884SSergey Matyukevich 
122628b91884SSergey Matyukevich 			kfree(mac->macinfo.wowlan);
122728b91884SSergey Matyukevich 			mac->macinfo.wowlan = NULL;
122828b91884SSergey Matyukevich 			qtnf_parse_wowlan_info(mac, wowlan);
122928b91884SSergey Matyukevich 			break;
123098f44cb0SIgor Mitsyanko 		default:
123128b91884SSergey Matyukevich 			pr_warn("MAC%u: unknown TLV type %u\n",
123228b91884SSergey Matyukevich 				mac->macid, tlv_type);
123398f44cb0SIgor Mitsyanko 			break;
123498f44cb0SIgor Mitsyanko 		}
1235805b28c0SSergey Matyukevich 
123698f44cb0SIgor Mitsyanko 		tlv_buf_size -= tlv_full_len;
123798f44cb0SIgor Mitsyanko 		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
123898f44cb0SIgor Mitsyanko 	}
123998f44cb0SIgor Mitsyanko 
124098f44cb0SIgor Mitsyanko 	if (tlv_buf_size) {
124198f44cb0SIgor Mitsyanko 		pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n",
124298f44cb0SIgor Mitsyanko 			mac->macid, tlv_buf_size);
124398f44cb0SIgor Mitsyanko 		return -EINVAL;
124498f44cb0SIgor Mitsyanko 	}
124598f44cb0SIgor Mitsyanko 
1246537faf26SSergey Matyukevich 	if (mac->macinfo.n_if_comb != n_comb) {
124798f44cb0SIgor Mitsyanko 		pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n",
1248537faf26SSergey Matyukevich 		       mac->macid, mac->macinfo.n_if_comb, n_comb);
124998f44cb0SIgor Mitsyanko 		return -EINVAL;
125098f44cb0SIgor Mitsyanko 	}
125198f44cb0SIgor Mitsyanko 
12529cbd5999SVasily Ulyanov 	if (ext_capa_len != ext_capa_mask_len) {
12539cbd5999SVasily Ulyanov 		pr_err("MAC%u: ext_capa/_mask lengths mismatch: %u != %u\n",
12549cbd5999SVasily Ulyanov 		       mac->macid, ext_capa_len, ext_capa_mask_len);
12559cbd5999SVasily Ulyanov 		return -EINVAL;
12569cbd5999SVasily Ulyanov 	}
12579cbd5999SVasily Ulyanov 
12589cbd5999SVasily Ulyanov 	if (ext_capa_len > 0) {
12599cbd5999SVasily Ulyanov 		ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL);
12609cbd5999SVasily Ulyanov 		if (!ext_capa)
12619cbd5999SVasily Ulyanov 			return -ENOMEM;
12629cbd5999SVasily Ulyanov 
12639cbd5999SVasily Ulyanov 		ext_capa_mask =
12649cbd5999SVasily Ulyanov 			kmemdup(ext_capa_mask, ext_capa_mask_len, GFP_KERNEL);
12659cbd5999SVasily Ulyanov 		if (!ext_capa_mask) {
12669cbd5999SVasily Ulyanov 			kfree(ext_capa);
12679cbd5999SVasily Ulyanov 			return -ENOMEM;
12689cbd5999SVasily Ulyanov 		}
12699cbd5999SVasily Ulyanov 	} else {
12709cbd5999SVasily Ulyanov 		ext_capa = NULL;
12719cbd5999SVasily Ulyanov 		ext_capa_mask = NULL;
12729cbd5999SVasily Ulyanov 	}
12739cbd5999SVasily Ulyanov 
1274ab1c64a1SSergey Matyukevich 	qtnf_mac_ext_caps_free(mac);
12759cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities = ext_capa;
12769cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities_mask = ext_capa_mask;
12779cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities_len = ext_capa_len;
12789cbd5999SVasily Ulyanov 
127998f44cb0SIgor Mitsyanko 	return 0;
128098f44cb0SIgor Mitsyanko }
128198f44cb0SIgor Mitsyanko 
128298f44cb0SIgor Mitsyanko static void
128398f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac,
128498f44cb0SIgor Mitsyanko 			    const struct qlink_resp_get_mac_info *resp_info)
128598f44cb0SIgor Mitsyanko {
128698f44cb0SIgor Mitsyanko 	struct qtnf_mac_info *mac_info;
128798f44cb0SIgor Mitsyanko 	struct qtnf_vif *vif;
128898f44cb0SIgor Mitsyanko 
128998f44cb0SIgor Mitsyanko 	mac_info = &mac->macinfo;
129098f44cb0SIgor Mitsyanko 
129198f44cb0SIgor Mitsyanko 	mac_info->bands_cap = resp_info->bands_cap;
129298f44cb0SIgor Mitsyanko 	memcpy(&mac_info->dev_mac, &resp_info->dev_mac,
129398f44cb0SIgor Mitsyanko 	       sizeof(mac_info->dev_mac));
129498f44cb0SIgor Mitsyanko 
129598f44cb0SIgor Mitsyanko 	ether_addr_copy(mac->macaddr, mac_info->dev_mac);
129698f44cb0SIgor Mitsyanko 
129798f44cb0SIgor Mitsyanko 	vif = qtnf_mac_get_base_vif(mac);
129898f44cb0SIgor Mitsyanko 	if (vif)
129998f44cb0SIgor Mitsyanko 		ether_addr_copy(vif->mac_addr, mac->macaddr);
130098f44cb0SIgor Mitsyanko 	else
130198f44cb0SIgor Mitsyanko 		pr_err("could not get valid base vif\n");
130298f44cb0SIgor Mitsyanko 
130398f44cb0SIgor Mitsyanko 	mac_info->num_tx_chain = resp_info->num_tx_chain;
130498f44cb0SIgor Mitsyanko 	mac_info->num_rx_chain = resp_info->num_rx_chain;
130598f44cb0SIgor Mitsyanko 
130698f44cb0SIgor Mitsyanko 	mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta);
130798f44cb0SIgor Mitsyanko 	mac_info->radar_detect_widths =
130898f44cb0SIgor Mitsyanko 			qlink_chan_width_mask_to_nl(le16_to_cpu(
130998f44cb0SIgor Mitsyanko 					resp_info->radar_detect_widths));
1310f1398fd2SVasily Ulyanov 	mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs);
131198f44cb0SIgor Mitsyanko 
1312d42df85fSIgor Mitsyanko 	memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask,
1313d42df85fSIgor Mitsyanko 	       sizeof(mac_info->ht_cap_mod_mask));
1314d42df85fSIgor Mitsyanko 	memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask,
1315d42df85fSIgor Mitsyanko 	       sizeof(mac_info->vht_cap_mod_mask));
131698f44cb0SIgor Mitsyanko }
131798f44cb0SIgor Mitsyanko 
1318e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_htcap(const u8 *info,
1319e294cbfdSIgor Mitsyanko 					  struct ieee80211_sta_ht_cap *bcap)
1320e294cbfdSIgor Mitsyanko {
1321e294cbfdSIgor Mitsyanko 	const struct ieee80211_ht_cap *ht_cap =
1322e294cbfdSIgor Mitsyanko 		(const struct ieee80211_ht_cap *)info;
1323e294cbfdSIgor Mitsyanko 
1324e294cbfdSIgor Mitsyanko 	bcap->ht_supported = true;
1325e294cbfdSIgor Mitsyanko 	bcap->cap = le16_to_cpu(ht_cap->cap_info);
1326e294cbfdSIgor Mitsyanko 	bcap->ampdu_factor =
1327e294cbfdSIgor Mitsyanko 		ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
1328e294cbfdSIgor Mitsyanko 	bcap->ampdu_density =
1329e294cbfdSIgor Mitsyanko 		(ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >>
1330e294cbfdSIgor Mitsyanko 		IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
1331e294cbfdSIgor Mitsyanko 	memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs));
1332e294cbfdSIgor Mitsyanko }
1333e294cbfdSIgor Mitsyanko 
1334e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info,
1335e294cbfdSIgor Mitsyanko 					   struct ieee80211_sta_vht_cap *bcap)
1336e294cbfdSIgor Mitsyanko {
1337e294cbfdSIgor Mitsyanko 	const struct ieee80211_vht_cap *vht_cap =
1338e294cbfdSIgor Mitsyanko 		(const struct ieee80211_vht_cap *)info;
1339e294cbfdSIgor Mitsyanko 
1340e294cbfdSIgor Mitsyanko 	bcap->vht_supported = true;
1341e294cbfdSIgor Mitsyanko 	bcap->cap = le32_to_cpu(vht_cap->vht_cap_info);
1342e294cbfdSIgor Mitsyanko 	memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs));
1343e294cbfdSIgor Mitsyanko }
1344e294cbfdSIgor Mitsyanko 
134598f44cb0SIgor Mitsyanko static int
1346e294cbfdSIgor Mitsyanko qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
1347e294cbfdSIgor Mitsyanko 			     struct qlink_resp_band_info_get *resp,
134898f44cb0SIgor Mitsyanko 			     size_t payload_len)
134998f44cb0SIgor Mitsyanko {
135098f44cb0SIgor Mitsyanko 	u16 tlv_type;
135198f44cb0SIgor Mitsyanko 	size_t tlv_len;
1352e294cbfdSIgor Mitsyanko 	size_t tlv_dlen;
135398f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
13545bf374abSSergey Matyukevich 	const struct qlink_channel *qchan;
135598f44cb0SIgor Mitsyanko 	struct ieee80211_channel *chan;
135698f44cb0SIgor Mitsyanko 	unsigned int chidx = 0;
135798f44cb0SIgor Mitsyanko 	u32 qflags;
135898f44cb0SIgor Mitsyanko 
1359e294cbfdSIgor Mitsyanko 	memset(&band->ht_cap, 0, sizeof(band->ht_cap));
1360e294cbfdSIgor Mitsyanko 	memset(&band->vht_cap, 0, sizeof(band->vht_cap));
1361e294cbfdSIgor Mitsyanko 
13624dd07d2bSSergey Matyukevich 	if (band->channels) {
13634dd07d2bSSergey Matyukevich 		if (band->n_channels == resp->num_chans) {
13644dd07d2bSSergey Matyukevich 			memset(band->channels, 0,
13654dd07d2bSSergey Matyukevich 			       sizeof(*band->channels) * band->n_channels);
13664dd07d2bSSergey Matyukevich 		} else {
136798f44cb0SIgor Mitsyanko 			kfree(band->channels);
13684dd07d2bSSergey Matyukevich 			band->n_channels = 0;
136998f44cb0SIgor Mitsyanko 			band->channels = NULL;
13704dd07d2bSSergey Matyukevich 		}
13714dd07d2bSSergey Matyukevich 	}
137298f44cb0SIgor Mitsyanko 
137398f44cb0SIgor Mitsyanko 	band->n_channels = resp->num_chans;
137498f44cb0SIgor Mitsyanko 	if (band->n_channels == 0)
137598f44cb0SIgor Mitsyanko 		return 0;
137698f44cb0SIgor Mitsyanko 
13774dd07d2bSSergey Matyukevich 	if (!band->channels)
13784dd07d2bSSergey Matyukevich 		band->channels = kcalloc(band->n_channels, sizeof(*chan),
13794dd07d2bSSergey Matyukevich 					 GFP_KERNEL);
138098f44cb0SIgor Mitsyanko 	if (!band->channels) {
138198f44cb0SIgor Mitsyanko 		band->n_channels = 0;
138298f44cb0SIgor Mitsyanko 		return -ENOMEM;
138398f44cb0SIgor Mitsyanko 	}
138498f44cb0SIgor Mitsyanko 
138598f44cb0SIgor Mitsyanko 	tlv = (struct qlink_tlv_hdr *)resp->info;
138698f44cb0SIgor Mitsyanko 
138798f44cb0SIgor Mitsyanko 	while (payload_len >= sizeof(*tlv)) {
138898f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
1389e294cbfdSIgor Mitsyanko 		tlv_dlen = le16_to_cpu(tlv->len);
1390e294cbfdSIgor Mitsyanko 		tlv_len = tlv_dlen + sizeof(*tlv);
139198f44cb0SIgor Mitsyanko 
139298f44cb0SIgor Mitsyanko 		if (tlv_len > payload_len) {
139398f44cb0SIgor Mitsyanko 			pr_warn("malformed TLV 0x%.2X; LEN: %zu\n",
139498f44cb0SIgor Mitsyanko 				tlv_type, tlv_len);
139598f44cb0SIgor Mitsyanko 			goto error_ret;
139698f44cb0SIgor Mitsyanko 		}
139798f44cb0SIgor Mitsyanko 
139898f44cb0SIgor Mitsyanko 		switch (tlv_type) {
139998f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_CHANNEL:
14005bf374abSSergey Matyukevich 			if (unlikely(tlv_dlen != sizeof(*qchan))) {
140198f44cb0SIgor Mitsyanko 				pr_err("invalid channel TLV len %zu\n",
140298f44cb0SIgor Mitsyanko 				       tlv_len);
140398f44cb0SIgor Mitsyanko 				goto error_ret;
140498f44cb0SIgor Mitsyanko 			}
140598f44cb0SIgor Mitsyanko 
140698f44cb0SIgor Mitsyanko 			if (chidx == band->n_channels) {
140798f44cb0SIgor Mitsyanko 				pr_err("too many channel TLVs\n");
140898f44cb0SIgor Mitsyanko 				goto error_ret;
140998f44cb0SIgor Mitsyanko 			}
141098f44cb0SIgor Mitsyanko 
14115bf374abSSergey Matyukevich 			qchan = (const struct qlink_channel *)tlv->val;
141298f44cb0SIgor Mitsyanko 			chan = &band->channels[chidx++];
141398f44cb0SIgor Mitsyanko 			qflags = le32_to_cpu(qchan->flags);
141498f44cb0SIgor Mitsyanko 
141598f44cb0SIgor Mitsyanko 			chan->hw_value = le16_to_cpu(qchan->hw_value);
141698f44cb0SIgor Mitsyanko 			chan->band = band->band;
141798f44cb0SIgor Mitsyanko 			chan->center_freq = le16_to_cpu(qchan->center_freq);
141898f44cb0SIgor Mitsyanko 			chan->max_antenna_gain = (int)qchan->max_antenna_gain;
141998f44cb0SIgor Mitsyanko 			chan->max_power = (int)qchan->max_power;
142098f44cb0SIgor Mitsyanko 			chan->max_reg_power = (int)qchan->max_reg_power;
142198f44cb0SIgor Mitsyanko 			chan->beacon_found = qchan->beacon_found;
142298f44cb0SIgor Mitsyanko 			chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms);
142398f44cb0SIgor Mitsyanko 			chan->flags = 0;
142498f44cb0SIgor Mitsyanko 
142598f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_DISABLED)
142698f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_DISABLED;
142798f44cb0SIgor Mitsyanko 
142898f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_IR)
142998f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_IR;
143098f44cb0SIgor Mitsyanko 
143198f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_HT40PLUS)
143298f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_HT40PLUS;
143398f44cb0SIgor Mitsyanko 
143498f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_HT40MINUS)
143598f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_HT40MINUS;
143698f44cb0SIgor Mitsyanko 
143798f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_OFDM)
143898f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_OFDM;
143998f44cb0SIgor Mitsyanko 
144098f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_80MHZ)
144198f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_80MHZ;
144298f44cb0SIgor Mitsyanko 
144398f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_160MHZ)
144498f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_160MHZ;
144598f44cb0SIgor Mitsyanko 
144698f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_INDOOR_ONLY)
144798f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_INDOOR_ONLY;
144898f44cb0SIgor Mitsyanko 
144998f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_IR_CONCURRENT)
145098f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_IR_CONCURRENT;
145198f44cb0SIgor Mitsyanko 
145298f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_20MHZ)
145398f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_20MHZ;
145498f44cb0SIgor Mitsyanko 
145598f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_10MHZ)
145698f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_10MHZ;
145798f44cb0SIgor Mitsyanko 
145898f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_RADAR) {
145998f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_RADAR;
146098f44cb0SIgor Mitsyanko 				chan->dfs_state_entered = jiffies;
146198f44cb0SIgor Mitsyanko 
146298f44cb0SIgor Mitsyanko 				if (qchan->dfs_state == QLINK_DFS_USABLE)
146398f44cb0SIgor Mitsyanko 					chan->dfs_state = NL80211_DFS_USABLE;
146498f44cb0SIgor Mitsyanko 				else if (qchan->dfs_state ==
146598f44cb0SIgor Mitsyanko 					QLINK_DFS_AVAILABLE)
146698f44cb0SIgor Mitsyanko 					chan->dfs_state = NL80211_DFS_AVAILABLE;
146798f44cb0SIgor Mitsyanko 				else
146898f44cb0SIgor Mitsyanko 					chan->dfs_state =
146998f44cb0SIgor Mitsyanko 						NL80211_DFS_UNAVAILABLE;
147098f44cb0SIgor Mitsyanko 			}
147198f44cb0SIgor Mitsyanko 
147298f44cb0SIgor Mitsyanko 			pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n",
147398f44cb0SIgor Mitsyanko 				 chan->hw_value, chan->flags, chan->max_power,
147498f44cb0SIgor Mitsyanko 				 chan->max_reg_power);
147598f44cb0SIgor Mitsyanko 			break;
1476e294cbfdSIgor Mitsyanko 		case WLAN_EID_HT_CAPABILITY:
1477e294cbfdSIgor Mitsyanko 			if (unlikely(tlv_dlen !=
1478e294cbfdSIgor Mitsyanko 				     sizeof(struct ieee80211_ht_cap))) {
1479e294cbfdSIgor Mitsyanko 				pr_err("bad HTCAP TLV len %zu\n", tlv_dlen);
1480e294cbfdSIgor Mitsyanko 				goto error_ret;
1481e294cbfdSIgor Mitsyanko 			}
1482e294cbfdSIgor Mitsyanko 
1483e294cbfdSIgor Mitsyanko 			qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap);
1484e294cbfdSIgor Mitsyanko 			break;
1485e294cbfdSIgor Mitsyanko 		case WLAN_EID_VHT_CAPABILITY:
1486e294cbfdSIgor Mitsyanko 			if (unlikely(tlv_dlen !=
1487e294cbfdSIgor Mitsyanko 				     sizeof(struct ieee80211_vht_cap))) {
1488e294cbfdSIgor Mitsyanko 				pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen);
1489e294cbfdSIgor Mitsyanko 				goto error_ret;
1490e294cbfdSIgor Mitsyanko 			}
1491e294cbfdSIgor Mitsyanko 
1492e294cbfdSIgor Mitsyanko 			qtnf_cmd_resp_band_fill_vhtcap(tlv->val,
1493e294cbfdSIgor Mitsyanko 						       &band->vht_cap);
1494e294cbfdSIgor Mitsyanko 			break;
149598f44cb0SIgor Mitsyanko 		default:
149698f44cb0SIgor Mitsyanko 			pr_warn("unknown TLV type: %#x\n", tlv_type);
149798f44cb0SIgor Mitsyanko 			break;
149898f44cb0SIgor Mitsyanko 		}
149998f44cb0SIgor Mitsyanko 
150098f44cb0SIgor Mitsyanko 		payload_len -= tlv_len;
1501e294cbfdSIgor Mitsyanko 		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_dlen);
150298f44cb0SIgor Mitsyanko 	}
150398f44cb0SIgor Mitsyanko 
150498f44cb0SIgor Mitsyanko 	if (payload_len) {
150598f44cb0SIgor Mitsyanko 		pr_err("malformed TLV buf; bytes left: %zu\n", payload_len);
150698f44cb0SIgor Mitsyanko 		goto error_ret;
150798f44cb0SIgor Mitsyanko 	}
150898f44cb0SIgor Mitsyanko 
150998f44cb0SIgor Mitsyanko 	if (band->n_channels != chidx) {
151098f44cb0SIgor Mitsyanko 		pr_err("channel count mismatch: reported=%d, parsed=%d\n",
151198f44cb0SIgor Mitsyanko 		       band->n_channels, chidx);
151298f44cb0SIgor Mitsyanko 		goto error_ret;
151398f44cb0SIgor Mitsyanko 	}
151498f44cb0SIgor Mitsyanko 
151598f44cb0SIgor Mitsyanko 	return 0;
151698f44cb0SIgor Mitsyanko 
151798f44cb0SIgor Mitsyanko error_ret:
151898f44cb0SIgor Mitsyanko 	kfree(band->channels);
151998f44cb0SIgor Mitsyanko 	band->channels = NULL;
152098f44cb0SIgor Mitsyanko 	band->n_channels = 0;
152198f44cb0SIgor Mitsyanko 
152298f44cb0SIgor Mitsyanko 	return -EINVAL;
152398f44cb0SIgor Mitsyanko }
152498f44cb0SIgor Mitsyanko 
152598f44cb0SIgor Mitsyanko static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac,
152698f44cb0SIgor Mitsyanko 					 const u8 *payload, size_t payload_len)
152798f44cb0SIgor Mitsyanko {
152898f44cb0SIgor Mitsyanko 	struct qtnf_mac_info *mac_info;
152998f44cb0SIgor Mitsyanko 	struct qlink_tlv_frag_rts_thr *phy_thr;
153098f44cb0SIgor Mitsyanko 	struct qlink_tlv_rlimit *limit;
153198f44cb0SIgor Mitsyanko 	struct qlink_tlv_cclass *class;
153298f44cb0SIgor Mitsyanko 	u16 tlv_type;
153398f44cb0SIgor Mitsyanko 	u16 tlv_value_len;
153498f44cb0SIgor Mitsyanko 	size_t tlv_full_len;
153598f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
153698f44cb0SIgor Mitsyanko 
153798f44cb0SIgor Mitsyanko 	mac_info = &mac->macinfo;
153898f44cb0SIgor Mitsyanko 
153998f44cb0SIgor Mitsyanko 	tlv = (struct qlink_tlv_hdr *)payload;
154098f44cb0SIgor Mitsyanko 	while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
154198f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
154298f44cb0SIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
154398f44cb0SIgor Mitsyanko 		tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
154498f44cb0SIgor Mitsyanko 
154598f44cb0SIgor Mitsyanko 		if (tlv_full_len > payload_len) {
154698f44cb0SIgor Mitsyanko 			pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n",
154798f44cb0SIgor Mitsyanko 				mac->macid, tlv_type, tlv_value_len);
154898f44cb0SIgor Mitsyanko 			return -EINVAL;
154998f44cb0SIgor Mitsyanko 		}
155098f44cb0SIgor Mitsyanko 
155198f44cb0SIgor Mitsyanko 		switch (tlv_type) {
155298f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_FRAG_THRESH:
155398f44cb0SIgor Mitsyanko 			phy_thr = (void *)tlv;
15549fe504a1SSergey Matyukevich 			mac_info->frag_thr = le32_to_cpu(phy_thr->thr);
155598f44cb0SIgor Mitsyanko 			break;
155698f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_RTS_THRESH:
155798f44cb0SIgor Mitsyanko 			phy_thr = (void *)tlv;
15589fe504a1SSergey Matyukevich 			mac_info->rts_thr = le32_to_cpu(phy_thr->thr);
155998f44cb0SIgor Mitsyanko 			break;
156098f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_SRETRY_LIMIT:
156198f44cb0SIgor Mitsyanko 			limit = (void *)tlv;
156298f44cb0SIgor Mitsyanko 			mac_info->sretry_limit = limit->rlimit;
156398f44cb0SIgor Mitsyanko 			break;
156498f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_LRETRY_LIMIT:
156598f44cb0SIgor Mitsyanko 			limit = (void *)tlv;
156698f44cb0SIgor Mitsyanko 			mac_info->lretry_limit = limit->rlimit;
156798f44cb0SIgor Mitsyanko 			break;
156898f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_COVERAGE_CLASS:
156998f44cb0SIgor Mitsyanko 			class = (void *)tlv;
157098f44cb0SIgor Mitsyanko 			mac_info->coverage_class = class->cclass;
157198f44cb0SIgor Mitsyanko 			break;
157298f44cb0SIgor Mitsyanko 		default:
157398f44cb0SIgor Mitsyanko 			pr_err("MAC%u: Unknown TLV type: %#x\n", mac->macid,
157498f44cb0SIgor Mitsyanko 			       le16_to_cpu(tlv->type));
157598f44cb0SIgor Mitsyanko 			break;
157698f44cb0SIgor Mitsyanko 		}
157798f44cb0SIgor Mitsyanko 
157898f44cb0SIgor Mitsyanko 		payload_len -= tlv_full_len;
157998f44cb0SIgor Mitsyanko 		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
158098f44cb0SIgor Mitsyanko 	}
158198f44cb0SIgor Mitsyanko 
158298f44cb0SIgor Mitsyanko 	if (payload_len) {
158398f44cb0SIgor Mitsyanko 		pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n",
158498f44cb0SIgor Mitsyanko 			mac->macid, payload_len);
158598f44cb0SIgor Mitsyanko 		return -EINVAL;
158698f44cb0SIgor Mitsyanko 	}
158798f44cb0SIgor Mitsyanko 
158898f44cb0SIgor Mitsyanko 	return 0;
158998f44cb0SIgor Mitsyanko }
159098f44cb0SIgor Mitsyanko 
15917c04b439SSergey Matyukevich static int
15927c04b439SSergey Matyukevich qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats,
15937c04b439SSergey Matyukevich 				  const u8 *payload, size_t payload_len)
15947c04b439SSergey Matyukevich {
15957c04b439SSergey Matyukevich 	struct qlink_chan_stats *qlink_stats;
15967c04b439SSergey Matyukevich 	const struct qlink_tlv_hdr *tlv;
15977c04b439SSergey Matyukevich 	size_t tlv_full_len;
15987c04b439SSergey Matyukevich 	u16 tlv_value_len;
15997c04b439SSergey Matyukevich 	u16 tlv_type;
16007c04b439SSergey Matyukevich 
16017c04b439SSergey Matyukevich 	tlv = (struct qlink_tlv_hdr *)payload;
16027c04b439SSergey Matyukevich 	while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
16037c04b439SSergey Matyukevich 		tlv_type = le16_to_cpu(tlv->type);
16047c04b439SSergey Matyukevich 		tlv_value_len = le16_to_cpu(tlv->len);
16057c04b439SSergey Matyukevich 		tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
16067c04b439SSergey Matyukevich 		if (tlv_full_len > payload_len) {
16077c04b439SSergey Matyukevich 			pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
16087c04b439SSergey Matyukevich 				tlv_type, tlv_value_len);
16097c04b439SSergey Matyukevich 			return -EINVAL;
16107c04b439SSergey Matyukevich 		}
16117c04b439SSergey Matyukevich 		switch (tlv_type) {
16127c04b439SSergey Matyukevich 		case QTN_TLV_ID_CHANNEL_STATS:
16137c04b439SSergey Matyukevich 			if (unlikely(tlv_value_len != sizeof(*qlink_stats))) {
16147c04b439SSergey Matyukevich 				pr_err("invalid CHANNEL_STATS entry size\n");
16157c04b439SSergey Matyukevich 				return -EINVAL;
16167c04b439SSergey Matyukevich 			}
16177c04b439SSergey Matyukevich 
16187c04b439SSergey Matyukevich 			qlink_stats = (void *)tlv->val;
16197c04b439SSergey Matyukevich 
16207c04b439SSergey Matyukevich 			stats->chan_num = le32_to_cpu(qlink_stats->chan_num);
16217c04b439SSergey Matyukevich 			stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx);
16227c04b439SSergey Matyukevich 			stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx);
16237c04b439SSergey Matyukevich 			stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy);
16247c04b439SSergey Matyukevich 			stats->cca_try = le32_to_cpu(qlink_stats->cca_try);
16257c04b439SSergey Matyukevich 			stats->chan_noise = qlink_stats->chan_noise;
16267c04b439SSergey Matyukevich 
16277c04b439SSergey Matyukevich 			pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n",
16287c04b439SSergey Matyukevich 				 stats->chan_num, stats->cca_try,
16297c04b439SSergey Matyukevich 				 stats->cca_busy, stats->chan_noise);
16307c04b439SSergey Matyukevich 			break;
16317c04b439SSergey Matyukevich 		default:
16327c04b439SSergey Matyukevich 			pr_warn("Unknown TLV type: %#x\n",
16337c04b439SSergey Matyukevich 				le16_to_cpu(tlv->type));
16347c04b439SSergey Matyukevich 		}
16357c04b439SSergey Matyukevich 		payload_len -= tlv_full_len;
16367c04b439SSergey Matyukevich 		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
16377c04b439SSergey Matyukevich 	}
16387c04b439SSergey Matyukevich 
16397c04b439SSergey Matyukevich 	if (payload_len) {
16407c04b439SSergey Matyukevich 		pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len);
16417c04b439SSergey Matyukevich 		return -EINVAL;
16427c04b439SSergey Matyukevich 	}
16437c04b439SSergey Matyukevich 
16447c04b439SSergey Matyukevich 	return 0;
16457c04b439SSergey Matyukevich }
16467c04b439SSergey Matyukevich 
164798f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
164898f44cb0SIgor Mitsyanko {
164998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
165098f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_mac_info *resp;
16511066bd19SSergey Matyukevich 	size_t var_data_len = 0;
165298f44cb0SIgor Mitsyanko 	int ret = 0;
165398f44cb0SIgor Mitsyanko 
165498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
165598f44cb0SIgor Mitsyanko 					    QLINK_CMD_MAC_INFO,
165698f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
1657c93fe71cSSergey Matyukevich 	if (!cmd_skb)
165898f44cb0SIgor Mitsyanko 		return -ENOMEM;
165998f44cb0SIgor Mitsyanko 
166098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
1661c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
166298f44cb0SIgor Mitsyanko 				       sizeof(*resp), &var_data_len);
1663c6ed298fSSergey Matyukevich 	if (ret)
166498f44cb0SIgor Mitsyanko 		goto out;
166598f44cb0SIgor Mitsyanko 
166698f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_mac_info *)resp_skb->data;
166798f44cb0SIgor Mitsyanko 	qtnf_cmd_resp_proc_mac_info(mac, resp);
166898f44cb0SIgor Mitsyanko 	ret = qtnf_parse_variable_mac_info(mac, resp->var_info, var_data_len);
166998f44cb0SIgor Mitsyanko 
167098f44cb0SIgor Mitsyanko out:
167198f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
167298f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
167398f44cb0SIgor Mitsyanko 
167498f44cb0SIgor Mitsyanko 	return ret;
167598f44cb0SIgor Mitsyanko }
167698f44cb0SIgor Mitsyanko 
167798f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus)
167898f44cb0SIgor Mitsyanko {
167998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
168098f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_hw_info *resp;
16811066bd19SSergey Matyukevich 	size_t info_len = 0;
168298f44cb0SIgor Mitsyanko 	int ret = 0;
168398f44cb0SIgor Mitsyanko 
168498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
168598f44cb0SIgor Mitsyanko 					    QLINK_CMD_GET_HW_INFO,
168698f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
1687c93fe71cSSergey Matyukevich 	if (!cmd_skb)
168898f44cb0SIgor Mitsyanko 		return -ENOMEM;
168998f44cb0SIgor Mitsyanko 
169098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1691c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
16924dd07d2bSSergey Matyukevich 				       sizeof(*resp), &info_len);
1693c6ed298fSSergey Matyukevich 	if (ret)
169498f44cb0SIgor Mitsyanko 		goto out;
169598f44cb0SIgor Mitsyanko 
169698f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_hw_info *)resp_skb->data;
16974dd07d2bSSergey Matyukevich 	ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len);
169898f44cb0SIgor Mitsyanko 
169998f44cb0SIgor Mitsyanko out:
170098f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(bus);
170198f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
170298f44cb0SIgor Mitsyanko 
170398f44cb0SIgor Mitsyanko 	return ret;
170498f44cb0SIgor Mitsyanko }
170598f44cb0SIgor Mitsyanko 
1706e294cbfdSIgor Mitsyanko int qtnf_cmd_band_info_get(struct qtnf_wmac *mac,
170798f44cb0SIgor Mitsyanko 			   struct ieee80211_supported_band *band)
170898f44cb0SIgor Mitsyanko {
170998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
1710e294cbfdSIgor Mitsyanko 	struct qlink_cmd_band_info_get *cmd;
1711e294cbfdSIgor Mitsyanko 	struct qlink_resp_band_info_get *resp;
17121066bd19SSergey Matyukevich 	size_t info_len = 0;
171398f44cb0SIgor Mitsyanko 	int ret = 0;
171498f44cb0SIgor Mitsyanko 	u8 qband;
171598f44cb0SIgor Mitsyanko 
171698f44cb0SIgor Mitsyanko 	switch (band->band) {
171798f44cb0SIgor Mitsyanko 	case NL80211_BAND_2GHZ:
171898f44cb0SIgor Mitsyanko 		qband = QLINK_BAND_2GHZ;
171998f44cb0SIgor Mitsyanko 		break;
172098f44cb0SIgor Mitsyanko 	case NL80211_BAND_5GHZ:
172198f44cb0SIgor Mitsyanko 		qband = QLINK_BAND_5GHZ;
172298f44cb0SIgor Mitsyanko 		break;
172398f44cb0SIgor Mitsyanko 	case NL80211_BAND_60GHZ:
172498f44cb0SIgor Mitsyanko 		qband = QLINK_BAND_60GHZ;
172598f44cb0SIgor Mitsyanko 		break;
172698f44cb0SIgor Mitsyanko 	default:
172798f44cb0SIgor Mitsyanko 		return -EINVAL;
172898f44cb0SIgor Mitsyanko 	}
172998f44cb0SIgor Mitsyanko 
1730bc0384eeSColin Ian King 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
1731e294cbfdSIgor Mitsyanko 					    QLINK_CMD_BAND_INFO_GET,
1732bc0384eeSColin Ian King 					    sizeof(*cmd));
1733bc0384eeSColin Ian King 	if (!cmd_skb)
1734bc0384eeSColin Ian King 		return -ENOMEM;
1735bc0384eeSColin Ian King 
1736e294cbfdSIgor Mitsyanko 	cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data;
173798f44cb0SIgor Mitsyanko 	cmd->band = qband;
17389ef75095SSergey Matyukevich 
17399ef75095SSergey Matyukevich 	qtnf_bus_lock(mac->bus);
1740c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
174198f44cb0SIgor Mitsyanko 				       sizeof(*resp), &info_len);
1742c6ed298fSSergey Matyukevich 	if (ret)
174398f44cb0SIgor Mitsyanko 		goto out;
174498f44cb0SIgor Mitsyanko 
1745e294cbfdSIgor Mitsyanko 	resp = (struct qlink_resp_band_info_get *)resp_skb->data;
174698f44cb0SIgor Mitsyanko 	if (resp->band != qband) {
174798f44cb0SIgor Mitsyanko 		pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid,
174898f44cb0SIgor Mitsyanko 		       resp->band, qband);
174998f44cb0SIgor Mitsyanko 		ret = -EINVAL;
175098f44cb0SIgor Mitsyanko 		goto out;
175198f44cb0SIgor Mitsyanko 	}
175298f44cb0SIgor Mitsyanko 
1753e294cbfdSIgor Mitsyanko 	ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len);
175498f44cb0SIgor Mitsyanko 
175598f44cb0SIgor Mitsyanko out:
17569ef75095SSergey Matyukevich 	qtnf_bus_unlock(mac->bus);
175798f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
175898f44cb0SIgor Mitsyanko 
175998f44cb0SIgor Mitsyanko 	return ret;
176098f44cb0SIgor Mitsyanko }
176198f44cb0SIgor Mitsyanko 
176298f44cb0SIgor Mitsyanko int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac)
176398f44cb0SIgor Mitsyanko {
176498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
176598f44cb0SIgor Mitsyanko 	struct qlink_resp_phy_params *resp;
17661066bd19SSergey Matyukevich 	size_t response_size = 0;
176798f44cb0SIgor Mitsyanko 	int ret = 0;
176898f44cb0SIgor Mitsyanko 
176998f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
177098f44cb0SIgor Mitsyanko 					    QLINK_CMD_PHY_PARAMS_GET,
177198f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
177298f44cb0SIgor Mitsyanko 	if (!cmd_skb)
177398f44cb0SIgor Mitsyanko 		return -ENOMEM;
177498f44cb0SIgor Mitsyanko 
177598f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
1776c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
177798f44cb0SIgor Mitsyanko 				       sizeof(*resp), &response_size);
1778c6ed298fSSergey Matyukevich 	if (ret)
177998f44cb0SIgor Mitsyanko 		goto out;
178098f44cb0SIgor Mitsyanko 
178198f44cb0SIgor Mitsyanko 	resp = (struct qlink_resp_phy_params *)resp_skb->data;
178298f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_resp_proc_phy_params(mac, resp->info, response_size);
178398f44cb0SIgor Mitsyanko 
178498f44cb0SIgor Mitsyanko out:
178598f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
178698f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
178798f44cb0SIgor Mitsyanko 
178898f44cb0SIgor Mitsyanko 	return ret;
178998f44cb0SIgor Mitsyanko }
179098f44cb0SIgor Mitsyanko 
179198f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed)
179298f44cb0SIgor Mitsyanko {
179398f44cb0SIgor Mitsyanko 	struct wiphy *wiphy = priv_to_wiphy(mac);
179498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
179598f44cb0SIgor Mitsyanko 	int ret = 0;
179698f44cb0SIgor Mitsyanko 
179798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
179898f44cb0SIgor Mitsyanko 					    QLINK_CMD_PHY_PARAMS_SET,
179998f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
180098f44cb0SIgor Mitsyanko 	if (!cmd_skb)
180198f44cb0SIgor Mitsyanko 		return -ENOMEM;
180298f44cb0SIgor Mitsyanko 
180398f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
180498f44cb0SIgor Mitsyanko 
180598f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
18069fe504a1SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_FRAG_THRESH,
180798f44cb0SIgor Mitsyanko 					 wiphy->frag_threshold);
180898f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_RTS_THRESHOLD)
18099fe504a1SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH,
181098f44cb0SIgor Mitsyanko 					 wiphy->rts_threshold);
181198f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_COVERAGE_CLASS)
181298f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS,
181398f44cb0SIgor Mitsyanko 					wiphy->coverage_class);
181498f44cb0SIgor Mitsyanko 
1815f3c8bd46SSergey Matyukevich 	if (changed & WIPHY_PARAM_RETRY_LONG)
1816f3c8bd46SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT,
1817f3c8bd46SSergey Matyukevich 					wiphy->retry_long);
1818f3c8bd46SSergey Matyukevich 
1819f3c8bd46SSergey Matyukevich 	if (changed & WIPHY_PARAM_RETRY_SHORT)
1820f3c8bd46SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT,
1821f3c8bd46SSergey Matyukevich 					wiphy->retry_short);
1822f3c8bd46SSergey Matyukevich 
1823c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
1824c6ed298fSSergey Matyukevich 	if (ret)
182598f44cb0SIgor Mitsyanko 		goto out;
182698f44cb0SIgor Mitsyanko 
182798f44cb0SIgor Mitsyanko out:
182898f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
1829c6ed298fSSergey Matyukevich 
183098f44cb0SIgor Mitsyanko 	return ret;
183198f44cb0SIgor Mitsyanko }
183298f44cb0SIgor Mitsyanko 
183398f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus)
183498f44cb0SIgor Mitsyanko {
183598f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
183698f44cb0SIgor Mitsyanko 	int ret = 0;
183798f44cb0SIgor Mitsyanko 
183898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
183998f44cb0SIgor Mitsyanko 					    QLINK_CMD_FW_INIT,
184098f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
1841c93fe71cSSergey Matyukevich 	if (!cmd_skb)
184298f44cb0SIgor Mitsyanko 		return -ENOMEM;
184398f44cb0SIgor Mitsyanko 
184498f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1845c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
1846c6ed298fSSergey Matyukevich 	if (ret)
184798f44cb0SIgor Mitsyanko 		goto out;
184898f44cb0SIgor Mitsyanko 
184998f44cb0SIgor Mitsyanko out:
185098f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(bus);
1851c6ed298fSSergey Matyukevich 
185298f44cb0SIgor Mitsyanko 	return ret;
185398f44cb0SIgor Mitsyanko }
185498f44cb0SIgor Mitsyanko 
185598f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus)
185698f44cb0SIgor Mitsyanko {
185798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
185898f44cb0SIgor Mitsyanko 
185998f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
186098f44cb0SIgor Mitsyanko 					    QLINK_CMD_FW_DEINIT,
186198f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
186298f44cb0SIgor Mitsyanko 	if (!cmd_skb)
186398f44cb0SIgor Mitsyanko 		return;
186498f44cb0SIgor Mitsyanko 
186598f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1866c6ed298fSSergey Matyukevich 	qtnf_cmd_send(bus, cmd_skb);
186798f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(bus);
186898f44cb0SIgor Mitsyanko }
186998f44cb0SIgor Mitsyanko 
187098f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
187198f44cb0SIgor Mitsyanko 			  const u8 *mac_addr, struct key_params *params)
187298f44cb0SIgor Mitsyanko {
187398f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
187498f44cb0SIgor Mitsyanko 	struct qlink_cmd_add_key *cmd;
187598f44cb0SIgor Mitsyanko 	int ret = 0;
187698f44cb0SIgor Mitsyanko 
187798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
187898f44cb0SIgor Mitsyanko 					    QLINK_CMD_ADD_KEY,
187998f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1880c93fe71cSSergey Matyukevich 	if (!cmd_skb)
188198f44cb0SIgor Mitsyanko 		return -ENOMEM;
188298f44cb0SIgor Mitsyanko 
188398f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
188498f44cb0SIgor Mitsyanko 
188598f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_add_key *)cmd_skb->data;
188698f44cb0SIgor Mitsyanko 
188798f44cb0SIgor Mitsyanko 	if (mac_addr)
188898f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->addr, mac_addr);
188998f44cb0SIgor Mitsyanko 	else
189098f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->addr);
189198f44cb0SIgor Mitsyanko 
189298f44cb0SIgor Mitsyanko 	cmd->cipher = cpu_to_le32(params->cipher);
189398f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
189498f44cb0SIgor Mitsyanko 	cmd->pairwise = pairwise;
189598f44cb0SIgor Mitsyanko 
189698f44cb0SIgor Mitsyanko 	if (params->key && params->key_len > 0)
189798f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY,
189898f44cb0SIgor Mitsyanko 					 params->key,
189998f44cb0SIgor Mitsyanko 					 params->key_len);
190098f44cb0SIgor Mitsyanko 
190198f44cb0SIgor Mitsyanko 	if (params->seq && params->seq_len > 0)
190298f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ,
190398f44cb0SIgor Mitsyanko 					 params->seq,
190498f44cb0SIgor Mitsyanko 					 params->seq_len);
190598f44cb0SIgor Mitsyanko 
1906c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1907c6ed298fSSergey Matyukevich 	if (ret)
190898f44cb0SIgor Mitsyanko 		goto out;
190998f44cb0SIgor Mitsyanko 
191098f44cb0SIgor Mitsyanko out:
191198f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1912c6ed298fSSergey Matyukevich 
191398f44cb0SIgor Mitsyanko 	return ret;
191498f44cb0SIgor Mitsyanko }
191598f44cb0SIgor Mitsyanko 
191698f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
191798f44cb0SIgor Mitsyanko 			  const u8 *mac_addr)
191898f44cb0SIgor Mitsyanko {
191998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
192098f44cb0SIgor Mitsyanko 	struct qlink_cmd_del_key *cmd;
192198f44cb0SIgor Mitsyanko 	int ret = 0;
192298f44cb0SIgor Mitsyanko 
192398f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
192498f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_KEY,
192598f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1926c93fe71cSSergey Matyukevich 	if (!cmd_skb)
192798f44cb0SIgor Mitsyanko 		return -ENOMEM;
192898f44cb0SIgor Mitsyanko 
192998f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
193098f44cb0SIgor Mitsyanko 
193198f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_del_key *)cmd_skb->data;
193298f44cb0SIgor Mitsyanko 
193398f44cb0SIgor Mitsyanko 	if (mac_addr)
193498f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->addr, mac_addr);
193598f44cb0SIgor Mitsyanko 	else
193698f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->addr);
193798f44cb0SIgor Mitsyanko 
193898f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
193998f44cb0SIgor Mitsyanko 	cmd->pairwise = pairwise;
194098f44cb0SIgor Mitsyanko 
1941c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1942c6ed298fSSergey Matyukevich 	if (ret)
194398f44cb0SIgor Mitsyanko 		goto out;
194498f44cb0SIgor Mitsyanko 
194598f44cb0SIgor Mitsyanko out:
194698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1947c6ed298fSSergey Matyukevich 
194898f44cb0SIgor Mitsyanko 	return ret;
194998f44cb0SIgor Mitsyanko }
195098f44cb0SIgor Mitsyanko 
195198f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index,
195298f44cb0SIgor Mitsyanko 				  bool unicast, bool multicast)
195398f44cb0SIgor Mitsyanko {
195498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
195598f44cb0SIgor Mitsyanko 	struct qlink_cmd_set_def_key *cmd;
195698f44cb0SIgor Mitsyanko 	int ret = 0;
195798f44cb0SIgor Mitsyanko 
195898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
195998f44cb0SIgor Mitsyanko 					    QLINK_CMD_SET_DEFAULT_KEY,
196098f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1961c93fe71cSSergey Matyukevich 	if (!cmd_skb)
196298f44cb0SIgor Mitsyanko 		return -ENOMEM;
196398f44cb0SIgor Mitsyanko 
196498f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
196598f44cb0SIgor Mitsyanko 
196698f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data;
196798f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
196898f44cb0SIgor Mitsyanko 	cmd->unicast = unicast;
196998f44cb0SIgor Mitsyanko 	cmd->multicast = multicast;
197098f44cb0SIgor Mitsyanko 
1971c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1972c6ed298fSSergey Matyukevich 	if (ret)
197398f44cb0SIgor Mitsyanko 		goto out;
197498f44cb0SIgor Mitsyanko 
197598f44cb0SIgor Mitsyanko out:
197698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1977c6ed298fSSergey Matyukevich 
197898f44cb0SIgor Mitsyanko 	return ret;
197998f44cb0SIgor Mitsyanko }
198098f44cb0SIgor Mitsyanko 
198198f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index)
198298f44cb0SIgor Mitsyanko {
198398f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
198498f44cb0SIgor Mitsyanko 	struct qlink_cmd_set_def_mgmt_key *cmd;
198598f44cb0SIgor Mitsyanko 	int ret = 0;
198698f44cb0SIgor Mitsyanko 
198798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
198898f44cb0SIgor Mitsyanko 					    QLINK_CMD_SET_DEFAULT_MGMT_KEY,
198998f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1990c93fe71cSSergey Matyukevich 	if (!cmd_skb)
199198f44cb0SIgor Mitsyanko 		return -ENOMEM;
199298f44cb0SIgor Mitsyanko 
199398f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
199498f44cb0SIgor Mitsyanko 
199598f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data;
199698f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
199798f44cb0SIgor Mitsyanko 
1998c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1999c6ed298fSSergey Matyukevich 	if (ret)
200098f44cb0SIgor Mitsyanko 		goto out;
200198f44cb0SIgor Mitsyanko 
200298f44cb0SIgor Mitsyanko out:
200398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2004c6ed298fSSergey Matyukevich 
200598f44cb0SIgor Mitsyanko 	return ret;
200698f44cb0SIgor Mitsyanko }
200798f44cb0SIgor Mitsyanko 
200898f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags)
200998f44cb0SIgor Mitsyanko {
201098f44cb0SIgor Mitsyanko 	u32 code = 0;
201198f44cb0SIgor Mitsyanko 
201298f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED))
201398f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_AUTHORIZED;
201498f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
201598f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_SHORT_PREAMBLE;
201698f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_WME))
201798f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_WME;
201898f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_MFP))
201998f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_MFP;
202098f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED))
202198f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_AUTHENTICATED;
202298f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER))
202398f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_TDLS_PEER;
202498f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED))
202598f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_ASSOCIATED;
202698f44cb0SIgor Mitsyanko 	return code;
202798f44cb0SIgor Mitsyanko }
202898f44cb0SIgor Mitsyanko 
202998f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac,
203098f44cb0SIgor Mitsyanko 			     struct station_parameters *params)
203198f44cb0SIgor Mitsyanko {
203298f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
203398f44cb0SIgor Mitsyanko 	struct qlink_cmd_change_sta *cmd;
203498f44cb0SIgor Mitsyanko 	int ret = 0;
203598f44cb0SIgor Mitsyanko 
203698f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
203798f44cb0SIgor Mitsyanko 					    QLINK_CMD_CHANGE_STA,
203898f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2039c93fe71cSSergey Matyukevich 	if (!cmd_skb)
204098f44cb0SIgor Mitsyanko 		return -ENOMEM;
204198f44cb0SIgor Mitsyanko 
204298f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
204398f44cb0SIgor Mitsyanko 
204498f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_change_sta *)cmd_skb->data;
204598f44cb0SIgor Mitsyanko 	ether_addr_copy(cmd->sta_addr, mac);
20464d2a7a1cSIgor Mitsyanko 	cmd->flag_update.mask =
20474d2a7a1cSIgor Mitsyanko 		cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_mask));
20484d2a7a1cSIgor Mitsyanko 	cmd->flag_update.value =
20494d2a7a1cSIgor Mitsyanko 		cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_set));
2050805b28c0SSergey Matyukevich 
2051805b28c0SSergey Matyukevich 	switch (vif->wdev.iftype) {
2052805b28c0SSergey Matyukevich 	case NL80211_IFTYPE_AP:
2053805b28c0SSergey Matyukevich 		cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP);
2054805b28c0SSergey Matyukevich 		break;
2055805b28c0SSergey Matyukevich 	case NL80211_IFTYPE_STATION:
2056805b28c0SSergey Matyukevich 		cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
2057805b28c0SSergey Matyukevich 		break;
2058805b28c0SSergey Matyukevich 	default:
2059805b28c0SSergey Matyukevich 		pr_err("unsupported iftype %d\n", vif->wdev.iftype);
2060805b28c0SSergey Matyukevich 		ret = -EINVAL;
2061805b28c0SSergey Matyukevich 		goto out;
2062805b28c0SSergey Matyukevich 	}
206398f44cb0SIgor Mitsyanko 
2064c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2065c6ed298fSSergey Matyukevich 	if (ret)
206698f44cb0SIgor Mitsyanko 		goto out;
206798f44cb0SIgor Mitsyanko 
206898f44cb0SIgor Mitsyanko out:
206998f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2070c6ed298fSSergey Matyukevich 
207198f44cb0SIgor Mitsyanko 	return ret;
207298f44cb0SIgor Mitsyanko }
207398f44cb0SIgor Mitsyanko 
207498f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif,
207598f44cb0SIgor Mitsyanko 			  struct station_del_parameters *params)
207698f44cb0SIgor Mitsyanko {
207798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
207898f44cb0SIgor Mitsyanko 	struct qlink_cmd_del_sta *cmd;
207998f44cb0SIgor Mitsyanko 	int ret = 0;
208098f44cb0SIgor Mitsyanko 
208198f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
208298f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_STA,
208398f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2084c93fe71cSSergey Matyukevich 	if (!cmd_skb)
208598f44cb0SIgor Mitsyanko 		return -ENOMEM;
208698f44cb0SIgor Mitsyanko 
208798f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
208898f44cb0SIgor Mitsyanko 
208998f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_del_sta *)cmd_skb->data;
209098f44cb0SIgor Mitsyanko 
209198f44cb0SIgor Mitsyanko 	if (params->mac)
209298f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->sta_addr, params->mac);
209398f44cb0SIgor Mitsyanko 	else
209498f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->sta_addr);	/* flush all stations */
209598f44cb0SIgor Mitsyanko 
209698f44cb0SIgor Mitsyanko 	cmd->subtype = params->subtype;
209798f44cb0SIgor Mitsyanko 	cmd->reason_code = cpu_to_le16(params->reason_code);
209898f44cb0SIgor Mitsyanko 
2099c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2100c6ed298fSSergey Matyukevich 	if (ret)
210198f44cb0SIgor Mitsyanko 		goto out;
210298f44cb0SIgor Mitsyanko 
210398f44cb0SIgor Mitsyanko out:
210498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2105c6ed298fSSergey Matyukevich 
210698f44cb0SIgor Mitsyanko 	return ret;
210798f44cb0SIgor Mitsyanko }
210898f44cb0SIgor Mitsyanko 
2109c9889671SIgor Mitsyanko static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb,
2110c9889671SIgor Mitsyanko 				     const struct ieee80211_channel *sc)
2111c9889671SIgor Mitsyanko {
2112c9889671SIgor Mitsyanko 	struct qlink_tlv_channel *qchan;
2113c9889671SIgor Mitsyanko 	u32 flags = 0;
2114c9889671SIgor Mitsyanko 
2115c9889671SIgor Mitsyanko 	qchan = skb_put_zero(cmd_skb, sizeof(*qchan));
2116c9889671SIgor Mitsyanko 	qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL);
2117c9889671SIgor Mitsyanko 	qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - sizeof(qchan->hdr));
21185bf374abSSergey Matyukevich 	qchan->chan.center_freq = cpu_to_le16(sc->center_freq);
21195bf374abSSergey Matyukevich 	qchan->chan.hw_value = cpu_to_le16(sc->hw_value);
2120c9889671SIgor Mitsyanko 
2121c9889671SIgor Mitsyanko 	if (sc->flags & IEEE80211_CHAN_NO_IR)
2122c9889671SIgor Mitsyanko 		flags |= QLINK_CHAN_NO_IR;
2123c9889671SIgor Mitsyanko 
2124c9889671SIgor Mitsyanko 	if (sc->flags & IEEE80211_CHAN_RADAR)
2125c9889671SIgor Mitsyanko 		flags |= QLINK_CHAN_RADAR;
2126c9889671SIgor Mitsyanko 
21275bf374abSSergey Matyukevich 	qchan->chan.flags = cpu_to_le32(flags);
2128c9889671SIgor Mitsyanko }
2129c9889671SIgor Mitsyanko 
21306fbef954SAndrey Shevchenko static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb,
21316fbef954SAndrey Shevchenko 				     const u8 *mac_addr,
21326fbef954SAndrey Shevchenko 				     const u8 *mac_addr_mask)
21336fbef954SAndrey Shevchenko {
21346fbef954SAndrey Shevchenko 	struct qlink_random_mac_addr *randmac;
21356fbef954SAndrey Shevchenko 	struct qlink_tlv_hdr *hdr =
21366fbef954SAndrey Shevchenko 		skb_put(cmd_skb, sizeof(*hdr) + sizeof(*randmac));
21376fbef954SAndrey Shevchenko 
21386fbef954SAndrey Shevchenko 	hdr->type = cpu_to_le16(QTN_TLV_ID_RANDOM_MAC_ADDR);
21396fbef954SAndrey Shevchenko 	hdr->len = cpu_to_le16(sizeof(*randmac));
21406fbef954SAndrey Shevchenko 	randmac = (struct qlink_random_mac_addr *)hdr->val;
21416fbef954SAndrey Shevchenko 
21426fbef954SAndrey Shevchenko 	memcpy(randmac->mac_addr, mac_addr, ETH_ALEN);
21436fbef954SAndrey Shevchenko 	memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN);
21446fbef954SAndrey Shevchenko }
21456fbef954SAndrey Shevchenko 
214698f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac)
214798f44cb0SIgor Mitsyanko {
214898f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
214998f44cb0SIgor Mitsyanko 	struct ieee80211_channel *sc;
215098f44cb0SIgor Mitsyanko 	struct cfg80211_scan_request *scan_req = mac->scan_req;
215198f44cb0SIgor Mitsyanko 	int n_channels;
215298f44cb0SIgor Mitsyanko 	int count = 0;
215398f44cb0SIgor Mitsyanko 	int ret;
215498f44cb0SIgor Mitsyanko 
215598f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
215698f44cb0SIgor Mitsyanko 					    QLINK_CMD_SCAN,
215798f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
2158c93fe71cSSergey Matyukevich 	if (!cmd_skb)
215998f44cb0SIgor Mitsyanko 		return -ENOMEM;
216098f44cb0SIgor Mitsyanko 
216198f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
216298f44cb0SIgor Mitsyanko 
216398f44cb0SIgor Mitsyanko 	if (scan_req->n_ssids != 0) {
216498f44cb0SIgor Mitsyanko 		while (count < scan_req->n_ssids) {
216598f44cb0SIgor Mitsyanko 			qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID,
216698f44cb0SIgor Mitsyanko 				scan_req->ssids[count].ssid,
216798f44cb0SIgor Mitsyanko 				scan_req->ssids[count].ssid_len);
216898f44cb0SIgor Mitsyanko 			count++;
216998f44cb0SIgor Mitsyanko 		}
217098f44cb0SIgor Mitsyanko 	}
217198f44cb0SIgor Mitsyanko 
217298f44cb0SIgor Mitsyanko 	if (scan_req->ie_len != 0)
217318b7470fSIgor Mitsyanko 		qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ,
217418b7470fSIgor Mitsyanko 					scan_req->ie, scan_req->ie_len);
217598f44cb0SIgor Mitsyanko 
217698f44cb0SIgor Mitsyanko 	if (scan_req->n_channels) {
217798f44cb0SIgor Mitsyanko 		n_channels = scan_req->n_channels;
217898f44cb0SIgor Mitsyanko 		count = 0;
217998f44cb0SIgor Mitsyanko 
218098f44cb0SIgor Mitsyanko 		while (n_channels != 0) {
218198f44cb0SIgor Mitsyanko 			sc = scan_req->channels[count];
218298f44cb0SIgor Mitsyanko 			if (sc->flags & IEEE80211_CHAN_DISABLED) {
218398f44cb0SIgor Mitsyanko 				n_channels--;
218498f44cb0SIgor Mitsyanko 				continue;
218598f44cb0SIgor Mitsyanko 			}
218698f44cb0SIgor Mitsyanko 
218798f44cb0SIgor Mitsyanko 			pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n",
218898f44cb0SIgor Mitsyanko 				 mac->macid, sc->hw_value, sc->center_freq,
218998f44cb0SIgor Mitsyanko 				 sc->flags);
219098f44cb0SIgor Mitsyanko 
2191c9889671SIgor Mitsyanko 			qtnf_cmd_channel_tlv_add(cmd_skb, sc);
219298f44cb0SIgor Mitsyanko 			n_channels--;
219398f44cb0SIgor Mitsyanko 			count++;
219498f44cb0SIgor Mitsyanko 		}
219598f44cb0SIgor Mitsyanko 	}
219698f44cb0SIgor Mitsyanko 
21976fbef954SAndrey Shevchenko 	if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
21986fbef954SAndrey Shevchenko 		pr_debug("MAC%u: scan with random addr=%pM, mask=%pM\n",
21996fbef954SAndrey Shevchenko 			 mac->macid,
22006fbef954SAndrey Shevchenko 			 scan_req->mac_addr, scan_req->mac_addr_mask);
22016fbef954SAndrey Shevchenko 
22026fbef954SAndrey Shevchenko 		qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr,
22036fbef954SAndrey Shevchenko 					 scan_req->mac_addr_mask);
22046fbef954SAndrey Shevchenko 	}
22056fbef954SAndrey Shevchenko 
22066d85930fSSergey Matyukevich 	if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH) {
22076d85930fSSergey Matyukevich 		pr_debug("MAC%u: flush cache before scan\n", mac->macid);
22086d85930fSSergey Matyukevich 
22096d85930fSSergey Matyukevich 		qtnf_cmd_skb_put_tlv_tag(cmd_skb, QTN_TLV_ID_SCAN_FLUSH);
22106d85930fSSergey Matyukevich 	}
22116d85930fSSergey Matyukevich 
22122525f188SSergey Matyukevich 	if (scan_req->duration) {
22132525f188SSergey Matyukevich 		pr_debug("MAC%u: %s scan duration %u\n", mac->macid,
22142525f188SSergey Matyukevich 			 scan_req->duration_mandatory ? "mandatory" : "max",
22152525f188SSergey Matyukevich 			 scan_req->duration);
22162525f188SSergey Matyukevich 
22172525f188SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_SCAN_DWELL,
22182525f188SSergey Matyukevich 					 scan_req->duration);
22192525f188SSergey Matyukevich 	}
22202525f188SSergey Matyukevich 
2221c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
2222c6ed298fSSergey Matyukevich 	if (ret)
222398f44cb0SIgor Mitsyanko 		goto out;
222498f44cb0SIgor Mitsyanko 
222598f44cb0SIgor Mitsyanko out:
222698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
2227c6ed298fSSergey Matyukevich 
222898f44cb0SIgor Mitsyanko 	return ret;
222998f44cb0SIgor Mitsyanko }
223098f44cb0SIgor Mitsyanko 
223198f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif,
223298f44cb0SIgor Mitsyanko 			  struct cfg80211_connect_params *sme)
223398f44cb0SIgor Mitsyanko {
223498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
223598f44cb0SIgor Mitsyanko 	struct qlink_cmd_connect *cmd;
2236d23d1361SIgor Mitsyanko 	struct qlink_auth_encr *aen;
223798f44cb0SIgor Mitsyanko 	int ret;
223898f44cb0SIgor Mitsyanko 	int i;
22399766d1ddSIgor Mitsyanko 	u32 connect_flags = 0;
224098f44cb0SIgor Mitsyanko 
224198f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
224298f44cb0SIgor Mitsyanko 					    QLINK_CMD_CONNECT,
224398f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2244c93fe71cSSergey Matyukevich 	if (!cmd_skb)
224598f44cb0SIgor Mitsyanko 		return -ENOMEM;
224698f44cb0SIgor Mitsyanko 
224798f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_connect *)cmd_skb->data;
224898f44cb0SIgor Mitsyanko 
22499766d1ddSIgor Mitsyanko 	ether_addr_copy(cmd->bssid, vif->bssid);
225098f44cb0SIgor Mitsyanko 
2251c9889671SIgor Mitsyanko 	if (sme->bssid_hint)
2252c9889671SIgor Mitsyanko 		ether_addr_copy(cmd->bssid_hint, sme->bssid_hint);
225396d4eaf2SIgor Mitsyanko 	else
2254c9889671SIgor Mitsyanko 		eth_zero_addr(cmd->bssid_hint);
2255c9889671SIgor Mitsyanko 
2256c9889671SIgor Mitsyanko 	if (sme->prev_bssid)
2257c9889671SIgor Mitsyanko 		ether_addr_copy(cmd->prev_bssid, sme->prev_bssid);
2258c9889671SIgor Mitsyanko 	else
2259c9889671SIgor Mitsyanko 		eth_zero_addr(cmd->prev_bssid);
226098f44cb0SIgor Mitsyanko 
2261f5d2ff43SAndrey Shevchenko 	if ((sme->bg_scan_period >= 0) &&
2262f5d2ff43SAndrey Shevchenko 	    (sme->bg_scan_period <= SHRT_MAX))
22639766d1ddSIgor Mitsyanko 		cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period);
22649766d1ddSIgor Mitsyanko 	else
2265f5d2ff43SAndrey Shevchenko 		cmd->bg_scan_period = cpu_to_le16(-1); /* use default value */
22669766d1ddSIgor Mitsyanko 
22679766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_DISABLE_HT)
22689766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_DISABLE_HT;
22699766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_DISABLE_VHT)
22709766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT;
22719766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_USE_RRM)
22729766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_USE_RRM;
22739766d1ddSIgor Mitsyanko 
22749766d1ddSIgor Mitsyanko 	cmd->flags = cpu_to_le32(connect_flags);
2275c9889671SIgor Mitsyanko 	memcpy(&cmd->ht_capa, &sme->ht_capa, sizeof(cmd->ht_capa));
2276c9889671SIgor Mitsyanko 	memcpy(&cmd->ht_capa_mask, &sme->ht_capa_mask,
2277c9889671SIgor Mitsyanko 	       sizeof(cmd->ht_capa_mask));
2278c9889671SIgor Mitsyanko 	memcpy(&cmd->vht_capa, &sme->vht_capa, sizeof(cmd->vht_capa));
2279c9889671SIgor Mitsyanko 	memcpy(&cmd->vht_capa_mask, &sme->vht_capa_mask,
2280c9889671SIgor Mitsyanko 	       sizeof(cmd->vht_capa_mask));
2281c9889671SIgor Mitsyanko 	cmd->pbss = sme->pbss;
228298f44cb0SIgor Mitsyanko 
2283d23d1361SIgor Mitsyanko 	aen = &cmd->aen;
2284d23d1361SIgor Mitsyanko 	aen->auth_type = sme->auth_type;
2285d23d1361SIgor Mitsyanko 	aen->privacy = !!sme->privacy;
2286c9889671SIgor Mitsyanko 	cmd->mfp = sme->mfp;
2287d23d1361SIgor Mitsyanko 	aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions);
2288d23d1361SIgor Mitsyanko 	aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group);
2289d23d1361SIgor Mitsyanko 	aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise);
229098f44cb0SIgor Mitsyanko 
229198f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++)
2292d23d1361SIgor Mitsyanko 		aen->ciphers_pairwise[i] =
2293d23d1361SIgor Mitsyanko 			cpu_to_le32(sme->crypto.ciphers_pairwise[i]);
229498f44cb0SIgor Mitsyanko 
2295d23d1361SIgor Mitsyanko 	aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites);
229698f44cb0SIgor Mitsyanko 
229798f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++)
2298d23d1361SIgor Mitsyanko 		aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]);
229998f44cb0SIgor Mitsyanko 
2300d23d1361SIgor Mitsyanko 	aen->control_port = sme->crypto.control_port;
2301d23d1361SIgor Mitsyanko 	aen->control_port_no_encrypt =
23029766d1ddSIgor Mitsyanko 		sme->crypto.control_port_no_encrypt;
2303d23d1361SIgor Mitsyanko 	aen->control_port_ethertype =
2304d23d1361SIgor Mitsyanko 		cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype));
230598f44cb0SIgor Mitsyanko 
23069766d1ddSIgor Mitsyanko 	qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid,
23079766d1ddSIgor Mitsyanko 				 sme->ssid_len);
230898f44cb0SIgor Mitsyanko 
230998f44cb0SIgor Mitsyanko 	if (sme->ie_len != 0)
231018b7470fSIgor Mitsyanko 		qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_REQ,
231118b7470fSIgor Mitsyanko 					sme->ie, sme->ie_len);
231298f44cb0SIgor Mitsyanko 
2313c9889671SIgor Mitsyanko 	if (sme->channel)
2314c9889671SIgor Mitsyanko 		qtnf_cmd_channel_tlv_add(cmd_skb, sme->channel);
2315c9889671SIgor Mitsyanko 
2316d23d1361SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
2317c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2318c6ed298fSSergey Matyukevich 	if (ret)
231998f44cb0SIgor Mitsyanko 		goto out;
232098f44cb0SIgor Mitsyanko 
232198f44cb0SIgor Mitsyanko out:
232298f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2323c6ed298fSSergey Matyukevich 
232498f44cb0SIgor Mitsyanko 	return ret;
232598f44cb0SIgor Mitsyanko }
232698f44cb0SIgor Mitsyanko 
232798f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code)
232898f44cb0SIgor Mitsyanko {
232998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
233098f44cb0SIgor Mitsyanko 	struct qlink_cmd_disconnect *cmd;
233198f44cb0SIgor Mitsyanko 	int ret;
233298f44cb0SIgor Mitsyanko 
233398f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
233498f44cb0SIgor Mitsyanko 					    QLINK_CMD_DISCONNECT,
233598f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2336c93fe71cSSergey Matyukevich 	if (!cmd_skb)
233798f44cb0SIgor Mitsyanko 		return -ENOMEM;
233898f44cb0SIgor Mitsyanko 
233998f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
234098f44cb0SIgor Mitsyanko 
234198f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_disconnect *)cmd_skb->data;
234298f44cb0SIgor Mitsyanko 	cmd->reason = cpu_to_le16(reason_code);
234398f44cb0SIgor Mitsyanko 
2344c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2345c6ed298fSSergey Matyukevich 	if (ret)
234698f44cb0SIgor Mitsyanko 		goto out;
234798f44cb0SIgor Mitsyanko 
234898f44cb0SIgor Mitsyanko out:
234998f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2350c6ed298fSSergey Matyukevich 
235198f44cb0SIgor Mitsyanko 	return ret;
235298f44cb0SIgor Mitsyanko }
235398f44cb0SIgor Mitsyanko 
235498f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up)
235598f44cb0SIgor Mitsyanko {
235698f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
235798f44cb0SIgor Mitsyanko 	struct qlink_cmd_updown *cmd;
235898f44cb0SIgor Mitsyanko 	int ret;
235998f44cb0SIgor Mitsyanko 
236098f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
236198f44cb0SIgor Mitsyanko 					    QLINK_CMD_UPDOWN_INTF,
236298f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2363c93fe71cSSergey Matyukevich 	if (!cmd_skb)
236498f44cb0SIgor Mitsyanko 		return -ENOMEM;
236598f44cb0SIgor Mitsyanko 
236698f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_updown *)cmd_skb->data;
236798f44cb0SIgor Mitsyanko 	cmd->if_up = !!up;
236898f44cb0SIgor Mitsyanko 
236998f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
2370c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2371c6ed298fSSergey Matyukevich 	if (ret)
237298f44cb0SIgor Mitsyanko 		goto out;
237398f44cb0SIgor Mitsyanko 
237498f44cb0SIgor Mitsyanko out:
237598f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2376c6ed298fSSergey Matyukevich 
237798f44cb0SIgor Mitsyanko 	return ret;
237898f44cb0SIgor Mitsyanko }
23794dd07d2bSSergey Matyukevich 
23804dd07d2bSSergey Matyukevich int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req)
23814dd07d2bSSergey Matyukevich {
23824dd07d2bSSergey Matyukevich 	struct sk_buff *cmd_skb;
23834dd07d2bSSergey Matyukevich 	int ret;
23844dd07d2bSSergey Matyukevich 	struct qlink_cmd_reg_notify *cmd;
23854dd07d2bSSergey Matyukevich 
23864dd07d2bSSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
23874dd07d2bSSergey Matyukevich 					    QLINK_CMD_REG_NOTIFY,
23884dd07d2bSSergey Matyukevich 					    sizeof(*cmd));
23894dd07d2bSSergey Matyukevich 	if (!cmd_skb)
23904dd07d2bSSergey Matyukevich 		return -ENOMEM;
23914dd07d2bSSergey Matyukevich 
23924dd07d2bSSergey Matyukevich 	cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data;
23934dd07d2bSSergey Matyukevich 	cmd->alpha2[0] = req->alpha2[0];
23944dd07d2bSSergey Matyukevich 	cmd->alpha2[1] = req->alpha2[1];
23954dd07d2bSSergey Matyukevich 
23964dd07d2bSSergey Matyukevich 	switch (req->initiator) {
23974dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_CORE:
23984dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_CORE;
23994dd07d2bSSergey Matyukevich 		break;
24004dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_USER:
24014dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_USER;
24024dd07d2bSSergey Matyukevich 		break;
24034dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_DRIVER:
24044dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER;
24054dd07d2bSSergey Matyukevich 		break;
24064dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
24074dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE;
24084dd07d2bSSergey Matyukevich 		break;
24094dd07d2bSSergey Matyukevich 	}
24104dd07d2bSSergey Matyukevich 
24114dd07d2bSSergey Matyukevich 	switch (req->user_reg_hint_type) {
24124dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_USER:
24134dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER;
24144dd07d2bSSergey Matyukevich 		break;
24154dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_CELL_BASE:
24164dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE;
24174dd07d2bSSergey Matyukevich 		break;
24184dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_INDOOR:
24194dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR;
24204dd07d2bSSergey Matyukevich 		break;
24214dd07d2bSSergey Matyukevich 	}
24224dd07d2bSSergey Matyukevich 
24234dd07d2bSSergey Matyukevich 	qtnf_bus_lock(bus);
2424c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
24254dd07d2bSSergey Matyukevich 	if (ret)
24264dd07d2bSSergey Matyukevich 		goto out;
24274dd07d2bSSergey Matyukevich 
24284dd07d2bSSergey Matyukevich out:
24294dd07d2bSSergey Matyukevich 	qtnf_bus_unlock(bus);
24304dd07d2bSSergey Matyukevich 
24314dd07d2bSSergey Matyukevich 	return ret;
24324dd07d2bSSergey Matyukevich }
24337c04b439SSergey Matyukevich 
24347c04b439SSergey Matyukevich int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
24357c04b439SSergey Matyukevich 			    struct qtnf_chan_stats *stats)
24367c04b439SSergey Matyukevich {
24377c04b439SSergey Matyukevich 	struct sk_buff *cmd_skb, *resp_skb = NULL;
24387c04b439SSergey Matyukevich 	struct qlink_cmd_get_chan_stats *cmd;
24397c04b439SSergey Matyukevich 	struct qlink_resp_get_chan_stats *resp;
24401066bd19SSergey Matyukevich 	size_t var_data_len = 0;
24417c04b439SSergey Matyukevich 	int ret = 0;
24427c04b439SSergey Matyukevich 
24437c04b439SSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
24447c04b439SSergey Matyukevich 					    QLINK_CMD_CHAN_STATS,
24457c04b439SSergey Matyukevich 					    sizeof(*cmd));
24467c04b439SSergey Matyukevich 	if (!cmd_skb)
24477c04b439SSergey Matyukevich 		return -ENOMEM;
24487c04b439SSergey Matyukevich 
24497c04b439SSergey Matyukevich 	qtnf_bus_lock(mac->bus);
24507c04b439SSergey Matyukevich 
24517c04b439SSergey Matyukevich 	cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data;
24527c04b439SSergey Matyukevich 	cmd->channel = cpu_to_le16(channel);
24537c04b439SSergey Matyukevich 
2454c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
24557c04b439SSergey Matyukevich 				       sizeof(*resp), &var_data_len);
2456c6ed298fSSergey Matyukevich 	if (ret)
24577c04b439SSergey Matyukevich 		goto out;
24587c04b439SSergey Matyukevich 
24597c04b439SSergey Matyukevich 	resp = (struct qlink_resp_get_chan_stats *)resp_skb->data;
24607c04b439SSergey Matyukevich 	ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info,
24617c04b439SSergey Matyukevich 						var_data_len);
24627c04b439SSergey Matyukevich 
24637c04b439SSergey Matyukevich out:
24647c04b439SSergey Matyukevich 	qtnf_bus_unlock(mac->bus);
24657c04b439SSergey Matyukevich 	consume_skb(resp_skb);
2466c6ed298fSSergey Matyukevich 
24677c04b439SSergey Matyukevich 	return ret;
24687c04b439SSergey Matyukevich }
246997883695SSergey Matyukevich 
24708c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif,
247197883695SSergey Matyukevich 			      struct cfg80211_csa_settings *params)
247297883695SSergey Matyukevich {
24738c015b90SIgor Mitsyanko 	struct qtnf_wmac *mac = vif->mac;
247497883695SSergey Matyukevich 	struct qlink_cmd_chan_switch *cmd;
247597883695SSergey Matyukevich 	struct sk_buff *cmd_skb;
247697883695SSergey Matyukevich 	int ret;
247797883695SSergey Matyukevich 
24788c015b90SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid,
247997883695SSergey Matyukevich 					    QLINK_CMD_CHAN_SWITCH,
248097883695SSergey Matyukevich 					    sizeof(*cmd));
2481c93fe71cSSergey Matyukevich 	if (!cmd_skb)
248297883695SSergey Matyukevich 		return -ENOMEM;
248397883695SSergey Matyukevich 
248497883695SSergey Matyukevich 	qtnf_bus_lock(mac->bus);
248597883695SSergey Matyukevich 
248697883695SSergey Matyukevich 	cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data;
248797883695SSergey Matyukevich 	cmd->channel = cpu_to_le16(params->chandef.chan->hw_value);
248897883695SSergey Matyukevich 	cmd->radar_required = params->radar_required;
248997883695SSergey Matyukevich 	cmd->block_tx = params->block_tx;
249097883695SSergey Matyukevich 	cmd->beacon_count = params->count;
249197883695SSergey Matyukevich 
2492c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
2493c6ed298fSSergey Matyukevich 	if (ret)
249497883695SSergey Matyukevich 		goto out;
249597883695SSergey Matyukevich 
249697883695SSergey Matyukevich out:
249797883695SSergey Matyukevich 	qtnf_bus_unlock(mac->bus);
2498c6ed298fSSergey Matyukevich 
249997883695SSergey Matyukevich 	return ret;
250097883695SSergey Matyukevich }
25019e5478b6SIgor Mitsyanko 
25029e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef)
25039e5478b6SIgor Mitsyanko {
25049e5478b6SIgor Mitsyanko 	struct qtnf_bus *bus = vif->mac->bus;
25059e5478b6SIgor Mitsyanko 	const struct qlink_resp_channel_get *resp;
25069e5478b6SIgor Mitsyanko 	struct sk_buff *cmd_skb;
25079e5478b6SIgor Mitsyanko 	struct sk_buff *resp_skb = NULL;
25089e5478b6SIgor Mitsyanko 	int ret;
25099e5478b6SIgor Mitsyanko 
25109e5478b6SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
25119e5478b6SIgor Mitsyanko 					    QLINK_CMD_CHAN_GET,
25129e5478b6SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
2513c93fe71cSSergey Matyukevich 	if (!cmd_skb)
25149e5478b6SIgor Mitsyanko 		return -ENOMEM;
25159e5478b6SIgor Mitsyanko 
25169e5478b6SIgor Mitsyanko 	qtnf_bus_lock(bus);
2517c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
25189e5478b6SIgor Mitsyanko 				       sizeof(*resp), NULL);
2519c6ed298fSSergey Matyukevich 	if (ret)
25209e5478b6SIgor Mitsyanko 		goto out;
25219e5478b6SIgor Mitsyanko 
25229e5478b6SIgor Mitsyanko 	resp = (const struct qlink_resp_channel_get *)resp_skb->data;
25239e5478b6SIgor Mitsyanko 	qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef);
25249e5478b6SIgor Mitsyanko 
25259e5478b6SIgor Mitsyanko out:
2526c6ed298fSSergey Matyukevich 	qtnf_bus_unlock(bus);
25279e5478b6SIgor Mitsyanko 	consume_skb(resp_skb);
2528c6ed298fSSergey Matyukevich 
25299e5478b6SIgor Mitsyanko 	return ret;
25309e5478b6SIgor Mitsyanko }
2531b05ee456SIgor Mitsyanko 
2532b05ee456SIgor Mitsyanko int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
2533b05ee456SIgor Mitsyanko 		       const struct cfg80211_chan_def *chdef,
2534b05ee456SIgor Mitsyanko 		       u32 cac_time_ms)
2535b05ee456SIgor Mitsyanko {
2536b05ee456SIgor Mitsyanko 	struct qtnf_bus *bus = vif->mac->bus;
2537b05ee456SIgor Mitsyanko 	struct sk_buff *cmd_skb;
2538b05ee456SIgor Mitsyanko 	struct qlink_cmd_start_cac *cmd;
2539b05ee456SIgor Mitsyanko 	int ret;
2540b05ee456SIgor Mitsyanko 
2541b05ee456SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
2542b05ee456SIgor Mitsyanko 					    QLINK_CMD_START_CAC,
2543b05ee456SIgor Mitsyanko 					    sizeof(*cmd));
2544c93fe71cSSergey Matyukevich 	if (!cmd_skb)
2545b05ee456SIgor Mitsyanko 		return -ENOMEM;
2546b05ee456SIgor Mitsyanko 
2547b05ee456SIgor Mitsyanko 	cmd = (struct qlink_cmd_start_cac *)cmd_skb->data;
2548b05ee456SIgor Mitsyanko 	cmd->cac_time_ms = cpu_to_le32(cac_time_ms);
2549b05ee456SIgor Mitsyanko 	qlink_chandef_cfg2q(chdef, &cmd->chan);
2550b05ee456SIgor Mitsyanko 
2551b05ee456SIgor Mitsyanko 	qtnf_bus_lock(bus);
2552c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2553b05ee456SIgor Mitsyanko 	if (ret)
2554c6ed298fSSergey Matyukevich 		goto out;
2555b05ee456SIgor Mitsyanko 
2556c6ed298fSSergey Matyukevich out:
2557c6ed298fSSergey Matyukevich 	qtnf_bus_unlock(bus);
2558b05ee456SIgor Mitsyanko 
2559b05ee456SIgor Mitsyanko 	return ret;
2560b05ee456SIgor Mitsyanko }
2561f1398fd2SVasily Ulyanov 
2562f1398fd2SVasily Ulyanov int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
2563f1398fd2SVasily Ulyanov 			 const struct cfg80211_acl_data *params)
2564f1398fd2SVasily Ulyanov {
2565f1398fd2SVasily Ulyanov 	struct qtnf_bus *bus = vif->mac->bus;
2566f1398fd2SVasily Ulyanov 	struct sk_buff *cmd_skb;
256733f98992SVasily Ulyanov 	struct qlink_tlv_hdr *tlv;
256833f98992SVasily Ulyanov 	size_t acl_size = qtnf_cmd_acl_data_size(params);
2569f1398fd2SVasily Ulyanov 	int ret;
2570f1398fd2SVasily Ulyanov 
2571f1398fd2SVasily Ulyanov 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
2572f1398fd2SVasily Ulyanov 					    QLINK_CMD_SET_MAC_ACL,
257333f98992SVasily Ulyanov 					    sizeof(struct qlink_cmd));
2574c93fe71cSSergey Matyukevich 	if (!cmd_skb)
2575f1398fd2SVasily Ulyanov 		return -ENOMEM;
2576f1398fd2SVasily Ulyanov 
257733f98992SVasily Ulyanov 	tlv = skb_put(cmd_skb, sizeof(*tlv) + acl_size);
257833f98992SVasily Ulyanov 	tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
257933f98992SVasily Ulyanov 	tlv->len = cpu_to_le16(acl_size);
258033f98992SVasily Ulyanov 	qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val);
2581f1398fd2SVasily Ulyanov 
2582f1398fd2SVasily Ulyanov 	qtnf_bus_lock(bus);
2583c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2584c6ed298fSSergey Matyukevich 	if (ret)
2585c6ed298fSSergey Matyukevich 		goto out;
2586c6ed298fSSergey Matyukevich 
2587c6ed298fSSergey Matyukevich out:
2588f1398fd2SVasily Ulyanov 	qtnf_bus_unlock(bus);
2589f1398fd2SVasily Ulyanov 
2590f1398fd2SVasily Ulyanov 	return ret;
2591f1398fd2SVasily Ulyanov }
25924775ad06SSergei Maksimenko 
25934775ad06SSergei Maksimenko int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout)
25944775ad06SSergei Maksimenko {
25954775ad06SSergei Maksimenko 	struct qtnf_bus *bus = vif->mac->bus;
25964775ad06SSergei Maksimenko 	struct sk_buff *cmd_skb;
25974775ad06SSergei Maksimenko 	struct qlink_cmd_pm_set *cmd;
25984775ad06SSergei Maksimenko 	int ret = 0;
25994775ad06SSergei Maksimenko 
26004775ad06SSergei Maksimenko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
26014775ad06SSergei Maksimenko 					    QLINK_CMD_PM_SET, sizeof(*cmd));
26024775ad06SSergei Maksimenko 	if (!cmd_skb)
26034775ad06SSergei Maksimenko 		return -ENOMEM;
26044775ad06SSergei Maksimenko 
26054775ad06SSergei Maksimenko 	cmd = (struct qlink_cmd_pm_set *)cmd_skb->data;
26064775ad06SSergei Maksimenko 	cmd->pm_mode = pm_mode;
26074775ad06SSergei Maksimenko 	cmd->pm_standby_timer = cpu_to_le32(timeout);
26084775ad06SSergei Maksimenko 
26094775ad06SSergei Maksimenko 	qtnf_bus_lock(bus);
26104775ad06SSergei Maksimenko 
2611c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2612c6ed298fSSergey Matyukevich 	if (ret)
26134775ad06SSergei Maksimenko 		goto out;
26144775ad06SSergei Maksimenko 
26154775ad06SSergei Maksimenko out:
26164775ad06SSergei Maksimenko 	qtnf_bus_unlock(bus);
2617c6ed298fSSergey Matyukevich 
26184775ad06SSergei Maksimenko 	return ret;
26194775ad06SSergei Maksimenko }
262028b91884SSergey Matyukevich 
262128b91884SSergey Matyukevich int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif,
262228b91884SSergey Matyukevich 			     const struct cfg80211_wowlan *wowl)
262328b91884SSergey Matyukevich {
262428b91884SSergey Matyukevich 	struct qtnf_bus *bus = vif->mac->bus;
262528b91884SSergey Matyukevich 	struct sk_buff *cmd_skb;
262628b91884SSergey Matyukevich 	struct qlink_cmd_wowlan_set *cmd;
262728b91884SSergey Matyukevich 	u32 triggers = 0;
262828b91884SSergey Matyukevich 	int count = 0;
262928b91884SSergey Matyukevich 	int ret = 0;
263028b91884SSergey Matyukevich 
263128b91884SSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
263228b91884SSergey Matyukevich 					    QLINK_CMD_WOWLAN_SET, sizeof(*cmd));
263328b91884SSergey Matyukevich 	if (!cmd_skb)
263428b91884SSergey Matyukevich 		return -ENOMEM;
263528b91884SSergey Matyukevich 
263628b91884SSergey Matyukevich 	qtnf_bus_lock(bus);
263728b91884SSergey Matyukevich 
263828b91884SSergey Matyukevich 	cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data;
263928b91884SSergey Matyukevich 
264028b91884SSergey Matyukevich 	if (wowl) {
264128b91884SSergey Matyukevich 		if (wowl->disconnect)
264228b91884SSergey Matyukevich 			triggers |=  QLINK_WOWLAN_TRIG_DISCONNECT;
264328b91884SSergey Matyukevich 
264428b91884SSergey Matyukevich 		if (wowl->magic_pkt)
264528b91884SSergey Matyukevich 			triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT;
264628b91884SSergey Matyukevich 
264728b91884SSergey Matyukevich 		if (wowl->n_patterns && wowl->patterns) {
264828b91884SSergey Matyukevich 			triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT;
264928b91884SSergey Matyukevich 			while (count < wowl->n_patterns) {
265028b91884SSergey Matyukevich 				qtnf_cmd_skb_put_tlv_arr(cmd_skb,
265128b91884SSergey Matyukevich 					QTN_TLV_ID_WOWLAN_PATTERN,
265228b91884SSergey Matyukevich 					wowl->patterns[count].pattern,
265328b91884SSergey Matyukevich 					wowl->patterns[count].pattern_len);
265428b91884SSergey Matyukevich 				count++;
265528b91884SSergey Matyukevich 			}
265628b91884SSergey Matyukevich 		}
265728b91884SSergey Matyukevich 	}
265828b91884SSergey Matyukevich 
265928b91884SSergey Matyukevich 	cmd->triggers = cpu_to_le32(triggers);
266028b91884SSergey Matyukevich 
2661c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2662c6ed298fSSergey Matyukevich 	if (ret)
266328b91884SSergey Matyukevich 		goto out;
266428b91884SSergey Matyukevich 
266528b91884SSergey Matyukevich out:
266628b91884SSergey Matyukevich 	qtnf_bus_unlock(bus);
266728b91884SSergey Matyukevich 	return ret;
266828b91884SSergey Matyukevich }
2669