summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath6kl/htc2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath6kl/htc2')
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.c1399
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.h383
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_events.c762
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox.c752
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c1255
-rw-r--r--drivers/net/wireless/ath6kl/htc2/AR6000/makefile22
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc.c558
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc_debug.h34
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc_internal.h213
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc_recv.c1545
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc_send.c1019
-rw-r--r--drivers/net/wireless/ath6kl/htc2/htc_services.c444
12 files changed, 8386 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.c b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.c
new file mode 100644
index 000000000000..dfcc60b548e4
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.c
@@ -0,0 +1,1399 @@
+//------------------------------------------------------------------------------
+// <copyright file="ar6k.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// AR6K device layer that handles register level I/O
+//
+// Author(s): ="Atheros"
+//==============================================================================
+
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "AR6002/hw2.0/hw/mbox_host_reg.h"
+#include "a_osapi.h"
+#include "../htc_debug.h"
+#include "hif.h"
+#include "htc_packet.h"
+#include "ar6k.h"
+
+#define MAILBOX_FOR_BLOCK_SIZE 1
+
+A_STATUS DevEnableInterrupts(AR6K_DEVICE *pDev);
+A_STATUS DevDisableInterrupts(AR6K_DEVICE *pDev);
+
+static void DevCleanupVirtualScatterSupport(AR6K_DEVICE *pDev);
+
+void AR6KFreeIOPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket)
+{
+ LOCK_AR6K(pDev);
+ HTC_PACKET_ENQUEUE(&pDev->RegisterIOList,pPacket);
+ UNLOCK_AR6K(pDev);
+}
+
+HTC_PACKET *AR6KAllocIOPacket(AR6K_DEVICE *pDev)
+{
+ HTC_PACKET *pPacket;
+
+ LOCK_AR6K(pDev);
+ pPacket = HTC_PACKET_DEQUEUE(&pDev->RegisterIOList);
+ UNLOCK_AR6K(pDev);
+
+ return pPacket;
+}
+
+void DevCleanup(AR6K_DEVICE *pDev)
+{
+ DevCleanupGMbox(pDev);
+
+ if (pDev->HifAttached) {
+ HIFDetachHTC(pDev->HIFDevice);
+ pDev->HifAttached = FALSE;
+ }
+
+ DevCleanupVirtualScatterSupport(pDev);
+
+ if (A_IS_MUTEX_VALID(&pDev->Lock)) {
+ A_MUTEX_DELETE(&pDev->Lock);
+ }
+}
+
+A_STATUS DevSetup(AR6K_DEVICE *pDev)
+{
+ A_UINT32 blocksizes[AR6K_MAILBOXES];
+ A_STATUS status = A_OK;
+ int i;
+ HTC_CALLBACKS htcCallbacks;
+
+ do {
+
+ DL_LIST_INIT(&pDev->ScatterReqHead);
+ /* initialize our free list of IO packets */
+ INIT_HTC_PACKET_QUEUE(&pDev->RegisterIOList);
+ A_MUTEX_INIT(&pDev->Lock);
+
+ A_MEMZERO(&htcCallbacks, sizeof(HTC_CALLBACKS));
+ /* the device layer handles these */
+ htcCallbacks.rwCompletionHandler = DevRWCompletionHandler;
+ htcCallbacks.dsrHandler = DevDsrHandler;
+ htcCallbacks.context = pDev;
+
+ status = HIFAttachHTC(pDev->HIFDevice, &htcCallbacks);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ pDev->HifAttached = TRUE;
+
+ /* get the addresses for all 4 mailboxes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_ADDR,
+ &pDev->MailBoxInfo, sizeof(pDev->MailBoxInfo));
+
+ if (status != A_OK) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* carve up register I/O packets (these are for ASYNC register I/O ) */
+ for (i = 0; i < AR6K_MAX_REG_IO_BUFFERS; i++) {
+ HTC_PACKET *pIOPacket;
+ pIOPacket = &pDev->RegIOBuffers[i].HtcPacket;
+ SET_HTC_PACKET_INFO_RX_REFILL(pIOPacket,
+ pDev,
+ pDev->RegIOBuffers[i].Buffer,
+ AR6K_REG_IO_BUFFER_SIZE,
+ 0); /* don't care */
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ /* get the block sizes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ blocksizes, sizeof(blocksizes));
+
+ if (status != A_OK) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* note: we actually get the block size of a mailbox other than 0, for SDIO the block
+ * size on mailbox 0 is artificially set to 1. So we use the block size that is set
+ * for the other 3 mailboxes */
+ pDev->BlockSize = blocksizes[MAILBOX_FOR_BLOCK_SIZE];
+ /* must be a power of 2 */
+ A_ASSERT((pDev->BlockSize & (pDev->BlockSize - 1)) == 0);
+
+ /* assemble mask, used for padding to a block */
+ pDev->BlockMask = pDev->BlockSize - 1;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("BlockSize: %d, MailboxAddress:0x%X \n",
+ pDev->BlockSize, pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX]));
+
+ pDev->GetPendingEventsFunc = NULL;
+ /* see if the HIF layer implements the get pending events function */
+ HIFConfigureDevice(pDev->HIFDevice,
+ HIF_DEVICE_GET_PENDING_EVENTS_FUNC,
+ &pDev->GetPendingEventsFunc,
+ sizeof(pDev->GetPendingEventsFunc));
+
+ /* assume we can process HIF interrupt events asynchronously */
+ pDev->HifIRQProcessingMode = HIF_DEVICE_IRQ_ASYNC_SYNC;
+
+ /* see if the HIF layer overrides this assumption */
+ HIFConfigureDevice(pDev->HIFDevice,
+ HIF_DEVICE_GET_IRQ_PROC_MODE,
+ &pDev->HifIRQProcessingMode,
+ sizeof(pDev->HifIRQProcessingMode));
+
+ switch (pDev->HifIRQProcessingMode) {
+ case HIF_DEVICE_IRQ_SYNC_ONLY:
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN,("HIF Interrupt processing is SYNC ONLY\n"));
+ /* see if HIF layer wants HTC to yield */
+ HIFConfigureDevice(pDev->HIFDevice,
+ HIF_DEVICE_GET_IRQ_YIELD_PARAMS,
+ &pDev->HifIRQYieldParams,
+ sizeof(pDev->HifIRQYieldParams));
+
+ if (pDev->HifIRQYieldParams.RecvPacketYieldCount > 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
+ ("HIF requests that DSR yield per %d RECV packets \n",
+ pDev->HifIRQYieldParams.RecvPacketYieldCount));
+ pDev->DSRCanYield = TRUE;
+ }
+ break;
+ case HIF_DEVICE_IRQ_ASYNC_SYNC:
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("HIF Interrupt processing is ASYNC and SYNC\n"));
+ break;
+ default:
+ A_ASSERT(FALSE);
+ }
+
+ pDev->HifMaskUmaskRecvEvent = NULL;
+
+ /* see if the HIF layer implements the mask/unmask recv events function */
+ HIFConfigureDevice(pDev->HIFDevice,
+ HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC,
+ &pDev->HifMaskUmaskRecvEvent,
+ sizeof(pDev->HifMaskUmaskRecvEvent));
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("HIF special overrides : 0x%X , 0x%X\n",
+ (A_UINT32)pDev->GetPendingEventsFunc, (A_UINT32)pDev->HifMaskUmaskRecvEvent));
+
+ status = DevDisableInterrupts(pDev);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ status = DevSetupGMbox(pDev);
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ if (pDev->HifAttached) {
+ HIFDetachHTC(pDev->HIFDevice);
+ pDev->HifAttached = FALSE;
+ }
+ }
+
+ return status;
+
+}
+
+A_STATUS DevEnableInterrupts(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+
+ LOCK_AR6K(pDev);
+
+ /* Enable all the interrupts except for the internal AR6000 CPU interrupt */
+ pDev->IrqEnableRegisters.int_status_enable = INT_STATUS_ENABLE_ERROR_SET(0x01) |
+ INT_STATUS_ENABLE_CPU_SET(0x01) |
+ INT_STATUS_ENABLE_COUNTER_SET(0x01);
+
+ if (NULL == pDev->GetPendingEventsFunc) {
+ pDev->IrqEnableRegisters.int_status_enable |= INT_STATUS_ENABLE_MBOX_DATA_SET(0x01);
+ } else {
+ /* The HIF layer provided us with a pending events function which means that
+ * the detection of pending mbox messages is handled in the HIF layer.
+ * This is the case for the SPI2 interface.
+ * In the normal case we enable MBOX interrupts, for the case
+ * with HIFs that offer this mechanism, we keep these interrupts
+ * masked */
+ pDev->IrqEnableRegisters.int_status_enable &= ~INT_STATUS_ENABLE_MBOX_DATA_SET(0x01);
+ }
+
+
+ /* Set up the CPU Interrupt Status Register */
+ pDev->IrqEnableRegisters.cpu_int_status_enable = CPU_INT_STATUS_ENABLE_BIT_SET(0x00);
+
+ /* Set up the Error Interrupt Status Register */
+ pDev->IrqEnableRegisters.error_status_enable =
+ ERROR_STATUS_ENABLE_RX_UNDERFLOW_SET(0x01) |
+ ERROR_STATUS_ENABLE_TX_OVERFLOW_SET(0x01);
+
+ /* Set up the Counter Interrupt Status Register (only for debug interrupt to catch fatal errors) */
+ pDev->IrqEnableRegisters.counter_int_status_enable =
+ COUNTER_INT_STATUS_ENABLE_BIT_SET(AR6K_TARGET_DEBUG_INTR_MASK);
+
+ /* copy into our temp area */
+ A_MEMCPY(&regs,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+
+ UNLOCK_AR6K(pDev);
+
+ /* always synchronous */
+ status = HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_enable,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+
+ if (status != A_OK) {
+ /* Can't write it for some reason */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Failed to update interrupt control registers err: %d\n", status));
+
+ }
+
+ return status;
+}
+
+A_STATUS DevDisableInterrupts(AR6K_DEVICE *pDev)
+{
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+
+ LOCK_AR6K(pDev);
+ /* Disable all interrupts */
+ pDev->IrqEnableRegisters.int_status_enable = 0;
+ pDev->IrqEnableRegisters.cpu_int_status_enable = 0;
+ pDev->IrqEnableRegisters.error_status_enable = 0;
+ pDev->IrqEnableRegisters.counter_int_status_enable = 0;
+ /* copy into our temp area */
+ A_MEMCPY(&regs,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+
+ UNLOCK_AR6K(pDev);
+
+ /* always synchronous */
+ return HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_enable,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+}
+
+/* enable device interrupts */
+A_STATUS DevUnmaskInterrupts(AR6K_DEVICE *pDev)
+{
+ /* for good measure, make sure interrupt are disabled before unmasking at the HIF
+ * layer.
+ * The rationale here is that between device insertion (where we clear the interrupts the first time)
+ * and when HTC is finally ready to handle interrupts, other software can perform target "soft" resets.
+ * The AR6K interrupt enables reset back to an "enabled" state when this happens.
+ * */
+ DevDisableInterrupts(pDev);
+
+ /* Unmask the host controller interrupts */
+ HIFUnMaskInterrupt(pDev->HIFDevice);
+
+ return DevEnableInterrupts(pDev);
+}
+
+/* disable all device interrupts */
+A_STATUS DevMaskInterrupts(AR6K_DEVICE *pDev)
+{
+ /* mask the interrupt at the HIF layer, we don't want a stray interrupt taken while
+ * we zero out our shadow registers in DevDisableInterrupts()*/
+ HIFMaskInterrupt(pDev->HIFDevice);
+
+ return DevDisableInterrupts(pDev);
+}
+
+/* callback when our fetch to enable/disable completes */
+static void DevDoEnableDisableRecvAsyncHandler(void *Context, HTC_PACKET *pPacket)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevDoEnableDisableRecvAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" Failed to disable receiver, status:%d \n", pPacket->Status));
+ }
+ /* free this IO packet */
+ AR6KFreeIOPacket(pDev,pPacket);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevDoEnableDisableRecvAsyncHandler \n"));
+}
+
+/* disable packet reception (used in case the host runs out of buffers)
+ * this is the "override" method when the HIF reports another methods to
+ * disable recv events */
+static A_STATUS DevDoEnableDisableRecvOverride(AR6K_DEVICE *pDev, A_BOOL EnableRecv, A_BOOL AsyncMode)
+{
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket = NULL;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("DevDoEnableDisableRecvOverride: Enable:%d Mode:%d\n",
+ EnableRecv,AsyncMode));
+
+ do {
+
+ if (AsyncMode) {
+
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevDoEnableDisableRecvAsyncHandler;
+ pIOPacket->pContext = pDev;
+
+ /* call the HIF layer override and do this asynchronously */
+ status = pDev->HifMaskUmaskRecvEvent(pDev->HIFDevice,
+ EnableRecv ? HIF_UNMASK_RECV : HIF_MASK_RECV,
+ pIOPacket);
+ break;
+ }
+
+ /* if we get here we are doing it synchronously */
+ status = pDev->HifMaskUmaskRecvEvent(pDev->HIFDevice,
+ EnableRecv ? HIF_UNMASK_RECV : HIF_MASK_RECV,
+ NULL);
+
+ } while (FALSE);
+
+ if (A_FAILED(status) && (pIOPacket != NULL)) {
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ return status;
+}
+
+/* disable packet reception (used in case the host runs out of buffers)
+ * this is the "normal" method using the interrupt enable registers through
+ * the host I/F */
+static A_STATUS DevDoEnableDisableRecvNormal(AR6K_DEVICE *pDev, A_BOOL EnableRecv, A_BOOL AsyncMode)
+{
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket = NULL;
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+
+ /* take the lock to protect interrupt enable shadows */
+ LOCK_AR6K(pDev);
+
+ if (EnableRecv) {
+ pDev->IrqEnableRegisters.int_status_enable |= INT_STATUS_ENABLE_MBOX_DATA_SET(0x01);
+ } else {
+ pDev->IrqEnableRegisters.int_status_enable &= ~INT_STATUS_ENABLE_MBOX_DATA_SET(0x01);
+ }
+
+ /* copy into our temp area */
+ A_MEMCPY(&regs,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+ UNLOCK_AR6K(pDev);
+
+ do {
+
+ if (AsyncMode) {
+
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* copy values to write to our async I/O buffer */
+ A_MEMCPY(pIOPacket->pBuffer,&regs,AR6K_IRQ_ENABLE_REGS_SIZE);
+
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevDoEnableDisableRecvAsyncHandler;
+ pIOPacket->pContext = pDev;
+
+ /* write it out asynchronously */
+ HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ pIOPacket->pBuffer,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_ASYNC_BYTE_INC,
+ pIOPacket);
+ break;
+ }
+
+ /* if we get here we are doing it synchronously */
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_enable,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+
+ } while (FALSE);
+
+ if (A_FAILED(status) && (pIOPacket != NULL)) {
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ return status;
+}
+
+
+A_STATUS DevStopRecv(AR6K_DEVICE *pDev, A_BOOL AsyncMode)
+{
+ if (NULL == pDev->HifMaskUmaskRecvEvent) {
+ return DevDoEnableDisableRecvNormal(pDev,FALSE,AsyncMode);
+ } else {
+ return DevDoEnableDisableRecvOverride(pDev,FALSE,AsyncMode);
+ }
+}
+
+A_STATUS DevEnableRecv(AR6K_DEVICE *pDev, A_BOOL AsyncMode)
+{
+ if (NULL == pDev->HifMaskUmaskRecvEvent) {
+ return DevDoEnableDisableRecvNormal(pDev,TRUE,AsyncMode);
+ } else {
+ return DevDoEnableDisableRecvOverride(pDev,TRUE,AsyncMode);
+ }
+}
+
+void DevDumpRegisters(AR6K_DEVICE *pDev,
+ AR6K_IRQ_PROC_REGISTERS *pIrqProcRegs,
+ AR6K_IRQ_ENABLE_REGISTERS *pIrqEnableRegs)
+{
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("\n<------- Register Table -------->\n"));
+
+ if (pIrqProcRegs != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Host Int Status: 0x%x\n",pIrqProcRegs->host_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("CPU Int Status: 0x%x\n",pIrqProcRegs->cpu_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Error Int Status: 0x%x\n",pIrqProcRegs->error_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Counter Int Status: 0x%x\n",pIrqProcRegs->counter_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Mbox Frame: 0x%x\n",pIrqProcRegs->mbox_frame));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Rx Lookahead Valid: 0x%x\n",pIrqProcRegs->rx_lookahead_valid));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Rx Lookahead 0: 0x%x\n",pIrqProcRegs->rx_lookahead[0]));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Rx Lookahead 1: 0x%x\n",pIrqProcRegs->rx_lookahead[1]));
+
+ if (pDev->MailBoxInfo.GMboxAddress != 0) {
+ /* if the target supports GMBOX hardware, dump some additional state */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("GMBOX Host Int Status 2: 0x%x\n",pIrqProcRegs->host_int_status2));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("GMBOX RX Avail: 0x%x\n",pIrqProcRegs->gmbox_rx_avail));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("GMBOX lookahead alias 0: 0x%x\n",pIrqProcRegs->rx_gmbox_lookahead_alias[0]));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("GMBOX lookahead alias 1: 0x%x\n",pIrqProcRegs->rx_gmbox_lookahead_alias[1]));
+ }
+
+ }
+
+ if (pIrqEnableRegs != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Int Status Enable: 0x%x\n",pIrqEnableRegs->int_status_enable));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("Counter Int Status Enable: 0x%x\n",pIrqEnableRegs->counter_int_status_enable));
+ }
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("<------------------------------->\n"));
+}
+
+
+#define DEV_GET_VIRT_DMA_INFO(p) ((DEV_SCATTER_DMA_VIRTUAL_INFO *)((p)->HIFPrivate[0]))
+
+static HIF_SCATTER_REQ *DevAllocScatterReq(HIF_DEVICE *Context)
+{
+ DL_LIST *pItem;
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+ LOCK_AR6K(pDev);
+ pItem = DL_ListRemoveItemFromHead(&pDev->ScatterReqHead);
+ UNLOCK_AR6K(pDev);
+ if (pItem != NULL) {
+ return A_CONTAINING_STRUCT(pItem, HIF_SCATTER_REQ, ListLink);
+ }
+ return NULL;
+}
+
+static void DevFreeScatterReq(HIF_DEVICE *Context, HIF_SCATTER_REQ *pReq)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+ LOCK_AR6K(pDev);
+ DL_ListInsertTail(&pDev->ScatterReqHead, &pReq->ListLink);
+ UNLOCK_AR6K(pDev);
+}
+
+A_STATUS DevCopyScatterListToFromDMABuffer(HIF_SCATTER_REQ *pReq, A_BOOL FromDMA)
+{
+ A_UINT8 *pDMABuffer = NULL;
+ int i, remaining;
+ A_UINT32 length;
+
+ pDMABuffer = pReq->pScatterBounceBuffer;
+
+ if (pDMABuffer == NULL) {
+ A_ASSERT(FALSE);
+ return A_EINVAL;
+ }
+
+ remaining = (int)pReq->TotalLength;
+
+ for (i = 0; i < pReq->ValidScatterEntries; i++) {
+
+ length = min((int)pReq->ScatterList[i].Length, remaining);
+
+ if (length != (int)pReq->ScatterList[i].Length) {
+ A_ASSERT(FALSE);
+ /* there is a problem with the scatter list */
+ return A_EINVAL;
+ }
+
+ if (FromDMA) {
+ /* from DMA buffer */
+ A_MEMCPY(pReq->ScatterList[i].pBuffer, pDMABuffer , length);
+ } else {
+ /* to DMA buffer */
+ A_MEMCPY(pDMABuffer, pReq->ScatterList[i].pBuffer, length);
+ }
+
+ pDMABuffer += length;
+ remaining -= length;
+ }
+
+ return A_OK;
+}
+
+static void DevReadWriteScatterAsyncHandler(void *Context, HTC_PACKET *pPacket)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+ HIF_SCATTER_REQ *pReq = (HIF_SCATTER_REQ *)pPacket->pPktContext;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+DevReadWriteScatterAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ pReq->CompletionStatus = pPacket->Status;
+
+ AR6KFreeIOPacket(pDev,pPacket);
+
+ pReq->CompletionRoutine(pReq);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-DevReadWriteScatterAsyncHandler \n"));
+}
+
+static A_STATUS DevReadWriteScatter(HIF_DEVICE *Context, HIF_SCATTER_REQ *pReq)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket = NULL;
+ A_UINT32 request = pReq->Request;
+
+ do {
+
+ if (pReq->TotalLength > AR6K_MAX_TRANSFER_SIZE_PER_SCATTER) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Invalid length: %d \n", pReq->TotalLength));
+ break;
+ }
+
+ if (pReq->TotalLength == 0) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ if (request & HIF_ASYNCHRONOUS) {
+ /* use an I/O packet to carry this request */
+ pIOPacket = AR6KAllocIOPacket(pDev);
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ /* save the request */
+ pIOPacket->pPktContext = pReq;
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevReadWriteScatterAsyncHandler;
+ pIOPacket->pContext = pDev;
+ }
+
+ if (request & HIF_WRITE) {
+ /* in virtual DMA, we are issuing the requests through the legacy HIFReadWrite API
+ * this API will adjust the address automatically for the last byte to fall on the mailbox
+ * EOM. */
+
+ /* if the address is an extended address, we can adjust the address here since the extended
+ * address will bypass the normal checks in legacy HIF layers */
+ if (pReq->Address == pDev->MailBoxInfo.MboxProp[HTC_MAILBOX].ExtendedAddress) {
+ pReq->Address += pDev->MailBoxInfo.MboxProp[HTC_MAILBOX].ExtendedSize - pReq->TotalLength;
+ }
+ }
+
+ /* use legacy readwrite */
+ status = HIFReadWrite(pDev->HIFDevice,
+ pReq->Address,
+ DEV_GET_VIRT_DMA_INFO(pReq)->pVirtDmaBuffer,
+ pReq->TotalLength,
+ request,
+ (request & HIF_ASYNCHRONOUS) ? pIOPacket : NULL);
+
+ } while (FALSE);
+
+ if ((status != A_PENDING) && A_FAILED(status) && (request & HIF_ASYNCHRONOUS)) {
+ if (pIOPacket != NULL) {
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+ pReq->CompletionStatus = status;
+ pReq->CompletionRoutine(pReq);
+ status = A_OK;
+ }
+
+ return status;
+}
+
+
+static void DevCleanupVirtualScatterSupport(AR6K_DEVICE *pDev)
+{
+ HIF_SCATTER_REQ *pReq;
+
+ while (1) {
+ pReq = DevAllocScatterReq((HIF_DEVICE *)pDev);
+ if (NULL == pReq) {
+ break;
+ }
+ A_FREE(pReq);
+ }
+
+}
+
+ /* function to set up virtual scatter support if HIF layer has not implemented the interface */
+static A_STATUS DevSetupVirtualScatterSupport(AR6K_DEVICE *pDev)
+{
+ A_STATUS status = A_OK;
+ int bufferSize, sgreqSize;
+ int i;
+ DEV_SCATTER_DMA_VIRTUAL_INFO *pVirtualInfo;
+ HIF_SCATTER_REQ *pReq;
+
+ bufferSize = sizeof(DEV_SCATTER_DMA_VIRTUAL_INFO) +
+ 2 * (A_GET_CACHE_LINE_BYTES()) + AR6K_MAX_TRANSFER_SIZE_PER_SCATTER;
+
+ sgreqSize = sizeof(HIF_SCATTER_REQ) +
+ (AR6K_SCATTER_ENTRIES_PER_REQ - 1) * (sizeof(HIF_SCATTER_ITEM));
+
+ for (i = 0; i < AR6K_SCATTER_REQS; i++) {
+ /* allocate the scatter request, buffer info and the actual virtual buffer itself */
+ pReq = (HIF_SCATTER_REQ *)A_MALLOC(sgreqSize + bufferSize);
+
+ if (NULL == pReq) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ A_MEMZERO(pReq, sgreqSize);
+
+ /* the virtual DMA starts after the scatter request struct */
+ pVirtualInfo = (DEV_SCATTER_DMA_VIRTUAL_INFO *)((A_UINT8 *)pReq + sgreqSize);
+ A_MEMZERO(pVirtualInfo, sizeof(DEV_SCATTER_DMA_VIRTUAL_INFO));
+
+ pVirtualInfo->pVirtDmaBuffer = &pVirtualInfo->DataArea[0];
+ /* align buffer to cache line in case host controller can actually DMA this */
+ pVirtualInfo->pVirtDmaBuffer = A_ALIGN_TO_CACHE_LINE(pVirtualInfo->pVirtDmaBuffer);
+ /* store the structure in the private area */
+ pReq->HIFPrivate[0] = pVirtualInfo;
+ /* we emulate a DMA bounce interface */
+ pReq->ScatterMethod = HIF_SCATTER_DMA_BOUNCE;
+ pReq->pScatterBounceBuffer = pVirtualInfo->pVirtDmaBuffer;
+ /* free request to the list */
+ DevFreeScatterReq((HIF_DEVICE *)pDev,pReq);
+ }
+
+ if (A_FAILED(status)) {
+ DevCleanupVirtualScatterSupport(pDev);
+ } else {
+ pDev->HifScatterInfo.pAllocateReqFunc = DevAllocScatterReq;
+ pDev->HifScatterInfo.pFreeReqFunc = DevFreeScatterReq;
+ pDev->HifScatterInfo.pReadWriteScatterFunc = DevReadWriteScatter;
+ pDev->HifScatterInfo.MaxScatterEntries = AR6K_SCATTER_ENTRIES_PER_REQ;
+ pDev->HifScatterInfo.MaxTransferSizePerScatterReq = AR6K_MAX_TRANSFER_SIZE_PER_SCATTER;
+ pDev->ScatterIsVirtual = TRUE;
+ }
+
+ return status;
+}
+
+
+A_STATUS DevSetupMsgBundling(AR6K_DEVICE *pDev, int MaxMsgsPerTransfer)
+{
+ A_STATUS status;
+
+ if (pDev->MailBoxInfo.Flags & HIF_MBOX_FLAG_NO_BUNDLING) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("HIF requires bundling disabled\n"));
+ return A_ENOTSUP;
+ }
+
+ status = HIFConfigureDevice(pDev->HIFDevice,
+ HIF_CONFIGURE_QUERY_SCATTER_REQUEST_SUPPORT,
+ &pDev->HifScatterInfo,
+ sizeof(pDev->HifScatterInfo));
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
+ ("AR6K: ** HIF layer does not support scatter requests (%d) \n",status));
+
+ /* we can try to use a virtual DMA scatter mechanism using legacy HIFReadWrite() */
+ status = DevSetupVirtualScatterSupport(pDev);
+
+ if (A_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("AR6K: virtual scatter transfers enabled (max scatter items:%d: maxlen:%d) \n",
+ DEV_GET_MAX_MSG_PER_BUNDLE(pDev), DEV_GET_MAX_BUNDLE_LENGTH(pDev)));
+ }
+
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("AR6K: HIF layer supports scatter requests (max scatter items:%d: maxlen:%d) \n",
+ DEV_GET_MAX_MSG_PER_BUNDLE(pDev), DEV_GET_MAX_BUNDLE_LENGTH(pDev)));
+ }
+
+ if (A_SUCCESS(status)) {
+ /* for the recv path, the maximum number of bytes per recv bundle is just limited
+ * by the maximum transfer size at the HIF layer */
+ pDev->MaxRecvBundleSize = pDev->HifScatterInfo.MaxTransferSizePerScatterReq;
+
+ /* for the send path, the max transfer size is limited by the existence and size of
+ * the extended mailbox address range */
+ if (pDev->MailBoxInfo.MboxProp[0].ExtendedAddress != 0) {
+ pDev->MaxSendBundleSize = pDev->MailBoxInfo.MboxProp[0].ExtendedSize;
+ } else {
+ /* legacy */
+ pDev->MaxSendBundleSize = AR6K_LEGACY_MAX_WRITE_LENGTH;
+ }
+
+ if (pDev->MaxSendBundleSize > pDev->HifScatterInfo.MaxTransferSizePerScatterReq) {
+ /* limit send bundle size to what the HIF can support for scatter requests */
+ pDev->MaxSendBundleSize = pDev->HifScatterInfo.MaxTransferSizePerScatterReq;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
+ ("AR6K: max recv: %d max send: %d \n",
+ DEV_GET_MAX_BUNDLE_RECV_LENGTH(pDev), DEV_GET_MAX_BUNDLE_SEND_LENGTH(pDev)));
+
+ }
+ return status;
+}
+
+A_STATUS DevSubmitScatterRequest(AR6K_DEVICE *pDev, HIF_SCATTER_REQ *pScatterReq, A_BOOL Read, A_BOOL Async)
+{
+ A_STATUS status;
+
+ if (Read) {
+ /* read operation */
+ pScatterReq->Request = (Async) ? HIF_RD_ASYNC_BLOCK_FIX : HIF_RD_SYNC_BLOCK_FIX;
+ pScatterReq->Address = pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX];
+ A_ASSERT(pScatterReq->TotalLength <= (A_UINT32)DEV_GET_MAX_BUNDLE_RECV_LENGTH(pDev));
+ } else {
+ A_UINT32 mailboxWidth;
+
+ /* write operation */
+ pScatterReq->Request = (Async) ? HIF_WR_ASYNC_BLOCK_INC : HIF_WR_SYNC_BLOCK_INC;
+ A_ASSERT(pScatterReq->TotalLength <= (A_UINT32)DEV_GET_MAX_BUNDLE_SEND_LENGTH(pDev));
+ if (pScatterReq->TotalLength > AR6K_LEGACY_MAX_WRITE_LENGTH) {
+ /* for large writes use the extended address */
+ pScatterReq->Address = pDev->MailBoxInfo.MboxProp[HTC_MAILBOX].ExtendedAddress;
+ mailboxWidth = pDev->MailBoxInfo.MboxProp[HTC_MAILBOX].ExtendedSize;
+ } else {
+ pScatterReq->Address = pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX];
+ mailboxWidth = AR6K_LEGACY_MAX_WRITE_LENGTH;
+ }
+
+ if (!pDev->ScatterIsVirtual) {
+ /* we are passing this scatter list down to the HIF layer' scatter request handler, fixup the address
+ * so that the last byte falls on the EOM, we do this for those HIFs that support the
+ * scatter API */
+ pScatterReq->Address += (mailboxWidth - pScatterReq->TotalLength);
+ }
+
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV | ATH_DEBUG_SEND,
+ ("DevSubmitScatterRequest, Entries: %d, Total Length: %d Mbox:0x%X (mode: %s : %s)\n",
+ pScatterReq->ValidScatterEntries,
+ pScatterReq->TotalLength,
+ pScatterReq->Address,
+ Async ? "ASYNC" : "SYNC",
+ (Read) ? "RD" : "WR"));
+
+ status = DEV_PREPARE_SCATTER_OPERATION(pScatterReq);
+
+ if (A_FAILED(status)) {
+ if (Async) {
+ pScatterReq->CompletionStatus = status;
+ pScatterReq->CompletionRoutine(pScatterReq);
+ return A_OK;
+ }
+ return status;
+ }
+
+ status = pDev->HifScatterInfo.pReadWriteScatterFunc(pDev->ScatterIsVirtual ? pDev : pDev->HIFDevice,
+ pScatterReq);
+ if (!Async) {
+ /* in sync mode, we can touch the scatter request */
+ pScatterReq->CompletionStatus = status;
+ DEV_FINISH_SCATTER_OPERATION(pScatterReq);
+ } else {
+ if (status == A_PENDING) {
+ status = A_OK;
+ }
+ }
+
+ return status;
+}
+
+
+#ifdef MBOXHW_UNIT_TEST
+
+
+/* This is a mailbox hardware unit test that must be called in a schedulable context
+ * This test is very simple, it will send a list of buffers with a counting pattern
+ * and the target will invert the data and send the message back
+ *
+ * the unit test has the following constraints:
+ *
+ * The target has at least 8 buffers of 256 bytes each. The host will send
+ * the following pattern of buffers in rapid succession :
+ *
+ * 1 buffer - 128 bytes
+ * 1 buffer - 256 bytes
+ * 1 buffer - 512 bytes
+ * 1 buffer - 1024 bytes
+ *
+ * The host will send the buffers to one mailbox and wait for buffers to be reflected
+ * back from the same mailbox. The target sends the buffers FIFO order.
+ * Once the final buffer has been received for a mailbox, the next mailbox is tested.
+ *
+ *
+ * Note: To simplifythe test , we assume that the chosen buffer sizes
+ * will fall on a nice block pad
+ *
+ * It is expected that higher-order tests will be written to stress the mailboxes using
+ * a message-based protocol (with some performance timming) that can create more
+ * randomness in the packets sent over mailboxes.
+ *
+ * */
+
+#define A_ROUND_UP_PWR2(x, align) (((int) (x) + ((align)-1)) & ~((align)-1))
+
+#define BUFFER_BLOCK_PAD 128
+
+#if 0
+#define BUFFER1 128
+#define BUFFER2 256
+#define BUFFER3 512
+#define BUFFER4 1024
+#endif
+
+#if 1
+#define BUFFER1 80
+#define BUFFER2 200
+#define BUFFER3 444
+#define BUFFER4 800
+#endif
+
+#define TOTAL_BYTES (A_ROUND_UP_PWR2(BUFFER1,BUFFER_BLOCK_PAD) + \
+ A_ROUND_UP_PWR2(BUFFER2,BUFFER_BLOCK_PAD) + \
+ A_ROUND_UP_PWR2(BUFFER3,BUFFER_BLOCK_PAD) + \
+ A_ROUND_UP_PWR2(BUFFER4,BUFFER_BLOCK_PAD) )
+
+#define TEST_BYTES (BUFFER1 + BUFFER2 + BUFFER3 + BUFFER4)
+
+#define TEST_CREDITS_RECV_TIMEOUT 100
+
+static A_UINT8 g_Buffer[TOTAL_BYTES];
+static A_UINT32 g_MailboxAddrs[AR6K_MAILBOXES];
+static A_UINT32 g_BlockSizes[AR6K_MAILBOXES];
+
+#define BUFFER_PROC_LIST_DEPTH 4
+
+typedef struct _BUFFER_PROC_LIST{
+ A_UINT8 *pBuffer;
+ A_UINT32 length;
+}BUFFER_PROC_LIST;
+
+
+#define PUSH_BUFF_PROC_ENTRY(pList,len,pCurrpos) \
+{ \
+ (pList)->pBuffer = (pCurrpos); \
+ (pList)->length = (len); \
+ (pCurrpos) += (len); \
+ (pList)++; \
+}
+
+/* a simple and crude way to send different "message" sizes */
+static void AssembleBufferList(BUFFER_PROC_LIST *pList)
+{
+ A_UINT8 *pBuffer = g_Buffer;
+
+#if BUFFER_PROC_LIST_DEPTH < 4
+#error "Buffer processing list depth is not deep enough!!"
+#endif
+
+ PUSH_BUFF_PROC_ENTRY(pList,BUFFER1,pBuffer);
+ PUSH_BUFF_PROC_ENTRY(pList,BUFFER2,pBuffer);
+ PUSH_BUFF_PROC_ENTRY(pList,BUFFER3,pBuffer);
+ PUSH_BUFF_PROC_ENTRY(pList,BUFFER4,pBuffer);
+
+}
+
+#define FILL_ZERO TRUE
+#define FILL_COUNTING FALSE
+static void InitBuffers(A_BOOL Zero)
+{
+ A_UINT16 *pBuffer16 = (A_UINT16 *)g_Buffer;
+ int i;
+
+ /* fill buffer with 16 bit counting pattern or zeros */
+ for (i = 0; i < (TOTAL_BYTES / 2) ; i++) {
+ if (!Zero) {
+ pBuffer16[i] = (A_UINT16)i;
+ } else {
+ pBuffer16[i] = 0;
+ }
+ }
+}
+
+
+static A_BOOL CheckOneBuffer(A_UINT16 *pBuffer16, int Length)
+{
+ int i;
+ A_UINT16 startCount;
+ A_BOOL success = TRUE;
+
+ /* get the starting count */
+ startCount = pBuffer16[0];
+ /* invert it, this is the expected value */
+ startCount = ~startCount;
+ /* scan the buffer and verify */
+ for (i = 0; i < (Length / 2) ; i++,startCount++) {
+ /* target will invert all the data */
+ if ((A_UINT16)pBuffer16[i] != (A_UINT16)~startCount) {
+ success = FALSE;
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Invalid Data Got:0x%X, Expecting:0x%X (offset:%d, total:%d) \n",
+ pBuffer16[i], ((A_UINT16)~startCount), i, Length));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("0x%X 0x%X 0x%X 0x%X \n",
+ pBuffer16[i], pBuffer16[i + 1], pBuffer16[i + 2],pBuffer16[i+3]));
+ break;
+ }
+ }
+
+ return success;
+}
+
+static A_BOOL CheckBuffers(void)
+{
+ int i;
+ A_BOOL success = TRUE;
+ BUFFER_PROC_LIST checkList[BUFFER_PROC_LIST_DEPTH];
+
+ /* assemble the list */
+ AssembleBufferList(checkList);
+
+ /* scan the buffers and verify */
+ for (i = 0; i < BUFFER_PROC_LIST_DEPTH ; i++) {
+ success = CheckOneBuffer((A_UINT16 *)checkList[i].pBuffer, checkList[i].length);
+ if (!success) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Buffer : 0x%X, Length:%d failed verify \n",
+ (A_UINT32)checkList[i].pBuffer, checkList[i].length));
+ break;
+ }
+ }
+
+ return success;
+}
+
+ /* find the end marker for the last buffer we will be sending */
+static A_UINT16 GetEndMarker(void)
+{
+ A_UINT8 *pBuffer;
+ BUFFER_PROC_LIST checkList[BUFFER_PROC_LIST_DEPTH];
+
+ /* fill up buffers with the normal counting pattern */
+ InitBuffers(FILL_COUNTING);
+
+ /* assemble the list we will be sending down */
+ AssembleBufferList(checkList);
+ /* point to the last 2 bytes of the last buffer */
+ pBuffer = &(checkList[BUFFER_PROC_LIST_DEPTH - 1].pBuffer[(checkList[BUFFER_PROC_LIST_DEPTH - 1].length) - 2]);
+
+ /* the last count in the last buffer is the marker */
+ return (A_UINT16)pBuffer[0] | ((A_UINT16)pBuffer[1] << 8);
+}
+
+#define ATH_PRINT_OUT_ZONE ATH_DEBUG_ERR
+
+/* send the ordered buffers to the target */
+static A_STATUS SendBuffers(AR6K_DEVICE *pDev, int mbox)
+{
+ A_STATUS status = A_OK;
+ A_UINT32 request = HIF_WR_SYNC_BLOCK_INC;
+ BUFFER_PROC_LIST sendList[BUFFER_PROC_LIST_DEPTH];
+ int i;
+ int totalBytes = 0;
+ int paddedLength;
+ int totalwPadding = 0;
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Sending buffers on mailbox : %d \n",mbox));
+
+ /* fill buffer with counting pattern */
+ InitBuffers(FILL_COUNTING);
+
+ /* assemble the order in which we send */
+ AssembleBufferList(sendList);
+
+ for (i = 0; i < BUFFER_PROC_LIST_DEPTH; i++) {
+
+ /* we are doing block transfers, so we need to pad everything to a block size */
+ paddedLength = (sendList[i].length + (g_BlockSizes[mbox] - 1)) &
+ (~(g_BlockSizes[mbox] - 1));
+
+ /* send each buffer synchronously */
+ status = HIFReadWrite(pDev->HIFDevice,
+ g_MailboxAddrs[mbox],
+ sendList[i].pBuffer,
+ paddedLength,
+ request,
+ NULL);
+ if (status != A_OK) {
+ break;
+ }
+ totalBytes += sendList[i].length;
+ totalwPadding += paddedLength;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Sent %d bytes (%d padded bytes) to mailbox : %d \n",totalBytes,totalwPadding,mbox));
+
+ return status;
+}
+
+/* poll the mailbox credit counter until we get a credit or timeout */
+static A_STATUS GetCredits(AR6K_DEVICE *pDev, int mbox, int *pCredits)
+{
+ A_STATUS status = A_OK;
+ int timeout = TEST_CREDITS_RECV_TIMEOUT;
+ A_UINT8 credits = 0;
+ A_UINT32 address;
+
+ while (TRUE) {
+
+ /* Read the counter register to get credits, this auto-decrements */
+ address = COUNT_DEC_ADDRESS + (AR6K_MAILBOXES + mbox) * 4;
+ status = HIFReadWrite(pDev->HIFDevice, address, &credits, sizeof(credits),
+ HIF_RD_SYNC_BYTE_FIX, NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Unable to decrement the command credit count register (mbox=%d)\n",mbox));
+ status = A_ERROR;
+ break;
+ }
+
+ if (credits) {
+ break;
+ }
+
+ timeout--;
+
+ if (timeout <= 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" Timeout reading credit registers (mbox=%d, address:0x%X) \n",mbox,address));
+ status = A_ERROR;
+ break;
+ }
+
+ /* delay a little, target may not be ready */
+ A_MDELAY(1000);
+
+ }
+
+ if (status == A_OK) {
+ *pCredits = credits;
+ }
+
+ return status;
+}
+
+
+/* wait for the buffers to come back */
+static A_STATUS RecvBuffers(AR6K_DEVICE *pDev, int mbox)
+{
+ A_STATUS status = A_OK;
+ A_UINT32 request = HIF_RD_SYNC_BLOCK_INC;
+ BUFFER_PROC_LIST recvList[BUFFER_PROC_LIST_DEPTH];
+ int curBuffer;
+ int credits;
+ int i;
+ int totalBytes = 0;
+ int paddedLength;
+ int totalwPadding = 0;
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Waiting for buffers on mailbox : %d \n",mbox));
+
+ /* zero the buffers */
+ InitBuffers(FILL_ZERO);
+
+ /* assemble the order in which we should receive */
+ AssembleBufferList(recvList);
+
+ curBuffer = 0;
+
+ while (curBuffer < BUFFER_PROC_LIST_DEPTH) {
+
+ /* get number of buffers that have been completed, this blocks
+ * until we get at least 1 credit or it times out */
+ status = GetCredits(pDev, mbox, &credits);
+
+ if (status != A_OK) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Got %d messages on mailbox : %d \n",credits, mbox));
+
+ /* get all the buffers that are sitting on the queue */
+ for (i = 0; i < credits; i++) {
+ A_ASSERT(curBuffer < BUFFER_PROC_LIST_DEPTH);
+ /* recv the current buffer synchronously, the buffers should come back in
+ * order... with padding applied by the target */
+ paddedLength = (recvList[curBuffer].length + (g_BlockSizes[mbox] - 1)) &
+ (~(g_BlockSizes[mbox] - 1));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ g_MailboxAddrs[mbox],
+ recvList[curBuffer].pBuffer,
+ paddedLength,
+ request,
+ NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to read %d bytes on mailbox:%d : address:0x%X \n",
+ recvList[curBuffer].length, mbox, g_MailboxAddrs[mbox]));
+ break;
+ }
+
+ totalwPadding += paddedLength;
+ totalBytes += recvList[curBuffer].length;
+ curBuffer++;
+ }
+
+ if (status != A_OK) {
+ break;
+ }
+ /* go back and get some more */
+ credits = 0;
+ }
+
+ if (totalBytes != TEST_BYTES) {
+ A_ASSERT(FALSE);
+ } else {
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Got all buffers on mbox:%d total recv :%d (w/Padding : %d) \n",
+ mbox, totalBytes, totalwPadding));
+ }
+
+ return status;
+
+
+}
+
+static A_STATUS DoOneMboxHWTest(AR6K_DEVICE *pDev, int mbox)
+{
+ A_STATUS status;
+
+ do {
+ /* send out buffers */
+ status = SendBuffers(pDev,mbox);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Sending buffers Failed : %d mbox:%d\n",status,mbox));
+ break;
+ }
+
+ /* go get them, this will block */
+ status = RecvBuffers(pDev, mbox);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Recv buffers Failed : %d mbox:%d\n",status,mbox));
+ break;
+ }
+
+ /* check the returned data patterns */
+ if (!CheckBuffers()) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Buffer Verify Failed : mbox:%d\n",mbox));
+ status = A_ERROR;
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, (" Send/Recv success! mailbox : %d \n",mbox));
+
+ } while (FALSE);
+
+ return status;
+}
+
+/* here is where the test starts */
+A_STATUS DoMboxHWTest(AR6K_DEVICE *pDev)
+{
+ int i;
+ A_STATUS status;
+ int credits = 0;
+ A_UINT8 params[4];
+ int numBufs;
+ int bufferSize;
+ A_UINT16 temp;
+
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, (" DoMboxHWTest START - \n"));
+
+ do {
+ /* get the addresses for all 4 mailboxes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_ADDR,
+ g_MailboxAddrs, sizeof(g_MailboxAddrs));
+
+ if (status != A_OK) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* get the block sizes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ g_BlockSizes, sizeof(g_BlockSizes));
+
+ if (status != A_OK) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* note, the HIF layer usually reports mbox 0 to have a block size of
+ * 1, but our test wants to run in block-mode for all mailboxes, so we treat all mailboxes
+ * the same. */
+ g_BlockSizes[0] = g_BlockSizes[1];
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Block Size to use: %d \n",g_BlockSizes[0]));
+
+ if (g_BlockSizes[1] > BUFFER_BLOCK_PAD) {
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("%d Block size is too large for buffer pad %d\n",
+ g_BlockSizes[1], BUFFER_BLOCK_PAD));
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Waiting for target.... \n"));
+
+ /* the target lets us know it is ready by giving us 1 credit on
+ * mailbox 0 */
+ status = GetCredits(pDev, 0, &credits);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to wait for target ready \n"));
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Target is ready ...\n"));
+
+ /* read the first 4 scratch registers */
+ status = HIFReadWrite(pDev->HIFDevice,
+ SCRATCH_ADDRESS,
+ params,
+ 4,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to wait get parameters \n"));
+ break;
+ }
+
+ numBufs = params[0];
+ bufferSize = (int)(((A_UINT16)params[2] << 8) | (A_UINT16)params[1]);
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE,
+ ("Target parameters: bufs per mailbox:%d, buffer size:%d bytes (total space: %d, minimum required space (w/padding): %d) \n",
+ numBufs, bufferSize, (numBufs * bufferSize), TOTAL_BYTES));
+
+ if ((numBufs * bufferSize) < TOTAL_BYTES) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Not Enough buffer space to run test! need:%d, got:%d \n",
+ TOTAL_BYTES, (numBufs*bufferSize)));
+ status = A_ERROR;
+ break;
+ }
+
+ temp = GetEndMarker();
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ SCRATCH_ADDRESS + 4,
+ (A_UINT8 *)&temp,
+ 2,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to write end marker \n"));
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("End Marker: 0x%X \n",temp));
+
+ temp = (A_UINT16)g_BlockSizes[1];
+ /* convert to a mask */
+ temp = temp - 1;
+ status = HIFReadWrite(pDev->HIFDevice,
+ SCRATCH_ADDRESS + 6,
+ (A_UINT8 *)&temp,
+ 2,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to write block mask \n"));
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, ("Set Block Mask: 0x%X \n",temp));
+
+ /* execute the test on each mailbox */
+ for (i = 0; i < AR6K_MAILBOXES; i++) {
+ status = DoOneMboxHWTest(pDev, i);
+ if (status != A_OK) {
+ break;
+ }
+ }
+
+ } while (FALSE);
+
+ if (status == A_OK) {
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, (" DoMboxHWTest DONE - SUCCESS! - \n"));
+ } else {
+ AR_DEBUG_PRINTF(ATH_PRINT_OUT_ZONE, (" DoMboxHWTest DONE - FAILED! - \n"));
+ }
+ /* don't let HTC_Start continue, the target is actually not running any HTC code */
+ return A_ERROR;
+}
+#endif
+
+
+
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.h b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.h
new file mode 100644
index 000000000000..b71ddb96cbde
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k.h
@@ -0,0 +1,383 @@
+//------------------------------------------------------------------------------
+// <copyright file="ar6k.h" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// AR6K device layer that handles register level I/O
+//
+// Author(s): ="Atheros"
+//==============================================================================
+#ifndef AR6K_H_
+#define AR6K_H_
+
+#include "hci_transport_api.h"
+#include "../htc_debug.h"
+
+#define AR6K_MAILBOXES 4
+
+/* HTC runs over mailbox 0 */
+#define HTC_MAILBOX 0
+
+#define AR6K_TARGET_DEBUG_INTR_MASK 0x01
+
+#define OTHER_INTS_ENABLED (INT_STATUS_ENABLE_ERROR_MASK | \
+ INT_STATUS_ENABLE_CPU_MASK | \
+ INT_STATUS_ENABLE_COUNTER_MASK)
+
+
+//#define MBOXHW_UNIT_TEST 1
+
+#include "athstartpack.h"
+typedef PREPACK struct _AR6K_IRQ_PROC_REGISTERS {
+ A_UINT8 host_int_status;
+ A_UINT8 cpu_int_status;
+ A_UINT8 error_int_status;
+ A_UINT8 counter_int_status;
+ A_UINT8 mbox_frame;
+ A_UINT8 rx_lookahead_valid;
+ A_UINT8 host_int_status2;
+ A_UINT8 gmbox_rx_avail;
+ A_UINT32 rx_lookahead[2];
+ A_UINT32 rx_gmbox_lookahead_alias[2];
+} POSTPACK AR6K_IRQ_PROC_REGISTERS;
+
+#define AR6K_IRQ_PROC_REGS_SIZE sizeof(AR6K_IRQ_PROC_REGISTERS)
+
+typedef PREPACK struct _AR6K_IRQ_ENABLE_REGISTERS {
+ A_UINT8 int_status_enable;
+ A_UINT8 cpu_int_status_enable;
+ A_UINT8 error_status_enable;
+ A_UINT8 counter_int_status_enable;
+} POSTPACK AR6K_IRQ_ENABLE_REGISTERS;
+
+typedef PREPACK struct _AR6K_GMBOX_CTRL_REGISTERS {
+ A_UINT8 int_status_enable;
+} POSTPACK AR6K_GMBOX_CTRL_REGISTERS;
+
+#include "athendpack.h"
+
+#define AR6K_IRQ_ENABLE_REGS_SIZE sizeof(AR6K_IRQ_ENABLE_REGISTERS)
+
+#define AR6K_REG_IO_BUFFER_SIZE 32
+#define AR6K_MAX_REG_IO_BUFFERS 8
+#define FROM_DMA_BUFFER TRUE
+#define TO_DMA_BUFFER FALSE
+#define AR6K_SCATTER_ENTRIES_PER_REQ 16
+#define AR6K_MAX_TRANSFER_SIZE_PER_SCATTER 16*1024
+#define AR6K_SCATTER_REQS 4
+#define AR6K_LEGACY_MAX_WRITE_LENGTH 2048
+
+/* buffers for ASYNC I/O */
+typedef struct AR6K_ASYNC_REG_IO_BUFFER {
+ HTC_PACKET HtcPacket; /* we use an HTC packet as a wrapper for our async register-based I/O */
+ A_UINT8 Buffer[AR6K_REG_IO_BUFFER_SIZE];
+} AR6K_ASYNC_REG_IO_BUFFER;
+
+typedef struct _AR6K_GMBOX_INFO {
+ void *pProtocolContext;
+ A_STATUS (*pMessagePendingCallBack)(void *pContext, A_UINT8 LookAheadBytes[], int ValidBytes);
+ A_STATUS (*pCreditsPendingCallback)(void *pContext, int NumCredits, A_BOOL CreditIRQEnabled);
+ void (*pTargetFailureCallback)(void *pContext, A_STATUS Status);
+ void (*pStateDumpCallback)(void *pContext);
+ A_BOOL CreditCountIRQEnabled;
+} AR6K_GMBOX_INFO;
+
+typedef struct _AR6K_DEVICE {
+ A_MUTEX_T Lock;
+ AR6K_IRQ_PROC_REGISTERS IrqProcRegisters;
+ AR6K_IRQ_ENABLE_REGISTERS IrqEnableRegisters;
+ void *HIFDevice;
+ A_UINT32 BlockSize;
+ A_UINT32 BlockMask;
+ HIF_DEVICE_MBOX_INFO MailBoxInfo;
+ HIF_PENDING_EVENTS_FUNC GetPendingEventsFunc;
+ void *HTCContext;
+ HTC_PACKET_QUEUE RegisterIOList;
+ AR6K_ASYNC_REG_IO_BUFFER RegIOBuffers[AR6K_MAX_REG_IO_BUFFERS];
+ void (*TargetFailureCallback)(void *Context);
+ A_STATUS (*MessagePendingCallback)(void *Context,
+ A_UINT32 LookAheads[],
+ int NumLookAheads,
+ A_BOOL *pAsyncProc,
+ int *pNumPktsFetched);
+ HIF_DEVICE_IRQ_PROCESSING_MODE HifIRQProcessingMode;
+ HIF_MASK_UNMASK_RECV_EVENT HifMaskUmaskRecvEvent;
+ A_BOOL HifAttached;
+ HIF_DEVICE_IRQ_YIELD_PARAMS HifIRQYieldParams;
+ A_BOOL DSRCanYield;
+ int CurrentDSRRecvCount;
+ HIF_DEVICE_SCATTER_SUPPORT_INFO HifScatterInfo;
+ DL_LIST ScatterReqHead;
+ A_BOOL ScatterIsVirtual;
+ int MaxRecvBundleSize;
+ int MaxSendBundleSize;
+ AR6K_GMBOX_INFO GMboxInfo;
+ A_BOOL GMboxEnabled;
+ AR6K_GMBOX_CTRL_REGISTERS GMboxControlRegisters;
+ int RecheckIRQStatusCnt;
+} AR6K_DEVICE;
+
+#define LOCK_AR6K(p) A_MUTEX_LOCK(&(p)->Lock);
+#define UNLOCK_AR6K(p) A_MUTEX_UNLOCK(&(p)->Lock);
+#define REF_IRQ_STATUS_RECHECK(p) (p)->RecheckIRQStatusCnt = 1 /* note: no need to lock this, it only gets set */
+
+A_STATUS DevSetup(AR6K_DEVICE *pDev);
+void DevCleanup(AR6K_DEVICE *pDev);
+A_STATUS DevUnmaskInterrupts(AR6K_DEVICE *pDev);
+A_STATUS DevMaskInterrupts(AR6K_DEVICE *pDev);
+A_STATUS DevPollMboxMsgRecv(AR6K_DEVICE *pDev,
+ A_UINT32 *pLookAhead,
+ int TimeoutMS);
+A_STATUS DevRWCompletionHandler(void *context, A_STATUS status);
+A_STATUS DevDsrHandler(void *context);
+A_STATUS DevCheckPendingRecvMsgsAsync(void *context);
+void DevAsyncIrqProcessComplete(AR6K_DEVICE *pDev);
+void DevDumpRegisters(AR6K_DEVICE *pDev,
+ AR6K_IRQ_PROC_REGISTERS *pIrqProcRegs,
+ AR6K_IRQ_ENABLE_REGISTERS *pIrqEnableRegs);
+
+#define DEV_STOP_RECV_ASYNC TRUE
+#define DEV_STOP_RECV_SYNC FALSE
+#define DEV_ENABLE_RECV_ASYNC TRUE
+#define DEV_ENABLE_RECV_SYNC FALSE
+A_STATUS DevStopRecv(AR6K_DEVICE *pDev, A_BOOL ASyncMode);
+A_STATUS DevEnableRecv(AR6K_DEVICE *pDev, A_BOOL ASyncMode);
+A_STATUS DevEnableInterrupts(AR6K_DEVICE *pDev);
+A_STATUS DevDisableInterrupts(AR6K_DEVICE *pDev);
+
+
+#define DEV_CALC_RECV_PADDED_LEN(pDev, length) (((length) + (pDev)->BlockMask) & (~((pDev)->BlockMask)))
+#define DEV_CALC_SEND_PADDED_LEN(pDev, length) DEV_CALC_RECV_PADDED_LEN(pDev,length)
+#define DEV_IS_LEN_BLOCK_ALIGNED(pDev, length) (((length) % (pDev)->BlockSize) == 0)
+
+static INLINE A_STATUS DevSendPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 SendLength) {
+ A_UINT32 paddedLength;
+ A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
+ A_STATUS status;
+
+ /* adjust the length to be a multiple of block size if appropriate */
+ paddedLength = DEV_CALC_SEND_PADDED_LEN(pDev, SendLength);
+
+#if 0
+ if (paddedLength > pPacket->BufferLength) {
+ A_ASSERT(FALSE);
+ if (pPacket->Completion != NULL) {
+ COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
+ return A_OK;
+ }
+ return A_EINVAL;
+ }
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("DevSendPacket, Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ paddedLength,
+ pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX],
+ sync ? "SYNC" : "ASYNC"));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX],
+ pPacket->pBuffer,
+ paddedLength, /* the padded length */
+ sync ? HIF_WR_SYNC_BLOCK_INC : HIF_WR_ASYNC_BLOCK_INC,
+ sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
+
+ if (sync) {
+ pPacket->Status = status;
+ } else {
+ if (status == A_PENDING) {
+ status = A_OK;
+ }
+ }
+
+ return status;
+}
+
+static INLINE A_STATUS DevRecvPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 RecvLength) {
+ A_UINT32 paddedLength;
+ A_STATUS status;
+ A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
+
+ /* adjust the length to be a multiple of block size if appropriate */
+ paddedLength = DEV_CALC_RECV_PADDED_LEN(pDev, RecvLength);
+
+ if (paddedLength > pPacket->BufferLength) {
+ A_ASSERT(FALSE);
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("DevRecvPacket, Not enough space for padlen:%d recvlen:%d bufferlen:%d \n",
+ paddedLength,RecvLength,pPacket->BufferLength));
+ if (pPacket->Completion != NULL) {
+ COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
+ return A_OK;
+ }
+ return A_EINVAL;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("DevRecvPacket (0x%X : hdr:0x%X) Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ (A_UINT32)pPacket, pPacket->PktInfo.AsRx.ExpectedHdr,
+ paddedLength,
+ pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX],
+ sync ? "SYNC" : "ASYNC"));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ pDev->MailBoxInfo.MboxAddresses[HTC_MAILBOX],
+ pPacket->pBuffer,
+ paddedLength,
+ sync ? HIF_RD_SYNC_BLOCK_FIX : HIF_RD_ASYNC_BLOCK_FIX,
+ sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
+
+ if (sync) {
+ pPacket->Status = status;
+ }
+
+ return status;
+}
+
+#define DEV_CHECK_RECV_YIELD(pDev) \
+ ((pDev)->CurrentDSRRecvCount >= (pDev)->HifIRQYieldParams.RecvPacketYieldCount)
+
+#define IS_DEV_IRQ_PROC_SYNC_MODE(pDev) (HIF_DEVICE_IRQ_SYNC_ONLY == (pDev)->HifIRQProcessingMode)
+#define IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(pDev) ((pDev)->HifIRQProcessingMode != HIF_DEVICE_IRQ_SYNC_ONLY)
+
+/**************************************************/
+/****** Scatter Function and Definitions
+ *
+ *
+ */
+
+A_STATUS DevCopyScatterListToFromDMABuffer(HIF_SCATTER_REQ *pReq, A_BOOL FromDMA);
+
+ /* copy any READ data back into scatter list */
+#define DEV_FINISH_SCATTER_OPERATION(pR) \
+ if (A_SUCCESS((pR)->CompletionStatus) && \
+ !((pR)->Request & HIF_WRITE) && \
+ ((pR)->ScatterMethod == HIF_SCATTER_DMA_BOUNCE)) { \
+ (pR)->CompletionStatus = DevCopyScatterListToFromDMABuffer((pR),FROM_DMA_BUFFER); \
+ }
+
+ /* copy any WRITE data to bounce buffer */
+static INLINE A_STATUS DEV_PREPARE_SCATTER_OPERATION(HIF_SCATTER_REQ *pReq) {
+ if ((pReq->Request & HIF_WRITE) && (pReq->ScatterMethod == HIF_SCATTER_DMA_BOUNCE)) {
+ return DevCopyScatterListToFromDMABuffer(pReq,TO_DMA_BUFFER);
+ } else {
+ return A_OK;
+ }
+}
+
+
+A_STATUS DevSetupMsgBundling(AR6K_DEVICE *pDev, int MaxMsgsPerTransfer);
+
+#define DEV_GET_MAX_MSG_PER_BUNDLE(pDev) (pDev)->HifScatterInfo.MaxScatterEntries
+#define DEV_GET_MAX_BUNDLE_LENGTH(pDev) (pDev)->HifScatterInfo.MaxTransferSizePerScatterReq
+#define DEV_ALLOC_SCATTER_REQ(pDev) \
+ (pDev)->HifScatterInfo.pAllocateReqFunc((pDev)->ScatterIsVirtual ? (pDev) : (pDev)->HIFDevice)
+
+#define DEV_FREE_SCATTER_REQ(pDev,pR) \
+ (pDev)->HifScatterInfo.pFreeReqFunc((pDev)->ScatterIsVirtual ? (pDev) : (pDev)->HIFDevice,(pR))
+
+#define DEV_GET_MAX_BUNDLE_RECV_LENGTH(pDev) (pDev)->MaxRecvBundleSize
+#define DEV_GET_MAX_BUNDLE_SEND_LENGTH(pDev) (pDev)->MaxSendBundleSize
+
+#define DEV_SCATTER_READ TRUE
+#define DEV_SCATTER_WRITE FALSE
+#define DEV_SCATTER_ASYNC TRUE
+#define DEV_SCATTER_SYNC FALSE
+A_STATUS DevSubmitScatterRequest(AR6K_DEVICE *pDev, HIF_SCATTER_REQ *pScatterReq, A_BOOL Read, A_BOOL Async);
+
+#ifdef MBOXHW_UNIT_TEST
+A_STATUS DoMboxHWTest(AR6K_DEVICE *pDev);
+#endif
+
+ /* completely virtual */
+typedef struct _DEV_SCATTER_DMA_VIRTUAL_INFO {
+ A_UINT8 *pVirtDmaBuffer; /* dma-able buffer - CPU accessible address */
+ A_UINT8 DataArea[1]; /* start of data area */
+} DEV_SCATTER_DMA_VIRTUAL_INFO;
+
+
+
+void DumpAR6KDevState(AR6K_DEVICE *pDev);
+
+/**************************************************/
+/****** GMBOX functions and definitions
+ *
+ *
+ */
+
+#ifdef ATH_AR6K_ENABLE_GMBOX
+
+void DevCleanupGMbox(AR6K_DEVICE *pDev);
+A_STATUS DevSetupGMbox(AR6K_DEVICE *pDev);
+A_STATUS DevCheckGMboxInterrupts(AR6K_DEVICE *pDev);
+void DevNotifyGMboxTargetFailure(AR6K_DEVICE *pDev);
+
+#else
+
+ /* compiled out */
+#define DevCleanupGMbox(p)
+#define DevCheckGMboxInterrupts(p) A_OK
+#define DevNotifyGMboxTargetFailure(p)
+
+static INLINE A_STATUS DevSetupGMbox(AR6K_DEVICE *pDev) {
+ pDev->GMboxEnabled = FALSE;
+ return A_OK;
+}
+
+#endif
+
+#ifdef ATH_AR6K_ENABLE_GMBOX
+
+ /* GMBOX protocol modules must expose each of these internal APIs */
+HCI_TRANSPORT_HANDLE GMboxAttachProtocol(AR6K_DEVICE *pDev, HCI_TRANSPORT_CONFIG_INFO *pInfo);
+A_STATUS GMboxProtocolInstall(AR6K_DEVICE *pDev);
+void GMboxProtocolUninstall(AR6K_DEVICE *pDev);
+
+ /* API used by GMBOX protocol modules */
+AR6K_DEVICE *HTCGetAR6KDevice(void *HTCHandle);
+#define DEV_GMBOX_SET_PROTOCOL(pDev,recv_callback,credits_pending,failure,statedump,context) \
+{ \
+ (pDev)->GMboxInfo.pProtocolContext = (context); \
+ (pDev)->GMboxInfo.pMessagePendingCallBack = (recv_callback); \
+ (pDev)->GMboxInfo.pCreditsPendingCallback = (credits_pending); \
+ (pDev)->GMboxInfo.pTargetFailureCallback = (failure); \
+ (pDev)->GMboxInfo.pStateDumpCallback = (statedump); \
+}
+
+#define DEV_GMBOX_GET_PROTOCOL(pDev) (pDev)->GMboxInfo.pProtocolContext
+
+A_STATUS DevGMboxWrite(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 WriteLength);
+A_STATUS DevGMboxRead(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 ReadLength);
+
+#define PROC_IO_ASYNC TRUE
+#define PROC_IO_SYNC FALSE
+typedef enum GMBOX_IRQ_ACTION_TYPE {
+ GMBOX_ACTION_NONE = 0,
+ GMBOX_DISABLE_ALL,
+ GMBOX_ERRORS_IRQ_ENABLE,
+ GMBOX_RECV_IRQ_ENABLE,
+ GMBOX_RECV_IRQ_DISABLE,
+ GMBOX_CREDIT_IRQ_ENABLE,
+ GMBOX_CREDIT_IRQ_DISABLE,
+} GMBOX_IRQ_ACTION_TYPE;
+
+A_STATUS DevGMboxIRQAction(AR6K_DEVICE *pDev, GMBOX_IRQ_ACTION_TYPE, A_BOOL AsyncMode);
+A_STATUS DevGMboxReadCreditCounter(AR6K_DEVICE *pDev, A_BOOL AsyncMode, int *pCredits);
+A_STATUS DevGMboxReadCreditSize(AR6K_DEVICE *pDev, int *pCreditSize);
+A_STATUS DevGMboxRecvLookAheadPeek(AR6K_DEVICE *pDev, A_UINT8 *pLookAheadBuffer, int *pLookAheadBytes);
+A_STATUS DevGMboxSetTargetInterrupt(AR6K_DEVICE *pDev, int SignalNumber, int AckTimeoutMS);
+
+#endif
+
+#endif /*AR6K_H_*/
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_events.c b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_events.c
new file mode 100644
index 000000000000..178cbb3e2962
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_events.c
@@ -0,0 +1,762 @@
+//------------------------------------------------------------------------------
+// <copyright file="ar6k_events.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// AR6K Driver layer event handling (i.e. interrupts, message polling)
+//
+// Author(s): ="Atheros"
+//==============================================================================
+
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "AR6002/hw2.0/hw/mbox_host_reg.h"
+#include "a_osapi.h"
+#include "../htc_debug.h"
+#include "hif.h"
+#include "htc_packet.h"
+#include "ar6k.h"
+
+extern void AR6KFreeIOPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket);
+extern HTC_PACKET *AR6KAllocIOPacket(AR6K_DEVICE *pDev);
+
+static A_STATUS DevServiceDebugInterrupt(AR6K_DEVICE *pDev);
+
+#define DELAY_PER_INTERVAL_MS 10 /* 10 MS delay per polling interval */
+
+/* completion routine for ALL HIF layer async I/O */
+A_STATUS DevRWCompletionHandler(void *context, A_STATUS status)
+{
+ HTC_PACKET *pPacket = (HTC_PACKET *)context;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("+DevRWCompletionHandler (Pkt:0x%X) , Status: %d \n",
+ (A_UINT32)pPacket,
+ status));
+
+ COMPLETE_HTC_PACKET(pPacket,status);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("-DevRWCompletionHandler\n"));
+
+ return A_OK;
+}
+
+/* mailbox recv message polling */
+A_STATUS DevPollMboxMsgRecv(AR6K_DEVICE *pDev,
+ A_UINT32 *pLookAhead,
+ int TimeoutMS)
+{
+ A_STATUS status = A_OK;
+ int timeout = TimeoutMS/DELAY_PER_INTERVAL_MS;
+
+ A_ASSERT(timeout > 0);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+DevPollMboxMsgRecv \n"));
+
+ while (TRUE) {
+
+ if (pDev->GetPendingEventsFunc != NULL) {
+
+ HIF_PENDING_EVENTS_INFO events;
+
+ /* the HIF layer uses a special mechanism to get events, do this
+ * synchronously */
+ status = pDev->GetPendingEventsFunc(pDev->HIFDevice,
+ &events,
+ NULL);
+ if (A_FAILED(status))
+ {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to get pending events \n"));
+ break;
+ }
+
+ if (events.Events & HIF_RECV_MSG_AVAIL)
+ {
+ /* there is a message available, the lookahead should be valid now */
+ *pLookAhead = events.LookAhead;
+
+ break;
+ }
+ } else {
+
+ /* this is the standard HIF way.... */
+ /* load the register table */
+ status = HIFReadWrite(pDev->HIFDevice,
+ HOST_INT_STATUS_ADDRESS,
+ (A_UINT8 *)&pDev->IrqProcRegisters,
+ AR6K_IRQ_PROC_REGS_SIZE,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ if (A_FAILED(status)){
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to read register table \n"));
+ break;
+ }
+
+ /* check for MBOX data and valid lookahead */
+ if (pDev->IrqProcRegisters.host_int_status & (1 << HTC_MAILBOX)) {
+ if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX))
+ {
+ /* mailbox has a message and the look ahead is valid */
+ *pLookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX];
+ break;
+ }
+ }
+
+ }
+
+ timeout--;
+
+ if (timeout <= 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" Timeout waiting for recv message \n"));
+ status = A_ERROR;
+
+ /* check if the target asserted */
+ if ( pDev->IrqProcRegisters.counter_int_status & AR6K_TARGET_DEBUG_INTR_MASK) {
+ /* target signaled an assert, process this pending interrupt
+ * this will call the target failure handler */
+ DevServiceDebugInterrupt(pDev);
+ }
+
+ break;
+ }
+
+ /* delay a little */
+ A_MDELAY(DELAY_PER_INTERVAL_MS);
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Retry Mbox Poll : %d \n",timeout));
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-DevPollMboxMsgRecv \n"));
+
+ return status;
+}
+
+static A_STATUS DevServiceCPUInterrupt(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+ A_UINT8 cpu_int_status;
+ A_UINT8 regBuffer[4];
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("CPU Interrupt\n"));
+ cpu_int_status = pDev->IrqProcRegisters.cpu_int_status &
+ pDev->IrqEnableRegisters.cpu_int_status_enable;
+ A_ASSERT(cpu_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ ("Valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
+ cpu_int_status));
+
+ /* Clear the interrupt */
+ pDev->IrqProcRegisters.cpu_int_status &= ~cpu_int_status; /* W1C */
+
+ /* set up the register transfer buffer to hit the register 4 times , this is done
+ * to make the access 4-byte aligned to mitigate issues with host bus interconnects that
+ * restrict bus transfer lengths to be a multiple of 4-bytes */
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ regBuffer[0] = cpu_int_status;
+ /* the remaining 4 values are set to zero which have no-effect */
+ regBuffer[1] = 0;
+ regBuffer[2] = 0;
+ regBuffer[3] = 0;
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ CPU_INT_STATUS_ADDRESS,
+ regBuffer,
+ 4,
+ HIF_WR_SYNC_BYTE_FIX,
+ NULL);
+
+ A_ASSERT(status == A_OK);
+ return status;
+}
+
+
+static A_STATUS DevServiceErrorInterrupt(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+ A_UINT8 error_int_status;
+ A_UINT8 regBuffer[4];
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error Interrupt\n"));
+ error_int_status = pDev->IrqProcRegisters.error_int_status & 0x0F;
+ A_ASSERT(error_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ ("Valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
+ error_int_status));
+
+ if (ERROR_INT_STATUS_WAKEUP_GET(error_int_status)) {
+ /* Wakeup */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error : Wakeup\n"));
+ }
+
+ if (ERROR_INT_STATUS_RX_UNDERFLOW_GET(error_int_status)) {
+ /* Rx Underflow */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Rx Underflow\n"));
+ }
+
+ if (ERROR_INT_STATUS_TX_OVERFLOW_GET(error_int_status)) {
+ /* Tx Overflow */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Tx Overflow\n"));
+ }
+
+ /* Clear the interrupt */
+ pDev->IrqProcRegisters.error_int_status &= ~error_int_status; /* W1C */
+
+ /* set up the register transfer buffer to hit the register 4 times , this is done
+ * to make the access 4-byte aligned to mitigate issues with host bus interconnects that
+ * restrict bus transfer lengths to be a multiple of 4-bytes */
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ regBuffer[0] = error_int_status;
+ /* the remaining 4 values are set to zero which have no-effect */
+ regBuffer[1] = 0;
+ regBuffer[2] = 0;
+ regBuffer[3] = 0;
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ ERROR_INT_STATUS_ADDRESS,
+ regBuffer,
+ 4,
+ HIF_WR_SYNC_BYTE_FIX,
+ NULL);
+
+ A_ASSERT(status == A_OK);
+ return status;
+}
+
+static A_STATUS DevServiceDebugInterrupt(AR6K_DEVICE *pDev)
+{
+ A_UINT32 dummy;
+ A_STATUS status;
+
+ /* Send a target failure event to the application */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Target debug interrupt\n"));
+
+ if (pDev->TargetFailureCallback != NULL) {
+ pDev->TargetFailureCallback(pDev->HTCContext);
+ }
+
+ if (pDev->GMboxEnabled) {
+ DevNotifyGMboxTargetFailure(pDev);
+ }
+
+ /* clear the interrupt , the debug error interrupt is
+ * counter 0 */
+ /* read counter to clear interrupt */
+ status = HIFReadWrite(pDev->HIFDevice,
+ COUNT_DEC_ADDRESS,
+ (A_UINT8 *)&dummy,
+ 4,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ A_ASSERT(status == A_OK);
+ return status;
+}
+
+static A_STATUS DevServiceCounterInterrupt(AR6K_DEVICE *pDev)
+{
+ A_UINT8 counter_int_status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Counter Interrupt\n"));
+
+ counter_int_status = pDev->IrqProcRegisters.counter_int_status &
+ pDev->IrqEnableRegisters.counter_int_status_enable;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ ("Valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
+ counter_int_status));
+
+ /* Check if the debug interrupt is pending
+ * NOTE: other modules like GMBOX may use the counter interrupt for
+ * credit flow control on other counters, we only need to check for the debug assertion
+ * counter interrupt */
+ if (counter_int_status & AR6K_TARGET_DEBUG_INTR_MASK) {
+ return DevServiceDebugInterrupt(pDev);
+ }
+
+ return A_OK;
+}
+
+/* callback when our fetch to get interrupt status registers completes */
+static void DevGetEventAsyncHandler(void *Context, HTC_PACKET *pPacket)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+ A_UINT32 lookAhead = 0;
+ A_BOOL otherInts = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGetEventAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ do {
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" GetEvents I/O request failed, status:%d \n", pPacket->Status));
+ /* bail out, don't unmask HIF interrupt */
+ break;
+ }
+
+ if (pDev->GetPendingEventsFunc != NULL) {
+ /* the HIF layer collected the information for us */
+ HIF_PENDING_EVENTS_INFO *pEvents = (HIF_PENDING_EVENTS_INFO *)pPacket->pBuffer;
+ if (pEvents->Events & HIF_RECV_MSG_AVAIL) {
+ lookAhead = pEvents->LookAhead;
+ if (0 == lookAhead) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" DevGetEventAsyncHandler1, lookAhead is zero! \n"));
+ }
+ }
+ if (pEvents->Events & HIF_OTHER_EVENTS) {
+ otherInts = TRUE;
+ }
+ } else {
+ /* standard interrupt table handling.... */
+ AR6K_IRQ_PROC_REGISTERS *pReg = (AR6K_IRQ_PROC_REGISTERS *)pPacket->pBuffer;
+ A_UINT8 host_int_status;
+
+ host_int_status = pReg->host_int_status & pDev->IrqEnableRegisters.int_status_enable;
+
+ if (host_int_status & (1 << HTC_MAILBOX)) {
+ host_int_status &= ~(1 << HTC_MAILBOX);
+ if (pReg->rx_lookahead_valid & (1 << HTC_MAILBOX)) {
+ /* mailbox has a message and the look ahead is valid */
+ lookAhead = pReg->rx_lookahead[HTC_MAILBOX];
+ if (0 == lookAhead) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" DevGetEventAsyncHandler2, lookAhead is zero! \n"));
+ }
+ }
+ }
+
+ if (host_int_status) {
+ /* there are other interrupts to handle */
+ otherInts = TRUE;
+ }
+ }
+
+ if (otherInts || (lookAhead == 0)) {
+ /* if there are other interrupts to process, we cannot do this in the async handler so
+ * ack the interrupt which will cause our sync handler to run again
+ * if however there are no more messages, we can now ack the interrupt */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ (" Acking interrupt from DevGetEventAsyncHandler (otherints:%d, lookahead:0x%X)\n",
+ otherInts, lookAhead));
+ HIFAckInterrupt(pDev->HIFDevice);
+ } else {
+ int fetched = 0;
+ A_STATUS status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ (" DevGetEventAsyncHandler : detected another message, lookahead :0x%X \n",
+ lookAhead));
+ /* lookahead is non-zero and there are no other interrupts to service,
+ * go get the next message */
+ status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, NULL, &fetched);
+
+ if (A_SUCCESS(status) && !fetched) {
+ /* HTC layer could not pull out messages due to lack of resources, stop IRQ processing */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("MessagePendingCallback did not pull any messages, force-ack \n"));
+ DevAsyncIrqProcessComplete(pDev);
+ }
+ }
+
+ } while (FALSE);
+
+ /* free this IO packet */
+ AR6KFreeIOPacket(pDev,pPacket);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGetEventAsyncHandler \n"));
+}
+
+/* called by the HTC layer when it wants us to check if the device has any more pending
+ * recv messages, this starts off a series of async requests to read interrupt registers */
+A_STATUS DevCheckPendingRecvMsgsAsync(void *context)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)context;
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket;
+
+ /* this is called in an ASYNC only context, we may NOT block, sleep or call any apis that can
+ * cause us to switch contexts */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevCheckPendingRecvMsgsAsync: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ do {
+
+ if (HIF_DEVICE_IRQ_SYNC_ONLY == pDev->HifIRQProcessingMode) {
+ /* break the async processing chain right here, no need to continue.
+ * The DevDsrHandler() will handle things in a loop when things are driven
+ * synchronously */
+ break;
+ }
+
+ /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake
+ * the target, if upper layers determine that we are in a low-throughput mode, we can
+ * rely on taking another interrupt rather than re-checking the status registers which can
+ * re-wake the target */
+ if (pDev->RecheckIRQStatusCnt == 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, re-acking HIF interrupts\n"));
+ /* ack interrupt */
+ HIFAckInterrupt(pDev->HIFDevice);
+ break;
+ }
+
+ /* first allocate one of our HTC packets we created for async I/O
+ * we reuse HTC packet definitions so that we can use the completion mechanism
+ * in DevRWCompletionHandler() */
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ /* there should be only 1 asynchronous request out at a time to read these registers
+ * so this should actually never happen */
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevGetEventAsyncHandler;
+ pIOPacket->pContext = pDev;
+
+ if (pDev->GetPendingEventsFunc) {
+ /* HIF layer has it's own mechanism, pass the IO to it.. */
+ status = pDev->GetPendingEventsFunc(pDev->HIFDevice,
+ (HIF_PENDING_EVENTS_INFO *)pIOPacket->pBuffer,
+ pIOPacket);
+
+ } else {
+ /* standard way, read the interrupt register table asynchronously again */
+ status = HIFReadWrite(pDev->HIFDevice,
+ HOST_INT_STATUS_ADDRESS,
+ pIOPacket->pBuffer,
+ AR6K_IRQ_PROC_REGS_SIZE,
+ HIF_RD_ASYNC_BYTE_INC,
+ pIOPacket);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Async IO issued to get interrupt status...\n"));
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevCheckPendingRecvMsgsAsync \n"));
+
+ return status;
+}
+
+void DevAsyncIrqProcessComplete(AR6K_DEVICE *pDev)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("DevAsyncIrqProcessComplete - forcing HIF IRQ ACK \n"));
+ HIFAckInterrupt(pDev->HIFDevice);
+}
+
+/* process pending interrupts synchronously */
+static A_STATUS ProcessPendingIRQs(AR6K_DEVICE *pDev, A_BOOL *pDone, A_BOOL *pASyncProcessing)
+{
+ A_STATUS status = A_OK;
+ A_UINT8 host_int_status = 0;
+ A_UINT32 lookAhead = 0;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ /*** NOTE: the HIF implementation guarantees that the context of this call allows
+ * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that
+ * can block or switch thread/task ontexts.
+ * This is a fully schedulable context.
+ * */
+ do {
+
+ if (pDev->IrqEnableRegisters.int_status_enable == 0) {
+ /* interrupt enables have been cleared, do not try to process any pending interrupts that
+ * may result in more bus transactions. The target may be unresponsive at this
+ * point. */
+ break;
+ }
+
+ if (pDev->GetPendingEventsFunc != NULL) {
+ HIF_PENDING_EVENTS_INFO events;
+
+ /* the HIF layer uses a special mechanism to get events
+ * get this synchronously */
+ status = pDev->GetPendingEventsFunc(pDev->HIFDevice,
+ &events,
+ NULL);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (events.Events & HIF_RECV_MSG_AVAIL) {
+ lookAhead = events.LookAhead;
+ if (0 == lookAhead) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n"));
+ }
+ }
+
+ if (!(events.Events & HIF_OTHER_EVENTS) ||
+ !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) {
+ /* no need to read the register table, no other interesting interrupts.
+ * Some interfaces (like SPI) can shadow interrupt sources without
+ * requiring the host to do a full table read */
+ break;
+ }
+
+ /* otherwise fall through and read the register table */
+ }
+
+ /*
+ * Read the first 28 bytes of the HTC register table. This will yield us
+ * the value of different int status registers and the lookahead
+ * registers.
+ * length = sizeof(int_status) + sizeof(cpu_int_status) +
+ * sizeof(error_int_status) + sizeof(counter_int_status) +
+ * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) +
+ * sizeof(hole) + sizeof(rx_lookahead) +
+ * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) +
+ * sizeof(error_status_enable) +
+ * sizeof(counter_int_status_enable);
+ *
+ */
+ status = HIFReadWrite(pDev->HIFDevice,
+ HOST_INT_STATUS_ADDRESS,
+ (A_UINT8 *)&pDev->IrqProcRegisters,
+ AR6K_IRQ_PROC_REGS_SIZE,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) {
+ DevDumpRegisters(pDev,
+ &pDev->IrqProcRegisters,
+ &pDev->IrqEnableRegisters);
+ }
+
+ /* Update only those registers that are enabled */
+ host_int_status = pDev->IrqProcRegisters.host_int_status &
+ pDev->IrqEnableRegisters.int_status_enable;
+
+ if (NULL == pDev->GetPendingEventsFunc) {
+ /* only look at mailbox status if the HIF layer did not provide this function,
+ * on some HIF interfaces reading the RX lookahead is not valid to do */
+ if (host_int_status & (1 << HTC_MAILBOX)) {
+ /* mask out pending mailbox value, we use "lookAhead" as the real flag for
+ * mailbox processing below */
+ host_int_status &= ~(1 << HTC_MAILBOX);
+ if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) {
+ /* mailbox has a message and the look ahead is valid */
+ lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX];
+ if (0 == lookAhead) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n"));
+ }
+ }
+ }
+ } else {
+ /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/
+ host_int_status &= ~(1 << HTC_MAILBOX);
+ }
+
+ if (pDev->GMboxEnabled) {
+ /*call GMBOX layer to process any interrupts of interest */
+ status = DevCheckGMboxInterrupts(pDev);
+ }
+
+ } while (FALSE);
+
+
+ do {
+
+ /* did the interrupt status fetches succeed? */
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if ((0 == host_int_status) && (0 == lookAhead)) {
+ /* nothing to process, the caller can use this to break out of a loop */
+ *pDone = TRUE;
+ break;
+ }
+
+ if (lookAhead != 0) {
+ int fetched = 0;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead));
+ /* Mailbox Interrupt, the HTC layer may issue async requests to empty the
+ * mailbox...
+ * When emptying the recv mailbox we use the async handler above called from the
+ * completion routine of the callers read request. This can improve performance
+ * by reducing context switching when we rapidly pull packets */
+ status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, pASyncProcessing, &fetched);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (!fetched) {
+ /* HTC could not pull any messages out due to lack of resources */
+ /* force DSR handler to ack the interrupt */
+ *pASyncProcessing = FALSE;
+ pDev->RecheckIRQStatusCnt = 0;
+ }
+ }
+
+ /* now handle the rest of them */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n",
+ host_int_status));
+
+ if (HOST_INT_STATUS_CPU_GET(host_int_status)) {
+ /* CPU Interrupt */
+ status = DevServiceCPUInterrupt(pDev);
+ if (A_FAILED(status)){
+ break;
+ }
+ }
+
+ if (HOST_INT_STATUS_ERROR_GET(host_int_status)) {
+ /* Error Interrupt */
+ status = DevServiceErrorInterrupt(pDev);
+ if (A_FAILED(status)){
+ break;
+ }
+ }
+
+ if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) {
+ /* Counter Interrupt */
+ status = DevServiceCounterInterrupt(pDev);
+ if (A_FAILED(status)){
+ break;
+ }
+ }
+
+ } while (FALSE);
+
+ /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake
+ * the target, if upper layers determine that we are in a low-throughput mode, we can
+ * rely on taking another interrupt rather than re-checking the status registers which can
+ * re-wake the target.
+ *
+ * NOTE : for host interfaces that use the special GetPendingEventsFunc, this optimization cannot
+ * be used due to possible side-effects. For example, SPI requires the host to drain all
+ * messages from the mailbox before exiting the ISR routine. */
+ if (!(*pASyncProcessing) && (pDev->RecheckIRQStatusCnt == 0) && (pDev->GetPendingEventsFunc == NULL)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, forcing done \n"));
+ *pDone = TRUE;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n",
+ *pDone, *pASyncProcessing, status));
+
+ return status;
+}
+
+
+/* Synchronousinterrupt handler, this handler kicks off all interrupt processing.*/
+A_STATUS DevDsrHandler(void *context)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)context;
+ A_STATUS status = A_OK;
+ A_BOOL done = FALSE;
+ A_BOOL asyncProc = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevDsrHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ /* reset the recv counter that tracks when we need to yield from the DSR */
+ pDev->CurrentDSRRecvCount = 0;
+ /* reset counter used to flag a re-scan of IRQ status registers on the target */
+ pDev->RecheckIRQStatusCnt = 0;
+
+ while (!done) {
+ status = ProcessPendingIRQs(pDev, &done, &asyncProc);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (HIF_DEVICE_IRQ_SYNC_ONLY == pDev->HifIRQProcessingMode) {
+ /* the HIF layer does not allow async IRQ processing, override the asyncProc flag */
+ asyncProc = FALSE;
+ /* this will cause us to re-enter ProcessPendingIRQ() and re-read interrupt status registers.
+ * this has a nice side effect of blocking us until all async read requests are completed.
+ * This behavior is required on some HIF implementations that do not allow ASYNC
+ * processing in interrupt handlers (like Windows CE) */
+
+ if (pDev->DSRCanYield && DEV_CHECK_RECV_YIELD(pDev)) {
+ /* ProcessPendingIRQs() pulled enough recv messages to satisfy the yield count, stop
+ * checking for more messages and return */
+ break;
+ }
+ }
+
+ if (asyncProc) {
+ /* the function performed some async I/O for performance, we
+ need to exit the ISR immediately, the check below will prevent the interrupt from being
+ Ack'd while we handle it asynchronously */
+ break;
+ }
+
+ }
+
+ if (A_SUCCESS(status) && !asyncProc) {
+ /* Ack the interrupt only if :
+ * 1. we did not get any errors in processing interrupts
+ * 2. there are no outstanding async processing requests */
+ if (pDev->DSRCanYield) {
+ /* if the DSR can yield do not ACK the interrupt, there could be more pending messages.
+ * The HIF layer must ACK the interrupt on behalf of HTC */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Yield in effect (cur RX count: %d) \n", pDev->CurrentDSRRecvCount));
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Acking interrupt from DevDsrHandler \n"));
+ HIFAckInterrupt(pDev->HIFDevice);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevDsrHandler \n"));
+ return status;
+}
+
+void DumpAR6KDevState(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+ AR6K_IRQ_PROC_REGISTERS procRegs;
+
+ LOCK_AR6K(pDev);
+ /* copy into our temp area */
+ A_MEMCPY(&regs,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+ UNLOCK_AR6K(pDev);
+
+ /* load the register table from the device */
+ status = HIFReadWrite(pDev->HIFDevice,
+ HOST_INT_STATUS_ADDRESS,
+ (A_UINT8 *)&procRegs,
+ AR6K_IRQ_PROC_REGS_SIZE,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("DumpAR6KDevState : Failed to read register table (%d) \n",status));
+ return;
+ }
+
+ DevDumpRegisters(pDev,&procRegs,&regs);
+
+ if (pDev->GMboxInfo.pStateDumpCallback != NULL) {
+ pDev->GMboxInfo.pStateDumpCallback(pDev->GMboxInfo.pProtocolContext);
+ }
+
+ /* dump any bus state at the HIF layer */
+ HIFConfigureDevice(pDev->HIFDevice,HIF_DEVICE_DEBUG_BUS_STATE,NULL,0);
+
+}
+
+
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox.c b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox.c
new file mode 100644
index 000000000000..f1408082a809
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox.c
@@ -0,0 +1,752 @@
+//------------------------------------------------------------------------------
+// <copyright file="ar6k_gmbox.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Generic MBOX API implementation
+//
+// Author(s): ="Atheros"
+//==============================================================================
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "a_osapi.h"
+#include "../htc_debug.h"
+#include "hif.h"
+#include "htc_packet.h"
+#include "ar6k.h"
+#include "hw/mbox_host_reg.h"
+#include "gmboxif.h"
+
+/*
+ * This file provides management functions and a toolbox for GMBOX protocol modules.
+ * Only one protocol module can be installed at a time. The determination of which protocol
+ * module is installed is determined at compile time.
+ *
+ */
+#ifdef ATH_AR6K_ENABLE_GMBOX
+ /* GMBOX definitions */
+#define GMBOX_INT_STATUS_ENABLE_REG 0x488
+#define GMBOX_INT_STATUS_RX_DATA (1 << 0)
+#define GMBOX_INT_STATUS_TX_OVERFLOW (1 << 1)
+#define GMBOX_INT_STATUS_RX_OVERFLOW (1 << 2)
+
+#define GMBOX_LOOKAHEAD_MUX_REG 0x498
+#define GMBOX_LA_MUX_OVERRIDE_2_3 (1 << 0)
+
+#define AR6K_GMBOX_CREDIT_DEC_ADDRESS (COUNT_DEC_ADDRESS + 4 * AR6K_GMBOX_CREDIT_COUNTER)
+#define AR6K_GMBOX_CREDIT_SIZE_ADDRESS (COUNT_ADDRESS + AR6K_GMBOX_CREDIT_SIZE_COUNTER)
+
+
+ /* external APIs for allocating and freeing internal I/O packets to handle ASYNC I/O */
+extern void AR6KFreeIOPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket);
+extern HTC_PACKET *AR6KAllocIOPacket(AR6K_DEVICE *pDev);
+
+
+/* callback when our fetch to enable/disable completes */
+static void DevGMboxIRQActionAsyncHandler(void *Context, HTC_PACKET *pPacket)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxIRQActionAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("IRQAction Operation (%d) failed! status:%d \n", pPacket->PktInfo.AsRx.HTCRxFlags,pPacket->Status));
+ }
+ /* free this IO packet */
+ AR6KFreeIOPacket(pDev,pPacket);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxIRQActionAsyncHandler \n"));
+}
+
+static A_STATUS DevGMboxCounterEnableDisable(AR6K_DEVICE *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, A_BOOL AsyncMode)
+{
+ A_STATUS status = A_OK;
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+ HTC_PACKET *pIOPacket = NULL;
+
+ LOCK_AR6K(pDev);
+
+ if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
+ pDev->GMboxInfo.CreditCountIRQEnabled = TRUE;
+ pDev->IrqEnableRegisters.counter_int_status_enable |=
+ COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER);
+ pDev->IrqEnableRegisters.int_status_enable |= INT_STATUS_ENABLE_COUNTER_SET(0x01);
+ } else {
+ pDev->GMboxInfo.CreditCountIRQEnabled = FALSE;
+ pDev->IrqEnableRegisters.counter_int_status_enable &=
+ ~(COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER));
+ }
+ /* copy into our temp area */
+ A_MEMCPY(&regs,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+
+ UNLOCK_AR6K(pDev);
+
+ do {
+
+ if (AsyncMode) {
+
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* copy values to write to our async I/O buffer */
+ A_MEMCPY(pIOPacket->pBuffer,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
+
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
+ pIOPacket->pContext = pDev;
+ pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
+ /* write it out asynchronously */
+ HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ pIOPacket->pBuffer,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_ASYNC_BYTE_INC,
+ pIOPacket);
+
+ pIOPacket = NULL;
+ break;
+ }
+
+ /* if we get here we are doing it synchronously */
+ status = HIFReadWrite(pDev->HIFDevice,
+ INT_STATUS_ENABLE_ADDRESS,
+ &regs.int_status_enable,
+ AR6K_IRQ_ENABLE_REGS_SIZE,
+ HIF_WR_SYNC_BYTE_INC,
+ NULL);
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
+ } else {
+ if (!AsyncMode) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ (" IRQAction Operation (%d) success \n", IrqAction));
+ }
+ }
+
+ if (pIOPacket != NULL) {
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ return status;
+}
+
+
+A_STATUS DevGMboxIRQAction(AR6K_DEVICE *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, A_BOOL AsyncMode)
+{
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket = NULL;
+ A_UINT8 GMboxIntControl[4];
+
+ if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
+ return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_ENABLE, AsyncMode);
+ } else if(GMBOX_CREDIT_IRQ_DISABLE == IrqAction) {
+ return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
+ }
+
+ if (GMBOX_DISABLE_ALL == IrqAction) {
+ /* disable credit IRQ, those are on a different set of registers */
+ DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
+ }
+
+ /* take the lock to protect interrupt enable shadows */
+ LOCK_AR6K(pDev);
+
+ switch (IrqAction) {
+
+ case GMBOX_DISABLE_ALL:
+ pDev->GMboxControlRegisters.int_status_enable = 0;
+ break;
+ case GMBOX_ERRORS_IRQ_ENABLE:
+ pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_TX_OVERFLOW |
+ GMBOX_INT_STATUS_RX_OVERFLOW;
+ break;
+ case GMBOX_RECV_IRQ_ENABLE:
+ pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_RX_DATA;
+ break;
+ case GMBOX_RECV_IRQ_DISABLE:
+ pDev->GMboxControlRegisters.int_status_enable &= ~GMBOX_INT_STATUS_RX_DATA;
+ break;
+ case GMBOX_ACTION_NONE:
+ default:
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ GMboxIntControl[0] = pDev->GMboxControlRegisters.int_status_enable;
+ GMboxIntControl[1] = GMboxIntControl[0];
+ GMboxIntControl[2] = GMboxIntControl[0];
+ GMboxIntControl[3] = GMboxIntControl[0];
+
+ UNLOCK_AR6K(pDev);
+
+ do {
+
+ if (AsyncMode) {
+
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* copy values to write to our async I/O buffer */
+ A_MEMCPY(pIOPacket->pBuffer,GMboxIntControl,sizeof(GMboxIntControl));
+
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
+ pIOPacket->pContext = pDev;
+ pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
+ /* write it out asynchronously */
+ HIFReadWrite(pDev->HIFDevice,
+ GMBOX_INT_STATUS_ENABLE_REG,
+ pIOPacket->pBuffer,
+ sizeof(GMboxIntControl),
+ HIF_WR_ASYNC_BYTE_FIX,
+ pIOPacket);
+ pIOPacket = NULL;
+ break;
+ }
+
+ /* if we get here we are doing it synchronously */
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ GMBOX_INT_STATUS_ENABLE_REG,
+ GMboxIntControl,
+ sizeof(GMboxIntControl),
+ HIF_WR_SYNC_BYTE_FIX,
+ NULL);
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
+ } else {
+ if (!AsyncMode) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
+ (" IRQAction Operation (%d) success \n", IrqAction));
+ }
+ }
+
+ if (pIOPacket != NULL) {
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ return status;
+}
+
+void DevCleanupGMbox(AR6K_DEVICE *pDev)
+{
+ if (pDev->GMboxEnabled) {
+ pDev->GMboxEnabled = FALSE;
+ GMboxProtocolUninstall(pDev);
+ }
+}
+
+A_STATUS DevSetupGMbox(AR6K_DEVICE *pDev)
+{
+ A_STATUS status = A_OK;
+ A_UINT8 muxControl[4];
+
+ do {
+
+ if (0 == pDev->MailBoxInfo.GMboxAddress) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,(" GMBOX Advertised: Address:0x%X , size:%d \n",
+ pDev->MailBoxInfo.GMboxAddress, pDev->MailBoxInfo.GMboxSize));
+
+ status = DevGMboxIRQAction(pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* write to mailbox look ahead mux control register, we want the
+ * GMBOX lookaheads to appear on lookaheads 2 and 3
+ * the register is 1-byte wide so we need to hit it 4 times to align the operation
+ * to 4-bytes */
+ muxControl[0] = GMBOX_LA_MUX_OVERRIDE_2_3;
+ muxControl[1] = GMBOX_LA_MUX_OVERRIDE_2_3;
+ muxControl[2] = GMBOX_LA_MUX_OVERRIDE_2_3;
+ muxControl[3] = GMBOX_LA_MUX_OVERRIDE_2_3;
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ GMBOX_LOOKAHEAD_MUX_REG,
+ muxControl,
+ sizeof(muxControl),
+ HIF_WR_SYNC_BYTE_FIX, /* hit this register 4 times */
+ NULL);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ status = GMboxProtocolInstall(pDev);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ pDev->GMboxEnabled = TRUE;
+
+ } while (FALSE);
+
+ return status;
+}
+
+A_STATUS DevCheckGMboxInterrupts(AR6K_DEVICE *pDev)
+{
+ A_STATUS status = A_OK;
+ A_UINT8 counter_int_status;
+ int credits;
+ A_UINT8 host_int_status2;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("+DevCheckGMboxInterrupts \n"));
+
+ /* the caller guarantees that this is a context that allows for blocking I/O */
+
+ do {
+
+ host_int_status2 = pDev->IrqProcRegisters.host_int_status2 &
+ pDev->GMboxControlRegisters.int_status_enable;
+
+ if (host_int_status2 & GMBOX_INT_STATUS_TX_OVERFLOW) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : TX Overflow \n"));
+ status = A_ECOMM;
+ }
+
+ if (host_int_status2 & GMBOX_INT_STATUS_RX_OVERFLOW) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : RX Overflow \n"));
+ status = A_ECOMM;
+ }
+
+ if (A_FAILED(status)) {
+ if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
+ pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, status);
+ }
+ break;
+ }
+
+ if (host_int_status2 & GMBOX_INT_STATUS_RX_DATA) {
+ if (pDev->IrqProcRegisters.gmbox_rx_avail > 0) {
+ A_ASSERT(pDev->GMboxInfo.pMessagePendingCallBack != NULL);
+ status = pDev->GMboxInfo.pMessagePendingCallBack(
+ pDev->GMboxInfo.pProtocolContext,
+ (A_UINT8 *)&pDev->IrqProcRegisters.rx_gmbox_lookahead_alias[0],
+ pDev->IrqProcRegisters.gmbox_rx_avail);
+ }
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ counter_int_status = pDev->IrqProcRegisters.counter_int_status &
+ pDev->IrqEnableRegisters.counter_int_status_enable;
+
+ /* check if credit interrupt is pending */
+ if (counter_int_status & (COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER))) {
+
+ /* do synchronous read */
+ status = DevGMboxReadCreditCounter(pDev, PROC_IO_SYNC, &credits);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ A_ASSERT(pDev->GMboxInfo.pCreditsPendingCallback != NULL);
+ status = pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
+ credits,
+ pDev->GMboxInfo.CreditCountIRQEnabled);
+ }
+
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("-DevCheckGMboxInterrupts (%d) \n",status));
+
+ return status;
+}
+
+
+A_STATUS DevGMboxWrite(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 WriteLength)
+{
+ A_UINT32 paddedLength;
+ A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
+ A_STATUS status;
+ A_UINT32 address;
+
+ /* adjust the length to be a multiple of block size if appropriate */
+ paddedLength = DEV_CALC_SEND_PADDED_LEN(pDev, WriteLength);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("DevGMboxWrite, Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ WriteLength,
+ pDev->MailBoxInfo.GMboxAddress,
+ sync ? "SYNC" : "ASYNC"));
+
+ /* last byte of packet has to hit the EOM marker */
+ address = pDev->MailBoxInfo.GMboxAddress + pDev->MailBoxInfo.GMboxSize - paddedLength;
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ address,
+ pPacket->pBuffer,
+ paddedLength, /* the padded length */
+ sync ? HIF_WR_SYNC_BLOCK_INC : HIF_WR_ASYNC_BLOCK_INC,
+ sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
+
+ if (sync) {
+ pPacket->Status = status;
+ } else {
+ if (status == A_PENDING) {
+ status = A_OK;
+ }
+ }
+
+ return status;
+}
+
+A_STATUS DevGMboxRead(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 ReadLength)
+{
+
+ A_UINT32 paddedLength;
+ A_STATUS status;
+ A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
+
+ /* adjust the length to be a multiple of block size if appropriate */
+ paddedLength = DEV_CALC_RECV_PADDED_LEN(pDev, ReadLength);
+
+ if (paddedLength > pPacket->BufferLength) {
+ A_ASSERT(FALSE);
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("DevGMboxRead, Not enough space for padlen:%d recvlen:%d bufferlen:%d \n",
+ paddedLength,ReadLength,pPacket->BufferLength));
+ if (pPacket->Completion != NULL) {
+ COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
+ return A_OK;
+ }
+ return A_EINVAL;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("DevGMboxRead (0x%X : hdr:0x%X) Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ (A_UINT32)pPacket, pPacket->PktInfo.AsRx.ExpectedHdr,
+ paddedLength,
+ pDev->MailBoxInfo.GMboxAddress,
+ sync ? "SYNC" : "ASYNC"));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ pDev->MailBoxInfo.GMboxAddress,
+ pPacket->pBuffer,
+ paddedLength,
+ sync ? HIF_RD_SYNC_BLOCK_FIX : HIF_RD_ASYNC_BLOCK_FIX,
+ sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
+
+ if (sync) {
+ pPacket->Status = status;
+ }
+
+ return status;
+}
+
+
+static int ProcessCreditCounterReadBuffer(A_UINT8 *pBuffer, int Length)
+{
+ int credits = 0;
+
+ /* theory of how this works:
+ * We read the credit decrement register multiple times on a byte-wide basis.
+ * The number of times (32) aligns the I/O operation to be a multiple of 4 bytes and provides a
+ * reasonable chance to acquire "all" pending credits in a single I/O operation.
+ *
+ * Once we obtain the filled buffer, we can walk through it looking for credit decrement transitions.
+ * Each non-zero byte represents a single credit decrement (which is a credit given back to the host)
+ * For example if the target provides 3 credits and added 4 more during the 32-byte read operation the following
+ * pattern "could" appear:
+ *
+ * 0x3 0x2 0x1 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x1 0x0 0x1 0x0 ......rest zeros
+ * <---------> <----------------------------->
+ * \_ credits aleady there \_ target adding 4 more credits
+ *
+ * The total available credits would be 7, since there are 7 non-zero bytes in the buffer.
+ *
+ * */
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ DebugDumpBytes(pBuffer, Length, "GMBOX Credit read buffer");
+ }
+
+ while (Length) {
+ if (*pBuffer != 0) {
+ credits++;
+ }
+ Length--;
+ pBuffer++;
+ }
+
+ return credits;
+}
+
+
+/* callback when our fetch to enable/disable completes */
+static void DevGMboxReadCreditsAsyncHandler(void *Context, HTC_PACKET *pPacket)
+{
+ AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxReadCreditsAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Read Credit Operation failed! status:%d \n", pPacket->Status));
+ } else {
+ int credits = 0;
+ credits = ProcessCreditCounterReadBuffer(pPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
+ pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
+ credits,
+ pDev->GMboxInfo.CreditCountIRQEnabled);
+
+
+ }
+ /* free this IO packet */
+ AR6KFreeIOPacket(pDev,pPacket);
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxReadCreditsAsyncHandler \n"));
+}
+
+A_STATUS DevGMboxReadCreditCounter(AR6K_DEVICE *pDev, A_BOOL AsyncMode, int *pCredits)
+{
+ A_STATUS status = A_OK;
+ HTC_PACKET *pIOPacket = NULL;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+DevGMboxReadCreditCounter (%s) \n", AsyncMode ? "ASYNC" : "SYNC"));
+
+ do {
+
+ pIOPacket = AR6KAllocIOPacket(pDev);
+
+ if (NULL == pIOPacket) {
+ status = A_NO_MEMORY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ A_MEMZERO(pIOPacket->pBuffer,AR6K_REG_IO_BUFFER_SIZE);
+
+ if (AsyncMode) {
+ /* stick in our completion routine when the I/O operation completes */
+ pIOPacket->Completion = DevGMboxReadCreditsAsyncHandler;
+ pIOPacket->pContext = pDev;
+ /* read registers asynchronously */
+ HIFReadWrite(pDev->HIFDevice,
+ AR6K_GMBOX_CREDIT_DEC_ADDRESS,
+ pIOPacket->pBuffer,
+ AR6K_REG_IO_BUFFER_SIZE, /* hit the register multiple times */
+ HIF_RD_ASYNC_BYTE_FIX,
+ pIOPacket);
+ pIOPacket = NULL;
+ break;
+ }
+
+ pIOPacket->Completion = NULL;
+ /* if we get here we are doing it synchronously */
+ status = HIFReadWrite(pDev->HIFDevice,
+ AR6K_GMBOX_CREDIT_DEC_ADDRESS,
+ pIOPacket->pBuffer,
+ AR6K_REG_IO_BUFFER_SIZE,
+ HIF_RD_SYNC_BYTE_FIX,
+ NULL);
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" DevGMboxReadCreditCounter failed! status:%d \n", status));
+ }
+
+ if (pIOPacket != NULL) {
+ if (A_SUCCESS(status)) {
+ /* sync mode processing */
+ *pCredits = ProcessCreditCounterReadBuffer(pIOPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
+ }
+ AR6KFreeIOPacket(pDev,pIOPacket);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-DevGMboxReadCreditCounter (%s) (%d) \n",
+ AsyncMode ? "ASYNC" : "SYNC", status));
+
+ return status;
+}
+
+A_STATUS DevGMboxReadCreditSize(AR6K_DEVICE *pDev, int *pCreditSize)
+{
+ A_STATUS status;
+ A_UINT8 buffer[4];
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ AR6K_GMBOX_CREDIT_SIZE_ADDRESS,
+ buffer,
+ sizeof(buffer),
+ HIF_RD_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
+ NULL);
+
+ if (A_SUCCESS(status)) {
+ if (buffer[0] == 0) {
+ *pCreditSize = 256;
+ } else {
+ *pCreditSize = buffer[0];
+ }
+
+ }
+
+ return status;
+}
+
+void DevNotifyGMboxTargetFailure(AR6K_DEVICE *pDev)
+{
+ /* Target ASSERTED!!! */
+ if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
+ pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, A_HARDWARE);
+ }
+}
+
+A_STATUS DevGMboxRecvLookAheadPeek(AR6K_DEVICE *pDev, A_UINT8 *pLookAheadBuffer, int *pLookAheadBytes)
+{
+
+ A_STATUS status = A_OK;
+ AR6K_IRQ_PROC_REGISTERS procRegs;
+ int maxCopy;
+
+ do {
+ /* on entry the caller provides the length of the lookahead buffer */
+ if (*pLookAheadBytes > sizeof(procRegs.rx_gmbox_lookahead_alias)) {
+ A_ASSERT(FALSE);
+ status = A_EINVAL;
+ break;
+ }
+
+ maxCopy = *pLookAheadBytes;
+ *pLookAheadBytes = 0;
+ /* load the register table from the device */
+ status = HIFReadWrite(pDev->HIFDevice,
+ HOST_INT_STATUS_ADDRESS,
+ (A_UINT8 *)&procRegs,
+ AR6K_IRQ_PROC_REGS_SIZE,
+ HIF_RD_SYNC_BYTE_INC,
+ NULL);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("DevGMboxRecvLookAheadPeek : Failed to read register table (%d) \n",status));
+ break;
+ }
+
+ if (procRegs.gmbox_rx_avail > 0) {
+ int bytes = procRegs.gmbox_rx_avail > maxCopy ? maxCopy : procRegs.gmbox_rx_avail;
+ A_MEMCPY(pLookAheadBuffer,&procRegs.rx_gmbox_lookahead_alias[0],bytes);
+ *pLookAheadBytes = bytes;
+ }
+
+ } while (FALSE);
+
+ return status;
+}
+
+A_STATUS DevGMboxSetTargetInterrupt(AR6K_DEVICE *pDev, int Signal, int AckTimeoutMS)
+{
+ A_STATUS status = A_OK;
+ int i;
+ A_UINT8 buffer[4];
+
+ A_MEMZERO(buffer, sizeof(buffer));
+
+ do {
+
+ if (Signal >= MBOX_SIG_HCI_BRIDGE_MAX) {
+ status = A_EINVAL;
+ break;
+ }
+
+ /* set the last buffer to do the actual signal trigger */
+ buffer[3] = (1 << Signal);
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ INT_WLAN_ADDRESS,
+ buffer,
+ sizeof(buffer),
+ HIF_WR_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
+ NULL);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ } while (FALSE);
+
+
+ if (A_SUCCESS(status)) {
+ /* now read back the register to see if the bit cleared */
+ while (AckTimeoutMS) {
+ status = HIFReadWrite(pDev->HIFDevice,
+ INT_WLAN_ADDRESS,
+ buffer,
+ sizeof(buffer),
+ HIF_RD_SYNC_BYTE_FIX,
+ NULL);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ for (i = 0; i < sizeof(buffer); i++) {
+ if (buffer[i] & (1 << Signal)) {
+ /* bit is still set */
+ break;
+ }
+ }
+
+ if (i >= sizeof(buffer)) {
+ /* done */
+ break;
+ }
+
+ AckTimeoutMS--;
+ A_MDELAY(1);
+ }
+
+ if (0 == AckTimeoutMS) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("DevGMboxSetTargetInterrupt : Ack Timed-out (sig:%d) \n",Signal));
+ status = A_ERROR;
+ }
+ }
+
+ return status;
+
+}
+
+#endif //ATH_AR6K_ENABLE_GMBOX
+
+
+
+
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c
new file mode 100644
index 000000000000..ec5c1dc18ed2
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c
@@ -0,0 +1,1255 @@
+//------------------------------------------------------------------------------
+// <copyright file="ar6k_prot_hciUart.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Protocol module for use in bridging HCI-UART packets over the GMBOX interface
+//
+// Author(s): ="Atheros"
+//==============================================================================
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "a_osapi.h"
+#include "../htc_debug.h"
+#include "hif.h"
+#include "htc_packet.h"
+#include "ar6k.h"
+#include "hci_transport_api.h"
+#include "gmboxif.h"
+#include "ar6000_diag.h"
+#include "hw/apb_map.h"
+#include "hw/mbox_reg.h"
+
+#ifdef ATH_AR6K_ENABLE_GMBOX
+#define HCI_UART_COMMAND_PKT 0x01
+#define HCI_UART_ACL_PKT 0x02
+#define HCI_UART_SCO_PKT 0x03
+#define HCI_UART_EVENT_PKT 0x04
+
+#define HCI_RECV_WAIT_BUFFERS (1 << 0)
+
+#define HCI_SEND_WAIT_CREDITS (1 << 0)
+
+#define HCI_UART_BRIDGE_CREDIT_SIZE 128
+
+#define CREDIT_POLL_COUNT 256
+
+#define HCI_DELAY_PER_INTERVAL_MS 10
+#define BTON_TIMEOUT_MS 500
+#define BTOFF_TIMEOUT_MS 500
+#define BAUD_TIMEOUT_MS 1
+
+typedef struct {
+ HCI_TRANSPORT_CONFIG_INFO HCIConfig;
+ A_BOOL HCIAttached;
+ A_BOOL HCIStopped;
+ A_UINT32 RecvStateFlags;
+ A_UINT32 SendStateFlags;
+ HCI_TRANSPORT_PACKET_TYPE WaitBufferType;
+ HTC_PACKET_QUEUE SendQueue; /* write queue holding HCI Command and ACL packets */
+ HTC_PACKET_QUEUE HCIACLRecvBuffers; /* recv queue holding buffers for incomming ACL packets */
+ HTC_PACKET_QUEUE HCIEventBuffers; /* recv queue holding buffers for incomming event packets */
+ AR6K_DEVICE *pDev;
+ A_MUTEX_T HCIRxLock;
+ A_MUTEX_T HCITxLock;
+ int CreditsMax;
+ int CreditsConsumed;
+ int CreditsAvailable;
+ int CreditSize;
+ int CreditsCurrentSeek;
+ int SendProcessCount;
+} GMBOX_PROTO_HCI_UART;
+
+#define LOCK_HCI_RX(t) A_MUTEX_LOCK(&(t)->HCIRxLock);
+#define UNLOCK_HCI_RX(t) A_MUTEX_UNLOCK(&(t)->HCIRxLock);
+#define LOCK_HCI_TX(t) A_MUTEX_LOCK(&(t)->HCITxLock);
+#define UNLOCK_HCI_TX(t) A_MUTEX_UNLOCK(&(t)->HCITxLock);
+
+#define DO_HCI_RECV_INDICATION(p,pt) \
+{ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI: Indicate Recv on packet:0x%X status:%d len:%d type:%d \n", \
+ (A_UINT32)(pt),(pt)->Status, A_SUCCESS((pt)->Status) ? (pt)->ActualLength : 0, HCI_GET_PACKET_TYPE(pt))); \
+ (p)->HCIConfig.pHCIPktRecv((p)->HCIConfig.pContext, (pt)); \
+}
+
+#define DO_HCI_SEND_INDICATION(p,pt) \
+{ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Indicate Send on packet:0x%X status:%d type:%d \n", \
+ (A_UINT32)(pt),(pt)->Status,HCI_GET_PACKET_TYPE(pt))); \
+ (p)->HCIConfig.pHCISendComplete((p)->HCIConfig.pContext, (pt)); \
+}
+
+static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous);
+
+static void HCIUartCleanup(GMBOX_PROTO_HCI_UART *pProtocol)
+{
+ A_ASSERT(pProtocol != NULL);
+
+ A_MUTEX_DELETE(&pProtocol->HCIRxLock);
+ A_MUTEX_DELETE(&pProtocol->HCITxLock);
+
+ A_FREE(pProtocol);
+}
+
+static A_STATUS InitTxCreditState(GMBOX_PROTO_HCI_UART *pProt)
+{
+ A_STATUS status;
+ int credits;
+ int creditPollCount = CREDIT_POLL_COUNT;
+ A_BOOL gotCredits = FALSE;
+
+ pProt->CreditsConsumed = 0;
+
+ do {
+
+ if (pProt->CreditsMax != 0) {
+ /* we can only call this only once per target reset */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI: InitTxCreditState - already called! \n"));
+ A_ASSERT(FALSE);
+ status = A_EINVAL;
+ break;
+ }
+
+ /* read the credit counter. At startup the target will set the credit counter
+ * to the max available, we read this in a loop because it may take
+ * multiple credit counter reads to get all credits */
+
+ while (creditPollCount) {
+
+ credits = 0;
+
+ status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (!gotCredits && (0 == credits)) {
+ creditPollCount--;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: credit is 0, retrying (%d) \n",creditPollCount));
+ A_MDELAY(HCI_DELAY_PER_INTERVAL_MS);
+ continue;
+ } else {
+ gotCredits = TRUE;
+ }
+
+ if (0 == credits) {
+ break;
+ }
+
+ pProt->CreditsMax += credits;
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (0 == creditPollCount) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("** HCI : Failed to get credits! GMBOX Target was not available \n"));
+ status = A_ERROR;
+ break;
+ }
+
+ /* now get the size */
+ status = DevGMboxReadCreditSize(pProt->pDev, &pProt->CreditSize);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ } while (FALSE);
+
+ if (A_SUCCESS(status)) {
+ pProt->CreditsAvailable = pProt->CreditsMax;
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI : InitTxCreditState - credits avail: %d, size: %d \n",
+ pProt->CreditsAvailable, pProt->CreditSize));
+ }
+
+ return status;
+}
+
+static A_STATUS CreditsAvailableCallback(void *pContext, int Credits, A_BOOL CreditIRQEnabled)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
+ A_BOOL enableCreditIrq = FALSE;
+ A_BOOL disableCreditIrq = FALSE;
+ A_BOOL doPendingSends = FALSE;
+ A_STATUS status = A_OK;
+
+ /** this callback is called under 2 conditions:
+ * 1. The credit IRQ interrupt was enabled and signaled.
+ * 2. A credit counter read completed.
+ *
+ * The function must not assume that the calling context can block !
+ */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback (Credits:%d, IRQ:%s) \n",
+ Credits, CreditIRQEnabled ? "ON" : "OFF"));
+
+ LOCK_HCI_RX(pProt);
+
+ do {
+
+ if (0 == Credits) {
+ if (!CreditIRQEnabled) {
+ /* enable credit IRQ */
+ enableCreditIrq = TRUE;
+ }
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: current credit state, consumed:%d available:%d max:%d seek:%d\n",
+ pProt->CreditsConsumed,
+ pProt->CreditsAvailable,
+ pProt->CreditsMax,
+ pProt->CreditsCurrentSeek));
+
+ pProt->CreditsAvailable += Credits;
+ A_ASSERT(pProt->CreditsAvailable <= pProt->CreditsMax);
+ pProt->CreditsConsumed -= Credits;
+ A_ASSERT(pProt->CreditsConsumed >= 0);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state, consumed:%d available:%d max:%d seek:%d\n",
+ pProt->CreditsConsumed,
+ pProt->CreditsAvailable,
+ pProt->CreditsMax,
+ pProt->CreditsCurrentSeek));
+
+ if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
+ /* we have enough credits to fullfill at least 1 packet waiting in the queue */
+ pProt->CreditsCurrentSeek = 0;
+ pProt->SendStateFlags &= ~HCI_SEND_WAIT_CREDITS;
+ doPendingSends = TRUE;
+ if (CreditIRQEnabled) {
+ /* credit IRQ was enabled, we shouldn't need it anymore */
+ disableCreditIrq = TRUE;
+ }
+ } else {
+ /* not enough credits yet, enable credit IRQ if we haven't already */
+ if (!CreditIRQEnabled) {
+ enableCreditIrq = TRUE;
+ }
+ }
+
+ } while (FALSE);
+
+ UNLOCK_HCI_RX(pProt);
+
+ if (enableCreditIrq) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Enabling credit count IRQ...\n"));
+ /* must use async only */
+ status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_ENABLE, PROC_IO_ASYNC);
+ } else if (disableCreditIrq) {
+ /* must use async only */
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Disabling credit count IRQ...\n"));
+ status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_DISABLE, PROC_IO_ASYNC);
+ }
+
+ if (doPendingSends) {
+ HCITrySend(pProt, NULL, FALSE);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback \n"));
+ return status;
+}
+
+static INLINE void NotifyTransportFailure(GMBOX_PROTO_HCI_UART *pProt, A_STATUS status)
+{
+ if (pProt->HCIConfig.TransportFailure != NULL) {
+ pProt->HCIConfig.TransportFailure(pProt->HCIConfig.pContext, status);
+ }
+}
+
+static void FailureCallback(void *pContext, A_STATUS Status)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
+
+ /* target assertion occured */
+ NotifyTransportFailure(pProt, Status);
+}
+
+static void StateDumpCallback(void *pContext)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("============ HCIUart State ======================\n"));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("RecvStateFlags : 0x%X \n",pProt->RecvStateFlags));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendStateFlags : 0x%X \n",pProt->SendStateFlags));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("WaitBufferType : %d \n",pProt->WaitBufferType));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendQueue Depth : %d \n",HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsMax : %d \n",pProt->CreditsMax));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsConsumed : %d \n",pProt->CreditsConsumed));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsAvailable : %d \n",pProt->CreditsAvailable));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("==================================================\n"));
+}
+
+static A_STATUS HCIUartMessagePending(void *pContext, A_UINT8 LookAheadBytes[], int ValidBytes)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
+ A_STATUS status = A_OK;
+ int totalRecvLength = 0;
+ HCI_TRANSPORT_PACKET_TYPE pktType = HCI_PACKET_INVALID;
+ A_BOOL recvRefillCalled = FALSE;
+ A_BOOL blockRecv = FALSE;
+ HTC_PACKET *pPacket = NULL;
+
+ /** caller guarantees that this is a fully block-able context (synch I/O is allowed) */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCIUartMessagePending Lookahead Bytes:%d \n",ValidBytes));
+
+ LOCK_HCI_RX(pProt);
+
+ do {
+
+ if (ValidBytes < 3) {
+ /* not enough for ACL or event header */
+ break;
+ }
+
+ if ((LookAheadBytes[0] == HCI_UART_ACL_PKT) && (ValidBytes < 5)) {
+ /* not enough for ACL data header */
+ break;
+ }
+
+ switch (LookAheadBytes[0]) {
+ case HCI_UART_EVENT_PKT:
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
+ LookAheadBytes[1], LookAheadBytes[2]));
+ totalRecvLength = LookAheadBytes[2];
+ totalRecvLength += 3; /* add type + event code + length field */
+ pktType = HCI_EVENT_TYPE;
+ break;
+ case HCI_UART_ACL_PKT:
+ totalRecvLength = (LookAheadBytes[4] << 8) | LookAheadBytes[3];
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI ACL: conn:0x%X length: %d \n",
+ ((LookAheadBytes[2] & 0xF0) << 8) | LookAheadBytes[1], totalRecvLength));
+ totalRecvLength += 5; /* add type + connection handle + length field */
+ pktType = HCI_ACL_TYPE;
+ break;
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",LookAheadBytes[0]));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (pProt->HCIConfig.pHCIPktRecvAlloc != NULL) {
+ UNLOCK_HCI_RX(pProt);
+ /* user is using a per-packet allocation callback */
+ pPacket = pProt->HCIConfig.pHCIPktRecvAlloc(pProt->HCIConfig.pContext,
+ pktType,
+ totalRecvLength);
+ LOCK_HCI_RX(pProt);
+
+ } else {
+ HTC_PACKET_QUEUE *pQueue;
+ /* user is using a refill handler that can refill multiple HTC buffers */
+
+ /* select buffer queue */
+ if (pktType == HCI_ACL_TYPE) {
+ pQueue = &pProt->HCIACLRecvBuffers;
+ } else {
+ pQueue = &pProt->HCIEventBuffers;
+ }
+
+ if (HTC_QUEUE_EMPTY(pQueue)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("** HCI pkt type: %d has no buffers available calling allocation handler \n",
+ pktType));
+ /* check for refill handler */
+ if (pProt->HCIConfig.pHCIPktRecvRefill != NULL) {
+ recvRefillCalled = TRUE;
+ UNLOCK_HCI_RX(pProt);
+ /* call the re-fill handler */
+ pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
+ pktType,
+ 0);
+ LOCK_HCI_RX(pProt);
+ /* check if we have more buffers */
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ /* fall through */
+ }
+ } else {
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HCI pkt type: %d now has %d recv buffers left \n",
+ pktType, HTC_PACKET_QUEUE_DEPTH(pQueue)));
+ }
+ }
+
+ if (NULL == pPacket) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("** HCI pkt type: %d has no buffers available stopping recv...\n", pktType));
+ /* this is not an error, we simply need to mark that we are waiting for buffers.*/
+ pProt->RecvStateFlags |= HCI_RECV_WAIT_BUFFERS;
+ pProt->WaitBufferType = pktType;
+ blockRecv = TRUE;
+ break;
+ }
+
+ if (totalRecvLength > (int)pPacket->BufferLength) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI-UART pkt: %d requires %d bytes (%d buffer bytes avail) ! \n",
+ LookAheadBytes[0], totalRecvLength, pPacket->BufferLength));
+ status = A_EINVAL;
+ break;
+ }
+
+ } while (FALSE);
+
+ UNLOCK_HCI_RX(pProt);
+
+ /* locks are released, we can go fetch the packet */
+
+ do {
+
+ if (A_FAILED(status) || (NULL == pPacket)) {
+ break;
+ }
+
+ /* do this synchronously, we don't need to be fast here */
+ pPacket->Completion = NULL;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI : getting recv packet len:%d hci-uart-type: %s \n",
+ totalRecvLength, (LookAheadBytes[0] == HCI_UART_EVENT_PKT) ? "EVENT" : "ACL"));
+
+ status = DevGMboxRead(pProt->pDev, pPacket, totalRecvLength);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (pPacket->pBuffer[0] != LookAheadBytes[0]) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not contain expected packet type: %d ! \n",
+ pPacket->pBuffer[0]));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (pPacket->pBuffer[0] == HCI_UART_EVENT_PKT) {
+ /* validate event header fields */
+ if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
+ (pPacket->pBuffer[2] != LookAheadBytes[2])) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
+ DebugDumpBytes(LookAheadBytes, 3, "Expected HCI-UART Header");
+ DebugDumpBytes(pPacket->pBuffer, 3, "** Bad HCI-UART Header");
+ status = A_EPROTO;
+ break;
+ }
+ } else if (pPacket->pBuffer[0] == HCI_UART_ACL_PKT) {
+ /* validate acl header fields */
+ if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
+ (pPacket->pBuffer[2] != LookAheadBytes[2]) ||
+ (pPacket->pBuffer[3] != LookAheadBytes[3]) ||
+ (pPacket->pBuffer[4] != LookAheadBytes[4])) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
+ DebugDumpBytes(LookAheadBytes, 5, "Expected HCI-UART Header");
+ DebugDumpBytes(pPacket->pBuffer, 5, "** Bad HCI-UART Header");
+ status = A_EPROTO;
+ break;
+ }
+ }
+
+ /* adjust buffer to move past packet ID */
+ pPacket->pBuffer++;
+ pPacket->ActualLength = totalRecvLength - 1;
+ pPacket->Status = A_OK;
+ /* indicate packet */
+ DO_HCI_RECV_INDICATION(pProt,pPacket);
+ pPacket = NULL;
+
+ /* check if we need to refill recv buffers */
+ if ((pProt->HCIConfig.pHCIPktRecvRefill != NULL) && !recvRefillCalled) {
+ HTC_PACKET_QUEUE *pQueue;
+ int watermark;
+
+ if (pktType == HCI_ACL_TYPE) {
+ watermark = pProt->HCIConfig.ACLRecvBufferWaterMark;
+ pQueue = &pProt->HCIACLRecvBuffers;
+ } else {
+ watermark = pProt->HCIConfig.EventRecvBufferWaterMark;
+ pQueue = &pProt->HCIEventBuffers;
+ }
+
+ if (HTC_PACKET_QUEUE_DEPTH(pQueue) < watermark) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("** HCI pkt type: %d watermark hit (%d) current:%d \n",
+ pktType, watermark, HTC_PACKET_QUEUE_DEPTH(pQueue)));
+ /* call the re-fill handler */
+ pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
+ pktType,
+ HTC_PACKET_QUEUE_DEPTH(pQueue));
+ }
+ }
+
+ } while (FALSE);
+
+ /* check if we need to disable the reciever */
+ if (A_FAILED(status) || blockRecv) {
+ DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_DISABLE, PROC_IO_SYNC);
+ }
+
+ /* see if we need to recycle the recv buffer */
+ if (A_FAILED(status) && (pPacket != NULL)) {
+ HTC_PACKET_QUEUE queue;
+
+ if (A_EPROTO == status) {
+ DebugDumpBytes(pPacket->pBuffer, totalRecvLength, "Bad HCI-UART Recv packet");
+ }
+ /* recycle packet */
+ HTC_PACKET_RESET_RX(pPacket);
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
+ HCI_TransportAddReceivePkts(pProt,&queue);
+ NotifyTransportFailure(pProt,status);
+ }
+
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCIUartMessagePending \n"));
+
+ return status;
+}
+
+static void HCISendPacketCompletion(void *Context, HTC_PACKET *pPacket)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)Context;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion (pPacket:0x%X) \n",(A_UINT32)pPacket));
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Send Packet (0x%X) failed: %d , len:%d \n",
+ (A_UINT32)pPacket, pPacket->Status, pPacket->ActualLength));
+ }
+
+ DO_HCI_SEND_INDICATION(pProt,pPacket);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion \n"));
+}
+
+static A_STATUS SeekCreditsSynch(GMBOX_PROTO_HCI_UART *pProt)
+{
+ A_STATUS status = A_OK;
+ int credits;
+ int retry = 100;
+
+ while (TRUE) {
+ credits = 0;
+ status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
+ if (A_FAILED(status)) {
+ break;
+ }
+ LOCK_HCI_TX(pProt);
+ pProt->CreditsAvailable += credits;
+ pProt->CreditsConsumed -= credits;
+ if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
+ pProt->CreditsCurrentSeek = 0;
+ UNLOCK_HCI_TX(pProt);
+ break;
+ }
+ UNLOCK_HCI_TX(pProt);
+ retry--;
+ if (0 == retry) {
+ status = A_EBUSY;
+ break;
+ }
+ A_MDELAY(20);
+ }
+
+ return status;
+}
+
+static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous)
+{
+ A_STATUS status = A_OK;
+ int transferLength;
+ int creditsRequired, remainder;
+ A_UINT8 hciUartType;
+ A_BOOL synchSendComplete = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCITrySend (pPacket:0x%X) %s \n",(A_UINT32)pPacket,
+ Synchronous ? "SYNC" :"ASYNC"));
+
+ LOCK_HCI_TX(pProt);
+
+ /* increment write processing count on entry */
+ pProt->SendProcessCount++;
+
+ do {
+
+ if (pProt->HCIStopped) {
+ status = A_ECANCELED;
+ break;
+ }
+
+ if (pPacket != NULL) {
+ /* packet was supplied */
+ if (Synchronous) {
+ /* in synchronous mode, the send queue can only hold 1 packet */
+ if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
+ status = A_EBUSY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ if (pProt->SendProcessCount > 1) {
+ /* another thread or task is draining the TX queues */
+ status = A_EBUSY;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
+
+ } else {
+ /* see if adding this packet hits the max depth (asynchronous mode only) */
+ if ((pProt->HCIConfig.MaxSendQueueDepth > 0) &&
+ ((HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue) + 1) >= pProt->HCIConfig.MaxSendQueueDepth)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("HCI Send queue is full, Depth:%d, Max:%d \n",
+ HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue),
+ pProt->HCIConfig.MaxSendQueueDepth));
+ /* queue will be full, invoke any callbacks to determine what action to take */
+ if (pProt->HCIConfig.pHCISendFull != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("HCI : Calling driver's send full callback.... \n"));
+ if (pProt->HCIConfig.pHCISendFull(pProt->HCIConfig.pContext,
+ pPacket) == HCI_SEND_FULL_DROP) {
+ /* drop it */
+ status = A_NO_RESOURCE;
+ break;
+ }
+ }
+ }
+
+ HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
+ }
+
+ }
+
+ if (pProt->SendStateFlags & HCI_SEND_WAIT_CREDITS) {
+ break;
+ }
+
+ if (pProt->SendProcessCount > 1) {
+ /* another thread or task is draining the TX queues */
+ break;
+ }
+
+ /***** beyond this point only 1 thread may enter ******/
+
+ /* now drain the send queue for transmission as long as we have enough
+ * credits */
+ while (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
+
+ pPacket = HTC_PACKET_DEQUEUE(&pProt->SendQueue);
+
+ switch (HCI_GET_PACKET_TYPE(pPacket)) {
+ case HCI_COMMAND_TYPE:
+ hciUartType = HCI_UART_COMMAND_PKT;
+ break;
+ case HCI_ACL_TYPE:
+ hciUartType = HCI_UART_ACL_PKT;
+ break;
+ default:
+ status = A_EINVAL;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Got head packet:0x%X , Type:%d Length: %d Remaining Queue Depth: %d\n",
+ (A_UINT32)pPacket, HCI_GET_PACKET_TYPE(pPacket), pPacket->ActualLength,
+ HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
+
+ transferLength = 1; /* UART type header is 1 byte */
+ transferLength += pPacket->ActualLength;
+ transferLength = DEV_CALC_SEND_PADDED_LEN(pProt->pDev, transferLength);
+
+ /* figure out how many credits this message requires */
+ creditsRequired = transferLength / pProt->CreditSize;
+ remainder = transferLength % pProt->CreditSize;
+
+ if (remainder) {
+ creditsRequired++;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Creds Required:%d Got:%d\n",
+ creditsRequired, pProt->CreditsAvailable));
+
+ if (creditsRequired > pProt->CreditsAvailable) {
+ if (Synchronous) {
+ /* in synchronous mode we need to seek credits in synchronously */
+ pProt->CreditsCurrentSeek = creditsRequired;
+ UNLOCK_HCI_TX(pProt);
+ status = SeekCreditsSynch(pProt);
+ LOCK_HCI_TX(pProt);
+ if (A_FAILED(status)) {
+ break;
+ }
+ /* fall through and continue processing this send op */
+ } else {
+ /* not enough credits, queue back to the head */
+ HTC_PACKET_ENQUEUE_TO_HEAD(&pProt->SendQueue,pPacket);
+ /* waiting for credits */
+ pProt->SendStateFlags |= HCI_SEND_WAIT_CREDITS;
+ /* provide a hint to reduce attempts to re-send if credits are dribbling back
+ * this hint is the short fall of credits */
+ pProt->CreditsCurrentSeek = creditsRequired;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: packet:0x%X placed back in queue. head packet needs: %d credits \n",
+ (A_UINT32)pPacket, pProt->CreditsCurrentSeek));
+ pPacket = NULL;
+ UNLOCK_HCI_TX(pProt);
+
+ /* schedule a credit counter read, our CreditsAvailableCallback callback will be called
+ * with the result */
+ DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_ASYNC, NULL);
+
+ LOCK_HCI_TX(pProt);
+ break;
+ }
+ }
+
+ /* caller guarantees some head room */
+ pPacket->pBuffer--;
+ pPacket->pBuffer[0] = hciUartType;
+
+ pProt->CreditsAvailable -= creditsRequired;
+ pProt->CreditsConsumed += creditsRequired;
+ A_ASSERT(pProt->CreditsConsumed <= pProt->CreditsMax);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state: consumed:%d available:%d max:%d\n",
+ pProt->CreditsConsumed, pProt->CreditsAvailable, pProt->CreditsMax));
+
+ UNLOCK_HCI_TX(pProt);
+
+ /* write it out */
+ if (Synchronous) {
+ pPacket->Completion = NULL;
+ pPacket->pContext = NULL;
+ } else {
+ pPacket->Completion = HCISendPacketCompletion;
+ pPacket->pContext = pProt;
+ }
+
+ status = DevGMboxWrite(pProt->pDev,pPacket,transferLength);
+ if (Synchronous) {
+ synchSendComplete = TRUE;
+ } else {
+ pPacket = NULL;
+ }
+
+ LOCK_HCI_TX(pProt);
+
+ }
+
+ } while (FALSE);
+
+ pProt->SendProcessCount--;
+ A_ASSERT(pProt->SendProcessCount >= 0);
+ UNLOCK_HCI_TX(pProt);
+
+ if (Synchronous) {
+ A_ASSERT(pPacket != NULL);
+ if (A_SUCCESS(status) && (!synchSendComplete)) {
+ status = A_EBUSY;
+ A_ASSERT(FALSE);
+ LOCK_HCI_TX(pProt);
+ if (pPacket->ListLink.pNext != NULL) {
+ /* remove from the queue */
+ HTC_PACKET_REMOVE(&pProt->SendQueue,pPacket);
+ }
+ UNLOCK_HCI_TX(pProt);
+ }
+ } else {
+ if (A_FAILED(status) && (pPacket != NULL)) {
+ pPacket->Status = status;
+ DO_HCI_SEND_INDICATION(pProt,pPacket);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HCITrySend: \n"));
+ return status;
+}
+
+static void FlushSendQueue(GMBOX_PROTO_HCI_UART *pProt)
+{
+ HTC_PACKET *pPacket;
+ HTC_PACKET_QUEUE discardQueue;
+
+ INIT_HTC_PACKET_QUEUE(&discardQueue);
+
+ LOCK_HCI_TX(pProt);
+
+ if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->SendQueue);
+ }
+
+ UNLOCK_HCI_TX(pProt);
+
+ /* discard packets */
+ while (!HTC_QUEUE_EMPTY(&discardQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
+ pPacket->Status = A_ECANCELED;
+ DO_HCI_SEND_INDICATION(pProt,pPacket);
+ }
+
+}
+
+static void FlushRecvBuffers(GMBOX_PROTO_HCI_UART *pProt)
+{
+ HTC_PACKET_QUEUE discardQueue;
+ HTC_PACKET *pPacket;
+
+ INIT_HTC_PACKET_QUEUE(&discardQueue);
+
+ LOCK_HCI_RX(pProt);
+ /*transfer list items from ACL and event buffer queues to the discard queue */
+ if (!HTC_QUEUE_EMPTY(&pProt->HCIACLRecvBuffers)) {
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIACLRecvBuffers);
+ }
+ if (!HTC_QUEUE_EMPTY(&pProt->HCIEventBuffers)) {
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIEventBuffers);
+ }
+ UNLOCK_HCI_RX(pProt);
+
+ /* now empty the discard queue */
+ while (!HTC_QUEUE_EMPTY(&discardQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
+ pPacket->Status = A_ECANCELED;
+ DO_HCI_RECV_INDICATION(pProt,pPacket);
+ }
+
+}
+
+/*** protocol module install entry point ***/
+
+A_STATUS GMboxProtocolInstall(AR6K_DEVICE *pDev)
+{
+ A_STATUS status = A_OK;
+ GMBOX_PROTO_HCI_UART *pProtocol = NULL;
+
+ do {
+
+ pProtocol = A_MALLOC(sizeof(GMBOX_PROTO_HCI_UART));
+
+ if (NULL == pProtocol) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ A_MEMZERO(pProtocol, sizeof(*pProtocol));
+ pProtocol->pDev = pDev;
+ INIT_HTC_PACKET_QUEUE(&pProtocol->SendQueue);
+ INIT_HTC_PACKET_QUEUE(&pProtocol->HCIACLRecvBuffers);
+ INIT_HTC_PACKET_QUEUE(&pProtocol->HCIEventBuffers);
+ A_MUTEX_INIT(&pProtocol->HCIRxLock);
+ A_MUTEX_INIT(&pProtocol->HCITxLock);
+
+ } while (FALSE);
+
+ if (A_SUCCESS(status)) {
+ LOCK_AR6K(pDev);
+ DEV_GMBOX_SET_PROTOCOL(pDev,
+ HCIUartMessagePending,
+ CreditsAvailableCallback,
+ FailureCallback,
+ StateDumpCallback,
+ pProtocol);
+ UNLOCK_AR6K(pDev);
+ } else {
+ if (pProtocol != NULL) {
+ HCIUartCleanup(pProtocol);
+ }
+ }
+
+ return status;
+}
+
+/*** protocol module uninstall entry point ***/
+void GMboxProtocolUninstall(AR6K_DEVICE *pDev)
+{
+ GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
+
+ if (pProtocol != NULL) {
+
+ /* notify anyone attached */
+ if (pProtocol->HCIAttached) {
+ A_ASSERT(pProtocol->HCIConfig.TransportRemoved != NULL);
+ pProtocol->HCIConfig.TransportRemoved(pProtocol->HCIConfig.pContext);
+ pProtocol->HCIAttached = FALSE;
+ }
+
+ HCIUartCleanup(pProtocol);
+ DEV_GMBOX_SET_PROTOCOL(pDev,NULL,NULL,NULL,NULL,NULL);
+ }
+
+}
+
+static A_STATUS NotifyTransportReady(GMBOX_PROTO_HCI_UART *pProt)
+{
+ HCI_TRANSPORT_PROPERTIES props;
+ A_STATUS status = A_OK;
+
+ do {
+
+ A_MEMZERO(&props,sizeof(props));
+
+ /* HCI UART only needs one extra byte at the head to indicate the packet TYPE */
+ props.HeadRoom = 1;
+ props.TailRoom = 0;
+ props.IOBlockPad = pProt->pDev->BlockSize;
+ if (pProt->HCIAttached) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI: notifying attached client to transport... \n"));
+ A_ASSERT(pProt->HCIConfig.TransportReady != NULL);
+ status = pProt->HCIConfig.TransportReady(pProt,
+ &props,
+ pProt->HCIConfig.pContext);
+ }
+
+ } while (FALSE);
+
+ return status;
+}
+
+/*********** HCI UART protocol implementation ************************************************/
+
+HCI_TRANSPORT_HANDLE HCI_TransportAttach(void *HTCHandle, HCI_TRANSPORT_CONFIG_INFO *pInfo)
+{
+ GMBOX_PROTO_HCI_UART *pProtocol = NULL;
+ AR6K_DEVICE *pDev;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportAttach \n"));
+
+ pDev = HTCGetAR6KDevice(HTCHandle);
+
+ LOCK_AR6K(pDev);
+
+ do {
+
+ pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
+
+ if (NULL == pProtocol) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not installed! \n"));
+ break;
+ }
+
+ if (pProtocol->HCIAttached) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol already attached! \n"));
+ break;
+ }
+
+ A_MEMCPY(&pProtocol->HCIConfig, pInfo, sizeof(HCI_TRANSPORT_CONFIG_INFO));
+
+ A_ASSERT(pProtocol->HCIConfig.pHCIPktRecv != NULL);
+ A_ASSERT(pProtocol->HCIConfig.pHCISendComplete != NULL);
+
+ pProtocol->HCIAttached = TRUE;
+
+ } while (FALSE);
+
+ UNLOCK_AR6K(pDev);
+
+ if (pProtocol != NULL) {
+ /* TODO ... should we use a worker? */
+ NotifyTransportReady(pProtocol);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach (0x%X) \n",(A_UINT32)pProtocol));
+ return (HCI_TRANSPORT_HANDLE)pProtocol;
+}
+
+void HCI_TransportDetach(HCI_TRANSPORT_HANDLE HciTrans)
+{
+ GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)HciTrans;
+ AR6K_DEVICE *pDev = pProtocol->pDev;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportDetach \n"));
+
+ LOCK_AR6K(pDev);
+ if (!pProtocol->HCIAttached) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not attached! \n"));
+ UNLOCK_AR6K(pDev);
+ return;
+ }
+ pProtocol->HCIAttached = FALSE;
+ UNLOCK_AR6K(pDev);
+
+ HCI_TransportStop(HciTrans);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach \n"));
+}
+
+A_STATUS HCI_TransportAddReceivePkts(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET_QUEUE *pQueue)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+ A_STATUS status = A_OK;
+ A_BOOL unblockRecv = FALSE;
+ HTC_PACKET *pPacket;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCI_TransportAddReceivePkt \n"));
+
+ LOCK_HCI_RX(pProt);
+
+ do {
+
+ if (pProt->HCIStopped) {
+ status = A_ECANCELED;
+ break;
+ }
+
+ pPacket = HTC_GET_PKT_AT_HEAD(pQueue);
+
+ if (NULL == pPacket) {
+ status = A_EINVAL;
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv packet added, type :%d, len:%d num:%d \n",
+ HCI_GET_PACKET_TYPE(pPacket), pPacket->BufferLength, HTC_PACKET_QUEUE_DEPTH(pQueue)));
+
+ if (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) {
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIEventBuffers, pQueue);
+ } else if (HCI_GET_PACKET_TYPE(pPacket) == HCI_ACL_TYPE) {
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIACLRecvBuffers, pQueue);
+ } else {
+ status = A_EINVAL;
+ break;
+ }
+
+ if (pProt->RecvStateFlags & HCI_RECV_WAIT_BUFFERS) {
+ if (pProt->WaitBufferType == HCI_GET_PACKET_TYPE(pPacket)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv was blocked on packet type :%d, unblocking.. \n",
+ pProt->WaitBufferType));
+ pProt->RecvStateFlags &= ~HCI_RECV_WAIT_BUFFERS;
+ pProt->WaitBufferType = HCI_PACKET_INVALID;
+ unblockRecv = TRUE;
+ }
+ }
+
+ } while (FALSE);
+
+ UNLOCK_HCI_RX(pProt);
+
+ if (A_FAILED(status)) {
+ while (!HTC_QUEUE_EMPTY(pQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ pPacket->Status = A_ECANCELED;
+ DO_HCI_RECV_INDICATION(pProt,pPacket);
+ }
+ }
+
+ if (unblockRecv) {
+ DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_ASYNC);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCI_TransportAddReceivePkt \n"));
+
+ return A_OK;
+}
+
+A_STATUS HCI_TransportSendPkt(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET *pPacket, A_BOOL Synchronous)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+
+ return HCITrySend(pProt,pPacket,Synchronous);
+}
+
+void HCI_TransportStop(HCI_TRANSPORT_HANDLE HciTrans)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStop \n"));
+
+ LOCK_AR6K(pProt->pDev);
+ if (pProt->HCIStopped) {
+ UNLOCK_AR6K(pProt->pDev);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
+ return;
+ }
+ pProt->HCIStopped = TRUE;
+ UNLOCK_AR6K(pProt->pDev);
+
+ /* disable interrupts */
+ DevGMboxIRQAction(pProt->pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
+ FlushSendQueue(pProt);
+ FlushRecvBuffers(pProt);
+
+ /* signal bridge side to power down BT */
+ DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_OFF, BTOFF_TIMEOUT_MS);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
+}
+
+A_STATUS HCI_TransportStart(HCI_TRANSPORT_HANDLE HciTrans)
+{
+ A_STATUS status;
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStart \n"));
+
+ /* set stopped in case we have a problem in starting */
+ pProt->HCIStopped = TRUE;
+
+ do {
+
+ status = InitTxCreditState(pProt);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ status = DevGMboxIRQAction(pProt->pDev, GMBOX_ERRORS_IRQ_ENABLE, PROC_IO_SYNC);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+ /* enable recv */
+ status = DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_SYNC);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+ /* signal bridge side to power up BT */
+ status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_ON, BTON_TIMEOUT_MS);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI_TransportStart : Failed to trigger BT ON \n"));
+ break;
+ }
+
+ /* we made it */
+ pProt->HCIStopped = FALSE;
+
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStart \n"));
+
+ return status;
+}
+
+A_STATUS HCI_TransportEnableDisableAsyncRecv(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+ return DevGMboxIRQAction(pProt->pDev,
+ Enable ? GMBOX_RECV_IRQ_ENABLE : GMBOX_RECV_IRQ_DISABLE,
+ PROC_IO_SYNC);
+
+}
+
+A_STATUS HCI_TransportRecvHCIEventSync(HCI_TRANSPORT_HANDLE HciTrans,
+ HTC_PACKET *pPacket,
+ int MaxPollMS)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+ A_STATUS status = A_OK;
+ A_UINT8 lookAhead[8];
+ int bytes;
+ int totalRecvLength;
+
+ MaxPollMS = MaxPollMS / 16;
+
+ if (MaxPollMS < 2) {
+ MaxPollMS = 2;
+ }
+
+ while (MaxPollMS) {
+
+ bytes = sizeof(lookAhead);
+ status = DevGMboxRecvLookAheadPeek(pProt->pDev,lookAhead,&bytes);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (bytes < 3) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI recv poll got bytes: %d, retry : %d \n",
+ bytes, MaxPollMS));
+ A_MDELAY(16);
+ MaxPollMS--;
+ continue;
+ }
+
+ totalRecvLength = 0;
+ switch (lookAhead[0]) {
+ case HCI_UART_EVENT_PKT:
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
+ lookAhead[1], lookAhead[2]));
+ totalRecvLength = lookAhead[2];
+ totalRecvLength += 3; /* add type + event code + length field */
+ break;
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",lookAhead[0]));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ pPacket->Completion = NULL;
+ status = DevGMboxRead(pProt->pDev,pPacket,totalRecvLength);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ pPacket->pBuffer++;
+ pPacket->ActualLength = totalRecvLength - 1;
+ pPacket->Status = A_OK;
+ break;
+ }
+
+ if (MaxPollMS == 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI recv poll timeout! \n"));
+ status = A_ERROR;
+ }
+
+ return status;
+}
+
+#define LSB_SCRATCH_IDX 4
+#define MSB_SCRATCH_IDX 5
+A_STATUS HCI_TransportSetBaudRate(HCI_TRANSPORT_HANDLE HciTrans, A_UINT32 Baud)
+{
+ GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
+ HIF_DEVICE *pHIFDevice = (HIF_DEVICE *)(pProt->pDev->HIFDevice);
+ A_UINT32 scaledBaud, scratchAddr;
+ A_STATUS status = A_OK;
+
+ /* Divide the desired baud rate by 100
+ * Store the LSB in the local scratch register 4 and the MSB in the local
+ * scratch register 5 for the target to read
+ */
+ scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * LSB_SCRATCH_IDX);
+ scaledBaud = (Baud / 100) & LOCAL_SCRATCH_VALUE_MASK;
+ status = ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud);
+ scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * MSB_SCRATCH_IDX);
+ scaledBaud = ((Baud / 100) >> (LOCAL_SCRATCH_VALUE_MSB+1)) & LOCAL_SCRATCH_VALUE_MASK;
+ status |= ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud);
+ if (A_OK != status) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to set up baud rate in scratch register!"));
+ return status;
+ }
+
+ /* Now interrupt the target to tell it about the baud rate */
+ status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BAUD_SET, BAUD_TIMEOUT_MS);
+ if (A_OK != status) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to tell target to change baud rate!"));
+ }
+
+ return status;
+}
+
+#endif //ATH_AR6K_ENABLE_GMBOX
+
diff --git a/drivers/net/wireless/ath6kl/htc2/AR6000/makefile b/drivers/net/wireless/ath6kl/htc2/AR6000/makefile
new file mode 100644
index 000000000000..6e53a111b67f
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/AR6000/makefile
@@ -0,0 +1,22 @@
+#------------------------------------------------------------------------------
+# <copyright file="makefile" company="Atheros">
+# Copyright (c) 2005-2007 Atheros Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+#
+#------------------------------------------------------------------------------
+#==============================================================================
+# Author(s): ="Atheros"
+#==============================================================================
+!INCLUDE $(_MAKEENVROOT)\makefile.def
+
+
+
diff --git a/drivers/net/wireless/ath6kl/htc2/htc.c b/drivers/net/wireless/ath6kl/htc2/htc.c
new file mode 100644
index 000000000000..0068685972a9
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc.c
@@ -0,0 +1,558 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#include "htc_internal.h"
+
+#ifdef DEBUG
+static ATH_DEBUG_MASK_DESCRIPTION g_HTCDebugDescription[] = {
+ { ATH_DEBUG_SEND , "Send"},
+ { ATH_DEBUG_RECV , "Recv"},
+ { ATH_DEBUG_SYNC , "Sync"},
+ { ATH_DEBUG_DUMP , "Dump Data (RX or TX)"},
+ { ATH_DEBUG_IRQ , "Interrupt Processing"}
+};
+
+ATH_DEBUG_INSTANTIATE_MODULE_VAR(htc,
+ "htc",
+ "Host Target Communications",
+ ATH_DEBUG_MASK_DEFAULTS,
+ ATH_DEBUG_DESCRIPTION_COUNT(g_HTCDebugDescription),
+ g_HTCDebugDescription);
+
+#endif
+
+static void HTCReportFailure(void *Context);
+static void ResetEndpointStates(HTC_TARGET *target);
+
+void HTCFreeControlBuffer(HTC_TARGET *target, HTC_PACKET *pPacket, HTC_PACKET_QUEUE *pList)
+{
+ LOCK_HTC(target);
+ HTC_PACKET_ENQUEUE(pList,pPacket);
+ UNLOCK_HTC(target);
+}
+
+HTC_PACKET *HTCAllocControlBuffer(HTC_TARGET *target, HTC_PACKET_QUEUE *pList)
+{
+ HTC_PACKET *pPacket;
+
+ LOCK_HTC(target);
+ pPacket = HTC_PACKET_DEQUEUE(pList);
+ UNLOCK_HTC(target);
+
+ return pPacket;
+}
+
+/* cleanup the HTC instance */
+static void HTCCleanup(HTC_TARGET *target)
+{
+ A_INT32 i;
+
+ DevCleanup(&target->Device);
+
+ for (i = 0;i < NUM_CONTROL_BUFFERS;i++) {
+ if (target->HTCControlBuffers[i].Buffer) {
+ A_FREE(target->HTCControlBuffers[i].Buffer);
+ }
+ }
+
+ if (A_IS_MUTEX_VALID(&target->HTCLock)) {
+ A_MUTEX_DELETE(&target->HTCLock);
+ }
+
+ if (A_IS_MUTEX_VALID(&target->HTCRxLock)) {
+ A_MUTEX_DELETE(&target->HTCRxLock);
+ }
+
+ if (A_IS_MUTEX_VALID(&target->HTCTxLock)) {
+ A_MUTEX_DELETE(&target->HTCTxLock);
+ }
+ /* free our instance */
+ A_FREE(target);
+}
+
+/* registered target arrival callback from the HIF layer */
+HTC_HANDLE HTCCreate(void *hif_handle, HTC_INIT_INFO *pInfo)
+{
+ HTC_TARGET *target = NULL;
+ A_STATUS status = A_OK;
+ int i;
+ A_UINT32 ctrl_bufsz;
+ A_UINT32 blocksizes[HTC_MAILBOX_NUM_MAX];
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCCreate - Enter\n"));
+
+ A_REGISTER_MODULE_DEBUG_INFO(htc);
+
+ do {
+
+ /* allocate target memory */
+ if ((target = (HTC_TARGET *)A_MALLOC(sizeof(HTC_TARGET))) == NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
+ status = A_ERROR;
+ break;
+ }
+
+ A_MEMZERO(target, sizeof(HTC_TARGET));
+ A_MUTEX_INIT(&target->HTCLock);
+ A_MUTEX_INIT(&target->HTCRxLock);
+ A_MUTEX_INIT(&target->HTCTxLock);
+ INIT_HTC_PACKET_QUEUE(&target->ControlBufferTXFreeList);
+ INIT_HTC_PACKET_QUEUE(&target->ControlBufferRXFreeList);
+
+ /* give device layer the hif device handle */
+ target->Device.HIFDevice = hif_handle;
+ /* give the device layer our context (for event processing)
+ * the device layer will register it's own context with HIF
+ * so we need to set this so we can fetch it in the target remove handler */
+ target->Device.HTCContext = target;
+ /* set device layer target failure callback */
+ target->Device.TargetFailureCallback = HTCReportFailure;
+ /* set device layer recv message pending callback */
+ target->Device.MessagePendingCallback = HTCRecvMessagePendingHandler;
+ target->EpWaitingForBuffers = ENDPOINT_MAX;
+
+ A_MEMCPY(&target->HTCInitInfo,pInfo,sizeof(HTC_INIT_INFO));
+
+ ResetEndpointStates(target);
+
+ /* setup device layer */
+ status = DevSetup(&target->Device);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+
+ /* get the block sizes */
+ status = HIFConfigureDevice(hif_handle, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ blocksizes, sizeof(blocksizes));
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to get block size info from HIF layer...\n"));
+ break;
+ }
+
+ /* Set the control buffer size based on the block size */
+ if (blocksizes[1] > HTC_MAX_CONTROL_MESSAGE_LENGTH) {
+ ctrl_bufsz = blocksizes[1] + HTC_HDR_LENGTH;
+ } else {
+ ctrl_bufsz = HTC_MAX_CONTROL_MESSAGE_LENGTH + HTC_HDR_LENGTH;
+ }
+ for (i = 0;i < NUM_CONTROL_BUFFERS;i++) {
+ target->HTCControlBuffers[i].Buffer = A_MALLOC(ctrl_bufsz);
+ if (target->HTCControlBuffers[i].Buffer == NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
+ status = A_ERROR;
+ break;
+ }
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* carve up buffers/packets for control messages */
+ for (i = 0; i < NUM_CONTROL_RX_BUFFERS; i++) {
+ HTC_PACKET *pControlPacket;
+ pControlPacket = &target->HTCControlBuffers[i].HtcPacket;
+ SET_HTC_PACKET_INFO_RX_REFILL(pControlPacket,
+ target,
+ target->HTCControlBuffers[i].Buffer,
+ ctrl_bufsz,
+ ENDPOINT_0);
+ HTC_FREE_CONTROL_RX(target,pControlPacket);
+ }
+
+ for (;i < NUM_CONTROL_BUFFERS;i++) {
+ HTC_PACKET *pControlPacket;
+ pControlPacket = &target->HTCControlBuffers[i].HtcPacket;
+ INIT_HTC_PACKET_INFO(pControlPacket,
+ target->HTCControlBuffers[i].Buffer,
+ ctrl_bufsz);
+ HTC_FREE_CONTROL_TX(target,pControlPacket);
+ }
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ if (target != NULL) {
+ HTCCleanup(target);
+ target = NULL;
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCCreate - Exit\n"));
+
+ return target;
+}
+
+void HTCDestroy(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCDestroy .. Destroying :0x%X \n",(A_UINT32)target));
+ HTCCleanup(target);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCDestroy \n"));
+}
+
+/* get the low level HIF device for the caller , the caller may wish to do low level
+ * HIF requests */
+void *HTCGetHifDevice(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ return target->Device.HIFDevice;
+}
+
+/* wait for the target to arrive (sends HTC Ready message)
+ * this operation is fully synchronous and the message is polled for */
+A_STATUS HTCWaitTarget(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ A_STATUS status;
+ HTC_PACKET *pPacket = NULL;
+ HTC_READY_EX_MSG *pRdyMsg;
+ HTC_SERVICE_CONNECT_REQ connect;
+ HTC_SERVICE_CONNECT_RESP resp;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCWaitTarget - Enter (target:0x%X) \n", (A_UINT32)target));
+
+ do {
+
+#ifdef MBOXHW_UNIT_TEST
+
+ status = DoMboxHWTest(&target->Device);
+
+ if (status != A_OK) {
+ break;
+ }
+
+#endif
+
+ /* we should be getting 1 control message that the target is ready */
+ status = HTCWaitforControlMessage(target, &pPacket);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" Target Not Available!!\n"));
+ break;
+ }
+
+ /* we controlled the buffer creation so it has to be properly aligned */
+ pRdyMsg = (HTC_READY_EX_MSG *)pPacket->pBuffer;
+
+ if ((pRdyMsg->Version2_0_Info.MessageID != HTC_MSG_READY_ID) ||
+ (pPacket->ActualLength < sizeof(HTC_READY_MSG))) {
+ /* this message is not valid */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+
+ if (pRdyMsg->Version2_0_Info.CreditCount == 0 || pRdyMsg->Version2_0_Info.CreditSize == 0) {
+ /* this message is not valid */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ target->TargetCredits = pRdyMsg->Version2_0_Info.CreditCount;
+ target->TargetCreditSize = pRdyMsg->Version2_0_Info.CreditSize;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN, (" Target Ready: credits: %d credit size: %d\n",
+ target->TargetCredits, target->TargetCreditSize));
+
+ /* check if this is an extended ready message */
+ if (pPacket->ActualLength >= sizeof(HTC_READY_EX_MSG)) {
+ /* this is an extended message */
+ target->HTCTargetVersion = pRdyMsg->HTCVersion;
+ target->MaxMsgPerBundle = pRdyMsg->MaxMsgsPerHTCBundle;
+ } else {
+ /* legacy */
+ target->HTCTargetVersion = HTC_VERSION_2P0;
+ target->MaxMsgPerBundle = 0;
+ }
+
+#ifdef HTC_FORCE_LEGACY_2P0
+ /* for testing and comparison...*/
+ target->HTCTargetVersion = HTC_VERSION_2P0;
+ target->MaxMsgPerBundle = 0;
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("Using HTC Protocol Version : %s (%d)\n ",
+ (target->HTCTargetVersion == HTC_VERSION_2P0) ? "2.0" : ">= 2.1",
+ target->HTCTargetVersion));
+
+ if (target->MaxMsgPerBundle > 0) {
+ /* limit what HTC can handle */
+ target->MaxMsgPerBundle = min(HTC_HOST_MAX_MSG_PER_BUNDLE, target->MaxMsgPerBundle);
+ /* target supports message bundling, setup device layer */
+ if (A_FAILED(DevSetupMsgBundling(&target->Device,target->MaxMsgPerBundle))) {
+ /* device layer can't handle bundling */
+ target->MaxMsgPerBundle = 0;
+ } else {
+ /* limit bundle what the device layer can handle */
+ target->MaxMsgPerBundle = min(DEV_GET_MAX_MSG_PER_BUNDLE(&target->Device),
+ target->MaxMsgPerBundle);
+ }
+ }
+
+ if (target->MaxMsgPerBundle > 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ (" HTC bundling allowed. Max Msg Per HTC Bundle: %d\n", target->MaxMsgPerBundle));
+ target->SendBundlingEnabled = TRUE;
+ target->RecvBundlingEnabled = TRUE;
+ if (!DEV_IS_LEN_BLOCK_ALIGNED(&target->Device,target->TargetCreditSize)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("*** Credit size: %d is not block aligned! Disabling send bundling \n",
+ target->TargetCreditSize));
+ /* disallow send bundling since the credit size is not aligned to a block size
+ * the I/O block padding will spill into the next credit buffer which is fatal */
+ target->SendBundlingEnabled = FALSE;
+ }
+ }
+
+ /* setup our pseudo HTC control endpoint connection */
+ A_MEMZERO(&connect,sizeof(connect));
+ A_MEMZERO(&resp,sizeof(resp));
+ connect.EpCallbacks.pContext = target;
+ connect.EpCallbacks.EpTxComplete = HTCControlTxComplete;
+ connect.EpCallbacks.EpRecv = HTCControlRecv;
+ connect.EpCallbacks.EpRecvRefill = NULL; /* not needed */
+ connect.EpCallbacks.EpSendFull = NULL; /* not nedded */
+ connect.MaxSendQueueDepth = NUM_CONTROL_BUFFERS;
+ connect.ServiceID = HTC_CTRL_RSVD_SVC;
+
+ /* connect fake service */
+ status = HTCConnectService((HTC_HANDLE)target,
+ &connect,
+ &resp);
+
+ if (!A_FAILED(status)) {
+ break;
+ }
+
+ } while (FALSE);
+
+ if (pPacket != NULL) {
+ HTC_FREE_CONTROL_RX(target,pPacket);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCWaitTarget - Exit\n"));
+
+ return status;
+}
+
+
+
+/* Start HTC, enable interrupts and let the target know host has finished setup */
+A_STATUS HTCStart(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_PACKET *pPacket;
+ A_STATUS status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Enter\n"));
+
+ /* make sure interrupts are disabled at the chip level,
+ * this function can be called again from a reboot of the target without shutting down HTC */
+ DevDisableInterrupts(&target->Device);
+ /* make sure state is cleared again */
+ target->OpStateFlags = 0;
+ target->RecvStateFlags = 0;
+
+ /* now that we are starting, push control receive buffers into the
+ * HTC control endpoint */
+
+ while (1) {
+ pPacket = HTC_ALLOC_CONTROL_RX(target);
+ if (NULL == pPacket) {
+ break;
+ }
+ HTCAddReceivePkt((HTC_HANDLE)target,pPacket);
+ }
+
+ do {
+
+ AR_DEBUG_ASSERT(target->InitCredits != NULL);
+ AR_DEBUG_ASSERT(target->EpCreditDistributionListHead != NULL);
+ AR_DEBUG_ASSERT(target->EpCreditDistributionListHead->pNext != NULL);
+
+ /* call init credits callback to do the distribution ,
+ * NOTE: the first entry in the distribution list is ENDPOINT_0, so
+ * we pass the start of the list after this one. */
+ target->InitCredits(target->pCredDistContext,
+ target->EpCreditDistributionListHead->pNext,
+ target->TargetCredits);
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) {
+ DumpCreditDistStates(target);
+ }
+
+ /* the caller is done connecting to services, so we can indicate to the
+ * target that the setup phase is complete */
+ status = HTCSendSetupComplete(target);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* unmask interrupts */
+ status = DevUnmaskInterrupts(&target->Device);
+
+ if (A_FAILED(status)) {
+ HTCStop(target);
+ }
+
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Exit\n"));
+ return status;
+}
+
+static void ResetEndpointStates(HTC_TARGET *target)
+{
+ HTC_ENDPOINT *pEndpoint;
+ int i;
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ pEndpoint = &target->EndPoint[i];
+
+ A_MEMZERO(&pEndpoint->CreditDist, sizeof(pEndpoint->CreditDist));
+ pEndpoint->ServiceID = 0;
+ pEndpoint->MaxMsgLength = 0;
+ pEndpoint->MaxTxQueueDepth = 0;
+#ifdef HTC_EP_STAT_PROFILING
+ A_MEMZERO(&pEndpoint->EndPointStats,sizeof(pEndpoint->EndPointStats));
+#endif
+ INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBuffers);
+ INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue);
+ INIT_HTC_PACKET_QUEUE(&pEndpoint->RecvIndicationQueue);
+ pEndpoint->target = target;
+ }
+ /* reset distribution list */
+ target->EpCreditDistributionListHead = NULL;
+}
+
+/* stop HTC communications, i.e. stop interrupt reception, and flush all queued buffers */
+void HTCStop(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCStop \n"));
+
+ LOCK_HTC(target);
+ /* mark that we are shutting down .. */
+ target->OpStateFlags |= HTC_OP_STATE_STOPPING;
+ UNLOCK_HTC(target);
+
+ /* Masking interrupts is a synchronous operation, when this function returns
+ * all pending HIF I/O has completed, we can safely flush the queues */
+ DevMaskInterrupts(&target->Device);
+
+ /* flush all send packets */
+ HTCFlushSendPkts(target);
+ /* flush all recv buffers */
+ HTCFlushRecvBuffers(target);
+
+ ResetEndpointStates(target);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCStop \n"));
+}
+
+void HTCDumpCreditStates(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+
+ LOCK_HTC_TX(target);
+
+ DumpCreditDistStates(target);
+
+ UNLOCK_HTC_TX(target);
+
+ DumpAR6KDevState(&target->Device);
+}
+
+/* report a target failure from the device, this is a callback from the device layer
+ * which uses a mechanism to report errors from the target (i.e. special interrupts) */
+static void HTCReportFailure(void *Context)
+{
+ HTC_TARGET *target = (HTC_TARGET *)Context;
+
+ target->TargetFailure = TRUE;
+
+ if (target->HTCInitInfo.TargetFailure != NULL) {
+ /* let upper layer know, it needs to call HTCStop() */
+ target->HTCInitInfo.TargetFailure(target->HTCInitInfo.pContext, A_ERROR);
+ }
+}
+
+A_BOOL HTCGetEndpointStatistics(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].EndPointStats, sizeof(HTC_ENDPOINT_STATS));
+ }
+
+ if (clearStats) {
+ /* reset stats */
+ A_MEMZERO(&target->EndPoint[Endpoint].EndPointStats, sizeof(HTC_ENDPOINT_STATS));
+ }
+
+ UNLOCK_HTC_RX(target);
+ UNLOCK_HTC_TX(target);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+AR6K_DEVICE *HTCGetAR6KDevice(void *HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ return &target->Device;
+}
+
diff --git a/drivers/net/wireless/ath6kl/htc2/htc_debug.h b/drivers/net/wireless/ath6kl/htc2/htc_debug.h
new file mode 100644
index 000000000000..0ceecf0dd36c
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc_debug.h
@@ -0,0 +1,34 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_debug.h" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#ifndef HTC_DEBUG_H_
+#define HTC_DEBUG_H_
+
+#define ATH_MODULE_NAME htc
+#include "a_debug.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_IRQ ATH_DEBUG_MAKE_MODULE_MASK(4)
+
+
+#endif /*HTC_DEBUG_H_*/
diff --git a/drivers/net/wireless/ath6kl/htc2/htc_internal.h b/drivers/net/wireless/ath6kl/htc2/htc_internal.h
new file mode 100644
index 000000000000..fbff23f598f1
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc_internal.h
@@ -0,0 +1,213 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_internal.h" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#ifndef _HTC_INTERNAL_H_
+#define _HTC_INTERNAL_H_
+
+/* for debugging, uncomment this to capture the last frame header, on frame header
+ * processing errors, the last frame header is dump for comparison */
+//#define HTC_CAPTURE_LAST_FRAME
+
+//#define HTC_EP_STAT_PROFILING
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Header files */
+
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "a_osapi.h"
+#include "htc_debug.h"
+#include "htc.h"
+#include "htc_api.h"
+#include "bmi_msg.h"
+#include "hif.h"
+#include "AR6000/ar6k.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_HOST_MAX_MSG_PER_BUNDLE 8
+#define HTC_MIN_HTC_MSGS_TO_BUNDLE 2
+
+/* packet flags */
+
+#define HTC_RX_PKT_IGNORE_LOOKAHEAD (1 << 0)
+#define HTC_RX_PKT_REFRESH_HDR (1 << 1)
+#define HTC_RX_PKT_PART_OF_BUNDLE (1 << 2)
+#define HTC_RX_PKT_NO_RECYCLE (1 << 3)
+
+/* scatter request flags */
+
+#define HTC_SCATTER_REQ_FLAGS_PARTIAL_BUNDLE (1 << 0)
+
+typedef struct _HTC_ENDPOINT {
+ HTC_ENDPOINT_ID Id;
+ HTC_SERVICE_ID ServiceID; /* service ID this endpoint is bound to
+ non-zero value means this endpoint is in use */
+ HTC_PACKET_QUEUE TxQueue; /* HTC frame buffer TX queue */
+ HTC_PACKET_QUEUE RxBuffers; /* HTC frame buffer RX list */
+ HTC_ENDPOINT_CREDIT_DIST CreditDist; /* credit distribution structure (exposed to driver layer) */
+ HTC_EP_CALLBACKS EpCallBacks; /* callbacks associated with this endpoint */
+ int MaxTxQueueDepth; /* max depth of the TX queue before we need to
+ call driver's full handler */
+ int MaxMsgLength; /* max length of endpoint message */
+ int TxProcessCount; /* reference count to continue tx processing */
+ HTC_PACKET_QUEUE RecvIndicationQueue; /* recv packets ready to be indicated */
+ int RxProcessCount; /* reference count to allow single processing context */
+ struct _HTC_TARGET *target; /* back pointer to target */
+ A_UINT8 SeqNo; /* TX seq no (helpful) for debugging */
+ A_UINT32 LocalConnectionFlags; /* local connection flags */
+#ifdef HTC_EP_STAT_PROFILING
+ HTC_ENDPOINT_STATS EndPointStats; /* endpoint statistics */
+#endif
+} HTC_ENDPOINT;
+
+#ifdef HTC_EP_STAT_PROFILING
+#define INC_HTC_EP_STAT(p,stat,count) (p)->EndPointStats.stat += (count);
+#else
+#define INC_HTC_EP_STAT(p,stat,count)
+#endif
+
+#define HTC_SERVICE_TX_PACKET_TAG HTC_TX_PACKET_TAG_INTERNAL
+
+#define NUM_CONTROL_BUFFERS 8
+#define NUM_CONTROL_TX_BUFFERS 2
+#define NUM_CONTROL_RX_BUFFERS (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS)
+
+typedef struct HTC_CONTROL_BUFFER {
+ HTC_PACKET HtcPacket;
+ A_UINT8 *Buffer;
+} HTC_CONTROL_BUFFER;
+
+#define HTC_RECV_WAIT_BUFFERS (1 << 0)
+#define HTC_OP_STATE_STOPPING (1 << 0)
+
+/* our HTC target state */
+typedef struct _HTC_TARGET {
+ HTC_ENDPOINT EndPoint[ENDPOINT_MAX];
+ HTC_CONTROL_BUFFER HTCControlBuffers[NUM_CONTROL_BUFFERS];
+ HTC_ENDPOINT_CREDIT_DIST *EpCreditDistributionListHead;
+ HTC_PACKET_QUEUE ControlBufferTXFreeList;
+ HTC_PACKET_QUEUE ControlBufferRXFreeList;
+ HTC_CREDIT_DIST_CALLBACK DistributeCredits;
+ HTC_CREDIT_INIT_CALLBACK InitCredits;
+ void *pCredDistContext;
+ int TargetCredits;
+ unsigned int TargetCreditSize;
+ A_MUTEX_T HTCLock;
+ A_MUTEX_T HTCRxLock;
+ A_MUTEX_T HTCTxLock;
+ AR6K_DEVICE Device; /* AR6K - specific state */
+ A_UINT32 OpStateFlags;
+ A_UINT32 RecvStateFlags;
+ HTC_ENDPOINT_ID EpWaitingForBuffers;
+ A_BOOL TargetFailure;
+#ifdef HTC_CAPTURE_LAST_FRAME
+ HTC_FRAME_HDR LastFrameHdr; /* useful for debugging */
+ A_UINT8 LastTrailer[256];
+ A_UINT8 LastTrailerLength;
+#endif
+ HTC_INIT_INFO HTCInitInfo;
+ A_UINT8 HTCTargetVersion;
+ int MaxMsgPerBundle; /* max messages per bundle for HTC */
+ A_BOOL SendBundlingEnabled; /* run time enable for send bundling (dynamic) */
+ int RecvBundlingEnabled; /* run time enable for recv bundling (dynamic) */
+} HTC_TARGET;
+
+#define HTC_STOPPING(t) ((t)->OpStateFlags & HTC_OP_STATE_STOPPING)
+#define LOCK_HTC(t) A_MUTEX_LOCK(&(t)->HTCLock);
+#define UNLOCK_HTC(t) A_MUTEX_UNLOCK(&(t)->HTCLock);
+#define LOCK_HTC_RX(t) A_MUTEX_LOCK(&(t)->HTCRxLock);
+#define UNLOCK_HTC_RX(t) A_MUTEX_UNLOCK(&(t)->HTCRxLock);
+#define LOCK_HTC_TX(t) A_MUTEX_LOCK(&(t)->HTCTxLock);
+#define UNLOCK_HTC_TX(t) A_MUTEX_UNLOCK(&(t)->HTCTxLock);
+
+#define GET_HTC_TARGET_FROM_HANDLE(hnd) ((HTC_TARGET *)(hnd))
+#define HTC_RECYCLE_RX_PKT(target,p,e) \
+{ \
+ if ((p)->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_NO_RECYCLE) { \
+ HTC_PACKET_RESET_RX(pPacket); \
+ pPacket->Status = A_ECANCELED; \
+ (e)->EpCallBacks.EpRecv((e)->EpCallBacks.pContext, \
+ (p)); \
+ } else { \
+ HTC_PACKET_RESET_RX(pPacket); \
+ HTCAddReceivePkt((HTC_HANDLE)(target),(p)); \
+ } \
+}
+
+/* internal HTC functions */
+void HTCControlTxComplete(void *Context, HTC_PACKET *pPacket);
+void HTCControlRecv(void *Context, HTC_PACKET *pPacket);
+A_STATUS HTCWaitforControlMessage(HTC_TARGET *target, HTC_PACKET **ppControlPacket);
+HTC_PACKET *HTCAllocControlBuffer(HTC_TARGET *target, HTC_PACKET_QUEUE *pList);
+void HTCFreeControlBuffer(HTC_TARGET *target, HTC_PACKET *pPacket, HTC_PACKET_QUEUE *pList);
+A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket);
+void HTCRecvCompleteHandler(void *Context, HTC_PACKET *pPacket);
+A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 MsgLookAheads[], int NumLookAheads, A_BOOL *pAsyncProc, int *pNumPktsFetched);
+void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint);
+A_STATUS HTCSendSetupComplete(HTC_TARGET *target);
+void HTCFlushRecvBuffers(HTC_TARGET *target);
+void HTCFlushSendPkts(HTC_TARGET *target);
+void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist);
+void DumpCreditDistStates(HTC_TARGET *target);
+void DebugDumpBytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription);
+
+static INLINE HTC_PACKET *HTC_ALLOC_CONTROL_TX(HTC_TARGET *target) {
+ HTC_PACKET *pPacket = HTCAllocControlBuffer(target,&target->ControlBufferTXFreeList);
+ if (pPacket != NULL) {
+ /* set payload pointer area with some headroom */
+ pPacket->pBuffer = pPacket->pBufferStart + HTC_HDR_LENGTH;
+ }
+ return pPacket;
+}
+
+#define HTC_FREE_CONTROL_TX(t,p) HTCFreeControlBuffer((t),(p),&(t)->ControlBufferTXFreeList)
+#define HTC_ALLOC_CONTROL_RX(t) HTCAllocControlBuffer((t),&(t)->ControlBufferRXFreeList)
+#define HTC_FREE_CONTROL_RX(t,p) \
+{ \
+ HTC_PACKET_RESET_RX(p); \
+ HTCFreeControlBuffer((t),(p),&(t)->ControlBufferRXFreeList); \
+}
+
+#define HTC_PREPARE_SEND_PKT(pP,sendflags,ctrl0,ctrl1) \
+{ \
+ A_UINT8 *pHdrBuf; \
+ (pP)->pBuffer -= HTC_HDR_LENGTH; \
+ pHdrBuf = (pP)->pBuffer; \
+ A_SET_UINT16_FIELD(pHdrBuf,HTC_FRAME_HDR,PayloadLen,(A_UINT16)(pP)->ActualLength); \
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,Flags,(sendflags)); \
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,EndpointID, (A_UINT8)(pP)->Endpoint); \
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,ControlBytes[0], (A_UINT8)(ctrl0)); \
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,ControlBytes[1], (A_UINT8)(ctrl1)); \
+}
+
+#define HTC_UNPREPARE_SEND_PKT(pP) \
+ (pP)->pBuffer += HTC_HDR_LENGTH; \
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTC_INTERNAL_H_ */
diff --git a/drivers/net/wireless/ath6kl/htc2/htc_recv.c b/drivers/net/wireless/ath6kl/htc2/htc_recv.c
new file mode 100644
index 000000000000..2b4c130b9467
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc_recv.c
@@ -0,0 +1,1545 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_recv.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#include "htc_internal.h"
+
+#define HTCIssueRecv(t, p) \
+ DevRecvPacket(&(t)->Device, \
+ (p), \
+ (p)->ActualLength)
+
+#define DO_RCV_COMPLETION(e,q) DoRecvCompletion(e,q)
+
+#define DUMP_RECV_PKT_INFO(pP) \
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC RECV packet 0x%X (%d bytes) (hdr:0x%X) on ep : %d \n", \
+ (A_UINT32)(pP), \
+ (pP)->ActualLength, \
+ (pP)->PktInfo.AsRx.ExpectedHdr, \
+ (pP)->Endpoint))
+
+#ifdef HTC_EP_STAT_PROFILING
+#define HTC_RX_STAT_PROFILE(t,ep,numLookAheads) \
+{ \
+ INC_HTC_EP_STAT((ep), RxReceived, 1); \
+ if ((numLookAheads) == 1) { \
+ INC_HTC_EP_STAT((ep), RxLookAheads, 1); \
+ } else if ((numLookAheads) > 1) { \
+ INC_HTC_EP_STAT((ep), RxBundleLookAheads, 1); \
+ } \
+}
+#else
+#define HTC_RX_STAT_PROFILE(t,ep,lookAhead)
+#endif
+
+static void DoRecvCompletion(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 */
+ do {
+ pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC calling ep %d recv callback on packet 0x%X \n", \
+ pEndpoint->Id, (A_UINT32)(pPacket)));
+ pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext, pPacket);
+ } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));
+ }
+
+ } while (FALSE);
+
+}
+
+static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target,
+ A_UINT8 *pBuffer,
+ int Length,
+ A_UINT32 *pNextLookAheads,
+ int *pNumLookAheads,
+ HTC_ENDPOINT_ID FromEndpoint)
+{
+ HTC_RECORD_HDR *pRecord;
+ A_UINT8 *pRecordBuf;
+ HTC_LOOKAHEAD_REPORT *pLookAhead;
+ A_UINT8 *pOrigBuffer;
+ int origLength;
+ A_STATUS status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (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);
+
+ if (pRecord->Length > 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",
+ pRecord->Length, pRecord->RecordID, Length));
+ status = A_EPROTO;
+ break;
+ }
+ /* start of record follows the header */
+ pRecordBuf = pBuffer;
+
+ switch (pRecord->RecordID) {
+ case HTC_RECORD_CREDITS:
+ AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_CREDIT_REPORT));
+ HTCProcessCreditRpt(target,
+ (HTC_CREDIT_REPORT *)pRecordBuf,
+ pRecord->Length / (sizeof(HTC_CREDIT_REPORT)),
+ FromEndpoint);
+ break;
+ case HTC_RECORD_LOOKAHEAD:
+ AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_LOOKAHEAD_REPORT));
+ pLookAhead = (HTC_LOOKAHEAD_REPORT *)pRecordBuf;
+ if ((pLookAhead->PreValid == ((~pLookAhead->PostValid) & 0xFF)) &&
+ (pNextLookAheads != NULL)) {
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ (" LookAhead Report Found (pre valid:0x%X, post valid:0x%X) \n",
+ pLookAhead->PreValid,
+ pLookAhead->PostValid));
+
+ /* look ahead bytes are valid, copy them over */
+ ((A_UINT8 *)(&pNextLookAheads[0]))[0] = pLookAhead->LookAhead[0];
+ ((A_UINT8 *)(&pNextLookAheads[0]))[1] = pLookAhead->LookAhead[1];
+ ((A_UINT8 *)(&pNextLookAheads[0]))[2] = pLookAhead->LookAhead[2];
+ ((A_UINT8 *)(&pNextLookAheads[0]))[3] = pLookAhead->LookAhead[3];
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ DebugDumpBytes((A_UINT8 *)pNextLookAheads,4,"Next Look Ahead");
+ }
+ /* just one normal lookahead */
+ *pNumLookAheads = 1;
+ }
+ break;
+ case HTC_RECORD_LOOKAHEAD_BUNDLE:
+ AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT));
+ if (pRecord->Length >= sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT) &&
+ (pNextLookAheads != NULL)) {
+ HTC_BUNDLED_LOOKAHEAD_REPORT *pBundledLookAheadRpt;
+ int i;
+
+ pBundledLookAheadRpt = (HTC_BUNDLED_LOOKAHEAD_REPORT *)pRecordBuf;
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ DebugDumpBytes(pRecordBuf,pRecord->Length,"Bundle LookAhead");
+ }
+
+ if ((pRecord->Length / (sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT))) >
+ HTC_HOST_MAX_MSG_PER_BUNDLE) {
+ /* this should never happen, the target restricts the number
+ * of messages per bundle configured by the host */
+ A_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ for (i = 0; i < (int)(pRecord->Length / (sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT))); i++) {
+ ((A_UINT8 *)(&pNextLookAheads[i]))[0] = pBundledLookAheadRpt->LookAhead[0];
+ ((A_UINT8 *)(&pNextLookAheads[i]))[1] = pBundledLookAheadRpt->LookAhead[1];
+ ((A_UINT8 *)(&pNextLookAheads[i]))[2] = pBundledLookAheadRpt->LookAhead[2];
+ ((A_UINT8 *)(&pNextLookAheads[i]))[3] = pBundledLookAheadRpt->LookAhead[3];
+ pBundledLookAheadRpt++;
+ }
+
+ *pNumLookAheads = i;
+ }
+ break;
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" unhandled record: id:%d length:%d \n",
+ pRecord->RecordID, pRecord->Length));
+ break;
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* advance buffer past this record for next time around */
+ pBuffer += pRecord->Length;
+ Length -= pRecord->Length;
+ }
+
+ if (A_FAILED(status)) {
+ DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer");
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n"));
+ return status;
+
+}
+
+/* process a received message (i.e. strip off header, process any trailer data)
+ * note : locks must be released when this function is called */
+static A_STATUS HTCProcessRecvHeader(HTC_TARGET *target,
+ HTC_PACKET *pPacket,
+ A_UINT32 *pNextLookAheads,
+ int *pNumLookAheads)
+{
+ A_UINT8 temp;
+ A_UINT8 *pBuf;
+ A_STATUS status = A_OK;
+ A_UINT16 payloadLen;
+ A_UINT32 lookAhead;
+
+ pBuf = pPacket->pBuffer;
+
+ if (pNumLookAheads != NULL) {
+ *pNumLookAheads = 0;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader \n"));
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ AR_DEBUG_PRINTBUF(pBuf,pPacket->ActualLength,"HTC Recv PKT");
+ }
+
+ do {
+ /* note, we cannot assume the alignment of pBuffer, so we use the safe macros to
+ * retrieve 16 bit fields */
+ payloadLen = A_GET_UINT16_FIELD(pBuf, HTC_FRAME_HDR, PayloadLen);
+
+ ((A_UINT8 *)&lookAhead)[0] = pBuf[0];
+ ((A_UINT8 *)&lookAhead)[1] = pBuf[1];
+ ((A_UINT8 *)&lookAhead)[2] = pBuf[2];
+ ((A_UINT8 *)&lookAhead)[3] = pBuf[3];
+
+ if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_REFRESH_HDR) {
+ /* refresh expected hdr, since this was unknown at the time we grabbed the packets
+ * as part of a bundle */
+ pPacket->PktInfo.AsRx.ExpectedHdr = lookAhead;
+ /* refresh actual length since we now have the real header */
+ pPacket->ActualLength = payloadLen + HTC_HDR_LENGTH;
+
+ /* validate the actual header that was refreshed */
+ if (pPacket->ActualLength > pPacket->BufferLength) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Refreshed HDR payload length (%d) in bundled RECV is invalid (hdr: 0x%X) \n",
+ payloadLen, lookAhead));
+ /* limit this to max buffer just to print out some of the buffer */
+ pPacket->ActualLength = min(pPacket->ActualLength, pPacket->BufferLength);
+ status = A_EPROTO;
+ break;
+ }
+
+ if (pPacket->Endpoint != A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, EndpointID)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Refreshed HDR endpoint (%d) does not match expected endpoint (%d) \n",
+ A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, EndpointID), pPacket->Endpoint));
+ status = A_EPROTO;
+ break;
+ }
+ }
+
+ if (lookAhead != pPacket->PktInfo.AsRx.ExpectedHdr) {
+ /* somehow the lookahead that gave us the full read length did not
+ * reflect the actual header in the pending message */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCProcessRecvHeader, lookahead mismatch! (pPkt:0x%X flags:0x%X) \n",
+ (A_UINT32)pPacket, pPacket->PktInfo.AsRx.HTCRxFlags));
+ DebugDumpBytes((A_UINT8 *)&pPacket->PktInfo.AsRx.ExpectedHdr,4,"Expected Message LookAhead");
+ DebugDumpBytes(pBuf,sizeof(HTC_FRAME_HDR),"Current Frame Header");
+#ifdef HTC_CAPTURE_LAST_FRAME
+ DebugDumpBytes((A_UINT8 *)&target->LastFrameHdr,sizeof(HTC_FRAME_HDR),"Last Frame Header");
+ if (target->LastTrailerLength != 0) {
+ DebugDumpBytes(target->LastTrailer,
+ target->LastTrailerLength,
+ "Last trailer");
+ }
+#endif
+ status = A_EPROTO;
+ break;
+ }
+
+ /* get flags */
+ temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, Flags);
+
+ if (temp & HTC_FLAGS_RECV_TRAILER) {
+ /* this packet has a trailer */
+
+ /* extract the trailer length in control byte 0 */
+ temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, ControlBytes[0]);
+
+ if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCProcessRecvHeader, invalid header (payloadlength should be :%d, CB[0] is:%d) \n",
+ payloadLen, temp));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
+ /* this packet was fetched as part of an HTC bundle, the embedded lookahead is
+ * not valid since the next packet may have already been fetched as part of the
+ * bundle */
+ pNextLookAheads = NULL;
+ pNumLookAheads = NULL;
+ }
+
+ /* process trailer data that follows HDR + application payload */
+ status = HTCProcessTrailer(target,
+ (pBuf + HTC_HDR_LENGTH + payloadLen - temp),
+ temp,
+ pNextLookAheads,
+ pNumLookAheads,
+ pPacket->Endpoint);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+#ifdef HTC_CAPTURE_LAST_FRAME
+ A_MEMCPY(target->LastTrailer, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp);
+ target->LastTrailerLength = temp;
+#endif
+ /* trim length by trailer bytes */
+ pPacket->ActualLength -= temp;
+ }
+#ifdef HTC_CAPTURE_LAST_FRAME
+ else {
+ target->LastTrailerLength = 0;
+ }
+#endif
+
+ /* if we get to this point, the packet is good */
+ /* remove header and adjust length */
+ pPacket->pBuffer += HTC_HDR_LENGTH;
+ pPacket->ActualLength -= HTC_HDR_LENGTH;
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ /* dump the whole packet */
+ DebugDumpBytes(pBuf,pPacket->ActualLength < 256 ? pPacket->ActualLength : 256 ,"BAD HTC Recv PKT");
+ } else {
+#ifdef HTC_CAPTURE_LAST_FRAME
+ A_MEMCPY(&target->LastFrameHdr,pBuf,sizeof(HTC_FRAME_HDR));
+#endif
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ if (pPacket->ActualLength > 0) {
+ AR_DEBUG_PRINTBUF(pPacket->pBuffer,pPacket->ActualLength,"HTC - Application Msg");
+ }
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessRecvHeader \n"));
+ return status;
+}
+
+static INLINE void HTCAsyncRecvCheckMorePackets(HTC_TARGET *target,
+ A_UINT32 NextLookAheads[],
+ int NumLookAheads,
+ A_BOOL CheckMoreMsgs)
+{
+ /* was there a lookahead for the next packet? */
+ if (NumLookAheads > 0) {
+ A_STATUS nextStatus;
+ int fetched = 0;
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTCAsyncRecvCheckMorePackets - num lookaheads were non-zero : %d \n",
+ NumLookAheads));
+ /* force status re-check */
+ REF_IRQ_STATUS_RECHECK(&target->Device);
+ /* we have more packets, get the next packet fetch started */
+ nextStatus = HTCRecvMessagePendingHandler(target, NextLookAheads, NumLookAheads, NULL, &fetched);
+ if (A_EPROTO == nextStatus) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Next look ahead from recv header was INVALID\n"));
+ DebugDumpBytes((A_UINT8 *)NextLookAheads,
+ NumLookAheads * (sizeof(A_UINT32)),
+ "BAD lookaheads from lookahead report");
+ }
+ if (A_SUCCESS(nextStatus) && !fetched) {
+ /* we could not fetch any more packets due to resources */
+ DevAsyncIrqProcessComplete(&target->Device);
+ }
+ } else {
+ if (CheckMoreMsgs) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTCAsyncRecvCheckMorePackets - rechecking for more messages...\n"));
+ /* if we did not get anything on the look-ahead,
+ * call device layer to asynchronously re-check for messages. If we can keep the async
+ * processing going we get better performance. If there is a pending message we will keep processing
+ * messages asynchronously which should pipeline things nicely */
+ DevCheckPendingRecvMsgsAsync(&target->Device);
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("HTCAsyncRecvCheckMorePackets - no check \n"));
+ }
+ }
+
+
+}
+
+ /* unload the recv completion queue */
+static INLINE void DrainRecvIndicationQueue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
+{
+ HTC_PACKET_QUEUE recvCompletions;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+DrainRecvIndicationQueue \n"));
+
+ INIT_HTC_PACKET_QUEUE(&recvCompletions);
+
+ LOCK_HTC_RX(target);
+
+ /* increment rx processing count on entry */
+ pEndpoint->RxProcessCount++;
+ if (pEndpoint->RxProcessCount > 1) {
+ pEndpoint->RxProcessCount--;
+ /* another thread or task is draining the RX completion queue on this endpoint
+ * that thread will reset the rx processing count when the queue is drained */
+ UNLOCK_HTC_RX(target);
+ return;
+ }
+
+ /******* at this point only 1 thread may enter ******/
+
+ while (TRUE) {
+
+ /* transfer items from main recv queue to the local one so we can release the lock */
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&recvCompletions, &pEndpoint->RecvIndicationQueue);
+
+ if (HTC_QUEUE_EMPTY(&recvCompletions)) {
+ /* all drained */
+ break;
+ }
+
+ /* release lock while we do the recv completions
+ * other threads can now queue more recv completions */
+ UNLOCK_HTC_RX(target);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("DrainRecvIndicationQueue : completing %d RECV packets \n",
+ HTC_PACKET_QUEUE_DEPTH(&recvCompletions)));
+ /* do completion */
+ DO_RCV_COMPLETION(pEndpoint,&recvCompletions);
+
+ /* re-acquire lock to grab some more completions */
+ LOCK_HTC_RX(target);
+ }
+
+ /* reset count */
+ pEndpoint->RxProcessCount = 0;
+ UNLOCK_HTC_RX(target);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-DrainRecvIndicationQueue \n"));
+
+}
+
+ /* optimization for recv packets, we can indicate a "hint" that there are more
+ * single-packets to fetch on this endpoint */
+#define SET_MORE_RX_PACKET_INDICATION_FLAG(L,N,E,P) \
+ if ((N) > 0) { SetRxPacketIndicationFlags((L)[0],(E),(P)); }
+
+ /* for bundled frames, we can force the flag to indicate there are more packets */
+#define FORCE_MORE_RX_PACKET_INDICATION_FLAG(P) \
+ (P)->PktInfo.AsRx.IndicationFlags |= HTC_RX_FLAGS_INDICATE_MORE_PKTS;
+
+ /* note: this function can be called with the RX lock held */
+static INLINE void SetRxPacketIndicationFlags(A_UINT32 LookAhead,
+ HTC_ENDPOINT *pEndpoint,
+ HTC_PACKET *pPacket)
+{
+ HTC_FRAME_HDR *pHdr = (HTC_FRAME_HDR *)&LookAhead;
+ /* check to see if the "next" packet is from the same endpoint of the
+ completing packet */
+ if (pHdr->EndpointID == pPacket->Endpoint) {
+ /* check that there is a buffer available to actually fetch it */
+ if (!HTC_QUEUE_EMPTY(&pEndpoint->RxBuffers)) {
+ /* provide a hint that there are more RX packets to fetch */
+ FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);
+ }
+ }
+}
+
+
+/* asynchronous completion handler for recv packet fetching, when the device layer
+ * completes a read request, it will call this completion handler */
+void HTCRecvCompleteHandler(void *Context, HTC_PACKET *pPacket)
+{
+ HTC_TARGET *target = (HTC_TARGET *)Context;
+ HTC_ENDPOINT *pEndpoint;
+ A_UINT32 nextLookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
+ int numLookAheads = 0;
+ A_STATUS status;
+ A_BOOL checkMorePkts = TRUE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (pkt:0x%X, status:%d, ep:%d) \n",
+ (A_UINT32)pPacket, pPacket->Status, pPacket->Endpoint));
+
+ A_ASSERT(!IS_DEV_IRQ_PROC_SYNC_MODE(&target->Device));
+ AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
+ pEndpoint = &target->EndPoint[pPacket->Endpoint];
+ pPacket->Completion = NULL;
+
+ /* get completion status */
+ status = pPacket->Status;
+
+ do {
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCRecvCompleteHandler: request failed (status:%d, ep:%d) \n",
+ pPacket->Status, pPacket->Endpoint));
+ break;
+ }
+ /* process the header for any trailer data */
+ status = HTCProcessRecvHeader(target,pPacket,nextLookAheads,&numLookAheads);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
+ /* this packet was part of a bundle that had to be broken up.
+ * It was fetched one message at a time. There may be other asynchronous reads queued behind this one.
+ * Do no issue another check for more packets since the last one in the series of requests
+ * will handle it */
+ checkMorePkts = FALSE;
+ }
+
+ DUMP_RECV_PKT_INFO(pPacket);
+ LOCK_HTC_RX(target);
+ SET_MORE_RX_PACKET_INDICATION_FLAG(nextLookAheads,numLookAheads,pEndpoint,pPacket);
+ /* we have a good packet, queue it to the completion queue */
+ HTC_PACKET_ENQUEUE(&pEndpoint->RecvIndicationQueue,pPacket);
+ HTC_RX_STAT_PROFILE(target,pEndpoint,numLookAheads);
+ UNLOCK_HTC_RX(target);
+
+ /* check for more recv packets before indicating */
+ HTCAsyncRecvCheckMorePackets(target,nextLookAheads,numLookAheads,checkMorePkts);
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCRecvCompleteHandler , message fetch failed (status = %d) \n",
+ status));
+ /* recycle this packet */
+ HTC_RECYCLE_RX_PKT(target, pPacket, pEndpoint);
+ } else {
+ /* a good packet was queued, drain the queue */
+ DrainRecvIndicationQueue(target,pEndpoint);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCRecvCompleteHandler\n"));
+}
+
+/* synchronously wait for a control message from the target,
+ * This function is used at initialization time ONLY. At init messages
+ * on ENDPOINT 0 are expected. */
+A_STATUS HTCWaitforControlMessage(HTC_TARGET *target, HTC_PACKET **ppControlPacket)
+{
+ A_STATUS status;
+ A_UINT32 lookAhead;
+ HTC_PACKET *pPacket = NULL;
+ HTC_FRAME_HDR *pHdr;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCWaitforControlMessage \n"));
+
+ do {
+
+ *ppControlPacket = NULL;
+
+ /* call the polling function to see if we have a message */
+ status = DevPollMboxMsgRecv(&target->Device,
+ &lookAhead,
+ HTC_TARGET_RESPONSE_TIMEOUT);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTCWaitforControlMessage : lookAhead : 0x%X \n", lookAhead));
+
+ /* check the lookahead */
+ pHdr = (HTC_FRAME_HDR *)&lookAhead;
+
+ if (pHdr->EndpointID != ENDPOINT_0) {
+ /* unexpected endpoint number, should be zero */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ if (A_FAILED(status)) {
+ /* bad message */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ pPacket = HTC_ALLOC_CONTROL_RX(target);
+
+ if (pPacket == NULL) {
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ pPacket->PktInfo.AsRx.HTCRxFlags = 0;
+ pPacket->PktInfo.AsRx.ExpectedHdr = lookAhead;
+ pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
+
+ if (pPacket->ActualLength > pPacket->BufferLength) {
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ /* we want synchronous operation */
+ pPacket->Completion = NULL;
+
+ /* get the message from the device, this will block */
+ status = HTCIssueRecv(target, pPacket);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* process receive header */
+ status = HTCProcessRecvHeader(target,pPacket,NULL,NULL);
+
+ pPacket->Status = status;
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCWaitforControlMessage, HTCProcessRecvHeader failed (status = %d) \n",
+ status));
+ break;
+ }
+
+ /* give the caller this control message packet, they are responsible to free */
+ *ppControlPacket = pPacket;
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ if (pPacket != NULL) {
+ /* cleanup buffer on error */
+ HTC_FREE_CONTROL_RX(target,pPacket);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCWaitforControlMessage \n"));
+
+ return status;
+}
+
+static A_STATUS AllocAndPrepareRxPackets(HTC_TARGET *target,
+ A_UINT32 LookAheads[],
+ int Messages,
+ HTC_ENDPOINT *pEndpoint,
+ HTC_PACKET_QUEUE *pQueue)
+{
+ A_STATUS status = A_OK;
+ HTC_PACKET *pPacket;
+ HTC_FRAME_HDR *pHdr;
+ int i,j;
+ int numMessages;
+ int fullLength;
+ A_BOOL noRecycle;
+
+ /* lock RX while we assemble the packet buffers */
+ LOCK_HTC_RX(target);
+
+ for (i = 0; i < Messages; i++) {
+
+ pHdr = (HTC_FRAME_HDR *)&LookAheads[i];
+
+ if (pHdr->EndpointID >= ENDPOINT_MAX) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Endpoint in look-ahead: %d \n",pHdr->EndpointID));
+ /* invalid endpoint */
+ status = A_EPROTO;
+ break;
+ }
+
+ if (pHdr->EndpointID != pEndpoint->Id) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Endpoint in look-ahead: %d should be : %d (index:%d)\n",
+ pHdr->EndpointID, pEndpoint->Id, i));
+ /* invalid endpoint */
+ status = A_EPROTO;
+ break;
+ }
+
+ if (pHdr->PayloadLen > HTC_MAX_PAYLOAD_LENGTH) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Payload length %d exceeds max HTC : %d !\n",
+ pHdr->PayloadLen, HTC_MAX_PAYLOAD_LENGTH));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (0 == pEndpoint->ServiceID) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Endpoint %d is not connected !\n",pHdr->EndpointID));
+ /* endpoint isn't even connected */
+ status = A_EPROTO;
+ break;
+ }
+
+ if ((pHdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK) == 0) {
+ /* HTC header only indicates 1 message to fetch */
+ numMessages = 1;
+ } else {
+ /* HTC header indicates that every packet to follow has the same padded length so that it can
+ * be optimally fetched as a full bundle */
+ numMessages = (pHdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK) >> HTC_FLAGS_RECV_BUNDLE_CNT_SHIFT;
+ /* the count doesn't include the starter frame, just a count of frames to follow */
+ numMessages++;
+ A_ASSERT(numMessages <= target->MaxMsgPerBundle);
+ INC_HTC_EP_STAT(pEndpoint, RxBundleIndFromHdr, 1);
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTC header indicates :%d messages can be fetched as a bundle \n",numMessages));
+ }
+
+ fullLength = DEV_CALC_RECV_PADDED_LEN(&target->Device,pHdr->PayloadLen + sizeof(HTC_FRAME_HDR));
+
+ /* get packet buffers for each message, if there was a bundle detected in the header,
+ * use pHdr as a template to fetch all packets in the bundle */
+ for (j = 0; j < numMessages; j++) {
+
+ /* reset flag, any packets allocated using the RecvAlloc() API cannot be recycled on cleanup,
+ * they must be explicitly returned */
+ noRecycle = FALSE;
+
+ if (pEndpoint->EpCallBacks.EpRecvAlloc != NULL) {
+ UNLOCK_HTC_RX(target);
+ noRecycle = TRUE;
+ /* user is using a per-packet allocation callback */
+ pPacket = pEndpoint->EpCallBacks.EpRecvAlloc(pEndpoint->EpCallBacks.pContext,
+ pEndpoint->Id,
+ fullLength);
+ LOCK_HTC_RX(target);
+
+ } else if ((pEndpoint->EpCallBacks.EpRecvAllocThresh != NULL) &&
+ (fullLength > pEndpoint->EpCallBacks.RecvAllocThreshold)) {
+ INC_HTC_EP_STAT(pEndpoint,RxAllocThreshHit,1);
+ INC_HTC_EP_STAT(pEndpoint,RxAllocThreshBytes,pHdr->PayloadLen);
+ /* threshold was hit, call the special recv allocation callback */
+ UNLOCK_HTC_RX(target);
+ noRecycle = TRUE;
+ /* user wants to allocate packets above a certain threshold */
+ pPacket = pEndpoint->EpCallBacks.EpRecvAllocThresh(pEndpoint->EpCallBacks.pContext,
+ pEndpoint->Id,
+ fullLength);
+ LOCK_HTC_RX(target);
+
+ } else {
+ /* user is using a refill handler that can refill multiple HTC buffers */
+
+ /* get a packet from the endpoint recv queue */
+ pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
+
+ if (NULL == pPacket) {
+ /* check for refill handler */
+ if (pEndpoint->EpCallBacks.EpRecvRefill != NULL) {
+ UNLOCK_HTC_RX(target);
+ /* call the re-fill handler */
+ pEndpoint->EpCallBacks.EpRecvRefill(pEndpoint->EpCallBacks.pContext,
+ pEndpoint->Id);
+ LOCK_HTC_RX(target);
+ /* check if we have more buffers */
+ pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
+ /* fall through */
+ }
+ }
+ }
+
+ if (NULL == pPacket) {
+ /* this is not an error, we simply need to mark that we are waiting for buffers.*/
+ target->RecvStateFlags |= HTC_RECV_WAIT_BUFFERS;
+ target->EpWaitingForBuffers = pEndpoint->Id;
+ status = A_NO_RESOURCE;
+ break;
+ }
+
+ AR_DEBUG_ASSERT(pPacket->Endpoint == pEndpoint->Id);
+ /* clear flags */
+ pPacket->PktInfo.AsRx.HTCRxFlags = 0;
+ pPacket->PktInfo.AsRx.IndicationFlags = 0;
+ pPacket->Status = A_OK;
+
+ if (noRecycle) {
+ /* flag that these packets cannot be recycled, they have to be returned to the
+ * user */
+ pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_NO_RECYCLE;
+ }
+ /* add packet to queue (also incase we need to cleanup down below) */
+ HTC_PACKET_ENQUEUE(pQueue,pPacket);
+
+ if (HTC_STOPPING(target)) {
+ status = A_ECANCELED;
+ break;
+ }
+
+ /* make sure this message can fit in the endpoint buffer */
+ if ((A_UINT32)fullLength > pPacket->BufferLength) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Payload Length Error : header reports payload of: %d (%d) endpoint buffer size: %d \n",
+ pHdr->PayloadLen, fullLength, pPacket->BufferLength));
+ status = A_EPROTO;
+ break;
+ }
+
+ if (j > 0) {
+ /* for messages fetched in a bundle the expected lookahead is unknown since we
+ * are only using the lookahead of the first packet as a template of what to
+ * expect for lengths */
+ /* flag that once we get the real HTC header we need to refesh the information */
+ pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_REFRESH_HDR;
+ /* set it to something invalid */
+ pPacket->PktInfo.AsRx.ExpectedHdr = 0xFFFFFFFF;
+ } else {
+
+ pPacket->PktInfo.AsRx.ExpectedHdr = LookAheads[i]; /* set expected look ahead */
+ }
+ /* set the amount of data to fetch */
+ pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
+ }
+
+ if (A_FAILED(status)) {
+ if (A_NO_RESOURCE == status) {
+ /* this is actually okay */
+ status = A_OK;
+ }
+ break;
+ }
+
+ }
+
+ UNLOCK_HTC_RX(target);
+
+ if (A_FAILED(status)) {
+ while (!HTC_QUEUE_EMPTY(pQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ /* recycle all allocated packets */
+ HTC_RECYCLE_RX_PKT(target,pPacket,&target->EndPoint[pPacket->Endpoint]);
+ }
+ }
+
+ return status;
+}
+
+static void HTCAsyncRecvScatterCompletion(HIF_SCATTER_REQ *pScatterReq)
+{
+ int i;
+ HTC_PACKET *pPacket;
+ HTC_ENDPOINT *pEndpoint;
+ A_UINT32 lookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
+ int numLookAheads = 0;
+ HTC_TARGET *target = (HTC_TARGET *)pScatterReq->Context;
+ A_STATUS status;
+ A_BOOL partialBundle = FALSE;
+ HTC_PACKET_QUEUE localRecvQueue;
+ A_BOOL procError = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCAsyncRecvScatterCompletion TotLen: %d Entries: %d\n",
+ pScatterReq->TotalLength, pScatterReq->ValidScatterEntries));
+
+ A_ASSERT(!IS_DEV_IRQ_PROC_SYNC_MODE(&target->Device));
+
+ if (A_FAILED(pScatterReq->CompletionStatus)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Recv Scatter Request Failed: %d \n",pScatterReq->CompletionStatus));
+ }
+
+ if (pScatterReq->CallerFlags & HTC_SCATTER_REQ_FLAGS_PARTIAL_BUNDLE) {
+ partialBundle = TRUE;
+ }
+
+ DEV_FINISH_SCATTER_OPERATION(pScatterReq);
+
+ INIT_HTC_PACKET_QUEUE(&localRecvQueue);
+
+ pPacket = (HTC_PACKET *)pScatterReq->ScatterList[0].pCallerContexts[0];
+ /* note: all packets in a scatter req are for the same endpoint ! */
+ pEndpoint = &target->EndPoint[pPacket->Endpoint];
+
+ /* walk through the scatter list and process */
+ /* **** NOTE: DO NOT HOLD ANY LOCKS here, HTCProcessRecvHeader can take the TX lock
+ * as it processes credit reports */
+ for (i = 0; i < pScatterReq->ValidScatterEntries; i++) {
+ pPacket = (HTC_PACKET *)pScatterReq->ScatterList[i].pCallerContexts[0];
+ A_ASSERT(pPacket != NULL);
+ /* reset count, we are only interested in the look ahead in the last packet when we
+ * break out of this loop */
+ numLookAheads = 0;
+
+ if (A_SUCCESS(pScatterReq->CompletionStatus)) {
+ /* process header for each of the recv packets */
+ status = HTCProcessRecvHeader(target,pPacket,lookAheads,&numLookAheads);
+ } else {
+ status = A_ERROR;
+ }
+
+ if (A_SUCCESS(status)) {
+#ifdef HTC_EP_STAT_PROFILING
+ LOCK_HTC_RX(target);
+ HTC_RX_STAT_PROFILE(target,pEndpoint,numLookAheads);
+ INC_HTC_EP_STAT(pEndpoint, RxPacketsBundled, 1);
+ UNLOCK_HTC_RX(target);
+#endif
+ if (i == (pScatterReq->ValidScatterEntries - 1)) {
+ /* last packet's more packets flag is set based on the lookahead */
+ SET_MORE_RX_PACKET_INDICATION_FLAG(lookAheads,numLookAheads,pEndpoint,pPacket);
+ } else {
+ /* packets in a bundle automatically have this flag set */
+ FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);
+ }
+
+ DUMP_RECV_PKT_INFO(pPacket);
+ /* since we can't hold a lock in this loop, we insert into our local recv queue for
+ * storage until we can transfer them to the recv completion queue */
+ HTC_PACKET_ENQUEUE(&localRecvQueue,pPacket);
+
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Recv packet scatter entry %d failed (out of %d) \n",
+ i, pScatterReq->ValidScatterEntries));
+ /* recycle failed recv */
+ HTC_RECYCLE_RX_PKT(target, pPacket, pEndpoint);
+ /* set flag and continue processing the remaining scatter entries */
+ procError = TRUE;
+ }
+
+ }
+
+ /* free scatter request */
+ DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
+
+ LOCK_HTC_RX(target);
+ /* transfer the packets in the local recv queue to the recv completion queue */
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RecvIndicationQueue, &localRecvQueue);
+
+ UNLOCK_HTC_RX(target);
+
+ if (!procError) {
+ /* pipeline the next check (asynchronously) for more packets */
+ HTCAsyncRecvCheckMorePackets(target,
+ lookAheads,
+ numLookAheads,
+ partialBundle ? FALSE : TRUE);
+ }
+
+ /* now drain the indication queue */
+ DrainRecvIndicationQueue(target,pEndpoint);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCAsyncRecvScatterCompletion \n"));
+}
+
+static A_STATUS HTCIssueRecvPacketBundle(HTC_TARGET *target,
+ HTC_PACKET_QUEUE *pRecvPktQueue,
+ HTC_PACKET_QUEUE *pSyncCompletionQueue,
+ int *pNumPacketsFetched,
+ A_BOOL PartialBundle)
+{
+ A_STATUS status = A_OK;
+ HIF_SCATTER_REQ *pScatterReq;
+ int i, totalLength;
+ int pktsToScatter;
+ HTC_PACKET *pPacket;
+ A_BOOL asyncMode = (pSyncCompletionQueue == NULL) ? TRUE : FALSE;
+ int scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_RECV_LENGTH(&target->Device);
+
+ pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue);
+ pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle);
+
+ if ((HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue) - pktsToScatter) > 0) {
+ /* we were forced to split this bundle receive operation
+ * all packets in this partial bundle must have their lookaheads ignored */
+ PartialBundle = TRUE;
+ /* this would only happen if the target ignored our max bundle limit */
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
+ ("HTCIssueRecvPacketBundle : partial bundle detected num:%d , %d \n",
+ HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue), pktsToScatter));
+ }
+
+ totalLength = 0;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCIssueRecvPacketBundle (Numpackets: %d , actual : %d) \n",
+ HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue), pktsToScatter));
+
+ do {
+
+ pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device);
+
+ if (pScatterReq == NULL) {
+ /* no scatter resources left, just let caller handle it the legacy way */
+ break;
+ }
+
+ pScatterReq->CallerFlags = 0;
+
+ if (PartialBundle) {
+ /* mark that this is a partial bundle, this has special ramifications to the
+ * scatter completion routine */
+ pScatterReq->CallerFlags |= HTC_SCATTER_REQ_FLAGS_PARTIAL_BUNDLE;
+ }
+
+ /* convert HTC packets to scatter list */
+ for (i = 0; i < pktsToScatter; i++) {
+ int paddedLength;
+
+ pPacket = HTC_PACKET_DEQUEUE(pRecvPktQueue);
+ A_ASSERT(pPacket != NULL);
+
+ paddedLength = DEV_CALC_RECV_PADDED_LEN(&target->Device, pPacket->ActualLength);
+
+ if ((scatterSpaceRemaining - paddedLength) < 0) {
+ /* exceeds what we can transfer, put the packet back */
+ HTC_PACKET_ENQUEUE_TO_HEAD(pRecvPktQueue,pPacket);
+ break;
+ }
+
+ scatterSpaceRemaining -= paddedLength;
+
+ if (PartialBundle || (i < (pktsToScatter - 1))) {
+ /* packet 0..n-1 cannot be checked for look-aheads since we are fetching a bundle
+ * the last packet however can have it's lookahead used */
+ pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_IGNORE_LOOKAHEAD;
+ }
+
+ /* note: 1 HTC packet per scatter entry */
+ /* setup packet into */
+ pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer;
+ pScatterReq->ScatterList[i].Length = paddedLength;
+
+ pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_PART_OF_BUNDLE;
+
+ if (asyncMode) {
+ /* save HTC packet for async completion routine */
+ pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket;
+ } else {
+ /* queue to caller's sync completion queue, caller will unload this when we return */
+ HTC_PACKET_ENQUEUE(pSyncCompletionQueue,pPacket);
+ }
+
+ A_ASSERT(pScatterReq->ScatterList[i].Length);
+ totalLength += pScatterReq->ScatterList[i].Length;
+ }
+
+ pScatterReq->TotalLength = totalLength;
+ pScatterReq->ValidScatterEntries = i;
+
+ if (asyncMode) {
+ pScatterReq->CompletionRoutine = HTCAsyncRecvScatterCompletion;
+ pScatterReq->Context = target;
+ }
+
+ status = DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_READ, asyncMode);
+
+ if (A_SUCCESS(status)) {
+ *pNumPacketsFetched = i;
+ }
+
+ if (!asyncMode) {
+ /* free scatter request */
+ DEV_FREE_SCATTER_REQ(&target->Device, pScatterReq);
+ }
+
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCIssueRecvPacketBundle (status:%d) (fetched:%d) \n",
+ status,*pNumPacketsFetched));
+
+ return status;
+}
+
+static INLINE void CheckRecvWaterMark(HTC_ENDPOINT *pEndpoint)
+{
+ /* see if endpoint is using a refill watermark
+ * ** no need to use a lock here, since we are only inspecting...
+ * caller may must not hold locks when calling this function */
+ if (pEndpoint->EpCallBacks.RecvRefillWaterMark > 0) {
+ if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->RxBuffers) < pEndpoint->EpCallBacks.RecvRefillWaterMark) {
+ /* call the re-fill handler before we continue */
+ pEndpoint->EpCallBacks.EpRecvRefill(pEndpoint->EpCallBacks.pContext,
+ pEndpoint->Id);
+ }
+ }
+}
+
+/* callback when device layer or lookahead report parsing detects a pending message */
+A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 MsgLookAheads[], int NumLookAheads, A_BOOL *pAsyncProc, int *pNumPktsFetched)
+{
+ HTC_TARGET *target = (HTC_TARGET *)Context;
+ A_STATUS status = A_OK;
+ HTC_PACKET *pPacket;
+ HTC_ENDPOINT *pEndpoint;
+ A_BOOL asyncProc = FALSE;
+ A_UINT32 lookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
+ int pktsFetched;
+ HTC_PACKET_QUEUE recvPktQueue, syncCompletedPktsQueue;
+ A_BOOL partialBundle;
+ HTC_ENDPOINT_ID id;
+ int totalFetched = 0;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler NumLookAheads: %d \n",NumLookAheads));
+
+ if (pNumPktsFetched != NULL) {
+ *pNumPktsFetched = 0;
+ }
+
+ if (IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(&target->Device)) {
+ /* We use async mode to get the packets if the device layer supports it.
+ * The device layer interfaces with HIF in which HIF may have restrictions on
+ * how interrupts are processed */
+ asyncProc = TRUE;
+ }
+
+ if (pAsyncProc != NULL) {
+ /* indicate to caller how we decided to process this */
+ *pAsyncProc = asyncProc;
+ }
+
+ if (NumLookAheads > HTC_HOST_MAX_MSG_PER_BUNDLE) {
+ A_ASSERT(FALSE);
+ return A_EPROTO;
+ }
+
+ /* on first entry copy the lookaheads into our temp array for processing */
+ A_MEMCPY(lookAheads, MsgLookAheads, (sizeof(A_UINT32)) * NumLookAheads);
+
+ while (TRUE) {
+
+ /* reset packets queues */
+ INIT_HTC_PACKET_QUEUE(&recvPktQueue);
+ INIT_HTC_PACKET_QUEUE(&syncCompletedPktsQueue);
+
+ if (NumLookAheads > HTC_HOST_MAX_MSG_PER_BUNDLE) {
+ status = A_EPROTO;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ /* first lookahead sets the expected endpoint IDs for all packets in a bundle */
+ id = ((HTC_FRAME_HDR *)&lookAheads[0])->EndpointID;
+ pEndpoint = &target->EndPoint[id];
+
+ if (id >= ENDPOINT_MAX) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("MsgPend, Invalid Endpoint in look-ahead: %d \n",id));
+ status = A_EPROTO;
+ break;
+ }
+
+ /* try to allocate as many HTC RX packets indicated by the lookaheads
+ * these packets are stored in the recvPkt queue */
+ status = AllocAndPrepareRxPackets(target,
+ lookAheads,
+ NumLookAheads,
+ pEndpoint,
+ &recvPktQueue);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) >= 2) {
+ /* a recv bundle was detected, force IRQ status re-check again */
+ REF_IRQ_STATUS_RECHECK(&target->Device);
+ }
+
+ totalFetched += HTC_PACKET_QUEUE_DEPTH(&recvPktQueue);
+
+ /* we've got packet buffers for all we can currently fetch,
+ * this count is not valid anymore */
+ NumLookAheads = 0;
+ partialBundle = FALSE;
+
+ /* now go fetch the list of HTC packets */
+ while (!HTC_QUEUE_EMPTY(&recvPktQueue)) {
+
+ pktsFetched = 0;
+
+ if (target->RecvBundlingEnabled && (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) > 1)) {
+ /* there are enough packets to attempt a bundle transfer and recv bundling is allowed */
+ status = HTCIssueRecvPacketBundle(target,
+ &recvPktQueue,
+ asyncProc ? NULL : &syncCompletedPktsQueue,
+ &pktsFetched,
+ partialBundle);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) != 0) {
+ /* we couldn't fetch all packets at one time, this creates a broken
+ * bundle */
+ partialBundle = TRUE;
+ }
+ }
+
+ /* see if the previous operation fetched any packets using bundling */
+ if (0 == pktsFetched) {
+ /* dequeue one packet */
+ pPacket = HTC_PACKET_DEQUEUE(&recvPktQueue);
+ A_ASSERT(pPacket != NULL);
+
+ if (asyncProc) {
+ /* we use async mode to get the packet if the device layer supports it
+ * set our callback and context */
+ pPacket->Completion = HTCRecvCompleteHandler;
+ pPacket->pContext = target;
+ } else {
+ /* fully synchronous */
+ pPacket->Completion = NULL;
+ }
+
+ if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) > 0) {
+ /* lookaheads in all packets except the last one in the bundle must be ignored */
+ pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_IGNORE_LOOKAHEAD;
+ }
+
+ /* go fetch the packet */
+ status = HTCIssueRecv(target, pPacket);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (!asyncProc) {
+ /* sent synchronously, queue this packet for synchronous completion */
+ HTC_PACKET_ENQUEUE(&syncCompletedPktsQueue,pPacket);
+ }
+
+ }
+
+ }
+
+ if (A_SUCCESS(status)) {
+ CheckRecvWaterMark(pEndpoint);
+ }
+
+ if (asyncProc) {
+ /* we did this asynchronously so we can get out of the loop, the asynch processing
+ * creates a chain of requests to continue processing pending messages in the
+ * context of callbacks */
+ break;
+ }
+
+ /* synchronous handling */
+ if (target->Device.DSRCanYield) {
+ /* for the SYNC case, increment count that tracks when the DSR should yield */
+ target->Device.CurrentDSRRecvCount++;
+ }
+
+ /* in the sync case, all packet buffers are now filled,
+ * we can process each packet, check lookaheads and then repeat */
+
+ /* unload sync completion queue */
+ while (!HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {
+ HTC_PACKET_QUEUE container;
+
+ pPacket = HTC_PACKET_DEQUEUE(&syncCompletedPktsQueue);
+ A_ASSERT(pPacket != NULL);
+
+ pEndpoint = &target->EndPoint[pPacket->Endpoint];
+ /* reset count on each iteration, we are only interested in the last packet's lookahead
+ * information when we break out of this loop */
+ NumLookAheads = 0;
+ /* process header for each of the recv packets
+ * note: the lookahead of the last packet is useful for us to continue in this loop */
+ status = HTCProcessRecvHeader(target,pPacket,lookAheads,&NumLookAheads);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {
+ /* last packet's more packets flag is set based on the lookahead */
+ SET_MORE_RX_PACKET_INDICATION_FLAG(lookAheads,NumLookAheads,pEndpoint,pPacket);
+ } else {
+ /* packets in a bundle automatically have this flag set */
+ FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);
+ }
+ /* good packet, indicate it */
+ HTC_RX_STAT_PROFILE(target,pEndpoint,NumLookAheads);
+
+ if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_PART_OF_BUNDLE) {
+ INC_HTC_EP_STAT(pEndpoint, RxPacketsBundled, 1);
+ }
+
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
+ DO_RCV_COMPLETION(pEndpoint,&container);
+ }
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (NumLookAheads == 0) {
+ /* no more look aheads */
+ break;
+ }
+
+ /* when we process recv synchronously we need to check if we should yield and stop
+ * fetching more packets indicated by the embedded lookaheads */
+ if (target->Device.DSRCanYield) {
+ if (DEV_CHECK_RECV_YIELD(&target->Device)) {
+ /* break out, don't fetch any more packets */
+ break;
+ }
+ }
+
+
+ /* check whether other OS contexts have queued any WMI command/data for WLAN.
+ * This check is needed only if WLAN Tx and Rx happens in same thread context */
+ A_CHECK_DRV_TX();
+
+ /* for SYNCH processing, if we get here, we are running through the loop again due to a detected lookahead.
+ * Set flag that we should re-check IRQ status registers again before leaving IRQ processing,
+ * this can net better performance in high throughput situations */
+ REF_IRQ_STATUS_RECHECK(&target->Device);
+ }
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Failed to get pending recv messages (%d) \n",status));
+ /* cleanup any packets we allocated but didn't use to actually fetch any packets */
+ while (!HTC_QUEUE_EMPTY(&recvPktQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(&recvPktQueue);
+ /* clean up packets */
+ HTC_RECYCLE_RX_PKT(target, pPacket, &target->EndPoint[pPacket->Endpoint]);
+ }
+ /* cleanup any packets in sync completion queue */
+ while (!HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {
+ pPacket = HTC_PACKET_DEQUEUE(&syncCompletedPktsQueue);
+ /* clean up packets */
+ HTC_RECYCLE_RX_PKT(target, pPacket, &target->EndPoint[pPacket->Endpoint]);
+ }
+ }
+
+ /* before leaving, check to see if host ran out of buffers and needs to stop the
+ * receiver */
+ if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
+ (" Host has no RX buffers, blocking receiver to prevent overrun.. \n"));
+ /* try to stop receive at the device layer */
+ DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC);
+ }
+
+ if (pNumPktsFetched != NULL) {
+ *pNumPktsFetched = totalFetched;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n"));
+
+ return status;
+}
+
+A_STATUS HTCAddReceivePktMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint;
+ A_BOOL unblockRecv = FALSE;
+ A_STATUS status = A_OK;
+ HTC_PACKET *pFirstPacket;
+
+ 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,
+ ("+- HTCAddReceivePktMultiple : endPointId: %d, cnt:%d, length: %d\n",
+ pFirstPacket->Endpoint,
+ HTC_PACKET_QUEUE_DEPTH(pPktQueue),
+ pFirstPacket->BufferLength));
+
+ do {
+
+ pEndpoint = &target->EndPoint[pFirstPacket->Endpoint];
+
+ LOCK_HTC_RX(target);
+
+ if (HTC_STOPPING(target)) {
+ HTC_PACKET *pPacket;
+
+ UNLOCK_HTC_RX(target);
+
+ /* 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_RCV_COMPLETION(pEndpoint,pPktQueue);
+ break;
+ }
+
+ /* store receive packets */
+ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RxBuffers, pPktQueue);
+
+ /* check if we are blocked waiting for a new buffer */
+ if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
+ if (target->EpWaitingForBuffers == pFirstPacket->Endpoint) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" receiver was blocked on ep:%d, unblocking.. \n",
+ target->EpWaitingForBuffers));
+ target->RecvStateFlags &= ~HTC_RECV_WAIT_BUFFERS;
+ target->EpWaitingForBuffers = ENDPOINT_MAX;
+ unblockRecv = TRUE;
+ }
+ }
+
+ UNLOCK_HTC_RX(target);
+
+ if (unblockRecv && !HTC_STOPPING(target)) {
+ /* TODO : implement a buffer threshold count? */
+ DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
+ }
+
+ } while (FALSE);
+
+ return status;
+}
+
+/* Makes a buffer available to the HTC module */
+A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
+{
+ HTC_PACKET_QUEUE queue;
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
+ return HTCAddReceivePktMultiple(HTCHandle, &queue);
+}
+
+void HTCUnblockRecv(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ A_BOOL unblockRecv = FALSE;
+
+ LOCK_HTC_RX(target);
+
+ /* check if we are blocked waiting for a new buffer */
+ if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HTCUnblockRx : receiver was blocked on ep:%d, unblocking.. \n",
+ target->EpWaitingForBuffers));
+ target->RecvStateFlags &= ~HTC_RECV_WAIT_BUFFERS;
+ target->EpWaitingForBuffers = ENDPOINT_MAX;
+ unblockRecv = TRUE;
+ }
+
+ UNLOCK_HTC_RX(target);
+
+ if (unblockRecv && !HTC_STOPPING(target)) {
+ /* re-enable */
+ DevEnableRecv(&target->Device,DEV_ENABLE_RECV_ASYNC);
+ }
+}
+
+static void HTCFlushRxQueue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET_QUEUE *pQueue)
+{
+ HTC_PACKET *pPacket;
+ HTC_PACKET_QUEUE container;
+
+ LOCK_HTC_RX(target);
+
+ while (1) {
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ if (NULL == pPacket) {
+ break;
+ }
+ UNLOCK_HTC_RX(target);
+ pPacket->Status = A_ECANCELED;
+ pPacket->ActualLength = 0;
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" Flushing RX packet:0x%X, length:%d, ep:%d \n",
+ (A_UINT32)pPacket, pPacket->BufferLength, pPacket->Endpoint));
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
+ /* give the packet back */
+ DO_RCV_COMPLETION(pEndpoint,&container);
+ LOCK_HTC_RX(target);
+ }
+
+ UNLOCK_HTC_RX(target);
+}
+
+static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
+{
+ /* flush any recv indications not already made */
+ HTCFlushRxQueue(target,pEndpoint,&pEndpoint->RecvIndicationQueue);
+ /* flush any rx buffers */
+ HTCFlushRxQueue(target,pEndpoint,&pEndpoint->RxBuffers);
+}
+
+void HTCFlushRecvBuffers(HTC_TARGET *target)
+{
+ HTC_ENDPOINT *pEndpoint;
+ int i;
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ pEndpoint = &target->EndPoint[i];
+ if (pEndpoint->ServiceID == 0) {
+ /* not in use.. */
+ continue;
+ }
+ HTCFlushEndpointRX(target,pEndpoint);
+ }
+}
+
+
+void HTCEnableRecv(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+
+ if (!HTC_STOPPING(target)) {
+ /* re-enable */
+ DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
+ }
+}
+
+void HTCDisableRecv(HTC_HANDLE HTCHandle)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+
+ if (!HTC_STOPPING(target)) {
+ /* disable */
+ DevStopRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
+ }
+}
+
+int HTCGetNumRecvBuffers(HTC_HANDLE HTCHandle,
+ HTC_ENDPOINT_ID Endpoint)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ return HTC_PACKET_QUEUE_DEPTH(&(target->EndPoint[Endpoint].RxBuffers));
+}
+
diff --git a/drivers/net/wireless/ath6kl/htc2/htc_send.c b/drivers/net/wireless/ath6kl/htc2/htc_send.c
new file mode 100644
index 000000000000..13eef67aaeaf
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc_send.c
@@ -0,0 +1,1019 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_send.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#include "htc_internal.h"
+
+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;
+
+#define DO_EP_TX_COMPLETION(ep,q) DoSendCompletion(ep,q)
+
+/* call the distribute credits callback with the distribution */
+#define DO_DISTRIBUTION(t,reason,description,pList) \
+{ \
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, \
+ (" calling distribute function (%s) (dfn:0x%X, ctxt:0x%X, dist:0x%X) \n", \
+ (description), \
+ (A_UINT32)(t)->DistributeCredits, \
+ (A_UINT32)(t)->pCredDistContext, \
+ (A_UINT32)pList)); \
+ (t)->DistributeCredits((t)->pCredDistContext, \
+ (pList), \
+ (reason)); \
+}
+
+static void DoSendCompletion(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 0x%X \n", \
+ pEndpoint->Id, (A_UINT32)(pPacket)));
+ pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);
+ } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));
+ }
+
+ } while (FALSE);
+
+}
+
+/* do final completion on sent packet */
+static INLINE void CompleteSentPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket)
+{
+ pPacket->Completion = NULL;
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("CompleteSentPacket: request failed (status:%d, ep:%d, length:%d creds:%d) \n",
+ pPacket->Status, pPacket->Endpoint, pPacket->ActualLength, pPacket->PktInfo.AsTx.CreditsUsed));
+ /* on failure to submit, reclaim credits for this packet */
+ LOCK_HTC_TX(target);
+ pEndpoint->CreditDist.TxCreditsToDist += pPacket->PktInfo.AsTx.CreditsUsed;
+ pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
+ DO_DISTRIBUTION(target,
+ HTC_CREDIT_DIST_SEND_COMPLETE,
+ "Send Complete",
+ target->EpCreditDistributionListHead->pNext);
+ UNLOCK_HTC_TX(target);
+ }
+ /* first, fixup the head room we allocated */
+ pPacket->pBuffer += HTC_HDR_LENGTH;
+}
+
+/* our internal send packet completion handler when packets are submited to the AR6K device
+ * layer */
+static void HTCSendPktCompletionHandler(void *Context, HTC_PACKET *pPacket)
+{
+ HTC_TARGET *target = (HTC_TARGET *)Context;
+ HTC_ENDPOINT *pEndpoint = &target->EndPoint[pPacket->Endpoint];
+ HTC_PACKET_QUEUE container;
+
+ CompleteSentPacket(target,pEndpoint,pPacket);
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
+ /* do completion */
+ DO_EP_TX_COMPLETION(pEndpoint,&container);
+}
+
+A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket)
+{
+ A_STATUS status;
+ A_BOOL sync = FALSE;
+
+ if (pPacket->Completion == NULL) {
+ /* mark that this request was synchronously issued */
+ sync = TRUE;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("+-HTCIssueSend: transmit length : %d (%s) \n",
+ pPacket->ActualLength + HTC_HDR_LENGTH,
+ sync ? "SYNC" : "ASYNC" ));
+
+ /* send message to device */
+ status = DevSendPacket(&target->Device,
+ pPacket,
+ pPacket->ActualLength + HTC_HDR_LENGTH);
+
+ if (sync) {
+ /* use local sync variable. If this was issued asynchronously, pPacket is no longer
+ * safe to access. */
+ pPacket->pBuffer += HTC_HDR_LENGTH;
+ }
+
+ /* if this request was asynchronous, the packet completion routine will be invoked by
+ * the device layer when the HIF layer completes the request */
+
+ return status;
+}
+
+ /* get HTC send packets from the TX queue on an endpoint */
+static INLINE void GetHTCSendPackets(HTC_TARGET *target,
+ HTC_ENDPOINT *pEndpoint,
+ HTC_PACKET_QUEUE *pQueue)
+{
+ int creditsRequired;
+ int remainder;
+ A_UINT8 sendFlags;
+ HTC_PACKET *pPacket;
+ unsigned int transferLength;
+
+ /****** NOTE : the TX lock is held when this function is called *****************/
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets \n"));
+
+ /* loop until we can grab as many packets out of the queue as we can */
+ while (TRUE) {
+
+ sendFlags = 0;
+ /* get packet at head, but don't remove it */
+ pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue);
+ if (pPacket == NULL) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%X , Queue Depth: %d\n",
+ (A_UINT32)pPacket, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
+
+ transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, pPacket->ActualLength + HTC_HDR_LENGTH);
+
+ if (transferLength <= target->TargetCreditSize) {
+ creditsRequired = 1;
+ } else {
+ /* figure out how many credits this message requires */
+ creditsRequired = transferLength / target->TargetCreditSize;
+ remainder = transferLength % target->TargetCreditSize;
+
+ if (remainder) {
+ creditsRequired++;
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Creds Required:%d Got:%d\n",
+ creditsRequired, pEndpoint->CreditDist.TxCredits));
+
+ if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
+
+ /* not enough credits */
+ if (pPacket->Endpoint == ENDPOINT_0) {
+ /* leave it in the queue */
+ break;
+ }
+ /* invoke the registered distribution function only if this is not
+ * endpoint 0, we let the driver layer provide more credits if it can.
+ * We pass the credit distribution list starting at the endpoint in question
+ * */
+
+ /* set how many credits we need */
+ pEndpoint->CreditDist.TxCreditsSeek =
+ creditsRequired - pEndpoint->CreditDist.TxCredits;
+ DO_DISTRIBUTION(target,
+ HTC_CREDIT_DIST_SEEK_CREDITS,
+ "Seek Credits",
+ &pEndpoint->CreditDist);
+ pEndpoint->CreditDist.TxCreditsSeek = 0;
+
+ if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
+ /* still not enough credits to send, leave packet in the queue */
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ (" Not enough credits for ep %d leaving packet in queue..\n",
+ pPacket->Endpoint));
+ break;
+ }
+
+ }
+
+ pEndpoint->CreditDist.TxCredits -= creditsRequired;
+ INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired);
+
+ /* check if we need credits back from the target */
+ if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
+ /* we are getting low on credits, see if we can ask for more from the distribution function */
+ pEndpoint->CreditDist.TxCreditsSeek =
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg - pEndpoint->CreditDist.TxCredits;
+
+ DO_DISTRIBUTION(target,
+ HTC_CREDIT_DIST_SEEK_CREDITS,
+ "Seek Credits",
+ &pEndpoint->CreditDist);
+
+ pEndpoint->CreditDist.TxCreditsSeek = 0;
+ /* see if we were successful in getting more */
+ if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
+ /* 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"));
+ }
+ }
+
+ /* now we can fully dequeue */
+ pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue);
+ /* save the number of credits this packet consumed */
+ pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired;
+ /* all TX packets are handled asynchronously */
+ pPacket->Completion = HTCSendPktCompletionHandler;
+ pPacket->pContext = target;
+ INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
+ /* save send flags */
+ pPacket->PktInfo.AsTx.SendFlags = sendFlags;
+ pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
+ pEndpoint->SeqNo++;
+ /* queue this packet into the caller's queue */
+ HTC_PACKET_ENQUEUE(pQueue,pPacket);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n"));
+
+}
+
+static void HTCAsyncSendScatterCompletion(HIF_SCATTER_REQ *pScatterReq)
+{
+ int i;
+ HTC_PACKET *pPacket;
+ HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *)pScatterReq->Context;
+ HTC_TARGET *target = (HTC_TARGET *)pEndpoint->target;
+ A_STATUS status = A_OK;
+ HTC_PACKET_QUEUE sendCompletes;
+
+ INIT_HTC_PACKET_QUEUE(&sendCompletes);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCAsyncSendScatterCompletion TotLen: %d Entries: %d\n",
+ pScatterReq->TotalLength, pScatterReq->ValidScatterEntries));
+
+ DEV_FINISH_SCATTER_OPERATION(pScatterReq);
+
+ if (A_FAILED(pScatterReq->CompletionStatus)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Send Scatter Request Failed: %d \n",pScatterReq->CompletionStatus));
+ status = A_ERROR;
+ }
+
+ /* walk through the scatter list and process */
+ for (i = 0; i < pScatterReq->ValidScatterEntries; i++) {
+ pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
+ A_ASSERT(pPacket != NULL);
+ pPacket->Status = status;
+ CompleteSentPacket(target,pEndpoint,pPacket);
+ /* add it to the completion queue */
+ HTC_PACKET_ENQUEUE(&sendCompletes, pPacket);
+ }
+
+ /* free scatter request */
+ DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
+ /* complete all packets */
+ DO_EP_TX_COMPLETION(pEndpoint,&sendCompletes);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCAsyncSendScatterCompletion \n"));
+}
+
+ /* drain a queue and send as bundles
+ * this function may return without fully draining the queue under the following conditions :
+ * - scatter resources are exhausted
+ * - a message that will consume a partial credit will stop the bundling process early
+ * - we drop below the minimum number of messages for a bundle
+ * */
+static void HTCIssueSendBundle(HTC_ENDPOINT *pEndpoint,
+ HTC_PACKET_QUEUE *pQueue,
+ int *pBundlesSent,
+ int *pTotalBundlesPkts)
+{
+ int pktsToScatter;
+ unsigned int scatterSpaceRemaining;
+ HIF_SCATTER_REQ *pScatterReq = NULL;
+ int i, packetsInScatterReq;
+ unsigned int transferLength;
+ HTC_PACKET *pPacket;
+ A_BOOL done = FALSE;
+ int bundlesSent = 0;
+ int totalPktsInBundle = 0;
+ HTC_TARGET *target = pEndpoint->target;
+ int creditRemainder = 0;
+ int creditPad;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCIssueSendBundle \n"));
+
+ while (!done) {
+
+ pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pQueue);
+ pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle);
+
+ if (pktsToScatter < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
+ /* not enough to bundle */
+ break;
+ }
+
+ pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device);
+
+ if (pScatterReq == NULL) {
+ /* no scatter resources */
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" No more scatter resources \n"));
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" pkts to scatter: %d \n", pktsToScatter));
+
+ pScatterReq->TotalLength = 0;
+ pScatterReq->ValidScatterEntries = 0;
+
+ packetsInScatterReq = 0;
+ scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_SEND_LENGTH(&target->Device);
+
+ for (i = 0; i < pktsToScatter; i++) {
+
+ pScatterReq->ScatterList[i].pCallerContexts[0] = NULL;
+
+ pPacket = HTC_GET_PKT_AT_HEAD(pQueue);
+ if (pPacket == NULL) {
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ creditPad = 0;
+ transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device,
+ pPacket->ActualLength + HTC_HDR_LENGTH);
+ /* see if the padded transfer length falls on a credit boundary */
+ creditRemainder = transferLength % target->TargetCreditSize;
+
+ if (creditRemainder != 0) {
+ /* the transfer consumes a "partial" credit, this packet cannot be bundled unless
+ * we add additional "dummy" padding (max 255 bytes) to consume the entire credit
+ *** NOTE: only allow the send padding if the endpoint is allowed to */
+ if (pEndpoint->LocalConnectionFlags & HTC_LOCAL_CONN_FLAGS_ENABLE_SEND_BUNDLE_PADDING) {
+ if (transferLength < target->TargetCreditSize) {
+ /* special case where the transfer is less than a credit */
+ creditPad = target->TargetCreditSize - transferLength;
+ } else {
+ creditPad = creditRemainder;
+ }
+
+ /* now check to see if we can indicate padding in the HTC header */
+ if ((creditPad > 0) && (creditPad <= 255)) {
+ /* adjust the transferlength of this packet with the new credit padding */
+ transferLength += creditPad;
+ } else {
+ /* the amount to pad is too large, bail on this packet, we have to
+ * send it using the non-bundled method */
+ pPacket = NULL;
+ }
+ } else {
+ /* bail on this packet, user does not want padding applied */
+ pPacket = NULL;
+ }
+ }
+
+ if (NULL == pPacket) {
+ /* can't bundle */
+ done = TRUE;
+ break;
+ }
+
+ if (scatterSpaceRemaining < transferLength) {
+ /* exceeds what we can transfer */
+ break;
+ }
+
+ scatterSpaceRemaining -= transferLength;
+ /* now remove it from the queue */
+ pPacket = HTC_PACKET_DEQUEUE(pQueue);
+ /* save it in the scatter list */
+ pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket;
+ /* prepare packet and flag message as part of a send bundle */
+ HTC_PREPARE_SEND_PKT(pPacket,
+ pPacket->PktInfo.AsTx.SendFlags | HTC_FLAGS_SEND_BUNDLE,
+ creditPad,
+ pPacket->PktInfo.AsTx.SeqNo);
+ pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer;
+ pScatterReq->ScatterList[i].Length = transferLength;
+ A_ASSERT(transferLength);
+ pScatterReq->TotalLength += transferLength;
+ pScatterReq->ValidScatterEntries++;
+ packetsInScatterReq++;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" %d, Adding packet : 0x%X, len:%d (remaining space:%d) \n",
+ i, (A_UINT32)pPacket,transferLength,scatterSpaceRemaining));
+ }
+
+ if (packetsInScatterReq >= HTC_MIN_HTC_MSGS_TO_BUNDLE) {
+ /* send path is always asynchronous */
+ pScatterReq->CompletionRoutine = HTCAsyncSendScatterCompletion;
+ pScatterReq->Context = pEndpoint;
+ bundlesSent++;
+ totalPktsInBundle += packetsInScatterReq;
+ packetsInScatterReq = 0;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Send Scatter total bytes: %d , entries: %d\n",
+ pScatterReq->TotalLength,pScatterReq->ValidScatterEntries));
+ DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_WRITE, DEV_SCATTER_ASYNC);
+ /* we don't own this anymore */
+ pScatterReq = NULL;
+ /* try to send some more */
+ continue;
+ }
+
+ /* not enough packets to use the scatter request, cleanup */
+ if (pScatterReq != NULL) {
+ if (packetsInScatterReq > 0) {
+ /* work backwards to requeue requests */
+ for (i = (packetsInScatterReq - 1); i >= 0; i--) {
+ pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
+ if (pPacket != NULL) {
+ /* undo any prep */
+ HTC_UNPREPARE_SEND_PKT(pPacket);
+ /* queue back to the head */
+ HTC_PACKET_ENQUEUE_TO_HEAD(pQueue,pPacket);
+ }
+ }
+ }
+ DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
+ }
+
+ /* if we get here, we sent all that we could, get out */
+ break;
+
+ }
+
+ *pBundlesSent = bundlesSent;
+ *pTotalBundlesPkts = totalPktsInBundle;
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCIssueSendBundle (sent:%d) \n",bundlesSent));
+
+ return;
+}
+
+/*
+ * if there are no credits, the packet(s) remains in the queue.
+ * this function returns the result of the attempt to send a queue of HTC packets */
+static HTC_SEND_QUEUE_RESULT HTCTrySend(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 bundlesSent;
+ int pktsInBundles;
+ int overflow;
+ HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (Queue:0x%X Depth:%d)\n",
+ (A_UINT32)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 */
+ 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: 0x%X \n",
+ (A_UINT32)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 */
+ HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
+ }
+
+ } ITERATE_END;
+
+ if (HTC_QUEUE_EMPTY(&sendQueue)) {
+ /* no packets made it in, caller will cleanup */
+ result = HTC_SEND_QUEUE_DROP;
+ break;
+ }
+ }
+
+ } while (FALSE);
+
+ if (result != HTC_SEND_QUEUE_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
+ return result;
+ }
+
+ LOCK_HTC_TX(target);
+
+ if (!HTC_QUEUE_EMPTY(&sendQueue)) {
+ /* transfer packets */
+ 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 */
+ pEndpoint->TxProcessCount++;
+ if (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 */
+ pEndpoint->TxProcessCount--;
+ UNLOCK_HTC_TX(target);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (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
+ * credits */
+ while (TRUE) {
+
+ if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) {
+ break;
+ }
+
+ /* get all the packets for this endpoint that we can for this pass */
+ GetHTCSendPackets(target, pEndpoint, &sendQueue);
+
+ if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) {
+ /* didn't get any packets due to a lack of credits */
+ break;
+ }
+
+ UNLOCK_HTC_TX(target);
+
+ /* any packets to send are now in our local send queue */
+
+ bundlesSent = 0;
+ pktsInBundles = 0;
+
+ while (TRUE) {
+
+ /* try to send a bundle on each pass */
+ if ((target->SendBundlingEnabled) &&
+ (HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+ int temp1,temp2;
+ /* bundling is enabled and there is at least a minimum number of packets in the send queue
+ * send what we can in this pass */
+ HTCIssueSendBundle(pEndpoint, &sendQueue, &temp1, &temp2);
+ bundlesSent += temp1;
+ pktsInBundles += temp2;
+ }
+
+ /* if not bundling or there was a packet that could not be placed in a bundle, pull it out
+ * and send it the normal way */
+ pPacket = HTC_PACKET_DEQUEUE(&sendQueue);
+ if (NULL == pPacket) {
+ /* local queue is fully drained */
+ break;
+ }
+ HTC_PREPARE_SEND_PKT(pPacket,
+ pPacket->PktInfo.AsTx.SendFlags,
+ 0,
+ pPacket->PktInfo.AsTx.SeqNo);
+ HTCIssueSend(target, pPacket);
+
+ /* go back and see if we can bundle some more */
+ }
+
+ LOCK_HTC_TX(target);
+
+ INC_HTC_EP_STAT(pEndpoint, TxBundles, bundlesSent);
+ INC_HTC_EP_STAT(pEndpoint, TxPacketsBundled, pktsInBundles);
+
+ }
+
+ /* done with this endpoint, we can clear the count */
+ pEndpoint->TxProcessCount = 0;
+ UNLOCK_HTC_TX(target);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
+
+ return HTC_SEND_QUEUE_OK;
+}
+
+A_STATUS HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint;
+ HTC_PACKET *pPacket;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: 0x%X, Pkts %d \n",
+ (A_UINT32)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) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
+ return A_EINVAL;
+ }
+
+ AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
+ pEndpoint = &target->EndPoint[pPacket->Endpoint];
+
+ HTCTrySend(target, pEndpoint, pPktQueue);
+
+ /* do completion on any packets that couldn't get in */
+ if (!HTC_QUEUE_EMPTY(pPktQueue)) {
+
+ HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
+ if (HTC_STOPPING(target)) {
+ pPacket->Status = A_ECANCELED;
+ } else {
+ pPacket->Status = A_NO_RESOURCE;
+ }
+ } HTC_PACKET_QUEUE_ITERATE_END;
+
+ DO_EP_TX_COMPLETION(pEndpoint,pPktQueue);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
+
+ return A_OK;
+}
+
+/* HTC API - HTCSendPkt */
+A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
+{
+ HTC_PACKET_QUEUE queue;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("+-HTCSendPkt: Enter endPointId: %d, buffer: 0x%X, length: %d \n",
+ pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->ActualLength));
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
+ return HTCSendPktsMultiple(HTCHandle, &queue);
+}
+
+/* check TX queues to drain because of credit distribution update */
+static INLINE void HTCCheckEndpointTxQueues(HTC_TARGET *target)
+{
+ HTC_ENDPOINT *pEndpoint;
+ HTC_ENDPOINT_CREDIT_DIST *pDistItem;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCCheckEndpointTxQueues \n"));
+ pDistItem = target->EpCreditDistributionListHead;
+
+ /* run through the credit distribution list to see
+ * if there are packets queued
+ * NOTE: no locks need to be taken since the distribution list
+ * is not dynamic (cannot be re-ordered) and we are not modifying any state */
+ while (pDistItem != NULL) {
+ pEndpoint = (HTC_ENDPOINT *)pDistItem->pHTCReserved;
+
+ if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) > 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n",
+ pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
+ /* try to start the stalled queue, this list is ordered by priority.
+ * Highest priority queue get's processed first, if there are credits available the
+ * highest priority queue will get a chance to reclaim credits from lower priority
+ * ones */
+ HTCTrySend(target, pEndpoint, NULL);
+ }
+
+ pDistItem = pDistItem->pNext;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCCheckEndpointTxQueues \n"));
+}
+
+/* process credit reports and call distribution function */
+void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
+{
+ int i;
+ HTC_ENDPOINT *pEndpoint;
+ int totalCredits = 0;
+ A_BOOL doDist = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries));
+
+ /* lock out TX while we update credits */
+ LOCK_HTC_TX(target);
+
+ for (i = 0; i < NumEntries; i++, pRpt++) {
+ if (pRpt->EndpointID >= ENDPOINT_MAX) {
+ AR_DEBUG_ASSERT(FALSE);
+ break;
+ }
+
+ pEndpoint = &target->EndPoint[pRpt->EndpointID];
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d got %d credits \n",
+ pRpt->EndpointID, pRpt->Credits));
+
+
+#ifdef HTC_EP_STAT_PROFILING
+
+ INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1);
+ INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, pRpt->Credits);
+
+ if (FromEndpoint == pRpt->EndpointID) {
+ /* this credit report arrived on the same endpoint indicating it arrived in an RX
+ * packet */
+ INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, pRpt->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, pRpt->Credits);
+ INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1);
+ } else {
+ /* arrived on another endpoint */
+ INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, pRpt->Credits);
+ INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1);
+ }
+
+#endif
+
+ if (ENDPOINT_0 == pRpt->EndpointID) {
+ /* always give endpoint 0 credits back */
+ pEndpoint->CreditDist.TxCredits += pRpt->Credits;
+ } else {
+ /* for all other endpoints, update credits to distribute, the distribution function
+ * will handle giving out credits back to the endpoints */
+ pEndpoint->CreditDist.TxCreditsToDist += pRpt->Credits;
+ /* flag that we have to do the distribution */
+ doDist = TRUE;
+ }
+
+ /* refresh tx depth for distribution function that will recover these credits
+ * NOTE: this is only valid when there are credits to recover! */
+ pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
+
+ totalCredits += pRpt->Credits;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Report indicated %d credits to distribute \n", totalCredits));
+
+ if (doDist) {
+ /* this was a credit return based on a completed send operations
+ * note, this is done with the lock held */
+ DO_DISTRIBUTION(target,
+ HTC_CREDIT_DIST_SEND_COMPLETE,
+ "Send Complete",
+ target->EpCreditDistributionListHead->pNext);
+ }
+
+ UNLOCK_HTC_TX(target);
+
+ if (totalCredits) {
+ HTCCheckEndpointTxQueues(target);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n"));
+}
+
+/* flush endpoint TX queue */
+static void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag)
+{
+ HTC_PACKET *pPacket;
+ HTC_PACKET_QUEUE discardQueue;
+ HTC_PACKET_QUEUE container;
+
+ /* initialize the discard queue */
+ INIT_HTC_PACKET_QUEUE(&discardQueue);
+
+ LOCK_HTC_TX(target);
+
+ /* interate from the front of the TX queue and flush out packets */
+ ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket, HTC_PACKET, ListLink) {
+
+ /* check for removal */
+ if ((HTC_TX_PACKET_TAG_ALL == Tag) || (Tag == pPacket->PktInfo.AsTx.Tag)) {
+ /* remove from queue */
+ HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket);
+ /* add it to the discard pile */
+ HTC_PACKET_ENQUEUE(&discardQueue, pPacket);
+ }
+
+ } ITERATE_END;
+
+ UNLOCK_HTC_TX(target);
+
+ /* empty the discard queue */
+ while (1) {
+ pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
+ if (NULL == pPacket) {
+ break;
+ }
+ pPacket->Status = A_ECANCELED;
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" Flushing TX packet:0x%X, length:%d, ep:%d tag:0x%X \n",
+ (A_UINT32)pPacket, pPacket->ActualLength, pPacket->Endpoint, pPacket->PktInfo.AsTx.Tag));
+ INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
+ DO_EP_TX_COMPLETION(pEndpoint,&container);
+ }
+
+}
+
+void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("--- EP : %d ServiceID: 0x%X --------------\n",
+ pEPDist->Endpoint, pEPDist->ServiceID));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" this:0x%X next:0x%X prev:0x%X\n",
+ (A_UINT32)pEPDist, (A_UINT32)pEPDist->pNext, (A_UINT32)pEPDist->pPrev));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" DistFlags : 0x%X \n", pEPDist->DistFlags));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsNorm : %d \n", pEPDist->TxCreditsNorm));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsMin : %d \n", pEPDist->TxCreditsMin));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCredits : %d \n", pEPDist->TxCredits));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsAssigned : %d \n", pEPDist->TxCreditsAssigned));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsSeek : %d \n", pEPDist->TxCreditsSeek));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditSize : %d \n", pEPDist->TxCreditSize));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsPerMaxMsg : %d \n", pEPDist->TxCreditsPerMaxMsg));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsToDist : %d \n", pEPDist->TxCreditsToDist));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxQueueDepth : %d \n",
+ HTC_PACKET_QUEUE_DEPTH(&((HTC_ENDPOINT *)pEPDist->pHTCReserved)->TxQueue)));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("----------------------------------------------------\n"));
+}
+
+void DumpCreditDistStates(HTC_TARGET *target)
+{
+ HTC_ENDPOINT_CREDIT_DIST *pEPList = target->EpCreditDistributionListHead;
+
+ while (pEPList != NULL) {
+ DumpCreditDist(pEPList);
+ pEPList = pEPList->pNext;
+ }
+
+ if (target->DistributeCredits != NULL) {
+ DO_DISTRIBUTION(target,
+ HTC_DUMP_CREDIT_STATE,
+ "Dump State",
+ NULL);
+ }
+}
+
+/* flush all send packets from all endpoint queues */
+void HTCFlushSendPkts(HTC_TARGET *target)
+{
+ HTC_ENDPOINT *pEndpoint;
+ int i;
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) {
+ DumpCreditDistStates(target);
+ }
+
+ for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+ pEndpoint = &target->EndPoint[i];
+ if (pEndpoint->ServiceID == 0) {
+ /* not in use.. */
+ continue;
+ }
+ HTCFlushEndpointTX(target,pEndpoint,HTC_TX_PACKET_TAG_ALL);
+ }
+
+
+}
+
+/* HTC API to flush an endpoint's TX queue*/
+void HTCFlushEndpoint(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->ServiceID == 0) {
+ AR_DEBUG_ASSERT(FALSE);
+ /* not in use.. */
+ return;
+ }
+
+ HTCFlushEndpointTX(target, pEndpoint, Tag);
+}
+
+/* HTC API to indicate activity to the credit distribution function */
+void HTCIndicateActivityChange(HTC_HANDLE HTCHandle,
+ HTC_ENDPOINT_ID Endpoint,
+ A_BOOL Active)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint];
+ A_BOOL doDist = FALSE;
+
+ if (pEndpoint->ServiceID == 0) {
+ AR_DEBUG_ASSERT(FALSE);
+ /* not in use.. */
+ return;
+ }
+
+ LOCK_HTC_TX(target);
+
+ if (Active) {
+ if (!(pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE)) {
+ /* mark active now */
+ pEndpoint->CreditDist.DistFlags |= HTC_EP_ACTIVE;
+ doDist = TRUE;
+ }
+ } else {
+ if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
+ /* mark inactive now */
+ pEndpoint->CreditDist.DistFlags &= ~HTC_EP_ACTIVE;
+ doDist = TRUE;
+ }
+ }
+
+ if (doDist) {
+ /* indicate current Tx Queue depth to the credit distribution function */
+ pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
+ /* do distribution again based on activity change
+ * note, this is done with the lock held */
+ DO_DISTRIBUTION(target,
+ HTC_CREDIT_DIST_ACTIVITY_CHANGE,
+ "Activity Change",
+ target->EpCreditDistributionListHead->pNext);
+ }
+
+ UNLOCK_HTC_TX(target);
+
+ if (doDist && !Active) {
+ /* if a stream went inactive and this resulted in a credit distribution change,
+ * some credits may now be available for HTC packets that are stuck in
+ * HTC queues */
+ HTCCheckEndpointTxQueues(target);
+ }
+}
+
+A_BOOL HTCIsEndpointActive(HTC_HANDLE HTCHandle,
+ HTC_ENDPOINT_ID Endpoint)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint];
+
+ if (pEndpoint->ServiceID == 0) {
+ return FALSE;
+ }
+
+ if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/drivers/net/wireless/ath6kl/htc2/htc_services.c b/drivers/net/wireless/ath6kl/htc2/htc_services.c
new file mode 100644
index 000000000000..acef96e238c3
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/htc2/htc_services.c
@@ -0,0 +1,444 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_services.c" company="Atheros">
+// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// Author(s): ="Atheros"
+//==============================================================================
+#include "htc_internal.h"
+
+void HTCControlTxComplete(void *Context, HTC_PACKET *pPacket)
+{
+ /* not implemented
+ * we do not send control TX frames during normal runtime, only during setup */
+ AR_DEBUG_ASSERT(FALSE);
+}
+
+ /* callback when a control message arrives on this endpoint */
+void HTCControlRecv(void *Context, HTC_PACKET *pPacket)
+{
+ AR_DEBUG_ASSERT(pPacket->Endpoint == ENDPOINT_0);
+
+ if (pPacket->Status == A_ECANCELED) {
+ /* this is a flush operation, return the control packet back to the pool */
+ HTC_FREE_CONTROL_RX((HTC_TARGET*)Context,pPacket);
+ return;
+ }
+
+ /* the only control messages we are expecting are NULL messages (credit resports) */
+ if (pPacket->ActualLength > 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCControlRecv, got message with length:%d \n",
+ pPacket->ActualLength + HTC_HDR_LENGTH));
+
+ /* dump header and message */
+ DebugDumpBytes(pPacket->pBuffer - HTC_HDR_LENGTH,
+ pPacket->ActualLength + HTC_HDR_LENGTH,
+ "Unexpected ENDPOINT 0 Message");
+ }
+
+ HTC_RECYCLE_RX_PKT((HTC_TARGET*)Context,pPacket,&((HTC_TARGET*)Context)->EndPoint[0]);
+}
+
+A_STATUS HTCSendSetupComplete(HTC_TARGET *target)
+{
+ HTC_PACKET *pSendPacket = NULL;
+ A_STATUS status;
+
+ do {
+ /* allocate a packet to send to the target */
+ pSendPacket = HTC_ALLOC_CONTROL_TX(target);
+
+ if (NULL == pSendPacket) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ if (target->HTCTargetVersion >= HTC_VERSION_2P1) {
+ HTC_SETUP_COMPLETE_EX_MSG *pSetupCompleteEx;
+ A_UINT32 setupFlags = 0;
+
+ pSetupCompleteEx = (HTC_SETUP_COMPLETE_EX_MSG *)pSendPacket->pBuffer;
+ A_MEMZERO(pSetupCompleteEx, sizeof(HTC_SETUP_COMPLETE_EX_MSG));
+ pSetupCompleteEx->MessageID = HTC_MSG_SETUP_COMPLETE_EX_ID;
+ if (target->MaxMsgPerBundle > 0) {
+ /* host can do HTC bundling, indicate this to the target */
+ setupFlags |= HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV;
+ pSetupCompleteEx->MaxMsgsPerBundledRecv = target->MaxMsgPerBundle;
+ }
+ A_MEMCPY(&pSetupCompleteEx->SetupFlags, &setupFlags, sizeof(pSetupCompleteEx->SetupFlags));
+ SET_HTC_PACKET_INFO_TX(pSendPacket,
+ NULL,
+ (A_UINT8 *)pSetupCompleteEx,
+ sizeof(HTC_SETUP_COMPLETE_EX_MSG),
+ ENDPOINT_0,
+ HTC_SERVICE_TX_PACKET_TAG);
+
+ } else {
+ HTC_SETUP_COMPLETE_MSG *pSetupComplete;
+ /* assemble setup complete message */
+ pSetupComplete = (HTC_SETUP_COMPLETE_MSG *)pSendPacket->pBuffer;
+ A_MEMZERO(pSetupComplete, sizeof(HTC_SETUP_COMPLETE_MSG));
+ pSetupComplete->MessageID = HTC_MSG_SETUP_COMPLETE_ID;
+ SET_HTC_PACKET_INFO_TX(pSendPacket,
+ NULL,
+ (A_UINT8 *)pSetupComplete,
+ sizeof(HTC_SETUP_COMPLETE_MSG),
+ ENDPOINT_0,
+ HTC_SERVICE_TX_PACKET_TAG);
+ }
+
+ /* we want synchronous operation */
+ pSendPacket->Completion = NULL;
+ HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0);
+ /* send the message */
+ status = HTCIssueSend(target,pSendPacket);
+
+ } while (FALSE);
+
+ if (pSendPacket != NULL) {
+ HTC_FREE_CONTROL_TX(target,pSendPacket);
+ }
+
+ return status;
+}
+
+
+A_STATUS HTCConnectService(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 *pRecvPacket = NULL;
+ 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;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:0x%X SvcID:0x%X \n",
+ (A_UINT32)target, pConnectReq->ServiceID));
+
+ do {
+
+ AR_DEBUG_ASSERT(pConnectReq->ServiceID != 0);
+
+ if (HTC_CTRL_RSVD_SVC == pConnectReq->ServiceID) {
+ /* special case for pseudo control service */
+ assignedEndpoint = ENDPOINT_0;
+ maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH;
+ } else {
+ /* allocate a packet to send to the target */
+ pSendPacket = HTC_ALLOC_CONTROL_TX(target);
+
+ if (NULL == pSendPacket) {
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_NO_MEMORY;
+ break;
+ }
+ /* assemble connect service message */
+ pConnectMsg = (HTC_CONNECT_SERVICE_MSG *)pSendPacket->pBuffer;
+ AR_DEBUG_ASSERT(pConnectMsg != NULL);
+ A_MEMZERO(pConnectMsg,sizeof(HTC_CONNECT_SERVICE_MSG));
+ pConnectMsg->MessageID = HTC_MSG_CONNECT_SERVICE_ID;
+ pConnectMsg->ServiceID = pConnectReq->ServiceID;
+ pConnectMsg->ConnectionFlags = pConnectReq->ConnectionFlags;
+ /* 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);
+ pConnectMsg->ServiceMetaLength = pConnectReq->MetaDataLength;
+ }
+
+ SET_HTC_PACKET_INFO_TX(pSendPacket,
+ NULL,
+ (A_UINT8 *)pConnectMsg,
+ sizeof(HTC_CONNECT_SERVICE_MSG) + pConnectMsg->ServiceMetaLength,
+ ENDPOINT_0,
+ HTC_SERVICE_TX_PACKET_TAG);
+
+ /* we want synchronous operation */
+ pSendPacket->Completion = NULL;
+ HTC_PREPARE_SEND_PKT(pSendPacket,0,0,0);
+ status = HTCIssueSend(target,pSendPacket);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* wait for response */
+ status = HTCWaitforControlMessage(target, &pRecvPacket);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+ /* we controlled the buffer creation so it has to be properly aligned */
+ pResponseMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)pRecvPacket->pBuffer;
+
+ if ((pResponseMsg->MessageID != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) ||
+ (pRecvPacket->ActualLength < sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) {
+ /* this message is not valid */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ pConnectResp->ConnectRespCode = pResponseMsg->Status;
+ /* check response status */
+ if (pResponseMsg->Status != HTC_SERVICE_SUCCESS) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" Target failed service 0x%X connect request (status:%d)\n",
+ pResponseMsg->ServiceID, pResponseMsg->Status));
+ status = A_EPROTO;
+ break;
+ }
+
+ assignedEndpoint = (HTC_ENDPOINT_ID) pResponseMsg->EndpointID;
+ maxMsgSize = pResponseMsg->MaxMsgSize;
+
+ if ((pConnectResp->pMetaData != NULL) &&
+ (pResponseMsg->ServiceMetaLength > 0) &&
+ (pResponseMsg->ServiceMetaLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
+ /* caller supplied a buffer and the target responded with data */
+ int copyLength = min((int)pConnectResp->BufferLength, (int)pResponseMsg->ServiceMetaLength);
+ /* copy the meta data */
+ A_MEMCPY(pConnectResp->pMetaData,
+ ((A_UINT8 *)pResponseMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG),
+ copyLength);
+ pConnectResp->ActualLength = copyLength;
+ }
+
+ }
+
+ /* 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->ServiceID != 0) {
+ /* endpoint already in use! */
+ AR_DEBUG_ASSERT(FALSE);
+ break;
+ }
+
+ /* return assigned endpoint to caller */
+ pConnectResp->Endpoint = assignedEndpoint;
+ pConnectResp->MaxMsgLength = maxMsgSize;
+
+ /* setup the endpoint */
+ pEndpoint->ServiceID = pConnectReq->ServiceID; /* this marks the endpoint in use */
+ pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth;
+ pEndpoint->MaxMsgLength = maxMsgSize;
+ /* copy all the callbacks */
+ pEndpoint->EpCallBacks = pConnectReq->EpCallbacks;
+ /* set the credit distribution info for this endpoint, this information is
+ * passed back to the credit distribution callback function */
+ pEndpoint->CreditDist.ServiceID = pConnectReq->ServiceID;
+ pEndpoint->CreditDist.pHTCReserved = pEndpoint;
+ pEndpoint->CreditDist.Endpoint = assignedEndpoint;
+ pEndpoint->CreditDist.TxCreditSize = target->TargetCreditSize;
+
+ if (pConnectReq->MaxSendMsgSize != 0) {
+ /* override TxCreditsPerMaxMsg calculation, this optimizes the credit-low indications
+ * since the host will actually issue smaller messages in the Send path */
+ if (pConnectReq->MaxSendMsgSize > maxMsgSize) {
+ /* can't be larger than the maximum the target can support */
+ AR_DEBUG_ASSERT(FALSE);
+ break;
+ }
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg = pConnectReq->MaxSendMsgSize / target->TargetCreditSize;
+ } else {
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize;
+ }
+
+ if (0 == pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg = 1;
+ }
+
+ /* save local connection flags */
+ pEndpoint->LocalConnectionFlags = pConnectReq->LocalConnectionFlags;
+
+ status = A_OK;
+
+ } while (FALSE);
+
+ if (pSendPacket != NULL) {
+ HTC_FREE_CONTROL_TX(target,pSendPacket);
+ }
+
+ if (pRecvPacket != NULL) {
+ HTC_FREE_CONTROL_RX(target,pRecvPacket);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCConnectService \n"));
+
+ return status;
+}
+
+static void AddToEndpointDistList(HTC_TARGET *target, HTC_ENDPOINT_CREDIT_DIST *pEpDist)
+{
+ HTC_ENDPOINT_CREDIT_DIST *pCurEntry,*pLastEntry;
+
+ if (NULL == target->EpCreditDistributionListHead) {
+ target->EpCreditDistributionListHead = pEpDist;
+ pEpDist->pNext = NULL;
+ pEpDist->pPrev = NULL;
+ return;
+ }
+
+ /* queue to the end of the list, this does not have to be very
+ * fast since this list is built at startup time */
+ pCurEntry = target->EpCreditDistributionListHead;
+
+ while (pCurEntry) {
+ pLastEntry = pCurEntry;
+ pCurEntry = pCurEntry->pNext;
+ }
+
+ pLastEntry->pNext = pEpDist;
+ pEpDist->pPrev = pLastEntry;
+ pEpDist->pNext = NULL;
+}
+
+
+
+/* default credit init callback */
+static void HTCDefaultCreditInit(void *Context,
+ HTC_ENDPOINT_CREDIT_DIST *pEPList,
+ int TotalCredits)
+{
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
+ int totalEps = 0;
+ int creditsPerEndpoint;
+
+ pCurEpDist = pEPList;
+ /* first run through the list and figure out how many endpoints we are dealing with */
+ while (pCurEpDist != NULL) {
+ pCurEpDist = pCurEpDist->pNext;
+ totalEps++;
+ }
+
+ /* even distribution */
+ creditsPerEndpoint = TotalCredits/totalEps;
+
+ pCurEpDist = pEPList;
+ /* run through the list and set minimum and normal credits and
+ * provide the endpoint with some credits to start */
+ while (pCurEpDist != NULL) {
+
+ if (creditsPerEndpoint < pCurEpDist->TxCreditsPerMaxMsg) {
+ /* too many endpoints and not enough credits */
+ AR_DEBUG_ASSERT(FALSE);
+ break;
+ }
+ /* our minimum is set for at least 1 max message */
+ pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
+ /* this value is ignored by our credit alg, since we do
+ * not dynamically adjust credits, this is the policy of
+ * the "default" credit distribution, something simple and easy */
+ pCurEpDist->TxCreditsNorm = 0xFFFF;
+ /* give the endpoint minimum credits */
+ pCurEpDist->TxCredits = creditsPerEndpoint;
+ pCurEpDist->TxCreditsAssigned = creditsPerEndpoint;
+ pCurEpDist = pCurEpDist->pNext;
+ }
+
+}
+
+/* default credit distribution callback, NOTE, this callback holds the TX lock */
+void HTCDefaultCreditDist(void *Context,
+ HTC_ENDPOINT_CREDIT_DIST *pEPDistList,
+ HTC_CREDIT_DIST_REASON Reason)
+{
+ HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
+
+ if (Reason == HTC_CREDIT_DIST_SEND_COMPLETE) {
+ pCurEpDist = pEPDistList;
+ /* simple distribution */
+ while (pCurEpDist != NULL) {
+ if (pCurEpDist->TxCreditsToDist > 0) {
+ /* just give the endpoint back the credits */
+ pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
+ pCurEpDist->TxCreditsToDist = 0;
+ }
+ pCurEpDist = pCurEpDist->pNext;
+ }
+ }
+
+ /* note we do not need to handle the other reason codes as this is a very
+ * simple distribution scheme, no need to seek for more credits or handle inactivity */
+}
+
+void HTCSetCreditDistribution(HTC_HANDLE HTCHandle,
+ void *pCreditDistContext,
+ HTC_CREDIT_DIST_CALLBACK CreditDistFunc,
+ HTC_CREDIT_INIT_CALLBACK CreditInitFunc,
+ HTC_SERVICE_ID ServicePriorityOrder[],
+ int ListLength)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ int i;
+ int ep;
+
+ if (CreditInitFunc != NULL) {
+ /* caller has supplied their own distribution functions */
+ target->InitCredits = CreditInitFunc;
+ AR_DEBUG_ASSERT(CreditDistFunc != NULL);
+ target->DistributeCredits = CreditDistFunc;
+ target->pCredDistContext = pCreditDistContext;
+ } else {
+ /* caller wants HTC to do distribution */
+ /* if caller wants service to handle distributions then
+ * it must set both of these to NULL! */
+ AR_DEBUG_ASSERT(CreditDistFunc == NULL);
+ target->InitCredits = HTCDefaultCreditInit;
+ target->DistributeCredits = HTCDefaultCreditDist;
+ target->pCredDistContext = target;
+ }
+
+ /* always add HTC control endpoint first, we only expose the list after the
+ * first one, this is added for TX queue checking */
+ AddToEndpointDistList(target, &target->EndPoint[ENDPOINT_0].CreditDist);
+
+ /* build the list of credit distribution structures in priority order
+ * supplied by the caller, these will follow endpoint 0 */
+ for (i = 0; i < ListLength; i++) {
+ /* match services with endpoints and add the endpoints to the distribution list
+ * in FIFO order */
+ for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) {
+ if (target->EndPoint[ep].ServiceID == ServicePriorityOrder[i]) {
+ /* queue this one to the list */
+ AddToEndpointDistList(target, &target->EndPoint[ep].CreditDist);
+ break;
+ }
+ }
+ AR_DEBUG_ASSERT(ep < ENDPOINT_MAX);
+ }
+
+}