diff options
| author | Prakash Dhavali <pdhavali@codeaurora.org> | 2016-03-02 00:54:45 -0800 |
|---|---|---|
| committer | Prakash Dhavali <pdhavali@codeaurora.org> | 2016-03-03 01:02:02 -0800 |
| commit | 142cee4bf22a1d052da15f3d8d050002121e4e77 (patch) | |
| tree | 367efbdeac65122d4759c59068890d55cdfa0754 /htc | |
| parent | abcec8c47e8ff57bf9ef22d56be4eec50406b6b9 (diff) | |
Initial host-common file folder cleanup and moves
Initial host-common file folder cleanup and moves
on top of baseline reference of MCL WLAN driver
SU#5.0.0.160.
Move dp, ht comm, hif, wmi and qdf folders one level up
Change-Id: I2120898024b1eafd5d651c48768dbf48bf05995d
Diffstat (limited to 'htc')
| -rw-r--r-- | htc/dl_list.h | 208 | ||||
| -rw-r--r-- | htc/htc.c | 881 | ||||
| -rw-r--r-- | htc/htc_api.h | 718 | ||||
| -rw-r--r-- | htc/htc_debug.h | 50 | ||||
| -rw-r--r-- | htc/htc_internal.h | 317 | ||||
| -rw-r--r-- | htc/htc_packet.h | 280 | ||||
| -rw-r--r-- | htc/htc_recv.c | 749 | ||||
| -rw-r--r-- | htc/htc_send.c | 2002 | ||||
| -rw-r--r-- | htc/htc_services.c | 369 |
9 files changed, 5574 insertions, 0 deletions
diff --git a/htc/dl_list.h b/htc/dl_list.h new file mode 100644 index 000000000000..1b5d72f780e2 --- /dev/null +++ b/htc/dl_list.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +/* ============================================================================== */ +/* Double-link list definitions (adapted from Atheros SDIO stack) */ +/* */ +/* Author(s): ="Atheros" */ +/* ============================================================================== */ + +#ifndef __DL_LIST_H___ +#define __DL_LIST_H___ + +#define A_CONTAINING_STRUCT(address, struct_type, field_name) \ + ((struct_type *)((char *)(address) - (char *)(&((struct_type *)0)->field_name))) + +/* list functions */ +/* pointers for the list */ +typedef struct _DL_LIST { + struct _DL_LIST *pPrev; + struct _DL_LIST *pNext; +} DL_LIST, *PDL_LIST; +/* + * DL_LIST_INIT , initialize doubly linked list + */ +#define DL_LIST_INIT(pList) \ + {(pList)->pPrev = pList; (pList)->pNext = pList; } + +/* faster macro to init list and add a single item */ +#define DL_LIST_INIT_AND_ADD(pList,pItem) \ + { (pList)->pPrev = (pItem); \ + (pList)->pNext = (pItem); \ + (pItem)->pNext = (pList); \ + (pItem)->pPrev = (pList); \ + } + +#define DL_LIST_IS_EMPTY(pList) (((pList)->pPrev == (pList)) && ((pList)->pNext == (pList))) +#define DL_LIST_GET_ITEM_AT_HEAD(pList) (pList)->pNext +#define DL_LIST_GET_ITEM_AT_TAIL(pList) (pList)->pPrev +/* + * ITERATE_OVER_LIST pStart is the list, pTemp is a temp list member + * NOT: do not use this function if the items in the list are deleted inside the + * iteration loop + */ +#define ITERATE_OVER_LIST(pStart, pTemp) \ + for((pTemp) =(pStart)->pNext; pTemp != (pStart); (pTemp) = (pTemp)->pNext) + +static __inline bool dl_list_is_entry_in_list(const DL_LIST *pList, + const DL_LIST *pEntry) +{ + const DL_LIST *pTmp; + + if (pList == pEntry) + return true; + + ITERATE_OVER_LIST(pList, pTmp) { + if (pTmp == pEntry) { + return true; + } + } + + return false; +} + +/* safe iterate macro that allows the item to be removed from the list + * the iteration continues to the next item in the list + */ +#define ITERATE_OVER_LIST_ALLOW_REMOVE(pStart,pItem,st,offset) \ + { \ + PDL_LIST pTemp; \ + pTemp = (pStart)->pNext; \ + while (pTemp != (pStart)) { \ + (pItem) = A_CONTAINING_STRUCT(pTemp,st,offset); \ + pTemp = pTemp->pNext; \ + +#define ITERATE_IS_VALID(pStart) dl_list_is_entry_in_list(pStart, pTemp) +#define ITERATE_RESET(pStart) pTemp=(pStart)->pNext + +#define ITERATE_END }} + +/* + * dl_list_insert_tail - insert pAdd to the end of the list + */ +static __inline PDL_LIST dl_list_insert_tail(PDL_LIST pList, PDL_LIST pAdd) +{ + /* insert at tail */ + pAdd->pPrev = pList->pPrev; + pAdd->pNext = pList; + if (pList->pPrev) { + pList->pPrev->pNext = pAdd; + } + pList->pPrev = pAdd; + return pAdd; +} + +/* + * dl_list_insert_head - insert pAdd into the head of the list + */ +static __inline PDL_LIST dl_list_insert_head(PDL_LIST pList, PDL_LIST pAdd) +{ + /* insert at head */ + pAdd->pPrev = pList; + pAdd->pNext = pList->pNext; + pList->pNext->pPrev = pAdd; + pList->pNext = pAdd; + return pAdd; +} + +#define DL_ListAdd(pList,pItem) dl_list_insert_head((pList),(pItem)) +/* + * dl_list_remove - remove pDel from list + */ +static __inline PDL_LIST dl_list_remove(PDL_LIST pDel) +{ + if (pDel->pNext != NULL) { + pDel->pNext->pPrev = pDel->pPrev; + } + if (pDel->pPrev != NULL) { + pDel->pPrev->pNext = pDel->pNext; + } + + /* point back to itself just to be safe, incase remove is called again */ + pDel->pNext = pDel; + pDel->pPrev = pDel; + return pDel; +} + +/* + * dl_list_remove_item_from_head - get a list item from the head + */ +static __inline PDL_LIST dl_list_remove_item_from_head(PDL_LIST pList) +{ + PDL_LIST pItem = NULL; + if (pList->pNext != pList) { + pItem = pList->pNext; + /* remove the first item from head */ + dl_list_remove(pItem); + } + return pItem; +} + +static __inline PDL_LIST dl_list_remove_item_from_tail(PDL_LIST pList) +{ + PDL_LIST pItem = NULL; + if (pList->pPrev != pList) { + pItem = pList->pPrev; + /* remove the item from tail */ + dl_list_remove(pItem); + } + return pItem; +} + +/* transfer src list items to the tail of the destination list */ +static __inline void dl_list_transfer_items_to_tail(PDL_LIST pDest, PDL_LIST pSrc) +{ + /* only concatenate if src is not empty */ + if (!DL_LIST_IS_EMPTY(pSrc)) { + /* cut out circular list in src and re-attach to end of dest */ + pSrc->pPrev->pNext = pDest; + pSrc->pNext->pPrev = pDest->pPrev; + pDest->pPrev->pNext = pSrc->pNext; + pDest->pPrev = pSrc->pPrev; + /* terminate src list, it is now empty */ + pSrc->pPrev = pSrc; + pSrc->pNext = pSrc; + } +} + +/* transfer src list items to the head of the destination list */ +static __inline void dl_list_transfer_items_to_head(PDL_LIST pDest, PDL_LIST pSrc) +{ + /* only concatenate if src is not empty */ + if (!DL_LIST_IS_EMPTY(pSrc)) { + /* cut out circular list in src and re-attach to start of dest */ + pSrc->pNext->pPrev = pDest; + pDest->pNext->pPrev = pSrc->pPrev; + pSrc->pPrev->pNext = pDest->pNext; + pDest->pNext = pSrc->pNext; + /* terminate src list, it is now empty */ + pSrc->pPrev = pSrc; + pSrc->pNext = pSrc; + } +} + +#endif /* __DL_LIST_H___ */ diff --git a/htc/htc.c b/htc/htc.c new file mode 100644 index 000000000000..d065193da3b7 --- /dev/null +++ b/htc/htc.c @@ -0,0 +1,881 @@ +/* + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#include "ol_if_athvar.h" +#include "htc_debug.h" +#include "htc_internal.h" +#include <cdf_nbuf.h> /* cdf_nbuf_t */ +#include <cdf_types.h> /* cdf_print */ +#include <hif.h> +#include "epping_main.h" +#include "hif_io32.h" +#include "cds_concurrency.h" +#include <cds_api.h> + +#ifdef DEBUG +static ATH_DEBUG_MASK_DESCRIPTION g_htc_debug_description[] = { + {ATH_DEBUG_SEND, "Send"}, + {ATH_DEBUG_RECV, "Recv"}, + {ATH_DEBUG_SYNC, "Sync"}, + {ATH_DEBUG_DUMP, "Dump Data (RX or TX)"}, + {ATH_DEBUG_SETUP, "Setup"}, +}; + +ATH_DEBUG_INSTANTIATE_MODULE_VAR(htc, + "htc", + "Host Target Communications", + ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_INFO | + ATH_DEBUG_SETUP, + ATH_DEBUG_DESCRIPTION_COUNT + (g_htc_debug_description), + g_htc_debug_description); + +#endif + +extern unsigned int htc_credit_flow; + +static void reset_endpoint_states(HTC_TARGET *target); + +static void destroy_htc_tx_ctrl_packet(HTC_PACKET *pPacket) +{ + cdf_nbuf_t netbuf; + netbuf = (cdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("free ctrl netbuf :0x%p \n", netbuf)); + if (netbuf != NULL) { + cdf_nbuf_free(netbuf); + } + + cdf_mem_free(pPacket); +} + +static HTC_PACKET *build_htc_tx_ctrl_packet(cdf_device_t osdev) +{ + HTC_PACKET *pPacket = NULL; + cdf_nbuf_t netbuf; + + do { + pPacket = (HTC_PACKET *) cdf_mem_malloc(sizeof(HTC_PACKET)); + if (NULL == pPacket) { + break; + } + A_MEMZERO(pPacket, sizeof(HTC_PACKET)); + netbuf = + cdf_nbuf_alloc(osdev, HTC_CONTROL_BUFFER_SIZE, 20, 4, true); + if (NULL == netbuf) { + cdf_mem_free(pPacket); + pPacket = NULL; + cdf_print("%s: nbuf alloc failed\n", __func__); + break; + } + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("alloc ctrl netbuf :0x%p \n", netbuf)); + SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf); + } while (false); + + return pPacket; +} + +void htc_free_control_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket) +{ + +#ifdef TODO_FIXME + LOCK_HTC(target); + HTC_PACKET_ENQUEUE(&target->ControlBufferTXFreeList, pPacket); + UNLOCK_HTC(target); + /* TODO_FIXME netbufs cannot be RESET! */ +#else + destroy_htc_tx_ctrl_packet(pPacket); +#endif + +} + +HTC_PACKET *htc_alloc_control_tx_packet(HTC_TARGET *target) +{ +#ifdef TODO_FIXME + HTC_PACKET *pPacket; + + LOCK_HTC(target); + pPacket = htc_packet_dequeue(&target->ControlBufferTXFreeList); + UNLOCK_HTC(target); + + return pPacket; +#else + return build_htc_tx_ctrl_packet(target->osdev); +#endif +} + +/* Set the target failure handling callback */ +void htc_set_target_failure_callback(HTC_HANDLE HTCHandle, + HTC_TARGET_FAILURE Callback) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + target->HTCInitInfo.TargetFailure = Callback; +} + +void htc_dump(HTC_HANDLE HTCHandle, uint8_t CmdId, bool start) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + hif_dump(target->hif_dev, CmdId, start); +} + +/* cleanup the HTC instance */ +static void htc_cleanup(HTC_TARGET *target) +{ + HTC_PACKET *pPacket; + /* cdf_nbuf_t netbuf; */ + + if (target->hif_dev != NULL) { + hif_detach_htc(target->hif_dev); + target->hif_dev = NULL; + } + + while (true) { + pPacket = allocate_htc_packet_container(target); + if (NULL == pPacket) { + break; + } + cdf_mem_free(pPacket); + } + + pPacket = target->pBundleFreeList; + while (pPacket) { + HTC_PACKET *pPacketTmp = (HTC_PACKET *) pPacket->ListLink.pNext; + cdf_mem_free(pPacket); + pPacket = pPacketTmp; + } +#ifdef TODO_FIXME + while (true) { + pPacket = htc_alloc_control_tx_packet(target); + if (NULL == pPacket) { + break; + } + netbuf = (cdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + if (netbuf != NULL) { + cdf_nbuf_free(netbuf); + } + + cdf_mem_free(pPacket); + } +#endif + + cdf_spinlock_destroy(&target->HTCLock); + cdf_spinlock_destroy(&target->HTCRxLock); + cdf_spinlock_destroy(&target->HTCTxLock); + cdf_spinlock_destroy(&target->HTCCreditLock); + + /* free our instance */ + cdf_mem_free(target); +} + +/* registered target arrival callback from the HIF layer */ +HTC_HANDLE htc_create(void *ol_sc, HTC_INIT_INFO *pInfo, cdf_device_t osdev) +{ + struct hif_msg_callbacks htcCallbacks; + HTC_ENDPOINT *pEndpoint = NULL; + HTC_TARGET *target = NULL; + int i; + + if (ol_sc == NULL) { + HTC_ERROR("%s: ol_sc = NULL", __func__); + return NULL; + } + HTC_TRACE("+htc_create .. HIF :%p", ol_sc); + + A_REGISTER_MODULE_DEBUG_INFO(htc); + + target = (HTC_TARGET *) cdf_mem_malloc(sizeof(HTC_TARGET)); + if (target == NULL) { + HTC_ERROR("%s: Unable to allocate memory", __func__); + return NULL; + } + + A_MEMZERO(target, sizeof(HTC_TARGET)); + + htc_runtime_pm_init(target); + cdf_spinlock_init(&target->HTCLock); + cdf_spinlock_init(&target->HTCRxLock); + cdf_spinlock_init(&target->HTCTxLock); + cdf_spinlock_init(&target->HTCCreditLock); + + do { + A_MEMCPY(&target->HTCInitInfo, pInfo, sizeof(HTC_INIT_INFO)); + target->host_handle = pInfo->pContext; + target->osdev = osdev; + + reset_endpoint_states(target); + + INIT_HTC_PACKET_QUEUE(&target->ControlBufferTXFreeList); + + for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { + HTC_PACKET *pPacket = + (HTC_PACKET *) cdf_mem_malloc(sizeof(HTC_PACKET)); + if (pPacket != NULL) { + A_MEMZERO(pPacket, sizeof(HTC_PACKET)); + free_htc_packet_container(target, pPacket); + } + } + +#ifdef TODO_FIXME + for (i = 0; i < NUM_CONTROL_TX_BUFFERS; i++) { + pPacket = build_htc_tx_ctrl_packet(); + if (NULL == pPacket) { + break; + } + htc_free_control_tx_packet(target, pPacket); + } +#endif + + /* setup HIF layer callbacks */ + cdf_mem_zero(&htcCallbacks, sizeof(struct hif_msg_callbacks)); + htcCallbacks.Context = target; + htcCallbacks.rxCompletionHandler = htc_rx_completion_handler; + htcCallbacks.txCompletionHandler = htc_tx_completion_handler; + htcCallbacks.txResourceAvailHandler = htc_tx_resource_avail_handler; + htcCallbacks.fwEventHandler = htc_fw_event_handler; + target->hif_dev = ol_sc; + + /* Get HIF default pipe for HTC message exchange */ + pEndpoint = &target->endpoint[ENDPOINT_0]; + + hif_post_init(target->hif_dev, target, &htcCallbacks); + hif_get_default_pipe(target->hif_dev, &pEndpoint->UL_PipeID, + &pEndpoint->DL_PipeID); + + } while (false); + + htc_recv_init(target); + + HTC_TRACE("-htc_create: (0x%p)", target); + + return (HTC_HANDLE) target; +} + +void htc_destroy(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("+htc_destroy .. Destroying :0x%p\n", target)); + hif_stop(htc_get_hif_device(HTCHandle)); + if (target) + htc_cleanup(target); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_destroy\n")); +} + +/* get the low level HIF device for the caller , the caller may wish to do low level + * HIF requests */ +void *htc_get_hif_device(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + return target->hif_dev; +} + +void htc_control_tx_complete(void *Context, HTC_PACKET *pPacket) +{ + HTC_TARGET *target = (HTC_TARGET *) Context; + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("+-htc_control_tx_complete 0x%p (l:%d) \n", pPacket, + pPacket->ActualLength)); + htc_free_control_tx_packet(target, pPacket); +} + +/* TODO, this is just a temporary max packet size */ +#define MAX_MESSAGE_SIZE 1536 + +/** + * htc_setup_target_buffer_assignments() - setup target buffer assignments + * @target: HTC Target Pointer + * + * Return: A_STATUS + */ +A_STATUS htc_setup_target_buffer_assignments(HTC_TARGET *target) +{ + HTC_SERVICE_TX_CREDIT_ALLOCATION *pEntry; + A_STATUS status; + int credits; + int creditsPerMaxMsg; + + creditsPerMaxMsg = MAX_MESSAGE_SIZE / target->TargetCreditSize; + if (MAX_MESSAGE_SIZE % target->TargetCreditSize) { + creditsPerMaxMsg++; + } + + /* TODO, this should be configured by the caller! */ + + credits = target->TotalTransmitCredits; + pEntry = &target->ServiceTxAllocTable[0]; + + /* + * Allocate all credists/HTC buffers to WMI. + * no buffers are used/required for data. data always + * remains on host. + */ + status = A_OK; + pEntry++; + pEntry->service_id = WMI_CONTROL_SVC; + pEntry->CreditAllocation = credits; + + if (WLAN_IS_EPPING_ENABLED(cds_get_conparam())) { + /* endpoint ping is a testing tool directly on top of HTC in + * both target and host sides. + * In target side, the endppint ping fw has no wlan stack and the + * FW mboxping app directly sits on HTC and it simply drops + * or loops back TX packets. For rx perf, FW mboxping app + * generates packets and passes packets to HTC to send to host. + * There is no WMI mesage exchanges between host and target + * in endpoint ping case. + * In host side, the endpoint ping driver is a Ethernet driver + * and it directly sits on HTC. Only HIF, HTC, CDF, ADF are + * used by the endpoint ping driver. There is no wifi stack + * at all in host side also. For tx perf use case, + * the user space mboxping app sends the raw packets to endpoint + * ping driver and it directly forwards to HTC for transmission + * to stress the bus. For the rx perf, HTC passes the received + * packets to endpoint ping driver and it is passed to the user + * space through the Ethernet interface. + * For credit allocation, in SDIO bus case, only BE service is + * used for tx/rx perf testing so that all credits are given + * to BE service. In PCIe and USB bus case, endpoint ping uses both + * BE and BK services to stress the bus so that the total credits + * are equally distributed to BE and BK services. + */ + pEntry->service_id = WMI_DATA_BE_SVC; + pEntry->CreditAllocation = (credits >> 1); + + pEntry++; + pEntry->service_id = WMI_DATA_BK_SVC; + pEntry->CreditAllocation = (credits >> 1); + } + + if (A_SUCCESS(status)) { + int i; + for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { + if (target->ServiceTxAllocTable[i].service_id != 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_INIT, + ("HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n", + i, + target->ServiceTxAllocTable[i]. + service_id, + target->ServiceTxAllocTable[i]. + CreditAllocation)); + } + } + } + + return status; +} + +A_UINT8 htc_get_credit_allocation(HTC_TARGET *target, A_UINT16 service_id) +{ + A_UINT8 allocation = 0; + int i; + + for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { + if (target->ServiceTxAllocTable[i].service_id == service_id) { + allocation = + target->ServiceTxAllocTable[i].CreditAllocation; + } + } + + if (0 == allocation) { + AR_DEBUG_PRINTF(ATH_DEBUG_INIT, + ("HTC Service TX : 0x%2.2X : allocation is zero!\n", + service_id)); + } + + return allocation; +} + +A_STATUS htc_wait_target(HTC_HANDLE HTCHandle) +{ + A_STATUS status = A_OK; + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_READY_EX_MSG *pReadyMsg; + HTC_SERVICE_CONNECT_REQ connect; + HTC_SERVICE_CONNECT_RESP resp; + HTC_READY_MSG *rdy_msg; + A_UINT16 htc_rdy_msg_id; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("htc_wait_target - Enter (target:0x%p) \n", HTCHandle)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("+HWT\n")); + + do { + + status = hif_start(target->hif_dev); + if (A_FAILED(status)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("hif_start failed\n")); + break; + } + + status = htc_wait_recv_ctrl_message(target); + + if (A_FAILED(status)) { + break; + } + + if (target->CtrlResponseLength < (sizeof(HTC_READY_EX_MSG))) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("Invalid HTC Ready Msg Len:%d! \n", + target->CtrlResponseLength)); + status = A_ECOMM; + break; + } + + pReadyMsg = (HTC_READY_EX_MSG *) target->CtrlResponseBuffer; + + rdy_msg = &pReadyMsg->Version2_0_Info; + htc_rdy_msg_id = + HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, MESSAGEID); + if (htc_rdy_msg_id != HTC_MSG_READY_ID) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("Invalid HTC Ready Msg : 0x%X ! \n", + htc_rdy_msg_id)); + status = A_ECOMM; + break; + } + + target->TotalTransmitCredits = + HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, CREDITCOUNT); + target->TargetCreditSize = + (int)HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, CREDITSIZE); + target->MaxMsgsPerHTCBundle = + (A_UINT8) pReadyMsg->MaxMsgsPerHTCBundle; + /* for old fw this value is set to 0. But the minimum value should be 1, + * i.e., no bundling */ + if (target->MaxMsgsPerHTCBundle < 1) + target->MaxMsgsPerHTCBundle = 1; + + AR_DEBUG_PRINTF(ATH_DEBUG_INIT, + ("Target Ready! : transmit resources : %d size:%d, MaxMsgsPerHTCBundle = %d\n", + target->TotalTransmitCredits, + target->TargetCreditSize, + target->MaxMsgsPerHTCBundle)); + + if ((0 == target->TotalTransmitCredits) + || (0 == target->TargetCreditSize)) { + status = A_ECOMM; + break; + } + /* done processing */ + target->CtrlResponseProcessing = false; + + htc_setup_target_buffer_assignments(target); + + /* setup our pseudo HTC control endpoint connection */ + A_MEMZERO(&connect, sizeof(connect)); + A_MEMZERO(&resp, sizeof(resp)); + connect.EpCallbacks.pContext = target; + connect.EpCallbacks.EpTxComplete = htc_control_tx_complete; + connect.EpCallbacks.EpRecv = htc_control_rx_complete; + connect.MaxSendQueueDepth = NUM_CONTROL_TX_BUFFERS; + connect.service_id = HTC_CTRL_RSVD_SVC; + + /* connect fake service */ + status = htc_connect_service((HTC_HANDLE) target, + &connect, &resp); + + } while (false); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_wait_target - Exit (%d)\n", status)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("-HWT\n")); + return status; +} + +/* start HTC, this is called after all services are connected */ +static A_STATUS htc_config_target_hif_pipe(HTC_TARGET *target) +{ + + return A_OK; +} + +static void reset_endpoint_states(HTC_TARGET *target) +{ + HTC_ENDPOINT *pEndpoint; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + pEndpoint->service_id = 0; + pEndpoint->MaxMsgLength = 0; + pEndpoint->MaxTxQueueDepth = 0; + pEndpoint->Id = i; + INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue); + INIT_HTC_PACKET_QUEUE(&pEndpoint->TxLookupQueue); + INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBufferHoldQueue); + pEndpoint->target = target; + /* pEndpoint->TxCreditFlowEnabled = (A_BOOL)htc_credit_flow; */ + pEndpoint->TxCreditFlowEnabled = (A_BOOL) 1; + cdf_atomic_init(&pEndpoint->TxProcessCount); + } +} + +A_STATUS htc_start(HTC_HANDLE HTCHandle) +{ + cdf_nbuf_t netbuf; + A_STATUS status = A_OK; + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_SETUP_COMPLETE_EX_MSG *pSetupComp; + HTC_PACKET *pSendPacket; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Enter\n")); + + do { + + htc_config_target_hif_pipe(target); + + /* allocate a buffer to send */ + pSendPacket = htc_alloc_control_tx_packet(target); + if (NULL == pSendPacket) { + AR_DEBUG_ASSERT(false); + cdf_print("%s: allocControlTxPacket failed\n", + __func__); + status = A_NO_MEMORY; + break; + } + + netbuf = + (cdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket); + /* assemble setup complete message */ + cdf_nbuf_put_tail(netbuf, sizeof(HTC_SETUP_COMPLETE_EX_MSG)); + pSetupComp = + (HTC_SETUP_COMPLETE_EX_MSG *) cdf_nbuf_data(netbuf); + A_MEMZERO(pSetupComp, sizeof(HTC_SETUP_COMPLETE_EX_MSG)); + + HTC_SET_FIELD(pSetupComp, HTC_SETUP_COMPLETE_EX_MSG, + MESSAGEID, HTC_MSG_SETUP_COMPLETE_EX_ID); + + if (!htc_credit_flow) { + AR_DEBUG_PRINTF(ATH_DEBUG_INIT, + ("HTC will not use TX credit flow control\n")); + pSetupComp->SetupFlags |= + HTC_SETUP_COMPLETE_FLAGS_DISABLE_TX_CREDIT_FLOW; + } else { + AR_DEBUG_PRINTF(ATH_DEBUG_INIT, + ("HTC using TX credit flow control\n")); + } + +#ifdef HIF_SDIO +#if ENABLE_BUNDLE_RX + if (HTC_ENABLE_BUNDLE(target)) + pSetupComp->SetupFlags |= + HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV; +#endif /* ENABLE_BUNDLE_RX */ +#endif /* HIF_SDIO */ + + SET_HTC_PACKET_INFO_TX(pSendPacket, + NULL, + (A_UINT8 *) pSetupComp, + sizeof(HTC_SETUP_COMPLETE_EX_MSG), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + status = htc_send_pkt((HTC_HANDLE) target, pSendPacket); + if (A_FAILED(status)) { + break; + } + + } while (false); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Exit\n")); + return status; +} + +/*flush all queued buffers for surpriseremove case*/ +void htc_flush_surprise_remove(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + int i; + HTC_ENDPOINT *pEndpoint; +#ifdef RX_SG_SUPPORT + cdf_nbuf_t netbuf; + cdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; +#endif + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_flush_surprise_remove \n")); + + /* cleanup endpoints */ + for (i = 0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + htc_flush_rx_hold_queue(target, pEndpoint); + htc_flush_endpoint_tx(target, pEndpoint, HTC_TX_PACKET_TAG_ALL); + } + + hif_flush_surprise_remove(target->hif_dev); + +#ifdef RX_SG_SUPPORT + LOCK_HTC_RX(target); + while ((netbuf = cdf_nbuf_queue_remove(rx_sg_queue)) != NULL) { + cdf_nbuf_free(netbuf); + } + RESET_RX_SG_CONFIG(target); + UNLOCK_HTC_RX(target); +#endif + + reset_endpoint_states(target); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_flush_surprise_remove \n")); +} + +/* stop HTC communications, i.e. stop interrupt reception, and flush all queued buffers */ +void htc_stop(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + int i; + HTC_ENDPOINT *pEndpoint; +#ifdef RX_SG_SUPPORT + cdf_nbuf_t netbuf; + cdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; +#endif + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_stop \n")); + + /* cleanup endpoints */ + for (i = 0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + htc_flush_rx_hold_queue(target, pEndpoint); + htc_flush_endpoint_tx(target, pEndpoint, HTC_TX_PACKET_TAG_ALL); + if (pEndpoint->ul_is_polled) { + cdf_softirq_timer_cancel(&pEndpoint->ul_poll_timer); + cdf_softirq_timer_free(&pEndpoint->ul_poll_timer); + } + } + + /* Note: htc_flush_endpoint_tx for all endpoints should be called before + * hif_stop - otherwise htc_tx_completion_handler called from + * hif_send_buffer_cleanup_on_pipe for residual tx frames in HIF layer, + * might queue the packet again to HIF Layer - which could cause tx + * buffer leak + */ + + hif_stop(target->hif_dev); + +#ifdef RX_SG_SUPPORT + LOCK_HTC_RX(target); + while ((netbuf = cdf_nbuf_queue_remove(rx_sg_queue)) != NULL) { + cdf_nbuf_free(netbuf); + } + RESET_RX_SG_CONFIG(target); + UNLOCK_HTC_RX(target); +#endif + + reset_endpoint_states(target); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_stop\n")); +} + +/** + * htc_runtime_pm_init(): runtime pm related intialization + * + * need to initialize a work item. + */ +void htc_runtime_pm_init(HTC_TARGET *target) +{ + cdf_create_work(&target->queue_kicker, htc_kick_queues, target); +} + +/** + * htc_runtime_suspend(): ensure htc is ready to suspend + * + * htc is ready to suspend if there are no pending pactets + * in the txrx queues. + * + * Return: 0 on success or -EBUSY if there are queued packets. + */ +int htc_runtime_suspend(void) +{ + ol_txrx_pdev_handle txrx_pdev = cds_get_context(CDF_MODULE_ID_TXRX); + + if (txrx_pdev == NULL) { + HTC_ERROR("%s: txrx context null", __func__); + return CDF_STATUS_E_FAULT; + } + + if (ol_txrx_get_tx_pending(txrx_pdev)) + return -EBUSY; + else + return 0; +} + +/** + * htc_runtime_resume(): resume htc + * + * The htc message queue needs to be kicked off after + * a runtime resume. Otherwise messages would get stuck. + * + * Return: 0 for success; + */ +int htc_runtime_resume(void) +{ + HTC_HANDLE htc_ctx = cds_get_context(CDF_MODULE_ID_HTC); + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_ctx); + + if (target == NULL) + return 0; + + cdf_schedule_work(&target->queue_kicker); + return 0; +} + +void htc_dump_credit_states(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + int i; + + for (i = 0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + if (0 == pEndpoint->service_id) + continue; + + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + ("--- EP : %d service_id: 0x%X --------------\n", + pEndpoint->Id, pEndpoint->service_id)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + (" TxCredits : %d\n", + pEndpoint->TxCredits)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + (" TxCreditSize : %d\n", + pEndpoint->TxCreditSize)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + (" TxCreditsPerMaxMsg : %d\n", + pEndpoint->TxCreditsPerMaxMsg)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + (" TxQueueDepth : %d\n", + HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, + ("----------------------------------------------------\n")); + } +} + +A_BOOL htc_get_endpoint_statistics(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint, + HTC_ENDPOINT_STAT_ACTION Action, + HTC_ENDPOINT_STATS *pStats) +{ +#ifdef HTC_EP_STAT_PROFILING + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + A_BOOL clearStats = false; + A_BOOL sample = false; + + switch (Action) { + case HTC_EP_STAT_SAMPLE: + sample = true; + break; + case HTC_EP_STAT_SAMPLE_AND_CLEAR: + sample = true; + clearStats = true; + break; + case HTC_EP_STAT_CLEAR: + clearStats = true; + break; + default: + break; + } + + A_ASSERT(Endpoint < ENDPOINT_MAX); + + /* lock out TX and RX while we sample and/or clear */ + LOCK_HTC_TX(target); + LOCK_HTC_RX(target); + + if (sample) { + A_ASSERT(pStats != NULL); + /* return the stats to the caller */ + A_MEMCPY(pStats, &target->endpoint[Endpoint].endpoint_stats, + sizeof(HTC_ENDPOINT_STATS)); + } + + if (clearStats) { + /* reset stats */ + A_MEMZERO(&target->endpoint[Endpoint].endpoint_stats, + sizeof(HTC_ENDPOINT_STATS)); + } + + UNLOCK_HTC_RX(target); + UNLOCK_HTC_TX(target); + + return true; +#else + return false; +#endif +} + +void *htc_get_targetdef(HTC_HANDLE htc_handle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); + + return hif_get_targetdef(target->hif_dev); +} + +/** + * htc_set_target_to_sleep() - set target to sleep + * @context: ol_softc context + * + * Return: none + */ +void htc_set_target_to_sleep(void *context) +{ + struct ol_softc *scn = (struct ol_softc *)context; + + hif_set_target_sleep(scn, true, false); +} + +/** + * htc_cancel_deferred_target_sleep() - cancel deferred target sleep + * @context: ol_softc context + * + * Return: none + */ +void htc_cancel_deferred_target_sleep(void *context) +{ + struct ol_softc *scn = (struct ol_softc *)context; + hif_cancel_deferred_target_sleep(scn); +} + +#ifdef IPA_OFFLOAD +/** + * htc_ipa_get_ce_resource() - get uc resource on lower layer + * @htc_handle: htc context + * @ce_sr_base_paddr: copyengine source ring base physical address + * @ce_sr_ring_size: copyengine source ring size + * @ce_reg_paddr: copyengine register physical address + * + * Return: None + */ +void htc_ipa_get_ce_resource(HTC_HANDLE htc_handle, + cdf_dma_addr_t *ce_sr_base_paddr, + uint32_t *ce_sr_ring_size, + cdf_dma_addr_t *ce_reg_paddr) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle); + + if (target->hif_dev != NULL) { + hif_ipa_get_ce_resource(target->hif_dev, + ce_sr_base_paddr, + ce_sr_ring_size, ce_reg_paddr); + } +} +#endif /* IPA_OFFLOAD */ diff --git a/htc/htc_api.h b/htc/htc_api.h new file mode 100644 index 000000000000..94b38296a5c3 --- /dev/null +++ b/htc/htc_api.h @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#ifndef _HTC_API_H_ +#define _HTC_API_H_ + +#include <athdefs.h> +#include "osapi_linux.h" +#include "htc_packet.h" +#include <htc.h> +#include <htc_services.h> +#include <cdf_types.h> /* cdf_device_t */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* TODO.. for BMI */ +#define ENDPOINT1 0 +/* TODO -remove me, but we have to fix BMI first */ +#define HTC_MAILBOX_NUM_MAX 4 + +/* this is the amount of header room required by users of HTC */ +#define HTC_HEADER_LEN HTC_HDR_LENGTH + +typedef void *HTC_HANDLE; + +typedef A_UINT16 HTC_SERVICE_ID; + +typedef void (*HTC_TARGET_FAILURE)(void *Instance, CDF_STATUS Status); + +typedef struct _HTC_INIT_INFO { + void *pContext; /* context for target notifications */ + void (*TargetFailure)(void *Instance, CDF_STATUS Status); + void (*TargetSendSuspendComplete)(void *ctx); +} HTC_INIT_INFO; + +/* Struct for HTC layer packet stats*/ +struct ol_ath_htc_stats { + int htc_get_pkt_q_fail_count; + int htc_pkt_q_empty_count; + int htc_send_q_empty_count; +}; + +/* To resume HTT Tx queue during runtime resume */ +typedef void (*HTC_EP_RESUME_TX_QUEUE)(void *); + +/* per service connection send completion */ +typedef void (*HTC_EP_SEND_PKT_COMPLETE)(void *, HTC_PACKET *); +/* per service connection callback when a plurality of packets have been sent + * The HTC_PACKET_QUEUE is a temporary queue object (e.g. freed on return from the callback) + * to hold a list of completed send packets. + * If the handler cannot fully traverse the packet queue before returning, it should + * transfer the items of the queue into the caller's private queue using: + * HTC_PACKET_ENQUEUE() */ +typedef void (*HTC_EP_SEND_PKT_COMP_MULTIPLE)(void *, + HTC_PACKET_QUEUE *); +/* per service connection pkt received */ +typedef void (*HTC_EP_RECV_PKT)(void *, HTC_PACKET *); +/* per service connection callback when a plurality of packets are received + * The HTC_PACKET_QUEUE is a temporary queue object (e.g. freed on return from the callback) + * to hold a list of recv packets. + * If the handler cannot fully traverse the packet queue before returning, it should + * transfer the items of the queue into the caller's private queue using: + * HTC_PACKET_ENQUEUE() */ +typedef void (*HTC_EP_RECV_PKT_MULTIPLE)(void *, HTC_PACKET_QUEUE *); + +/* Optional per service connection receive buffer re-fill callback, + * On some OSes (like Linux) packets are allocated from a global pool and indicated up + * to the network stack. The driver never gets the packets back from the OS. For these OSes + * a refill callback can be used to allocate and re-queue buffers into HTC. + * + * On other OSes, the network stack can call into the driver's OS-specifc "return_packet" handler and + * the driver can re-queue these buffers into HTC. In this regard a refill callback is + * unnecessary */ +typedef void (*HTC_EP_RECV_REFILL)(void *, HTC_ENDPOINT_ID Endpoint); + +/* Optional per service connection receive buffer allocation callback. + * On some systems packet buffers are an extremely limited resource. Rather than + * queue largest-possible-sized buffers to HTC, some systems would rather + * allocate a specific size as the packet is received. The trade off is + * slightly more processing (callback invoked for each RX packet) + * for the benefit of committing fewer buffer resources into HTC. + * + * The callback is provided the length of the pending packet to fetch. This includes the + * HTC header length plus the length of payload. The callback can return a pointer to + * the allocated HTC packet for immediate use. + * + * Alternatively a variant of this handler can be used to allocate large receive packets as needed. + * For example an application can use the refill mechanism for normal packets and the recv-alloc mechanism to + * handle the case where a large packet buffer is required. This can significantly reduce the + * amount of "committed" memory used to receive packets. + * + * */ +typedef HTC_PACKET *(*HTC_EP_RECV_ALLOC)(void *, + HTC_ENDPOINT_ID Endpoint, + int Length); + +typedef enum _HTC_SEND_FULL_ACTION { + HTC_SEND_FULL_KEEP = 0, /* packet that overflowed should be kept in the queue */ + HTC_SEND_FULL_DROP = 1, /* packet that overflowed should be dropped */ +} HTC_SEND_FULL_ACTION; + +/* Optional per service connection callback when a send queue is full. This can occur if the + * host continues queueing up TX packets faster than credits can arrive + * To prevent the host (on some Oses like Linux) from continuously queueing packets + * and consuming resources, this callback is provided so that that the host + * can disable TX in the subsystem (i.e. network stack). + * This callback is invoked for each packet that "overflows" the HTC queue. The callback can + * determine whether the new packet that overflowed the queue can be kept (HTC_SEND_FULL_KEEP) or + * dropped (HTC_SEND_FULL_DROP). If a packet is dropped, the EpTxComplete handler will be called + * and the packet's status field will be set to A_NO_RESOURCE. + * Other OSes require a "per-packet" indication for each completed TX packet, this + * closed loop mechanism will prevent the network stack from overunning the NIC + * The packet to keep or drop is passed for inspection to the registered handler the handler + * must ONLY inspect the packet, it may not free or reclaim the packet. */ +typedef HTC_SEND_FULL_ACTION (*HTC_EP_SEND_QUEUE_FULL)(void *, + HTC_PACKET * + pPacket); + +typedef struct _HTC_EP_CALLBACKS { + void *pContext; /* context for each callback */ + HTC_EP_SEND_PKT_COMPLETE EpTxComplete; /* tx completion callback for connected endpoint */ + HTC_EP_RECV_PKT EpRecv; /* receive callback for connected endpoint */ + HTC_EP_RECV_REFILL EpRecvRefill; /* OPTIONAL receive re-fill callback for connected endpoint */ + HTC_EP_SEND_QUEUE_FULL EpSendFull; /* OPTIONAL send full callback */ + HTC_EP_RECV_ALLOC EpRecvAlloc; /* OPTIONAL recv allocation callback */ + HTC_EP_RECV_ALLOC EpRecvAllocThresh; /* OPTIONAL recv allocation callback based on a threshold */ + HTC_EP_SEND_PKT_COMP_MULTIPLE EpTxCompleteMultiple; /* OPTIONAL completion handler for multiple complete + indications (EpTxComplete must be NULL) */ + HTC_EP_RECV_PKT_MULTIPLE EpRecvPktMultiple; /* OPTIONAL completion handler for multiple + recv packet indications (EpRecv must be NULL) */ + HTC_EP_RESUME_TX_QUEUE ep_resume_tx_queue; + int RecvAllocThreshold; /* if EpRecvAllocThresh is non-NULL, HTC will compare the + threshold value to the current recv packet length and invoke + the EpRecvAllocThresh callback to acquire a packet buffer */ + int RecvRefillWaterMark; /* if a EpRecvRefill handler is provided, this value + can be used to set a trigger refill callback + when the recv queue drops below this value + if set to 0, the refill is only called when packets + are empty */ +} HTC_EP_CALLBACKS; + +/* service connection information */ +typedef struct _HTC_SERVICE_CONNECT_REQ { + HTC_SERVICE_ID service_id; /* service ID to connect to */ + A_UINT16 ConnectionFlags; /* connection flags, see htc protocol definition */ + A_UINT8 *pMetaData; /* ptr to optional service-specific meta-data */ + A_UINT8 MetaDataLength; /* optional meta data length */ + HTC_EP_CALLBACKS EpCallbacks; /* endpoint callbacks */ + int MaxSendQueueDepth; /* maximum depth of any send queue */ + A_UINT32 LocalConnectionFlags; /* HTC flags for the host-side (local) connection */ + unsigned int MaxSendMsgSize; /* override max message size in send direction */ +} HTC_SERVICE_CONNECT_REQ; + +#define HTC_LOCAL_CONN_FLAGS_ENABLE_SEND_BUNDLE_PADDING (1 << 0) /* enable send bundle padding for this endpoint */ + +/* service connection response information */ +typedef struct _HTC_SERVICE_CONNECT_RESP { + A_UINT8 *pMetaData; /* caller supplied buffer to optional meta-data */ + A_UINT8 BufferLength; /* length of caller supplied buffer */ + A_UINT8 ActualLength; /* actual length of meta data */ + HTC_ENDPOINT_ID Endpoint; /* endpoint to communicate over */ + unsigned int MaxMsgLength; /* max length of all messages over this endpoint */ + A_UINT8 ConnectRespCode; /* connect response code from target */ +} HTC_SERVICE_CONNECT_RESP; + +/* endpoint distribution structure */ +typedef struct _HTC_ENDPOINT_CREDIT_DIST { + struct _HTC_ENDPOINT_CREDIT_DIST *pNext; + struct _HTC_ENDPOINT_CREDIT_DIST *pPrev; + HTC_SERVICE_ID service_id; /* Service ID (set by HTC) */ + HTC_ENDPOINT_ID Endpoint; /* endpoint for this distribution struct (set by HTC) */ + A_UINT32 DistFlags; /* distribution flags, distribution function can + set default activity using SET_EP_ACTIVE() macro */ + int TxCreditsNorm; /* credits for normal operation, anything above this + indicates the endpoint is over-subscribed, this field + is only relevant to the credit distribution function */ + int TxCreditsMin; /* floor for credit distribution, this field is + only relevant to the credit distribution function */ + int TxCreditsAssigned; /* number of credits assigned to this EP, this field + is only relevant to the credit dist function */ + int TxCredits; /* current credits available, this field is used by + HTC to determine whether a message can be sent or + must be queued */ + int TxCreditsToDist; /* pending credits to distribute on this endpoint, this + is set by HTC when credit reports arrive. + The credit distribution functions sets this to zero + when it distributes the credits */ + int TxCreditsSeek; /* this is the number of credits that the current pending TX + packet needs to transmit. This is set by HTC when + and endpoint needs credits in order to transmit */ + int TxCreditSize; /* size in bytes of each credit (set by HTC) */ + int TxCreditsPerMaxMsg; /* credits required for a maximum sized messages (set by HTC) */ + void *pHTCReserved; /* reserved for HTC use */ + int TxQueueDepth; /* current depth of TX queue , i.e. messages waiting for credits + This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE + or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint + that has non-zero credits to recover + */ +} HTC_ENDPOINT_CREDIT_DIST; + +#define HTC_EP_ACTIVE ((A_UINT32) (1u << 31)) + +/* macro to check if an endpoint has gone active, useful for credit + * distributions */ +#define IS_EP_ACTIVE(epDist) ((epDist)->DistFlags & HTC_EP_ACTIVE) +#define SET_EP_ACTIVE(epDist) (epDist)->DistFlags |= HTC_EP_ACTIVE + +/* credit distibution code that is passed into the distrbution function, + * there are mandatory and optional codes that must be handled */ +typedef enum _HTC_CREDIT_DIST_REASON { + HTC_CREDIT_DIST_SEND_COMPLETE = 0, /* credits available as a result of completed + send operations (MANDATORY) resulting in credit reports */ + HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1, /* a change in endpoint activity occured (OPTIONAL) */ + HTC_CREDIT_DIST_SEEK_CREDITS, /* an endpoint needs to "seek" credits (OPTIONAL) */ + HTC_DUMP_CREDIT_STATE /* for debugging, dump any state information that is kept by + the distribution function */ +} HTC_CREDIT_DIST_REASON; + +typedef void (*HTC_CREDIT_DIST_CALLBACK)(void *Context, + HTC_ENDPOINT_CREDIT_DIST * + pEPList, + HTC_CREDIT_DIST_REASON + Reason); + +typedef void (*HTC_CREDIT_INIT_CALLBACK)(void *Context, + HTC_ENDPOINT_CREDIT_DIST * + pEPList, int TotalCredits); + +/* endpoint statistics action */ +typedef enum _HTC_ENDPOINT_STAT_ACTION { + HTC_EP_STAT_SAMPLE = 0, /* only read statistics */ + HTC_EP_STAT_SAMPLE_AND_CLEAR = 1, /* sample and immediately clear statistics */ + HTC_EP_STAT_CLEAR /* clear only */ +} HTC_ENDPOINT_STAT_ACTION; + +/* endpoint statistics */ +typedef struct _HTC_ENDPOINT_STATS { + A_UINT32 TxPosted; /* number of TX packets posted to the endpoint */ + A_UINT32 TxCreditLowIndications; /* number of times the host set the credit-low flag in a send message on + this endpoint */ + A_UINT32 TxIssued; /* running count of total TX packets issued */ + A_UINT32 TxPacketsBundled; /* running count of TX packets that were issued in bundles */ + A_UINT32 TxBundles; /* running count of TX bundles that were issued */ + A_UINT32 TxDropped; /* tx packets that were dropped */ + A_UINT32 TxCreditRpts; /* running count of total credit reports received for this endpoint */ + A_UINT32 TxCreditRptsFromRx; /* credit reports received from this endpoint's RX packets */ + A_UINT32 TxCreditRptsFromOther; /* credit reports received from RX packets of other endpoints */ + A_UINT32 TxCreditRptsFromEp0; /* credit reports received from endpoint 0 RX packets */ + A_UINT32 TxCreditsFromRx; /* count of credits received via Rx packets on this endpoint */ + A_UINT32 TxCreditsFromOther; /* count of credits received via another endpoint */ + A_UINT32 TxCreditsFromEp0; /* count of credits received via another endpoint */ + A_UINT32 TxCreditsConsummed; /* count of consummed credits */ + A_UINT32 TxCreditsReturned; /* count of credits returned */ + A_UINT32 RxReceived; /* count of RX packets received */ + A_UINT32 RxLookAheads; /* count of lookahead records + found in messages received on this endpoint */ + A_UINT32 RxPacketsBundled; /* count of recv packets received in a bundle */ + A_UINT32 RxBundleLookAheads; /* count of number of bundled lookaheads */ + A_UINT32 RxBundleIndFromHdr; /* count of the number of bundle indications from the HTC header */ + A_UINT32 RxAllocThreshHit; /* count of the number of times the recv allocation threshhold was hit */ + A_UINT32 RxAllocThreshBytes; /* total number of bytes */ +} HTC_ENDPOINT_STATS; + +/* ------ Function Prototypes ------ */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Create an instance of HTC over the underlying HIF device + @function name: htc_create + @input: HifDevice - hif device handle, + pInfo - initialization information + @output: + @return: HTC_HANDLE on success, NULL on failure + @notes: + @example: + @see also: htc_destroy + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +HTC_HANDLE htc_create(void *HifDevice, + HTC_INIT_INFO *pInfo, cdf_device_t osdev); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Get the underlying HIF device handle + @function name: htc_get_hif_device + @input: HTCHandle - handle passed into the AddInstance callback + @output: + @return: opaque HIF device handle usable in HIF API calls. + @notes: + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void *htc_get_hif_device(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Set credit distribution parameters + @function name: htc_set_credit_distribution + @input: HTCHandle - HTC handle + pCreditDistCont - caller supplied context to pass into distribution functions + CreditDistFunc - Distribution function callback + CreditDistInit - Credit Distribution initialization callback + ServicePriorityOrder - Array containing list of service IDs, lowest index is highest + priority + ListLength - number of elements in ServicePriorityOrder + @output: + @return: + @notes: The user can set a custom credit distribution function to handle special requirements + for each endpoint. A default credit distribution routine can be used by setting + CreditInitFunc to NULL. The default credit distribution is only provided for simple + "fair" credit distribution without regard to any prioritization. + + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_set_credit_distribution(HTC_HANDLE HTCHandle, + void *pCreditDistContext, + HTC_CREDIT_DIST_CALLBACK CreditDistFunc, + HTC_CREDIT_INIT_CALLBACK CreditInitFunc, + HTC_SERVICE_ID ServicePriorityOrder[], + int ListLength); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Wait for the target to indicate the HTC layer is ready + @function name: htc_wait_target + @input: HTCHandle - HTC handle + @output: + @return: + @notes: This API blocks until the target responds with an HTC ready message. + The caller should not connect services until the target has indicated it is + ready. + @example: + @see also: htc_connect_service + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_wait_target(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Start target service communications + @function name: htc_start + @input: HTCHandle - HTC handle + @output: + @return: + @notes: This API indicates to the target that the service connection phase is complete + and the target can freely start all connected services. This API should only be + called AFTER all service connections have been made. TCStart will issue a + SETUP_COMPLETE message to the target to indicate that all service connections + have been made and the target can start communicating over the endpoints. + @example: + @see also: htc_connect_service + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_start(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Add receive packet to HTC + @function name: htc_add_receive_pkt + @input: HTCHandle - HTC handle + pPacket - HTC receive packet to add + @output: + @return: A_OK on success + @notes: user must supply HTC packets for capturing incomming HTC frames. The caller + must initialize each HTC packet using the SET_HTC_PACKET_INFO_RX_REFILL() + macro. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_add_receive_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Connect to an HTC service + @function name: htc_connect_service + @input: HTCHandle - HTC handle + pReq - connection details + @output: pResp - connection response + @return: + @notes: Service connections must be performed before htc_start. User provides callback handlers + for various endpoint events. + @example: + @see also: htc_start + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_connect_service(HTC_HANDLE HTCHandle, + HTC_SERVICE_CONNECT_REQ *pReq, + HTC_SERVICE_CONNECT_RESP *pResp); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: HTC register log dump + @function name: htc_dump + @input: HTCHandle - HTC handle + CmdId - Log command + start - start/print logs + @output: + @return: + @notes: Register logs will be started/printed. + be flushed. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +void htc_dump(HTC_HANDLE HTCHandle, uint8_t CmdId, bool start); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Send an HTC packet + @function name: htc_send_pkt + @input: HTCHandle - HTC handle + pPacket - packet to send + @output: + @return: A_OK + @notes: Caller must initialize packet using SET_HTC_PACKET_INFO_TX() macro. + This interface is fully asynchronous. On error, HTC SendPkt will + call the registered Endpoint callback to cleanup the packet. + @example: + @see also: htc_flush_endpoint + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_send_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Send an HTC packet containing a tx descriptor and data + @function name: htc_send_data_pkt + @input: HTCHandle - HTC handle + pPacket - packet to send + @output: + @return: A_OK + @notes: Caller must initialize packet using SET_HTC_PACKET_INFO_TX() macro. + Caller must provide headroom in an initial fragment added to the + network buffer to store a HTC_FRAME_HDR. + This interface is fully asynchronous. On error, htc_send_data_pkt will + call the registered Endpoint EpDataTxComplete callback to cleanup + the packet. + @example: + @see also: htc_send_pkt + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifdef ATH_11AC_TXCOMPACT +A_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, cdf_nbuf_t netbuf, + int Epid, int ActualLength); +#else /*ATH_11AC_TXCOMPACT */ +A_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket, + A_UINT8 more_data); +#endif /*ATH_11AC_TXCOMPACT */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Flush HTC when target is removed surprisely service communications + @function name: htc_flush_surprise_remove + @input: HTCHandle - HTC handle + @output: + @return: + @notes: All receive and pending TX packets will + be flushed. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_flush_surprise_remove(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Stop HTC service communications + @function name: htc_stop + @input: HTCHandle - HTC handle + @output: + @return: + @notes: HTC communications is halted. All receive and pending TX packets will + be flushed. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_stop(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Destory HTC service + @function name: htc_destroy + @input: HTCHandle + @output: + @return: + @notes: This cleans up all resources allocated by htc_create(). + @example: + @see also: htc_create + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_destroy(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Flush pending TX packets + @function name: htc_flush_endpoint + @input: HTCHandle - HTC handle + Endpoint - Endpoint to flush + Tag - flush tag + @output: + @return: + @notes: The Tag parameter is used to selectively flush packets with matching tags. + The value of 0 forces all packets to be flush regardless of tag. + @example: + @see also: htc_send_pkt + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_flush_endpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, + HTC_TX_TAG Tag); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Dump credit distribution state + @function name: htc_dump_credit_states + @input: HTCHandle - HTC handle + @output: + @return: + @notes: This dumps all credit distribution information to the debugger + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_dump_credit_states(HTC_HANDLE HTCHandle); +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Indicate a traffic activity change on an endpoint + @function name: htc_indicate_activity_change + @input: HTCHandle - HTC handle + Endpoint - endpoint in which activity has changed + Active - true if active, false if it has become inactive + @output: + @return: + @notes: This triggers the registered credit distribution function to + re-adjust credits for active/inactive endpoints. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_indicate_activity_change(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint, A_BOOL Active); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Get endpoint statistics + @function name: htc_get_endpoint_statistics + @input: HTCHandle - HTC handle + Endpoint - Endpoint identifier + Action - action to take with statistics + @output: + pStats - statistics that were sampled (can be NULL if Action is HTC_EP_STAT_CLEAR) + + @return: true if statistics profiling is enabled, otherwise false. + + @notes: Statistics is a compile-time option and this function may return false + if HTC is not compiled with profiling. + + The caller can specify the statistic "action" to take when sampling + the statistics. This includes: + + HTC_EP_STAT_SAMPLE: The pStats structure is filled with the current values. + HTC_EP_STAT_SAMPLE_AND_CLEAR: The structure is filled and the current statistics + are cleared. + HTC_EP_STAT_CLEA : the statistics are cleared, the called can pass a NULL value for + pStats + + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_BOOL htc_get_endpoint_statistics(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint, + HTC_ENDPOINT_STAT_ACTION Action, + HTC_ENDPOINT_STATS *pStats); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Unblock HTC message reception + @function name: htc_unblock_recv + @input: HTCHandle - HTC handle + @output: + @return: + @notes: + HTC will block the receiver if the EpRecvAlloc callback fails to provide a packet. + The caller can use this API to indicate to HTC when resources (buffers) are available + such that the receiver can be unblocked and HTC may re-attempt fetching the pending message. + + This API is not required if the user uses the EpRecvRefill callback or uses the HTCAddReceivePacket() + API to recycle or provide receive packets to HTC. + + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_unblock_recv(HTC_HANDLE HTCHandle); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: send a series of HTC packets + @function name: htc_send_pkts_multiple + @input: HTCHandle - HTC handle + pPktQueue - local queue holding packets to send + @output: + @return: A_OK + @notes: Caller must initialize each packet using SET_HTC_PACKET_INFO_TX() macro. + The queue must only contain packets directed at the same endpoint. + Caller supplies a pointer to an HTC_PACKET_QUEUE structure holding the TX packets in FIFO order. + This API will remove the packets from the pkt queue and place them into the HTC Tx Queue + and bundle messages where possible. + The caller may allocate the pkt queue on the stack to hold the packets. + This interface is fully asynchronous. On error, htc_send_pkts will + call the registered Endpoint callback to cleanup the packet. + @example: + @see also: htc_flush_endpoint + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_send_pkts_multiple(HTC_HANDLE HTCHandle, + HTC_PACKET_QUEUE *pPktQueue); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Add multiple receive packets to HTC + @function name: htc_add_receive_pkt_multiple + @input: HTCHandle - HTC handle + pPktQueue - HTC receive packet queue holding packets to add + @output: + @return: A_OK on success + @notes: user must supply HTC packets for capturing incomming HTC frames. The caller + must initialize each HTC packet using the SET_HTC_PACKET_INFO_RX_REFILL() + macro. The queue must only contain recv packets for the same endpoint. + Caller supplies a pointer to an HTC_PACKET_QUEUE structure holding the recv packet. + This API will remove the packets from the pkt queue and place them into internal + recv packet list. + The caller may allocate the pkt queue on the stack to hold the packets. + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_STATUS htc_add_receive_pkt_multiple(HTC_HANDLE HTCHandle, + HTC_PACKET_QUEUE *pPktQueue); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Check if an endpoint is marked active + @function name: htc_is_endpoint_active + @input: HTCHandle - HTC handle + Endpoint - endpoint to check for active state + @output: + @return: returns true if Endpoint is Active + @notes: + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +A_BOOL htc_is_endpoint_active(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Get the number of recv buffers currently queued into an HTC endpoint + @function name: htc_get_num_recv_buffers + @input: HTCHandle - HTC handle + Endpoint - endpoint to check + @output: + @return: returns number of buffers in queue + @notes: + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +int htc_get_num_recv_buffers(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @desc: Set the target failure handling callback in HTC layer + @function name: htc_set_target_failure_callback + @input: HTCHandle - HTC handle + Callback - target failure handling callback + @output: + @return: + @notes: + @example: + @see also: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void htc_set_target_failure_callback(HTC_HANDLE HTCHandle, + HTC_TARGET_FAILURE Callback); + +/* internally used functions for testing... */ +void htc_enable_recv(HTC_HANDLE HTCHandle); +void htc_disable_recv(HTC_HANDLE HTCHandle); +A_STATUS HTCWaitForPendingRecv(HTC_HANDLE HTCHandle, + A_UINT32 TimeoutInMs, + A_BOOL *pbIsRecvPending); + +/* function to fetch stats from htc layer*/ +struct ol_ath_htc_stats *ieee80211_ioctl_get_htc_stats(HTC_HANDLE + HTCHandle); + +#ifdef HIF_USB +#define HTCReturnReceivePkt(target,p,osbuf) \ + A_NETBUF_FREE(osbuf); \ + if(p->Status == A_CLONE) { \ + cdf_mem_free(p); \ + } +#else +#define HTCReturnReceivePkt(target,p,osbuf) htc_add_receive_pkt(target,p) +#endif + +#ifdef WLAN_FEATURE_FASTPATH +#define HTC_TX_DESC_FILL(_htc_tx_desc, _download_len, _ep_id, _seq_no) \ +do { \ + HTC_WRITE32((_htc_tx_desc), \ + SM((_download_len), HTC_FRAME_HDR_PAYLOADLEN) | \ + SM((_ep_id), HTC_FRAME_HDR_ENDPOINTID)); \ + \ + HTC_WRITE32((A_UINT32 *)(_htc_tx_desc) + 1, \ + SM((_seq_no), HTC_FRAME_HDR_CONTROLBYTES1));\ +} while (0) +#endif /* WLAN_FEATURE_FASTPATH */ + +#ifdef __cplusplus +} +#endif +void htc_get_control_endpoint_tx_host_credits(HTC_HANDLE HTCHandle, int *credit); +void htc_dump_counter_info(HTC_HANDLE HTCHandle); +void *htc_get_targetdef(HTC_HANDLE htc_handle); +void htc_set_target_to_sleep(void *context); +void htc_cancel_deferred_target_sleep(void *context); +int htc_runtime_suspend(void); +int htc_runtime_resume(void); + +/* Disable ASPM : Disable PCIe low power */ +void htc_disable_aspm(void); + +#ifdef IPA_OFFLOAD +void htc_ipa_get_ce_resource(HTC_HANDLE htc_handle, + cdf_dma_addr_t *ce_sr_base_paddr, + uint32_t *ce_sr_ring_size, + cdf_dma_addr_t *ce_reg_paddr); +#else +#define htc_ipa_get_ce_resource(htc_handle, \ + ce_sr_base_paddr, \ + ce_sr_ring_size, \ + ce_reg_paddr) /* NO-OP */ +#endif /* IPA_OFFLOAD */ +#endif /* _HTC_API_H_ */ diff --git a/htc/htc_debug.h b/htc/htc_debug.h new file mode 100644 index 000000000000..fe0c8496f813 --- /dev/null +++ b/htc/htc_debug.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#ifndef HTC_DEBUG_H_ +#define HTC_DEBUG_H_ + +#define ATH_MODULE_NAME htc +#include "a_debug.h" +#include "cdf_trace.h" + +/* ------- Debug related stuff ------- */ + +#define ATH_DEBUG_SEND ATH_DEBUG_MAKE_MODULE_MASK(0) +#define ATH_DEBUG_RECV ATH_DEBUG_MAKE_MODULE_MASK(1) +#define ATH_DEBUG_SYNC ATH_DEBUG_MAKE_MODULE_MASK(2) +#define ATH_DEBUG_DUMP ATH_DEBUG_MAKE_MODULE_MASK(3) +#define ATH_DEBUG_SETUP ATH_DEBUG_MAKE_MODULE_MASK(4) +#define HTC_ERROR(args ...) \ + CDF_TRACE(CDF_MODULE_ID_HTC, CDF_TRACE_LEVEL_ERROR, ## args) +#define HTC_WARN(args ...) \ + CDF_TRACE(CDF_MODULE_ID_HTC, CDF_TRACE_LEVEL_WARN, ## args) +#define HTC_INFO(args ...) \ + CDF_TRACE(CDF_MODULE_ID_HTC, CDF_TRACE_LEVEL_INFO, ## args) +#define HTC_TRACE(args ...) \ + CDF_TRACE(CDF_MODULE_ID_HTC, CDF_TRACE_LEVEL_DEBUG, ## args) +#endif /*HTC_DEBUG_H_ */ diff --git a/htc/htc_internal.h b/htc/htc_internal.h new file mode 100644 index 000000000000..5b98d1368aae --- /dev/null +++ b/htc/htc_internal.h @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#ifndef _HTC_INTERNAL_H_ +#define _HTC_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <athdefs.h> +#include "a_types.h" +#include "osapi_linux.h" +#include <cdf_nbuf.h> +#include <cdf_types.h> +#include <cdf_lock.h> +#include <cdf_softirq_timer.h> +#include <cdf_atomic.h> +#include "hif.h" +#include <htc.h> +#include "htc_api.h" +#include "htc_packet.h" + +/* HTC operational parameters */ +#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ +#define HTC_TARGET_DEBUG_INTR_MASK 0x01 +#define HTC_TARGET_CREDIT_INTR_MASK 0xF0 +#define HTC_MIN_MSG_PER_BUNDLE 2 +#if defined(HIF_USB) +#define HTC_MAX_MSG_PER_BUNDLE 9 +#else +#define HTC_MAX_MSG_PER_BUNDLE 16 +#endif +/* + * HTC_MAX_TX_BUNDLE_SEND_LIMIT - + * This value is in units of tx frame fragments. + * It needs to be at least as large as the maximum number of tx frames in a + * HTC download bundle times the average number of fragments in each such frame + * (In certain operating systems, such as Linux, we expect to only have + * a single fragment per frame anyway.) + */ +#define HTC_MAX_TX_BUNDLE_SEND_LIMIT 255 + +#define HTC_PACKET_CONTAINER_ALLOCATION 32 +#define NUM_CONTROL_TX_BUFFERS 2 +#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CONTROL_MESSAGE_LENGTH + HTC_HDR_LENGTH) +#define HTC_CONTROL_BUFFER_ALIGN 32 +#define HTC_TARGET_RESPONSE_POLL_MS 10 +#if !defined(A_SIMOS_DEVHOST) +#define HTC_TARGET_MAX_RESPONSE_POLL 200 /* actual HW */ +#else +#define HTC_TARGET_MAX_RESPONSE_POLL 600 /* host + target simulation */ +#endif + +#define HTC_SERVICE_TX_PACKET_TAG HTC_TX_PACKET_TAG_INTERNAL + +#define HTC_CREDIT_HISTORY_MAX 1024 + +typedef enum { + HTC_REQUEST_CREDIT, + HTC_PROCESS_CREDIT_REPORT, + HTC_SUSPEND_ACK, + HTC_SUSPEND_NACK, +} htc_credit_exchange_type; + +typedef struct { + htc_credit_exchange_type type; + uint64_t time; + uint32_t tx_credit; + uint32_t htc_tx_queue_depth; +} HTC_CREDIT_HISTORY; + +typedef struct _HTC_ENDPOINT { + HTC_ENDPOINT_ID Id; + + /* service ID this endpoint is bound to + * non-zero value means this endpoint is in use + */ + HTC_SERVICE_ID service_id; + + HTC_EP_CALLBACKS EpCallBacks; /* callbacks associated with this endpoint */ + HTC_PACKET_QUEUE TxQueue; /* HTC frame buffer TX queue */ + int MaxTxQueueDepth; /* max depth of the TX queue before we need to + call driver's full handler */ + int MaxMsgLength; /* max length of endpoint message */ + uint8_t UL_PipeID; + uint8_t DL_PipeID; + int ul_is_polled; /* Need to call HIF to get tx completion callbacks? */ + cdf_softirq_timer_t ul_poll_timer; + int ul_poll_timer_active; + int ul_outstanding_cnt; + int dl_is_polled; /* Need to call HIF to fetch rx? (Not currently supported.) */ +#if 0 /* not currently supported */ + cdf_softirq_timer_t dl_poll_timer; +#endif + + HTC_PACKET_QUEUE TxLookupQueue; /* lookup queue to match netbufs to htc packets */ + HTC_PACKET_QUEUE RxBufferHoldQueue; /* temporary hold queue for back compatibility */ + A_UINT8 SeqNo; /* TX seq no (helpful) for debugging */ + cdf_atomic_t TxProcessCount; /* serialization */ + struct _HTC_TARGET *target; + int TxCredits; /* TX credits available on this endpoint */ + int TxCreditSize; /* size in bytes of each credit (set by HTC) */ + int TxCreditsPerMaxMsg; /* credits required per max message (precalculated) */ +#ifdef HTC_EP_STAT_PROFILING + HTC_ENDPOINT_STATS endpoint_stats; /* endpoint statistics */ +#endif + A_BOOL TxCreditFlowEnabled; +} HTC_ENDPOINT; + +#ifdef HTC_EP_STAT_PROFILING +#define INC_HTC_EP_STAT(p, stat, count) ((p)->endpoint_stats.stat += (count)) +#else +#define INC_HTC_EP_STAT(p, stat, count) +#endif + +typedef struct { + A_UINT16 service_id; + A_UINT8 CreditAllocation; +} HTC_SERVICE_TX_CREDIT_ALLOCATION; + +#define HTC_MAX_SERVICE_ALLOC_ENTRIES 8 + +/* Error codes for HTC layer packet stats*/ +enum ol_ath_htc_pkt_ecodes { + GET_HTC_PKT_Q_FAIL = 0, /* error- get packet at head of HTC_PACKET_Q */ + HTC_PKT_Q_EMPTY, + HTC_SEND_Q_EMPTY +}; +/* our HTC target state */ +typedef struct _HTC_TARGET { + struct ol_softc *hif_dev; + HTC_ENDPOINT endpoint[ENDPOINT_MAX]; + cdf_spinlock_t HTCLock; + cdf_spinlock_t HTCRxLock; + cdf_spinlock_t HTCTxLock; + cdf_spinlock_t HTCCreditLock; + A_UINT32 HTCStateFlags; + void *host_handle; + HTC_INIT_INFO HTCInitInfo; + HTC_PACKET *pHTCPacketStructPool; /* pool of HTC packets */ + HTC_PACKET_QUEUE ControlBufferTXFreeList; + A_UINT8 CtrlResponseBuffer[HTC_MAX_CONTROL_MESSAGE_LENGTH]; + int CtrlResponseLength; + cdf_event_t ctrl_response_valid; + A_BOOL CtrlResponseProcessing; + int TotalTransmitCredits; + HTC_SERVICE_TX_CREDIT_ALLOCATION + ServiceTxAllocTable[HTC_MAX_SERVICE_ALLOC_ENTRIES]; + int TargetCreditSize; +#ifdef RX_SG_SUPPORT + cdf_nbuf_queue_t RxSgQueue; + A_BOOL IsRxSgInprogress; + A_UINT32 CurRxSgTotalLen; /* current total length */ + A_UINT32 ExpRxSgTotalLen; /* expected total length */ +#endif + cdf_device_t osdev; + struct ol_ath_htc_stats htc_pkt_stats; + HTC_PACKET *pBundleFreeList; + A_UINT32 ce_send_cnt; + A_UINT32 TX_comp_cnt; + A_UINT8 MaxMsgsPerHTCBundle; + cdf_work_t queue_kicker; +} HTC_TARGET; + +#define HTC_ENABLE_BUNDLE(target) (target->MaxMsgsPerHTCBundle > 1) +#ifdef RX_SG_SUPPORT +#define RESET_RX_SG_CONFIG(_target) \ + _target->ExpRxSgTotalLen = 0; \ + _target->CurRxSgTotalLen = 0; \ + _target->IsRxSgInprogress = false; +#endif + +#define HTC_STATE_STOPPING (1 << 0) +#define HTC_STOPPING(t) ((t)->HTCStateFlags & HTC_STATE_STOPPING) +#define LOCK_HTC(t) cdf_spin_lock_bh(&(t)->HTCLock); +#define UNLOCK_HTC(t) cdf_spin_unlock_bh(&(t)->HTCLock); +#define LOCK_HTC_RX(t) cdf_spin_lock_bh(&(t)->HTCRxLock); +#define UNLOCK_HTC_RX(t) cdf_spin_unlock_bh(&(t)->HTCRxLock); +#define LOCK_HTC_TX(t) cdf_spin_lock_bh(&(t)->HTCTxLock); +#define UNLOCK_HTC_TX(t) cdf_spin_unlock_bh(&(t)->HTCTxLock); +#define LOCK_HTC_CREDIT(t) cdf_spin_lock_bh(&(t)->HTCCreditLock); +#define UNLOCK_HTC_CREDIT(t) cdf_spin_unlock_bh(&(t)->HTCCreditLock); + +#define GET_HTC_TARGET_FROM_HANDLE(hnd) ((HTC_TARGET *)(hnd)) + +#define IS_TX_CREDIT_FLOW_ENABLED(ep) ((ep)->TxCreditFlowEnabled) + +#define HTC_POLL_CLEANUP_PERIOD_MS 10 /* milliseconds */ + +/* Macro to Increment the HTC_PACKET_ERRORS for Tx.*/ +#define OL_ATH_HTC_PKT_ERROR_COUNT_INCR(_target,_ecode) \ + do { \ + if(_ecode==GET_HTC_PKT_Q_FAIL) (_target->htc_pkt_stats.htc_get_pkt_q_fail_count)+=1; \ + if(_ecode==HTC_PKT_Q_EMPTY) (_target->htc_pkt_stats.htc_pkt_q_empty_count)+=1; \ + if(_ecode==HTC_SEND_Q_EMPTY) (_target->htc_pkt_stats.htc_send_q_empty_count)+=1; \ + } while(0); +/* internal HTC functions */ + +CDF_STATUS htc_rx_completion_handler(void *Context, cdf_nbuf_t netbuf, + uint8_t pipeID); +CDF_STATUS htc_tx_completion_handler(void *Context, cdf_nbuf_t netbuf, + unsigned int transferID, uint32_t toeplitz_hash_result); + +HTC_PACKET *allocate_htc_bundle_packet(HTC_TARGET *target); +void free_htc_bundle_packet(HTC_TARGET *target, HTC_PACKET *pPacket); + +HTC_PACKET *allocate_htc_packet_container(HTC_TARGET *target); +void free_htc_packet_container(HTC_TARGET *target, HTC_PACKET *pPacket); +void htc_flush_rx_hold_queue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint); +void htc_flush_endpoint_tx(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, + HTC_TX_TAG Tag); +void htc_recv_init(HTC_TARGET *target); +A_STATUS htc_wait_recv_ctrl_message(HTC_TARGET *target); +void htc_free_control_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket); +HTC_PACKET *htc_alloc_control_tx_packet(HTC_TARGET *target); +A_UINT8 htc_get_credit_allocation(HTC_TARGET *target, A_UINT16 service_id); +void htc_tx_resource_avail_handler(void *context, A_UINT8 pipeID); +void htc_control_rx_complete(void *Context, HTC_PACKET *pPacket); +void htc_process_credit_rpt(HTC_TARGET *target, + HTC_CREDIT_REPORT *pRpt, + int NumEntries, HTC_ENDPOINT_ID FromEndpoint); +void htc_fw_event_handler(void *context, CDF_STATUS status); +void htc_send_complete_check_cleanup(void *context); +void htc_runtime_pm_init(HTC_TARGET *target); +void htc_kick_queues(void *context); + +void htc_credit_record(htc_credit_exchange_type type, uint32_t tx_credit, + uint32_t htc_tx_queue_depth); + +static inline void htc_send_complete_poll_timer_stop(HTC_ENDPOINT * + pEndpoint) { + LOCK_HTC_TX(pEndpoint->target); + if (pEndpoint->ul_poll_timer_active) { + /* cdf_softirq_timer_cancel(&pEndpoint->ul_poll_timer); */ + pEndpoint->ul_poll_timer_active = 0; + } + UNLOCK_HTC_TX(pEndpoint->target); +} + +static inline void htc_send_complete_poll_timer_start(HTC_ENDPOINT * + pEndpoint) { + LOCK_HTC_TX(pEndpoint->target); + if (pEndpoint->ul_outstanding_cnt + && !pEndpoint->ul_poll_timer_active) { + /* + cdf_softirq_timer_start( + &pEndpoint->ul_poll_timer, HTC_POLL_CLEANUP_PERIOD_MS); + */ + pEndpoint->ul_poll_timer_active = 1; + } + UNLOCK_HTC_TX(pEndpoint->target); +} + +static inline void +htc_send_complete_check(HTC_ENDPOINT *pEndpoint, int force) { + /* + * Stop the polling-cleanup timer that will result in a later call to + * this function. It may get started again below, if there are still + * outsending sends. + */ + htc_send_complete_poll_timer_stop(pEndpoint); + /* + * Check whether HIF has any prior sends that have finished, + * have not had the post-processing done. + */ + hif_send_complete_check(pEndpoint->target->hif_dev, + pEndpoint->UL_PipeID, force); + /* + * If there are still outstanding sends after polling, start a timer + * to check again a little later. + */ + htc_send_complete_poll_timer_start(pEndpoint); +} + +#ifdef __cplusplus +} +#endif + +#ifndef DEBUG_BUNDLE +#define DEBUG_BUNDLE 0 +#endif + +#ifdef HIF_SDIO +#ifndef ENABLE_BUNDLE_TX +#define ENABLE_BUNDLE_TX 1 +#endif + +#ifndef ENABLE_BUNDLE_RX +#define ENABLE_BUNDLE_RX 1 +#endif +#endif /* HIF_SDIO */ +#endif /* !_HTC_HOST_INTERNAL_H_ */ diff --git a/htc/htc_packet.h b/htc/htc_packet.h new file mode 100644 index 000000000000..ca3698f019b7 --- /dev/null +++ b/htc/htc_packet.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#ifndef HTC_PACKET_H_ +#define HTC_PACKET_H_ + +#include <osdep.h> +#include <a_types.h> /* A_UINT16, etc. */ +#include "dl_list.h" + +/* ------ Endpoint IDS ------ */ +typedef enum { + ENDPOINT_UNUSED = -1, + ENDPOINT_0 = 0, + ENDPOINT_1 = 1, + ENDPOINT_2 = 2, + ENDPOINT_3, + ENDPOINT_4, + ENDPOINT_5, + ENDPOINT_6, + ENDPOINT_7, + ENDPOINT_8, + ENDPOINT_MAX, +} HTC_ENDPOINT_ID; + +struct _HTC_PACKET; + +typedef void (*HTC_PACKET_COMPLETION)(void *, struct _HTC_PACKET *); + +typedef A_UINT16 HTC_TX_TAG; + +typedef struct _HTC_TX_PACKET_INFO { + HTC_TX_TAG Tag; /* tag used to selective flush packets */ + int CreditsUsed; /* number of credits used for this TX packet (HTC internal) */ + A_UINT8 SendFlags; /* send flags (HTC internal) */ + int SeqNo; /* internal seq no for debugging (HTC internal) */ + A_UINT32 Flags; /* internal use */ +} HTC_TX_PACKET_INFO; + +/** + * HTC_TX_PACKET_TAG_XXX - #defines for tagging packets for special handling + * HTC_TX_PACKET_TAG_ALL: zero is reserved and used to flush ALL packets + * HTC_TX_PACKET_TAG_INTERNAL: internal tags start here + * HTC_TX_PACKET_TAG_USER_DEFINED: user-defined tags start here + * HTC_TX_PACKET_TAG_BUNDLED: indicate this is a bundled tx packet + * HTC_TX_PACKET_TAG_AUTO_PM: indicate a power management wmi command + */ +#define HTC_TX_PACKET_TAG_ALL 0 +#define HTC_TX_PACKET_TAG_INTERNAL 1 +#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_TX_PACKET_TAG_INTERNAL + 9) +#define HTC_TX_PACKET_TAG_BUNDLED (HTC_TX_PACKET_TAG_USER_DEFINED + 1) +#define HTC_TX_PACKET_TAG_AUTO_PM (HTC_TX_PACKET_TAG_USER_DEFINED + 2) + +/* Tag packet for runtime put after sending */ +#define HTC_TX_PACKET_TAG_RUNTIME_PUT (HTC_TX_PACKET_TAG_USER_DEFINED + 3) + + +#define HTC_TX_PACKET_FLAG_FIXUP_NETBUF (1 << 0) + +typedef struct _HTC_RX_PACKET_INFO { + A_UINT32 ExpectedHdr; /* HTC internal use */ + A_UINT32 HTCRxFlags; /* HTC internal use */ + A_UINT32 IndicationFlags; /* indication flags set on each RX packet indication */ +} HTC_RX_PACKET_INFO; + +#define HTC_RX_FLAGS_INDICATE_MORE_PKTS (1 << 0) /* more packets on this endpoint are being fetched */ + +/* wrapper around endpoint-specific packets */ +typedef struct _HTC_PACKET { + DL_LIST ListLink; /* double link */ + void *pPktContext; /* caller's per packet specific context */ + + A_UINT8 *pBufferStart; /* the true buffer start , the caller can + store the real buffer start here. In + receive callbacks, the HTC layer sets pBuffer + to the start of the payload past the header. This + field allows the caller to reset pBuffer when it + recycles receive packets back to HTC */ + /* + * Pointer to the start of the buffer. In the transmit + * direction this points to the start of the payload. In the + * receive direction, however, the buffer when queued up + * points to the start of the HTC header but when returned + * to the caller points to the start of the payload + */ + A_UINT8 *pBuffer; /* payload start (RX/TX) */ + A_UINT32 BufferLength; /* length of buffer */ + A_UINT32 ActualLength; /* actual length of payload */ + HTC_ENDPOINT_ID Endpoint; /* endpoint that this packet was sent/recv'd from */ + A_STATUS Status; /* completion status */ + union { + HTC_TX_PACKET_INFO AsTx; /* Tx Packet specific info */ + HTC_RX_PACKET_INFO AsRx; /* Rx Packet specific info */ + } PktInfo; + + /* the following fields are for internal HTC use */ + A_UINT32 netbufOrigHeadRoom; + HTC_PACKET_COMPLETION Completion; /* completion */ + void *pContext; /* HTC private completion context */ + void *pNetBufContext; /* optimization for network-oriented data, the HTC packet + can pass the network buffer corresponding to the HTC packet + lower layers may optimized the transfer knowing this is + a network buffer */ +} HTC_PACKET; + +#define COMPLETE_HTC_PACKET(p,status) \ + { \ + (p)->Status = (status); \ + (p)->Completion((p)->pContext,(p)); \ + } + +#define INIT_HTC_PACKET_INFO(p,b,len) \ + { \ + (p)->pBufferStart = (b); \ + (p)->BufferLength = (len); \ + } + +/* macro to set an initial RX packet for refilling HTC */ +#define SET_HTC_PACKET_INFO_RX_REFILL(p,c,b,len,ep) \ + { \ + (p)->pPktContext = (c); \ + (p)->pBuffer = (b); \ + (p)->pBufferStart = (b); \ + (p)->BufferLength = (len); \ + (p)->Endpoint = (ep); \ + } + +/* fast macro to recycle an RX packet that will be re-queued to HTC */ +#define HTC_PACKET_RESET_RX(p) \ + { (p)->pBuffer = (p)->pBufferStart; (p)->ActualLength = 0; } + +/* macro to set packet parameters for TX */ +#define SET_HTC_PACKET_INFO_TX(p,c,b,len,ep,tag) \ + { \ + (p)->pPktContext = (c); \ + (p)->pBuffer = (b); \ + (p)->ActualLength = (len); \ + (p)->Endpoint = (ep); \ + (p)->PktInfo.AsTx.Tag = (tag); \ + (p)->PktInfo.AsTx.Flags = 0; \ + (p)->PktInfo.AsTx.SendFlags = 0; \ + } + +#define SET_HTC_PACKET_NET_BUF_CONTEXT(p,nb) \ + (p)->pNetBufContext = (nb) + +#define GET_HTC_PACKET_NET_BUF_CONTEXT(p) (p)->pNetBufContext + +/* HTC Packet Queueing Macros */ +typedef struct _HTC_PACKET_QUEUE { + DL_LIST QueueHead; + int Depth; +} HTC_PACKET_QUEUE; + +/* initialize queue */ +#define INIT_HTC_PACKET_QUEUE(pQ) \ + { \ + DL_LIST_INIT(& (pQ)->QueueHead); \ + (pQ)->Depth = 0; \ + } + +/* enqueue HTC packet to the tail of the queue */ +#define HTC_PACKET_ENQUEUE(pQ,p) \ + { dl_list_insert_tail(& (pQ)->QueueHead,& (p)->ListLink); \ + (pQ)->Depth ++; \ + } + +/* enqueue HTC packet to the tail of the queue */ +#define HTC_PACKET_ENQUEUE_TO_HEAD(pQ,p) \ + { dl_list_insert_head(& (pQ)->QueueHead,& (p)->ListLink); \ + (pQ)->Depth ++; \ + } +/* test if a queue is empty */ +#define HTC_QUEUE_EMPTY(pQ) ((pQ)->Depth == 0) +/* get packet at head without removing it */ +static INLINE HTC_PACKET *htc_get_pkt_at_head(HTC_PACKET_QUEUE *queue) +{ + if (queue->Depth == 0) { + return NULL; + } + return + A_CONTAINING_STRUCT((DL_LIST_GET_ITEM_AT_HEAD(&queue->QueueHead)), + HTC_PACKET, ListLink); +} + +/* remove a packet from a queue, where-ever it is in the queue */ +#define HTC_PACKET_REMOVE(pQ,p) \ + { \ + dl_list_remove(& (p)->ListLink); \ + (pQ)->Depth --; \ + } + +/* dequeue an HTC packet from the head of the queue */ +static INLINE HTC_PACKET *htc_packet_dequeue(HTC_PACKET_QUEUE *queue) +{ + DL_LIST *pItem = dl_list_remove_item_from_head(&queue->QueueHead); + if (pItem != NULL) { + queue->Depth--; + return A_CONTAINING_STRUCT(pItem, HTC_PACKET, ListLink); + } + return NULL; +} + +/* dequeue an HTC packet from the tail of the queue */ +static INLINE HTC_PACKET *htc_packet_dequeue_tail(HTC_PACKET_QUEUE *queue) +{ + DL_LIST *pItem = dl_list_remove_item_from_tail(&queue->QueueHead); + if (pItem != NULL) { + queue->Depth--; + return A_CONTAINING_STRUCT(pItem, HTC_PACKET, ListLink); + } + return NULL; +} + +#define HTC_PACKET_QUEUE_DEPTH(pQ) (pQ)->Depth + +#define HTC_GET_ENDPOINT_FROM_PKT(p) (p)->Endpoint +#define HTC_GET_TAG_FROM_PKT(p) (p)->PktInfo.AsTx.Tag + +/* transfer the packets from one queue to the tail of another queue */ +#define HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(pQDest,pQSrc) \ + { \ + dl_list_transfer_items_to_tail(&(pQDest)->QueueHead,&(pQSrc)->QueueHead); \ + (pQDest)->Depth += (pQSrc)->Depth; \ + (pQSrc)->Depth = 0; \ + } + +/* + * Transfer the packets from one queue to the head of another queue. + * This xfer_to_head(q1,q2) is basically equivalent to xfer_to_tail(q2,q1), + * but it updates the queue descriptor object for the initial queue to refer + * to the concatenated queue. + */ +#define HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(pQDest, pQSrc) \ + { \ + dl_list_transfer_items_to_head(&(pQDest)->QueueHead,&(pQSrc)->QueueHead); \ + (pQDest)->Depth += (pQSrc)->Depth; \ + (pQSrc)->Depth = 0; \ + } + +/* fast version to init and add a single packet to a queue */ +#define INIT_HTC_PACKET_QUEUE_AND_ADD(pQ,pP) \ + { \ + DL_LIST_INIT_AND_ADD(&(pQ)->QueueHead,&(pP)->ListLink) \ + (pQ)->Depth = 1; \ + } + +#define HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pQ, pPTemp) \ + ITERATE_OVER_LIST_ALLOW_REMOVE(&(pQ)->QueueHead,(pPTemp), HTC_PACKET, ListLink) + +#define HTC_PACKET_QUEUE_ITERATE_IS_VALID(pQ) ITERATE_IS_VALID(&(pQ)->QueueHead) +#define HTC_PACKET_QUEUE_ITERATE_RESET(pQ) ITERATE_RESET(&(pQ)->QueueHead) + +#define HTC_PACKET_QUEUE_ITERATE_END ITERATE_END + +#endif /*HTC_PACKET_H_ */ diff --git a/htc/htc_recv.c b/htc/htc_recv.c new file mode 100644 index 000000000000..79bf6c6847d0 --- /dev/null +++ b/htc/htc_recv.c @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#include "htc_debug.h" +#include "htc_internal.h" +#include "cds_api.h" +#include <cdf_nbuf.h> /* cdf_nbuf_t */ +#include "epping_main.h" + +/* HTC Control message receive timeout msec */ +#define HTC_CONTROL_RX_TIMEOUT 3000 + +#ifdef DEBUG +void debug_dump_bytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription) +{ + A_CHAR stream[60]; + A_CHAR byteOffsetStr[10]; + A_UINT32 i; + A_UINT16 offset, count, byteOffset; + + A_PRINTF("<---------Dumping %d Bytes : %s ------>\n", length, + pDescription); + + count = 0; + offset = 0; + byteOffset = 0; + for (i = 0; i < length; i++) { + A_SNPRINTF(stream + offset, (sizeof(stream) - offset), + "%02X ", buffer[i]); + count++; + offset += 3; + + if (count == 16) { + count = 0; + offset = 0; + A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X", + byteOffset); + A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); + A_MEMZERO(stream, 60); + byteOffset += 16; + } + } + + if (offset != 0) { + A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X", + byteOffset); + A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); + } + + A_PRINTF("<------------------------------------------------->\n"); +} +#else +void debug_dump_bytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription) +{ +} +#endif + +static A_STATUS htc_process_trailer(HTC_TARGET *target, + A_UINT8 *pBuffer, + int Length, HTC_ENDPOINT_ID FromEndpoint); + +static void do_recv_completion(HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pQueueToIndicate) +{ + + do { + + if (HTC_QUEUE_EMPTY(pQueueToIndicate)) { + /* nothing to indicate */ + break; + } + + if (pEndpoint->EpCallBacks.EpRecvPktMultiple != NULL) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + (" HTC calling ep %d, recv multiple callback (%d pkts) \n", + pEndpoint->Id, + HTC_PACKET_QUEUE_DEPTH + (pQueueToIndicate))); + /* a recv multiple handler is being used, pass the queue to the handler */ + pEndpoint->EpCallBacks.EpRecvPktMultiple(pEndpoint-> + EpCallBacks. + pContext, + pQueueToIndicate); + INIT_HTC_PACKET_QUEUE(pQueueToIndicate); + } else { + HTC_PACKET *pPacket; + /* using legacy EpRecv */ + while (!HTC_QUEUE_EMPTY(pQueueToIndicate)) { + pPacket = htc_packet_dequeue(pQueueToIndicate); + if (pEndpoint->EpCallBacks.EpRecv == NULL) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("HTC ep %d has NULL recv callback on packet %p\n", + pEndpoint->Id, + pPacket)); + continue; + } + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("HTC calling ep %d recv callback on packet %p\n", + pEndpoint->Id, pPacket)); + pEndpoint->EpCallBacks.EpRecv(pEndpoint-> + EpCallBacks. + pContext, + pPacket); + } + } + + } while (false); + +} + +static void recv_packet_completion(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, + HTC_PACKET *pPacket) +{ + HTC_PACKET_QUEUE container; + INIT_HTC_PACKET_QUEUE_AND_ADD(&container, pPacket); + /* do completion */ + do_recv_completion(pEndpoint, &container); +} + +void htc_control_rx_complete(void *Context, HTC_PACKET *pPacket) +{ + /* TODO, can't really receive HTC control messages yet.... */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("Invalid call to htc_control_rx_complete\n")); +} + +void htc_unblock_recv(HTC_HANDLE HTCHandle) +{ + /* TODO find the Need in new model */ +} + +void htc_enable_recv(HTC_HANDLE HTCHandle) +{ + + /* TODO find the Need in new model */ +} + +void htc_disable_recv(HTC_HANDLE HTCHandle) +{ + + /* TODO find the Need in new model */ +} + +int htc_get_num_recv_buffers(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint = &target->endpoint[Endpoint]; + return HTC_PACKET_QUEUE_DEPTH(&pEndpoint->RxBufferHoldQueue); +} + +HTC_PACKET *allocate_htc_packet_container(HTC_TARGET *target) +{ + HTC_PACKET *pPacket; + + LOCK_HTC_RX(target); + + if (NULL == target->pHTCPacketStructPool) { + UNLOCK_HTC_RX(target); + return NULL; + } + + pPacket = target->pHTCPacketStructPool; + target->pHTCPacketStructPool = (HTC_PACKET *) pPacket->ListLink.pNext; + + UNLOCK_HTC_RX(target); + + pPacket->ListLink.pNext = NULL; + return pPacket; +} + +void free_htc_packet_container(HTC_TARGET *target, HTC_PACKET *pPacket) +{ + LOCK_HTC_RX(target); + + if (NULL == target->pHTCPacketStructPool) { + target->pHTCPacketStructPool = pPacket; + pPacket->ListLink.pNext = NULL; + } else { + pPacket->ListLink.pNext = + (DL_LIST *) target->pHTCPacketStructPool; + target->pHTCPacketStructPool = pPacket; + } + + UNLOCK_HTC_RX(target); +} + +#ifdef RX_SG_SUPPORT +cdf_nbuf_t rx_sg_to_single_netbuf(HTC_TARGET *target) +{ + cdf_nbuf_t skb; + uint8_t *anbdata; + uint8_t *anbdata_new; + uint32_t anblen; + cdf_nbuf_t new_skb = NULL; + uint32_t sg_queue_len; + cdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; + + sg_queue_len = cdf_nbuf_queue_len(rx_sg_queue); + + if (sg_queue_len <= 1) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("rx_sg_to_single_netbuf: invalid sg queue len %u\n")); + goto _failed; + } + + new_skb = cdf_nbuf_alloc(target->ExpRxSgTotalLen, 0, 4, false); + if (new_skb == NULL) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("rx_sg_to_single_netbuf: can't allocate %u size netbuf\n", + target->ExpRxSgTotalLen)); + goto _failed; + } + + cdf_nbuf_peek_header(new_skb, &anbdata_new, &anblen); + + skb = cdf_nbuf_queue_remove(rx_sg_queue); + do { + cdf_nbuf_peek_header(skb, &anbdata, &anblen); + cdf_mem_copy(anbdata_new, anbdata, cdf_nbuf_len(skb)); + cdf_nbuf_put_tail(new_skb, cdf_nbuf_len(skb)); + anbdata_new += cdf_nbuf_len(skb); + cdf_nbuf_free(skb); + skb = cdf_nbuf_queue_remove(rx_sg_queue); + } while (skb != NULL); + + RESET_RX_SG_CONFIG(target); + return new_skb; + +_failed: + + while ((skb = cdf_nbuf_queue_remove(rx_sg_queue)) != NULL) { + cdf_nbuf_free(skb); + } + + RESET_RX_SG_CONFIG(target); + return NULL; +} +#endif + +CDF_STATUS htc_rx_completion_handler(void *Context, cdf_nbuf_t netbuf, + uint8_t pipeID) +{ + CDF_STATUS status = CDF_STATUS_SUCCESS; + HTC_FRAME_HDR *HtcHdr; + HTC_TARGET *target = (HTC_TARGET *) Context; + uint8_t *netdata; + uint32_t netlen; + HTC_ENDPOINT *pEndpoint; + HTC_PACKET *pPacket; + A_UINT16 payloadLen; + uint32_t trailerlen = 0; + A_UINT8 htc_ep_id; + +#ifdef RX_SG_SUPPORT + LOCK_HTC_RX(target); + if (target->IsRxSgInprogress) { + target->CurRxSgTotalLen += cdf_nbuf_len(netbuf); + cdf_nbuf_queue_add(&target->RxSgQueue, netbuf); + if (target->CurRxSgTotalLen == target->ExpRxSgTotalLen) { + netbuf = rx_sg_to_single_netbuf(target); + if (netbuf == NULL) { + UNLOCK_HTC_RX(target); + goto _out; + } + } else { + netbuf = NULL; + UNLOCK_HTC_RX(target); + goto _out; + } + } + UNLOCK_HTC_RX(target); +#endif + + netdata = cdf_nbuf_data(netbuf); + netlen = cdf_nbuf_len(netbuf); + + HtcHdr = (HTC_FRAME_HDR *) netdata; + + do { + + htc_ep_id = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, ENDPOINTID); + + if (htc_ep_id >= ENDPOINT_MAX) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("HTC Rx: invalid EndpointID=%d\n", + htc_ep_id)); + debug_dump_bytes((A_UINT8 *) HtcHdr, + sizeof(HTC_FRAME_HDR), "BAD HTC Header"); + status = CDF_STATUS_E_FAILURE; + CDF_BUG(0); + break; + } + + pEndpoint = &target->endpoint[htc_ep_id]; + + /* + * If this endpoint that received a message from the target has + * a to-target HIF pipe whose send completions are polled rather + * than interrupt-driven, this is a good point to ask HIF to check + * whether it has any completed sends to handle. + */ + if (pEndpoint->ul_is_polled) { + htc_send_complete_check(pEndpoint, 1); + } + + payloadLen = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, PAYLOADLEN); + + if (netlen < (payloadLen + HTC_HDR_LENGTH)) { +#ifdef RX_SG_SUPPORT + LOCK_HTC_RX(target); + target->IsRxSgInprogress = true; + cdf_nbuf_queue_init(&target->RxSgQueue); + cdf_nbuf_queue_add(&target->RxSgQueue, netbuf); + target->ExpRxSgTotalLen = (payloadLen + HTC_HDR_LENGTH); + target->CurRxSgTotalLen += netlen; + UNLOCK_HTC_RX(target); + netbuf = NULL; + break; +#else + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("HTC Rx: insufficient length, got:%d expected =%zu\n", + netlen, payloadLen + HTC_HDR_LENGTH)); + debug_dump_bytes((A_UINT8 *) HtcHdr, + sizeof(HTC_FRAME_HDR), + "BAD RX packet length"); + status = CDF_STATUS_E_FAILURE; + CDF_BUG(0); + break; +#endif + } +#ifdef HTC_EP_STAT_PROFILING + LOCK_HTC_RX(target); + INC_HTC_EP_STAT(pEndpoint, RxReceived, 1); + UNLOCK_HTC_RX(target); +#endif + + /* if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { */ + { + A_UINT8 temp; + A_STATUS temp_status; + /* get flags to check for trailer */ + temp = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, FLAGS); + if (temp & HTC_FLAGS_RECV_TRAILER) { + /* extract the trailer length */ + temp = + HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, + CONTROLBYTES0); + if ((temp < sizeof(HTC_RECORD_HDR)) + || (temp > payloadLen)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("htc_rx_completion_handler, invalid header (payloadlength should be :%d, CB[0] is:%d)\n", + payloadLen, temp)); + status = CDF_STATUS_E_INVAL; + break; + } + + trailerlen = temp; + /* process trailer data that follows HDR + application payload */ + temp_status = htc_process_trailer(target, + ((A_UINT8 *) HtcHdr + + HTC_HDR_LENGTH + + payloadLen - temp), + temp, htc_ep_id); + if (A_FAILED(temp_status)) { + status = CDF_STATUS_E_FAILURE; + break; + } + + } + } + + if (((int)payloadLen - (int)trailerlen) <= 0) { + /* zero length packet with trailer data, just drop these */ + break; + } + + if (htc_ep_id == ENDPOINT_0) { + A_UINT16 message_id; + HTC_UNKNOWN_MSG *htc_msg; + int wow_nack = 0; + + /* remove HTC header */ + cdf_nbuf_pull_head(netbuf, HTC_HDR_LENGTH); + netdata = cdf_nbuf_data(netbuf); + netlen = cdf_nbuf_len(netbuf); + + htc_msg = (HTC_UNKNOWN_MSG *) netdata; + message_id = + HTC_GET_FIELD(htc_msg, HTC_UNKNOWN_MSG, MESSAGEID); + + switch (message_id) { + default: + /* handle HTC control message */ + if (target->CtrlResponseProcessing) { + /* this is a fatal error, target should not be sending unsolicited messages + * on the endpoint 0 */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("HTC Rx Ctrl still processing\n")); + status = CDF_STATUS_E_FAILURE; + CDF_BUG(false); + break; + } + + LOCK_HTC_RX(target); + target->CtrlResponseLength = + min((int)netlen, + HTC_MAX_CONTROL_MESSAGE_LENGTH); + A_MEMCPY(target->CtrlResponseBuffer, netdata, + target->CtrlResponseLength); + + /* Requester will clear this flag */ + target->CtrlResponseProcessing = true; + UNLOCK_HTC_RX(target); + + cdf_event_set(&target->ctrl_response_valid); + break; + case HTC_MSG_SEND_SUSPEND_COMPLETE: + wow_nack = 0; + LOCK_HTC_CREDIT(target); + htc_credit_record(HTC_SUSPEND_ACK, + pEndpoint->TxCredits, + HTC_PACKET_QUEUE_DEPTH( + &pEndpoint->TxQueue)); + UNLOCK_HTC_CREDIT(target); + target->HTCInitInfo. + TargetSendSuspendComplete((void *) + &wow_nack); + break; + case HTC_MSG_NACK_SUSPEND: + wow_nack = 1; + LOCK_HTC_CREDIT(target); + htc_credit_record(HTC_SUSPEND_ACK, + pEndpoint->TxCredits, + HTC_PACKET_QUEUE_DEPTH( + &pEndpoint->TxQueue)); + UNLOCK_HTC_CREDIT(target); + + target->HTCInitInfo. + TargetSendSuspendComplete((void *) + &wow_nack); + break; + } + + cdf_nbuf_free(netbuf); + netbuf = NULL; + break; + } + + /* the current message based HIF architecture allocates net bufs for recv packets + * since this layer bridges that HIF to upper layers , which expects HTC packets, + * we form the packets here + * TODO_FIXME */ + pPacket = allocate_htc_packet_container(target); + if (NULL == pPacket) { + status = CDF_STATUS_E_RESOURCES; + break; + } + pPacket->Status = CDF_STATUS_SUCCESS; + pPacket->Endpoint = htc_ep_id; + pPacket->pPktContext = netbuf; + pPacket->pBuffer = cdf_nbuf_data(netbuf) + HTC_HDR_LENGTH; + pPacket->ActualLength = netlen - HTC_HEADER_LEN - trailerlen; + + cdf_nbuf_pull_head(netbuf, HTC_HEADER_LEN); + cdf_nbuf_set_pktlen(netbuf, pPacket->ActualLength); + + recv_packet_completion(target, pEndpoint, pPacket); + /* recover the packet container */ + free_htc_packet_container(target, pPacket); + netbuf = NULL; + + } while (false); + +#ifdef RX_SG_SUPPORT +_out: +#endif + + if (netbuf != NULL) { + cdf_nbuf_free(netbuf); + } + + return status; + +} + +A_STATUS htc_add_receive_pkt_multiple(HTC_HANDLE HTCHandle, + HTC_PACKET_QUEUE *pPktQueue) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + HTC_PACKET *pFirstPacket; + A_STATUS status = A_OK; + HTC_PACKET *pPacket; + + pFirstPacket = htc_get_pkt_at_head(pPktQueue); + + if (NULL == pFirstPacket) { + A_ASSERT(false); + return A_EINVAL; + } + + AR_DEBUG_ASSERT(pFirstPacket->Endpoint < ENDPOINT_MAX); + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("+- htc_add_receive_pkt_multiple : endPointId: %d, cnt:%d, length: %d\n", + pFirstPacket->Endpoint, + HTC_PACKET_QUEUE_DEPTH(pPktQueue), + pFirstPacket->BufferLength)); + + pEndpoint = &target->endpoint[pFirstPacket->Endpoint]; + + LOCK_HTC_RX(target); + + do { + + if (HTC_STOPPING(target)) { + status = A_ERROR; + break; + } + + /* store receive packets */ + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RxBufferHoldQueue, + pPktQueue); + + } while (false); + + UNLOCK_HTC_RX(target); + + if (A_FAILED(status)) { + /* walk through queue and mark each one canceled */ + HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue, pPacket) { + pPacket->Status = A_ECANCELED; + } + HTC_PACKET_QUEUE_ITERATE_END; + + do_recv_completion(pEndpoint, pPktQueue); + } + + return status; +} + +A_STATUS htc_add_receive_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) +{ + HTC_PACKET_QUEUE queue; + INIT_HTC_PACKET_QUEUE_AND_ADD(&queue, pPacket); + return htc_add_receive_pkt_multiple(HTCHandle, &queue); +} + +void htc_flush_rx_hold_queue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint) +{ + HTC_PACKET *pPacket; + HTC_PACKET_QUEUE container; + + LOCK_HTC_RX(target); + + while (1) { + pPacket = htc_packet_dequeue(&pEndpoint->RxBufferHoldQueue); + if (NULL == pPacket) { + break; + } + UNLOCK_HTC_RX(target); + pPacket->Status = A_ECANCELED; + pPacket->ActualLength = 0; + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + (" Flushing RX packet:%p, length:%d, ep:%d \n", + pPacket, pPacket->BufferLength, + pPacket->Endpoint)); + INIT_HTC_PACKET_QUEUE_AND_ADD(&container, pPacket); + /* give the packet back */ + do_recv_completion(pEndpoint, &container); + LOCK_HTC_RX(target); + } + + UNLOCK_HTC_RX(target); +} + +void htc_recv_init(HTC_TARGET *target) +{ + /* Initialize ctrl_response_valid to block */ + cdf_event_init(&target->ctrl_response_valid); +} + +/* polling routine to wait for a control packet to be received */ +A_STATUS htc_wait_recv_ctrl_message(HTC_TARGET *target) +{ +/* int count = HTC_TARGET_MAX_RESPONSE_POLL; */ + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCWaitCtrlMessageRecv\n")); + + /* Wait for BMI request/response transaction to complete */ + if (cdf_wait_single_event(&target->ctrl_response_valid, + cdf_system_msecs_to_ticks(HTC_CONTROL_RX_TIMEOUT))) { + CDF_BUG(0); + return A_ERROR; + } + + LOCK_HTC_RX(target); + /* caller will clear this flag */ + target->CtrlResponseProcessing = true; + + UNLOCK_HTC_RX(target); + +#if 0 + while (count > 0) { + + LOCK_HTC_RX(target); + + if (target->CtrlResponseValid) { + target->CtrlResponseValid = false; + /* caller will clear this flag */ + target->CtrlResponseProcessing = true; + UNLOCK_HTC_RX(target); + break; + } + + UNLOCK_HTC_RX(target); + + count--; + A_MSLEEP(HTC_TARGET_RESPONSE_POLL_MS); + } + + if (count <= 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("-HTCWaitCtrlMessageRecv: Timeout!\n")); + return A_ECOMM; + } +#endif + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCWaitCtrlMessageRecv success\n")); + return A_OK; +} + +static A_STATUS htc_process_trailer(HTC_TARGET *target, + A_UINT8 *pBuffer, + int Length, HTC_ENDPOINT_ID FromEndpoint) +{ + HTC_RECORD_HDR *pRecord; + A_UINT8 htc_rec_id; + A_UINT8 htc_rec_len; + A_UINT8 *pRecordBuf; + A_UINT8 *pOrigBuffer; + int origLength; + A_STATUS status; + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("+htc_process_trailer (length:%d) \n", Length)); + + if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { + AR_DEBUG_PRINTBUF(pBuffer, Length, "Recv Trailer"); + } + + pOrigBuffer = pBuffer; + origLength = Length; + status = A_OK; + + while (Length > 0) { + + if (Length < sizeof(HTC_RECORD_HDR)) { + status = A_EPROTO; + break; + } + /* these are byte aligned structs */ + pRecord = (HTC_RECORD_HDR *) pBuffer; + Length -= sizeof(HTC_RECORD_HDR); + pBuffer += sizeof(HTC_RECORD_HDR); + + htc_rec_len = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, LENGTH); + htc_rec_id = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, RECORDID); + + if (htc_rec_len > Length) { + /* no room left in buffer for record */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" invalid record length: %d (id:%d) buffer has: %d bytes left \n", + htc_rec_len, htc_rec_id, Length)); + status = A_EPROTO; + break; + } + /* start of record follows the header */ + pRecordBuf = pBuffer; + + switch (htc_rec_id) { + case HTC_RECORD_CREDITS: + AR_DEBUG_ASSERT(htc_rec_len >= + sizeof(HTC_CREDIT_REPORT)); + htc_process_credit_rpt(target, + (HTC_CREDIT_REPORT *) pRecordBuf, + htc_rec_len / + (sizeof(HTC_CREDIT_REPORT)), + FromEndpoint); + break; + +#ifdef HIF_SDIO + case HTC_RECORD_LOOKAHEAD: + /* Process in HIF layer */ + break; + + case HTC_RECORD_LOOKAHEAD_BUNDLE: + /* Process in HIF layer */ + break; +#endif /* HIF_SDIO */ + + default: + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" HTC unhandled record: id:%d length:%d \n", + htc_rec_id, htc_rec_len)); + break; + } + + if (A_FAILED(status)) { + break; + } + + /* advance buffer past this record for next time around */ + pBuffer += htc_rec_len; + Length -= htc_rec_len; + } + + if (A_FAILED(status)) { + debug_dump_bytes(pOrigBuffer, origLength, "BAD Recv Trailer"); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-htc_process_trailer \n")); + return status; + +} diff --git a/htc/htc_send.c b/htc/htc_send.c new file mode 100644 index 000000000000..26821fd542a4 --- /dev/null +++ b/htc/htc_send.c @@ -0,0 +1,2002 @@ +/* + * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#include "htc_debug.h" +#include "htc_internal.h" +#include <cdf_nbuf.h> /* cdf_nbuf_t */ +#include <cdf_memory.h> /* cdf_mem_malloc */ +#include "epping_main.h" + +/* #define USB_HIF_SINGLE_PIPE_DATA_SCHED */ +/* #ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED */ +#define DATA_EP_SIZE 4 +/* #endif */ +#define HTC_DATA_RESOURCE_THRS 256 +#define HTC_DATA_MINDESC_PERPACKET 2 + +typedef enum _HTC_SEND_QUEUE_RESULT { + HTC_SEND_QUEUE_OK = 0, /* packet was queued */ + HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ +} HTC_SEND_QUEUE_RESULT; + +#ifndef DEBUG_CREDIT +#define DEBUG_CREDIT 0 +#endif + +#if DEBUG_CREDIT +/* bit mask to enable debug certain endpoint */ +static unsigned ep_debug_mask = + (1 << ENDPOINT_0) | (1 << ENDPOINT_1) | (1 << ENDPOINT_2); +#endif + +/* HTC Control Path Credit History */ +A_UINT32 g_htc_credit_history_idx = 0; +HTC_CREDIT_HISTORY htc_credit_history_buffer[HTC_CREDIT_HISTORY_MAX]; + +/** + * htc_credit_record() - records tx que state & credit transactions + * @type: type of echange can be HTC_REQUEST_CREDIT + * or HTC_PROCESS_CREDIT_REPORT + * @tx_credits: current number of tx_credits + * @htc_tx_queue_depth: current hct tx queue depth + * + * This function records the credits and pending commands whenever a command is + * sent or credits are returned. Call this after the credits have been updated + * according to the transaction. Call this before dequeing commands. + * + * Consider making this function accept an HTC_ENDPOINT and find the current + * credits and queue depth itself. + * + * Consider moving the LOCK_HTC_CREDIT(target); logic into this function as well. + */ +void htc_credit_record(htc_credit_exchange_type type, uint32_t tx_credit, + uint32_t htc_tx_queue_depth) { + if (HTC_CREDIT_HISTORY_MAX <= g_htc_credit_history_idx) + g_htc_credit_history_idx = 0; + + htc_credit_history_buffer[g_htc_credit_history_idx].type = type; + htc_credit_history_buffer[g_htc_credit_history_idx].time = + cdf_get_log_timestamp(); + htc_credit_history_buffer[g_htc_credit_history_idx].tx_credit = + tx_credit; + htc_credit_history_buffer[g_htc_credit_history_idx].htc_tx_queue_depth = + htc_tx_queue_depth; + g_htc_credit_history_idx++; + +#ifdef QCA_WIFI_3_0_EMU + if (type == HTC_REQUEST_CREDIT) + printk("\nrequest_credits-> current_credit %d, pending commands %d\n", + tx_credit, htc_tx_queue_depth); + + else if (type == HTC_PROCESS_CREDIT_REPORT) + printk("\ncredit_report<- current_credit %d, pending commands %d\n", + tx_credit, htc_tx_queue_depth); +#endif +} + +void htc_dump_counter_info(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("\n%s: ce_send_cnt = %d, TX_comp_cnt = %d\n", + __func__, target->ce_send_cnt, target->TX_comp_cnt)); +} + +void htc_get_control_endpoint_tx_host_credits(HTC_HANDLE HTCHandle, int *credits) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + int i; + + if (!credits || !target) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: invalid args", __func__)); + return; + } + + *credits = 0; + LOCK_HTC_TX(target); + for (i = 0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + if (pEndpoint->service_id == WMI_CONTROL_SVC) { + *credits = pEndpoint->TxCredits; + break; + } + } + UNLOCK_HTC_TX(target); +} + +static INLINE void restore_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket) +{ + if (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) { + cdf_nbuf_t netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + cdf_nbuf_unmap(target->osdev, netbuf, CDF_DMA_TO_DEVICE); + cdf_nbuf_pull_head(netbuf, sizeof(HTC_FRAME_HDR)); + pPacket->PktInfo.AsTx.Flags &= ~HTC_TX_PACKET_FLAG_FIXUP_NETBUF; + } + +} + +static void do_send_completion(HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pQueueToIndicate) +{ + do { + + if (HTC_QUEUE_EMPTY(pQueueToIndicate)) { + /* nothing to indicate */ + break; + } + + if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" HTC calling ep %d, send complete multiple callback (%d pkts) \n", + pEndpoint->Id, + HTC_PACKET_QUEUE_DEPTH + (pQueueToIndicate))); + /* a multiple send complete handler is being used, pass the queue to the handler */ + pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint-> + EpCallBacks. + pContext, + pQueueToIndicate); + /* all packets are now owned by the callback, reset queue to be safe */ + INIT_HTC_PACKET_QUEUE(pQueueToIndicate); + } else { + HTC_PACKET *pPacket; + /* using legacy EpTxComplete */ + do { + pPacket = htc_packet_dequeue(pQueueToIndicate); + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" HTC calling ep %d send complete callback on packet %p \n", + pEndpoint->Id, pPacket)); + pEndpoint->EpCallBacks.EpTxComplete(pEndpoint-> + EpCallBacks. + pContext, + pPacket); + } while (!HTC_QUEUE_EMPTY(pQueueToIndicate)); + } + + } while (false); + +} + +static void send_packet_completion(HTC_TARGET *target, HTC_PACKET *pPacket) +{ + HTC_ENDPOINT *pEndpoint = &target->endpoint[pPacket->Endpoint]; + HTC_PACKET_QUEUE container; + + restore_tx_packet(target, pPacket); + INIT_HTC_PACKET_QUEUE_AND_ADD(&container, pPacket); + + /* do completion */ + do_send_completion(pEndpoint, &container); +} + +void htc_send_complete_check_cleanup(void *context) +{ + HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *) context; + htc_send_complete_check(pEndpoint, 1); +} + +HTC_PACKET *allocate_htc_bundle_packet(HTC_TARGET *target) +{ + HTC_PACKET *pPacket; + HTC_PACKET_QUEUE *pQueueSave; + cdf_nbuf_t netbuf; + LOCK_HTC_TX(target); + if (NULL == target->pBundleFreeList) { + UNLOCK_HTC_TX(target); + netbuf = cdf_nbuf_alloc(NULL, + target->MaxMsgsPerHTCBundle * + target->TargetCreditSize, 0, 4, false); + AR_DEBUG_ASSERT(netbuf); + if (!netbuf) { + return NULL; + } + pPacket = cdf_mem_malloc(sizeof(HTC_PACKET)); + AR_DEBUG_ASSERT(pPacket); + if (!pPacket) { + cdf_nbuf_free(netbuf); + return NULL; + } + pQueueSave = cdf_mem_malloc(sizeof(HTC_PACKET_QUEUE)); + AR_DEBUG_ASSERT(pQueueSave); + if (!pQueueSave) { + cdf_nbuf_free(netbuf); + cdf_mem_free(pPacket); + return NULL; + } + INIT_HTC_PACKET_QUEUE(pQueueSave); + pPacket->pContext = pQueueSave; + SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf); + pPacket->pBuffer = cdf_nbuf_data(netbuf); + pPacket->BufferLength = cdf_nbuf_len(netbuf); + + /* store the original head room so that we can restore this when we "free" the packet */ + /* free packet puts the packet back on the free list */ + pPacket->netbufOrigHeadRoom = cdf_nbuf_headroom(netbuf); + return pPacket; + } + /* already done malloc - restore from free list */ + pPacket = target->pBundleFreeList; + AR_DEBUG_ASSERT(pPacket); + if (!pPacket) { + UNLOCK_HTC_TX(target); + return NULL; + } + target->pBundleFreeList = (HTC_PACKET *) pPacket->ListLink.pNext; + UNLOCK_HTC_TX(target); + pPacket->ListLink.pNext = NULL; + + return pPacket; +} + +void free_htc_bundle_packet(HTC_TARGET *target, HTC_PACKET *pPacket) +{ + A_UINT32 curentHeadRoom; + cdf_nbuf_t netbuf; + HTC_PACKET_QUEUE *pQueueSave; + + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + AR_DEBUG_ASSERT(netbuf); + if (!netbuf) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("\n%s: Invalid netbuf in HTC " + "Packet\n", __func__)); + return; + } + /* HIF adds data to the headroom section of the nbuf, restore the original */ + /* size. If this is not done, headroom keeps shrinking with every HIF send */ + /* and eventually HIF ends up doing another malloc big enough to store the */ + /* data + its header */ + + curentHeadRoom = cdf_nbuf_headroom(netbuf); + cdf_nbuf_pull_head(netbuf, + pPacket->netbufOrigHeadRoom - curentHeadRoom); + cdf_nbuf_trim_tail(netbuf, cdf_nbuf_len(netbuf)); + + /* restore the pBuffer pointer. HIF changes this */ + pPacket->pBuffer = cdf_nbuf_data(netbuf); + pPacket->BufferLength = cdf_nbuf_len(netbuf); + + /* restore queue */ + pQueueSave = (HTC_PACKET_QUEUE *) pPacket->pContext; + AR_DEBUG_ASSERT(pQueueSave); + + INIT_HTC_PACKET_QUEUE(pQueueSave); + + LOCK_HTC_TX(target); + if (target->pBundleFreeList == NULL) { + target->pBundleFreeList = pPacket; + pPacket->ListLink.pNext = NULL; + } else { + pPacket->ListLink.pNext = (DL_LIST *) target->pBundleFreeList; + target->pBundleFreeList = pPacket; + } + UNLOCK_HTC_TX(target); +} + +#if defined(HIF_USB) || defined(HIF_SDIO) +#ifdef ENABLE_BUNDLE_TX +static A_STATUS htc_send_bundled_netbuf(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + unsigned char *pBundleBuffer, + HTC_PACKET *pPacketTx) +{ + cdf_size_t data_len; + A_STATUS status; + cdf_nbuf_t bundleBuf; + uint32_t data_attr = 0; + + bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx); + data_len = pBundleBuffer - cdf_nbuf_data(bundleBuf); + cdf_nbuf_put_tail(bundleBuf, data_len); + SET_HTC_PACKET_INFO_TX(pPacketTx, + target, + pBundleBuffer, + data_len, + pEndpoint->Id, HTC_TX_PACKET_TAG_BUNDLED); + LOCK_HTC_TX(target); + HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacketTx); + UNLOCK_HTC_TX(target); +#if DEBUG_BUNDLE + cdf_print(" Send bundle EP%d buffer size:0x%x, total:0x%x, count:%d.\n", + pEndpoint->Id, + pEndpoint->TxCreditSize, + data_len, data_len / pEndpoint->TxCreditSize); +#endif + status = hif_send_head(target->hif_dev, + pEndpoint->UL_PipeID, + pEndpoint->Id, data_len, bundleBuf, data_attr); + if (status != A_OK) { + cdf_print("%s:hif_send_head failed(len=%d).\n", __FUNCTION__, + data_len); + } + return status; +} + +static void htc_issue_packets_bundle(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pPktQueue) +{ + int i, frag_count, nbytes; + cdf_nbuf_t netbuf, bundleBuf; + unsigned char *pBundleBuffer = NULL; + HTC_PACKET *pPacket = NULL, *pPacketTx = NULL; + HTC_FRAME_HDR *pHtcHdr; + int creditPad, creditRemainder, transferLength, bundlesSpaceRemaining = + 0; + HTC_PACKET_QUEUE *pQueueSave = NULL; + + bundlesSpaceRemaining = + target->MaxMsgsPerHTCBundle * pEndpoint->TxCreditSize; + pPacketTx = allocate_htc_bundle_packet(target); + if (!pPacketTx) { + /* good time to panic */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("allocate_htc_bundle_packet failed \n")); + AR_DEBUG_ASSERT(false); + return; + } + bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx); + pBundleBuffer = cdf_nbuf_data(bundleBuf); + pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext; + while (1) { + pPacket = htc_packet_dequeue(pPktQueue); + if (pPacket == NULL) { + break; + } + creditPad = 0; + transferLength = pPacket->ActualLength + HTC_HDR_LENGTH; + creditRemainder = transferLength % pEndpoint->TxCreditSize; + if (creditRemainder != 0) { + if (transferLength < pEndpoint->TxCreditSize) { + creditPad = + pEndpoint->TxCreditSize - transferLength; + } else { + creditPad = creditRemainder; + } + transferLength += creditPad; + } + + if (bundlesSpaceRemaining < transferLength) { + /* send out previous buffer */ + htc_send_bundled_netbuf(target, pEndpoint, pBundleBuffer, + pPacketTx); + if (HTC_PACKET_QUEUE_DEPTH(pPktQueue) < + HTC_MIN_MSG_PER_BUNDLE) { + return; + } + bundlesSpaceRemaining = + target->MaxMsgsPerHTCBundle * + pEndpoint->TxCreditSize; + pPacketTx = allocate_htc_bundle_packet(target); + if (!pPacketTx) { + /* good time to panic */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("allocate_htc_bundle_packet failed \n")); + AR_DEBUG_ASSERT(false); + return; + } + bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx); + pBundleBuffer = cdf_nbuf_data(bundleBuf); + pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext; + } + + bundlesSpaceRemaining -= transferLength; + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + pHtcHdr = (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, 0); + HTC_WRITE32(pHtcHdr, + SM(pPacket->ActualLength, + HTC_FRAME_HDR_PAYLOADLEN) | SM(pPacket->PktInfo. + AsTx. + SendFlags | + HTC_FLAGS_SEND_BUNDLE, + HTC_FRAME_HDR_FLAGS) + | SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID)); + HTC_WRITE32((A_UINT32 *) pHtcHdr + 1, + SM(pPacket->PktInfo.AsTx.SeqNo, + HTC_FRAME_HDR_CONTROLBYTES1) | SM(creditPad, + HTC_FRAME_HDR_RESERVED)); + pHtcHdr->reserved = creditPad; + frag_count = cdf_nbuf_get_num_frags(netbuf); + nbytes = pPacket->ActualLength + HTC_HDR_LENGTH; + for (i = 0; i < frag_count && nbytes > 0; i++) { + int frag_len = cdf_nbuf_get_frag_len(netbuf, i); + unsigned char *frag_addr = + cdf_nbuf_get_frag_vaddr(netbuf, i); + if (frag_len > nbytes) { + frag_len = nbytes; + } + A_MEMCPY(pBundleBuffer, frag_addr, frag_len); + nbytes -= frag_len; + pBundleBuffer += frag_len; + } + HTC_PACKET_ENQUEUE(pQueueSave, pPacket); + pBundleBuffer += creditPad; + } + if (pBundleBuffer != cdf_nbuf_data(bundleBuf)) { + /* send out remaining buffer */ + htc_send_bundled_netbuf(target, pEndpoint, pBundleBuffer, + pPacketTx); + } else { + free_htc_bundle_packet(target, pPacketTx); + } +} +#endif /* ENABLE_BUNDLE_TX */ +#endif + +static A_STATUS htc_issue_packets(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pPktQueue) +{ + A_STATUS status = A_OK; + cdf_nbuf_t netbuf; + HTC_PACKET *pPacket = NULL; + uint16_t payloadLen; + HTC_FRAME_HDR *pHtcHdr; + uint32_t data_attr = 0; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("+htc_issue_packets: Queue: %p, Pkts %d \n", pPktQueue, + HTC_PACKET_QUEUE_DEPTH(pPktQueue))); + while (true) { +#if defined(HIF_USB) || defined(HIF_SDIO) +#ifdef ENABLE_BUNDLE_TX + if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint) && + HTC_ENABLE_BUNDLE(target) && + HTC_PACKET_QUEUE_DEPTH(pPktQueue) >= + HTC_MIN_MSG_PER_BUNDLE) { + htc_issue_packets_bundle(target, pEndpoint, pPktQueue); + } +#endif +#endif + /* if not bundling or there was a packet that could not be placed in a bundle, + * and send it by normal way + */ + pPacket = htc_packet_dequeue(pPktQueue); + if (NULL == pPacket) { + /* local queue is fully drained */ + break; + } + + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + AR_DEBUG_ASSERT(netbuf); + /* Non-credit enabled endpoints have been mapped and setup by now, + * so no need to revisit the HTC headers + */ + if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { + + payloadLen = pPacket->ActualLength; + /* setup HTC frame header */ + + pHtcHdr = + (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, + 0); + AR_DEBUG_ASSERT(pHtcHdr); + + HTC_WRITE32(pHtcHdr, + SM(payloadLen, + HTC_FRAME_HDR_PAYLOADLEN) | SM(pPacket-> + PktInfo. + AsTx. + SendFlags, + HTC_FRAME_HDR_FLAGS) + | SM(pPacket->Endpoint, + HTC_FRAME_HDR_ENDPOINTID)); + HTC_WRITE32(((A_UINT32 *) pHtcHdr) + 1, + SM(pPacket->PktInfo.AsTx.SeqNo, + HTC_FRAME_HDR_CONTROLBYTES1)); + + /* + * Now that the HTC frame header has been added, the netbuf can be + * mapped. This only applies to non-data frames, since data frames + * were already mapped as they entered into the driver. + * Check the "FIXUP_NETBUF" flag to see whether this is a data netbuf + * that is already mapped, or a non-data netbuf that needs to be + * mapped. + */ + if (pPacket->PktInfo.AsTx. + Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) { + cdf_nbuf_map(target->osdev, + GET_HTC_PACKET_NET_BUF_CONTEXT + (pPacket), CDF_DMA_TO_DEVICE); + } + } + LOCK_HTC_TX(target); + /* store in look up queue to match completions */ + HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacket); + INC_HTC_EP_STAT(pEndpoint, TxIssued, 1); + pEndpoint->ul_outstanding_cnt++; + UNLOCK_HTC_TX(target); + + hif_send_complete_check(target->hif_dev, pEndpoint->UL_PipeID, false); + status = hif_send_head(target->hif_dev, + pEndpoint->UL_PipeID, pEndpoint->Id, + HTC_HDR_LENGTH + pPacket->ActualLength, + netbuf, data_attr); +#if DEBUG_BUNDLE + cdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n", + pEndpoint->Id, + pEndpoint->TxCreditSize, + HTC_HDR_LENGTH + pPacket->ActualLength); +#endif + + target->ce_send_cnt++; + + if (cdf_unlikely(A_FAILED(status))) { + if (status != A_NO_RESOURCE) { + /* TODO : if more than 1 endpoint maps to the same PipeID it is possible + * to run out of resources in the HIF layer. Don't emit the error */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("hif_send Failed status:%d \n", + status)); + } + LOCK_HTC_TX(target); + target->ce_send_cnt--; + pEndpoint->ul_outstanding_cnt--; + HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacket); + /* reclaim credits */ +#if defined(HIF_USB) + if (pEndpoint->Id >= ENDPOINT_2 + && pEndpoint->Id <= ENDPOINT_5) + target->avail_tx_credits += + pPacket->PktInfo.AsTx.CreditsUsed; + else + pEndpoint->TxCredits += + pPacket->PktInfo.AsTx.CreditsUsed; +#else + pEndpoint->TxCredits += + pPacket->PktInfo.AsTx.CreditsUsed; +#endif + /* put it back into the callers queue */ + HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket); + UNLOCK_HTC_TX(target); + break; + } + + /* + * For HTT messages without a response from fw, + * do the runtime put here. + * otherwise runtime put will be done when the fw response comes + */ + if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_RUNTIME_PUT) + hif_pm_runtime_put(target->hif_dev); + } + if (cdf_unlikely(A_FAILED(status))) { +#if defined(HIF_USB) + if (pEndpoint->Id >= ENDPOINT_2 && pEndpoint->Id <= ENDPOINT_5) + target->avail_tx_credits += + pPacket->PktInfo.AsTx.CreditsUsed; + else + pEndpoint->TxCredits += + pPacket->PktInfo.AsTx.CreditsUsed; +#endif + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("htc_issue_packets, failed pkt:0x%p status:%d", + pPacket, status)); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_issue_packets \n")); + + return status; +} + +#ifdef FEATURE_RUNTIME_PM +/** + * extract_htc_pm_packtes(): move pm packets from endpoint into queue + * @endpoint: which enpoint to extract packets from + * @queue: a queue to store extracted packets in. + * + * remove pm packets from the endpoint's tx queue. + * queue them into a queue + */ +static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{ + HTC_PACKET *packet; + + /* only WMI endpoint has power management packets */ + if (endpoint->service_id != WMI_CONTROL_SVC) + return; + + ITERATE_OVER_LIST_ALLOW_REMOVE(&endpoint->TxQueue.QueueHead, packet, + HTC_PACKET, ListLink) { + if (packet->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_AUTO_PM) { + HTC_PACKET_REMOVE(&endpoint->TxQueue, packet); + HTC_PACKET_ENQUEUE(queue, packet); + } + } ITERATE_END +} + +/** + * queue_htc_pm_packets(): queue pm packets with priority + * @endpoint: enpoint to queue packets to + * @queue: queue of pm packets to enque + * + * suspend resume packets get special treatment & priority. + * need to queue them at the front of the queue. + */ +static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{ + if (endpoint->service_id != WMI_CONTROL_SVC) + return; + + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&endpoint->TxQueue, queue); +} +#else +static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{} + +static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint, + HTC_PACKET_QUEUE *queue) +{} +#endif + + +/* get HTC send packets from the TX queue on an endpoint, based on available credits */ +void get_htc_send_packets_credit_based(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pQueue) +{ + int creditsRequired; + int remainder; + A_UINT8 sendFlags; + HTC_PACKET *pPacket; + unsigned int transferLength; + HTC_PACKET_QUEUE *tx_queue; + HTC_PACKET_QUEUE pm_queue; + bool do_pm_get = false; + + /****** NOTE : the TX lock is held when this function is called *****************/ + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+get_htc_send_packets_credit_based\n")); + + INIT_HTC_PACKET_QUEUE(&pm_queue); + extract_htc_pm_packets(pEndpoint, &pm_queue); + if (HTC_QUEUE_EMPTY(&pm_queue)) { + tx_queue = &pEndpoint->TxQueue; + do_pm_get = true; + } else { + tx_queue = &pm_queue; + } + + /* loop until we can grab as many packets out of the queue as we can */ + while (true) { + if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) { + /* bus suspended, runtime resume issued */ + CDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0); + break; + } + + sendFlags = 0; + /* get packet at head, but don't remove it */ + pPacket = htc_get_pkt_at_head(tx_queue); + if (pPacket == NULL) { + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); + break; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Got head packet:%p , Queue Depth: %d\n", + pPacket, + HTC_PACKET_QUEUE_DEPTH(tx_queue))); + + transferLength = pPacket->ActualLength + HTC_HDR_LENGTH; + + if (transferLength <= pEndpoint->TxCreditSize) { + creditsRequired = 1; + } else { + /* figure out how many credits this message requires */ + creditsRequired = + transferLength / pEndpoint->TxCreditSize; + remainder = transferLength % pEndpoint->TxCreditSize; + + if (remainder) { + creditsRequired++; + } + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Credits Required:%d Got:%d\n", + creditsRequired, pEndpoint->TxCredits)); + + if (pEndpoint->Id == ENDPOINT_0) { + /* endpoint 0 is special, it always has a credit and does not require credit based + * flow control */ + creditsRequired = 0; +#if defined(HIF_USB) + } else if (pEndpoint->Id >= ENDPOINT_2 + && pEndpoint->Id <= ENDPOINT_5) { + if (target->avail_tx_credits < creditsRequired) + break; + + target->avail_tx_credits -= creditsRequired; + + if (target->avail_tx_credits < 9) { + /* tell the target we need credits ASAP! */ + sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + INC_HTC_EP_STAT(pEndpoint, + TxCreditLowIndications, 1); + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Host Needs Credits \n")); + } +#endif + } else { + + if (pEndpoint->TxCredits < creditsRequired) { +#if DEBUG_CREDIT + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" EP%d, No Credit now. %d < %d\n", + pEndpoint->Id, + pEndpoint->TxCredits, + creditsRequired)); +#endif + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); + break; + } + + pEndpoint->TxCredits -= creditsRequired; + INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, + creditsRequired); + + /* check if we need credits back from the target */ + if (pEndpoint->TxCredits <= + pEndpoint->TxCreditsPerMaxMsg) { + /* tell the target we need credits ASAP! */ + sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + + if (pEndpoint->service_id == WMI_CONTROL_SVC) { + LOCK_HTC_CREDIT(target); + htc_credit_record(HTC_REQUEST_CREDIT, + pEndpoint->TxCredits, + HTC_PACKET_QUEUE_DEPTH + (tx_queue)); + UNLOCK_HTC_CREDIT(target); + } + + INC_HTC_EP_STAT(pEndpoint, + TxCreditLowIndications, 1); +#if DEBUG_CREDIT + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" EP%d Needs Credits\n", + pEndpoint->Id)); +#endif + } + } + + /* now we can fully dequeue */ + pPacket = htc_packet_dequeue(tx_queue); + if (pPacket) { + /* save the number of credits this packet consumed */ + pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired; + /* save send flags */ + pPacket->PktInfo.AsTx.SendFlags = sendFlags; + + /* queue this packet into the caller's queue */ + HTC_PACKET_ENQUEUE(pQueue, pPacket); + } + } + + if (!HTC_QUEUE_EMPTY(&pm_queue)) + queue_htc_pm_packets(pEndpoint, &pm_queue); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("-get_htc_send_packets_credit_based\n")); + +} + +void get_htc_send_packets(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pQueue, int Resources) +{ + + HTC_PACKET *pPacket; + HTC_PACKET_QUEUE *tx_queue; + HTC_PACKET_QUEUE pm_queue; + bool do_pm_get; + + /****** NOTE : the TX lock is held when this function is called *****************/ + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("+get_htc_send_packets %d resources\n", Resources)); + + INIT_HTC_PACKET_QUEUE(&pm_queue); + extract_htc_pm_packets(pEndpoint, &pm_queue); + if (HTC_QUEUE_EMPTY(&pm_queue)) { + tx_queue = &pEndpoint->TxQueue; + do_pm_get = true; + } else { + tx_queue = &pm_queue; + } + + /* loop until we can grab as many packets out of the queue as we can */ + while (Resources > 0) { + int num_frags; + + if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) { + /* bus suspended, runtime resume issued */ + CDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0); + break; + } + + pPacket = htc_packet_dequeue(tx_queue); + if (pPacket == NULL) { + if (do_pm_get) + hif_pm_runtime_put(target->hif_dev); + break; + } + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Got packet:%p , New Queue Depth: %d\n", + pPacket, + HTC_PACKET_QUEUE_DEPTH(tx_queue))); + /* For non-credit path the sequence number is already embedded + * in the constructed HTC header + */ +#if 0 + pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo; + pEndpoint->SeqNo++; +#endif + pPacket->PktInfo.AsTx.SendFlags = 0; + pPacket->PktInfo.AsTx.CreditsUsed = 0; + /* queue this packet into the caller's queue */ + HTC_PACKET_ENQUEUE(pQueue, pPacket); + + /* + * FIX THIS: + * For now, avoid calling cdf_nbuf_get_num_frags before calling + * cdf_nbuf_map, because the MacOS version of cdf_nbuf_t doesn't + * support cdf_nbuf_get_num_frags until after cdf_nbuf_map has + * been done. + * Assume that the non-data netbufs, i.e. the WMI message netbufs, + * consist of a single fragment. + */ + num_frags = + (pPacket->PktInfo.AsTx. + Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) ? 1 + /* WMI messages are in a single-fragment network buffer */ : + cdf_nbuf_get_num_frags(GET_HTC_PACKET_NET_BUF_CONTEXT + (pPacket)); + Resources -= num_frags; + } + + if (!HTC_QUEUE_EMPTY(&pm_queue)) + queue_htc_pm_packets(pEndpoint, &pm_queue); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-get_htc_send_packets\n")); + +} + +static HTC_SEND_QUEUE_RESULT htc_try_send(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + HTC_PACKET_QUEUE *pCallersSendQueue) +{ + HTC_PACKET_QUEUE sendQueue; /* temp queue to hold packets at various stages */ + HTC_PACKET *pPacket; + int tx_resources; + int overflow; + HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+htc_try_send (Queue:%p Depth:%d)\n", + pCallersSendQueue, + (pCallersSendQueue == + NULL) ? 0 : + HTC_PACKET_QUEUE_DEPTH + (pCallersSendQueue))); + + /* init the local send queue */ + INIT_HTC_PACKET_QUEUE(&sendQueue); + + do { + + if (NULL == pCallersSendQueue) { + /* caller didn't provide a queue, just wants us to check queues and send */ + break; + } + + if (HTC_QUEUE_EMPTY(pCallersSendQueue)) { + /* empty queue */ + OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target, + HTC_PKT_Q_EMPTY); + result = HTC_SEND_QUEUE_DROP; + break; + } + + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >= + pEndpoint->MaxTxQueueDepth) { + /* we've already overflowed */ + overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue); + } else { + /* figure out how much we will overflow by */ + overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); + overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue); + /* figure out how much we will overflow the TX queue by */ + overflow -= pEndpoint->MaxTxQueueDepth; + } + + /* if overflow is negative or zero, we are okay */ + if (overflow > 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d \n", + pEndpoint->Id, overflow, + HTC_PACKET_QUEUE_DEPTH(&pEndpoint-> + TxQueue), + pEndpoint->MaxTxQueueDepth)); + } + if ((overflow <= 0) + || (pEndpoint->EpCallBacks.EpSendFull == NULL)) { + /* all packets will fit or caller did not provide send full indication handler + * -- just move all of them to the local sendQueue object */ + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue, + pCallersSendQueue); + } else { + int i; + int goodPkts = + HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) - + overflow; + + A_ASSERT(goodPkts >= 0); + /* we have overflowed, and a callback is provided */ + /* dequeue all non-overflow packets into the sendqueue */ + for (i = 0; i < goodPkts; i++) { + /* pop off caller's queue */ + pPacket = htc_packet_dequeue(pCallersSendQueue); + A_ASSERT(pPacket != NULL); + /* insert into local queue */ + HTC_PACKET_ENQUEUE(&sendQueue, pPacket); + } + + /* the caller's queue has all the packets that won't fit */ + /* walk through the caller's queue and indicate each one to the send full handler */ + ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue-> + QueueHead, pPacket, + HTC_PACKET, ListLink) { + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Indicating overflowed TX packet: %p \n", + pPacket)); + /* + * Remove headroom reserved for HTC_FRAME_HDR before giving + * the packet back to the user via the EpSendFull callback. + */ + restore_tx_packet(target, pPacket); + + if (pEndpoint->EpCallBacks. + EpSendFull(pEndpoint->EpCallBacks.pContext, + pPacket) == HTC_SEND_FULL_DROP) { + /* callback wants the packet dropped */ + INC_HTC_EP_STAT(pEndpoint, TxDropped, + 1); + /* leave this one in the caller's queue for cleanup */ + } else { + /* callback wants to keep this packet, remove from caller's queue */ + HTC_PACKET_REMOVE(pCallersSendQueue, + pPacket); + /* put it in the send queue */ + /* add HTC_FRAME_HDR space reservation again */ + cdf_nbuf_push_head + (GET_HTC_PACKET_NET_BUF_CONTEXT + (pPacket), sizeof(HTC_FRAME_HDR)); + + HTC_PACKET_ENQUEUE(&sendQueue, pPacket); + } + + } + ITERATE_END; + + if (HTC_QUEUE_EMPTY(&sendQueue)) { + /* no packets made it in, caller will cleanup */ + OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target, + HTC_SEND_Q_EMPTY); + result = HTC_SEND_QUEUE_DROP; + break; + } + } + + } while (false); + + if (result != HTC_SEND_QUEUE_OK) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send: \n")); + return result; + } + + if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { + tx_resources = + hif_get_free_queue_number(target->hif_dev, + pEndpoint->UL_PipeID); + } else { + tx_resources = 0; + } + + LOCK_HTC_TX(target); + + if (!HTC_QUEUE_EMPTY(&sendQueue)) { + /* transfer packets to tail */ + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue, + &sendQueue); + A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue)); + INIT_HTC_PACKET_QUEUE(&sendQueue); + } + + /* increment tx processing count on entry */ + cdf_atomic_inc(&pEndpoint->TxProcessCount); + if (cdf_atomic_read(&pEndpoint->TxProcessCount) > 1) { + /* another thread or task is draining the TX queues on this endpoint + * that thread will reset the tx processing count when the queue is drained */ + cdf_atomic_dec(&pEndpoint->TxProcessCount); + UNLOCK_HTC_TX(target); + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send (busy) \n")); + return HTC_SEND_QUEUE_OK; + } + + /***** beyond this point only 1 thread may enter ******/ + + /* now drain the endpoint TX queue for transmission as long as we have enough + * transmit resources */ + while (true) { + + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) { + break; + } + + if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { +#if DEBUG_CREDIT + int cred = pEndpoint->TxCredits; +#endif + /* credit based mechanism provides flow control based on target transmit resource availability, we + * assume that the HIF layer will always have bus resources greater than target transmit resources */ + get_htc_send_packets_credit_based(target, pEndpoint, + &sendQueue); +#if DEBUG_CREDIT + if (ep_debug_mask & (1 << pEndpoint->Id)) { + if (cred - pEndpoint->TxCredits > 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" <HTC> Decrease EP%d %d - %d = %d credits.\n", + pEndpoint->Id, cred, + cred - + pEndpoint->TxCredits, + pEndpoint->TxCredits)); + } + } +#endif + } else { + /* get all the packets for this endpoint that we can for this pass */ + get_htc_send_packets(target, pEndpoint, &sendQueue, + tx_resources); + } + + if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) { + /* didn't get any packets due to a lack of resources or TX queue was drained */ + break; + } + + UNLOCK_HTC_TX(target); + + /* send what we can */ + result = htc_issue_packets(target, pEndpoint, &sendQueue); + if (result) { + int i; + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("htc_issue_packets, failed status:%d put it back to head of callersSendQueue", + result)); + + for (i = HTC_PACKET_QUEUE_DEPTH(&sendQueue); i > 0; i--) + hif_pm_runtime_put(target->hif_dev); + + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, + &sendQueue); + LOCK_HTC_TX(target); + break; + } + + if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { + tx_resources = + hif_get_free_queue_number(target->hif_dev, + pEndpoint->UL_PipeID); + } + + LOCK_HTC_TX(target); + + } + + UNLOCK_HTC_TX(target); + /* done with this endpoint, we can clear the count */ + cdf_atomic_init(&pEndpoint->TxProcessCount); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send: \n")); + + return HTC_SEND_QUEUE_OK; +} + +#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED +static A_UINT16 htc_send_pkts_sched_check(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID id) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + HTC_ENDPOINT_ID eid; + HTC_PACKET_QUEUE *pTxQueue; + A_UINT16 resources; + A_UINT16 acQueueStatus[DATA_EP_SIZE] = { 0, 0, 0, 0 }; + + if (id < ENDPOINT_2 || id > ENDPOINT_5) { + return 1; + } + + for (eid = ENDPOINT_2; eid <= ENDPOINT_5; eid++) { + pEndpoint = &target->endpoint[eid]; + pTxQueue = &pEndpoint->TxQueue; + + if (HTC_QUEUE_EMPTY(pTxQueue)) { + acQueueStatus[eid - 2] = 1; + } + } + + switch (id) { + case ENDPOINT_2: /* BE */ + return (acQueueStatus[0] && acQueueStatus[2] + && acQueueStatus[3]); + case ENDPOINT_3: /* BK */ + return (acQueueStatus[0] && acQueueStatus[1] && acQueueStatus[2] + && acQueueStatus[3]); + case ENDPOINT_4: /* VI */ + return (acQueueStatus[2] && acQueueStatus[3]); + case ENDPOINT_5: /* VO */ + return (acQueueStatus[3]); + default: + return 0; + } + +} + +static A_STATUS htc_send_pkts_sched_queue(HTC_TARGET *target, + HTC_PACKET_QUEUE *pPktQueue, + HTC_ENDPOINT_ID eid) +{ + HTC_ENDPOINT *pEndpoint; + HTC_PACKET_QUEUE *pTxQueue; + HTC_PACKET *pPacket; + int goodPkts; + + pEndpoint = &target->endpoint[eid]; + pTxQueue = &pEndpoint->TxQueue; + + LOCK_HTC_TX(target); + + goodPkts = + pEndpoint->MaxTxQueueDepth - + HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); + + if (goodPkts > 0) { + while (!HTC_QUEUE_EMPTY(pPktQueue)) { + pPacket = htc_packet_dequeue(pPktQueue); + HTC_PACKET_ENQUEUE(pTxQueue, pPacket); + goodPkts--; + + if (goodPkts <= 0) { + break; + } + } + } + + if (HTC_PACKET_QUEUE_DEPTH(pPktQueue)) { + ITERATE_OVER_LIST_ALLOW_REMOVE(&pPktQueue->QueueHead, pPacket, + HTC_PACKET, ListLink) { + + if (pEndpoint->EpCallBacks. + EpSendFull(pEndpoint->EpCallBacks.pContext, + pPacket) == HTC_SEND_FULL_DROP) { + INC_HTC_EP_STAT(pEndpoint, TxDropped, 1); + } else { + HTC_PACKET_REMOVE(pPktQueue, pPacket); + HTC_PACKET_ENQUEUE(pTxQueue, pPacket); + } + } + ITERATE_END; + } + + UNLOCK_HTC_TX(target); + + return A_OK; +} + +#endif + +A_STATUS htc_send_pkts_multiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + HTC_PACKET *pPacket; + cdf_nbuf_t netbuf; + HTC_FRAME_HDR *pHtcHdr; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("+htc_send_pkts_multiple: Queue: %p, Pkts %d \n", + pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue))); + + /* get packet at head to figure out which endpoint these packets will go into */ + pPacket = htc_get_pkt_at_head(pPktQueue); + if (NULL == pPacket) { + OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target, GET_HTC_PKT_Q_FAIL); + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_send_pkts_multiple \n")); + return A_EINVAL; + } + + AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX); + pEndpoint = &target->endpoint[pPacket->Endpoint]; + + if (!pEndpoint->service_id) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("%s service_id is invalid\n", + __func__)); + return A_EINVAL; + } + +#ifdef HTC_EP_STAT_PROFILING + LOCK_HTC_TX(target); + INC_HTC_EP_STAT(pEndpoint, TxPosted, HTC_PACKET_QUEUE_DEPTH(pPktQueue)); + UNLOCK_HTC_TX(target); +#endif + + /* provide room in each packet's netbuf for the HTC frame header */ + HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue, pPacket) { + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + AR_DEBUG_ASSERT(netbuf); + + cdf_nbuf_push_head(netbuf, sizeof(HTC_FRAME_HDR)); + /* setup HTC frame header */ + pHtcHdr = (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, 0); + AR_DEBUG_ASSERT(pHtcHdr); + HTC_WRITE32(pHtcHdr, + SM(pPacket->ActualLength, + HTC_FRAME_HDR_PAYLOADLEN) | SM(pPacket->Endpoint, + HTC_FRAME_HDR_ENDPOINTID)); + + LOCK_HTC_TX(target); + + pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo; + pEndpoint->SeqNo++; + + HTC_WRITE32(((A_UINT32 *) pHtcHdr) + 1, + SM(pPacket->PktInfo.AsTx.SeqNo, + HTC_FRAME_HDR_CONTROLBYTES1)); + + UNLOCK_HTC_TX(target); + /* + * Now that the HTC frame header has been added, the netbuf can be + * mapped. This only applies to non-data frames, since data frames + * were already mapped as they entered into the driver. + */ + cdf_nbuf_map(target->osdev, + GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket), + CDF_DMA_TO_DEVICE); + + pPacket->PktInfo.AsTx.Flags |= HTC_TX_PACKET_FLAG_FIXUP_NETBUF; + } + HTC_PACKET_QUEUE_ITERATE_END; + +#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED + if (!htc_send_pkts_sched_check(HTCHandle, pEndpoint->Id)) { + htc_send_pkts_sched_queue(HTCHandle, pPktQueue, pEndpoint->Id); + } else { + htc_try_send(target, pEndpoint, pPktQueue); + } +#else + htc_try_send(target, pEndpoint, pPktQueue); +#endif + + /* do completion on any packets that couldn't get in */ + if (!HTC_QUEUE_EMPTY(pPktQueue)) { + + HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue, pPacket) { + /* remove the headroom reserved for HTC_FRAME_HDR */ + restore_tx_packet(target, pPacket); + + if (HTC_STOPPING(target)) { + pPacket->Status = A_ECANCELED; + } else { + pPacket->Status = A_NO_RESOURCE; + } + } + HTC_PACKET_QUEUE_ITERATE_END; + + do_send_completion(pEndpoint, pPktQueue); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_send_pkts_multiple \n")); + + return A_OK; +} + +/* HTC API - htc_send_pkt */ +A_STATUS htc_send_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) +{ + HTC_PACKET_QUEUE queue; + + if (HTCHandle == NULL || pPacket == NULL) { + return A_ERROR; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("+-htc_send_pkt: Enter endPointId: %d, buffer: %p, length: %d \n", + pPacket->Endpoint, pPacket->pBuffer, + pPacket->ActualLength)); + INIT_HTC_PACKET_QUEUE_AND_ADD(&queue, pPacket); + return htc_send_pkts_multiple(HTCHandle, &queue); +} + +#ifdef ATH_11AC_TXCOMPACT + +A_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, cdf_nbuf_t netbuf, int Epid, + int ActualLength) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + HTC_FRAME_HDR *pHtcHdr; + A_STATUS status = A_OK; + int tx_resources; + uint32_t data_attr = 0; + + pEndpoint = &target->endpoint[Epid]; + + tx_resources = + hif_get_free_queue_number(target->hif_dev, pEndpoint->UL_PipeID); + + if (tx_resources < HTC_DATA_RESOURCE_THRS) { + if (pEndpoint->ul_is_polled) { + hif_send_complete_check(pEndpoint->target->hif_dev, + pEndpoint->UL_PipeID, 1); + tx_resources = + hif_get_free_queue_number(target->hif_dev, + pEndpoint->UL_PipeID); + } + if (tx_resources < HTC_DATA_MINDESC_PERPACKET) { + return A_ERROR; + } + } + + if (hif_pm_runtime_get(target->hif_dev)) + return A_ERROR; + + pHtcHdr = (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, 0); + AR_DEBUG_ASSERT(pHtcHdr); + + data_attr = cdf_nbuf_data_attr_get(netbuf); + + HTC_WRITE32(pHtcHdr, SM(ActualLength, HTC_FRAME_HDR_PAYLOADLEN) | + SM(Epid, HTC_FRAME_HDR_ENDPOINTID)); + /* + * If the HIF pipe for the data endpoint is polled rather than + * interrupt-driven, this is a good point to check whether any + * data previously sent through the HIF pipe have finished being + * sent. + * Since this may result in callbacks to htc_tx_completion_handler, + * which can take the HTC tx lock, make the hif_send_complete_check + * call before acquiring the HTC tx lock. + * Call hif_send_complete_check directly, rather than calling + * htc_send_complete_check, and call the PollTimerStart separately + * after calling hif_send_head, so the timer will be started to + * check for completion of the new outstanding download (in the + * unexpected event that other polling calls don't catch it). + */ + + LOCK_HTC_TX(target); + + HTC_WRITE32(((A_UINT32 *) pHtcHdr) + 1, + SM(pEndpoint->SeqNo, HTC_FRAME_HDR_CONTROLBYTES1)); + + pEndpoint->SeqNo++; + + NBUF_UPDATE_TX_PKT_COUNT(netbuf, NBUF_TX_PKT_HTC); + DPTRACE(cdf_dp_trace(netbuf, CDF_DP_TRACE_HTC_PACKET_PTR_RECORD, + (uint8_t *)(cdf_nbuf_data(netbuf)), + sizeof(cdf_nbuf_data(netbuf)))); + status = hif_send_head(target->hif_dev, + pEndpoint->UL_PipeID, + pEndpoint->Id, ActualLength, netbuf, data_attr); + + UNLOCK_HTC_TX(target); + return status; +} +#else /*ATH_11AC_TXCOMPACT */ + +A_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket, + A_UINT8 more_data) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint; + HTC_FRAME_HDR *pHtcHdr; + HTC_PACKET_QUEUE sendQueue; + cdf_nbuf_t netbuf = NULL; + int tx_resources; + A_STATUS status = A_OK; + uint32_t data_attr = 0; + + if (pPacket) { + AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX); + pEndpoint = &target->endpoint[pPacket->Endpoint]; + + /* add HTC_FRAME_HDR in the initial fragment */ + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + pHtcHdr = (HTC_FRAME_HDR *) cdf_nbuf_get_frag_vaddr(netbuf, 0); + AR_DEBUG_ASSERT(pHtcHdr); + + HTC_WRITE32(pHtcHdr, + SM(pPacket->ActualLength, + HTC_FRAME_HDR_PAYLOADLEN) | SM(pPacket->PktInfo. + AsTx.SendFlags, + HTC_FRAME_HDR_FLAGS) + | SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID)); + /* + * If the HIF pipe for the data endpoint is polled rather than + * interrupt-driven, this is a good point to check whether any + * data previously sent through the HIF pipe have finished being + * sent. + * Since this may result in callbacks to htc_tx_completion_handler, + * which can take the HTC tx lock, make the hif_send_complete_check + * call before acquiring the HTC tx lock. + * Call hif_send_complete_check directly, rather than calling + * htc_send_complete_check, and call the PollTimerStart separately + * after calling hif_send_head, so the timer will be started to + * check for completion of the new outstanding download (in the + * unexpected event that other polling calls don't catch it). + */ + if (pEndpoint->ul_is_polled) { + htc_send_complete_poll_timer_stop(pEndpoint); + hif_send_complete_check(pEndpoint->target->hif_dev, + pEndpoint->UL_PipeID, 0); + } + + LOCK_HTC_TX(target); + + pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo; + pEndpoint->SeqNo++; + + HTC_WRITE32(((A_UINT32 *) pHtcHdr) + 1, + SM(pPacket->PktInfo.AsTx.SeqNo, + HTC_FRAME_HDR_CONTROLBYTES1)); + + /* append new packet to pEndpoint->TxQueue */ + HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue, pPacket); +#ifdef ENABLE_BUNDLE_TX + if (HTC_ENABLE_BUNDLE(target) && (more_data)) { + UNLOCK_HTC_TX(target); + return A_OK; + } +#endif + } else { + LOCK_HTC_TX(target); + pEndpoint = &target->endpoint[1]; + } + + /* increment tx processing count on entry */ + cdf_atomic_inc(&pEndpoint->TxProcessCount); + if (cdf_atomic_read(&pEndpoint->TxProcessCount) > 1) { + /* + * Another thread or task is draining the TX queues on this endpoint. + * That thread will reset the tx processing count when the queue is + * drained. + */ + cdf_atomic_dec(&pEndpoint->TxProcessCount); + UNLOCK_HTC_TX(target); + return A_OK; + } + + /***** beyond this point only 1 thread may enter ******/ + + INIT_HTC_PACKET_QUEUE(&sendQueue); + if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { +#if DEBUG_CREDIT + int cred = pEndpoint->TxCredits; +#endif + get_htc_send_packets_credit_based(target, pEndpoint, &sendQueue); +#if DEBUG_CREDIT + if (ep_debug_mask & (1 << pEndpoint->Id)) { + if (cred - pEndpoint->TxCredits > 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" <HTC> Decrease EP%d %d - %d = %d credits.\n", + pEndpoint->Id, cred, + cred - pEndpoint->TxCredits, + pEndpoint->TxCredits)); + } + } +#endif + UNLOCK_HTC_TX(target); + } +#ifdef ENABLE_BUNDLE_TX + else if (HTC_ENABLE_BUNDLE(target)) { + /* Dequeue max packets from endpoint tx queue */ + get_htc_send_packets(target, pEndpoint, &sendQueue, + HTC_MAX_TX_BUNDLE_SEND_LIMIT); + UNLOCK_HTC_TX(target); + } +#endif + else { + /* + * Now drain the endpoint TX queue for transmission as long as we have + * enough transmit resources + */ + tx_resources = + hif_get_free_queue_number(target->hif_dev, + pEndpoint->UL_PipeID); + get_htc_send_packets(target, pEndpoint, &sendQueue, tx_resources); + UNLOCK_HTC_TX(target); + } + NBUF_UPDATE_TX_PKT_COUNT(netbuf, NBUF_TX_PKT_HTC); + DPTRACE(cdf_dp_trace(netbuf, CDF_DP_TRACE_HTC_PACKET_PTR_RECORD, + (uint8_t *)(cdf_nbuf_data(netbuf)), + sizeof(cdf_nbuf_data(netbuf)))); + + /* send what we can */ + while (true) { +#if defined(HIF_USB) || defined(HIF_SDIO) +#ifdef ENABLE_BUNDLE_TX + if (HTC_ENABLE_BUNDLE(target) && + HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= + HTC_MIN_MSG_PER_BUNDLE) { + htc_issue_packets_bundle(target, pEndpoint, &sendQueue); + } +#endif +#endif + pPacket = htc_packet_dequeue(&sendQueue); + if (pPacket == NULL) { + break; + } + netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + + LOCK_HTC_TX(target); + /* store in look up queue to match completions */ + HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacket); + INC_HTC_EP_STAT(pEndpoint, TxIssued, 1); + pEndpoint->ul_outstanding_cnt++; + UNLOCK_HTC_TX(target); + + status = hif_send_head(target->hif_dev, + pEndpoint->UL_PipeID, + pEndpoint->Id, + HTC_HDR_LENGTH + pPacket->ActualLength, + netbuf, data_attr); +#if DEBUG_BUNDLE + cdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n", + pEndpoint->Id, + pEndpoint->TxCreditSize, + HTC_HDR_LENGTH + pPacket->ActualLength); +#endif + + if (cdf_unlikely(A_FAILED(status))) { + LOCK_HTC_TX(target); + pEndpoint->ul_outstanding_cnt--; + /* remove this packet from the tx completion queue */ + HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacket); + + /* + * Don't bother reclaiming credits - HTC flow control + * is not applicable to tx data. + * In LL systems, there is no download flow control, + * since there's virtually no download delay. + * In HL systems, the txrx SW explicitly performs the + * tx flow control. + */ + /* pEndpoint->TxCredits += pPacket->PktInfo.AsTx.CreditsUsed; */ + + /* put this frame back at the front of the sendQueue */ + HTC_PACKET_ENQUEUE_TO_HEAD(&sendQueue, pPacket); + + /* put the sendQueue back at the front of pEndpoint->TxQueue */ + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, + &sendQueue); + UNLOCK_HTC_TX(target); + break; /* still need to reset TxProcessCount */ + } + } + /* done with this endpoint, we can clear the count */ + cdf_atomic_init(&pEndpoint->TxProcessCount); + + if (pEndpoint->ul_is_polled) { + /* + * Start a cleanup timer to poll for download completion. + * The download completion should be noticed promptly from + * other polling calls, but the timer provides a safety net + * in case other polling calls don't occur as expected. + */ + htc_send_complete_poll_timer_start(pEndpoint); + } + + return status; +} +#endif /*ATH_11AC_TXCOMPACT */ + +/* + * In the adapted HIF layer, cdf_nbuf_t are passed between HIF and HTC, since upper layers expects + * HTC_PACKET containers we use the completed netbuf and lookup its corresponding HTC packet buffer + * from a lookup list. + * This is extra overhead that can be fixed by re-aligning HIF interfaces with HTC. + * + */ +static HTC_PACKET *htc_lookup_tx_packet(HTC_TARGET *target, + HTC_ENDPOINT *pEndpoint, + cdf_nbuf_t netbuf) +{ + HTC_PACKET *pPacket = NULL; + HTC_PACKET *pFoundPacket = NULL; + HTC_PACKET_QUEUE lookupQueue; + + INIT_HTC_PACKET_QUEUE(&lookupQueue); + LOCK_HTC_TX(target); + + /* mark that HIF has indicated the send complete for another packet */ + pEndpoint->ul_outstanding_cnt--; + + /* Dequeue first packet directly because of in-order completion */ + pPacket = htc_packet_dequeue(&pEndpoint->TxLookupQueue); + if (cdf_unlikely(!pPacket)) { + UNLOCK_HTC_TX(target); + return NULL; + } + if (netbuf == (cdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) { + UNLOCK_HTC_TX(target); + return pPacket; + } else { + HTC_PACKET_ENQUEUE(&lookupQueue, pPacket); + } + + /* + * Move TX lookup queue to temp queue because most of packets that are not index 0 + * are not top 10 packets. + */ + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&lookupQueue, + &pEndpoint->TxLookupQueue); + UNLOCK_HTC_TX(target); + + ITERATE_OVER_LIST_ALLOW_REMOVE(&lookupQueue.QueueHead, pPacket, + HTC_PACKET, ListLink) { + + if (NULL == pPacket) { + pFoundPacket = pPacket; + break; + } + /* check for removal */ + if (netbuf == + (cdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) { + /* found it */ + HTC_PACKET_REMOVE(&lookupQueue, pPacket); + pFoundPacket = pPacket; + break; + } + + } + ITERATE_END; + + LOCK_HTC_TX(target); + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxLookupQueue, + &lookupQueue); + UNLOCK_HTC_TX(target); + + return pFoundPacket; +} + +CDF_STATUS htc_tx_completion_handler(void *Context, + cdf_nbuf_t netbuf, unsigned int EpID, + uint32_t toeplitz_hash_result) +{ + HTC_TARGET *target = (HTC_TARGET *) Context; + HTC_ENDPOINT *pEndpoint; + HTC_PACKET *pPacket; +#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED + HTC_ENDPOINT_ID eid[DATA_EP_SIZE] = + { ENDPOINT_5, ENDPOINT_4, ENDPOINT_2, ENDPOINT_3 }; + int epidIdx; + A_UINT16 resourcesThresh[DATA_EP_SIZE]; /* urb resources */ + A_UINT16 resources; + A_UINT16 resourcesMax; +#endif + + pEndpoint = &target->endpoint[EpID]; + target->TX_comp_cnt++; + + do { + pPacket = htc_lookup_tx_packet(target, pEndpoint, netbuf); + if (NULL == pPacket) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("HTC TX lookup failed!\n")); + /* may have already been flushed and freed */ + netbuf = NULL; + break; + } + if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM) + hif_pm_runtime_put(target->hif_dev); + + if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_BUNDLED) { + HTC_PACKET *pPacketTemp; + HTC_PACKET_QUEUE *pQueueSave = + (HTC_PACKET_QUEUE *) pPacket->pContext; + HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pQueueSave, + pPacketTemp) { + pPacket->Status = A_OK; + send_packet_completion(target, pPacketTemp); + } + HTC_PACKET_QUEUE_ITERATE_END; + free_htc_bundle_packet(target, pPacket); + return CDF_STATUS_SUCCESS; + } + /* will be giving this buffer back to upper layers */ + netbuf = NULL; + pPacket->Status = CDF_STATUS_SUCCESS; + send_packet_completion(target, pPacket); + + } while (false); + + if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { + /* note: when using TX credit flow, the re-checking of queues happens + * when credits flow back from the target. + * in the non-TX credit case, we recheck after the packet completes */ + htc_try_send(target, pEndpoint, NULL); + } + + return CDF_STATUS_SUCCESS; +} + +/* callback when TX resources become available */ +void htc_tx_resource_avail_handler(void *context, A_UINT8 pipeID) +{ + int i; + HTC_TARGET *target = (HTC_TARGET *) context; + HTC_ENDPOINT *pEndpoint = NULL; + + for (i = 0; i < ENDPOINT_MAX; i++) { + pEndpoint = &target->endpoint[i]; + if (pEndpoint->service_id != 0) { + if (pEndpoint->UL_PipeID == pipeID) { + break; + } + } + } + + if (i >= ENDPOINT_MAX) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("Invalid pipe indicated for TX resource avail : %d!\n", + pipeID)); + return; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("HIF indicated more resources for pipe:%d \n", + pipeID)); + + htc_try_send(target, pEndpoint, NULL); +} + +/** + * htc_kick_queues(): resumes tx transactions of suspended endpoints + * @context: pointer to the htc target context + * + * Iterates throught the enpoints and provides a context to empty queues + * int the hif layer when they are stalled due to runtime suspend. + * + * Return: none + */ +void htc_kick_queues(void *context) +{ + int i; + HTC_TARGET *target = (HTC_TARGET *)context; + HTC_ENDPOINT *endpoint = NULL; + + for (i = 0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + + if (endpoint->service_id == 0) + continue; + + if (endpoint->EpCallBacks.ep_resume_tx_queue) + endpoint->EpCallBacks.ep_resume_tx_queue( + endpoint->EpCallBacks.pContext); + + htc_try_send(target, endpoint, NULL); + } +} + +/* flush endpoint TX queue */ +void htc_flush_endpoint_tx(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, + HTC_TX_TAG Tag) +{ + HTC_PACKET *pPacket; + + LOCK_HTC_TX(target); + while (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) { + pPacket = htc_packet_dequeue(&pEndpoint->TxQueue); + + if (pPacket) { + /* let the sender know the packet was not delivered */ + pPacket->Status = A_ECANCELED; + send_packet_completion(target, pPacket); + } + } + UNLOCK_HTC_TX(target); +} + +/* HTC API to flush an endpoint's TX queue*/ +void htc_flush_endpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, + HTC_TX_TAG Tag) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + HTC_ENDPOINT *pEndpoint = &target->endpoint[Endpoint]; + + if (pEndpoint->service_id == 0) { + AR_DEBUG_ASSERT(false); + /* not in use.. */ + return; + } + + htc_flush_endpoint_tx(target, pEndpoint, Tag); +} + +/* HTC API to indicate activity to the credit distribution function */ +void htc_indicate_activity_change(HTC_HANDLE HTCHandle, + HTC_ENDPOINT_ID Endpoint, A_BOOL Active) +{ + /* TODO */ +} + +A_BOOL htc_is_endpoint_active(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint) +{ + return true; +} + +/* process credit reports and call distribution function */ +void htc_process_credit_rpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, + int NumEntries, HTC_ENDPOINT_ID FromEndpoint) +{ + int i; + HTC_ENDPOINT *pEndpoint; + int totalCredits = 0; + A_UINT8 rpt_credits, rpt_ep_id; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("+htc_process_credit_rpt, Credit Report Entries:%d \n", + NumEntries)); + + /* lock out TX while we update credits */ + LOCK_HTC_TX(target); + + for (i = 0; i < NumEntries; i++, pRpt++) { + + rpt_ep_id = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, ENDPOINTID); + + if (rpt_ep_id >= ENDPOINT_MAX) { + AR_DEBUG_ASSERT(false); + break; + } + + rpt_credits = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, CREDITS); + + pEndpoint = &target->endpoint[rpt_ep_id]; +#if DEBUG_CREDIT + if (ep_debug_mask & (1 << pEndpoint->Id)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" <HTC> Increase EP%d %d + %d = %d credits\n", + rpt_ep_id, pEndpoint->TxCredits, + rpt_credits, + pEndpoint->TxCredits + rpt_credits)); + } +#endif + +#ifdef HTC_EP_STAT_PROFILING + + INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1); + INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, rpt_credits); + + if (FromEndpoint == rpt_ep_id) { + /* this credit report arrived on the same endpoint indicating it arrived in an RX + * packet */ + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, + rpt_credits); + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1); + } else if (FromEndpoint == ENDPOINT_0) { + /* this credit arrived on endpoint 0 as a NULL message */ + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, + rpt_credits); + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1); + } else { + /* arrived on another endpoint */ + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, + rpt_credits); + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1); + } + +#endif +#if defined(HIF_USB) + if (pEndpoint->Id >= ENDPOINT_2 && pEndpoint->Id <= ENDPOINT_5) { + HTC_ENDPOINT_ID eid[DATA_EP_SIZE] = + { ENDPOINT_5, ENDPOINT_4, ENDPOINT_2, ENDPOINT_3 }; + int epid_idx; + + target->avail_tx_credits += rpt_credits; + + for (epid_idx = 0; epid_idx < DATA_EP_SIZE; epid_idx++) { + pEndpoint = &target->endpoint[eid[epid_idx]]; + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) { + break; + } + + } + UNLOCK_HTC_TX(target); + htc_try_send(target, pEndpoint, NULL); + LOCK_HTC_TX(target); + } else { + pEndpoint->TxCredits += rpt_credits; + + if (pEndpoint->TxCredits + && HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) { + UNLOCK_HTC_TX(target); + htc_try_send(target, pEndpoint, NULL); + LOCK_HTC_TX(target); + } + } +#else + pEndpoint->TxCredits += rpt_credits; + + if (pEndpoint->service_id == WMI_CONTROL_SVC) { + LOCK_HTC_CREDIT(target); + htc_credit_record(HTC_PROCESS_CREDIT_REPORT, + pEndpoint->TxCredits, + HTC_PACKET_QUEUE_DEPTH(&pEndpoint-> + TxQueue)); + UNLOCK_HTC_CREDIT(target); + } + + if (pEndpoint->TxCredits + && HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) { + UNLOCK_HTC_TX(target); +#ifdef ATH_11AC_TXCOMPACT + htc_try_send(target, pEndpoint, NULL); +#else + if (pEndpoint->service_id == HTT_DATA_MSG_SVC) { + htc_send_data_pkt(target, NULL, 0); + } else { + htc_try_send(target, pEndpoint, NULL); + } +#endif + LOCK_HTC_TX(target); + } +#endif + totalCredits += rpt_credits; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + (" Report indicated %d credits to distribute \n", + totalCredits)); + + UNLOCK_HTC_TX(target); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_process_credit_rpt \n")); +} + +/* function to fetch stats from htc layer*/ +struct ol_ath_htc_stats *ieee80211_ioctl_get_htc_stats(HTC_HANDLE HTCHandle) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + + return (&(target->htc_pkt_stats)); +} diff --git a/htc/htc_services.c b/htc/htc_services.c new file mode 100644 index 000000000000..0e6ce13a6c90 --- /dev/null +++ b/htc/htc_services.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved. + * + * Previously licensed under the ISC license by Qualcomm Atheros, Inc. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file was originally distributed by Qualcomm Atheros, Inc. + * under proprietary terms before Copyright ownership was assigned + * to the Linux Foundation. + */ + +#include "htc_debug.h" +#include "htc_internal.h" +#include <cdf_nbuf.h> /* cdf_nbuf_t */ +#include "hif.h" + +/* use credit flow control over HTC */ +unsigned int htc_credit_flow = 1; +#ifndef DEBUG_CREDIT +#define DEBUG_CREDIT 0 +#endif + +A_STATUS htc_connect_service(HTC_HANDLE HTCHandle, + HTC_SERVICE_CONNECT_REQ *pConnectReq, + HTC_SERVICE_CONNECT_RESP *pConnectResp) +{ + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); + A_STATUS status = A_OK; + HTC_PACKET *pSendPacket = NULL; + HTC_CONNECT_SERVICE_RESPONSE_MSG *pResponseMsg; + HTC_CONNECT_SERVICE_MSG *pConnectMsg; + HTC_ENDPOINT_ID assignedEndpoint = ENDPOINT_MAX; + HTC_ENDPOINT *pEndpoint; + unsigned int maxMsgSize = 0; + cdf_nbuf_t netbuf; + A_UINT8 txAlloc; + int length; + A_BOOL disableCreditFlowCtrl = false; + A_UINT16 conn_flags; + A_UINT16 rsp_msg_id, rsp_msg_serv_id, rsp_msg_max_msg_size; + A_UINT8 rsp_msg_status, rsp_msg_end_id, rsp_msg_serv_meta_len; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("+htc_connect_service, target:%p SvcID:0x%X\n", target, + pConnectReq->service_id)); + + do { + + AR_DEBUG_ASSERT(pConnectReq->service_id != 0); + + if (HTC_CTRL_RSVD_SVC == pConnectReq->service_id) { + /* special case for pseudo control service */ + assignedEndpoint = ENDPOINT_0; + maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH; + txAlloc = 0; + + } else { + + txAlloc = htc_get_credit_allocation(target, + pConnectReq->service_id); + + if (!txAlloc) { + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("Service %d does not allocate target credits!\n", + pConnectReq->service_id)); + } + + /* allocate a packet to send to the target */ + pSendPacket = htc_alloc_control_tx_packet(target); + + if (NULL == pSendPacket) { + AR_DEBUG_ASSERT(false); + status = A_NO_MEMORY; + break; + } + + netbuf = + (cdf_nbuf_t) + GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket); + length = + sizeof(HTC_CONNECT_SERVICE_MSG) + + pConnectReq->MetaDataLength; + + /* assemble connect service message */ + cdf_nbuf_put_tail(netbuf, length); + pConnectMsg = + (HTC_CONNECT_SERVICE_MSG *) cdf_nbuf_data(netbuf); + + if (NULL == pConnectMsg) { + AR_DEBUG_ASSERT(0); + status = A_EFAULT; + break; + } + + A_MEMZERO(pConnectMsg, sizeof(HTC_CONNECT_SERVICE_MSG)); + + conn_flags = + (pConnectReq-> + ConnectionFlags & ~HTC_SET_RECV_ALLOC_MASK) | + HTC_CONNECT_FLAGS_SET_RECV_ALLOCATION(txAlloc); + HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, + MESSAGEID, HTC_MSG_CONNECT_SERVICE_ID); + HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, + SERVICE_ID, pConnectReq->service_id); + HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, + CONNECTIONFLAGS, conn_flags); + + if (pConnectReq-> + ConnectionFlags & + HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL) { + disableCreditFlowCtrl = true; + } +#if defined(HIF_USB) + if (!htc_credit_flow) { + disableCreditFlowCtrl = true; + } +#else + /* Only enable credit for WMI service */ + if (!htc_credit_flow + && pConnectReq->service_id != WMI_CONTROL_SVC) { + disableCreditFlowCtrl = true; + } +#endif + /* check caller if it wants to transfer meta data */ + if ((pConnectReq->pMetaData != NULL) && + (pConnectReq->MetaDataLength <= + HTC_SERVICE_META_DATA_MAX_LENGTH)) { + /* copy meta data into message buffer (after header ) */ + A_MEMCPY((A_UINT8 *) pConnectMsg + + sizeof(HTC_CONNECT_SERVICE_MSG), + pConnectReq->pMetaData, + pConnectReq->MetaDataLength); + + HTC_SET_FIELD(pConnectMsg, + HTC_CONNECT_SERVICE_MSG, + SERVICEMETALENGTH, + pConnectReq->MetaDataLength); + } + + SET_HTC_PACKET_INFO_TX(pSendPacket, + NULL, + (A_UINT8 *) pConnectMsg, + length, + ENDPOINT_0, + HTC_SERVICE_TX_PACKET_TAG); + + status = htc_send_pkt((HTC_HANDLE) target, pSendPacket); + /* we don't own it anymore */ + pSendPacket = NULL; + if (A_FAILED(status)) { + break; + } + + /* wait for response */ + status = htc_wait_recv_ctrl_message(target); + if (A_FAILED(status)) { + break; + } + /* we controlled the buffer creation so it has to be properly aligned */ + pResponseMsg = + (HTC_CONNECT_SERVICE_RESPONSE_MSG *) target-> + CtrlResponseBuffer; + + rsp_msg_id = HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + MESSAGEID); + rsp_msg_serv_id = + HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + SERVICEID); + rsp_msg_status = + HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + STATUS); + rsp_msg_end_id = + HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + ENDPOINTID); + rsp_msg_max_msg_size = + HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + MAXMSGSIZE); + rsp_msg_serv_meta_len = + HTC_GET_FIELD(pResponseMsg, + HTC_CONNECT_SERVICE_RESPONSE_MSG, + SERVICEMETALENGTH); + + if ((rsp_msg_id != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) + || (target->CtrlResponseLength < + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) { + /* this message is not valid */ + AR_DEBUG_ASSERT(false); + status = A_EPROTO; + break; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, + ("htc_connect_service, service 0x%X connect response from target status:%d, assigned ep: %d\n", + rsp_msg_serv_id, rsp_msg_status, + rsp_msg_end_id)); + + pConnectResp->ConnectRespCode = rsp_msg_status; + + /* check response status */ + if (rsp_msg_status != HTC_SERVICE_SUCCESS) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + (" Target failed service 0x%X connect request (status:%d)\n", + rsp_msg_serv_id, + rsp_msg_status)); + status = A_EPROTO; +#ifdef QCA_TX_HTT2_SUPPORT + /* Keep work and not to block the control message. */ + target->CtrlResponseProcessing = false; +#endif /* QCA_TX_HTT2_SUPPORT */ + break; + } + + assignedEndpoint = (HTC_ENDPOINT_ID) rsp_msg_end_id; + maxMsgSize = rsp_msg_max_msg_size; + + if ((pConnectResp->pMetaData != NULL) && + (rsp_msg_serv_meta_len > 0) && + (rsp_msg_serv_meta_len <= + HTC_SERVICE_META_DATA_MAX_LENGTH)) { + /* caller supplied a buffer and the target responded with data */ + int copyLength = + min((int)pConnectResp->BufferLength, + (int)rsp_msg_serv_meta_len); + /* copy the meta data */ + A_MEMCPY(pConnectResp->pMetaData, + ((A_UINT8 *) pResponseMsg) + + sizeof + (HTC_CONNECT_SERVICE_RESPONSE_MSG), + copyLength); + pConnectResp->ActualLength = copyLength; + } + /* done processing response buffer */ + target->CtrlResponseProcessing = false; + } + + /* the rest of these are parameter checks so set the error status */ + status = A_EPROTO; + + if (assignedEndpoint >= ENDPOINT_MAX) { + AR_DEBUG_ASSERT(false); + break; + } + + if (0 == maxMsgSize) { + AR_DEBUG_ASSERT(false); + break; + } + + pEndpoint = &target->endpoint[assignedEndpoint]; + pEndpoint->Id = assignedEndpoint; + if (pEndpoint->service_id != 0) { + /* endpoint already in use! */ + AR_DEBUG_ASSERT(false); + break; + } + + /* return assigned endpoint to caller */ + pConnectResp->Endpoint = assignedEndpoint; + pConnectResp->MaxMsgLength = maxMsgSize; + + /* setup the endpoint */ + /* service_id marks the endpoint in use */ + pEndpoint->service_id = pConnectReq->service_id; + pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth; + pEndpoint->MaxMsgLength = maxMsgSize; + pEndpoint->TxCredits = txAlloc; + pEndpoint->TxCreditSize = target->TargetCreditSize; + pEndpoint->TxCreditsPerMaxMsg = + maxMsgSize / target->TargetCreditSize; + if (maxMsgSize % target->TargetCreditSize) { + pEndpoint->TxCreditsPerMaxMsg++; + } +#if DEBUG_CREDIT + cdf_print(" Endpoint%d initial credit:%d, size:%d.\n", + pEndpoint->Id, pEndpoint->TxCredits, + pEndpoint->TxCreditSize); +#endif + + /* copy all the callbacks */ + pEndpoint->EpCallBacks = pConnectReq->EpCallbacks; + + status = hif_map_service_to_pipe(target->hif_dev, + pEndpoint->service_id, + &pEndpoint->UL_PipeID, + &pEndpoint->DL_PipeID, + &pEndpoint->ul_is_polled, + &pEndpoint->dl_is_polled); + if (A_FAILED(status)) { + break; + } + + cdf_assert(!pEndpoint->dl_is_polled); /* not currently supported */ + + if (pEndpoint->ul_is_polled) { + cdf_softirq_timer_init(target->osdev, + &pEndpoint->ul_poll_timer, + htc_send_complete_check_cleanup, + pEndpoint, + CDF_TIMER_TYPE_SW); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SETUP, + ("HTC Service:0x%4.4X, ULpipe:%d DLpipe:%d id:%d Ready\n", + pEndpoint->service_id, pEndpoint->UL_PipeID, + pEndpoint->DL_PipeID, pEndpoint->Id)); + + if (disableCreditFlowCtrl && pEndpoint->TxCreditFlowEnabled) { + pEndpoint->TxCreditFlowEnabled = false; + AR_DEBUG_PRINTF(ATH_DEBUG_WARN, + ("HTC Service:0x%4.4X ep:%d TX flow control disabled\n", + pEndpoint->service_id, + assignedEndpoint)); + } + + } while (false); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_connect_service \n")); + + return status; +} + +void htc_set_credit_distribution(HTC_HANDLE HTCHandle, + void *pCreditDistContext, + HTC_CREDIT_DIST_CALLBACK CreditDistFunc, + HTC_CREDIT_INIT_CALLBACK CreditInitFunc, + HTC_SERVICE_ID ServicePriorityOrder[], + int ListLength) +{ + /* NOT Supported, this transport does not use a credit based flow control mechanism */ + +} + +void htc_fw_event_handler(void *context, CDF_STATUS status) +{ + HTC_TARGET *target = (HTC_TARGET *) context; + HTC_INIT_INFO *initInfo = &target->HTCInitInfo; + + /* check if target failure handler exists and pass error code to it. */ + if (target->HTCInitInfo.TargetFailure != NULL) { + initInfo->TargetFailure(initInfo->pContext, status); + } +} + +/* Disable ASPM : disable PCIe low power */ +void htc_disable_aspm(void) +{ + hif_disable_aspm(); +} |
