summaryrefslogtreecommitdiff
path: root/htc
diff options
context:
space:
mode:
authorPrakash Dhavali <pdhavali@codeaurora.org>2016-03-02 00:54:45 -0800
committerPrakash Dhavali <pdhavali@codeaurora.org>2016-03-03 01:02:02 -0800
commit142cee4bf22a1d052da15f3d8d050002121e4e77 (patch)
tree367efbdeac65122d4759c59068890d55cdfa0754 /htc
parentabcec8c47e8ff57bf9ef22d56be4eec50406b6b9 (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.h208
-rw-r--r--htc/htc.c881
-rw-r--r--htc/htc_api.h718
-rw-r--r--htc/htc_debug.h50
-rw-r--r--htc/htc_internal.h317
-rw-r--r--htc/htc_packet.h280
-rw-r--r--htc/htc_recv.c749
-rw-r--r--htc/htc_send.c2002
-rw-r--r--htc/htc_services.c369
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();
+}