xref: /openbmc/linux/drivers/net/wireless/quantenna/qtnfmac/commands.c (revision b3860e7a3e7197bd471f546733ad2ee9c23af010)
1ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+
2ff233cb5SSergey 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 
14501c3be1SIgor Mitsyanko /* Let device itself to select best values for current conditions */
15b63967caSIgor Mitsyanko #define QTNF_SCAN_TIME_AUTO	0
16b63967caSIgor Mitsyanko 
17501c3be1SIgor Mitsyanko #define QTNF_SCAN_DWELL_ACTIVE_DEFAULT		90
18501c3be1SIgor Mitsyanko #define QTNF_SCAN_DWELL_PASSIVE_DEFAULT		100
19b63967caSIgor Mitsyanko #define QTNF_SCAN_SAMPLE_DURATION_DEFAULT	QTNF_SCAN_TIME_AUTO
20b63967caSIgor Mitsyanko 
2198f44cb0SIgor Mitsyanko static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp,
2298f44cb0SIgor Mitsyanko 				       u16 cmd_id, u8 mac_id, u8 vif_id,
2398f44cb0SIgor Mitsyanko 				       size_t resp_size)
2498f44cb0SIgor Mitsyanko {
2598f44cb0SIgor Mitsyanko 	if (unlikely(le16_to_cpu(resp->cmd_id) != cmd_id)) {
2698f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad cmd_id in response: 0x%.4X\n",
2798f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, le16_to_cpu(resp->cmd_id));
2898f44cb0SIgor Mitsyanko 		return -EINVAL;
2998f44cb0SIgor Mitsyanko 	}
3098f44cb0SIgor Mitsyanko 
3198f44cb0SIgor Mitsyanko 	if (unlikely(resp->macid != mac_id)) {
3298f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad MAC in response: %u\n",
3398f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, resp->macid);
3498f44cb0SIgor Mitsyanko 		return -EINVAL;
3598f44cb0SIgor Mitsyanko 	}
3698f44cb0SIgor Mitsyanko 
3798f44cb0SIgor Mitsyanko 	if (unlikely(resp->vifid != vif_id)) {
3898f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad VIF in response: %u\n",
3998f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id, resp->vifid);
4098f44cb0SIgor Mitsyanko 		return -EINVAL;
4198f44cb0SIgor Mitsyanko 	}
4298f44cb0SIgor Mitsyanko 
4398f44cb0SIgor Mitsyanko 	if (unlikely(le16_to_cpu(resp->mhdr.len) < resp_size)) {
4498f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u CMD%x: bad response size %u < %zu\n",
4598f44cb0SIgor Mitsyanko 			mac_id, vif_id, cmd_id,
4698f44cb0SIgor Mitsyanko 			le16_to_cpu(resp->mhdr.len), resp_size);
4798f44cb0SIgor Mitsyanko 		return -ENOSPC;
4898f44cb0SIgor Mitsyanko 	}
4998f44cb0SIgor Mitsyanko 
5098f44cb0SIgor Mitsyanko 	return 0;
5198f44cb0SIgor Mitsyanko }
5298f44cb0SIgor Mitsyanko 
5336e8c538SIgor Mitsyanko static int qtnf_cmd_resp_result_decode(enum qlink_cmd_result qcode)
5436e8c538SIgor Mitsyanko {
5536e8c538SIgor Mitsyanko 	switch (qcode) {
5636e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_OK:
5736e8c538SIgor Mitsyanko 		return 0;
5836e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_INVALID:
5936e8c538SIgor Mitsyanko 		return -EINVAL;
6036e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_ENOTSUPP:
6136e8c538SIgor Mitsyanko 		return -ENOTSUPP;
6236e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_ENOTFOUND:
6336e8c538SIgor Mitsyanko 		return -ENOENT;
6436e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EALREADY:
6536e8c538SIgor Mitsyanko 		return -EALREADY;
6636e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EADDRINUSE:
6736e8c538SIgor Mitsyanko 		return -EADDRINUSE;
6836e8c538SIgor Mitsyanko 	case QLINK_CMD_RESULT_EADDRNOTAVAIL:
6936e8c538SIgor Mitsyanko 		return -EADDRNOTAVAIL;
70126824f5SAndrey Shevchenko 	case QLINK_CMD_RESULT_EBUSY:
71126824f5SAndrey Shevchenko 		return -EBUSY;
7236e8c538SIgor Mitsyanko 	default:
7336e8c538SIgor Mitsyanko 		return -EFAULT;
7436e8c538SIgor Mitsyanko 	}
7536e8c538SIgor Mitsyanko }
7636e8c538SIgor Mitsyanko 
7798f44cb0SIgor Mitsyanko static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
7898f44cb0SIgor Mitsyanko 				    struct sk_buff *cmd_skb,
7998f44cb0SIgor Mitsyanko 				    struct sk_buff **response_skb,
8098f44cb0SIgor Mitsyanko 				    size_t const_resp_size,
8198f44cb0SIgor Mitsyanko 				    size_t *var_resp_size)
8298f44cb0SIgor Mitsyanko {
8398f44cb0SIgor Mitsyanko 	struct qlink_cmd *cmd;
841066bd19SSergey Matyukevich 	struct qlink_resp *resp = NULL;
8598f44cb0SIgor Mitsyanko 	struct sk_buff *resp_skb = NULL;
864a33f21cSSergey Matyukevich 	int resp_res = 0;
8798f44cb0SIgor Mitsyanko 	u16 cmd_id;
88c6ed298fSSergey Matyukevich 	u8 mac_id;
89c6ed298fSSergey Matyukevich 	u8 vif_id;
9098f44cb0SIgor Mitsyanko 	int ret;
9198f44cb0SIgor Mitsyanko 
9298f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd *)cmd_skb->data;
9398f44cb0SIgor Mitsyanko 	cmd_id = le16_to_cpu(cmd->cmd_id);
9498f44cb0SIgor Mitsyanko 	mac_id = cmd->macid;
9598f44cb0SIgor Mitsyanko 	vif_id = cmd->vifid;
9698f44cb0SIgor Mitsyanko 	cmd->mhdr.len = cpu_to_le16(cmd_skb->len);
9798f44cb0SIgor Mitsyanko 
983844dec0SSergey Matyukevich 	pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id, cmd_id);
99c6ed298fSSergey Matyukevich 
10083b00f6eSSergey Matyukevich 	if (!qtnf_fw_is_up(bus) && cmd_id != QLINK_CMD_FW_INIT) {
10198f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n",
1023844dec0SSergey Matyukevich 			mac_id, vif_id, cmd_id, bus->fw_state);
103b60769e2SDmitry Lebed 		dev_kfree_skb(cmd_skb);
10498f44cb0SIgor Mitsyanko 		return -ENODEV;
10598f44cb0SIgor Mitsyanko 	}
10698f44cb0SIgor Mitsyanko 
10798f44cb0SIgor Mitsyanko 	ret = qtnf_trans_send_cmd_with_resp(bus, cmd_skb, &resp_skb);
108c6ed298fSSergey Matyukevich 	if (ret)
10998f44cb0SIgor Mitsyanko 		goto out;
11098f44cb0SIgor Mitsyanko 
1111066bd19SSergey Matyukevich 	if (WARN_ON(!resp_skb || !resp_skb->data)) {
1121066bd19SSergey Matyukevich 		ret = -EFAULT;
1131066bd19SSergey Matyukevich 		goto out;
1141066bd19SSergey Matyukevich 	}
1151066bd19SSergey Matyukevich 
1161066bd19SSergey Matyukevich 	resp = (struct qlink_resp *)resp_skb->data;
1174a33f21cSSergey Matyukevich 	resp_res = le16_to_cpu(resp->result);
11898f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id,
11998f44cb0SIgor Mitsyanko 					  const_resp_size);
120c6ed298fSSergey Matyukevich 	if (ret)
12198f44cb0SIgor Mitsyanko 		goto out;
12298f44cb0SIgor Mitsyanko 
12398f44cb0SIgor Mitsyanko 	/* Return length of variable part of response */
12498f44cb0SIgor Mitsyanko 	if (response_skb && var_resp_size)
12598f44cb0SIgor Mitsyanko 		*var_resp_size = le16_to_cpu(resp->mhdr.len) - const_resp_size;
12698f44cb0SIgor Mitsyanko 
12798f44cb0SIgor Mitsyanko out:
12898f44cb0SIgor Mitsyanko 	if (response_skb)
12998f44cb0SIgor Mitsyanko 		*response_skb = resp_skb;
13098f44cb0SIgor Mitsyanko 	else
13198f44cb0SIgor Mitsyanko 		consume_skb(resp_skb);
13298f44cb0SIgor Mitsyanko 
1334a33f21cSSergey Matyukevich 	if (!ret)
1344a33f21cSSergey Matyukevich 		return qtnf_cmd_resp_result_decode(resp_res);
135c6ed298fSSergey Matyukevich 
136c6ed298fSSergey Matyukevich 	pr_warn("VIF%u.%u: cmd 0x%.4X failed: %d\n",
1373844dec0SSergey Matyukevich 		mac_id, vif_id, cmd_id, ret);
138c6ed298fSSergey Matyukevich 
13998f44cb0SIgor Mitsyanko 	return ret;
14098f44cb0SIgor Mitsyanko }
14198f44cb0SIgor Mitsyanko 
142c6ed298fSSergey Matyukevich static inline int qtnf_cmd_send(struct qtnf_bus *bus, struct sk_buff *cmd_skb)
14398f44cb0SIgor Mitsyanko {
144c6ed298fSSergey Matyukevich 	return qtnf_cmd_send_with_reply(bus, cmd_skb, NULL,
14598f44cb0SIgor Mitsyanko 					sizeof(struct qlink_resp), NULL);
14698f44cb0SIgor Mitsyanko }
14798f44cb0SIgor Mitsyanko 
14898f44cb0SIgor Mitsyanko static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no,
14998f44cb0SIgor Mitsyanko 						 size_t cmd_size)
15098f44cb0SIgor Mitsyanko {
15198f44cb0SIgor Mitsyanko 	struct qlink_cmd *cmd;
15298f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
15398f44cb0SIgor Mitsyanko 
15498f44cb0SIgor Mitsyanko 	cmd_skb = __dev_alloc_skb(sizeof(*cmd) +
15598f44cb0SIgor Mitsyanko 				  QTNF_MAX_CMD_BUF_SIZE, GFP_KERNEL);
15698f44cb0SIgor Mitsyanko 	if (unlikely(!cmd_skb)) {
15798f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u CMD %u: alloc failed\n", macid, vifid, cmd_no);
15898f44cb0SIgor Mitsyanko 		return NULL;
15998f44cb0SIgor Mitsyanko 	}
16098f44cb0SIgor Mitsyanko 
161b080db58SJohannes Berg 	skb_put_zero(cmd_skb, cmd_size);
16298f44cb0SIgor Mitsyanko 
16398f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd *)cmd_skb->data;
16498f44cb0SIgor Mitsyanko 	cmd->mhdr.len = cpu_to_le16(cmd_skb->len);
16598f44cb0SIgor Mitsyanko 	cmd->mhdr.type = cpu_to_le16(QLINK_MSG_TYPE_CMD);
16698f44cb0SIgor Mitsyanko 	cmd->cmd_id = cpu_to_le16(cmd_no);
16798f44cb0SIgor Mitsyanko 	cmd->macid = macid;
16898f44cb0SIgor Mitsyanko 	cmd->vifid = vifid;
16998f44cb0SIgor Mitsyanko 
17098f44cb0SIgor Mitsyanko 	return cmd_skb;
17198f44cb0SIgor Mitsyanko }
17298f44cb0SIgor Mitsyanko 
17318b7470fSIgor Mitsyanko static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type,
17418b7470fSIgor Mitsyanko 				    const u8 *buf, size_t len)
17518b7470fSIgor Mitsyanko {
17618b7470fSIgor Mitsyanko 	struct qlink_tlv_ie_set *tlv;
17718b7470fSIgor Mitsyanko 
1788b0b5f1bSIgor Mitsyanko 	tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) +
1798b0b5f1bSIgor Mitsyanko 						 round_up(len, QLINK_ALIGN));
18018b7470fSIgor Mitsyanko 	tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET);
18118b7470fSIgor Mitsyanko 	tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr));
18218b7470fSIgor Mitsyanko 	tlv->type = frame_type;
18318b7470fSIgor Mitsyanko 	tlv->flags = 0;
18418b7470fSIgor Mitsyanko 
18518b7470fSIgor Mitsyanko 	if (len && buf)
18618b7470fSIgor Mitsyanko 		memcpy(tlv->ie_data, buf, len);
18718b7470fSIgor Mitsyanko }
18818b7470fSIgor Mitsyanko 
18917011da0SIgor Mitsyanko static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
19017011da0SIgor Mitsyanko 				      const struct cfg80211_ap_settings *s)
19198f44cb0SIgor Mitsyanko {
19217011da0SIgor Mitsyanko 	unsigned int len = sizeof(struct qlink_cmd_start_ap);
19398f44cb0SIgor Mitsyanko 
1948b0b5f1bSIgor Mitsyanko 	len += round_up(s->ssid_len, QLINK_ALIGN);
1958b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.head_len, QLINK_ALIGN);
1968b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.tail_len, QLINK_ALIGN);
1978b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.beacon_ies_len, QLINK_ALIGN);
1988b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.proberesp_ies_len, QLINK_ALIGN);
1998b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.assocresp_ies_len, QLINK_ALIGN);
2008b0b5f1bSIgor Mitsyanko 	len += round_up(s->beacon.probe_resp_len, QLINK_ALIGN);
20198f44cb0SIgor Mitsyanko 
20217011da0SIgor Mitsyanko 	if (cfg80211_chandef_valid(&s->chandef))
20317011da0SIgor Mitsyanko 		len += sizeof(struct qlink_tlv_chandef);
20498f44cb0SIgor Mitsyanko 
2058b0b5f1bSIgor Mitsyanko 	if (s->acl) {
2068b0b5f1bSIgor Mitsyanko 		unsigned int acl_len = struct_size(s->acl, mac_addrs,
2078b0b5f1bSIgor Mitsyanko 						   s->acl->n_acl_entries);
2088b0b5f1bSIgor Mitsyanko 
20933f98992SVasily Ulyanov 		len += sizeof(struct qlink_tlv_hdr) +
2108b0b5f1bSIgor Mitsyanko 			round_up(acl_len, QLINK_ALIGN);
2118b0b5f1bSIgor Mitsyanko 	}
212f1398fd2SVasily Ulyanov 
21317011da0SIgor Mitsyanko 	if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
21417011da0SIgor Mitsyanko 		pr_err("VIF%u.%u: can not fit AP settings: %u\n",
21517011da0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, len);
21617011da0SIgor Mitsyanko 		return false;
21798f44cb0SIgor Mitsyanko 	}
21898f44cb0SIgor Mitsyanko 
21917011da0SIgor Mitsyanko 	return true;
22098f44cb0SIgor Mitsyanko }
22198f44cb0SIgor Mitsyanko 
222df0af4c7SMikhail Karpenko static void qtnf_cmd_tlv_ie_ext_add(struct sk_buff *cmd_skb, u8 eid_ext,
223df0af4c7SMikhail Karpenko 				    const void *buf, size_t len)
224df0af4c7SMikhail Karpenko {
225df0af4c7SMikhail Karpenko 	struct qlink_tlv_ext_ie *tlv;
226df0af4c7SMikhail Karpenko 
227df0af4c7SMikhail Karpenko 	tlv = (struct qlink_tlv_ext_ie *)skb_put(cmd_skb, sizeof(*tlv) + len);
228df0af4c7SMikhail Karpenko 	tlv->hdr.type = cpu_to_le16(WLAN_EID_EXTENSION);
229df0af4c7SMikhail Karpenko 	tlv->hdr.len = cpu_to_le16(sizeof(*tlv) + len - sizeof(tlv->hdr));
230df0af4c7SMikhail Karpenko 	tlv->eid_ext = eid_ext;
231df0af4c7SMikhail Karpenko 
232df0af4c7SMikhail Karpenko 	if (len && buf)
233df0af4c7SMikhail Karpenko 		memcpy(tlv->ie_data, buf, len);
234df0af4c7SMikhail Karpenko }
235df0af4c7SMikhail Karpenko 
23617011da0SIgor Mitsyanko int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
2379b692df1SIgor Mitsyanko 			   const struct cfg80211_ap_settings *s)
23898f44cb0SIgor Mitsyanko {
23998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
24017011da0SIgor Mitsyanko 	struct qlink_cmd_start_ap *cmd;
2418b5f4aa7SIgor Mitsyanko 	struct qlink_auth_encr *aen;
24298f44cb0SIgor Mitsyanko 	int ret;
24398f44cb0SIgor Mitsyanko 	int i;
24498f44cb0SIgor Mitsyanko 
24517011da0SIgor Mitsyanko 	if (!qtnf_cmd_start_ap_can_fit(vif, s))
24617011da0SIgor Mitsyanko 		return -E2BIG;
24717011da0SIgor Mitsyanko 
24898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
24917011da0SIgor Mitsyanko 					    QLINK_CMD_START_AP,
2508b5f4aa7SIgor Mitsyanko 					    sizeof(*cmd));
251c93fe71cSSergey Matyukevich 	if (!cmd_skb)
25298f44cb0SIgor Mitsyanko 		return -ENOMEM;
25398f44cb0SIgor Mitsyanko 
25417011da0SIgor Mitsyanko 	cmd = (struct qlink_cmd_start_ap *)cmd_skb->data;
2558b5f4aa7SIgor Mitsyanko 	cmd->dtim_period = s->dtim_period;
2568b5f4aa7SIgor Mitsyanko 	cmd->beacon_interval = cpu_to_le16(s->beacon_interval);
2578b5f4aa7SIgor Mitsyanko 	cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid);
2588b5f4aa7SIgor Mitsyanko 	cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout);
2598b5f4aa7SIgor Mitsyanko 	cmd->smps_mode = s->smps_mode;
2608b5f4aa7SIgor Mitsyanko 	cmd->p2p_ctwindow = s->p2p_ctwindow;
2618b5f4aa7SIgor Mitsyanko 	cmd->p2p_opp_ps = s->p2p_opp_ps;
2628b5f4aa7SIgor Mitsyanko 	cmd->pbss = s->pbss;
2638b5f4aa7SIgor Mitsyanko 	cmd->ht_required = s->ht_required;
2648b5f4aa7SIgor Mitsyanko 	cmd->vht_required = s->vht_required;
265ed7791d9SMikhail Karpenko 	cmd->twt_responder = s->twt_responder;
266ed7791d9SMikhail Karpenko 	if (s->he_obss_pd.enable) {
267ed7791d9SMikhail Karpenko 		cmd->sr_params.sr_control |= QLINK_SR_SRG_INFORMATION_PRESENT;
268ed7791d9SMikhail Karpenko 		cmd->sr_params.srg_obss_pd_min_offset =
269ed7791d9SMikhail Karpenko 			s->he_obss_pd.min_offset;
270ed7791d9SMikhail Karpenko 		cmd->sr_params.srg_obss_pd_max_offset =
271ed7791d9SMikhail Karpenko 			s->he_obss_pd.max_offset;
272ed7791d9SMikhail Karpenko 	}
27398f44cb0SIgor Mitsyanko 
2748b5f4aa7SIgor Mitsyanko 	aen = &cmd->aen;
2758b5f4aa7SIgor Mitsyanko 	aen->auth_type = s->auth_type;
2768b5f4aa7SIgor Mitsyanko 	aen->privacy = !!s->privacy;
2778b5f4aa7SIgor Mitsyanko 	aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions);
2788b5f4aa7SIgor Mitsyanko 	aen->cipher_group = cpu_to_le32(s->crypto.cipher_group);
2798b5f4aa7SIgor Mitsyanko 	aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise);
28098f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++)
2818b5f4aa7SIgor Mitsyanko 		aen->ciphers_pairwise[i] =
2829b692df1SIgor Mitsyanko 				cpu_to_le32(s->crypto.ciphers_pairwise[i]);
2838b5f4aa7SIgor Mitsyanko 	aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites);
28498f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++)
2858b5f4aa7SIgor Mitsyanko 		aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]);
2868b5f4aa7SIgor Mitsyanko 	aen->control_port = s->crypto.control_port;
2878b5f4aa7SIgor Mitsyanko 	aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt;
2888b5f4aa7SIgor Mitsyanko 	aen->control_port_ethertype =
2899b692df1SIgor Mitsyanko 		cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype));
29098f44cb0SIgor Mitsyanko 
291f99201cbSIgor Mitsyanko 	if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN)
292f99201cbSIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid,
293f99201cbSIgor Mitsyanko 					 s->ssid_len);
294f99201cbSIgor Mitsyanko 
295f99201cbSIgor Mitsyanko 	if (cfg80211_chandef_valid(&s->chandef)) {
296f99201cbSIgor Mitsyanko 		struct qlink_tlv_chandef *chtlv =
297f99201cbSIgor Mitsyanko 			(struct qlink_tlv_chandef *)skb_put(cmd_skb,
298f99201cbSIgor Mitsyanko 							    sizeof(*chtlv));
299f99201cbSIgor Mitsyanko 
300f99201cbSIgor Mitsyanko 		chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF);
301f99201cbSIgor Mitsyanko 		chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) -
302f99201cbSIgor Mitsyanko 					     sizeof(chtlv->hdr));
3035bf374abSSergey Matyukevich 		qlink_chandef_cfg2q(&s->chandef, &chtlv->chdef);
304f99201cbSIgor Mitsyanko 	}
305f99201cbSIgor Mitsyanko 
30617011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD,
30717011da0SIgor Mitsyanko 				s->beacon.head, s->beacon.head_len);
30817011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_TAIL,
30917011da0SIgor Mitsyanko 				s->beacon.tail, s->beacon.tail_len);
31017011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_IES,
31117011da0SIgor Mitsyanko 				s->beacon.beacon_ies, s->beacon.beacon_ies_len);
31217011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP,
31317011da0SIgor Mitsyanko 				s->beacon.probe_resp, s->beacon.probe_resp_len);
31417011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP_IES,
31517011da0SIgor Mitsyanko 				s->beacon.proberesp_ies,
31617011da0SIgor Mitsyanko 				s->beacon.proberesp_ies_len);
31717011da0SIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_RESP,
31817011da0SIgor Mitsyanko 				s->beacon.assocresp_ies,
31917011da0SIgor Mitsyanko 				s->beacon.assocresp_ies_len);
32017011da0SIgor Mitsyanko 
321a3945f43SIgor Mitsyanko 	if (s->ht_cap) {
322a3945f43SIgor Mitsyanko 		struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *)
3238b0b5f1bSIgor Mitsyanko 			skb_put(cmd_skb, sizeof(*tlv) +
3248b0b5f1bSIgor Mitsyanko 				round_up(sizeof(*s->ht_cap), QLINK_ALIGN));
325a3945f43SIgor Mitsyanko 
326a3945f43SIgor Mitsyanko 		tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
327a3945f43SIgor Mitsyanko 		tlv->len = cpu_to_le16(sizeof(*s->ht_cap));
328a3945f43SIgor Mitsyanko 		memcpy(tlv->val, s->ht_cap, sizeof(*s->ht_cap));
329a3945f43SIgor Mitsyanko 	}
330a3945f43SIgor Mitsyanko 
331a3945f43SIgor Mitsyanko 	if (s->vht_cap) {
332a3945f43SIgor Mitsyanko 		struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *)
333a3945f43SIgor Mitsyanko 			skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->vht_cap));
334a3945f43SIgor Mitsyanko 
335a3945f43SIgor Mitsyanko 		tlv->type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
336a3945f43SIgor Mitsyanko 		tlv->len = cpu_to_le16(sizeof(*s->vht_cap));
337a3945f43SIgor Mitsyanko 		memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
338a3945f43SIgor Mitsyanko 	}
339a3945f43SIgor Mitsyanko 
340df0af4c7SMikhail Karpenko 	if (s->he_cap)
341df0af4c7SMikhail Karpenko 		qtnf_cmd_tlv_ie_ext_add(cmd_skb, WLAN_EID_EXT_HE_CAPABILITY,
342df0af4c7SMikhail Karpenko 					s->he_cap, sizeof(*s->he_cap));
343df0af4c7SMikhail Karpenko 
344f1398fd2SVasily Ulyanov 	if (s->acl) {
34595336d4cSGustavo A. R. Silva 		size_t acl_size = struct_size(s->acl, mac_addrs,
34695336d4cSGustavo A. R. Silva 					      s->acl->n_acl_entries);
347f1398fd2SVasily Ulyanov 		struct qlink_tlv_hdr *tlv =
3488b0b5f1bSIgor Mitsyanko 			skb_put(cmd_skb,
3498b0b5f1bSIgor Mitsyanko 				sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN));
350f1398fd2SVasily Ulyanov 
351f1398fd2SVasily Ulyanov 		tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
352f1398fd2SVasily Ulyanov 		tlv->len = cpu_to_le16(acl_size);
353f1398fd2SVasily Ulyanov 		qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val);
354f1398fd2SVasily Ulyanov 	}
355f1398fd2SVasily Ulyanov 
3568b5f4aa7SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
357c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
358c6ed298fSSergey Matyukevich 	if (ret)
35998f44cb0SIgor Mitsyanko 		goto out;
36098f44cb0SIgor Mitsyanko 
36117011da0SIgor Mitsyanko 	netif_carrier_on(vif->netdev);
36217011da0SIgor Mitsyanko 
36398f44cb0SIgor Mitsyanko out:
36498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
365c6ed298fSSergey Matyukevich 
36698f44cb0SIgor Mitsyanko 	return ret;
36798f44cb0SIgor Mitsyanko }
36898f44cb0SIgor Mitsyanko 
36998f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif)
37098f44cb0SIgor Mitsyanko {
37198f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
37298f44cb0SIgor Mitsyanko 	int ret;
37398f44cb0SIgor Mitsyanko 
37498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
37598f44cb0SIgor Mitsyanko 					    QLINK_CMD_STOP_AP,
37698f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
377c93fe71cSSergey Matyukevich 	if (!cmd_skb)
37898f44cb0SIgor Mitsyanko 		return -ENOMEM;
37998f44cb0SIgor Mitsyanko 
38098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
381c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
382c6ed298fSSergey Matyukevich 	if (ret)
38398f44cb0SIgor Mitsyanko 		goto out;
38498f44cb0SIgor Mitsyanko 
38598f44cb0SIgor Mitsyanko out:
38698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
387c6ed298fSSergey Matyukevich 
38898f44cb0SIgor Mitsyanko 	return ret;
38998f44cb0SIgor Mitsyanko }
39098f44cb0SIgor Mitsyanko 
39198f44cb0SIgor Mitsyanko int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg)
39298f44cb0SIgor Mitsyanko {
39398f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
39498f44cb0SIgor Mitsyanko 	struct qlink_cmd_mgmt_frame_register *cmd;
39598f44cb0SIgor Mitsyanko 	int ret;
39698f44cb0SIgor Mitsyanko 
39798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
39898f44cb0SIgor Mitsyanko 					    QLINK_CMD_REGISTER_MGMT,
39998f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
400c93fe71cSSergey Matyukevich 	if (!cmd_skb)
40198f44cb0SIgor Mitsyanko 		return -ENOMEM;
40298f44cb0SIgor Mitsyanko 
40398f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
40498f44cb0SIgor Mitsyanko 
40598f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_mgmt_frame_register *)cmd_skb->data;
40698f44cb0SIgor Mitsyanko 	cmd->frame_type = cpu_to_le16(frame_type);
40798f44cb0SIgor Mitsyanko 	cmd->do_register = reg;
40898f44cb0SIgor Mitsyanko 
409c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
410c6ed298fSSergey Matyukevich 	if (ret)
41198f44cb0SIgor Mitsyanko 		goto out;
41298f44cb0SIgor Mitsyanko 
41398f44cb0SIgor Mitsyanko out:
41498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
415c6ed298fSSergey Matyukevich 
41698f44cb0SIgor Mitsyanko 	return ret;
41798f44cb0SIgor Mitsyanko }
41898f44cb0SIgor Mitsyanko 
419bc70732fSIgor Mitsyanko int qtnf_cmd_send_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
42098f44cb0SIgor Mitsyanko 			u16 freq, const u8 *buf, size_t len)
42198f44cb0SIgor Mitsyanko {
42298f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
423bc70732fSIgor Mitsyanko 	struct qlink_cmd_frame_tx *cmd;
42498f44cb0SIgor Mitsyanko 	int ret;
42598f44cb0SIgor Mitsyanko 
42698f44cb0SIgor Mitsyanko 	if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) {
42798f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: frame is too big: %zu\n", vif->mac->macid,
42898f44cb0SIgor Mitsyanko 			vif->vifid, len);
42998f44cb0SIgor Mitsyanko 		return -E2BIG;
43098f44cb0SIgor Mitsyanko 	}
43198f44cb0SIgor Mitsyanko 
43298f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
433bc70732fSIgor Mitsyanko 					    QLINK_CMD_SEND_FRAME,
43498f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
435c93fe71cSSergey Matyukevich 	if (!cmd_skb)
43698f44cb0SIgor Mitsyanko 		return -ENOMEM;
43798f44cb0SIgor Mitsyanko 
43898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
43998f44cb0SIgor Mitsyanko 
440bc70732fSIgor Mitsyanko 	cmd = (struct qlink_cmd_frame_tx *)cmd_skb->data;
44198f44cb0SIgor Mitsyanko 	cmd->cookie = cpu_to_le32(cookie);
44298f44cb0SIgor Mitsyanko 	cmd->freq = cpu_to_le16(freq);
44398f44cb0SIgor Mitsyanko 	cmd->flags = cpu_to_le16(flags);
44498f44cb0SIgor Mitsyanko 
44598f44cb0SIgor Mitsyanko 	if (len && buf)
44698f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_buffer(cmd_skb, buf, len);
44798f44cb0SIgor Mitsyanko 
448c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
449c6ed298fSSergey Matyukevich 	if (ret)
45098f44cb0SIgor Mitsyanko 		goto out;
45198f44cb0SIgor Mitsyanko 
45298f44cb0SIgor Mitsyanko out:
45398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
454c6ed298fSSergey Matyukevich 
45598f44cb0SIgor Mitsyanko 	return ret;
45698f44cb0SIgor Mitsyanko }
45798f44cb0SIgor Mitsyanko 
45898f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
45998f44cb0SIgor Mitsyanko 				 const u8 *buf, size_t len)
46098f44cb0SIgor Mitsyanko {
46198f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
46298f44cb0SIgor Mitsyanko 	int ret;
46398f44cb0SIgor Mitsyanko 
4644d1f0fabSIgor Mitsyanko 	if (len > QTNF_MAX_CMD_BUF_SIZE) {
46598f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid,
46698f44cb0SIgor Mitsyanko 			vif->vifid, frame_type, len);
46798f44cb0SIgor Mitsyanko 		return -E2BIG;
46898f44cb0SIgor Mitsyanko 	}
46998f44cb0SIgor Mitsyanko 
47098f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
47198f44cb0SIgor Mitsyanko 					    QLINK_CMD_MGMT_SET_APPIE,
4724d1f0fabSIgor Mitsyanko 					    sizeof(struct qlink_cmd));
473c93fe71cSSergey Matyukevich 	if (!cmd_skb)
47498f44cb0SIgor Mitsyanko 		return -ENOMEM;
47598f44cb0SIgor Mitsyanko 
4764d1f0fabSIgor Mitsyanko 	qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len);
4774d1f0fabSIgor Mitsyanko 
47898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
479c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
480c6ed298fSSergey Matyukevich 	if (ret)
48198f44cb0SIgor Mitsyanko 		goto out;
48298f44cb0SIgor Mitsyanko 
48398f44cb0SIgor Mitsyanko out:
48498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
485c6ed298fSSergey Matyukevich 
48698f44cb0SIgor Mitsyanko 	return ret;
48798f44cb0SIgor Mitsyanko }
48898f44cb0SIgor Mitsyanko 
48998f44cb0SIgor Mitsyanko static void
49098f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(struct rate_info *rate_dst,
49198f44cb0SIgor Mitsyanko 			 const struct qlink_sta_info_rate *rate_src)
49298f44cb0SIgor Mitsyanko {
49398f44cb0SIgor Mitsyanko 	rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10;
49498f44cb0SIgor Mitsyanko 
49598f44cb0SIgor Mitsyanko 	rate_dst->mcs = rate_src->mcs;
49698f44cb0SIgor Mitsyanko 	rate_dst->nss = rate_src->nss;
49798f44cb0SIgor Mitsyanko 	rate_dst->flags = 0;
49898f44cb0SIgor Mitsyanko 
49998f44cb0SIgor Mitsyanko 	switch (rate_src->bw) {
5004d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_5:
50198f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_5;
50298f44cb0SIgor Mitsyanko 		break;
5034d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_10:
50498f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_10;
50598f44cb0SIgor Mitsyanko 		break;
5064d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_20:
5074d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_20_NOHT:
50898f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_20;
50998f44cb0SIgor Mitsyanko 		break;
5104d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_40:
51198f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_40;
51298f44cb0SIgor Mitsyanko 		break;
5134d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_80:
51498f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_80;
51598f44cb0SIgor Mitsyanko 		break;
5164d2a7a1cSIgor Mitsyanko 	case QLINK_CHAN_WIDTH_160:
51798f44cb0SIgor Mitsyanko 		rate_dst->bw = RATE_INFO_BW_160;
51898f44cb0SIgor Mitsyanko 		break;
51998f44cb0SIgor Mitsyanko 	default:
52098f44cb0SIgor Mitsyanko 		rate_dst->bw = 0;
52198f44cb0SIgor Mitsyanko 		break;
52298f44cb0SIgor Mitsyanko 	}
52398f44cb0SIgor Mitsyanko 
52498f44cb0SIgor Mitsyanko 	if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HT_MCS)
52598f44cb0SIgor Mitsyanko 		rate_dst->flags |= RATE_INFO_FLAGS_MCS;
52698f44cb0SIgor Mitsyanko 	else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS)
52798f44cb0SIgor Mitsyanko 		rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS;
528b73f0aacSSergey Matyukevich 	else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HE_MCS)
529b73f0aacSSergey Matyukevich 		rate_dst->flags |= RATE_INFO_FLAGS_HE_MCS;
530d5657b70SSergey Matyukevich 
531d5657b70SSergey Matyukevich 	if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_SHORT_GI)
532d5657b70SSergey Matyukevich 		rate_dst->flags |= RATE_INFO_FLAGS_SHORT_GI;
53398f44cb0SIgor Mitsyanko }
53498f44cb0SIgor Mitsyanko 
53598f44cb0SIgor Mitsyanko static void
53698f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst,
53798f44cb0SIgor Mitsyanko 			  const struct qlink_sta_info_state *src)
53898f44cb0SIgor Mitsyanko {
53998f44cb0SIgor Mitsyanko 	u32 mask, value;
54098f44cb0SIgor Mitsyanko 
54198f44cb0SIgor Mitsyanko 	dst->mask = 0;
54298f44cb0SIgor Mitsyanko 	dst->set = 0;
54398f44cb0SIgor Mitsyanko 
54498f44cb0SIgor Mitsyanko 	mask = le32_to_cpu(src->mask);
54598f44cb0SIgor Mitsyanko 	value = le32_to_cpu(src->value);
54698f44cb0SIgor Mitsyanko 
54798f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_AUTHORIZED) {
54898f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_AUTHORIZED);
54998f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_AUTHORIZED)
55098f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
55198f44cb0SIgor Mitsyanko 	}
55298f44cb0SIgor Mitsyanko 
55398f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_SHORT_PREAMBLE) {
55498f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
55598f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_SHORT_PREAMBLE)
55698f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
55798f44cb0SIgor Mitsyanko 	}
55898f44cb0SIgor Mitsyanko 
55998f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_WME) {
56098f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_WME);
56198f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_WME)
56298f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_WME);
56398f44cb0SIgor Mitsyanko 	}
56498f44cb0SIgor Mitsyanko 
56598f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_MFP) {
56698f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_MFP);
56798f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_MFP)
56898f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_MFP);
56998f44cb0SIgor Mitsyanko 	}
57098f44cb0SIgor Mitsyanko 
57198f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_AUTHENTICATED) {
57298f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
57398f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_AUTHENTICATED)
57498f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
57598f44cb0SIgor Mitsyanko 	}
57698f44cb0SIgor Mitsyanko 
57798f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_TDLS_PEER) {
57898f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
57998f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_TDLS_PEER)
58098f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
58198f44cb0SIgor Mitsyanko 	}
58298f44cb0SIgor Mitsyanko 
58398f44cb0SIgor Mitsyanko 	if (mask & QLINK_STA_FLAG_ASSOCIATED) {
58498f44cb0SIgor Mitsyanko 		dst->mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
58598f44cb0SIgor Mitsyanko 		if (value & QLINK_STA_FLAG_ASSOCIATED)
58698f44cb0SIgor Mitsyanko 			dst->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
58798f44cb0SIgor Mitsyanko 	}
58898f44cb0SIgor Mitsyanko }
58998f44cb0SIgor Mitsyanko 
59098f44cb0SIgor Mitsyanko static void
5918b0b5f1bSIgor Mitsyanko qtnf_cmd_sta_info_parse(struct station_info *sinfo, const u8 *data,
5924d2a7a1cSIgor Mitsyanko 			size_t resp_size)
59398f44cb0SIgor Mitsyanko {
5948b0b5f1bSIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
5954d2a7a1cSIgor Mitsyanko 	const struct qlink_sta_stats *stats = NULL;
5964d2a7a1cSIgor Mitsyanko 	const u8 *map = NULL;
5974d2a7a1cSIgor Mitsyanko 	unsigned int map_len = 0;
5984d2a7a1cSIgor Mitsyanko 	unsigned int stats_len = 0;
5994d2a7a1cSIgor Mitsyanko 	u16 tlv_len;
60098f44cb0SIgor Mitsyanko 
6014d2a7a1cSIgor Mitsyanko #define qtnf_sta_stat_avail(stat_name, bitn)	\
6024d2a7a1cSIgor Mitsyanko 	(qtnf_utils_is_bit_set(map, bitn, map_len) && \
6034d2a7a1cSIgor Mitsyanko 	 (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len))
60498f44cb0SIgor Mitsyanko 
6058b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, data, resp_size) {
6064d2a7a1cSIgor Mitsyanko 		tlv_len = le16_to_cpu(tlv->len);
60798f44cb0SIgor Mitsyanko 
6084d2a7a1cSIgor Mitsyanko 		switch (le16_to_cpu(tlv->type)) {
609310cd5ddSIgor Mitsyanko 		case QTN_TLV_ID_BITMAP:
6104d2a7a1cSIgor Mitsyanko 			map_len = tlv_len;
6114d2a7a1cSIgor Mitsyanko 			map = tlv->val;
61298f44cb0SIgor Mitsyanko 			break;
6134d2a7a1cSIgor Mitsyanko 		case QTN_TLV_ID_STA_STATS:
6144d2a7a1cSIgor Mitsyanko 			stats_len = tlv_len;
6154d2a7a1cSIgor Mitsyanko 			stats = (const struct qlink_sta_stats *)tlv->val;
61698f44cb0SIgor Mitsyanko 			break;
61798f44cb0SIgor Mitsyanko 		default:
61898f44cb0SIgor Mitsyanko 			break;
61998f44cb0SIgor Mitsyanko 		}
6208b0b5f1bSIgor Mitsyanko 	}
6214d2a7a1cSIgor Mitsyanko 
6228b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, data, resp_size)) {
6238b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
6248b0b5f1bSIgor Mitsyanko 		return;
62598f44cb0SIgor Mitsyanko 	}
62698f44cb0SIgor Mitsyanko 
6274d2a7a1cSIgor Mitsyanko 	if (!map || !stats)
6284d2a7a1cSIgor Mitsyanko 		return;
6294d2a7a1cSIgor Mitsyanko 
6304d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) {
63122d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
6324d2a7a1cSIgor Mitsyanko 		sinfo->inactive_time = le32_to_cpu(stats->inactive_time);
63398f44cb0SIgor Mitsyanko 	}
63498f44cb0SIgor Mitsyanko 
6354d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(connected_time,
6364d2a7a1cSIgor Mitsyanko 				QLINK_STA_INFO_CONNECTED_TIME)) {
63722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
6384d2a7a1cSIgor Mitsyanko 		sinfo->connected_time = le32_to_cpu(stats->connected_time);
6394d2a7a1cSIgor Mitsyanko 	}
6404d2a7a1cSIgor Mitsyanko 
6414d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) {
64222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
6434d2a7a1cSIgor Mitsyanko 		sinfo->signal = stats->signal - QLINK_RSSI_OFFSET;
6444d2a7a1cSIgor Mitsyanko 	}
6454d2a7a1cSIgor Mitsyanko 
6464d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) {
64722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
6484d2a7a1cSIgor Mitsyanko 		sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET;
6494d2a7a1cSIgor Mitsyanko 	}
6504d2a7a1cSIgor Mitsyanko 
6514d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) {
65222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
6534d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate);
6544d2a7a1cSIgor Mitsyanko 	}
6554d2a7a1cSIgor Mitsyanko 
6564d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) {
65722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
6584d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate);
6594d2a7a1cSIgor Mitsyanko 	}
6604d2a7a1cSIgor Mitsyanko 
6614d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) {
66222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
6634d2a7a1cSIgor Mitsyanko 		qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags);
6644d2a7a1cSIgor Mitsyanko 	}
6654d2a7a1cSIgor Mitsyanko 
6664d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) {
66722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
6684d2a7a1cSIgor Mitsyanko 		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
6694d2a7a1cSIgor Mitsyanko 	}
6704d2a7a1cSIgor Mitsyanko 
6714d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) {
67222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
6734d2a7a1cSIgor Mitsyanko 		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
6744d2a7a1cSIgor Mitsyanko 	}
6754d2a7a1cSIgor Mitsyanko 
6764d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) {
67722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
6784d2a7a1cSIgor Mitsyanko 		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
6794d2a7a1cSIgor Mitsyanko 	}
6804d2a7a1cSIgor Mitsyanko 
6814d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) {
68222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
6834d2a7a1cSIgor Mitsyanko 		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
6844d2a7a1cSIgor Mitsyanko 	}
6854d2a7a1cSIgor Mitsyanko 
6864d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) {
68722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
6884d2a7a1cSIgor Mitsyanko 		sinfo->rx_packets = le32_to_cpu(stats->rx_packets);
6894d2a7a1cSIgor Mitsyanko 	}
6904d2a7a1cSIgor Mitsyanko 
6914d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) {
69222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
6934d2a7a1cSIgor Mitsyanko 		sinfo->tx_packets = le32_to_cpu(stats->tx_packets);
6944d2a7a1cSIgor Mitsyanko 	}
6954d2a7a1cSIgor Mitsyanko 
6964d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) {
69722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);
6984d2a7a1cSIgor Mitsyanko 		sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon);
6994d2a7a1cSIgor Mitsyanko 	}
7004d2a7a1cSIgor Mitsyanko 
7014d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) {
70222d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC);
7034d2a7a1cSIgor Mitsyanko 		sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc);
7044d2a7a1cSIgor Mitsyanko 	}
7054d2a7a1cSIgor Mitsyanko 
7064d2a7a1cSIgor Mitsyanko 	if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) {
70722d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
7084d2a7a1cSIgor Mitsyanko 		sinfo->tx_failed = le32_to_cpu(stats->tx_failed);
7094d2a7a1cSIgor Mitsyanko 	}
7104d2a7a1cSIgor Mitsyanko 
7114d2a7a1cSIgor Mitsyanko #undef qtnf_sta_stat_avail
71298f44cb0SIgor Mitsyanko }
71398f44cb0SIgor Mitsyanko 
71498f44cb0SIgor Mitsyanko int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
71598f44cb0SIgor Mitsyanko 			  struct station_info *sinfo)
71698f44cb0SIgor Mitsyanko {
71798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
71898f44cb0SIgor Mitsyanko 	struct qlink_cmd_get_sta_info *cmd;
71998f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_sta_info *resp;
7201066bd19SSergey Matyukevich 	size_t var_resp_len = 0;
72198f44cb0SIgor Mitsyanko 	int ret = 0;
72298f44cb0SIgor Mitsyanko 
72398f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
72498f44cb0SIgor Mitsyanko 					    QLINK_CMD_GET_STA_INFO,
72598f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
726c93fe71cSSergey Matyukevich 	if (!cmd_skb)
72798f44cb0SIgor Mitsyanko 		return -ENOMEM;
72898f44cb0SIgor Mitsyanko 
72998f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
73098f44cb0SIgor Mitsyanko 
73198f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_get_sta_info *)cmd_skb->data;
73298f44cb0SIgor Mitsyanko 	ether_addr_copy(cmd->sta_addr, sta_mac);
73398f44cb0SIgor Mitsyanko 
73498f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb,
735c6ed298fSSergey Matyukevich 				       sizeof(*resp), &var_resp_len);
736c6ed298fSSergey Matyukevich 	if (ret)
73798f44cb0SIgor Mitsyanko 		goto out;
73898f44cb0SIgor Mitsyanko 
73998f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_sta_info *)resp_skb->data;
74098f44cb0SIgor Mitsyanko 
741c6ed298fSSergey Matyukevich 	if (!ether_addr_equal(sta_mac, resp->sta_addr)) {
74298f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: wrong mac in reply: %pM != %pM\n",
74398f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, resp->sta_addr, sta_mac);
74498f44cb0SIgor Mitsyanko 		ret = -EINVAL;
74598f44cb0SIgor Mitsyanko 		goto out;
74698f44cb0SIgor Mitsyanko 	}
74798f44cb0SIgor Mitsyanko 
7488b0b5f1bSIgor Mitsyanko 	qtnf_cmd_sta_info_parse(sinfo, resp->info, var_resp_len);
74998f44cb0SIgor Mitsyanko 
75098f44cb0SIgor Mitsyanko out:
75198f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
75298f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
75398f44cb0SIgor Mitsyanko 
75498f44cb0SIgor Mitsyanko 	return ret;
75598f44cb0SIgor Mitsyanko }
75698f44cb0SIgor Mitsyanko 
75798f44cb0SIgor Mitsyanko static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif,
75898f44cb0SIgor Mitsyanko 					 enum nl80211_iftype iftype,
759de624a35SSergey Matyukevich 					 int use4addr,
76098f44cb0SIgor Mitsyanko 					 u8 *mac_addr,
76198f44cb0SIgor Mitsyanko 					 enum qlink_cmd_type cmd_type)
76298f44cb0SIgor Mitsyanko {
76398f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
76498f44cb0SIgor Mitsyanko 	struct qlink_cmd_manage_intf *cmd;
76598f44cb0SIgor Mitsyanko 	const struct qlink_resp_manage_intf *resp;
76698f44cb0SIgor Mitsyanko 	int ret = 0;
76798f44cb0SIgor Mitsyanko 
76898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
76998f44cb0SIgor Mitsyanko 					    cmd_type,
77098f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
771c93fe71cSSergey Matyukevich 	if (!cmd_skb)
77298f44cb0SIgor Mitsyanko 		return -ENOMEM;
77398f44cb0SIgor Mitsyanko 
77498f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
77598f44cb0SIgor Mitsyanko 
77698f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data;
777de624a35SSergey Matyukevich 	cmd->intf_info.use4addr = use4addr;
77898f44cb0SIgor Mitsyanko 
77998f44cb0SIgor Mitsyanko 	switch (iftype) {
78098f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_AP:
78198f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP);
78298f44cb0SIgor Mitsyanko 		break;
78398f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_STATION:
78498f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
78598f44cb0SIgor Mitsyanko 		break;
78698f44cb0SIgor Mitsyanko 	default:
78798f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: unsupported type %d\n", vif->mac->macid,
78898f44cb0SIgor Mitsyanko 		       vif->vifid, iftype);
78998f44cb0SIgor Mitsyanko 		ret = -EINVAL;
79098f44cb0SIgor Mitsyanko 		goto out;
79198f44cb0SIgor Mitsyanko 	}
79298f44cb0SIgor Mitsyanko 
79398f44cb0SIgor Mitsyanko 	if (mac_addr)
79498f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->intf_info.mac_addr, mac_addr);
79598f44cb0SIgor Mitsyanko 	else
79698f44cb0SIgor Mitsyanko 		eth_zero_addr(cmd->intf_info.mac_addr);
79798f44cb0SIgor Mitsyanko 
79898f44cb0SIgor Mitsyanko 	ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb,
799c6ed298fSSergey Matyukevich 				       sizeof(*resp), NULL);
800c6ed298fSSergey Matyukevich 	if (ret)
80198f44cb0SIgor Mitsyanko 		goto out;
80298f44cb0SIgor Mitsyanko 
80398f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_manage_intf *)resp_skb->data;
80498f44cb0SIgor Mitsyanko 	ether_addr_copy(vif->mac_addr, resp->intf_info.mac_addr);
80598f44cb0SIgor Mitsyanko 
80698f44cb0SIgor Mitsyanko out:
80798f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
80898f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
80998f44cb0SIgor Mitsyanko 
81098f44cb0SIgor Mitsyanko 	return ret;
81198f44cb0SIgor Mitsyanko }
81298f44cb0SIgor Mitsyanko 
813de624a35SSergey Matyukevich int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype,
814de624a35SSergey Matyukevich 			   int use4addr, u8 *mac_addr)
81598f44cb0SIgor Mitsyanko {
816de624a35SSergey Matyukevich 	return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
81798f44cb0SIgor Mitsyanko 			QLINK_CMD_ADD_INTF);
81898f44cb0SIgor Mitsyanko }
81998f44cb0SIgor Mitsyanko 
82098f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif,
821de624a35SSergey Matyukevich 				   enum nl80211_iftype iftype,
822de624a35SSergey Matyukevich 				   int use4addr,
823de624a35SSergey Matyukevich 				   u8 *mac_addr)
82498f44cb0SIgor Mitsyanko {
82593eeab26SIgor Mitsyanko 	int ret;
82693eeab26SIgor Mitsyanko 
82793eeab26SIgor Mitsyanko 	ret = qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
82898f44cb0SIgor Mitsyanko 					    QLINK_CMD_CHANGE_INTF);
82993eeab26SIgor Mitsyanko 
83093eeab26SIgor Mitsyanko 	/* Regulatory settings may be different for different interface types */
83193eeab26SIgor Mitsyanko 	if (ret == 0 && vif->wdev.iftype != iftype) {
83293eeab26SIgor Mitsyanko 		enum nl80211_band band;
83393eeab26SIgor Mitsyanko 		struct wiphy *wiphy = priv_to_wiphy(vif->mac);
83493eeab26SIgor Mitsyanko 
83593eeab26SIgor Mitsyanko 		for (band = 0; band < NUM_NL80211_BANDS; ++band) {
83693eeab26SIgor Mitsyanko 			if (!wiphy->bands[band])
83793eeab26SIgor Mitsyanko 				continue;
83893eeab26SIgor Mitsyanko 
83993eeab26SIgor Mitsyanko 			qtnf_cmd_band_info_get(vif->mac, wiphy->bands[band]);
84093eeab26SIgor Mitsyanko 		}
84193eeab26SIgor Mitsyanko 	}
84293eeab26SIgor Mitsyanko 
84393eeab26SIgor Mitsyanko 	return ret;
84498f44cb0SIgor Mitsyanko }
84598f44cb0SIgor Mitsyanko 
84698f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_intf(struct qtnf_vif *vif)
84798f44cb0SIgor Mitsyanko {
84898f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
84998f44cb0SIgor Mitsyanko 	struct qlink_cmd_manage_intf *cmd;
85098f44cb0SIgor Mitsyanko 	int ret = 0;
85198f44cb0SIgor Mitsyanko 
85298f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
85398f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_INTF,
85498f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
855c93fe71cSSergey Matyukevich 	if (!cmd_skb)
85698f44cb0SIgor Mitsyanko 		return -ENOMEM;
85798f44cb0SIgor Mitsyanko 
85898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
85998f44cb0SIgor Mitsyanko 
86098f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data;
86198f44cb0SIgor Mitsyanko 
86298f44cb0SIgor Mitsyanko 	switch (vif->wdev.iftype) {
86398f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_AP:
86498f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP);
86598f44cb0SIgor Mitsyanko 		break;
86698f44cb0SIgor Mitsyanko 	case NL80211_IFTYPE_STATION:
86798f44cb0SIgor Mitsyanko 		cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
86898f44cb0SIgor Mitsyanko 		break;
86998f44cb0SIgor Mitsyanko 	default:
87098f44cb0SIgor Mitsyanko 		pr_warn("VIF%u.%u: unsupported iftype %d\n", vif->mac->macid,
87198f44cb0SIgor Mitsyanko 			vif->vifid, vif->wdev.iftype);
87298f44cb0SIgor Mitsyanko 		ret = -EINVAL;
87398f44cb0SIgor Mitsyanko 		goto out;
87498f44cb0SIgor Mitsyanko 	}
87598f44cb0SIgor Mitsyanko 
87698f44cb0SIgor Mitsyanko 	eth_zero_addr(cmd->intf_info.mac_addr);
87798f44cb0SIgor Mitsyanko 
878c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
879c6ed298fSSergey Matyukevich 	if (ret)
88098f44cb0SIgor Mitsyanko 		goto out;
88198f44cb0SIgor Mitsyanko 
88298f44cb0SIgor Mitsyanko out:
88398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
88498f44cb0SIgor Mitsyanko 	return ret;
88598f44cb0SIgor Mitsyanko }
88698f44cb0SIgor Mitsyanko 
88798f44cb0SIgor Mitsyanko static int
88898f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
8894dd07d2bSSergey Matyukevich 			   const struct qlink_resp_get_hw_info *resp,
8904dd07d2bSSergey Matyukevich 			   size_t info_len)
89198f44cb0SIgor Mitsyanko {
89298f44cb0SIgor Mitsyanko 	struct qtnf_hw_info *hwinfo = &bus->hw_info;
8934dd07d2bSSergey Matyukevich 	const struct qlink_tlv_hdr *tlv;
8945ec5b532SVasily Ulyanov 	const char *bld_name = NULL;
8955ec5b532SVasily Ulyanov 	const char *bld_rev = NULL;
8965ec5b532SVasily Ulyanov 	const char *bld_type = NULL;
8975ec5b532SVasily Ulyanov 	const char *bld_label = NULL;
8985ec5b532SVasily Ulyanov 	u32 bld_tmstamp = 0;
8995ec5b532SVasily Ulyanov 	u32 plat_id = 0;
9005ec5b532SVasily Ulyanov 	const char *hw_id = NULL;
9015ec5b532SVasily Ulyanov 	const char *calibration_ver = NULL;
9025ec5b532SVasily Ulyanov 	const char *uboot_ver = NULL;
9035ec5b532SVasily Ulyanov 	u32 hw_ver = 0;
9044dd07d2bSSergey Matyukevich 	u16 tlv_type;
905310cd5ddSIgor Mitsyanko 	u16 tlv_len;
90698f44cb0SIgor Mitsyanko 
90798f44cb0SIgor Mitsyanko 	hwinfo->num_mac = resp->num_mac;
90898f44cb0SIgor Mitsyanko 	hwinfo->mac_bitmap = resp->mac_bitmap;
90998f44cb0SIgor Mitsyanko 	hwinfo->fw_ver = le32_to_cpu(resp->fw_ver);
91098f44cb0SIgor Mitsyanko 	hwinfo->total_tx_chain = resp->total_tx_chain;
91198f44cb0SIgor Mitsyanko 	hwinfo->total_rx_chain = resp->total_rx_chain;
9124dd07d2bSSergey Matyukevich 
9135ec5b532SVasily Ulyanov 	bld_tmstamp = le32_to_cpu(resp->bld_tmstamp);
9145ec5b532SVasily Ulyanov 	plat_id = le32_to_cpu(resp->plat_id);
9155ec5b532SVasily Ulyanov 	hw_ver = le32_to_cpu(resp->hw_ver);
9165ec5b532SVasily Ulyanov 
9178b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, resp->info, info_len) {
9184dd07d2bSSergey Matyukevich 		tlv_type = le16_to_cpu(tlv->type);
919310cd5ddSIgor Mitsyanko 		tlv_len = le16_to_cpu(tlv->len);
9204dd07d2bSSergey Matyukevich 
9214dd07d2bSSergey Matyukevich 		switch (tlv_type) {
9225ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_NAME:
9235ec5b532SVasily Ulyanov 			bld_name = (const void *)tlv->val;
9245ec5b532SVasily Ulyanov 			break;
9255ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_REV:
9265ec5b532SVasily Ulyanov 			bld_rev = (const void *)tlv->val;
9275ec5b532SVasily Ulyanov 			break;
9285ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_TYPE:
9295ec5b532SVasily Ulyanov 			bld_type = (const void *)tlv->val;
9305ec5b532SVasily Ulyanov 			break;
9315ec5b532SVasily Ulyanov 		case QTN_TLV_ID_BUILD_LABEL:
9325ec5b532SVasily Ulyanov 			bld_label = (const void *)tlv->val;
9335ec5b532SVasily Ulyanov 			break;
9345ec5b532SVasily Ulyanov 		case QTN_TLV_ID_HW_ID:
9355ec5b532SVasily Ulyanov 			hw_id = (const void *)tlv->val;
9365ec5b532SVasily Ulyanov 			break;
9375ec5b532SVasily Ulyanov 		case QTN_TLV_ID_CALIBRATION_VER:
9385ec5b532SVasily Ulyanov 			calibration_ver = (const void *)tlv->val;
9395ec5b532SVasily Ulyanov 			break;
9405ec5b532SVasily Ulyanov 		case QTN_TLV_ID_UBOOT_VER:
9415ec5b532SVasily Ulyanov 			uboot_ver = (const void *)tlv->val;
9425ec5b532SVasily Ulyanov 			break;
943310cd5ddSIgor Mitsyanko 		case QTN_TLV_ID_BITMAP:
944310cd5ddSIgor Mitsyanko 			memcpy(hwinfo->hw_capab, tlv->val,
945310cd5ddSIgor Mitsyanko 			       min(sizeof(hwinfo->hw_capab), (size_t)tlv_len));
946310cd5ddSIgor Mitsyanko 			break;
9474dd07d2bSSergey Matyukevich 		default:
9484dd07d2bSSergey Matyukevich 			break;
9494dd07d2bSSergey Matyukevich 		}
9508b0b5f1bSIgor Mitsyanko 	}
9514dd07d2bSSergey Matyukevich 
9528b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, resp->info, info_len)) {
9538b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
9548b0b5f1bSIgor Mitsyanko 		return -EINVAL;
9554dd07d2bSSergey Matyukevich 	}
9564dd07d2bSSergey Matyukevich 
957a3ebb033SIgor Mitsyanko 	pr_info("\nBuild name:            %s\n"
958a3ebb033SIgor Mitsyanko 		"Build revision:        %s\n"
959a3ebb033SIgor Mitsyanko 		"Build type:            %s\n"
960a3ebb033SIgor Mitsyanko 		"Build label:           %s\n"
961a3ebb033SIgor Mitsyanko 		"Build timestamp:       %lu\n"
962a3ebb033SIgor Mitsyanko 		"Platform ID:           %lu\n"
963a3ebb033SIgor Mitsyanko 		"Hardware ID:           %s\n"
964a3ebb033SIgor Mitsyanko 		"Calibration version:   %s\n"
965a3ebb033SIgor Mitsyanko 		"U-Boot version:        %s\n"
966a3ebb033SIgor Mitsyanko 		"Hardware version:      0x%08x\n"
967a3ebb033SIgor Mitsyanko 		"Qlink ver:             %u.%u\n"
968a3ebb033SIgor Mitsyanko 		"MACs map:              %#x\n"
969a3ebb033SIgor Mitsyanko 		"Chains Rx-Tx:          %ux%u\n"
970a3ebb033SIgor Mitsyanko 		"FW version:            0x%x\n",
9715ec5b532SVasily Ulyanov 		bld_name, bld_rev, bld_type, bld_label,
9725ec5b532SVasily Ulyanov 		(unsigned long)bld_tmstamp,
9735ec5b532SVasily Ulyanov 		(unsigned long)plat_id,
974a3ebb033SIgor Mitsyanko 		hw_id, calibration_ver, uboot_ver, hw_ver,
975a3ebb033SIgor Mitsyanko 		QLINK_VER_MAJOR(bus->hw_info.ql_proto_ver),
976a3ebb033SIgor Mitsyanko 		QLINK_VER_MINOR(bus->hw_info.ql_proto_ver),
977a3ebb033SIgor Mitsyanko 		hwinfo->mac_bitmap,
978a3ebb033SIgor Mitsyanko 		hwinfo->total_rx_chain, hwinfo->total_tx_chain,
979a3ebb033SIgor Mitsyanko 		hwinfo->fw_ver);
9805ec5b532SVasily Ulyanov 
9810b419d01SVasily Ulyanov 	strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version));
9820b419d01SVasily Ulyanov 	hwinfo->hw_version = hw_ver;
9830b419d01SVasily Ulyanov 
98498f44cb0SIgor Mitsyanko 	return 0;
98598f44cb0SIgor Mitsyanko }
98698f44cb0SIgor Mitsyanko 
98728b91884SSergey Matyukevich static void
98828b91884SSergey Matyukevich qtnf_parse_wowlan_info(struct qtnf_wmac *mac,
98928b91884SSergey Matyukevich 		       const struct qlink_wowlan_capab_data *wowlan)
99028b91884SSergey Matyukevich {
99128b91884SSergey Matyukevich 	struct qtnf_mac_info *mac_info = &mac->macinfo;
99228b91884SSergey Matyukevich 	const struct qlink_wowlan_support *data1;
99328b91884SSergey Matyukevich 	struct wiphy_wowlan_support *supp;
99428b91884SSergey Matyukevich 
99528b91884SSergey Matyukevich 	supp = kzalloc(sizeof(*supp), GFP_KERNEL);
99628b91884SSergey Matyukevich 	if (!supp)
99728b91884SSergey Matyukevich 		return;
99828b91884SSergey Matyukevich 
99928b91884SSergey Matyukevich 	switch (le16_to_cpu(wowlan->version)) {
100028b91884SSergey Matyukevich 	case 0x1:
100128b91884SSergey Matyukevich 		data1 = (struct qlink_wowlan_support *)wowlan->data;
100228b91884SSergey Matyukevich 
100328b91884SSergey Matyukevich 		supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT;
100428b91884SSergey Matyukevich 		supp->n_patterns = le32_to_cpu(data1->n_patterns);
100528b91884SSergey Matyukevich 		supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len);
100628b91884SSergey Matyukevich 		supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len);
100728b91884SSergey Matyukevich 
100828b91884SSergey Matyukevich 		mac_info->wowlan = supp;
100928b91884SSergey Matyukevich 		break;
101028b91884SSergey Matyukevich 	default:
101128b91884SSergey Matyukevich 		pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n",
101228b91884SSergey Matyukevich 			mac->macid, le16_to_cpu(wowlan->version));
101328b91884SSergey Matyukevich 		kfree(supp);
101428b91884SSergey Matyukevich 		break;
101528b91884SSergey Matyukevich 	}
101628b91884SSergey Matyukevich }
101728b91884SSergey Matyukevich 
1018c698bce0SIgor Mitsyanko static int
1019c698bce0SIgor Mitsyanko qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
1020c698bce0SIgor Mitsyanko 			     const struct qlink_resp_get_mac_info *resp,
1021c698bce0SIgor Mitsyanko 			     size_t tlv_buf_size)
102298f44cb0SIgor Mitsyanko {
1023bc5db734SIgor Mitsyanko 	struct ieee80211_iface_combination *comb = mac->macinfo.if_comb;
1024537faf26SSergey Matyukevich 	size_t n_comb = 0;
1025537faf26SSergey Matyukevich 	struct ieee80211_iface_limit *limits;
1026537faf26SSergey Matyukevich 	const struct qlink_iface_limit_record *rec;
1027537faf26SSergey Matyukevich 	const struct qlink_iface_limit *lim;
102828b91884SSergey Matyukevich 	const struct qlink_wowlan_capab_data *wowlan;
1029537faf26SSergey Matyukevich 	u16 rec_len;
1030537faf26SSergey Matyukevich 	u16 tlv_type;
1031537faf26SSergey Matyukevich 	u16 tlv_value_len;
103298f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
10339cbd5999SVasily Ulyanov 	u8 *ext_capa = NULL;
10349cbd5999SVasily Ulyanov 	u8 *ext_capa_mask = NULL;
10359cbd5999SVasily Ulyanov 	u8 ext_capa_len = 0;
10369cbd5999SVasily Ulyanov 	u8 ext_capa_mask_len = 0;
1037537faf26SSergey Matyukevich 	int i = 0;
1038c698bce0SIgor Mitsyanko 	struct ieee80211_reg_rule *rule;
1039c698bce0SIgor Mitsyanko 	unsigned int rule_idx = 0;
1040c698bce0SIgor Mitsyanko 	const struct qlink_tlv_reg_rule *tlv_rule;
1041c698bce0SIgor Mitsyanko 
1042c698bce0SIgor Mitsyanko 	if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
1043c698bce0SIgor Mitsyanko 		return -E2BIG;
1044c698bce0SIgor Mitsyanko 
10459a1ace64SGustavo A. R. Silva 	mac->rd = kzalloc(struct_size(mac->rd, reg_rules, resp->n_reg_rules),
10469a1ace64SGustavo A. R. Silva 			  GFP_KERNEL);
1047c698bce0SIgor Mitsyanko 	if (!mac->rd)
1048c698bce0SIgor Mitsyanko 		return -ENOMEM;
1049c698bce0SIgor Mitsyanko 
1050c698bce0SIgor Mitsyanko 	mac->rd->n_reg_rules = resp->n_reg_rules;
1051c698bce0SIgor Mitsyanko 	mac->rd->alpha2[0] = resp->alpha2[0];
1052c698bce0SIgor Mitsyanko 	mac->rd->alpha2[1] = resp->alpha2[1];
1053c698bce0SIgor Mitsyanko 
1054c698bce0SIgor Mitsyanko 	switch (resp->dfs_region) {
1055c698bce0SIgor Mitsyanko 	case QLINK_DFS_FCC:
1056c698bce0SIgor Mitsyanko 		mac->rd->dfs_region = NL80211_DFS_FCC;
1057c698bce0SIgor Mitsyanko 		break;
1058c698bce0SIgor Mitsyanko 	case QLINK_DFS_ETSI:
1059c698bce0SIgor Mitsyanko 		mac->rd->dfs_region = NL80211_DFS_ETSI;
1060c698bce0SIgor Mitsyanko 		break;
1061c698bce0SIgor Mitsyanko 	case QLINK_DFS_JP:
1062c698bce0SIgor Mitsyanko 		mac->rd->dfs_region = NL80211_DFS_JP;
1063c698bce0SIgor Mitsyanko 		break;
1064c698bce0SIgor Mitsyanko 	case QLINK_DFS_UNSET:
1065c698bce0SIgor Mitsyanko 	default:
1066c698bce0SIgor Mitsyanko 		mac->rd->dfs_region = NL80211_DFS_UNSET;
1067c698bce0SIgor Mitsyanko 		break;
1068c698bce0SIgor Mitsyanko 	}
106998f44cb0SIgor Mitsyanko 
10708b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, resp->var_info, tlv_buf_size) {
107198f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
107298f44cb0SIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
107398f44cb0SIgor Mitsyanko 
107498f44cb0SIgor Mitsyanko 		switch (tlv_type) {
107598f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_IFACE_LIMIT:
1076537faf26SSergey Matyukevich 			if (unlikely(!comb)) {
1077537faf26SSergey Matyukevich 				pr_warn("MAC%u: no combinations advertised\n",
107898f44cb0SIgor Mitsyanko 					mac->macid);
107998f44cb0SIgor Mitsyanko 				return -EINVAL;
108098f44cb0SIgor Mitsyanko 			}
108198f44cb0SIgor Mitsyanko 
1082537faf26SSergey Matyukevich 			if (n_comb >= mac->macinfo.n_if_comb) {
1083537faf26SSergey Matyukevich 				pr_warn("MAC%u: combinations count exceeded\n",
108498f44cb0SIgor Mitsyanko 					mac->macid);
1085537faf26SSergey Matyukevich 				n_comb++;
1086537faf26SSergey Matyukevich 				break;
1087537faf26SSergey Matyukevich 			}
1088537faf26SSergey Matyukevich 
1089537faf26SSergey Matyukevich 			rec = (void *)tlv->val;
1090537faf26SSergey Matyukevich 			rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim);
1091537faf26SSergey Matyukevich 
1092537faf26SSergey Matyukevich 			if (unlikely(tlv_value_len != rec_len)) {
1093537faf26SSergey Matyukevich 				pr_warn("MAC%u: record %zu size mismatch\n",
1094537faf26SSergey Matyukevich 					mac->macid, n_comb);
109598f44cb0SIgor Mitsyanko 				return -EINVAL;
109698f44cb0SIgor Mitsyanko 			}
109798f44cb0SIgor Mitsyanko 
10986396bb22SKees Cook 			limits = kcalloc(rec->n_limits, sizeof(*limits),
1099537faf26SSergey Matyukevich 					 GFP_KERNEL);
1100537faf26SSergey Matyukevich 			if (!limits)
1101537faf26SSergey Matyukevich 				return -ENOMEM;
110241c8fa0cSSergey Matyukevich 
1103537faf26SSergey Matyukevich 			comb[n_comb].num_different_channels =
1104537faf26SSergey Matyukevich 				rec->num_different_channels;
1105537faf26SSergey Matyukevich 			comb[n_comb].max_interfaces =
1106537faf26SSergey Matyukevich 				le16_to_cpu(rec->max_interfaces);
1107537faf26SSergey Matyukevich 			comb[n_comb].n_limits = rec->n_limits;
1108537faf26SSergey Matyukevich 			comb[n_comb].limits = limits;
110998f44cb0SIgor Mitsyanko 
1110537faf26SSergey Matyukevich 			for (i = 0; i < rec->n_limits; i++) {
1111537faf26SSergey Matyukevich 				lim = &rec->limits[i];
1112537faf26SSergey Matyukevich 				limits[i].max = le16_to_cpu(lim->max_num);
1113537faf26SSergey Matyukevich 				limits[i].types =
1114537faf26SSergey Matyukevich 					qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type));
1115537faf26SSergey Matyukevich 				pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n",
1116537faf26SSergey Matyukevich 					 mac->macid, n_comb,
1117537faf26SSergey Matyukevich 					 limits[i].max, limits[i].types);
1118537faf26SSergey Matyukevich 			}
111998f44cb0SIgor Mitsyanko 
1120537faf26SSergey Matyukevich 			n_comb++;
112198f44cb0SIgor Mitsyanko 			break;
11229cbd5999SVasily Ulyanov 		case WLAN_EID_EXT_CAPABILITY:
11239cbd5999SVasily Ulyanov 			if (unlikely(tlv_value_len > U8_MAX))
11249cbd5999SVasily Ulyanov 				return -EINVAL;
11259cbd5999SVasily Ulyanov 			ext_capa = (u8 *)tlv->val;
11269cbd5999SVasily Ulyanov 			ext_capa_len = tlv_value_len;
11279cbd5999SVasily Ulyanov 			break;
11289cbd5999SVasily Ulyanov 		case QTN_TLV_ID_EXT_CAPABILITY_MASK:
11299cbd5999SVasily Ulyanov 			if (unlikely(tlv_value_len > U8_MAX))
11309cbd5999SVasily Ulyanov 				return -EINVAL;
11319cbd5999SVasily Ulyanov 			ext_capa_mask = (u8 *)tlv->val;
11329cbd5999SVasily Ulyanov 			ext_capa_mask_len = tlv_value_len;
11339cbd5999SVasily Ulyanov 			break;
113428b91884SSergey Matyukevich 		case QTN_TLV_ID_WOWLAN_CAPAB:
113528b91884SSergey Matyukevich 			if (tlv_value_len < sizeof(*wowlan))
113628b91884SSergey Matyukevich 				return -EINVAL;
113728b91884SSergey Matyukevich 
113828b91884SSergey Matyukevich 			wowlan = (void *)tlv->val;
113928b91884SSergey Matyukevich 			if (!le16_to_cpu(wowlan->len)) {
114028b91884SSergey Matyukevich 				pr_warn("MAC%u: skip empty WoWLAN data\n",
114128b91884SSergey Matyukevich 					mac->macid);
114228b91884SSergey Matyukevich 				break;
114328b91884SSergey Matyukevich 			}
114428b91884SSergey Matyukevich 
114528b91884SSergey Matyukevich 			rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len);
114628b91884SSergey Matyukevich 			if (unlikely(tlv_value_len != rec_len)) {
114728b91884SSergey Matyukevich 				pr_warn("MAC%u: WoWLAN data size mismatch\n",
114828b91884SSergey Matyukevich 					mac->macid);
114928b91884SSergey Matyukevich 				return -EINVAL;
115028b91884SSergey Matyukevich 			}
115128b91884SSergey Matyukevich 
115228b91884SSergey Matyukevich 			kfree(mac->macinfo.wowlan);
115328b91884SSergey Matyukevich 			mac->macinfo.wowlan = NULL;
115428b91884SSergey Matyukevich 			qtnf_parse_wowlan_info(mac, wowlan);
115528b91884SSergey Matyukevich 			break;
1156c698bce0SIgor Mitsyanko 		case QTN_TLV_ID_REG_RULE:
1157c698bce0SIgor Mitsyanko 			if (rule_idx >= resp->n_reg_rules) {
1158c698bce0SIgor Mitsyanko 				pr_warn("unexpected number of rules: %u\n",
1159c698bce0SIgor Mitsyanko 					resp->n_reg_rules);
1160c698bce0SIgor Mitsyanko 				return -EINVAL;
1161c698bce0SIgor Mitsyanko 			}
1162c698bce0SIgor Mitsyanko 
1163c698bce0SIgor Mitsyanko 			if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) {
1164c698bce0SIgor Mitsyanko 				pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
1165c698bce0SIgor Mitsyanko 					tlv_type, tlv_value_len);
1166c698bce0SIgor Mitsyanko 				return -EINVAL;
1167c698bce0SIgor Mitsyanko 			}
1168c698bce0SIgor Mitsyanko 
1169c698bce0SIgor Mitsyanko 			tlv_rule = (const struct qlink_tlv_reg_rule *)tlv;
1170c698bce0SIgor Mitsyanko 			rule = &mac->rd->reg_rules[rule_idx++];
1171c698bce0SIgor Mitsyanko 			qlink_utils_regrule_q2nl(rule, tlv_rule);
1172c698bce0SIgor Mitsyanko 			break;
117398f44cb0SIgor Mitsyanko 		default:
117428b91884SSergey Matyukevich 			pr_warn("MAC%u: unknown TLV type %u\n",
117528b91884SSergey Matyukevich 				mac->macid, tlv_type);
117698f44cb0SIgor Mitsyanko 			break;
117798f44cb0SIgor Mitsyanko 		}
117898f44cb0SIgor Mitsyanko 	}
117998f44cb0SIgor Mitsyanko 
11808b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, resp->var_info, tlv_buf_size)) {
11818b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
118298f44cb0SIgor Mitsyanko 		return -EINVAL;
118398f44cb0SIgor Mitsyanko 	}
118498f44cb0SIgor Mitsyanko 
1185537faf26SSergey Matyukevich 	if (mac->macinfo.n_if_comb != n_comb) {
118698f44cb0SIgor Mitsyanko 		pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n",
1187537faf26SSergey Matyukevich 		       mac->macid, mac->macinfo.n_if_comb, n_comb);
118898f44cb0SIgor Mitsyanko 		return -EINVAL;
118998f44cb0SIgor Mitsyanko 	}
119098f44cb0SIgor Mitsyanko 
11919cbd5999SVasily Ulyanov 	if (ext_capa_len != ext_capa_mask_len) {
11929cbd5999SVasily Ulyanov 		pr_err("MAC%u: ext_capa/_mask lengths mismatch: %u != %u\n",
11939cbd5999SVasily Ulyanov 		       mac->macid, ext_capa_len, ext_capa_mask_len);
11949cbd5999SVasily Ulyanov 		return -EINVAL;
11959cbd5999SVasily Ulyanov 	}
11969cbd5999SVasily Ulyanov 
1197c698bce0SIgor Mitsyanko 	if (rule_idx != resp->n_reg_rules) {
1198c698bce0SIgor Mitsyanko 		pr_warn("unexpected number of rules: expected %u got %u\n",
1199c698bce0SIgor Mitsyanko 			resp->n_reg_rules, rule_idx);
1200c698bce0SIgor Mitsyanko 		return -EINVAL;
1201c698bce0SIgor Mitsyanko 	}
1202c698bce0SIgor Mitsyanko 
12039cbd5999SVasily Ulyanov 	if (ext_capa_len > 0) {
12049cbd5999SVasily Ulyanov 		ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL);
12059cbd5999SVasily Ulyanov 		if (!ext_capa)
12069cbd5999SVasily Ulyanov 			return -ENOMEM;
12079cbd5999SVasily Ulyanov 
12089cbd5999SVasily Ulyanov 		ext_capa_mask =
12099cbd5999SVasily Ulyanov 			kmemdup(ext_capa_mask, ext_capa_mask_len, GFP_KERNEL);
12109cbd5999SVasily Ulyanov 		if (!ext_capa_mask) {
12119cbd5999SVasily Ulyanov 			kfree(ext_capa);
12129cbd5999SVasily Ulyanov 			return -ENOMEM;
12139cbd5999SVasily Ulyanov 		}
12149cbd5999SVasily Ulyanov 	} else {
12159cbd5999SVasily Ulyanov 		ext_capa = NULL;
12169cbd5999SVasily Ulyanov 		ext_capa_mask = NULL;
12179cbd5999SVasily Ulyanov 	}
12189cbd5999SVasily Ulyanov 
1219ab1c64a1SSergey Matyukevich 	qtnf_mac_ext_caps_free(mac);
12209cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities = ext_capa;
12219cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities_mask = ext_capa_mask;
12229cbd5999SVasily Ulyanov 	mac->macinfo.extended_capabilities_len = ext_capa_len;
12239cbd5999SVasily Ulyanov 
122498f44cb0SIgor Mitsyanko 	return 0;
122598f44cb0SIgor Mitsyanko }
122698f44cb0SIgor Mitsyanko 
1227bc5db734SIgor Mitsyanko static int
122898f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac,
122998f44cb0SIgor Mitsyanko 			    const struct qlink_resp_get_mac_info *resp_info)
123098f44cb0SIgor Mitsyanko {
123198f44cb0SIgor Mitsyanko 	struct qtnf_mac_info *mac_info;
123298f44cb0SIgor Mitsyanko 	struct qtnf_vif *vif;
123398f44cb0SIgor Mitsyanko 
1234bc5db734SIgor Mitsyanko 	qtnf_mac_iface_comb_free(mac);
1235bc5db734SIgor Mitsyanko 
123698f44cb0SIgor Mitsyanko 	mac_info = &mac->macinfo;
123798f44cb0SIgor Mitsyanko 
123898f44cb0SIgor Mitsyanko 	mac_info->bands_cap = resp_info->bands_cap;
12394e14e76cSIgor Mitsyanko 	ether_addr_copy(mac->macaddr, resp_info->dev_mac);
124098f44cb0SIgor Mitsyanko 
124198f44cb0SIgor Mitsyanko 	vif = qtnf_mac_get_base_vif(mac);
124298f44cb0SIgor Mitsyanko 	if (vif)
124398f44cb0SIgor Mitsyanko 		ether_addr_copy(vif->mac_addr, mac->macaddr);
124498f44cb0SIgor Mitsyanko 	else
124598f44cb0SIgor Mitsyanko 		pr_err("could not get valid base vif\n");
124698f44cb0SIgor Mitsyanko 
124798f44cb0SIgor Mitsyanko 	mac_info->num_tx_chain = resp_info->num_tx_chain;
124898f44cb0SIgor Mitsyanko 	mac_info->num_rx_chain = resp_info->num_rx_chain;
124998f44cb0SIgor Mitsyanko 
125098f44cb0SIgor Mitsyanko 	mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta);
125198f44cb0SIgor Mitsyanko 	mac_info->radar_detect_widths =
125298f44cb0SIgor Mitsyanko 			qlink_chan_width_mask_to_nl(le16_to_cpu(
125398f44cb0SIgor Mitsyanko 					resp_info->radar_detect_widths));
1254e70cf22bSIgor Mitsyanko 	mac_info->max_acl_mac_addrs = le16_to_cpu(resp_info->max_acl_mac_addrs);
1255e70cf22bSIgor Mitsyanko 	mac_info->frag_thr = le32_to_cpu(resp_info->frag_threshold);
1256e70cf22bSIgor Mitsyanko 	mac_info->rts_thr = le32_to_cpu(resp_info->rts_threshold);
1257e70cf22bSIgor Mitsyanko 	mac_info->sretry_limit = resp_info->retry_short;
1258e70cf22bSIgor Mitsyanko 	mac_info->lretry_limit = resp_info->retry_long;
1259e70cf22bSIgor Mitsyanko 	mac_info->coverage_class = resp_info->coverage_class;
12600d18a9c0SIgor Mitsyanko 	mac_info->max_scan_ssids = resp_info->max_scan_ssids;
126198f44cb0SIgor Mitsyanko 
1262d42df85fSIgor Mitsyanko 	memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask,
1263d42df85fSIgor Mitsyanko 	       sizeof(mac_info->ht_cap_mod_mask));
1264d42df85fSIgor Mitsyanko 	memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask,
1265d42df85fSIgor Mitsyanko 	       sizeof(mac_info->vht_cap_mod_mask));
1266bc5db734SIgor Mitsyanko 
1267bc5db734SIgor Mitsyanko 	mac_info->n_if_comb = resp_info->n_iface_combinations;
1268bc5db734SIgor Mitsyanko 	mac_info->if_comb = kcalloc(mac->macinfo.n_if_comb,
1269bc5db734SIgor Mitsyanko 				    sizeof(*mac->macinfo.if_comb),
1270bc5db734SIgor Mitsyanko 				    GFP_KERNEL);
1271bc5db734SIgor Mitsyanko 
1272bc5db734SIgor Mitsyanko 	if (!mac->macinfo.if_comb)
1273bc5db734SIgor Mitsyanko 		return -ENOMEM;
1274bc5db734SIgor Mitsyanko 
1275bc5db734SIgor Mitsyanko 	return 0;
127698f44cb0SIgor Mitsyanko }
127798f44cb0SIgor Mitsyanko 
1278e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_htcap(const u8 *info,
1279e294cbfdSIgor Mitsyanko 					  struct ieee80211_sta_ht_cap *bcap)
1280e294cbfdSIgor Mitsyanko {
1281e294cbfdSIgor Mitsyanko 	const struct ieee80211_ht_cap *ht_cap =
1282e294cbfdSIgor Mitsyanko 		(const struct ieee80211_ht_cap *)info;
1283e294cbfdSIgor Mitsyanko 
1284e294cbfdSIgor Mitsyanko 	bcap->ht_supported = true;
1285e294cbfdSIgor Mitsyanko 	bcap->cap = le16_to_cpu(ht_cap->cap_info);
1286e294cbfdSIgor Mitsyanko 	bcap->ampdu_factor =
1287e294cbfdSIgor Mitsyanko 		ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
1288e294cbfdSIgor Mitsyanko 	bcap->ampdu_density =
1289e294cbfdSIgor Mitsyanko 		(ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >>
1290e294cbfdSIgor Mitsyanko 		IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
1291e294cbfdSIgor Mitsyanko 	memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs));
1292e294cbfdSIgor Mitsyanko }
1293e294cbfdSIgor Mitsyanko 
1294e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info,
1295e294cbfdSIgor Mitsyanko 					   struct ieee80211_sta_vht_cap *bcap)
1296e294cbfdSIgor Mitsyanko {
1297e294cbfdSIgor Mitsyanko 	const struct ieee80211_vht_cap *vht_cap =
1298e294cbfdSIgor Mitsyanko 		(const struct ieee80211_vht_cap *)info;
1299e294cbfdSIgor Mitsyanko 
1300e294cbfdSIgor Mitsyanko 	bcap->vht_supported = true;
1301e294cbfdSIgor Mitsyanko 	bcap->cap = le32_to_cpu(vht_cap->vht_cap_info);
1302e294cbfdSIgor Mitsyanko 	memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs));
1303e294cbfdSIgor Mitsyanko }
1304e294cbfdSIgor Mitsyanko 
1305df0af4c7SMikhail Karpenko static void qtnf_cmd_conv_iftype(struct ieee80211_sband_iftype_data
1306df0af4c7SMikhail Karpenko 				  *iftype_data,
1307df0af4c7SMikhail Karpenko 				  const struct qlink_sband_iftype_data
1308df0af4c7SMikhail Karpenko 				  *qlink_data)
1309df0af4c7SMikhail Karpenko {
1310df0af4c7SMikhail Karpenko 	iftype_data->types_mask = le16_to_cpu(qlink_data->types_mask);
1311df0af4c7SMikhail Karpenko 
1312df0af4c7SMikhail Karpenko 	iftype_data->he_cap.has_he = true;
1313df0af4c7SMikhail Karpenko 	memcpy(&iftype_data->he_cap.he_cap_elem, &qlink_data->he_cap_elem,
1314df0af4c7SMikhail Karpenko 	       sizeof(qlink_data->he_cap_elem));
1315df0af4c7SMikhail Karpenko 	memcpy(iftype_data->he_cap.ppe_thres, qlink_data->ppe_thres,
1316df0af4c7SMikhail Karpenko 	       ARRAY_SIZE(qlink_data->ppe_thres));
1317df0af4c7SMikhail Karpenko 
1318df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80 =
1319df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.rx_mcs_80;
1320df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80 =
1321df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.tx_mcs_80;
1322df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160 =
1323df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.rx_mcs_160;
1324df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160 =
1325df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.tx_mcs_160;
1326df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80p80 =
1327df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.rx_mcs_80p80;
1328df0af4c7SMikhail Karpenko 	iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80p80 =
1329df0af4c7SMikhail Karpenko 		qlink_data->he_mcs_nss_supp.tx_mcs_80p80;
1330df0af4c7SMikhail Karpenko }
1331df0af4c7SMikhail Karpenko 
1332df0af4c7SMikhail Karpenko static int qtnf_cmd_band_fill_iftype(const u8 *data,
1333df0af4c7SMikhail Karpenko 				     struct ieee80211_supported_band *band)
1334df0af4c7SMikhail Karpenko {
1335df0af4c7SMikhail Karpenko 	unsigned int i;
1336df0af4c7SMikhail Karpenko 	struct ieee80211_sband_iftype_data *iftype_data;
1337df0af4c7SMikhail Karpenko 	const struct qlink_tlv_iftype_data *tlv =
1338df0af4c7SMikhail Karpenko 		(const struct qlink_tlv_iftype_data *)data;
1339df0af4c7SMikhail Karpenko 	size_t payload_len = tlv->n_iftype_data * sizeof(*tlv->iftype_data) +
1340df0af4c7SMikhail Karpenko 		sizeof(*tlv) -
1341df0af4c7SMikhail Karpenko 		sizeof(struct qlink_tlv_hdr);
1342df0af4c7SMikhail Karpenko 
1343df0af4c7SMikhail Karpenko 	if (tlv->hdr.len != cpu_to_le16(payload_len)) {
1344df0af4c7SMikhail Karpenko 		pr_err("bad IFTYPE_DATA TLV len %u\n", tlv->hdr.len);
1345df0af4c7SMikhail Karpenko 		return -EINVAL;
1346df0af4c7SMikhail Karpenko 	}
1347df0af4c7SMikhail Karpenko 
1348df0af4c7SMikhail Karpenko 	kfree(band->iftype_data);
1349df0af4c7SMikhail Karpenko 	band->iftype_data = NULL;
1350df0af4c7SMikhail Karpenko 	band->n_iftype_data = tlv->n_iftype_data;
1351df0af4c7SMikhail Karpenko 	if (band->n_iftype_data == 0)
1352df0af4c7SMikhail Karpenko 		return 0;
1353df0af4c7SMikhail Karpenko 
1354df0af4c7SMikhail Karpenko 	iftype_data = kcalloc(band->n_iftype_data, sizeof(*iftype_data),
1355df0af4c7SMikhail Karpenko 			      GFP_KERNEL);
1356df0af4c7SMikhail Karpenko 	if (!iftype_data) {
1357df0af4c7SMikhail Karpenko 		band->n_iftype_data = 0;
1358df0af4c7SMikhail Karpenko 		return -ENOMEM;
1359df0af4c7SMikhail Karpenko 	}
1360df0af4c7SMikhail Karpenko 	band->iftype_data = iftype_data;
1361df0af4c7SMikhail Karpenko 
1362df0af4c7SMikhail Karpenko 	for (i = 0; i < band->n_iftype_data; i++)
1363df0af4c7SMikhail Karpenko 		qtnf_cmd_conv_iftype(iftype_data++, &tlv->iftype_data[i]);
1364df0af4c7SMikhail Karpenko 
1365df0af4c7SMikhail Karpenko 	return 0;
1366df0af4c7SMikhail Karpenko }
1367df0af4c7SMikhail Karpenko 
136898f44cb0SIgor Mitsyanko static int
1369e294cbfdSIgor Mitsyanko qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
1370e294cbfdSIgor Mitsyanko 			     struct qlink_resp_band_info_get *resp,
137198f44cb0SIgor Mitsyanko 			     size_t payload_len)
137298f44cb0SIgor Mitsyanko {
137398f44cb0SIgor Mitsyanko 	u16 tlv_type;
1374e294cbfdSIgor Mitsyanko 	size_t tlv_dlen;
137598f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
13765bf374abSSergey Matyukevich 	const struct qlink_channel *qchan;
137798f44cb0SIgor Mitsyanko 	struct ieee80211_channel *chan;
137898f44cb0SIgor Mitsyanko 	unsigned int chidx = 0;
137998f44cb0SIgor Mitsyanko 	u32 qflags;
1380df0af4c7SMikhail Karpenko 	int ret = -EINVAL;
138198f44cb0SIgor Mitsyanko 
1382e294cbfdSIgor Mitsyanko 	memset(&band->ht_cap, 0, sizeof(band->ht_cap));
1383e294cbfdSIgor Mitsyanko 	memset(&band->vht_cap, 0, sizeof(band->vht_cap));
1384e294cbfdSIgor Mitsyanko 
13854dd07d2bSSergey Matyukevich 	if (band->channels) {
13864dd07d2bSSergey Matyukevich 		if (band->n_channels == resp->num_chans) {
13874dd07d2bSSergey Matyukevich 			memset(band->channels, 0,
13884dd07d2bSSergey Matyukevich 			       sizeof(*band->channels) * band->n_channels);
13894dd07d2bSSergey Matyukevich 		} else {
139098f44cb0SIgor Mitsyanko 			kfree(band->channels);
13914dd07d2bSSergey Matyukevich 			band->n_channels = 0;
139298f44cb0SIgor Mitsyanko 			band->channels = NULL;
13934dd07d2bSSergey Matyukevich 		}
13944dd07d2bSSergey Matyukevich 	}
139598f44cb0SIgor Mitsyanko 
139698f44cb0SIgor Mitsyanko 	band->n_channels = resp->num_chans;
139798f44cb0SIgor Mitsyanko 	if (band->n_channels == 0)
139898f44cb0SIgor Mitsyanko 		return 0;
139998f44cb0SIgor Mitsyanko 
14004dd07d2bSSergey Matyukevich 	if (!band->channels)
14014dd07d2bSSergey Matyukevich 		band->channels = kcalloc(band->n_channels, sizeof(*chan),
14024dd07d2bSSergey Matyukevich 					 GFP_KERNEL);
140398f44cb0SIgor Mitsyanko 	if (!band->channels) {
140498f44cb0SIgor Mitsyanko 		band->n_channels = 0;
140598f44cb0SIgor Mitsyanko 		return -ENOMEM;
140698f44cb0SIgor Mitsyanko 	}
140798f44cb0SIgor Mitsyanko 
14088b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, resp->info, payload_len) {
140998f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
1410e294cbfdSIgor Mitsyanko 		tlv_dlen = le16_to_cpu(tlv->len);
141198f44cb0SIgor Mitsyanko 
141298f44cb0SIgor Mitsyanko 		switch (tlv_type) {
141398f44cb0SIgor Mitsyanko 		case QTN_TLV_ID_CHANNEL:
14145bf374abSSergey Matyukevich 			if (unlikely(tlv_dlen != sizeof(*qchan))) {
141598f44cb0SIgor Mitsyanko 				pr_err("invalid channel TLV len %zu\n",
14168b0b5f1bSIgor Mitsyanko 				       tlv_dlen);
141798f44cb0SIgor Mitsyanko 				goto error_ret;
141898f44cb0SIgor Mitsyanko 			}
141998f44cb0SIgor Mitsyanko 
142098f44cb0SIgor Mitsyanko 			if (chidx == band->n_channels) {
142198f44cb0SIgor Mitsyanko 				pr_err("too many channel TLVs\n");
142298f44cb0SIgor Mitsyanko 				goto error_ret;
142398f44cb0SIgor Mitsyanko 			}
142498f44cb0SIgor Mitsyanko 
14255bf374abSSergey Matyukevich 			qchan = (const struct qlink_channel *)tlv->val;
142698f44cb0SIgor Mitsyanko 			chan = &band->channels[chidx++];
142798f44cb0SIgor Mitsyanko 			qflags = le32_to_cpu(qchan->flags);
142898f44cb0SIgor Mitsyanko 
142998f44cb0SIgor Mitsyanko 			chan->hw_value = le16_to_cpu(qchan->hw_value);
143098f44cb0SIgor Mitsyanko 			chan->band = band->band;
143198f44cb0SIgor Mitsyanko 			chan->center_freq = le16_to_cpu(qchan->center_freq);
143298f44cb0SIgor Mitsyanko 			chan->max_antenna_gain = (int)qchan->max_antenna_gain;
143398f44cb0SIgor Mitsyanko 			chan->max_power = (int)qchan->max_power;
143498f44cb0SIgor Mitsyanko 			chan->max_reg_power = (int)qchan->max_reg_power;
143598f44cb0SIgor Mitsyanko 			chan->beacon_found = qchan->beacon_found;
143698f44cb0SIgor Mitsyanko 			chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms);
143798f44cb0SIgor Mitsyanko 			chan->flags = 0;
143898f44cb0SIgor Mitsyanko 
143998f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_DISABLED)
144098f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_DISABLED;
144198f44cb0SIgor Mitsyanko 
144298f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_IR)
144398f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_IR;
144498f44cb0SIgor Mitsyanko 
144598f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_HT40PLUS)
144698f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_HT40PLUS;
144798f44cb0SIgor Mitsyanko 
144898f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_HT40MINUS)
144998f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_HT40MINUS;
145098f44cb0SIgor Mitsyanko 
145198f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_OFDM)
145298f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_OFDM;
145398f44cb0SIgor Mitsyanko 
145498f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_80MHZ)
145598f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_80MHZ;
145698f44cb0SIgor Mitsyanko 
145798f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_160MHZ)
145898f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_160MHZ;
145998f44cb0SIgor Mitsyanko 
146098f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_INDOOR_ONLY)
146198f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_INDOOR_ONLY;
146298f44cb0SIgor Mitsyanko 
146398f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_IR_CONCURRENT)
146498f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_IR_CONCURRENT;
146598f44cb0SIgor Mitsyanko 
146698f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_20MHZ)
146798f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_20MHZ;
146898f44cb0SIgor Mitsyanko 
146998f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_NO_10MHZ)
147098f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_NO_10MHZ;
147198f44cb0SIgor Mitsyanko 
147298f44cb0SIgor Mitsyanko 			if (qflags & QLINK_CHAN_RADAR) {
147398f44cb0SIgor Mitsyanko 				chan->flags |= IEEE80211_CHAN_RADAR;
147498f44cb0SIgor Mitsyanko 				chan->dfs_state_entered = jiffies;
147598f44cb0SIgor Mitsyanko 
147698f44cb0SIgor Mitsyanko 				if (qchan->dfs_state == QLINK_DFS_USABLE)
147798f44cb0SIgor Mitsyanko 					chan->dfs_state = NL80211_DFS_USABLE;
147898f44cb0SIgor Mitsyanko 				else if (qchan->dfs_state ==
147998f44cb0SIgor Mitsyanko 					QLINK_DFS_AVAILABLE)
148098f44cb0SIgor Mitsyanko 					chan->dfs_state = NL80211_DFS_AVAILABLE;
148198f44cb0SIgor Mitsyanko 				else
148298f44cb0SIgor Mitsyanko 					chan->dfs_state =
148398f44cb0SIgor Mitsyanko 						NL80211_DFS_UNAVAILABLE;
148498f44cb0SIgor Mitsyanko 			}
148598f44cb0SIgor Mitsyanko 
148698f44cb0SIgor Mitsyanko 			pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n",
148798f44cb0SIgor Mitsyanko 				 chan->hw_value, chan->flags, chan->max_power,
148898f44cb0SIgor Mitsyanko 				 chan->max_reg_power);
148998f44cb0SIgor Mitsyanko 			break;
1490e294cbfdSIgor Mitsyanko 		case WLAN_EID_HT_CAPABILITY:
1491e294cbfdSIgor Mitsyanko 			if (unlikely(tlv_dlen !=
1492e294cbfdSIgor Mitsyanko 				     sizeof(struct ieee80211_ht_cap))) {
1493e294cbfdSIgor Mitsyanko 				pr_err("bad HTCAP TLV len %zu\n", tlv_dlen);
1494e294cbfdSIgor Mitsyanko 				goto error_ret;
1495e294cbfdSIgor Mitsyanko 			}
1496e294cbfdSIgor Mitsyanko 
1497e294cbfdSIgor Mitsyanko 			qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap);
1498e294cbfdSIgor Mitsyanko 			break;
1499e294cbfdSIgor Mitsyanko 		case WLAN_EID_VHT_CAPABILITY:
1500e294cbfdSIgor Mitsyanko 			if (unlikely(tlv_dlen !=
1501e294cbfdSIgor Mitsyanko 				     sizeof(struct ieee80211_vht_cap))) {
1502e294cbfdSIgor Mitsyanko 				pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen);
1503e294cbfdSIgor Mitsyanko 				goto error_ret;
1504e294cbfdSIgor Mitsyanko 			}
1505e294cbfdSIgor Mitsyanko 
1506e294cbfdSIgor Mitsyanko 			qtnf_cmd_resp_band_fill_vhtcap(tlv->val,
1507e294cbfdSIgor Mitsyanko 						       &band->vht_cap);
1508e294cbfdSIgor Mitsyanko 			break;
1509df0af4c7SMikhail Karpenko 		case QTN_TLV_ID_IFTYPE_DATA:
1510df0af4c7SMikhail Karpenko 			ret = qtnf_cmd_band_fill_iftype((const uint8_t *)tlv,
1511df0af4c7SMikhail Karpenko 							band);
1512df0af4c7SMikhail Karpenko 			if (ret)
1513df0af4c7SMikhail Karpenko 				goto error_ret;
1514df0af4c7SMikhail Karpenko 			break;
151598f44cb0SIgor Mitsyanko 		default:
151698f44cb0SIgor Mitsyanko 			pr_warn("unknown TLV type: %#x\n", tlv_type);
151798f44cb0SIgor Mitsyanko 			break;
151898f44cb0SIgor Mitsyanko 		}
151998f44cb0SIgor Mitsyanko 	}
152098f44cb0SIgor Mitsyanko 
15218b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, resp->info, payload_len)) {
15228b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
152398f44cb0SIgor Mitsyanko 		goto error_ret;
152498f44cb0SIgor Mitsyanko 	}
152598f44cb0SIgor Mitsyanko 
152698f44cb0SIgor Mitsyanko 	if (band->n_channels != chidx) {
152798f44cb0SIgor Mitsyanko 		pr_err("channel count mismatch: reported=%d, parsed=%d\n",
152898f44cb0SIgor Mitsyanko 		       band->n_channels, chidx);
152998f44cb0SIgor Mitsyanko 		goto error_ret;
153098f44cb0SIgor Mitsyanko 	}
153198f44cb0SIgor Mitsyanko 
153298f44cb0SIgor Mitsyanko 	return 0;
153398f44cb0SIgor Mitsyanko 
153498f44cb0SIgor Mitsyanko error_ret:
153598f44cb0SIgor Mitsyanko 	kfree(band->channels);
153698f44cb0SIgor Mitsyanko 	band->channels = NULL;
153798f44cb0SIgor Mitsyanko 	band->n_channels = 0;
153898f44cb0SIgor Mitsyanko 
1539df0af4c7SMikhail Karpenko 	return ret;
154098f44cb0SIgor Mitsyanko }
154198f44cb0SIgor Mitsyanko 
154298f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
154398f44cb0SIgor Mitsyanko {
154498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
154598f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_mac_info *resp;
15461066bd19SSergey Matyukevich 	size_t var_data_len = 0;
154798f44cb0SIgor Mitsyanko 	int ret = 0;
154898f44cb0SIgor Mitsyanko 
154998f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
155098f44cb0SIgor Mitsyanko 					    QLINK_CMD_MAC_INFO,
155198f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
1552c93fe71cSSergey Matyukevich 	if (!cmd_skb)
155398f44cb0SIgor Mitsyanko 		return -ENOMEM;
155498f44cb0SIgor Mitsyanko 
155598f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
1556c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
155798f44cb0SIgor Mitsyanko 				       sizeof(*resp), &var_data_len);
1558c6ed298fSSergey Matyukevich 	if (ret)
155998f44cb0SIgor Mitsyanko 		goto out;
156098f44cb0SIgor Mitsyanko 
156198f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_mac_info *)resp_skb->data;
1562bc5db734SIgor Mitsyanko 	ret = qtnf_cmd_resp_proc_mac_info(mac, resp);
1563bc5db734SIgor Mitsyanko 	if (ret)
1564bc5db734SIgor Mitsyanko 		goto out;
1565bc5db734SIgor Mitsyanko 
1566c698bce0SIgor Mitsyanko 	ret = qtnf_parse_variable_mac_info(mac, resp, var_data_len);
156798f44cb0SIgor Mitsyanko 
156898f44cb0SIgor Mitsyanko out:
156998f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
157098f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
157198f44cb0SIgor Mitsyanko 
157298f44cb0SIgor Mitsyanko 	return ret;
157398f44cb0SIgor Mitsyanko }
157498f44cb0SIgor Mitsyanko 
157598f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus)
157698f44cb0SIgor Mitsyanko {
157798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
157898f44cb0SIgor Mitsyanko 	const struct qlink_resp_get_hw_info *resp;
15791066bd19SSergey Matyukevich 	size_t info_len = 0;
158098f44cb0SIgor Mitsyanko 	int ret = 0;
158198f44cb0SIgor Mitsyanko 
158298f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
158398f44cb0SIgor Mitsyanko 					    QLINK_CMD_GET_HW_INFO,
158498f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
1585c93fe71cSSergey Matyukevich 	if (!cmd_skb)
158698f44cb0SIgor Mitsyanko 		return -ENOMEM;
158798f44cb0SIgor Mitsyanko 
158898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1589c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
15904dd07d2bSSergey Matyukevich 				       sizeof(*resp), &info_len);
1591c6ed298fSSergey Matyukevich 	if (ret)
159298f44cb0SIgor Mitsyanko 		goto out;
159398f44cb0SIgor Mitsyanko 
159498f44cb0SIgor Mitsyanko 	resp = (const struct qlink_resp_get_hw_info *)resp_skb->data;
15954dd07d2bSSergey Matyukevich 	ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len);
159698f44cb0SIgor Mitsyanko 
159798f44cb0SIgor Mitsyanko out:
159898f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(bus);
159998f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
160098f44cb0SIgor Mitsyanko 
160198f44cb0SIgor Mitsyanko 	return ret;
160298f44cb0SIgor Mitsyanko }
160398f44cb0SIgor Mitsyanko 
1604e294cbfdSIgor Mitsyanko int qtnf_cmd_band_info_get(struct qtnf_wmac *mac,
160598f44cb0SIgor Mitsyanko 			   struct ieee80211_supported_band *band)
160698f44cb0SIgor Mitsyanko {
160798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb, *resp_skb = NULL;
1608e294cbfdSIgor Mitsyanko 	struct qlink_cmd_band_info_get *cmd;
1609e294cbfdSIgor Mitsyanko 	struct qlink_resp_band_info_get *resp;
16101066bd19SSergey Matyukevich 	size_t info_len = 0;
161198f44cb0SIgor Mitsyanko 	int ret = 0;
16122c31129fSIgor Mitsyanko 	u8 qband = qlink_utils_band_cfg2q(band->band);
161398f44cb0SIgor Mitsyanko 
1614bc0384eeSColin Ian King 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
1615e294cbfdSIgor Mitsyanko 					    QLINK_CMD_BAND_INFO_GET,
1616bc0384eeSColin Ian King 					    sizeof(*cmd));
1617bc0384eeSColin Ian King 	if (!cmd_skb)
1618bc0384eeSColin Ian King 		return -ENOMEM;
1619bc0384eeSColin Ian King 
1620e294cbfdSIgor Mitsyanko 	cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data;
162198f44cb0SIgor Mitsyanko 	cmd->band = qband;
16229ef75095SSergey Matyukevich 
16239ef75095SSergey Matyukevich 	qtnf_bus_lock(mac->bus);
1624c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
162598f44cb0SIgor Mitsyanko 				       sizeof(*resp), &info_len);
1626c6ed298fSSergey Matyukevich 	if (ret)
162798f44cb0SIgor Mitsyanko 		goto out;
162898f44cb0SIgor Mitsyanko 
1629e294cbfdSIgor Mitsyanko 	resp = (struct qlink_resp_band_info_get *)resp_skb->data;
163098f44cb0SIgor Mitsyanko 	if (resp->band != qband) {
163198f44cb0SIgor Mitsyanko 		pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid,
163298f44cb0SIgor Mitsyanko 		       resp->band, qband);
163398f44cb0SIgor Mitsyanko 		ret = -EINVAL;
163498f44cb0SIgor Mitsyanko 		goto out;
163598f44cb0SIgor Mitsyanko 	}
163698f44cb0SIgor Mitsyanko 
1637e294cbfdSIgor Mitsyanko 	ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len);
163898f44cb0SIgor Mitsyanko 
163998f44cb0SIgor Mitsyanko out:
16409ef75095SSergey Matyukevich 	qtnf_bus_unlock(mac->bus);
164198f44cb0SIgor Mitsyanko 	consume_skb(resp_skb);
164298f44cb0SIgor Mitsyanko 
164398f44cb0SIgor Mitsyanko 	return ret;
164498f44cb0SIgor Mitsyanko }
164598f44cb0SIgor Mitsyanko 
164698f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed)
164798f44cb0SIgor Mitsyanko {
164898f44cb0SIgor Mitsyanko 	struct wiphy *wiphy = priv_to_wiphy(mac);
164998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
165098f44cb0SIgor Mitsyanko 	int ret = 0;
165198f44cb0SIgor Mitsyanko 
165298f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
165398f44cb0SIgor Mitsyanko 					    QLINK_CMD_PHY_PARAMS_SET,
165498f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
165598f44cb0SIgor Mitsyanko 	if (!cmd_skb)
165698f44cb0SIgor Mitsyanko 		return -ENOMEM;
165798f44cb0SIgor Mitsyanko 
165898f44cb0SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
165998f44cb0SIgor Mitsyanko 
166098f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
16619fe504a1SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_FRAG_THRESH,
166298f44cb0SIgor Mitsyanko 					 wiphy->frag_threshold);
166398f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_RTS_THRESHOLD)
16649fe504a1SSergey Matyukevich 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH,
166598f44cb0SIgor Mitsyanko 					 wiphy->rts_threshold);
166698f44cb0SIgor Mitsyanko 	if (changed & WIPHY_PARAM_COVERAGE_CLASS)
16678b0b5f1bSIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS,
166898f44cb0SIgor Mitsyanko 					 wiphy->coverage_class);
166998f44cb0SIgor Mitsyanko 
1670f3c8bd46SSergey Matyukevich 	if (changed & WIPHY_PARAM_RETRY_LONG)
16718b0b5f1bSIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT,
1672f3c8bd46SSergey Matyukevich 					 wiphy->retry_long);
1673f3c8bd46SSergey Matyukevich 
1674f3c8bd46SSergey Matyukevich 	if (changed & WIPHY_PARAM_RETRY_SHORT)
16758b0b5f1bSIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT,
1676f3c8bd46SSergey Matyukevich 					 wiphy->retry_short);
1677f3c8bd46SSergey Matyukevich 
1678c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
1679c6ed298fSSergey Matyukevich 	if (ret)
168098f44cb0SIgor Mitsyanko 		goto out;
168198f44cb0SIgor Mitsyanko 
168298f44cb0SIgor Mitsyanko out:
168398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
1684c6ed298fSSergey Matyukevich 
168598f44cb0SIgor Mitsyanko 	return ret;
168698f44cb0SIgor Mitsyanko }
168798f44cb0SIgor Mitsyanko 
168898f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus)
168998f44cb0SIgor Mitsyanko {
1690a3ebb033SIgor Mitsyanko 	struct sk_buff *resp_skb = NULL;
1691a3ebb033SIgor Mitsyanko 	struct qlink_resp_init_fw *resp;
1692a3ebb033SIgor Mitsyanko 	struct qlink_cmd_init_fw *cmd;
169398f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
1694a3ebb033SIgor Mitsyanko 	size_t info_len = 0;
1695a3ebb033SIgor Mitsyanko 	int ret;
169698f44cb0SIgor Mitsyanko 
169798f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
169898f44cb0SIgor Mitsyanko 					    QLINK_CMD_FW_INIT,
1699a3ebb033SIgor Mitsyanko 					    sizeof(*cmd));
1700c93fe71cSSergey Matyukevich 	if (!cmd_skb)
170198f44cb0SIgor Mitsyanko 		return -ENOMEM;
170298f44cb0SIgor Mitsyanko 
1703a3ebb033SIgor Mitsyanko 	cmd = (struct qlink_cmd_init_fw *)cmd_skb->data;
1704a3ebb033SIgor Mitsyanko 	cmd->qlink_proto_ver = cpu_to_le32(QLINK_PROTO_VER);
1705a3ebb033SIgor Mitsyanko 
170698f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1707a3ebb033SIgor Mitsyanko 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
1708a3ebb033SIgor Mitsyanko 				       sizeof(*resp), &info_len);
1709a3ebb033SIgor Mitsyanko 	qtnf_bus_unlock(bus);
1710a3ebb033SIgor Mitsyanko 
1711c6ed298fSSergey Matyukevich 	if (ret)
171298f44cb0SIgor Mitsyanko 		goto out;
171398f44cb0SIgor Mitsyanko 
1714a3ebb033SIgor Mitsyanko 	resp = (struct qlink_resp_init_fw *)resp_skb->data;
1715a3ebb033SIgor Mitsyanko 	bus->hw_info.ql_proto_ver = le32_to_cpu(resp->qlink_proto_ver);
1716c6ed298fSSergey Matyukevich 
1717a3ebb033SIgor Mitsyanko out:
1718a3ebb033SIgor Mitsyanko 	consume_skb(resp_skb);
171998f44cb0SIgor Mitsyanko 	return ret;
172098f44cb0SIgor Mitsyanko }
172198f44cb0SIgor Mitsyanko 
172298f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus)
172398f44cb0SIgor Mitsyanko {
172498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
172598f44cb0SIgor Mitsyanko 
172698f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
172798f44cb0SIgor Mitsyanko 					    QLINK_CMD_FW_DEINIT,
172898f44cb0SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
172998f44cb0SIgor Mitsyanko 	if (!cmd_skb)
173098f44cb0SIgor Mitsyanko 		return;
173198f44cb0SIgor Mitsyanko 
173298f44cb0SIgor Mitsyanko 	qtnf_bus_lock(bus);
1733c6ed298fSSergey Matyukevich 	qtnf_cmd_send(bus, cmd_skb);
173498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(bus);
173598f44cb0SIgor Mitsyanko }
173698f44cb0SIgor Mitsyanko 
173798f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
173898f44cb0SIgor Mitsyanko 			  const u8 *mac_addr, struct key_params *params)
173998f44cb0SIgor Mitsyanko {
174098f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
174198f44cb0SIgor Mitsyanko 	struct qlink_cmd_add_key *cmd;
174298f44cb0SIgor Mitsyanko 	int ret = 0;
174398f44cb0SIgor Mitsyanko 
174498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
174598f44cb0SIgor Mitsyanko 					    QLINK_CMD_ADD_KEY,
174698f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1747c93fe71cSSergey Matyukevich 	if (!cmd_skb)
174898f44cb0SIgor Mitsyanko 		return -ENOMEM;
174998f44cb0SIgor Mitsyanko 
175098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
175198f44cb0SIgor Mitsyanko 
175298f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_add_key *)cmd_skb->data;
175398f44cb0SIgor Mitsyanko 
175498f44cb0SIgor Mitsyanko 	if (mac_addr)
175598f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->addr, mac_addr);
175698f44cb0SIgor Mitsyanko 	else
175798f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->addr);
175898f44cb0SIgor Mitsyanko 
175998f44cb0SIgor Mitsyanko 	cmd->cipher = cpu_to_le32(params->cipher);
176098f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
176198f44cb0SIgor Mitsyanko 	cmd->pairwise = pairwise;
176298f44cb0SIgor Mitsyanko 
176398f44cb0SIgor Mitsyanko 	if (params->key && params->key_len > 0)
176498f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY,
176598f44cb0SIgor Mitsyanko 					 params->key,
176698f44cb0SIgor Mitsyanko 					 params->key_len);
176798f44cb0SIgor Mitsyanko 
176898f44cb0SIgor Mitsyanko 	if (params->seq && params->seq_len > 0)
176998f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ,
177098f44cb0SIgor Mitsyanko 					 params->seq,
177198f44cb0SIgor Mitsyanko 					 params->seq_len);
177298f44cb0SIgor Mitsyanko 
1773c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1774c6ed298fSSergey Matyukevich 	if (ret)
177598f44cb0SIgor Mitsyanko 		goto out;
177698f44cb0SIgor Mitsyanko 
177798f44cb0SIgor Mitsyanko out:
177898f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1779c6ed298fSSergey Matyukevich 
178098f44cb0SIgor Mitsyanko 	return ret;
178198f44cb0SIgor Mitsyanko }
178298f44cb0SIgor Mitsyanko 
178398f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
178498f44cb0SIgor Mitsyanko 			  const u8 *mac_addr)
178598f44cb0SIgor Mitsyanko {
178698f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
178798f44cb0SIgor Mitsyanko 	struct qlink_cmd_del_key *cmd;
178898f44cb0SIgor Mitsyanko 	int ret = 0;
178998f44cb0SIgor Mitsyanko 
179098f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
179198f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_KEY,
179298f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1793c93fe71cSSergey Matyukevich 	if (!cmd_skb)
179498f44cb0SIgor Mitsyanko 		return -ENOMEM;
179598f44cb0SIgor Mitsyanko 
179698f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
179798f44cb0SIgor Mitsyanko 
179898f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_del_key *)cmd_skb->data;
179998f44cb0SIgor Mitsyanko 
180098f44cb0SIgor Mitsyanko 	if (mac_addr)
180198f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->addr, mac_addr);
180298f44cb0SIgor Mitsyanko 	else
180398f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->addr);
180498f44cb0SIgor Mitsyanko 
180598f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
180698f44cb0SIgor Mitsyanko 	cmd->pairwise = pairwise;
180798f44cb0SIgor Mitsyanko 
1808c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1809c6ed298fSSergey Matyukevich 	if (ret)
181098f44cb0SIgor Mitsyanko 		goto out;
181198f44cb0SIgor Mitsyanko 
181298f44cb0SIgor Mitsyanko out:
181398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1814c6ed298fSSergey Matyukevich 
181598f44cb0SIgor Mitsyanko 	return ret;
181698f44cb0SIgor Mitsyanko }
181798f44cb0SIgor Mitsyanko 
181898f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index,
181998f44cb0SIgor Mitsyanko 				  bool unicast, bool multicast)
182098f44cb0SIgor Mitsyanko {
182198f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
182298f44cb0SIgor Mitsyanko 	struct qlink_cmd_set_def_key *cmd;
182398f44cb0SIgor Mitsyanko 	int ret = 0;
182498f44cb0SIgor Mitsyanko 
182598f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
182698f44cb0SIgor Mitsyanko 					    QLINK_CMD_SET_DEFAULT_KEY,
182798f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1828c93fe71cSSergey Matyukevich 	if (!cmd_skb)
182998f44cb0SIgor Mitsyanko 		return -ENOMEM;
183098f44cb0SIgor Mitsyanko 
183198f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
183298f44cb0SIgor Mitsyanko 
183398f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data;
183498f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
183598f44cb0SIgor Mitsyanko 	cmd->unicast = unicast;
183698f44cb0SIgor Mitsyanko 	cmd->multicast = multicast;
183798f44cb0SIgor Mitsyanko 
1838c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1839c6ed298fSSergey Matyukevich 	if (ret)
184098f44cb0SIgor Mitsyanko 		goto out;
184198f44cb0SIgor Mitsyanko 
184298f44cb0SIgor Mitsyanko out:
184398f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1844c6ed298fSSergey Matyukevich 
184598f44cb0SIgor Mitsyanko 	return ret;
184698f44cb0SIgor Mitsyanko }
184798f44cb0SIgor Mitsyanko 
184898f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index)
184998f44cb0SIgor Mitsyanko {
185098f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
185198f44cb0SIgor Mitsyanko 	struct qlink_cmd_set_def_mgmt_key *cmd;
185298f44cb0SIgor Mitsyanko 	int ret = 0;
185398f44cb0SIgor Mitsyanko 
185498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
185598f44cb0SIgor Mitsyanko 					    QLINK_CMD_SET_DEFAULT_MGMT_KEY,
185698f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1857c93fe71cSSergey Matyukevich 	if (!cmd_skb)
185898f44cb0SIgor Mitsyanko 		return -ENOMEM;
185998f44cb0SIgor Mitsyanko 
186098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
186198f44cb0SIgor Mitsyanko 
186298f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data;
186398f44cb0SIgor Mitsyanko 	cmd->key_index = key_index;
186498f44cb0SIgor Mitsyanko 
1865c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1866c6ed298fSSergey Matyukevich 	if (ret)
186798f44cb0SIgor Mitsyanko 		goto out;
186898f44cb0SIgor Mitsyanko 
186998f44cb0SIgor Mitsyanko out:
187098f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1871c6ed298fSSergey Matyukevich 
187298f44cb0SIgor Mitsyanko 	return ret;
187398f44cb0SIgor Mitsyanko }
187498f44cb0SIgor Mitsyanko 
187598f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags)
187698f44cb0SIgor Mitsyanko {
187798f44cb0SIgor Mitsyanko 	u32 code = 0;
187898f44cb0SIgor Mitsyanko 
187998f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED))
188098f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_AUTHORIZED;
188198f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
188298f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_SHORT_PREAMBLE;
188398f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_WME))
188498f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_WME;
188598f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_MFP))
188698f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_MFP;
188798f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED))
188898f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_AUTHENTICATED;
188998f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER))
189098f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_TDLS_PEER;
189198f44cb0SIgor Mitsyanko 	if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED))
189298f44cb0SIgor Mitsyanko 		code |= QLINK_STA_FLAG_ASSOCIATED;
189398f44cb0SIgor Mitsyanko 	return code;
189498f44cb0SIgor Mitsyanko }
189598f44cb0SIgor Mitsyanko 
189698f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac,
189798f44cb0SIgor Mitsyanko 			     struct station_parameters *params)
189898f44cb0SIgor Mitsyanko {
189998f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
190098f44cb0SIgor Mitsyanko 	struct qlink_cmd_change_sta *cmd;
190198f44cb0SIgor Mitsyanko 	int ret = 0;
190298f44cb0SIgor Mitsyanko 
190398f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
190498f44cb0SIgor Mitsyanko 					    QLINK_CMD_CHANGE_STA,
190598f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1906c93fe71cSSergey Matyukevich 	if (!cmd_skb)
190798f44cb0SIgor Mitsyanko 		return -ENOMEM;
190898f44cb0SIgor Mitsyanko 
190998f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
191098f44cb0SIgor Mitsyanko 
191198f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_change_sta *)cmd_skb->data;
191298f44cb0SIgor Mitsyanko 	ether_addr_copy(cmd->sta_addr, mac);
19134d2a7a1cSIgor Mitsyanko 	cmd->flag_update.mask =
19144d2a7a1cSIgor Mitsyanko 		cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_mask));
19154d2a7a1cSIgor Mitsyanko 	cmd->flag_update.value =
19164d2a7a1cSIgor Mitsyanko 		cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_set));
1917805b28c0SSergey Matyukevich 
1918805b28c0SSergey Matyukevich 	switch (vif->wdev.iftype) {
1919805b28c0SSergey Matyukevich 	case NL80211_IFTYPE_AP:
1920805b28c0SSergey Matyukevich 		cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP);
1921805b28c0SSergey Matyukevich 		break;
1922805b28c0SSergey Matyukevich 	case NL80211_IFTYPE_STATION:
1923805b28c0SSergey Matyukevich 		cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
1924805b28c0SSergey Matyukevich 		break;
1925805b28c0SSergey Matyukevich 	default:
1926805b28c0SSergey Matyukevich 		pr_err("unsupported iftype %d\n", vif->wdev.iftype);
1927805b28c0SSergey Matyukevich 		ret = -EINVAL;
1928805b28c0SSergey Matyukevich 		goto out;
1929805b28c0SSergey Matyukevich 	}
193098f44cb0SIgor Mitsyanko 
1931c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1932c6ed298fSSergey Matyukevich 	if (ret)
193398f44cb0SIgor Mitsyanko 		goto out;
193498f44cb0SIgor Mitsyanko 
193598f44cb0SIgor Mitsyanko out:
193698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1937c6ed298fSSergey Matyukevich 
193898f44cb0SIgor Mitsyanko 	return ret;
193998f44cb0SIgor Mitsyanko }
194098f44cb0SIgor Mitsyanko 
194198f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif,
194298f44cb0SIgor Mitsyanko 			  struct station_del_parameters *params)
194398f44cb0SIgor Mitsyanko {
194498f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
194598f44cb0SIgor Mitsyanko 	struct qlink_cmd_del_sta *cmd;
194698f44cb0SIgor Mitsyanko 	int ret = 0;
194798f44cb0SIgor Mitsyanko 
194898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
194998f44cb0SIgor Mitsyanko 					    QLINK_CMD_DEL_STA,
195098f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
1951c93fe71cSSergey Matyukevich 	if (!cmd_skb)
195298f44cb0SIgor Mitsyanko 		return -ENOMEM;
195398f44cb0SIgor Mitsyanko 
195498f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
195598f44cb0SIgor Mitsyanko 
195698f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_del_sta *)cmd_skb->data;
195798f44cb0SIgor Mitsyanko 
195898f44cb0SIgor Mitsyanko 	if (params->mac)
195998f44cb0SIgor Mitsyanko 		ether_addr_copy(cmd->sta_addr, params->mac);
196098f44cb0SIgor Mitsyanko 	else
196198f44cb0SIgor Mitsyanko 		eth_broadcast_addr(cmd->sta_addr);	/* flush all stations */
196298f44cb0SIgor Mitsyanko 
196398f44cb0SIgor Mitsyanko 	cmd->subtype = params->subtype;
196498f44cb0SIgor Mitsyanko 	cmd->reason_code = cpu_to_le16(params->reason_code);
196598f44cb0SIgor Mitsyanko 
1966c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
1967c6ed298fSSergey Matyukevich 	if (ret)
196898f44cb0SIgor Mitsyanko 		goto out;
196998f44cb0SIgor Mitsyanko 
197098f44cb0SIgor Mitsyanko out:
197198f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
1972c6ed298fSSergey Matyukevich 
197398f44cb0SIgor Mitsyanko 	return ret;
197498f44cb0SIgor Mitsyanko }
197598f44cb0SIgor Mitsyanko 
1976c9889671SIgor Mitsyanko static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb,
1977c9889671SIgor Mitsyanko 				     const struct ieee80211_channel *sc)
1978c9889671SIgor Mitsyanko {
19792c31129fSIgor Mitsyanko 	struct qlink_tlv_channel *tlv;
19802c31129fSIgor Mitsyanko 	struct qlink_channel *qch;
1981c9889671SIgor Mitsyanko 
19822c31129fSIgor Mitsyanko 	tlv = skb_put_zero(cmd_skb, sizeof(*tlv));
19832c31129fSIgor Mitsyanko 	qch = &tlv->chan;
19842c31129fSIgor Mitsyanko 	tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL);
19852c31129fSIgor Mitsyanko 	tlv->hdr.len = cpu_to_le16(sizeof(*qch));
1986c9889671SIgor Mitsyanko 
19872c31129fSIgor Mitsyanko 	qch->center_freq = cpu_to_le16(sc->center_freq);
19882c31129fSIgor Mitsyanko 	qch->hw_value = cpu_to_le16(sc->hw_value);
19892c31129fSIgor Mitsyanko 	qch->band = qlink_utils_band_cfg2q(sc->band);
19902c31129fSIgor Mitsyanko 	qch->max_power = sc->max_power;
19912c31129fSIgor Mitsyanko 	qch->max_reg_power = sc->max_reg_power;
19922c31129fSIgor Mitsyanko 	qch->max_antenna_gain = sc->max_antenna_gain;
19932c31129fSIgor Mitsyanko 	qch->beacon_found = sc->beacon_found;
19942c31129fSIgor Mitsyanko 	qch->dfs_state = qlink_utils_dfs_state_cfg2q(sc->dfs_state);
19952c31129fSIgor Mitsyanko 	qch->flags = cpu_to_le32(qlink_utils_chflags_cfg2q(sc->flags));
1996c9889671SIgor Mitsyanko }
1997c9889671SIgor Mitsyanko 
19986fbef954SAndrey Shevchenko static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb,
19996fbef954SAndrey Shevchenko 				     const u8 *mac_addr,
20006fbef954SAndrey Shevchenko 				     const u8 *mac_addr_mask)
20016fbef954SAndrey Shevchenko {
20026fbef954SAndrey Shevchenko 	struct qlink_random_mac_addr *randmac;
20036fbef954SAndrey Shevchenko 	struct qlink_tlv_hdr *hdr =
20046fbef954SAndrey Shevchenko 		skb_put(cmd_skb, sizeof(*hdr) + sizeof(*randmac));
20056fbef954SAndrey Shevchenko 
20066fbef954SAndrey Shevchenko 	hdr->type = cpu_to_le16(QTN_TLV_ID_RANDOM_MAC_ADDR);
20076fbef954SAndrey Shevchenko 	hdr->len = cpu_to_le16(sizeof(*randmac));
20086fbef954SAndrey Shevchenko 	randmac = (struct qlink_random_mac_addr *)hdr->val;
20096fbef954SAndrey Shevchenko 
20106fbef954SAndrey Shevchenko 	memcpy(randmac->mac_addr, mac_addr, ETH_ALEN);
20116fbef954SAndrey Shevchenko 	memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN);
20126fbef954SAndrey Shevchenko }
20136fbef954SAndrey Shevchenko 
201498f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac)
201598f44cb0SIgor Mitsyanko {
201698f44cb0SIgor Mitsyanko 	struct cfg80211_scan_request *scan_req = mac->scan_req;
2017501c3be1SIgor Mitsyanko 	u16 dwell_passive = QTNF_SCAN_DWELL_PASSIVE_DEFAULT;
2018501c3be1SIgor Mitsyanko 	u16 dwell_active = QTNF_SCAN_DWELL_ACTIVE_DEFAULT;
2019501c3be1SIgor Mitsyanko 	struct wireless_dev *wdev = scan_req->wdev;
2020501c3be1SIgor Mitsyanko 	struct ieee80211_channel *sc;
2021501c3be1SIgor Mitsyanko 	struct qlink_cmd_scan *cmd;
2022501c3be1SIgor Mitsyanko 	struct sk_buff *cmd_skb;
2023501c3be1SIgor Mitsyanko 	int n_channels = 0;
2024501c3be1SIgor Mitsyanko 	u64 flags = 0;
2025501c3be1SIgor Mitsyanko 	int count;
202698f44cb0SIgor Mitsyanko 	int ret;
202798f44cb0SIgor Mitsyanko 
202898f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
202998f44cb0SIgor Mitsyanko 					    QLINK_CMD_SCAN,
2030501c3be1SIgor Mitsyanko 					    sizeof(*cmd));
2031c93fe71cSSergey Matyukevich 	if (!cmd_skb)
203298f44cb0SIgor Mitsyanko 		return -ENOMEM;
203398f44cb0SIgor Mitsyanko 
2034501c3be1SIgor Mitsyanko 	cmd = (struct qlink_cmd_scan *)cmd_skb->data;
203598f44cb0SIgor Mitsyanko 
2036501c3be1SIgor Mitsyanko 	if (scan_req->duration) {
2037501c3be1SIgor Mitsyanko 		dwell_active = scan_req->duration;
2038501c3be1SIgor Mitsyanko 		dwell_passive = scan_req->duration;
2039501c3be1SIgor Mitsyanko 	} else if (wdev->iftype == NL80211_IFTYPE_STATION &&
2040501c3be1SIgor Mitsyanko 		   wdev->current_bss) {
2041501c3be1SIgor Mitsyanko 		/* let device select dwell based on traffic conditions */
2042501c3be1SIgor Mitsyanko 		dwell_active = QTNF_SCAN_TIME_AUTO;
2043501c3be1SIgor Mitsyanko 		dwell_passive = QTNF_SCAN_TIME_AUTO;
2044501c3be1SIgor Mitsyanko 	}
2045501c3be1SIgor Mitsyanko 
2046501c3be1SIgor Mitsyanko 	cmd->n_ssids = cpu_to_le16(scan_req->n_ssids);
2047501c3be1SIgor Mitsyanko 	for (count = 0; count < scan_req->n_ssids; ++count) {
204898f44cb0SIgor Mitsyanko 		qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID,
204998f44cb0SIgor Mitsyanko 					 scan_req->ssids[count].ssid,
205098f44cb0SIgor Mitsyanko 					 scan_req->ssids[count].ssid_len);
205198f44cb0SIgor Mitsyanko 	}
205298f44cb0SIgor Mitsyanko 
205398f44cb0SIgor Mitsyanko 	if (scan_req->ie_len != 0)
205418b7470fSIgor Mitsyanko 		qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ,
205518b7470fSIgor Mitsyanko 					scan_req->ie, scan_req->ie_len);
205698f44cb0SIgor Mitsyanko 
2057501c3be1SIgor Mitsyanko 	for (count = 0; count < scan_req->n_channels; ++count) {
205898f44cb0SIgor Mitsyanko 		sc = scan_req->channels[count];
2059501c3be1SIgor Mitsyanko 		if (sc->flags & IEEE80211_CHAN_DISABLED)
206098f44cb0SIgor Mitsyanko 			continue;
206198f44cb0SIgor Mitsyanko 
2062501c3be1SIgor Mitsyanko 		pr_debug("[MAC%u] scan chan=%d, freq=%d, flags=%#x\n",
206398f44cb0SIgor Mitsyanko 			 mac->macid, sc->hw_value, sc->center_freq,
206498f44cb0SIgor Mitsyanko 			 sc->flags);
206598f44cb0SIgor Mitsyanko 
2066c9889671SIgor Mitsyanko 		qtnf_cmd_channel_tlv_add(cmd_skb, sc);
2067501c3be1SIgor Mitsyanko 		++n_channels;
206898f44cb0SIgor Mitsyanko 	}
206998f44cb0SIgor Mitsyanko 
2070501c3be1SIgor Mitsyanko 	if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH)
2071501c3be1SIgor Mitsyanko 		flags |= QLINK_SCAN_FLAG_FLUSH;
2072501c3be1SIgor Mitsyanko 
2073501c3be1SIgor Mitsyanko 	if (scan_req->duration_mandatory)
2074501c3be1SIgor Mitsyanko 		flags |= QLINK_SCAN_FLAG_DURATION_MANDATORY;
2075501c3be1SIgor Mitsyanko 
2076501c3be1SIgor Mitsyanko 	cmd->n_channels = cpu_to_le16(n_channels);
2077501c3be1SIgor Mitsyanko 	cmd->active_dwell = cpu_to_le16(dwell_active);
2078501c3be1SIgor Mitsyanko 	cmd->passive_dwell = cpu_to_le16(dwell_passive);
2079501c3be1SIgor Mitsyanko 	cmd->sample_duration = cpu_to_le16(QTNF_SCAN_SAMPLE_DURATION_DEFAULT);
2080501c3be1SIgor Mitsyanko 	cmd->flags = cpu_to_le64(flags);
2081501c3be1SIgor Mitsyanko 
2082501c3be1SIgor Mitsyanko 	pr_debug("[MAC%u] %s scan dwell active=%u passive=%u duration=%u\n",
2083501c3be1SIgor Mitsyanko 		 mac->macid,
2084501c3be1SIgor Mitsyanko 		 scan_req->duration_mandatory ? "mandatory" : "max",
2085501c3be1SIgor Mitsyanko 		 dwell_active, dwell_passive,
2086501c3be1SIgor Mitsyanko 		 QTNF_SCAN_SAMPLE_DURATION_DEFAULT);
2087b63967caSIgor Mitsyanko 
20886fbef954SAndrey Shevchenko 	if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
2089501c3be1SIgor Mitsyanko 		pr_debug("[MAC%u] scan with random addr=%pM, mask=%pM\n",
20906fbef954SAndrey Shevchenko 			 mac->macid,
20916fbef954SAndrey Shevchenko 			 scan_req->mac_addr, scan_req->mac_addr_mask);
20926fbef954SAndrey Shevchenko 		qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr,
20936fbef954SAndrey Shevchenko 					 scan_req->mac_addr_mask);
20946fbef954SAndrey Shevchenko 	}
20956fbef954SAndrey Shevchenko 
2096501c3be1SIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
2097c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
209898f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
2099c6ed298fSSergey Matyukevich 
210098f44cb0SIgor Mitsyanko 	return ret;
210198f44cb0SIgor Mitsyanko }
210298f44cb0SIgor Mitsyanko 
210398f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif,
210498f44cb0SIgor Mitsyanko 			  struct cfg80211_connect_params *sme)
210598f44cb0SIgor Mitsyanko {
210698f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
210798f44cb0SIgor Mitsyanko 	struct qlink_cmd_connect *cmd;
2108d23d1361SIgor Mitsyanko 	struct qlink_auth_encr *aen;
210998f44cb0SIgor Mitsyanko 	int ret;
211098f44cb0SIgor Mitsyanko 	int i;
21119766d1ddSIgor Mitsyanko 	u32 connect_flags = 0;
211298f44cb0SIgor Mitsyanko 
211398f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
211498f44cb0SIgor Mitsyanko 					    QLINK_CMD_CONNECT,
211598f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2116c93fe71cSSergey Matyukevich 	if (!cmd_skb)
211798f44cb0SIgor Mitsyanko 		return -ENOMEM;
211898f44cb0SIgor Mitsyanko 
211998f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_connect *)cmd_skb->data;
212098f44cb0SIgor Mitsyanko 
21219766d1ddSIgor Mitsyanko 	ether_addr_copy(cmd->bssid, vif->bssid);
212298f44cb0SIgor Mitsyanko 
2123c9889671SIgor Mitsyanko 	if (sme->bssid_hint)
2124c9889671SIgor Mitsyanko 		ether_addr_copy(cmd->bssid_hint, sme->bssid_hint);
212596d4eaf2SIgor Mitsyanko 	else
2126c9889671SIgor Mitsyanko 		eth_zero_addr(cmd->bssid_hint);
2127c9889671SIgor Mitsyanko 
2128c9889671SIgor Mitsyanko 	if (sme->prev_bssid)
2129c9889671SIgor Mitsyanko 		ether_addr_copy(cmd->prev_bssid, sme->prev_bssid);
2130c9889671SIgor Mitsyanko 	else
2131c9889671SIgor Mitsyanko 		eth_zero_addr(cmd->prev_bssid);
213298f44cb0SIgor Mitsyanko 
2133f5d2ff43SAndrey Shevchenko 	if ((sme->bg_scan_period >= 0) &&
2134f5d2ff43SAndrey Shevchenko 	    (sme->bg_scan_period <= SHRT_MAX))
21359766d1ddSIgor Mitsyanko 		cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period);
21369766d1ddSIgor Mitsyanko 	else
2137f5d2ff43SAndrey Shevchenko 		cmd->bg_scan_period = cpu_to_le16(-1); /* use default value */
21389766d1ddSIgor Mitsyanko 
21399766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_DISABLE_HT)
21409766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_DISABLE_HT;
21419766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_DISABLE_VHT)
21429766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT;
21439766d1ddSIgor Mitsyanko 	if (sme->flags & ASSOC_REQ_USE_RRM)
21449766d1ddSIgor Mitsyanko 		connect_flags |= QLINK_STA_CONNECT_USE_RRM;
21459766d1ddSIgor Mitsyanko 
21469766d1ddSIgor Mitsyanko 	cmd->flags = cpu_to_le32(connect_flags);
2147c9889671SIgor Mitsyanko 	memcpy(&cmd->ht_capa, &sme->ht_capa, sizeof(cmd->ht_capa));
2148c9889671SIgor Mitsyanko 	memcpy(&cmd->ht_capa_mask, &sme->ht_capa_mask,
2149c9889671SIgor Mitsyanko 	       sizeof(cmd->ht_capa_mask));
2150c9889671SIgor Mitsyanko 	memcpy(&cmd->vht_capa, &sme->vht_capa, sizeof(cmd->vht_capa));
2151c9889671SIgor Mitsyanko 	memcpy(&cmd->vht_capa_mask, &sme->vht_capa_mask,
2152c9889671SIgor Mitsyanko 	       sizeof(cmd->vht_capa_mask));
2153c9889671SIgor Mitsyanko 	cmd->pbss = sme->pbss;
215498f44cb0SIgor Mitsyanko 
2155d23d1361SIgor Mitsyanko 	aen = &cmd->aen;
2156d23d1361SIgor Mitsyanko 	aen->auth_type = sme->auth_type;
2157d23d1361SIgor Mitsyanko 	aen->privacy = !!sme->privacy;
2158c9889671SIgor Mitsyanko 	cmd->mfp = sme->mfp;
2159d23d1361SIgor Mitsyanko 	aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions);
2160d23d1361SIgor Mitsyanko 	aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group);
2161d23d1361SIgor Mitsyanko 	aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise);
216298f44cb0SIgor Mitsyanko 
216398f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++)
2164d23d1361SIgor Mitsyanko 		aen->ciphers_pairwise[i] =
2165d23d1361SIgor Mitsyanko 			cpu_to_le32(sme->crypto.ciphers_pairwise[i]);
216698f44cb0SIgor Mitsyanko 
2167d23d1361SIgor Mitsyanko 	aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites);
216898f44cb0SIgor Mitsyanko 
216998f44cb0SIgor Mitsyanko 	for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++)
2170d23d1361SIgor Mitsyanko 		aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]);
217198f44cb0SIgor Mitsyanko 
2172d23d1361SIgor Mitsyanko 	aen->control_port = sme->crypto.control_port;
2173d23d1361SIgor Mitsyanko 	aen->control_port_no_encrypt =
21749766d1ddSIgor Mitsyanko 		sme->crypto.control_port_no_encrypt;
2175d23d1361SIgor Mitsyanko 	aen->control_port_ethertype =
2176d23d1361SIgor Mitsyanko 		cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype));
217798f44cb0SIgor Mitsyanko 
21789766d1ddSIgor Mitsyanko 	qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid,
21799766d1ddSIgor Mitsyanko 				 sme->ssid_len);
218098f44cb0SIgor Mitsyanko 
218198f44cb0SIgor Mitsyanko 	if (sme->ie_len != 0)
218218b7470fSIgor Mitsyanko 		qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_REQ,
218318b7470fSIgor Mitsyanko 					sme->ie, sme->ie_len);
218498f44cb0SIgor Mitsyanko 
2185c9889671SIgor Mitsyanko 	if (sme->channel)
2186c9889671SIgor Mitsyanko 		qtnf_cmd_channel_tlv_add(cmd_skb, sme->channel);
2187c9889671SIgor Mitsyanko 
2188d23d1361SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
2189c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2190c6ed298fSSergey Matyukevich 	if (ret)
219198f44cb0SIgor Mitsyanko 		goto out;
219298f44cb0SIgor Mitsyanko 
219398f44cb0SIgor Mitsyanko out:
219498f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2195c6ed298fSSergey Matyukevich 
219698f44cb0SIgor Mitsyanko 	return ret;
219798f44cb0SIgor Mitsyanko }
219898f44cb0SIgor Mitsyanko 
219947b08e75SSergey Matyukevich int qtnf_cmd_send_external_auth(struct qtnf_vif *vif,
220047b08e75SSergey Matyukevich 				struct cfg80211_external_auth_params *auth)
220147b08e75SSergey Matyukevich {
220247b08e75SSergey Matyukevich 	struct sk_buff *cmd_skb;
220347b08e75SSergey Matyukevich 	struct qlink_cmd_external_auth *cmd;
220447b08e75SSergey Matyukevich 	int ret;
220547b08e75SSergey Matyukevich 
220647b08e75SSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
220747b08e75SSergey Matyukevich 					    QLINK_CMD_EXTERNAL_AUTH,
220847b08e75SSergey Matyukevich 					    sizeof(*cmd));
220947b08e75SSergey Matyukevich 	if (!cmd_skb)
221047b08e75SSergey Matyukevich 		return -ENOMEM;
221147b08e75SSergey Matyukevich 
221247b08e75SSergey Matyukevich 	cmd = (struct qlink_cmd_external_auth *)cmd_skb->data;
221347b08e75SSergey Matyukevich 
2214*b3860e7aSSergey Matyukevich 	ether_addr_copy(cmd->peer, auth->bssid);
221547b08e75SSergey Matyukevich 	cmd->status = cpu_to_le16(auth->status);
221647b08e75SSergey Matyukevich 
221747b08e75SSergey Matyukevich 	qtnf_bus_lock(vif->mac->bus);
221847b08e75SSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
221947b08e75SSergey Matyukevich 	if (ret)
222047b08e75SSergey Matyukevich 		goto out;
222147b08e75SSergey Matyukevich 
222247b08e75SSergey Matyukevich out:
222347b08e75SSergey Matyukevich 	qtnf_bus_unlock(vif->mac->bus);
222447b08e75SSergey Matyukevich 
222547b08e75SSergey Matyukevich 	return ret;
222647b08e75SSergey Matyukevich }
222747b08e75SSergey Matyukevich 
222898f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code)
222998f44cb0SIgor Mitsyanko {
223098f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
223198f44cb0SIgor Mitsyanko 	struct qlink_cmd_disconnect *cmd;
223298f44cb0SIgor Mitsyanko 	int ret;
223398f44cb0SIgor Mitsyanko 
223498f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
223598f44cb0SIgor Mitsyanko 					    QLINK_CMD_DISCONNECT,
223698f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2237c93fe71cSSergey Matyukevich 	if (!cmd_skb)
223898f44cb0SIgor Mitsyanko 		return -ENOMEM;
223998f44cb0SIgor Mitsyanko 
224098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
224198f44cb0SIgor Mitsyanko 
224298f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_disconnect *)cmd_skb->data;
224398f44cb0SIgor Mitsyanko 	cmd->reason = cpu_to_le16(reason_code);
224498f44cb0SIgor Mitsyanko 
2245c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2246c6ed298fSSergey Matyukevich 	if (ret)
224798f44cb0SIgor Mitsyanko 		goto out;
224898f44cb0SIgor Mitsyanko 
224998f44cb0SIgor Mitsyanko out:
225098f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2251c6ed298fSSergey Matyukevich 
225298f44cb0SIgor Mitsyanko 	return ret;
225398f44cb0SIgor Mitsyanko }
225498f44cb0SIgor Mitsyanko 
225598f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up)
225698f44cb0SIgor Mitsyanko {
225798f44cb0SIgor Mitsyanko 	struct sk_buff *cmd_skb;
225898f44cb0SIgor Mitsyanko 	struct qlink_cmd_updown *cmd;
225998f44cb0SIgor Mitsyanko 	int ret;
226098f44cb0SIgor Mitsyanko 
226198f44cb0SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
226298f44cb0SIgor Mitsyanko 					    QLINK_CMD_UPDOWN_INTF,
226398f44cb0SIgor Mitsyanko 					    sizeof(*cmd));
2264c93fe71cSSergey Matyukevich 	if (!cmd_skb)
226598f44cb0SIgor Mitsyanko 		return -ENOMEM;
226698f44cb0SIgor Mitsyanko 
226798f44cb0SIgor Mitsyanko 	cmd = (struct qlink_cmd_updown *)cmd_skb->data;
226898f44cb0SIgor Mitsyanko 	cmd->if_up = !!up;
226998f44cb0SIgor Mitsyanko 
227098f44cb0SIgor Mitsyanko 	qtnf_bus_lock(vif->mac->bus);
2271c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(vif->mac->bus, cmd_skb);
2272c6ed298fSSergey Matyukevich 	if (ret)
227398f44cb0SIgor Mitsyanko 		goto out;
227498f44cb0SIgor Mitsyanko 
227598f44cb0SIgor Mitsyanko out:
227698f44cb0SIgor Mitsyanko 	qtnf_bus_unlock(vif->mac->bus);
2277c6ed298fSSergey Matyukevich 
227898f44cb0SIgor Mitsyanko 	return ret;
227998f44cb0SIgor Mitsyanko }
22804dd07d2bSSergey Matyukevich 
2281888f1564SIgor Mitsyanko int qtnf_cmd_reg_notify(struct qtnf_wmac *mac, struct regulatory_request *req,
2282155b424cSSergey Matyukevich 			bool slave_radar, bool dfs_offload)
22834dd07d2bSSergey Matyukevich {
2284a2fbaaf7SIgor Mitsyanko 	struct wiphy *wiphy = priv_to_wiphy(mac);
2285d1231721SIgor Mitsyanko 	struct qtnf_bus *bus = mac->bus;
22864dd07d2bSSergey Matyukevich 	struct sk_buff *cmd_skb;
22874dd07d2bSSergey Matyukevich 	int ret;
22884dd07d2bSSergey Matyukevich 	struct qlink_cmd_reg_notify *cmd;
2289a2fbaaf7SIgor Mitsyanko 	enum nl80211_band band;
2290a2fbaaf7SIgor Mitsyanko 	const struct ieee80211_supported_band *cfg_band;
22914dd07d2bSSergey Matyukevich 
2292d1231721SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
22934dd07d2bSSergey Matyukevich 					    QLINK_CMD_REG_NOTIFY,
22944dd07d2bSSergey Matyukevich 					    sizeof(*cmd));
22954dd07d2bSSergey Matyukevich 	if (!cmd_skb)
22964dd07d2bSSergey Matyukevich 		return -ENOMEM;
22974dd07d2bSSergey Matyukevich 
22984dd07d2bSSergey Matyukevich 	cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data;
22994dd07d2bSSergey Matyukevich 	cmd->alpha2[0] = req->alpha2[0];
23004dd07d2bSSergey Matyukevich 	cmd->alpha2[1] = req->alpha2[1];
23014dd07d2bSSergey Matyukevich 
23024dd07d2bSSergey Matyukevich 	switch (req->initiator) {
23034dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_CORE:
23044dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_CORE;
23054dd07d2bSSergey Matyukevich 		break;
23064dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_USER:
23074dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_USER;
23084dd07d2bSSergey Matyukevich 		break;
23094dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_DRIVER:
23104dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER;
23114dd07d2bSSergey Matyukevich 		break;
23124dd07d2bSSergey Matyukevich 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
23134dd07d2bSSergey Matyukevich 		cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE;
23144dd07d2bSSergey Matyukevich 		break;
23154dd07d2bSSergey Matyukevich 	}
23164dd07d2bSSergey Matyukevich 
23174dd07d2bSSergey Matyukevich 	switch (req->user_reg_hint_type) {
23184dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_USER:
23194dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER;
23204dd07d2bSSergey Matyukevich 		break;
23214dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_CELL_BASE:
23224dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE;
23234dd07d2bSSergey Matyukevich 		break;
23244dd07d2bSSergey Matyukevich 	case NL80211_USER_REG_HINT_INDOOR:
23254dd07d2bSSergey Matyukevich 		cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR;
23264dd07d2bSSergey Matyukevich 		break;
23274dd07d2bSSergey Matyukevich 	}
23284dd07d2bSSergey Matyukevich 
2329438fb43bSIgor Mitsyanko 	switch (req->dfs_region) {
2330438fb43bSIgor Mitsyanko 	case NL80211_DFS_FCC:
2331438fb43bSIgor Mitsyanko 		cmd->dfs_region = QLINK_DFS_FCC;
2332438fb43bSIgor Mitsyanko 		break;
2333438fb43bSIgor Mitsyanko 	case NL80211_DFS_ETSI:
2334438fb43bSIgor Mitsyanko 		cmd->dfs_region = QLINK_DFS_ETSI;
2335438fb43bSIgor Mitsyanko 		break;
2336438fb43bSIgor Mitsyanko 	case NL80211_DFS_JP:
2337438fb43bSIgor Mitsyanko 		cmd->dfs_region = QLINK_DFS_JP;
2338438fb43bSIgor Mitsyanko 		break;
2339438fb43bSIgor Mitsyanko 	default:
2340438fb43bSIgor Mitsyanko 		cmd->dfs_region = QLINK_DFS_UNSET;
2341438fb43bSIgor Mitsyanko 		break;
2342438fb43bSIgor Mitsyanko 	}
2343438fb43bSIgor Mitsyanko 
2344888f1564SIgor Mitsyanko 	cmd->slave_radar = slave_radar;
2345155b424cSSergey Matyukevich 	cmd->dfs_offload = dfs_offload;
2346a2fbaaf7SIgor Mitsyanko 	cmd->num_channels = 0;
2347a2fbaaf7SIgor Mitsyanko 
2348a2fbaaf7SIgor Mitsyanko 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
2349a2fbaaf7SIgor Mitsyanko 		unsigned int i;
2350a2fbaaf7SIgor Mitsyanko 
2351a2fbaaf7SIgor Mitsyanko 		cfg_band = wiphy->bands[band];
2352a2fbaaf7SIgor Mitsyanko 		if (!cfg_band)
2353a2fbaaf7SIgor Mitsyanko 			continue;
2354a2fbaaf7SIgor Mitsyanko 
2355a2fbaaf7SIgor Mitsyanko 		cmd->num_channels += cfg_band->n_channels;
2356a2fbaaf7SIgor Mitsyanko 
2357a2fbaaf7SIgor Mitsyanko 		for (i = 0; i < cfg_band->n_channels; ++i) {
2358a2fbaaf7SIgor Mitsyanko 			qtnf_cmd_channel_tlv_add(cmd_skb,
2359a2fbaaf7SIgor Mitsyanko 						 &cfg_band->channels[i]);
2360a2fbaaf7SIgor Mitsyanko 		}
2361a2fbaaf7SIgor Mitsyanko 	}
2362a2fbaaf7SIgor Mitsyanko 
23634dd07d2bSSergey Matyukevich 	qtnf_bus_lock(bus);
2364c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
23654dd07d2bSSergey Matyukevich 	qtnf_bus_unlock(bus);
23664dd07d2bSSergey Matyukevich 
23674dd07d2bSSergey Matyukevich 	return ret;
23684dd07d2bSSergey Matyukevich }
23697c04b439SSergey Matyukevich 
2370601ce21fSIgor Mitsyanko static int
2371601ce21fSIgor Mitsyanko qtnf_cmd_resp_proc_chan_stat_info(struct survey_info *survey,
2372601ce21fSIgor Mitsyanko 				  const u8 *payload, size_t payload_len)
2373601ce21fSIgor Mitsyanko {
2374601ce21fSIgor Mitsyanko 	const struct qlink_chan_stats *stats = NULL;
2375601ce21fSIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
2376601ce21fSIgor Mitsyanko 	u16 tlv_value_len;
2377601ce21fSIgor Mitsyanko 	u16 tlv_type;
2378601ce21fSIgor Mitsyanko 	const u8 *map = NULL;
2379601ce21fSIgor Mitsyanko 	unsigned int map_len = 0;
2380601ce21fSIgor Mitsyanko 	unsigned int stats_len = 0;
2381601ce21fSIgor Mitsyanko 
23828b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, payload, payload_len) {
2383601ce21fSIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
2384601ce21fSIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
2385601ce21fSIgor Mitsyanko 
2386601ce21fSIgor Mitsyanko 		switch (tlv_type) {
2387601ce21fSIgor Mitsyanko 		case QTN_TLV_ID_BITMAP:
2388601ce21fSIgor Mitsyanko 			map = tlv->val;
2389601ce21fSIgor Mitsyanko 			map_len = tlv_value_len;
2390601ce21fSIgor Mitsyanko 			break;
2391601ce21fSIgor Mitsyanko 		case QTN_TLV_ID_CHANNEL_STATS:
2392601ce21fSIgor Mitsyanko 			stats = (struct qlink_chan_stats *)tlv->val;
2393601ce21fSIgor Mitsyanko 			stats_len = tlv_value_len;
2394601ce21fSIgor Mitsyanko 			break;
2395601ce21fSIgor Mitsyanko 		default:
2396601ce21fSIgor Mitsyanko 			pr_info("Unknown TLV type: %#x\n", tlv_type);
2397601ce21fSIgor Mitsyanko 			break;
2398601ce21fSIgor Mitsyanko 		}
2399601ce21fSIgor Mitsyanko 	}
2400601ce21fSIgor Mitsyanko 
24018b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, payload, payload_len)) {
24028b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
2403601ce21fSIgor Mitsyanko 		return -EINVAL;
2404601ce21fSIgor Mitsyanko 	}
2405601ce21fSIgor Mitsyanko 
2406601ce21fSIgor Mitsyanko 	if (!map || !stats)
2407601ce21fSIgor Mitsyanko 		return 0;
2408601ce21fSIgor Mitsyanko 
2409601ce21fSIgor Mitsyanko #define qtnf_chan_stat_avail(stat_name, bitn)	\
2410601ce21fSIgor Mitsyanko 	(qtnf_utils_is_bit_set(map, bitn, map_len) && \
2411601ce21fSIgor Mitsyanko 	 (offsetofend(struct qlink_chan_stats, stat_name) <= stats_len))
2412601ce21fSIgor Mitsyanko 
2413601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(time_on, QLINK_CHAN_STAT_TIME_ON)) {
2414601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME;
2415601ce21fSIgor Mitsyanko 		survey->time = le64_to_cpu(stats->time_on);
2416601ce21fSIgor Mitsyanko 	}
2417601ce21fSIgor Mitsyanko 
2418601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(time_tx, QLINK_CHAN_STAT_TIME_TX)) {
2419601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME_TX;
2420601ce21fSIgor Mitsyanko 		survey->time_tx = le64_to_cpu(stats->time_tx);
2421601ce21fSIgor Mitsyanko 	}
2422601ce21fSIgor Mitsyanko 
2423601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(time_rx, QLINK_CHAN_STAT_TIME_RX)) {
2424601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME_RX;
2425601ce21fSIgor Mitsyanko 		survey->time_rx = le64_to_cpu(stats->time_rx);
2426601ce21fSIgor Mitsyanko 	}
2427601ce21fSIgor Mitsyanko 
2428601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(cca_busy, QLINK_CHAN_STAT_CCA_BUSY)) {
2429601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME_BUSY;
2430601ce21fSIgor Mitsyanko 		survey->time_busy = le64_to_cpu(stats->cca_busy);
2431601ce21fSIgor Mitsyanko 	}
2432601ce21fSIgor Mitsyanko 
2433601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(cca_busy_ext, QLINK_CHAN_STAT_CCA_BUSY_EXT)) {
2434601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME_EXT_BUSY;
2435601ce21fSIgor Mitsyanko 		survey->time_ext_busy = le64_to_cpu(stats->cca_busy_ext);
2436601ce21fSIgor Mitsyanko 	}
2437601ce21fSIgor Mitsyanko 
2438601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(time_scan, QLINK_CHAN_STAT_TIME_SCAN)) {
2439601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_TIME_SCAN;
2440601ce21fSIgor Mitsyanko 		survey->time_scan = le64_to_cpu(stats->time_scan);
2441601ce21fSIgor Mitsyanko 	}
2442601ce21fSIgor Mitsyanko 
2443601ce21fSIgor Mitsyanko 	if (qtnf_chan_stat_avail(chan_noise, QLINK_CHAN_STAT_CHAN_NOISE)) {
2444601ce21fSIgor Mitsyanko 		survey->filled |= SURVEY_INFO_NOISE_DBM;
2445601ce21fSIgor Mitsyanko 		survey->noise = stats->chan_noise;
2446601ce21fSIgor Mitsyanko 	}
2447601ce21fSIgor Mitsyanko 
2448601ce21fSIgor Mitsyanko #undef qtnf_chan_stat_avail
2449601ce21fSIgor Mitsyanko 
2450601ce21fSIgor Mitsyanko 	return 0;
2451601ce21fSIgor Mitsyanko }
2452601ce21fSIgor Mitsyanko 
2453601ce21fSIgor Mitsyanko int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u32 chan_freq,
2454601ce21fSIgor Mitsyanko 			    struct survey_info *survey)
24557c04b439SSergey Matyukevich {
24567c04b439SSergey Matyukevich 	struct sk_buff *cmd_skb, *resp_skb = NULL;
24577c04b439SSergey Matyukevich 	struct qlink_cmd_get_chan_stats *cmd;
24587c04b439SSergey Matyukevich 	struct qlink_resp_get_chan_stats *resp;
24591066bd19SSergey Matyukevich 	size_t var_data_len = 0;
24607c04b439SSergey Matyukevich 	int ret = 0;
24617c04b439SSergey Matyukevich 
24627c04b439SSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
24637c04b439SSergey Matyukevich 					    QLINK_CMD_CHAN_STATS,
24647c04b439SSergey Matyukevich 					    sizeof(*cmd));
24657c04b439SSergey Matyukevich 	if (!cmd_skb)
24667c04b439SSergey Matyukevich 		return -ENOMEM;
24677c04b439SSergey Matyukevich 
24687c04b439SSergey Matyukevich 	cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data;
2469601ce21fSIgor Mitsyanko 	cmd->channel_freq = cpu_to_le32(chan_freq);
24707c04b439SSergey Matyukevich 
2471601ce21fSIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
2472c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
24737c04b439SSergey Matyukevich 				       sizeof(*resp), &var_data_len);
2474601ce21fSIgor Mitsyanko 	qtnf_bus_unlock(mac->bus);
2475601ce21fSIgor Mitsyanko 
2476c6ed298fSSergey Matyukevich 	if (ret)
24777c04b439SSergey Matyukevich 		goto out;
24787c04b439SSergey Matyukevich 
24797c04b439SSergey Matyukevich 	resp = (struct qlink_resp_get_chan_stats *)resp_skb->data;
2480601ce21fSIgor Mitsyanko 
2481601ce21fSIgor Mitsyanko 	if (le32_to_cpu(resp->chan_freq) != chan_freq) {
2482601ce21fSIgor Mitsyanko 		pr_err("[MAC%u] channel stats freq %u != requested %u\n",
2483601ce21fSIgor Mitsyanko 		       mac->macid, le32_to_cpu(resp->chan_freq), chan_freq);
2484601ce21fSIgor Mitsyanko 		ret = -EINVAL;
2485601ce21fSIgor Mitsyanko 		goto out;
2486601ce21fSIgor Mitsyanko 	}
2487601ce21fSIgor Mitsyanko 
2488601ce21fSIgor Mitsyanko 	ret = qtnf_cmd_resp_proc_chan_stat_info(survey, resp->info,
24897c04b439SSergey Matyukevich 						var_data_len);
24907c04b439SSergey Matyukevich 
24917c04b439SSergey Matyukevich out:
24927c04b439SSergey Matyukevich 	consume_skb(resp_skb);
2493c6ed298fSSergey Matyukevich 
24947c04b439SSergey Matyukevich 	return ret;
24957c04b439SSergey Matyukevich }
249697883695SSergey Matyukevich 
24978c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif,
249897883695SSergey Matyukevich 			      struct cfg80211_csa_settings *params)
249997883695SSergey Matyukevich {
25008c015b90SIgor Mitsyanko 	struct qtnf_wmac *mac = vif->mac;
250197883695SSergey Matyukevich 	struct qlink_cmd_chan_switch *cmd;
250297883695SSergey Matyukevich 	struct sk_buff *cmd_skb;
250397883695SSergey Matyukevich 	int ret;
25045edadc5aSIgor Mitsyanko 	u64 flags = 0;
250597883695SSergey Matyukevich 
25068c015b90SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid,
250797883695SSergey Matyukevich 					    QLINK_CMD_CHAN_SWITCH,
250897883695SSergey Matyukevich 					    sizeof(*cmd));
2509c93fe71cSSergey Matyukevich 	if (!cmd_skb)
251097883695SSergey Matyukevich 		return -ENOMEM;
251197883695SSergey Matyukevich 
25125edadc5aSIgor Mitsyanko 	if (params->radar_required)
25135edadc5aSIgor Mitsyanko 		flags |= QLINK_CHAN_SW_RADAR_REQUIRED;
25145edadc5aSIgor Mitsyanko 
25155edadc5aSIgor Mitsyanko 	if (params->block_tx)
25165edadc5aSIgor Mitsyanko 		flags |= QLINK_CHAN_SW_BLOCK_TX;
251797883695SSergey Matyukevich 
251897883695SSergey Matyukevich 	cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data;
25195edadc5aSIgor Mitsyanko 	qlink_chandef_cfg2q(&params->chandef, &cmd->channel);
25205edadc5aSIgor Mitsyanko 	cmd->flags = cpu_to_le64(flags);
252197883695SSergey Matyukevich 	cmd->beacon_count = params->count;
252297883695SSergey Matyukevich 
25235edadc5aSIgor Mitsyanko 	qtnf_bus_lock(mac->bus);
2524c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(mac->bus, cmd_skb);
252597883695SSergey Matyukevich 	qtnf_bus_unlock(mac->bus);
2526c6ed298fSSergey Matyukevich 
252797883695SSergey Matyukevich 	return ret;
252897883695SSergey Matyukevich }
25299e5478b6SIgor Mitsyanko 
25309e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef)
25319e5478b6SIgor Mitsyanko {
25329e5478b6SIgor Mitsyanko 	struct qtnf_bus *bus = vif->mac->bus;
25339e5478b6SIgor Mitsyanko 	const struct qlink_resp_channel_get *resp;
25349e5478b6SIgor Mitsyanko 	struct sk_buff *cmd_skb;
25359e5478b6SIgor Mitsyanko 	struct sk_buff *resp_skb = NULL;
25369e5478b6SIgor Mitsyanko 	int ret;
25379e5478b6SIgor Mitsyanko 
25389e5478b6SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
25399e5478b6SIgor Mitsyanko 					    QLINK_CMD_CHAN_GET,
25409e5478b6SIgor Mitsyanko 					    sizeof(struct qlink_cmd));
2541c93fe71cSSergey Matyukevich 	if (!cmd_skb)
25429e5478b6SIgor Mitsyanko 		return -ENOMEM;
25439e5478b6SIgor Mitsyanko 
25449e5478b6SIgor Mitsyanko 	qtnf_bus_lock(bus);
2545c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
25469e5478b6SIgor Mitsyanko 				       sizeof(*resp), NULL);
2547c6ed298fSSergey Matyukevich 	if (ret)
25489e5478b6SIgor Mitsyanko 		goto out;
25499e5478b6SIgor Mitsyanko 
25509e5478b6SIgor Mitsyanko 	resp = (const struct qlink_resp_channel_get *)resp_skb->data;
25519e5478b6SIgor Mitsyanko 	qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef);
25529e5478b6SIgor Mitsyanko 
25539e5478b6SIgor Mitsyanko out:
2554c6ed298fSSergey Matyukevich 	qtnf_bus_unlock(bus);
25559e5478b6SIgor Mitsyanko 	consume_skb(resp_skb);
2556c6ed298fSSergey Matyukevich 
25579e5478b6SIgor Mitsyanko 	return ret;
25589e5478b6SIgor Mitsyanko }
2559b05ee456SIgor Mitsyanko 
2560b05ee456SIgor Mitsyanko int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
2561b05ee456SIgor Mitsyanko 		       const struct cfg80211_chan_def *chdef,
2562b05ee456SIgor Mitsyanko 		       u32 cac_time_ms)
2563b05ee456SIgor Mitsyanko {
2564b05ee456SIgor Mitsyanko 	struct qtnf_bus *bus = vif->mac->bus;
2565b05ee456SIgor Mitsyanko 	struct sk_buff *cmd_skb;
2566b05ee456SIgor Mitsyanko 	struct qlink_cmd_start_cac *cmd;
2567b05ee456SIgor Mitsyanko 	int ret;
2568b05ee456SIgor Mitsyanko 
2569b05ee456SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
2570b05ee456SIgor Mitsyanko 					    QLINK_CMD_START_CAC,
2571b05ee456SIgor Mitsyanko 					    sizeof(*cmd));
2572c93fe71cSSergey Matyukevich 	if (!cmd_skb)
2573b05ee456SIgor Mitsyanko 		return -ENOMEM;
2574b05ee456SIgor Mitsyanko 
2575b05ee456SIgor Mitsyanko 	cmd = (struct qlink_cmd_start_cac *)cmd_skb->data;
2576b05ee456SIgor Mitsyanko 	cmd->cac_time_ms = cpu_to_le32(cac_time_ms);
2577b05ee456SIgor Mitsyanko 	qlink_chandef_cfg2q(chdef, &cmd->chan);
2578b05ee456SIgor Mitsyanko 
2579b05ee456SIgor Mitsyanko 	qtnf_bus_lock(bus);
2580c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2581b05ee456SIgor Mitsyanko 	if (ret)
2582c6ed298fSSergey Matyukevich 		goto out;
2583b05ee456SIgor Mitsyanko 
2584c6ed298fSSergey Matyukevich out:
2585c6ed298fSSergey Matyukevich 	qtnf_bus_unlock(bus);
2586b05ee456SIgor Mitsyanko 
2587b05ee456SIgor Mitsyanko 	return ret;
2588b05ee456SIgor Mitsyanko }
2589f1398fd2SVasily Ulyanov 
2590f1398fd2SVasily Ulyanov int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
2591f1398fd2SVasily Ulyanov 			 const struct cfg80211_acl_data *params)
2592f1398fd2SVasily Ulyanov {
2593f1398fd2SVasily Ulyanov 	struct qtnf_bus *bus = vif->mac->bus;
2594f1398fd2SVasily Ulyanov 	struct sk_buff *cmd_skb;
259533f98992SVasily Ulyanov 	struct qlink_tlv_hdr *tlv;
259695336d4cSGustavo A. R. Silva 	size_t acl_size = struct_size(params, mac_addrs, params->n_acl_entries);
2597f1398fd2SVasily Ulyanov 	int ret;
2598f1398fd2SVasily Ulyanov 
2599f1398fd2SVasily Ulyanov 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
2600f1398fd2SVasily Ulyanov 					    QLINK_CMD_SET_MAC_ACL,
260133f98992SVasily Ulyanov 					    sizeof(struct qlink_cmd));
2602c93fe71cSSergey Matyukevich 	if (!cmd_skb)
2603f1398fd2SVasily Ulyanov 		return -ENOMEM;
2604f1398fd2SVasily Ulyanov 
26058b0b5f1bSIgor Mitsyanko 	tlv = skb_put(cmd_skb, sizeof(*tlv) + round_up(acl_size, QLINK_ALIGN));
260633f98992SVasily Ulyanov 	tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
260733f98992SVasily Ulyanov 	tlv->len = cpu_to_le16(acl_size);
260833f98992SVasily Ulyanov 	qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val);
2609f1398fd2SVasily Ulyanov 
2610f1398fd2SVasily Ulyanov 	qtnf_bus_lock(bus);
2611c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2612c6ed298fSSergey Matyukevich 	if (ret)
2613c6ed298fSSergey Matyukevich 		goto out;
2614c6ed298fSSergey Matyukevich 
2615c6ed298fSSergey Matyukevich out:
2616f1398fd2SVasily Ulyanov 	qtnf_bus_unlock(bus);
2617f1398fd2SVasily Ulyanov 
2618f1398fd2SVasily Ulyanov 	return ret;
2619f1398fd2SVasily Ulyanov }
26204775ad06SSergei Maksimenko 
26214775ad06SSergei Maksimenko int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout)
26224775ad06SSergei Maksimenko {
26234775ad06SSergei Maksimenko 	struct qtnf_bus *bus = vif->mac->bus;
26244775ad06SSergei Maksimenko 	struct sk_buff *cmd_skb;
26254775ad06SSergei Maksimenko 	struct qlink_cmd_pm_set *cmd;
26264775ad06SSergei Maksimenko 	int ret = 0;
26274775ad06SSergei Maksimenko 
26284775ad06SSergei Maksimenko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
26294775ad06SSergei Maksimenko 					    QLINK_CMD_PM_SET, sizeof(*cmd));
26304775ad06SSergei Maksimenko 	if (!cmd_skb)
26314775ad06SSergei Maksimenko 		return -ENOMEM;
26324775ad06SSergei Maksimenko 
26334775ad06SSergei Maksimenko 	cmd = (struct qlink_cmd_pm_set *)cmd_skb->data;
26344775ad06SSergei Maksimenko 	cmd->pm_mode = pm_mode;
26354775ad06SSergei Maksimenko 	cmd->pm_standby_timer = cpu_to_le32(timeout);
26364775ad06SSergei Maksimenko 
26374775ad06SSergei Maksimenko 	qtnf_bus_lock(bus);
26384775ad06SSergei Maksimenko 
2639c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2640c6ed298fSSergey Matyukevich 	if (ret)
26414775ad06SSergei Maksimenko 		goto out;
26424775ad06SSergei Maksimenko 
26434775ad06SSergei Maksimenko out:
26444775ad06SSergei Maksimenko 	qtnf_bus_unlock(bus);
2645c6ed298fSSergey Matyukevich 
26464775ad06SSergei Maksimenko 	return ret;
26474775ad06SSergei Maksimenko }
264828b91884SSergey Matyukevich 
26490756e913SMikhail Karpenko int qtnf_cmd_get_tx_power(const struct qtnf_vif *vif, int *dbm)
26500756e913SMikhail Karpenko {
26510756e913SMikhail Karpenko 	struct qtnf_bus *bus = vif->mac->bus;
26520756e913SMikhail Karpenko 	const struct qlink_resp_txpwr *resp;
26530756e913SMikhail Karpenko 	struct sk_buff *resp_skb = NULL;
26540756e913SMikhail Karpenko 	struct qlink_cmd_txpwr *cmd;
26550756e913SMikhail Karpenko 	struct sk_buff *cmd_skb;
26560756e913SMikhail Karpenko 	int ret = 0;
26570756e913SMikhail Karpenko 
26580756e913SMikhail Karpenko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
26590756e913SMikhail Karpenko 					    QLINK_CMD_TXPWR, sizeof(*cmd));
26600756e913SMikhail Karpenko 	if (!cmd_skb)
26610756e913SMikhail Karpenko 		return -ENOMEM;
26620756e913SMikhail Karpenko 
26630756e913SMikhail Karpenko 	cmd = (struct qlink_cmd_txpwr *)cmd_skb->data;
26640756e913SMikhail Karpenko 	cmd->op_type = QLINK_TXPWR_GET;
26650756e913SMikhail Karpenko 
26660756e913SMikhail Karpenko 	qtnf_bus_lock(bus);
26670756e913SMikhail Karpenko 
26680756e913SMikhail Karpenko 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
26690756e913SMikhail Karpenko 				       sizeof(*resp), NULL);
26700756e913SMikhail Karpenko 	if (ret)
26710756e913SMikhail Karpenko 		goto out;
26720756e913SMikhail Karpenko 
26730756e913SMikhail Karpenko 	resp = (const struct qlink_resp_txpwr *)resp_skb->data;
26740756e913SMikhail Karpenko 	*dbm = MBM_TO_DBM(le32_to_cpu(resp->txpwr));
26750756e913SMikhail Karpenko 
26760756e913SMikhail Karpenko out:
26770756e913SMikhail Karpenko 	qtnf_bus_unlock(bus);
26780756e913SMikhail Karpenko 	consume_skb(resp_skb);
26790756e913SMikhail Karpenko 
26800756e913SMikhail Karpenko 	return ret;
26810756e913SMikhail Karpenko }
26820756e913SMikhail Karpenko 
26830756e913SMikhail Karpenko int qtnf_cmd_set_tx_power(const struct qtnf_vif *vif,
26840756e913SMikhail Karpenko 			  enum nl80211_tx_power_setting type, int mbm)
26850756e913SMikhail Karpenko {
26860756e913SMikhail Karpenko 	struct qtnf_bus *bus = vif->mac->bus;
26870756e913SMikhail Karpenko 	const struct qlink_resp_txpwr *resp;
26880756e913SMikhail Karpenko 	struct sk_buff *resp_skb = NULL;
26890756e913SMikhail Karpenko 	struct qlink_cmd_txpwr *cmd;
26900756e913SMikhail Karpenko 	struct sk_buff *cmd_skb;
26910756e913SMikhail Karpenko 	int ret = 0;
26920756e913SMikhail Karpenko 
26930756e913SMikhail Karpenko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
26940756e913SMikhail Karpenko 					    QLINK_CMD_TXPWR, sizeof(*cmd));
26950756e913SMikhail Karpenko 	if (!cmd_skb)
26960756e913SMikhail Karpenko 		return -ENOMEM;
26970756e913SMikhail Karpenko 
26980756e913SMikhail Karpenko 	cmd = (struct qlink_cmd_txpwr *)cmd_skb->data;
26990756e913SMikhail Karpenko 	cmd->op_type = QLINK_TXPWR_SET;
27000756e913SMikhail Karpenko 	cmd->txpwr_setting = type;
27010756e913SMikhail Karpenko 	cmd->txpwr = cpu_to_le32(mbm);
27020756e913SMikhail Karpenko 
27030756e913SMikhail Karpenko 	qtnf_bus_lock(bus);
27040756e913SMikhail Karpenko 
27050756e913SMikhail Karpenko 	ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb,
27060756e913SMikhail Karpenko 				       sizeof(*resp), NULL);
27070756e913SMikhail Karpenko 
27080756e913SMikhail Karpenko 	qtnf_bus_unlock(bus);
27090756e913SMikhail Karpenko 	consume_skb(resp_skb);
27100756e913SMikhail Karpenko 
27110756e913SMikhail Karpenko 	return ret;
27120756e913SMikhail Karpenko }
27130756e913SMikhail Karpenko 
271428b91884SSergey Matyukevich int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif,
271528b91884SSergey Matyukevich 			     const struct cfg80211_wowlan *wowl)
271628b91884SSergey Matyukevich {
271728b91884SSergey Matyukevich 	struct qtnf_bus *bus = vif->mac->bus;
271828b91884SSergey Matyukevich 	struct sk_buff *cmd_skb;
271928b91884SSergey Matyukevich 	struct qlink_cmd_wowlan_set *cmd;
272028b91884SSergey Matyukevich 	u32 triggers = 0;
272128b91884SSergey Matyukevich 	int count = 0;
272228b91884SSergey Matyukevich 	int ret = 0;
272328b91884SSergey Matyukevich 
272428b91884SSergey Matyukevich 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
272528b91884SSergey Matyukevich 					    QLINK_CMD_WOWLAN_SET, sizeof(*cmd));
272628b91884SSergey Matyukevich 	if (!cmd_skb)
272728b91884SSergey Matyukevich 		return -ENOMEM;
272828b91884SSergey Matyukevich 
272928b91884SSergey Matyukevich 	qtnf_bus_lock(bus);
273028b91884SSergey Matyukevich 
273128b91884SSergey Matyukevich 	cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data;
273228b91884SSergey Matyukevich 
273328b91884SSergey Matyukevich 	if (wowl) {
273428b91884SSergey Matyukevich 		if (wowl->disconnect)
273528b91884SSergey Matyukevich 			triggers |=  QLINK_WOWLAN_TRIG_DISCONNECT;
273628b91884SSergey Matyukevich 
273728b91884SSergey Matyukevich 		if (wowl->magic_pkt)
273828b91884SSergey Matyukevich 			triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT;
273928b91884SSergey Matyukevich 
274028b91884SSergey Matyukevich 		if (wowl->n_patterns && wowl->patterns) {
274128b91884SSergey Matyukevich 			triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT;
274228b91884SSergey Matyukevich 			while (count < wowl->n_patterns) {
274328b91884SSergey Matyukevich 				qtnf_cmd_skb_put_tlv_arr(cmd_skb,
274428b91884SSergey Matyukevich 					QTN_TLV_ID_WOWLAN_PATTERN,
274528b91884SSergey Matyukevich 					wowl->patterns[count].pattern,
274628b91884SSergey Matyukevich 					wowl->patterns[count].pattern_len);
274728b91884SSergey Matyukevich 				count++;
274828b91884SSergey Matyukevich 			}
274928b91884SSergey Matyukevich 		}
275028b91884SSergey Matyukevich 	}
275128b91884SSergey Matyukevich 
275228b91884SSergey Matyukevich 	cmd->triggers = cpu_to_le32(triggers);
275328b91884SSergey Matyukevich 
2754c6ed298fSSergey Matyukevich 	ret = qtnf_cmd_send(bus, cmd_skb);
2755c6ed298fSSergey Matyukevich 	if (ret)
275628b91884SSergey Matyukevich 		goto out;
275728b91884SSergey Matyukevich 
275828b91884SSergey Matyukevich out:
275928b91884SSergey Matyukevich 	qtnf_bus_unlock(bus);
276028b91884SSergey Matyukevich 	return ret;
276128b91884SSergey Matyukevich }
2762decfc5c7SIgor Mitsyanko 
2763decfc5c7SIgor Mitsyanko int qtnf_cmd_netdev_changeupper(const struct qtnf_vif *vif, int br_domain)
2764decfc5c7SIgor Mitsyanko {
2765decfc5c7SIgor Mitsyanko 	struct qtnf_bus *bus = vif->mac->bus;
2766decfc5c7SIgor Mitsyanko 	struct sk_buff *cmd_skb;
2767decfc5c7SIgor Mitsyanko 	struct qlink_cmd_ndev_changeupper *cmd;
2768decfc5c7SIgor Mitsyanko 	int ret;
2769decfc5c7SIgor Mitsyanko 
2770decfc5c7SIgor Mitsyanko 	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
2771decfc5c7SIgor Mitsyanko 					    QLINK_CMD_NDEV_EVENT,
2772decfc5c7SIgor Mitsyanko 					    sizeof(*cmd));
2773decfc5c7SIgor Mitsyanko 	if (!cmd_skb)
2774decfc5c7SIgor Mitsyanko 		return -ENOMEM;
2775decfc5c7SIgor Mitsyanko 
2776decfc5c7SIgor Mitsyanko 	pr_debug("[VIF%u.%u] set broadcast domain to %d\n",
2777decfc5c7SIgor Mitsyanko 		 vif->mac->macid, vif->vifid, br_domain);
2778decfc5c7SIgor Mitsyanko 
2779decfc5c7SIgor Mitsyanko 	cmd = (struct qlink_cmd_ndev_changeupper *)cmd_skb->data;
2780decfc5c7SIgor Mitsyanko 	cmd->nehdr.event = cpu_to_le16(QLINK_NDEV_EVENT_CHANGEUPPER);
2781decfc5c7SIgor Mitsyanko 	cmd->upper_type = QLINK_NDEV_UPPER_TYPE_BRIDGE;
2782decfc5c7SIgor Mitsyanko 	cmd->br_domain = cpu_to_le32(br_domain);
2783decfc5c7SIgor Mitsyanko 
2784decfc5c7SIgor Mitsyanko 	qtnf_bus_lock(bus);
2785decfc5c7SIgor Mitsyanko 	ret = qtnf_cmd_send(bus, cmd_skb);
2786decfc5c7SIgor Mitsyanko 	qtnf_bus_unlock(bus);
2787decfc5c7SIgor Mitsyanko 
2788decfc5c7SIgor Mitsyanko 	if (ret)
2789decfc5c7SIgor Mitsyanko 		pr_err("[VIF%u.%u] failed to set broadcast domain\n",
2790decfc5c7SIgor Mitsyanko 		       vif->mac->macid, vif->vifid);
2791decfc5c7SIgor Mitsyanko 
2792decfc5c7SIgor Mitsyanko 	return ret;
2793decfc5c7SIgor Mitsyanko }
2794