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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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