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