xref: /linux/drivers/net/wireless/morsemicro/mm81x/command.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2*b1906ceaSLachlan Hodges /*
3*b1906ceaSLachlan Hodges  * Copyright (c) 2017-2026 Morse Micro
4*b1906ceaSLachlan Hodges  */
5*b1906ceaSLachlan Hodges 
6*b1906ceaSLachlan Hodges #include <linux/types.h>
7*b1906ceaSLachlan Hodges #include <linux/atomic.h>
8*b1906ceaSLachlan Hodges #include <linux/slab.h>
9*b1906ceaSLachlan Hodges #include <linux/workqueue.h>
10*b1906ceaSLachlan Hodges 
11*b1906ceaSLachlan Hodges #include "command.h"
12*b1906ceaSLachlan Hodges #include "mac.h"
13*b1906ceaSLachlan Hodges #include "ps.h"
14*b1906ceaSLachlan Hodges #include "hif.h"
15*b1906ceaSLachlan Hodges 
16*b1906ceaSLachlan Hodges #define MM_MAX_COMMAND_RETRY 2
17*b1906ceaSLachlan Hodges #define HOST_CMD_DEFAULT_TIMEOUT_MS 600
18*b1906ceaSLachlan Hodges #define HOST_CMD_POWERSAVE_TIMEOUT_MS 2000
19*b1906ceaSLachlan Hodges 
20*b1906ceaSLachlan Hodges #define INIT_CMD_HDR(_req, _cmd, _vif_id)				\
21*b1906ceaSLachlan Hodges 	((struct host_cmd_header){					\
22*b1906ceaSLachlan Hodges 		.message_id = cpu_to_le16(_cmd),			\
23*b1906ceaSLachlan Hodges 		.len = cpu_to_le16(sizeof(_req) - sizeof((_req).hdr)),	\
24*b1906ceaSLachlan Hodges 		.vif_id = cpu_to_le16(_vif_id),				\
25*b1906ceaSLachlan Hodges 	})
26*b1906ceaSLachlan Hodges 
27*b1906ceaSLachlan Hodges struct host_cmd_resp_cb {
28*b1906ceaSLachlan Hodges 	int ret;
29*b1906ceaSLachlan Hodges 	u32 length;
30*b1906ceaSLachlan Hodges 	struct host_cmd_resp *dest_resp;
31*b1906ceaSLachlan Hodges };
32*b1906ceaSLachlan Hodges 
mm81x_cmd_tx(struct mm81x * mors,struct host_cmd_resp * resp,struct host_cmd_req * req,u32 length,u32 timeout)33*b1906ceaSLachlan Hodges static int mm81x_cmd_tx(struct mm81x *mors, struct host_cmd_resp *resp,
34*b1906ceaSLachlan Hodges 			struct host_cmd_req *req, u32 length, u32 timeout)
35*b1906ceaSLachlan Hodges {
36*b1906ceaSLachlan Hodges 	int cmd_len;
37*b1906ceaSLachlan Hodges 	int ret = 0;
38*b1906ceaSLachlan Hodges 	u16 host_id;
39*b1906ceaSLachlan Hodges 	int retry = 0;
40*b1906ceaSLachlan Hodges 	unsigned long wait_ret = 0;
41*b1906ceaSLachlan Hodges 	struct sk_buff *skb;
42*b1906ceaSLachlan Hodges 	struct mm81x_skbq *cmd_q = mm81x_hif_get_tx_cmd_queue(mors);
43*b1906ceaSLachlan Hodges 	struct host_cmd_resp_cb *resp_cb;
44*b1906ceaSLachlan Hodges 	DECLARE_COMPLETION_ONSTACK(cmd_comp);
45*b1906ceaSLachlan Hodges 
46*b1906ceaSLachlan Hodges 	BUILD_BUG_ON(sizeof(struct host_cmd_resp_cb) >
47*b1906ceaSLachlan Hodges 		     IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
48*b1906ceaSLachlan Hodges 
49*b1906ceaSLachlan Hodges 	cmd_len = sizeof(*req) + le16_to_cpu(req->hdr.len);
50*b1906ceaSLachlan Hodges 	req->hdr.flags = cpu_to_le16(HOST_CMD_TYPE_REQ);
51*b1906ceaSLachlan Hodges 
52*b1906ceaSLachlan Hodges 	mutex_lock(&mors->cmd_wait);
53*b1906ceaSLachlan Hodges 	mors->cmd_seq++;
54*b1906ceaSLachlan Hodges 	if (mors->cmd_seq > HOST_CMD_HOST_ID_SEQ_MAX)
55*b1906ceaSLachlan Hodges 		mors->cmd_seq = 1;
56*b1906ceaSLachlan Hodges 	host_id = mors->cmd_seq << HOST_CMD_HOST_ID_SEQ_SHIFT;
57*b1906ceaSLachlan Hodges 
58*b1906ceaSLachlan Hodges 	mm81x_ps_disable(mors);
59*b1906ceaSLachlan Hodges 
60*b1906ceaSLachlan Hodges 	do {
61*b1906ceaSLachlan Hodges 		req->hdr.host_id = cpu_to_le16(host_id | retry);
62*b1906ceaSLachlan Hodges 
63*b1906ceaSLachlan Hodges 		skb = mm81x_skbq_alloc_skb(cmd_q, cmd_len);
64*b1906ceaSLachlan Hodges 		if (!skb) {
65*b1906ceaSLachlan Hodges 			ret = -ENOMEM;
66*b1906ceaSLachlan Hodges 			break;
67*b1906ceaSLachlan Hodges 		}
68*b1906ceaSLachlan Hodges 
69*b1906ceaSLachlan Hodges 		memcpy(skb->data, req, cmd_len);
70*b1906ceaSLachlan Hodges 		resp_cb = (struct host_cmd_resp_cb *)IEEE80211_SKB_CB(skb)
71*b1906ceaSLachlan Hodges 				  ->driver_data;
72*b1906ceaSLachlan Hodges 		resp_cb->length = length;
73*b1906ceaSLachlan Hodges 		resp_cb->dest_resp = resp;
74*b1906ceaSLachlan Hodges 
75*b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "CMD 0x%04x:%04x",
76*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.message_id),
77*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.host_id));
78*b1906ceaSLachlan Hodges 
79*b1906ceaSLachlan Hodges 		mutex_lock(&mors->cmd_lock);
80*b1906ceaSLachlan Hodges 		mors->cmd_comp = &cmd_comp;
81*b1906ceaSLachlan Hodges 		if (retry > 0)
82*b1906ceaSLachlan Hodges 			reinit_completion(&cmd_comp);
83*b1906ceaSLachlan Hodges 		timeout = timeout ? timeout : HOST_CMD_DEFAULT_TIMEOUT_MS;
84*b1906ceaSLachlan Hodges 		ret = mm81x_skbq_skb_tx(cmd_q, &skb, NULL,
85*b1906ceaSLachlan Hodges 					MM81X_SKB_CHAN_COMMAND);
86*b1906ceaSLachlan Hodges 		mutex_unlock(&mors->cmd_lock);
87*b1906ceaSLachlan Hodges 
88*b1906ceaSLachlan Hodges 		if (ret) {
89*b1906ceaSLachlan Hodges 			dev_err(mors->dev, "mm81x_skbq_tx fail: %d", ret);
90*b1906ceaSLachlan Hodges 			break;
91*b1906ceaSLachlan Hodges 		}
92*b1906ceaSLachlan Hodges 
93*b1906ceaSLachlan Hodges 		wait_ret = wait_for_completion_timeout(
94*b1906ceaSLachlan Hodges 			&cmd_comp, msecs_to_jiffies(timeout));
95*b1906ceaSLachlan Hodges 		mutex_lock(&mors->cmd_lock);
96*b1906ceaSLachlan Hodges 		mors->cmd_comp = NULL;
97*b1906ceaSLachlan Hodges 
98*b1906ceaSLachlan Hodges 		if (!wait_ret) {
99*b1906ceaSLachlan Hodges 			dev_err(mors->dev,
100*b1906ceaSLachlan Hodges 				"Try:%d Command %04x:%04x timeout after %u ms",
101*b1906ceaSLachlan Hodges 				retry, le16_to_cpu(req->hdr.message_id),
102*b1906ceaSLachlan Hodges 				le16_to_cpu(req->hdr.host_id), timeout);
103*b1906ceaSLachlan Hodges 			ret = -ETIMEDOUT;
104*b1906ceaSLachlan Hodges 		} else {
105*b1906ceaSLachlan Hodges 			ret = (length && resp) ? le32_to_cpu(resp->status) :
106*b1906ceaSLachlan Hodges 						 resp_cb->ret;
107*b1906ceaSLachlan Hodges 			if (ret > 0 || ret < -MAX_ERRNO)
108*b1906ceaSLachlan Hodges 				ret = -EIO;
109*b1906ceaSLachlan Hodges 
110*b1906ceaSLachlan Hodges 			dev_dbg(mors->dev, "Command 0x%04x:%04x status 0x%08x",
111*b1906ceaSLachlan Hodges 				le16_to_cpu(req->hdr.message_id),
112*b1906ceaSLachlan Hodges 				le16_to_cpu(req->hdr.host_id), ret);
113*b1906ceaSLachlan Hodges 			if (ret) {
114*b1906ceaSLachlan Hodges 				dev_err(mors->dev,
115*b1906ceaSLachlan Hodges 					"Command 0x%04x:%04x error %d",
116*b1906ceaSLachlan Hodges 					le16_to_cpu(req->hdr.message_id),
117*b1906ceaSLachlan Hodges 					le16_to_cpu(req->hdr.host_id), ret);
118*b1906ceaSLachlan Hodges 			}
119*b1906ceaSLachlan Hodges 		}
120*b1906ceaSLachlan Hodges 		/* Free the command request */
121*b1906ceaSLachlan Hodges 		spin_lock_bh(&cmd_q->lock);
122*b1906ceaSLachlan Hodges 		mm81x_skbq_skb_finish(cmd_q, skb, NULL);
123*b1906ceaSLachlan Hodges 		spin_unlock_bh(&cmd_q->lock);
124*b1906ceaSLachlan Hodges 		mutex_unlock(&mors->cmd_lock);
125*b1906ceaSLachlan Hodges 
126*b1906ceaSLachlan Hodges 		retry++;
127*b1906ceaSLachlan Hodges 	} while ((ret == -ETIMEDOUT) && retry < MM_MAX_COMMAND_RETRY);
128*b1906ceaSLachlan Hodges 
129*b1906ceaSLachlan Hodges 	mm81x_ps_enable(mors);
130*b1906ceaSLachlan Hodges 	mutex_unlock(&mors->cmd_wait);
131*b1906ceaSLachlan Hodges 
132*b1906ceaSLachlan Hodges 	if (ret == -ETIMEDOUT) {
133*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Command %02x:%02x timed out",
134*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.message_id),
135*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.host_id));
136*b1906ceaSLachlan Hodges 	} else if (ret != 0) {
137*b1906ceaSLachlan Hodges 		dev_err(mors->dev,
138*b1906ceaSLachlan Hodges 			"Command %02x:%02x failed with rc %d (0x%x)\n",
139*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.message_id),
140*b1906ceaSLachlan Hodges 			le16_to_cpu(req->hdr.host_id), ret, ret);
141*b1906ceaSLachlan Hodges 	}
142*b1906ceaSLachlan Hodges 
143*b1906ceaSLachlan Hodges 	return ret;
144*b1906ceaSLachlan Hodges }
145*b1906ceaSLachlan Hodges 
mm81x_cmd_resp_process(struct mm81x * mors,struct sk_buff * skb)146*b1906ceaSLachlan Hodges int mm81x_cmd_resp_process(struct mm81x *mors, struct sk_buff *skb)
147*b1906ceaSLachlan Hodges {
148*b1906ceaSLachlan Hodges 	int length, ret = -ESRCH; /* No such process */
149*b1906ceaSLachlan Hodges 	struct mm81x_skbq *cmd_q = mm81x_hif_get_tx_cmd_queue(mors);
150*b1906ceaSLachlan Hodges 	struct host_cmd_resp *src_resp = (struct host_cmd_resp *)(skb->data);
151*b1906ceaSLachlan Hodges 	struct sk_buff *cmd_skb = NULL;
152*b1906ceaSLachlan Hodges 	struct host_cmd_resp_cb *resp_cb;
153*b1906ceaSLachlan Hodges 	struct host_cmd_resp *dest_resp;
154*b1906ceaSLachlan Hodges 	struct host_cmd_req *req;
155*b1906ceaSLachlan Hodges 	u16 message_id = 0;
156*b1906ceaSLachlan Hodges 	u16 host_id = 0;
157*b1906ceaSLachlan Hodges 	u16 resp_message_id = le16_to_cpu(src_resp->hdr.message_id);
158*b1906ceaSLachlan Hodges 	u16 resp_host_id = le16_to_cpu(src_resp->hdr.host_id);
159*b1906ceaSLachlan Hodges 	bool is_late_response = false;
160*b1906ceaSLachlan Hodges 
161*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "EVT 0x%04x:0x%04x", resp_message_id, resp_host_id);
162*b1906ceaSLachlan Hodges 
163*b1906ceaSLachlan Hodges 	if (!HOST_CMD_IS_RESP(src_resp)) {
164*b1906ceaSLachlan Hodges 		ret = mm81x_mac_event_recv(mors, skb);
165*b1906ceaSLachlan Hodges 		goto exit_free;
166*b1906ceaSLachlan Hodges 	}
167*b1906ceaSLachlan Hodges 
168*b1906ceaSLachlan Hodges 	mutex_lock(&mors->cmd_lock);
169*b1906ceaSLachlan Hodges 
170*b1906ceaSLachlan Hodges 	cmd_skb = mm81x_skbq_tx_pending(cmd_q);
171*b1906ceaSLachlan Hodges 	if (cmd_skb) {
172*b1906ceaSLachlan Hodges 		mm81x_skbq_pull_hdr_post_tx(cmd_skb);
173*b1906ceaSLachlan Hodges 		req = (struct host_cmd_req *)cmd_skb->data;
174*b1906ceaSLachlan Hodges 		message_id = le16_to_cpu(req->hdr.message_id);
175*b1906ceaSLachlan Hodges 		host_id = le16_to_cpu(req->hdr.host_id);
176*b1906ceaSLachlan Hodges 	}
177*b1906ceaSLachlan Hodges 
178*b1906ceaSLachlan Hodges 	/*
179*b1906ceaSLachlan Hodges 	 * If there is no pending command or the sequence ID does not match,
180*b1906ceaSLachlan Hodges 	 * this is a late response for a timed out command which has been
181*b1906ceaSLachlan Hodges 	 * cleaned up, so just free up the response. If a command was retried,
182*b1906ceaSLachlan Hodges 	 * the response may be from the retry or from the original command
183*b1906ceaSLachlan Hodges 	 * (late response) but not from both because the firmware will silently
184*b1906ceaSLachlan Hodges 	 * drop a retry if it received the initial request. So a mismatched
185*b1906ceaSLachlan Hodges 	 * retry counter is treated as a matched command and response.
186*b1906ceaSLachlan Hodges 	 */
187*b1906ceaSLachlan Hodges 	if (!cmd_skb || message_id != resp_message_id ||
188*b1906ceaSLachlan Hodges 	    (host_id & HOST_CMD_HOST_ID_SEQ_MASK) !=
189*b1906ceaSLachlan Hodges 		    (resp_host_id & HOST_CMD_HOST_ID_SEQ_MASK)) {
190*b1906ceaSLachlan Hodges 		dev_err(mors->dev,
191*b1906ceaSLachlan Hodges 			"Late response for timed out req 0x%04x:%04x have 0x%04x:%04x 0x%04x",
192*b1906ceaSLachlan Hodges 			resp_message_id, resp_host_id, message_id, host_id,
193*b1906ceaSLachlan Hodges 			mors->cmd_seq);
194*b1906ceaSLachlan Hodges 		is_late_response = true;
195*b1906ceaSLachlan Hodges 		goto exit;
196*b1906ceaSLachlan Hodges 	}
197*b1906ceaSLachlan Hodges 	if ((host_id & HOST_CMD_HOST_ID_RETRY_MASK) !=
198*b1906ceaSLachlan Hodges 	    (resp_host_id & HOST_CMD_HOST_ID_RETRY_MASK))
199*b1906ceaSLachlan Hodges 		dev_dbg(mors->dev,
200*b1906ceaSLachlan Hodges 			"Command retry mismatch 0x%04x:%04x 0x%04x:%04x",
201*b1906ceaSLachlan Hodges 			message_id, host_id, resp_message_id, resp_host_id);
202*b1906ceaSLachlan Hodges 
203*b1906ceaSLachlan Hodges 	resp_cb = (struct host_cmd_resp_cb *)IEEE80211_SKB_CB(cmd_skb)
204*b1906ceaSLachlan Hodges 			  ->driver_data;
205*b1906ceaSLachlan Hodges 	length = resp_cb->length;
206*b1906ceaSLachlan Hodges 	dest_resp = resp_cb->dest_resp;
207*b1906ceaSLachlan Hodges 	if (length >= sizeof(struct host_cmd_resp) && dest_resp) {
208*b1906ceaSLachlan Hodges 		ret = 0;
209*b1906ceaSLachlan Hodges 		length = min_t(int, length,
210*b1906ceaSLachlan Hodges 			       le16_to_cpu(src_resp->hdr.len) +
211*b1906ceaSLachlan Hodges 				       sizeof(struct host_cmd_header));
212*b1906ceaSLachlan Hodges 		memcpy(dest_resp, src_resp, length);
213*b1906ceaSLachlan Hodges 	} else {
214*b1906ceaSLachlan Hodges 		ret = le32_to_cpu(src_resp->status);
215*b1906ceaSLachlan Hodges 	}
216*b1906ceaSLachlan Hodges 
217*b1906ceaSLachlan Hodges 	resp_cb->ret = ret;
218*b1906ceaSLachlan Hodges 
219*b1906ceaSLachlan Hodges exit:
220*b1906ceaSLachlan Hodges 	if (cmd_skb && !is_late_response) {
221*b1906ceaSLachlan Hodges 		/* Complete if not already timed out */
222*b1906ceaSLachlan Hodges 		if (mors->cmd_comp)
223*b1906ceaSLachlan Hodges 			complete(mors->cmd_comp);
224*b1906ceaSLachlan Hodges 	}
225*b1906ceaSLachlan Hodges 
226*b1906ceaSLachlan Hodges 	mutex_unlock(&mors->cmd_lock);
227*b1906ceaSLachlan Hodges exit_free:
228*b1906ceaSLachlan Hodges 	dev_kfree_skb(skb);
229*b1906ceaSLachlan Hodges 	return 0;
230*b1906ceaSLachlan Hodges }
231*b1906ceaSLachlan Hodges 
mm81x_cmd_sta_state(struct mm81x * mors,struct mm81x_vif * mors_vif,u16 aid,struct ieee80211_sta * sta,enum ieee80211_sta_state state)232*b1906ceaSLachlan Hodges int mm81x_cmd_sta_state(struct mm81x *mors, struct mm81x_vif *mors_vif, u16 aid,
233*b1906ceaSLachlan Hodges 			struct ieee80211_sta *sta,
234*b1906ceaSLachlan Hodges 			enum ieee80211_sta_state state)
235*b1906ceaSLachlan Hodges {
236*b1906ceaSLachlan Hodges 	struct host_cmd_req_set_sta_state req = {
237*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_SET_STA_STATE,
238*b1906ceaSLachlan Hodges 				    mors_vif->id),
239*b1906ceaSLachlan Hodges 		.aid = cpu_to_le16(aid),
240*b1906ceaSLachlan Hodges 		.state = cpu_to_le16(state),
241*b1906ceaSLachlan Hodges 		.uapsd_queues = sta->uapsd_queues,
242*b1906ceaSLachlan Hodges 	};
243*b1906ceaSLachlan Hodges 
244*b1906ceaSLachlan Hodges 	memcpy(req.sta_addr, sta->addr, sizeof(req.sta_addr));
245*b1906ceaSLachlan Hodges 
246*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
247*b1906ceaSLachlan Hodges }
248*b1906ceaSLachlan Hodges 
mm81x_cmd_add_if(struct mm81x * mors,u16 * vif_id,const u8 * addr,enum nl80211_iftype type)249*b1906ceaSLachlan Hodges int mm81x_cmd_add_if(struct mm81x *mors, u16 *vif_id, const u8 *addr,
250*b1906ceaSLachlan Hodges 		     enum nl80211_iftype type)
251*b1906ceaSLachlan Hodges {
252*b1906ceaSLachlan Hodges 	int ret;
253*b1906ceaSLachlan Hodges 	struct host_cmd_req_add_interface req = {
254*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_ADD_INTERFACE, 0),
255*b1906ceaSLachlan Hodges 	};
256*b1906ceaSLachlan Hodges 	struct host_cmd_resp_add_interface resp;
257*b1906ceaSLachlan Hodges 
258*b1906ceaSLachlan Hodges 	switch (type) {
259*b1906ceaSLachlan Hodges 	case NL80211_IFTYPE_STATION:
260*b1906ceaSLachlan Hodges 		req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_STA);
261*b1906ceaSLachlan Hodges 		break;
262*b1906ceaSLachlan Hodges 	case NL80211_IFTYPE_AP:
263*b1906ceaSLachlan Hodges 		req.interface_type = cpu_to_le32(HOST_CMD_INTERFACE_TYPE_AP);
264*b1906ceaSLachlan Hodges 		break;
265*b1906ceaSLachlan Hodges 	default:
266*b1906ceaSLachlan Hodges 		return -EOPNOTSUPP;
267*b1906ceaSLachlan Hodges 	}
268*b1906ceaSLachlan Hodges 
269*b1906ceaSLachlan Hodges 	memcpy(req.addr.octet, addr, sizeof(req.addr.octet));
270*b1906ceaSLachlan Hodges 
271*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&resp,
272*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(resp), 0);
273*b1906ceaSLachlan Hodges 	if (!ret)
274*b1906ceaSLachlan Hodges 		*vif_id = le16_to_cpu(resp.hdr.vif_id);
275*b1906ceaSLachlan Hodges 
276*b1906ceaSLachlan Hodges 	return ret;
277*b1906ceaSLachlan Hodges }
278*b1906ceaSLachlan Hodges 
mm81x_cmd_get_capabilities(struct mm81x * mors,u16 vif_id,struct mm81x_fw_caps * capabilities)279*b1906ceaSLachlan Hodges int mm81x_cmd_get_capabilities(struct mm81x *mors, u16 vif_id,
280*b1906ceaSLachlan Hodges 			       struct mm81x_fw_caps *capabilities)
281*b1906ceaSLachlan Hodges {
282*b1906ceaSLachlan Hodges 	int ret;
283*b1906ceaSLachlan Hodges 	int i;
284*b1906ceaSLachlan Hodges 	struct host_cmd_req_get_capabilities req = {
285*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_GET_CAPABILITIES, vif_id),
286*b1906ceaSLachlan Hodges 	};
287*b1906ceaSLachlan Hodges 	struct host_cmd_resp_get_capabilities rsp;
288*b1906ceaSLachlan Hodges 
289*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&rsp,
290*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(rsp), 0);
291*b1906ceaSLachlan Hodges 	if (ret)
292*b1906ceaSLachlan Hodges 		return ret;
293*b1906ceaSLachlan Hodges 
294*b1906ceaSLachlan Hodges 	capabilities->ampdu_mss = rsp.capabilities.ampdu_mss;
295*b1906ceaSLachlan Hodges 	capabilities->mm81x_mmss_offset = rsp.morse_mmss_offset;
296*b1906ceaSLachlan Hodges 	capabilities->beamformee_sts_capability =
297*b1906ceaSLachlan Hodges 		rsp.capabilities.beamformee_sts_capability;
298*b1906ceaSLachlan Hodges 	capabilities->maximum_ampdu_length_exponent =
299*b1906ceaSLachlan Hodges 		rsp.capabilities.maximum_ampdu_length_exponent;
300*b1906ceaSLachlan Hodges 	capabilities->number_sounding_dimensions =
301*b1906ceaSLachlan Hodges 		rsp.capabilities.number_sounding_dimensions;
302*b1906ceaSLachlan Hodges 	for (i = 0; i < FW_CAPABILITIES_FLAGS_WIDTH; i++)
303*b1906ceaSLachlan Hodges 		capabilities->flags[i] = le32_to_cpu(rsp.capabilities.flags[i]);
304*b1906ceaSLachlan Hodges 
305*b1906ceaSLachlan Hodges 	return ret;
306*b1906ceaSLachlan Hodges }
307*b1906ceaSLachlan Hodges 
mm81x_cmd_get_max_txpower(struct mm81x * mors,s32 * out_power_mbm)308*b1906ceaSLachlan Hodges int mm81x_cmd_get_max_txpower(struct mm81x *mors, s32 *out_power_mbm)
309*b1906ceaSLachlan Hodges {
310*b1906ceaSLachlan Hodges 	int ret;
311*b1906ceaSLachlan Hodges 	struct host_cmd_req_get_max_txpower req = {
312*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_GET_MAX_TXPOWER, 0),
313*b1906ceaSLachlan Hodges 	};
314*b1906ceaSLachlan Hodges 	struct host_cmd_resp_get_max_txpower resp;
315*b1906ceaSLachlan Hodges 
316*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&resp,
317*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(resp), 0);
318*b1906ceaSLachlan Hodges 	if (!ret)
319*b1906ceaSLachlan Hodges 		*out_power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
320*b1906ceaSLachlan Hodges 
321*b1906ceaSLachlan Hodges 	return ret;
322*b1906ceaSLachlan Hodges }
323*b1906ceaSLachlan Hodges 
mm81x_cmd_hw_scan(struct mm81x * mors,struct mm81x_hw_scan_params * params,bool store)324*b1906ceaSLachlan Hodges int mm81x_cmd_hw_scan(struct mm81x *mors, struct mm81x_hw_scan_params *params,
325*b1906ceaSLachlan Hodges 		      bool store)
326*b1906ceaSLachlan Hodges {
327*b1906ceaSLachlan Hodges 	int ret;
328*b1906ceaSLachlan Hodges 	struct host_cmd_req_hw_scan *req;
329*b1906ceaSLachlan Hodges 	size_t cmd_size;
330*b1906ceaSLachlan Hodges 	u8 *buf;
331*b1906ceaSLachlan Hodges 	u32 flags = 0;
332*b1906ceaSLachlan Hodges 
333*b1906ceaSLachlan Hodges 	cmd_size = mm81x_hw_scan_h_get_cmd_size(params);
334*b1906ceaSLachlan Hodges 	cmd_size = ROUND_BYTES_TO_WORD(cmd_size);
335*b1906ceaSLachlan Hodges 
336*b1906ceaSLachlan Hodges 	req = kzalloc(cmd_size, GFP_KERNEL);
337*b1906ceaSLachlan Hodges 	if (!req)
338*b1906ceaSLachlan Hodges 		return -ENOMEM;
339*b1906ceaSLachlan Hodges 
340*b1906ceaSLachlan Hodges 	buf = req->variable;
341*b1906ceaSLachlan Hodges 
342*b1906ceaSLachlan Hodges 	if (store)
343*b1906ceaSLachlan Hodges 		flags = HOST_CMD_HW_SCAN_FLAGS_STORE;
344*b1906ceaSLachlan Hodges 	else if (params->operation == MM81X_HW_SCAN_OP_START)
345*b1906ceaSLachlan Hodges 		flags |= HOST_CMD_HW_SCAN_FLAGS_START;
346*b1906ceaSLachlan Hodges 	else if (params->operation == MM81X_HW_SCAN_OP_STOP)
347*b1906ceaSLachlan Hodges 		flags |= HOST_CMD_HW_SCAN_FLAGS_ABORT;
348*b1906ceaSLachlan Hodges 
349*b1906ceaSLachlan Hodges 	flags |= HOST_CMD_HW_SCAN_FLAGS_1MHZ_PROBES;
350*b1906ceaSLachlan Hodges 
351*b1906ceaSLachlan Hodges 	if (params->operation == MM81X_HW_SCAN_OP_START) {
352*b1906ceaSLachlan Hodges 		req->dwell_time_ms = cpu_to_le32(params->dwell_time_ms);
353*b1906ceaSLachlan Hodges 		buf = mm81x_hw_scan_h_insert_tlvs(params, buf);
354*b1906ceaSLachlan Hodges 	}
355*b1906ceaSLachlan Hodges 
356*b1906ceaSLachlan Hodges 	req->flags = cpu_to_le32(flags);
357*b1906ceaSLachlan Hodges 	req->hdr = INIT_CMD_HDR((*req), HOST_CMD_ID_HW_SCAN, 0);
358*b1906ceaSLachlan Hodges 	req->hdr.len = cpu_to_le16((u16)((buf - (u8 *)req) - sizeof(req->hdr)));
359*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)req, 0, 0);
360*b1906ceaSLachlan Hodges 	kfree(req);
361*b1906ceaSLachlan Hodges 
362*b1906ceaSLachlan Hodges 	return ret;
363*b1906ceaSLachlan Hodges }
364*b1906ceaSLachlan Hodges 
mm81x_cmd_set_txpower(struct mm81x * mors,s32 * out_power_mbm,int txpower_mbm)365*b1906ceaSLachlan Hodges int mm81x_cmd_set_txpower(struct mm81x *mors, s32 *out_power_mbm,
366*b1906ceaSLachlan Hodges 			  int txpower_mbm)
367*b1906ceaSLachlan Hodges {
368*b1906ceaSLachlan Hodges 	int ret;
369*b1906ceaSLachlan Hodges 	struct host_cmd_req_set_txpower req = {
370*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_SET_TXPOWER, 0),
371*b1906ceaSLachlan Hodges 		.power_qdbm = cpu_to_le32(MBM_TO_QDBM(txpower_mbm)),
372*b1906ceaSLachlan Hodges 	};
373*b1906ceaSLachlan Hodges 	struct host_cmd_resp_set_txpower resp;
374*b1906ceaSLachlan Hodges 
375*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&resp,
376*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(resp), 0);
377*b1906ceaSLachlan Hodges 	if (!ret)
378*b1906ceaSLachlan Hodges 		*out_power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
379*b1906ceaSLachlan Hodges 
380*b1906ceaSLachlan Hodges 	return ret;
381*b1906ceaSLachlan Hodges }
382*b1906ceaSLachlan Hodges 
mm81x_cmd_set_channel(struct mm81x * mors,u32 op_chan_freq_hz,u8 pri_1mhz_chan_idx,u8 op_bw_mhz,u8 pri_bw_mhz,s32 * power_mbm)383*b1906ceaSLachlan Hodges int mm81x_cmd_set_channel(struct mm81x *mors, u32 op_chan_freq_hz,
384*b1906ceaSLachlan Hodges 			  u8 pri_1mhz_chan_idx, u8 op_bw_mhz, u8 pri_bw_mhz,
385*b1906ceaSLachlan Hodges 			  s32 *power_mbm)
386*b1906ceaSLachlan Hodges {
387*b1906ceaSLachlan Hodges 	int ret;
388*b1906ceaSLachlan Hodges 	struct host_cmd_req_set_channel req = {
389*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_SET_CHANNEL, 0),
390*b1906ceaSLachlan Hodges 		.op_chan_freq_hz = cpu_to_le32(op_chan_freq_hz),
391*b1906ceaSLachlan Hodges 		.op_bw_mhz = op_bw_mhz,
392*b1906ceaSLachlan Hodges 		.pri_bw_mhz = pri_bw_mhz,
393*b1906ceaSLachlan Hodges 		.pri_1mhz_chan_idx = pri_1mhz_chan_idx,
394*b1906ceaSLachlan Hodges 		.dot11_mode = HOST_CMD_DOT11_PROTO_MODE_AH,
395*b1906ceaSLachlan Hodges 	};
396*b1906ceaSLachlan Hodges 	struct host_cmd_resp_set_channel resp;
397*b1906ceaSLachlan Hodges 
398*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&resp,
399*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(resp), 0);
400*b1906ceaSLachlan Hodges 	if (!ret)
401*b1906ceaSLachlan Hodges 		*power_mbm = QDBM_TO_MBM(le32_to_cpu(resp.power_qdbm));
402*b1906ceaSLachlan Hodges 
403*b1906ceaSLachlan Hodges 	return ret;
404*b1906ceaSLachlan Hodges }
405*b1906ceaSLachlan Hodges 
mm81x_cmd_disable_key(struct mm81x * mors,struct mm81x_vif * mors_vif,u16 aid,struct ieee80211_key_conf * key)406*b1906ceaSLachlan Hodges int mm81x_cmd_disable_key(struct mm81x *mors, struct mm81x_vif *mors_vif,
407*b1906ceaSLachlan Hodges 			  u16 aid, struct ieee80211_key_conf *key)
408*b1906ceaSLachlan Hodges {
409*b1906ceaSLachlan Hodges 	struct host_cmd_req_disable_key req = {
410*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_DISABLE_KEY, mors_vif->id),
411*b1906ceaSLachlan Hodges 		.aid = cpu_to_le32(aid),
412*b1906ceaSLachlan Hodges 		.key_idx = key->hw_key_idx,
413*b1906ceaSLachlan Hodges 		.key_type =
414*b1906ceaSLachlan Hodges 			cpu_to_le32((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
415*b1906ceaSLachlan Hodges 					    HOST_CMD_TEMPORAL_KEY_TYPE_PTK :
416*b1906ceaSLachlan Hodges 					    HOST_CMD_TEMPORAL_KEY_TYPE_GTK),
417*b1906ceaSLachlan Hodges 	};
418*b1906ceaSLachlan Hodges 
419*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
420*b1906ceaSLachlan Hodges }
421*b1906ceaSLachlan Hodges 
mm81x_cmd_install_key(struct mm81x * mors,struct mm81x_vif * mors_vif,u16 aid,struct ieee80211_key_conf * key,enum host_cmd_key_cipher cipher,enum host_cmd_aes_key_len length)422*b1906ceaSLachlan Hodges int mm81x_cmd_install_key(struct mm81x *mors, struct mm81x_vif *mors_vif,
423*b1906ceaSLachlan Hodges 			  u16 aid, struct ieee80211_key_conf *key,
424*b1906ceaSLachlan Hodges 			  enum host_cmd_key_cipher cipher,
425*b1906ceaSLachlan Hodges 			  enum host_cmd_aes_key_len length)
426*b1906ceaSLachlan Hodges {
427*b1906ceaSLachlan Hodges 	int ret;
428*b1906ceaSLachlan Hodges 	struct host_cmd_req_install_key req = {
429*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_INSTALL_KEY, mors_vif->id),
430*b1906ceaSLachlan Hodges 		.pn = cpu_to_le64(atomic64_read(&key->tx_pn)),
431*b1906ceaSLachlan Hodges 		.aid = cpu_to_le32(aid),
432*b1906ceaSLachlan Hodges 		.cipher = cipher,
433*b1906ceaSLachlan Hodges 		.key_length = length,
434*b1906ceaSLachlan Hodges 		.key_idx = key->keyidx,
435*b1906ceaSLachlan Hodges 		.key_type = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
436*b1906ceaSLachlan Hodges 				    HOST_CMD_TEMPORAL_KEY_TYPE_PTK :
437*b1906ceaSLachlan Hodges 				    HOST_CMD_TEMPORAL_KEY_TYPE_GTK,
438*b1906ceaSLachlan Hodges 	};
439*b1906ceaSLachlan Hodges 	struct host_cmd_resp_install_key resp;
440*b1906ceaSLachlan Hodges 
441*b1906ceaSLachlan Hodges 	if (key->keylen > sizeof(req.key))
442*b1906ceaSLachlan Hodges 		return -EINVAL;
443*b1906ceaSLachlan Hodges 
444*b1906ceaSLachlan Hodges 	memcpy(req.key, key->key, key->keylen);
445*b1906ceaSLachlan Hodges 
446*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, (struct host_cmd_resp *)&resp,
447*b1906ceaSLachlan Hodges 			   (struct host_cmd_req *)&req, sizeof(resp), 0);
448*b1906ceaSLachlan Hodges 	if (!ret) {
449*b1906ceaSLachlan Hodges 		key->hw_key_idx = resp.key_idx;
450*b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "Installed key @ hw index: %d",
451*b1906ceaSLachlan Hodges 			resp.key_idx);
452*b1906ceaSLachlan Hodges 	}
453*b1906ceaSLachlan Hodges 
454*b1906ceaSLachlan Hodges 	return ret;
455*b1906ceaSLachlan Hodges }
456*b1906ceaSLachlan Hodges 
mm81x_cmd_cfg_multicast_filter(struct mm81x * mors,struct mm81x_vif * mors_vif)457*b1906ceaSLachlan Hodges int mm81x_cmd_cfg_multicast_filter(struct mm81x *mors,
458*b1906ceaSLachlan Hodges 				   struct mm81x_vif *mors_vif)
459*b1906ceaSLachlan Hodges {
460*b1906ceaSLachlan Hodges 	struct host_cmd_req_mcast_filter *req;
461*b1906ceaSLachlan Hodges 	struct mcast_filter *filter = mors->mcast_filter;
462*b1906ceaSLachlan Hodges 	u16 filter_list_len = sizeof(filter->addr_list[0]) * filter->count;
463*b1906ceaSLachlan Hodges 	u16 alloc_len = filter_list_len + sizeof(*req);
464*b1906ceaSLachlan Hodges 	int ret = 0;
465*b1906ceaSLachlan Hodges 
466*b1906ceaSLachlan Hodges 	req = kzalloc(alloc_len, GFP_KERNEL);
467*b1906ceaSLachlan Hodges 	if (!req)
468*b1906ceaSLachlan Hodges 		return -ENOMEM;
469*b1906ceaSLachlan Hodges 
470*b1906ceaSLachlan Hodges 	req->hdr = INIT_CMD_HDR((*req), HOST_CMD_ID_MCAST_FILTER, mors_vif->id);
471*b1906ceaSLachlan Hodges 	req->hdr.len = cpu_to_le16(alloc_len - sizeof(req->hdr));
472*b1906ceaSLachlan Hodges 	req->count = filter->count;
473*b1906ceaSLachlan Hodges 	memcpy(req->hw_addr, filter->addr_list, filter_list_len);
474*b1906ceaSLachlan Hodges 
475*b1906ceaSLachlan Hodges 	ret = mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)req, 0, 0);
476*b1906ceaSLachlan Hodges 	kfree(req);
477*b1906ceaSLachlan Hodges 	return ret;
478*b1906ceaSLachlan Hodges }
479*b1906ceaSLachlan Hodges 
mm81x_cmd_cfg_bss(struct mm81x * mors,u16 vif_id,u16 beacon_int,u16 dtim_period,u32 cssid)480*b1906ceaSLachlan Hodges int mm81x_cmd_cfg_bss(struct mm81x *mors, u16 vif_id, u16 beacon_int,
481*b1906ceaSLachlan Hodges 		      u16 dtim_period, u32 cssid)
482*b1906ceaSLachlan Hodges {
483*b1906ceaSLachlan Hodges 	struct host_cmd_req_bss_config req = {
484*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_BSS_CONFIG, vif_id),
485*b1906ceaSLachlan Hodges 		.beacon_interval_tu = cpu_to_le16(beacon_int),
486*b1906ceaSLachlan Hodges 		.cssid = cpu_to_le32(cssid),
487*b1906ceaSLachlan Hodges 		.dtim_period = cpu_to_le16(dtim_period),
488*b1906ceaSLachlan Hodges 	};
489*b1906ceaSLachlan Hodges 
490*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
491*b1906ceaSLachlan Hodges }
492*b1906ceaSLachlan Hodges 
mm81x_cmd_config_beacon_timer(struct mm81x * mors,void * mm81x_vif,bool enabled)493*b1906ceaSLachlan Hodges int mm81x_cmd_config_beacon_timer(struct mm81x *mors, void *mm81x_vif,
494*b1906ceaSLachlan Hodges 				  bool enabled)
495*b1906ceaSLachlan Hodges {
496*b1906ceaSLachlan Hodges 	struct mm81x_vif *vif = mm81x_vif;
497*b1906ceaSLachlan Hodges 	struct host_cmd_req_bss_beacon_config req = {
498*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_BSS_BEACON_CONFIG,
499*b1906ceaSLachlan Hodges 				    vif->id),
500*b1906ceaSLachlan Hodges 		.enable = enabled,
501*b1906ceaSLachlan Hodges 	};
502*b1906ceaSLachlan Hodges 
503*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
504*b1906ceaSLachlan Hodges }
505*b1906ceaSLachlan Hodges 
mm81x_cmd_set_ps(struct mm81x * mors,bool enabled)506*b1906ceaSLachlan Hodges int mm81x_cmd_set_ps(struct mm81x *mors, bool enabled)
507*b1906ceaSLachlan Hodges {
508*b1906ceaSLachlan Hodges 	struct host_cmd_req_config_ps req = {
509*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_CONFIG_PS, 0),
510*b1906ceaSLachlan Hodges 		.enabled = (u8)enabled,
511*b1906ceaSLachlan Hodges 	};
512*b1906ceaSLachlan Hodges 
513*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0,
514*b1906ceaSLachlan Hodges 			    HOST_CMD_POWERSAVE_TIMEOUT_MS);
515*b1906ceaSLachlan Hodges }
516*b1906ceaSLachlan Hodges 
mm81x_cmd_cfg_qos(struct mm81x * mors,struct mm81x_queue_params * params)517*b1906ceaSLachlan Hodges int mm81x_cmd_cfg_qos(struct mm81x *mors, struct mm81x_queue_params *params)
518*b1906ceaSLachlan Hodges {
519*b1906ceaSLachlan Hodges 	struct host_cmd_req_set_qos_params req = {
520*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_SET_QOS_PARAMS, 0),
521*b1906ceaSLachlan Hodges 		.uapsd = params->uapsd,
522*b1906ceaSLachlan Hodges 		.queue_idx = params->aci,
523*b1906ceaSLachlan Hodges 		.aifs_slot_count = params->aifs,
524*b1906ceaSLachlan Hodges 		.contention_window_min = cpu_to_le16(params->cw_min),
525*b1906ceaSLachlan Hodges 		.contention_window_max = cpu_to_le16(params->cw_max),
526*b1906ceaSLachlan Hodges 		.max_txop_usec = cpu_to_le32(params->txop),
527*b1906ceaSLachlan Hodges 	};
528*b1906ceaSLachlan Hodges 
529*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
530*b1906ceaSLachlan Hodges }
531*b1906ceaSLachlan Hodges 
mm81x_cmd_rm_if(struct mm81x * mors,u16 vif_id)532*b1906ceaSLachlan Hodges int mm81x_cmd_rm_if(struct mm81x *mors, u16 vif_id)
533*b1906ceaSLachlan Hodges {
534*b1906ceaSLachlan Hodges 	struct host_cmd_req_remove_interface req = {
535*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_REMOVE_INTERFACE, vif_id),
536*b1906ceaSLachlan Hodges 	};
537*b1906ceaSLachlan Hodges 
538*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
539*b1906ceaSLachlan Hodges }
540*b1906ceaSLachlan Hodges 
mm81x_cmd_set_frag_threshold(struct mm81x * mors,u32 frag_threshold)541*b1906ceaSLachlan Hodges int mm81x_cmd_set_frag_threshold(struct mm81x *mors, u32 frag_threshold)
542*b1906ceaSLachlan Hodges {
543*b1906ceaSLachlan Hodges 	struct host_cmd_req_get_set_generic_param req = {
544*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_GET_SET_GENERIC_PARAM, 0),
545*b1906ceaSLachlan Hodges 		.param_id = cpu_to_le32(HOST_CMD_PARAM_ID_FRAGMENT_THRESHOLD),
546*b1906ceaSLachlan Hodges 		.action = cpu_to_le32(HOST_CMD_PARAM_ACTION_SET),
547*b1906ceaSLachlan Hodges 		.value = cpu_to_le32(frag_threshold),
548*b1906ceaSLachlan Hodges 	};
549*b1906ceaSLachlan Hodges 
550*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, NULL, (struct host_cmd_req *)&req, 0, 0);
551*b1906ceaSLachlan Hodges }
552*b1906ceaSLachlan Hodges 
mm81x_cmd_get_disabled_channels(struct mm81x * mors,struct host_cmd_resp_get_disabled_channels * resp,uint resp_len)553*b1906ceaSLachlan Hodges int mm81x_cmd_get_disabled_channels(
554*b1906ceaSLachlan Hodges 	struct mm81x *mors, struct host_cmd_resp_get_disabled_channels *resp,
555*b1906ceaSLachlan Hodges 	uint resp_len)
556*b1906ceaSLachlan Hodges {
557*b1906ceaSLachlan Hodges 	struct host_cmd_req req = {
558*b1906ceaSLachlan Hodges 		.hdr = INIT_CMD_HDR(req, HOST_CMD_ID_GET_DISABLED_CHANNELS, 0),
559*b1906ceaSLachlan Hodges 	};
560*b1906ceaSLachlan Hodges 
561*b1906ceaSLachlan Hodges 	return mm81x_cmd_tx(mors, (struct host_cmd_resp *)resp, &req, resp_len,
562*b1906ceaSLachlan Hodges 			    0);
563*b1906ceaSLachlan Hodges }
564