1 /* 2 * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "htc_debug.h" 20 #include "htc_internal.h" 21 #include "htc_credit_history.h" 22 #include "htc_hang_event.h" 23 #include <hif.h> 24 #include <qdf_nbuf.h> /* qdf_nbuf_t */ 25 #include <qdf_types.h> /* qdf_print */ 26 27 #define MAX_HTC_RX_BUNDLE 2 28 29 #if defined(WLAN_DEBUG) || defined(DEBUG) 30 static ATH_DEBUG_MASK_DESCRIPTION g_htc_debug_description[] = { 31 {ATH_DEBUG_SEND, "Send"}, 32 {ATH_DEBUG_RECV, "Recv"}, 33 {ATH_DEBUG_SYNC, "Sync"}, 34 {ATH_DEBUG_DUMP, "Dump Data (RX or TX)"}, 35 {ATH_DEBUG_SETUP, "Setup"}, 36 }; 37 38 ATH_DEBUG_INSTANTIATE_MODULE_VAR(htc, 39 "htc", 40 "Host Target Communications", 41 ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_INFO | 42 ATH_DEBUG_SETUP, 43 ATH_DEBUG_DESCRIPTION_COUNT 44 (g_htc_debug_description), 45 g_htc_debug_description); 46 47 #endif 48 49 #if (defined(WMI_MULTI_MAC_SVC) || defined(QCA_WIFI_QCA8074) || \ 50 defined(QCA_WIFI_QCA6018) || defined(QCA_WIFI_QCA9574)) 51 static const uint32_t svc_id[] = {WMI_CONTROL_SVC, WMI_CONTROL_SVC_WMAC1, 52 WMI_CONTROL_SVC_WMAC2}; 53 #else 54 static const uint32_t svc_id[] = {WMI_CONTROL_SVC}; 55 #endif 56 57 extern unsigned int htc_credit_flow; 58 59 static void reset_endpoint_states(HTC_TARGET *target); 60 61 static void destroy_htc_tx_ctrl_packet(HTC_PACKET *pPacket) 62 { 63 qdf_nbuf_t netbuf; 64 65 netbuf = (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); 66 if (netbuf) 67 qdf_nbuf_free(netbuf); 68 qdf_mem_free(pPacket); 69 } 70 71 static HTC_PACKET *build_htc_tx_ctrl_packet(qdf_device_t osdev) 72 { 73 HTC_PACKET *pPacket = NULL; 74 qdf_nbuf_t netbuf; 75 76 do { 77 pPacket = (HTC_PACKET *) qdf_mem_malloc(sizeof(HTC_PACKET)); 78 if (!pPacket) 79 break; 80 netbuf = qdf_nbuf_alloc(osdev, HTC_CONTROL_BUFFER_SIZE, 81 20, 4, true); 82 if (!netbuf) { 83 qdf_mem_free(pPacket); 84 pPacket = NULL; 85 break; 86 } 87 SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf); 88 } while (false); 89 90 return pPacket; 91 } 92 93 void htc_free_control_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket) 94 { 95 96 #ifdef TODO_FIXME 97 LOCK_HTC(target); 98 HTC_PACKET_ENQUEUE(&target->ControlBufferTXFreeList, pPacket); 99 UNLOCK_HTC(target); 100 /* TODO_FIXME netbufs cannot be RESET! */ 101 #else 102 destroy_htc_tx_ctrl_packet(pPacket); 103 #endif 104 105 } 106 107 HTC_PACKET *htc_alloc_control_tx_packet(HTC_TARGET *target) 108 { 109 #ifdef TODO_FIXME 110 HTC_PACKET *pPacket; 111 112 LOCK_HTC(target); 113 pPacket = htc_packet_dequeue(&target->ControlBufferTXFreeList); 114 UNLOCK_HTC(target); 115 116 return pPacket; 117 #else 118 return build_htc_tx_ctrl_packet(target->osdev); 119 #endif 120 } 121 122 /* Set the target failure handling callback */ 123 void htc_set_target_failure_callback(HTC_HANDLE HTCHandle, 124 HTC_TARGET_FAILURE Callback) 125 { 126 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 127 128 target->HTCInitInfo.TargetFailure = Callback; 129 } 130 131 void htc_dump(HTC_HANDLE HTCHandle, uint8_t CmdId, bool start) 132 { 133 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 134 135 hif_dump(target->hif_dev, CmdId, start); 136 } 137 138 void htc_ce_tasklet_debug_dump(HTC_HANDLE htc_handle) 139 { 140 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 141 142 if (!target->hif_dev) 143 return; 144 145 hif_display_stats(target->hif_dev); 146 } 147 148 /* cleanup the HTC instance */ 149 static void htc_cleanup(HTC_TARGET *target) 150 { 151 HTC_PACKET *pPacket; 152 int i; 153 HTC_ENDPOINT *endpoint; 154 HTC_PACKET_QUEUE *pkt_queue; 155 qdf_nbuf_t netbuf; 156 157 while (htc_dec_return_runtime_cnt((void *)target) >= 0) { 158 hif_pm_runtime_put(target->hif_dev, RTPM_ID_HTC); 159 hif_pm_runtime_update_stats(target->hif_dev, RTPM_ID_HTC, 160 HIF_PM_HTC_STATS_PUT_HTC_CLEANUP); 161 } 162 163 if (target->hif_dev) { 164 hif_detach_htc(target->hif_dev); 165 hif_mask_interrupt_call(target->hif_dev); 166 target->hif_dev = NULL; 167 } 168 169 while (true) { 170 pPacket = allocate_htc_packet_container(target); 171 if (!pPacket) 172 break; 173 qdf_mem_free(pPacket); 174 } 175 176 LOCK_HTC_TX(target); 177 pPacket = target->pBundleFreeList; 178 target->pBundleFreeList = NULL; 179 UNLOCK_HTC_TX(target); 180 while (pPacket) { 181 HTC_PACKET *pPacketTmp = (HTC_PACKET *) pPacket->ListLink.pNext; 182 netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); 183 if (netbuf) 184 qdf_nbuf_free(netbuf); 185 pkt_queue = pPacket->pContext; 186 if (pkt_queue) 187 qdf_mem_free(pkt_queue); 188 qdf_mem_free(pPacket); 189 pPacket = pPacketTmp; 190 } 191 192 #ifdef TODO_FIXME 193 while (true) { 194 pPacket = htc_alloc_control_tx_packet(target); 195 if (!pPacket) 196 break; 197 netbuf = (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); 198 if (netbuf) 199 qdf_nbuf_free(netbuf); 200 qdf_mem_free(pPacket); 201 } 202 #endif 203 204 htc_flush_endpoint_txlookupQ(target, ENDPOINT_0, true); 205 206 qdf_spinlock_destroy(&target->HTCLock); 207 qdf_spinlock_destroy(&target->HTCRxLock); 208 qdf_spinlock_destroy(&target->HTCTxLock); 209 for (i = 0; i < ENDPOINT_MAX; i++) { 210 endpoint = &target->endpoint[i]; 211 qdf_spinlock_destroy(&endpoint->lookup_queue_lock); 212 } 213 214 /* free our instance */ 215 qdf_mem_free(target); 216 } 217 218 #ifdef FEATURE_RUNTIME_PM 219 /** 220 * htc_runtime_pm_init(): runtime pm related intialization 221 * 222 * need to initialize a work item. 223 */ 224 static void htc_runtime_pm_init(HTC_TARGET *target) 225 { 226 qdf_create_work(0, &target->queue_kicker, htc_kick_queues, target); 227 } 228 229 /** 230 * htc_runtime_suspend() - runtime suspend HTC 231 * 232 * @htc_ctx: HTC context pointer 233 * 234 * This is a dummy function for symmetry. 235 * 236 * Return: 0 for success 237 */ 238 int htc_runtime_suspend(HTC_HANDLE htc_ctx) 239 { 240 return 0; 241 } 242 243 /** 244 * htc_runtime_resume(): resume htc 245 * 246 * The htc message queue needs to be kicked off after 247 * a runtime resume. Otherwise messages would get stuck. 248 * 249 * @htc_ctx: HTC context pointer 250 * 251 * Return: 0 for success; 252 */ 253 int htc_runtime_resume(HTC_HANDLE htc_ctx) 254 { 255 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_ctx); 256 257 if (!target) 258 return 0; 259 260 qdf_sched_work(0, &target->queue_kicker); 261 return 0; 262 } 263 264 /** 265 * htc_runtime_pm_deinit(): runtime pm related de-intialization 266 * 267 * need to de-initialize the work item. 268 * 269 * @target: HTC target pointer 270 * 271 */ 272 static void htc_runtime_pm_deinit(HTC_TARGET *target) 273 { 274 if (!target) 275 return; 276 277 qdf_destroy_work(0, &target->queue_kicker); 278 } 279 280 int32_t htc_dec_return_runtime_cnt(HTC_HANDLE htc) 281 { 282 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc); 283 284 return qdf_atomic_dec_return(&target->htc_runtime_cnt); 285 } 286 287 /** 288 * htc_init_runtime_cnt: Initialize htc runtime count 289 * @htc: HTC handle 290 * 291 * Return: None 292 */ 293 static inline 294 void htc_init_runtime_cnt(HTC_TARGET *target) 295 { 296 qdf_atomic_init(&target->htc_runtime_cnt); 297 } 298 #else 299 static inline void htc_runtime_pm_init(HTC_TARGET *target) { } 300 static inline void htc_runtime_pm_deinit(HTC_TARGET *target) { } 301 302 static inline 303 void htc_init_runtime_cnt(HTC_TARGET *target) 304 { 305 } 306 #endif 307 308 #if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) 309 static 310 void htc_update_rx_bundle_stats(void *ctx, uint8_t no_of_pkt_in_bundle) 311 { 312 HTC_TARGET *target = (HTC_TARGET *)ctx; 313 314 no_of_pkt_in_bundle--; 315 if (target && (no_of_pkt_in_bundle < HTC_MAX_MSG_PER_BUNDLE_RX)) 316 target->rx_bundle_stats[no_of_pkt_in_bundle]++; 317 } 318 #else 319 static 320 void htc_update_rx_bundle_stats(void *ctx, uint8_t no_of_pkt_in_bundle) 321 { 322 } 323 #endif 324 325 /* registered target arrival callback from the HIF layer */ 326 HTC_HANDLE htc_create(void *ol_sc, struct htc_init_info *pInfo, 327 qdf_device_t osdev, uint32_t con_mode) 328 { 329 struct hif_msg_callbacks htcCallbacks; 330 HTC_ENDPOINT *pEndpoint = NULL; 331 HTC_TARGET *target = NULL; 332 int i; 333 334 if (!ol_sc) { 335 HTC_ERROR("%s: ol_sc = NULL", __func__); 336 return NULL; 337 } 338 HTC_TRACE("+htc_create .. HIF :%pK", ol_sc); 339 340 A_REGISTER_MODULE_DEBUG_INFO(htc); 341 342 target = (HTC_TARGET *) qdf_mem_malloc(sizeof(HTC_TARGET)); 343 if (!target) 344 return NULL; 345 346 htc_runtime_pm_init(target); 347 htc_credit_history_init(); 348 qdf_spinlock_create(&target->HTCLock); 349 qdf_spinlock_create(&target->HTCRxLock); 350 qdf_spinlock_create(&target->HTCTxLock); 351 for (i = 0; i < ENDPOINT_MAX; i++) { 352 pEndpoint = &target->endpoint[i]; 353 qdf_spinlock_create(&pEndpoint->lookup_queue_lock); 354 } 355 target->is_nodrop_pkt = false; 356 target->htc_hdr_length_check = false; 357 target->wmi_ep_count = 1; 358 359 do { 360 qdf_mem_copy(&target->HTCInitInfo, pInfo, 361 sizeof(struct htc_init_info)); 362 target->host_handle = pInfo->pContext; 363 target->osdev = osdev; 364 target->con_mode = con_mode; 365 366 /* If htc_ready_timeout_ms is not configured from CFG, 367 * assign the default timeout value here. 368 */ 369 370 if (!target->HTCInitInfo.htc_ready_timeout_ms) 371 target->HTCInitInfo.htc_ready_timeout_ms = 372 HTC_CONTROL_RX_TIMEOUT; 373 374 reset_endpoint_states(target); 375 376 INIT_HTC_PACKET_QUEUE(&target->ControlBufferTXFreeList); 377 378 for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { 379 HTC_PACKET *pPacket = (HTC_PACKET *) 380 qdf_mem_malloc(sizeof(HTC_PACKET)); 381 if (pPacket) 382 free_htc_packet_container(target, pPacket); 383 } 384 385 #ifdef TODO_FIXME 386 for (i = 0; i < NUM_CONTROL_TX_BUFFERS; i++) { 387 pPacket = build_htc_tx_ctrl_packet(); 388 if (!pPacket) 389 break; 390 htc_free_control_tx_packet(target, pPacket); 391 } 392 #endif 393 394 /* setup HIF layer callbacks */ 395 qdf_mem_zero(&htcCallbacks, sizeof(struct hif_msg_callbacks)); 396 htcCallbacks.Context = target; 397 htcCallbacks.rxCompletionHandler = htc_rx_completion_handler; 398 htcCallbacks.txCompletionHandler = htc_tx_completion_handler; 399 htcCallbacks.txResourceAvailHandler = 400 htc_tx_resource_avail_handler; 401 htcCallbacks.fwEventHandler = htc_fw_event_handler; 402 htcCallbacks.update_bundle_stats = htc_update_rx_bundle_stats; 403 target->hif_dev = ol_sc; 404 405 /* Get HIF default pipe for HTC message exchange */ 406 pEndpoint = &target->endpoint[ENDPOINT_0]; 407 408 hif_post_init(target->hif_dev, target, &htcCallbacks); 409 hif_get_default_pipe(target->hif_dev, &pEndpoint->UL_PipeID, 410 &pEndpoint->DL_PipeID); 411 hif_set_initial_wakeup_cb(target->hif_dev, 412 pInfo->target_initial_wakeup_cb, 413 pInfo->target_psoc); 414 415 } while (false); 416 417 htc_recv_init(target); 418 htc_init_runtime_cnt(target); 419 420 HTC_TRACE("-htc_create: (0x%pK)", target); 421 422 htc_hang_event_notifier_register(target); 423 424 return (HTC_HANDLE) target; 425 } 426 427 void htc_destroy(HTC_HANDLE HTCHandle) 428 { 429 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 430 431 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 432 ("+htc_destroy .. Destroying :0x%pK\n", target)); 433 htc_hang_event_notifier_unregister(); 434 hif_stop(htc_get_hif_device(HTCHandle)); 435 if (target) 436 htc_cleanup(target); 437 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_destroy\n")); 438 htc_credit_history_deinit(); 439 } 440 441 /* get the low level HIF device for the caller , the caller may wish to do low 442 * level HIF requests 443 */ 444 void *htc_get_hif_device(HTC_HANDLE HTCHandle) 445 { 446 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 447 448 return target->hif_dev; 449 } 450 451 static void htc_control_tx_complete(void *Context, HTC_PACKET *pPacket) 452 { 453 HTC_TARGET *target = (HTC_TARGET *) Context; 454 455 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 456 ("+-htc_control_tx_complete 0x%pK (l:%d)\n", pPacket, 457 pPacket->ActualLength)); 458 htc_free_control_tx_packet(target, pPacket); 459 } 460 461 /* TODO, this is just a temporary max packet size */ 462 #define MAX_MESSAGE_SIZE 1536 463 464 /** 465 * htc_setup_epping_credit_allocation() - allocate credits/HTC buffers to WMI 466 * @scn: pointer to hif_opaque_softc 467 * @pEntry: pointer to tx credit allocation entry 468 * @credits: number of credits 469 * 470 * Return: None 471 */ 472 static void 473 htc_setup_epping_credit_allocation(struct hif_opaque_softc *scn, 474 struct htc_service_tx_credit_allocation *pEntry, 475 int credits) 476 { 477 switch (hif_get_bus_type(scn)) { 478 case QDF_BUS_TYPE_PCI: 479 case QDF_BUS_TYPE_USB: 480 pEntry++; 481 pEntry->service_id = WMI_DATA_BE_SVC; 482 pEntry->CreditAllocation = (credits >> 1); 483 484 pEntry++; 485 pEntry->service_id = WMI_DATA_BK_SVC; 486 pEntry->CreditAllocation = (credits >> 1); 487 break; 488 case QDF_BUS_TYPE_SDIO: 489 pEntry++; 490 pEntry->service_id = WMI_DATA_BE_SVC; 491 pEntry->CreditAllocation = credits; 492 break; 493 default: 494 break; 495 } 496 } 497 498 /** 499 * htc_setup_target_buffer_assignments() - setup target buffer assignments 500 * @target: HTC Target Pointer 501 * 502 * Return: A_STATUS 503 */ 504 static 505 A_STATUS htc_setup_target_buffer_assignments(HTC_TARGET *target) 506 { 507 struct htc_service_tx_credit_allocation *pEntry; 508 A_STATUS status; 509 int credits; 510 int creditsPerMaxMsg; 511 512 creditsPerMaxMsg = MAX_MESSAGE_SIZE / target->TargetCreditSize; 513 if (MAX_MESSAGE_SIZE % target->TargetCreditSize) 514 creditsPerMaxMsg++; 515 516 /* TODO, this should be configured by the caller! */ 517 518 credits = target->TotalTransmitCredits; 519 pEntry = &target->ServiceTxAllocTable[0]; 520 521 status = A_OK; 522 /* 523 * Allocate all credists/HTC buffers to WMI. 524 * no buffers are used/required for data. data always 525 * remains on host. 526 */ 527 if (HTC_IS_EPPING_ENABLED(target->con_mode)) { 528 pEntry++; 529 pEntry->service_id = WMI_CONTROL_SVC; 530 pEntry->CreditAllocation = credits; 531 /* endpoint ping is a testing tool directly on top of HTC in 532 * both target and host sides. 533 * In target side, the endppint ping fw has no wlan stack and 534 * FW mboxping app directly sits on HTC and it simply drops 535 * or loops back TX packets. For rx perf, FW mboxping app 536 * generates packets and passes packets to HTC to send to host. 537 * There is no WMI message exchanges between host and target 538 * in endpoint ping case. 539 * In host side, the endpoint ping driver is a Ethernet driver 540 * and it directly sits on HTC. Only HIF, HTC, QDF, ADF are 541 * used by the endpoint ping driver. There is no wifi stack 542 * at all in host side also. For tx perf use case, 543 * the user space mboxping app sends the raw packets to endpoint 544 * ping driver and it directly forwards to HTC for transmission 545 * to stress the bus. For the rx perf, HTC passes the received 546 * packets to endpoint ping driver and it is passed to the user 547 * space through the Ethernet interface. 548 * For credit allocation, in SDIO bus case, only BE service is 549 * used for tx/rx perf testing so that all credits are given 550 * to BE service. In PCIe and USB bus case, endpoint ping uses 551 * both BE and BK services to stress the bus so that the total 552 * credits are equally distributed to BE and BK services. 553 */ 554 555 htc_setup_epping_credit_allocation(target->hif_dev, 556 pEntry, credits); 557 } else { 558 int i; 559 uint32_t max_wmi_svc = (sizeof(svc_id) / sizeof(uint32_t)); 560 561 if ((target->wmi_ep_count == 0) || 562 (target->wmi_ep_count > max_wmi_svc)) 563 return A_ERROR; 564 565 /* 566 * Divide credit among number of endpoints for WMI 567 */ 568 credits = credits / target->wmi_ep_count; 569 for (i = 0; i < target->wmi_ep_count; i++) { 570 status = A_OK; 571 pEntry++; 572 pEntry->service_id = svc_id[i]; 573 pEntry->CreditAllocation = credits; 574 } 575 } 576 577 if (A_SUCCESS(status)) { 578 int i; 579 580 for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { 581 if (target->ServiceTxAllocTable[i].service_id != 0) { 582 AR_DEBUG_PRINTF(ATH_DEBUG_INIT, 583 ("SVS Index : %d TX : 0x%2.2X : alloc:%d", 584 i, 585 target->ServiceTxAllocTable[i]. 586 service_id, 587 target->ServiceTxAllocTable[i]. 588 CreditAllocation)); 589 } 590 } 591 } 592 593 return status; 594 } 595 596 uint8_t htc_get_credit_allocation(HTC_TARGET *target, uint16_t service_id) 597 { 598 uint8_t allocation = 0; 599 int i; 600 601 for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { 602 if (target->ServiceTxAllocTable[i].service_id == service_id) { 603 allocation = 604 target->ServiceTxAllocTable[i].CreditAllocation; 605 } 606 } 607 608 if (0 == allocation) { 609 AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1, 610 ("HTC Service TX : 0x%2.2X : allocation is zero!\n", 611 service_id)); 612 } 613 614 return allocation; 615 } 616 617 QDF_STATUS htc_wait_target(HTC_HANDLE HTCHandle) 618 { 619 QDF_STATUS status = QDF_STATUS_SUCCESS; 620 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 621 HTC_READY_EX_MSG *pReadyMsg; 622 struct htc_service_connect_req connect; 623 struct htc_service_connect_resp resp; 624 HTC_READY_MSG *rdy_msg; 625 uint16_t htc_rdy_msg_id; 626 uint8_t i = 0; 627 HTC_PACKET *rx_bundle_packet, *temp_bundle_packet; 628 629 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 630 ("htc_wait_target - Enter (target:0x%pK)\n", HTCHandle)); 631 AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1, ("+HWT\n")); 632 633 do { 634 635 status = hif_start(target->hif_dev); 636 if (QDF_IS_STATUS_ERROR(status)) { 637 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, 638 ("hif_start failed\n")); 639 break; 640 } 641 642 status = htc_wait_recv_ctrl_message(target); 643 644 if (QDF_IS_STATUS_ERROR(status)) 645 break; 646 647 if (target->CtrlResponseLength < (sizeof(HTC_READY_EX_MSG))) { 648 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, 649 ("Invalid HTC Ready Msg Len:%d!\n", 650 target->CtrlResponseLength)); 651 status = QDF_STATUS_E_BADMSG; 652 break; 653 } 654 655 pReadyMsg = (HTC_READY_EX_MSG *) target->CtrlResponseBuffer; 656 657 rdy_msg = &pReadyMsg->Version2_0_Info; 658 htc_rdy_msg_id = 659 HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, MESSAGEID); 660 if (htc_rdy_msg_id != HTC_MSG_READY_ID) { 661 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, 662 ("Invalid HTC Ready Msg : 0x%X!\n", 663 htc_rdy_msg_id)); 664 status = QDF_STATUS_E_BADMSG; 665 break; 666 } 667 668 target->TotalTransmitCredits = HTC_GET_FIELD(rdy_msg, 669 HTC_READY_MSG, CREDITCOUNT); 670 if (target->HTCInitInfo.cfg_wmi_credit_cnt && 671 (target->HTCInitInfo.cfg_wmi_credit_cnt < 672 target->TotalTransmitCredits)) 673 /* 674 * If INI configured value is less than FW advertised, 675 * then use INI configured value, otherwise use FW 676 * advertised. 677 */ 678 target->TotalTransmitCredits = 679 target->HTCInitInfo.cfg_wmi_credit_cnt; 680 681 target->TargetCreditSize = 682 (int)HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, CREDITSIZE); 683 target->MaxMsgsPerHTCBundle = 684 (uint8_t) pReadyMsg->MaxMsgsPerHTCBundle; 685 UPDATE_ALT_CREDIT(target, pReadyMsg->AltDataCreditSize); 686 /* for old fw this value is set to 0. But the minimum value 687 * should be 1, i.e., no bundling 688 */ 689 if (target->MaxMsgsPerHTCBundle < 1) 690 target->MaxMsgsPerHTCBundle = 1; 691 692 AR_DEBUG_PRINTF(ATH_DEBUG_INIT, 693 ("Target Ready! TX resource : %d size:%d, MaxMsgsPerHTCBundle = %d", 694 target->TotalTransmitCredits, 695 target->TargetCreditSize, 696 target->MaxMsgsPerHTCBundle)); 697 698 if ((0 == target->TotalTransmitCredits) 699 || (0 == target->TargetCreditSize)) { 700 status = QDF_STATUS_E_ABORTED; 701 break; 702 } 703 704 /* Allocate expected number of RX bundle buffer allocation */ 705 if (HTC_RX_BUNDLE_ENABLED(target)) { 706 temp_bundle_packet = NULL; 707 for (i = 0; i < MAX_HTC_RX_BUNDLE; i++) { 708 rx_bundle_packet = 709 allocate_htc_bundle_packet(target); 710 if (rx_bundle_packet) 711 rx_bundle_packet->ListLink.pNext = 712 (DL_LIST *)temp_bundle_packet; 713 else 714 break; 715 716 temp_bundle_packet = rx_bundle_packet; 717 } 718 LOCK_HTC_TX(target); 719 target->pBundleFreeList = temp_bundle_packet; 720 UNLOCK_HTC_TX(target); 721 } 722 723 /* done processing */ 724 target->CtrlResponseProcessing = false; 725 726 htc_setup_target_buffer_assignments(target); 727 728 /* setup our pseudo HTC control endpoint connection */ 729 qdf_mem_zero(&connect, sizeof(connect)); 730 qdf_mem_zero(&resp, sizeof(resp)); 731 connect.EpCallbacks.pContext = target; 732 connect.EpCallbacks.EpTxComplete = htc_control_tx_complete; 733 connect.EpCallbacks.EpRecv = htc_control_rx_complete; 734 connect.MaxSendQueueDepth = NUM_CONTROL_TX_BUFFERS; 735 connect.service_id = HTC_CTRL_RSVD_SVC; 736 737 /* connect fake service */ 738 status = htc_connect_service((HTC_HANDLE) target, 739 &connect, &resp); 740 741 } while (false); 742 743 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_wait_target - Exit (%d)\n", 744 status)); 745 AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1, ("-HWT\n")); 746 return status; 747 } 748 749 /* start HTC, this is called after all services are connected */ 750 static A_STATUS htc_config_target_hif_pipe(HTC_TARGET *target) 751 { 752 753 return A_OK; 754 } 755 756 static void reset_endpoint_states(HTC_TARGET *target) 757 { 758 HTC_ENDPOINT *pEndpoint; 759 int i; 760 761 for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { 762 pEndpoint = &target->endpoint[i]; 763 pEndpoint->service_id = 0; 764 pEndpoint->MaxMsgLength = 0; 765 pEndpoint->MaxTxQueueDepth = 0; 766 pEndpoint->Id = i; 767 INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue); 768 INIT_HTC_PACKET_QUEUE(&pEndpoint->TxLookupQueue); 769 INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBufferHoldQueue); 770 pEndpoint->target = target; 771 pEndpoint->TxCreditFlowEnabled = (bool)htc_credit_flow; 772 pEndpoint->num_requeues_warn = 0; 773 pEndpoint->total_num_requeues = 0; 774 qdf_atomic_init(&pEndpoint->TxProcessCount); 775 } 776 } 777 778 /** 779 * htc_start() - Main HTC function to trigger HTC start 780 * @HTCHandle: pointer to HTC handle 781 * 782 * Return: QDF_STATUS_SUCCESS for success or an appropriate QDF_STATUS error 783 */ 784 QDF_STATUS htc_start(HTC_HANDLE HTCHandle) 785 { 786 qdf_nbuf_t netbuf; 787 QDF_STATUS status = QDF_STATUS_SUCCESS; 788 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 789 HTC_SETUP_COMPLETE_EX_MSG *pSetupComp; 790 HTC_PACKET *pSendPacket; 791 792 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Enter\n")); 793 794 do { 795 796 htc_config_target_hif_pipe(target); 797 798 /* allocate a buffer to send */ 799 pSendPacket = htc_alloc_control_tx_packet(target); 800 if (!pSendPacket) { 801 AR_DEBUG_ASSERT(false); 802 qdf_print("%s: allocControlTxPacket failed", 803 __func__); 804 status = QDF_STATUS_E_NOMEM; 805 break; 806 } 807 808 netbuf = 809 (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket); 810 /* assemble setup complete message */ 811 qdf_nbuf_put_tail(netbuf, sizeof(HTC_SETUP_COMPLETE_EX_MSG)); 812 pSetupComp = 813 (HTC_SETUP_COMPLETE_EX_MSG *) qdf_nbuf_data(netbuf); 814 qdf_mem_zero(pSetupComp, sizeof(HTC_SETUP_COMPLETE_EX_MSG)); 815 816 HTC_SET_FIELD(pSetupComp, HTC_SETUP_COMPLETE_EX_MSG, 817 MESSAGEID, HTC_MSG_SETUP_COMPLETE_EX_ID); 818 819 if (!htc_credit_flow) { 820 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 821 ("HTC will not use TX credit flow control")); 822 pSetupComp->SetupFlags |= 823 HTC_SETUP_COMPLETE_FLAGS_DISABLE_TX_CREDIT_FLOW; 824 } else { 825 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, 826 ("HTC using TX credit flow control")); 827 } 828 829 if ((hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_SDIO) || 830 (hif_get_bus_type(target->hif_dev) == 831 QDF_BUS_TYPE_USB)) { 832 if (HTC_RX_BUNDLE_ENABLED(target)) 833 pSetupComp->SetupFlags |= 834 HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV; 835 hif_set_bundle_mode(target->hif_dev, true, 836 HTC_MAX_MSG_PER_BUNDLE_RX); 837 pSetupComp->MaxMsgsPerBundledRecv = HTC_MAX_MSG_PER_BUNDLE_RX; 838 } 839 840 SET_HTC_PACKET_INFO_TX(pSendPacket, 841 NULL, 842 (uint8_t *) pSetupComp, 843 sizeof(HTC_SETUP_COMPLETE_EX_MSG), 844 ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); 845 846 status = htc_send_pkt((HTC_HANDLE) target, pSendPacket); 847 if (QDF_IS_STATUS_ERROR(status)) 848 break; 849 } while (false); 850 851 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Exit\n")); 852 return status; 853 } 854 855 /*flush all queued buffers for surpriseremove case*/ 856 void htc_flush_surprise_remove(HTC_HANDLE HTCHandle) 857 { 858 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 859 int i; 860 HTC_ENDPOINT *pEndpoint; 861 #ifdef RX_SG_SUPPORT 862 qdf_nbuf_t netbuf; 863 qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; 864 #endif 865 866 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_flush_surprise_remove\n")); 867 868 /* cleanup endpoints */ 869 for (i = 0; i < ENDPOINT_MAX; i++) { 870 pEndpoint = &target->endpoint[i]; 871 htc_flush_rx_hold_queue(target, pEndpoint); 872 htc_flush_endpoint_tx(target, pEndpoint, HTC_TX_PACKET_TAG_ALL); 873 } 874 875 hif_flush_surprise_remove(target->hif_dev); 876 877 #ifdef RX_SG_SUPPORT 878 LOCK_HTC_RX(target); 879 while ((netbuf = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL) 880 qdf_nbuf_free(netbuf); 881 RESET_RX_SG_CONFIG(target); 882 UNLOCK_HTC_RX(target); 883 #endif 884 885 reset_endpoint_states(target); 886 887 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_flush_surprise_remove\n")); 888 } 889 890 /* stop HTC communications, i.e. stop interrupt reception, and flush all queued 891 * buffers 892 */ 893 void htc_stop(HTC_HANDLE HTCHandle) 894 { 895 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 896 int i; 897 HTC_ENDPOINT *endpoint; 898 #ifdef RX_SG_SUPPORT 899 qdf_nbuf_t netbuf; 900 qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; 901 #endif 902 903 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_stop\n")); 904 905 htc_runtime_pm_deinit(target); 906 907 HTC_INFO("%s: endpoints cleanup\n", __func__); 908 /* cleanup endpoints */ 909 for (i = 0; i < ENDPOINT_MAX; i++) { 910 endpoint = &target->endpoint[i]; 911 htc_flush_rx_hold_queue(target, endpoint); 912 htc_flush_endpoint_tx(target, endpoint, HTC_TX_PACKET_TAG_ALL); 913 if (endpoint->ul_is_polled) { 914 qdf_timer_stop(&endpoint->ul_poll_timer); 915 qdf_timer_free(&endpoint->ul_poll_timer); 916 } 917 } 918 919 /* Note: htc_flush_endpoint_tx for all endpoints should be called before 920 * hif_stop - otherwise htc_tx_completion_handler called from 921 * hif_send_buffer_cleanup_on_pipe for residual tx frames in HIF layer, 922 * might queue the packet again to HIF Layer - which could cause tx 923 * buffer leak 924 */ 925 926 HTC_INFO("%s: stopping hif layer\n", __func__); 927 hif_stop(target->hif_dev); 928 929 #ifdef RX_SG_SUPPORT 930 LOCK_HTC_RX(target); 931 while ((netbuf = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL) 932 qdf_nbuf_free(netbuf); 933 RESET_RX_SG_CONFIG(target); 934 UNLOCK_HTC_RX(target); 935 #endif 936 937 /** 938 * In SSR case, HTC tx completion callback for wmi will be blocked 939 * by TARGET_STATUS_RESET and HTC packets will be left unfreed on 940 * lookup queue. 941 * 942 * In case of target failing to send wmi_ready_event, the htc connect 943 * msg buffer will be left unmapped and not freed. So calling the 944 * completion handler for this buffer will handle this scenario. 945 */ 946 HTC_INFO("%s: flush endpoints Tx lookup queue\n", __func__); 947 for (i = 0; i < ENDPOINT_MAX; i++) { 948 endpoint = &target->endpoint[i]; 949 if (endpoint->service_id == WMI_CONTROL_SVC) 950 htc_flush_endpoint_txlookupQ(target, i, false); 951 else if (endpoint->service_id == HTC_CTRL_RSVD_SVC) 952 htc_flush_endpoint_txlookupQ(target, i, true); 953 } 954 HTC_INFO("%s: resetting endpoints state\n", __func__); 955 956 reset_endpoint_states(target); 957 958 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_stop\n")); 959 } 960 961 void htc_dump_credit_states(HTC_HANDLE HTCHandle) 962 { 963 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 964 HTC_ENDPOINT *pEndpoint; 965 int i; 966 967 for (i = 0; i < ENDPOINT_MAX; i++) { 968 pEndpoint = &target->endpoint[i]; 969 if (0 == pEndpoint->service_id) 970 continue; 971 972 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 973 ("--- EP : %d service_id: 0x%X --------------\n", 974 pEndpoint->Id, pEndpoint->service_id)); 975 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 976 (" TxCredits : %d\n", 977 pEndpoint->TxCredits)); 978 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 979 (" TxCreditSize : %d\n", 980 pEndpoint->TxCreditSize)); 981 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 982 (" TxCreditsPerMaxMsg : %d\n", 983 pEndpoint->TxCreditsPerMaxMsg)); 984 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 985 (" TxQueueDepth : %d\n", 986 HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); 987 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 988 ("----------------------------------------\n")); 989 } 990 } 991 992 bool htc_get_endpoint_statistics(HTC_HANDLE HTCHandle, 993 HTC_ENDPOINT_ID Endpoint, 994 enum htc_endpoint_stat_action Action, 995 struct htc_endpoint_stats *pStats) 996 { 997 #ifdef HTC_EP_STAT_PROFILING 998 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 999 bool clearStats = false; 1000 bool sample = false; 1001 1002 switch (Action) { 1003 case HTC_EP_STAT_SAMPLE: 1004 sample = true; 1005 break; 1006 case HTC_EP_STAT_SAMPLE_AND_CLEAR: 1007 sample = true; 1008 clearStats = true; 1009 break; 1010 case HTC_EP_STAT_CLEAR: 1011 clearStats = true; 1012 break; 1013 default: 1014 break; 1015 } 1016 1017 A_ASSERT(Endpoint < ENDPOINT_MAX); 1018 1019 /* lock out TX and RX while we sample and/or clear */ 1020 LOCK_HTC_TX(target); 1021 LOCK_HTC_RX(target); 1022 1023 if (sample) { 1024 A_ASSERT(pStats); 1025 /* return the stats to the caller */ 1026 qdf_mem_copy(pStats, &target->endpoint[Endpoint].endpoint_stats, 1027 sizeof(struct htc_endpoint_stats)); 1028 } 1029 1030 if (clearStats) { 1031 /* reset stats */ 1032 qdf_mem_zero(&target->endpoint[Endpoint].endpoint_stats, 1033 sizeof(struct htc_endpoint_stats)); 1034 } 1035 1036 UNLOCK_HTC_RX(target); 1037 UNLOCK_HTC_TX(target); 1038 1039 return true; 1040 #else 1041 return false; 1042 #endif 1043 } 1044 1045 void *htc_get_targetdef(HTC_HANDLE htc_handle) 1046 { 1047 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1048 1049 return hif_get_targetdef(target->hif_dev); 1050 } 1051 1052 #ifdef IPA_OFFLOAD 1053 /** 1054 * htc_ipa_get_ce_resource() - get uc resource on lower layer 1055 * @htc_handle: htc context 1056 * @ce_sr_base_paddr: copyengine source ring base physical address 1057 * @ce_sr_ring_size: copyengine source ring size 1058 * @ce_reg_paddr: copyengine register physical address 1059 * 1060 * Return: None 1061 */ 1062 void htc_ipa_get_ce_resource(HTC_HANDLE htc_handle, 1063 qdf_shared_mem_t **ce_sr, 1064 uint32_t *ce_sr_ring_size, 1065 qdf_dma_addr_t *ce_reg_paddr) 1066 { 1067 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1068 1069 if (target->hif_dev) 1070 hif_ipa_get_ce_resource(target->hif_dev, 1071 ce_sr, ce_sr_ring_size, ce_reg_paddr); 1072 } 1073 #endif /* IPA_OFFLOAD */ 1074 1075 #if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) 1076 1077 void htc_dump_bundle_stats(HTC_HANDLE HTCHandle) 1078 { 1079 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 1080 int total, i; 1081 1082 total = 0; 1083 for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_RX; i++) 1084 total += target->rx_bundle_stats[i]; 1085 1086 if (total) { 1087 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("RX Bundle stats:\n")); 1088 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("Total RX packets: %d\n", 1089 total)); 1090 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ( 1091 "Number of bundle: Number of packets\n")); 1092 for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_RX; i++) 1093 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 1094 ("%10d:%10d(%2d%s)\n", (i+1), 1095 target->rx_bundle_stats[i], 1096 ((target->rx_bundle_stats[i]*100)/ 1097 total), "%")); 1098 } 1099 1100 1101 total = 0; 1102 for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_TX; i++) 1103 total += target->tx_bundle_stats[i]; 1104 1105 if (total) { 1106 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("TX Bundle stats:\n")); 1107 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("Total TX packets: %d\n", 1108 total)); 1109 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 1110 ("Number of bundle: Number of packets\n")); 1111 for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_TX; i++) 1112 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, 1113 ("%10d:%10d(%2d%s)\n", (i+1), 1114 target->tx_bundle_stats[i], 1115 ((target->tx_bundle_stats[i]*100)/ 1116 total), "%")); 1117 } 1118 } 1119 1120 void htc_clear_bundle_stats(HTC_HANDLE HTCHandle) 1121 { 1122 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 1123 1124 qdf_mem_zero(&target->rx_bundle_stats, sizeof(target->rx_bundle_stats)); 1125 qdf_mem_zero(&target->tx_bundle_stats, sizeof(target->tx_bundle_stats)); 1126 } 1127 #endif 1128 1129 /** 1130 * htc_vote_link_down - API to vote for link down 1131 * @htc_handle: HTC handle 1132 * 1133 * API for upper layers to call HIF to vote for link down 1134 * 1135 * Return: void 1136 */ 1137 void htc_vote_link_down(HTC_HANDLE htc_handle) 1138 { 1139 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1140 1141 if (!target->hif_dev) 1142 return; 1143 1144 hif_vote_link_down(target->hif_dev); 1145 } 1146 1147 /** 1148 * htc_vote_link_up - API to vote for link up 1149 * @htc_handle: HTC Handle 1150 * 1151 * API for upper layers to call HIF to vote for link up 1152 * 1153 * Return: void 1154 */ 1155 void htc_vote_link_up(HTC_HANDLE htc_handle) 1156 { 1157 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1158 1159 if (!target->hif_dev) 1160 return; 1161 1162 hif_vote_link_up(target->hif_dev); 1163 } 1164 1165 /** 1166 * htc_can_suspend_link - API to query HIF for link status 1167 * @htc_handle: HTC Handle 1168 * 1169 * API for upper layers to call HIF to query if the link can suspend 1170 * 1171 * Return: void 1172 */ 1173 bool htc_can_suspend_link(HTC_HANDLE htc_handle) 1174 { 1175 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1176 1177 if (!target->hif_dev) 1178 return false; 1179 1180 return hif_can_suspend_link(target->hif_dev); 1181 } 1182 1183 #ifdef FEATURE_RUNTIME_PM 1184 int htc_pm_runtime_get(HTC_HANDLE htc_handle) 1185 { 1186 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1187 1188 return hif_pm_runtime_get(target->hif_dev, 1189 RTPM_ID_HTC, false); 1190 } 1191 1192 int htc_pm_runtime_put(HTC_HANDLE htc_handle) 1193 { 1194 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1195 1196 hif_pm_runtime_update_stats(target->hif_dev, RTPM_ID_HTC, 1197 HIF_PM_HTC_STATS_PUT_HTT_RESPONSE); 1198 return hif_pm_runtime_put(target->hif_dev, RTPM_ID_HTC); 1199 } 1200 #endif 1201 1202 /** 1203 * htc_set_wmi_endpoint_count: Set number of WMI endpoint 1204 * @htc_handle: HTC handle 1205 * @wmi_ep_count: WMI enpoint count 1206 * 1207 * return: None 1208 */ 1209 void htc_set_wmi_endpoint_count(HTC_HANDLE htc_handle, uint8_t wmi_ep_count) 1210 { 1211 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1212 1213 target->wmi_ep_count = wmi_ep_count; 1214 } 1215 1216 /** 1217 * htc_get_wmi_endpoint_count: Get number of WMI endpoint 1218 * @htc_handle: HTC handle 1219 * 1220 * return: WMI enpoint count 1221 */ 1222 uint8_t htc_get_wmi_endpoint_count(HTC_HANDLE htc_handle) 1223 { 1224 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); 1225 1226 return target->wmi_ep_count; 1227 } 1228