aboutsummaryrefslogtreecommitdiff
path: root/drivers/ar6000/htc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ar6000/htc')
-rw-r--r--drivers/ar6000/htc/ar6k.c991
-rw-r--r--drivers/ar6000/htc/ar6k.h191
-rw-r--r--drivers/ar6000/htc/ar6k_events.c638
-rw-r--r--drivers/ar6000/htc/htc.c508
-rw-r--r--drivers/ar6000/htc/htc_debug.h65
-rw-r--r--drivers/ar6000/htc/htc_internal.h168
-rw-r--r--drivers/ar6000/htc/htc_recv.c703
-rw-r--r--drivers/ar6000/htc/htc_send.c538
-rw-r--r--drivers/ar6000/htc/htc_services.c403
9 files changed, 4205 insertions, 0 deletions
diff --git a/drivers/ar6000/htc/ar6k.c b/drivers/ar6000/htc/ar6k.c
new file mode 100644
index 00000000000..72472abb0d8
--- /dev/null
+++ b/drivers/ar6000/htc/ar6k.c
@@ -0,0 +1,991 @@
+/*
+ * AR6K device layer that handles register level I/O
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "AR6Khwreg.h"
+#include "a_osapi.h"
+#include "a_debug.h"
+#include "hif.h"
+#include "htc_packet.h"
+#include "ar6k.h"
+
+#define MAILBOX_FOR_BLOCK_SIZE 1
+
+extern A_UINT32 resetok;
+
+static A_STATUS DevEnableInterrupts(AR6K_DEVICE *pDev);
+static A_STATUS DevDisableInterrupts(AR6K_DEVICE *pDev);
+
+#define LOCK_AR6K(p) A_MUTEX_LOCK(&(p)->Lock);
+#define UNLOCK_AR6K(p) A_MUTEX_UNLOCK(&(p)->Lock);
+
+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;
+}
+
+A_STATUS DevSetup(AR6K_DEVICE *pDev)
+{
+ A_UINT32 mailboxaddrs[AR6K_MAILBOXES];
+ A_UINT32 blocksizes[AR6K_MAILBOXES];
+ A_STATUS status = A_OK;
+ int i;
+
+ AR_DEBUG_ASSERT(AR6K_IRQ_PROC_REGS_SIZE == 16);
+ AR_DEBUG_ASSERT(AR6K_IRQ_ENABLE_REGS_SIZE == 4);
+
+ do {
+ /* give a handle to HIF for this target */
+ HIFSetHandle(pDev->HIFDevice, (void *)pDev);
+ /* initialize our free list of IO packets */
+ INIT_HTC_PACKET_QUEUE(&pDev->RegisterIOList);
+ A_MUTEX_INIT(&pDev->Lock);
+
+ /* get the addresses for all 4 mailboxes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_ADDR,
+ mailboxaddrs, sizeof(mailboxaddrs));
+
+ if (status != A_OK) {
+ AR_DEBUG_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 address of the mailbox we are using */
+ pDev->MailboxAddress = mailboxaddrs[HTC_MAILBOX];
+
+ /* get the block sizes */
+ status = HIFConfigureDevice(pDev->HIFDevice, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ blocksizes, sizeof(blocksizes));
+
+ if (status != A_OK) {
+ AR_DEBUG_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 */
+ AR_DEBUG_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->MailboxAddress));
+
+ 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_TRC,("HIF Interrupt processing is SYNC ONLY\n"));
+ break;
+ case HIF_DEVICE_IRQ_ASYNC_SYNC:
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("HIF Interrupt processing is ASYNC and SYNC\n"));
+ break;
+ default:
+ AR_DEBUG_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);
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ /* make sure handle is cleared */
+ HIFSetHandle(pDev->HIFDevice, NULL);
+ }
+
+ return status;
+
+}
+
+static A_STATUS DevEnableInterrupts(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+ AR6K_IRQ_ENABLE_REGISTERS regs;
+
+ LOCK_AR6K(pDev);
+
+ /* Enable all the interrupts except for the dragon 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;
+}
+
+static 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)
+{
+ /* Unmask the host controller interrupts */
+ HIFUnMaskInterrupt(pDev->HIFDevice);
+
+ return DevEnableInterrupts(pDev);
+}
+
+/* disable all device interrupts */
+A_STATUS DevMaskInterrupts(AR6K_DEVICE *pDev)
+{
+ A_STATUS status;
+
+ status = DevDisableInterrupts(pDev);
+
+ if (A_SUCCESS(status)) {
+ /* Disable the interrupt at the HIF layer */
+ HIFMaskInterrupt(pDev->HIFDevice);
+ }
+
+ return status;
+}
+
+/* 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;
+ AR_DEBUG_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;
+ AR_DEBUG_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_IRQ_PROC_REGISTERS *pIrqProcRegs,
+ AR6K_IRQ_ENABLE_REGISTERS *pIrqEnableRegs)
+{
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("\n<------- Register Table -------->\n"));
+
+ if (pIrqProcRegs != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Int Status: 0x%x\n",pIrqProcRegs->host_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("CPU Int Status: 0x%x\n",pIrqProcRegs->cpu_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Error Int Status: 0x%x\n",pIrqProcRegs->error_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Counter Int Status: 0x%x\n",pIrqProcRegs->counter_int_status));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Mbox Frame: 0x%x\n",pIrqProcRegs->mbox_frame));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Rx Lookahead Valid: 0x%x\n",pIrqProcRegs->rx_lookahead_valid));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Rx Lookahead 0: 0x%x\n",pIrqProcRegs->rx_lookahead[0]));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Rx Lookahead 1: 0x%x\n",pIrqProcRegs->rx_lookahead[1]));
+ }
+
+ if (pIrqEnableRegs != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Int Status Enable: 0x%x\n",pIrqEnableRegs->int_status_enable));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP,
+ ("Counter Int Status Enable: 0x%x\n",pIrqEnableRegs->counter_int_status_enable));
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("<------------------------------->\n"));
+ }
+}
+
+
+#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 */
+ msleep(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++) {
+ AR_DEBUG_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) {
+ AR_DEBUG_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) {
+ AR_DEBUG_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) {
+ AR_DEBUG_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/ar6000/htc/ar6k.h b/drivers/ar6000/htc/ar6k.h
new file mode 100644
index 00000000000..301ab34a37e
--- /dev/null
+++ b/drivers/ar6000/htc/ar6k.h
@@ -0,0 +1,191 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#ifndef AR6K_H_
+#define AR6K_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 hole[2];
+ A_UINT32 rx_lookahead[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;
+
+#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
+
+/* 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_DEVICE {
+ A_MUTEX_T Lock;
+ AR6K_IRQ_PROC_REGISTERS IrqProcRegisters;
+ AR6K_IRQ_ENABLE_REGISTERS IrqEnableRegisters;
+ void *HIFDevice;
+ A_UINT32 BlockSize;
+ A_UINT32 BlockMask;
+ A_UINT32 MailboxAddress;
+ 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 LookAhead, A_BOOL *pAsyncProc);
+ HIF_DEVICE_IRQ_PROCESSING_MODE HifIRQProcessingMode;
+ HIF_MASK_UNMASK_RECV_EVENT HifMaskUmaskRecvEvent;
+} AR6K_DEVICE;
+
+#define IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(pDev) ((pDev)->HifIRQProcessingMode != HIF_DEVICE_IRQ_SYNC_ONLY)
+
+A_STATUS DevSetup(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 DevDumpRegisters(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);
+
+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 = (SendLength + (pDev->BlockMask)) &
+ (~(pDev->BlockMask));
+#if 0 // BufferLength may not be set in , fix this...
+ if (paddedLength > pPacket->BufferLength) {
+ AR_DEBUG_ASSERT(FALSE);
+ if (pPacket->Completion != NULL) {
+ COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
+ }
+ return A_EINVAL;
+ }
+#endif
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("DevSendPacket, Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ paddedLength,
+ pDev->MailboxAddress,
+ sync ? "SYNC" : "ASYNC"));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ pDev->MailboxAddress,
+ 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;
+ }
+
+ 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 = (RecvLength + (pDev->BlockMask)) &
+ (~(pDev->BlockMask));
+ if (paddedLength > pPacket->BufferLength) {
+ AR_DEBUG_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_EINVAL;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("DevRecvPacket, Padded Length: %d Mbox:0x%X (mode:%s)\n",
+ paddedLength,
+ pDev->MailboxAddress,
+ sync ? "SYNC" : "ASYNC"));
+
+ status = HIFReadWrite(pDev->HIFDevice,
+ pDev->MailboxAddress,
+ pPacket->pBuffer,
+ paddedLength,
+ sync ? HIF_RD_SYNC_BLOCK_INC : HIF_RD_ASYNC_BLOCK_INC,
+ sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
+
+ if (sync) {
+ pPacket->Status = status;
+ }
+
+ return status;
+}
+
+#ifdef MBOXHW_UNIT_TEST
+A_STATUS DoMboxHWTest(AR6K_DEVICE *pDev);
+#endif
+
+#endif /*AR6K_H_*/
diff --git a/drivers/ar6000/htc/ar6k_events.c b/drivers/ar6000/htc/ar6k_events.c
new file mode 100644
index 00000000000..91b29af3711
--- /dev/null
+++ b/drivers/ar6000/htc/ar6k_events.c
@@ -0,0 +1,638 @@
+/*
+ * AR6K Driver layer event handling (i.e. interrupts, message polling)
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "AR6Khwreg.h"
+#include "a_osapi.h"
+#include "a_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;
+
+ COMPLETE_HTC_PACKET(pPacket,status);
+
+ 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;
+
+ AR_DEBUG_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 */
+ msleep(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;
+ AR_DEBUG_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);
+
+ AR_DEBUG_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;
+ AR_DEBUG_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);
+
+ AR_DEBUG_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);
+ }
+
+ /* 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);
+
+ AR_DEBUG_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_ASSERT(counter_int_status);
+ 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 */
+ 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 {
+ 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 */
+ pDev->MessagePendingCallback(pDev->HTCContext, lookAhead, NULL);
+ }
+
+ } 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;
+ }
+ /* 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;
+ AR_DEBUG_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;
+}
+
+/* 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->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->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);
+ }
+
+ } 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) {
+ 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, pASyncProcessing);
+ if (A_FAILED(status)) {
+ break;
+ }
+ }
+
+ /* 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);
+
+ 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));
+
+
+ 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 (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 */
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Acking interrupt from DevDsrHandler \n"));
+ HIFAckInterrupt(pDev->HIFDevice);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevDsrHandler \n"));
+ return A_OK;
+}
+
+
diff --git a/drivers/ar6000/htc/htc.c b/drivers/ar6000/htc/htc.c
new file mode 100644
index 00000000000..d52ed944e0d
--- /dev/null
+++ b/drivers/ar6000/htc/htc.c
@@ -0,0 +1,508 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#include "htc_internal.h"
+
+
+static HTC_INIT_INFO HTCInitInfo = {NULL,NULL,NULL};
+static A_BOOL HTCInitialized = FALSE;
+
+static A_STATUS HTCTargetInsertedHandler(void *hif_handle);
+static A_STATUS HTCTargetRemovedHandler(void *handle, A_STATUS status);
+static void HTCReportFailure(void *Context);
+
+/* Initializes the HTC layer */
+A_STATUS HTCInit(HTC_INIT_INFO *pInitInfo)
+{
+ HTC_CALLBACKS htcCallbacks;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Enter\n"));
+ if (HTCInitialized) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Exit\n"));
+ return A_OK;
+ }
+
+ A_MEMCPY(&HTCInitInfo,pInitInfo,sizeof(HTC_INIT_INFO));
+
+ A_MEMZERO(&htcCallbacks, sizeof(HTC_CALLBACKS));
+
+ /* setup HIF layer callbacks */
+ htcCallbacks.deviceInsertedHandler = HTCTargetInsertedHandler;
+ htcCallbacks.deviceRemovedHandler = HTCTargetRemovedHandler;
+ /* the device layer handles these */
+ htcCallbacks.rwCompletionHandler = DevRWCompletionHandler;
+ htcCallbacks.dsrHandler = DevDsrHandler;
+ HIFInit(&htcCallbacks);
+ HTCInitialized = TRUE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Exit\n"));
+ return A_OK;
+}
+
+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)
+{
+ 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 */
+static A_STATUS HTCTargetInsertedHandler(void *hif_handle)
+{
+ HTC_TARGET *target = NULL;
+ A_STATUS status;
+ int i;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Enter\n"));
+
+ 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;
+
+ /* setup device layer */
+ status = DevSetup(&target->Device);
+
+ 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,
+ HTC_CONTROL_BUFFER_SIZE,
+ 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,
+ HTC_CONTROL_BUFFER_SIZE);
+ HTC_FREE_CONTROL_TX(target,pControlPacket);
+ }
+
+ } while (FALSE);
+
+ if (A_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" calling AddInstance callback \n"));
+ /* announce ourselves */
+ HTCInitInfo.AddInstance((HTC_HANDLE)target);
+ } else {
+ if (target != NULL) {
+ HTCCleanup(target);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Exit\n"));
+
+ return status;
+}
+
+/* registered removal callback from the HIF layer */
+static A_STATUS HTCTargetRemovedHandler(void *handle, A_STATUS status)
+{
+ HTC_TARGET *target;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCTargetRemovedHandler handle:0x%X \n",(A_UINT32)handle));
+
+ if (NULL == handle) {
+ /* this could be NULL in the event that target initialization failed */
+ return A_OK;
+ }
+
+ target = ((AR6K_DEVICE *)handle)->HTCContext;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" removing target:0x%X instance:0x%X ... \n",
+ (A_UINT32)target, (A_UINT32)target->pInstanceContext));
+
+ if (target->pInstanceContext != NULL) {
+ /* let upper layer know, it needs to call HTCStop() */
+ HTCInitInfo.DeleteInstance(target->pInstanceContext);
+ }
+
+ HIFShutDownDevice(target->Device.HIFDevice);
+
+ HTCCleanup(target);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCTargetRemovedHandler \n"));
+ return A_OK;
+}
+
+/* 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;
+}
+
+/* set the instance block for this HTC handle, so that on removal, the blob can be
+ * returned to the caller */
+void HTCSetInstance(HTC_HANDLE HTCHandle, void *Instance)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+
+ target->pInstanceContext = Instance;
+}
+
+/* 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_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_MSG *)pPacket->pBuffer;
+
+ if ((pRdyMsg->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->CreditCount == 0 || pRdyMsg->CreditSize == 0) {
+ /* this message is not valid */
+ AR_DEBUG_ASSERT(FALSE);
+ status = A_EPROTO;
+ break;
+ }
+
+ target->TargetCredits = pRdyMsg->CreditCount;
+ target->TargetCreditSize = pRdyMsg->CreditSize;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" Target Ready: credits: %d credit size: %d\n",
+ target->TargetCredits, target->TargetCreditSize));
+
+ /* 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 needed */
+ connect.EpCallbacks.EpSendAvail = NULL; /* not needed */
+ 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"));
+
+ /* 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;
+}
+
+
+/* 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"));
+
+ /* mark that we are shutting down .. */
+ target->HTCStateFlags |= HTC_STATE_STOPPING;
+
+ /* 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);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCStop \n"));
+}
+
+/* undo what was done in HTCInit() */
+void HTCShutDown(void)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCShutDown: \n"));
+ HTCInitialized = FALSE;
+ /* undo HTCInit */
+ HIFShutDownDevice(NULL);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCShutDown: \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);
+}
+
+/* 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->pInstanceContext != NULL) && (HTCInitInfo.TargetFailure != NULL)) {
+ /* let upper layer know, it needs to call HTCStop() */
+ HTCInitInfo.TargetFailure(target->pInstanceContext, A_ERROR);
+ }
+}
+
+void DebugDumpBytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription)
+{
+ A_CHAR stream[60];
+ A_UINT32 i;
+ A_UINT16 offset, count;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("<---------Dumping %d Bytes : %s ------>\n", length, pDescription));
+
+ count = 0;
+ offset = 0;
+ for(i = 0; i < length; i++) {
+ sprintf(stream + offset, "%2.2X ", buffer[i]);
+ count ++;
+ offset += 3;
+
+ if(count == 16) {
+ count = 0;
+ offset = 0;
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("[H]: %s\n", stream));
+ A_MEMZERO(stream, 60);
+ }
+ }
+
+ if(offset != 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("[H]: %s\n", stream));
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("<------------------------------------------------->\n"));
+}
+
+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
+}
diff --git a/drivers/ar6000/htc/htc_debug.h b/drivers/ar6000/htc/htc_debug.h
new file mode 100644
index 00000000000..08080be51a2
--- /dev/null
+++ b/drivers/ar6000/htc/htc_debug.h
@@ -0,0 +1,65 @@
+#ifndef HTC_DEBUG_H_
+#define HTC_DEBUG_H_
+/*
+ *
+ * Copyright (c) 2004-2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+/* ------- Debug related stuff ------- */
+enum {
+ ATH_DEBUG_SEND = 0x0001,
+ ATH_DEBUG_RECV = 0x0002,
+ ATH_DEBUG_SYNC = 0x0004,
+ ATH_DEBUG_DUMP = 0x0008,
+ ATH_DEBUG_IRQ = 0x0010,
+ ATH_DEBUG_TRC = 0x0020,
+ ATH_DEBUG_WARN = 0x0040,
+ ATH_DEBUG_ERR = 0x0080,
+ ATH_DEBUG_ANY = 0xFFFF,
+};
+
+#ifdef DEBUG
+
+// TODO FIX usage of A_PRINTF!
+#define AR_DEBUG_LVL_CHECK(lvl) (debughtc & (lvl))
+#define AR_DEBUG_PRINTBUF(buffer, length, desc) do { \
+ if (debughtc & ATH_DEBUG_DUMP) { \
+ DebugDumpBytes(buffer, length,desc); \
+ } \
+} while(0)
+#define PRINTX_ARG(arg...) arg
+#define AR_DEBUG_PRINTF(flags, args) do { \
+ if (debughtc & (flags)) { \
+ A_PRINTF(KERN_ALERT PRINTX_ARG args); \
+ } \
+} while (0)
+#define AR_DEBUG_ASSERT(test) do { \
+ if (!(test)) { \
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Debug Assert Caught, File %s, Line: %d, Test:%s \n",__FILE__, __LINE__,#test)); \
+ } \
+} while(0)
+extern int debughtc;
+#else
+#define AR_DEBUG_PRINTF(flags, args)
+#define AR_DEBUG_PRINTBUF(buffer, length, desc)
+#define AR_DEBUG_ASSERT(test)
+#define AR_DEBUG_LVL_CHECK(lvl) 0
+#endif
+
+void DebugDumpBytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription);
+
+#endif /*HTC_DEBUG_H_*/
diff --git a/drivers/ar6000/htc/htc_internal.h b/drivers/ar6000/htc/htc_internal.h
new file mode 100644
index 00000000000..ebb8ac16ed1
--- /dev/null
+++ b/drivers/ar6000/htc/htc_internal.h
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#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 "a_debug.h"
+#include "htc.h"
+#include "htc_api.h"
+#include "bmi_msg.h"
+#include "hif.h"
+#include "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
+
+typedef struct _HTC_ENDPOINT {
+ 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 CurrentTxQueueDepth; /* current TX queue depth */
+ int MaxMsgLength; /* max length of endpoint message */
+#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)
+
+#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CONTROL_MESSAGE_LENGTH + HTC_HDR_LENGTH)
+
+typedef struct HTC_CONTROL_BUFFER {
+ HTC_PACKET HtcPacket;
+ A_UINT8 Buffer[HTC_CONTROL_BUFFER_SIZE];
+} HTC_CONTROL_BUFFER;
+
+/* 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;
+ int TargetCreditSize;
+ A_MUTEX_T HTCLock;
+ A_MUTEX_T HTCRxLock;
+ A_MUTEX_T HTCTxLock;
+ AR6K_DEVICE Device; /* AR6K - specific state */
+ A_UINT32 HTCStateFlags;
+ HTC_ENDPOINT_ID EpWaitingForBuffers;
+ A_BOOL TargetFailure;
+ void *pInstanceContext;
+#define HTC_STATE_WAIT_BUFFERS (1 << 0)
+#define HTC_STATE_STOPPING (1 << 1)
+#ifdef HTC_CAPTURE_LAST_FRAME
+ HTC_FRAME_HDR LastFrameHdr; /* useful for debugging */
+ A_UINT8 LastTrailer[256];
+ A_UINT8 LastTrailerLength;
+#endif
+} HTC_TARGET;
+
+#define HTC_STOPPING(t) ((t)->HTCStateFlags & HTC_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) \
+{ \
+ 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, A_UINT8 Flags);
+A_STATUS HTCIssueRecv(HTC_TARGET *target, HTC_PACKET *pPacket);
+void HTCRecvCompleteHandler(void *Context, HTC_PACKET *pPacket);
+A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 LookAhead, A_BOOL *pAsyncProc);
+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); \
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTC_INTERNAL_H_ */
diff --git a/drivers/ar6000/htc/htc_recv.c b/drivers/ar6000/htc/htc_recv.c
new file mode 100644
index 00000000000..4be2b0833c8
--- /dev/null
+++ b/drivers/ar6000/htc/htc_recv.c
@@ -0,0 +1,703 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#include "htc_internal.h"
+
+#define HTCIssueRecv(t, p) \
+ DevRecvPacket(&(t)->Device, \
+ (p), \
+ (p)->ActualLength)
+
+#define DO_RCV_COMPLETION(t,p,e) \
+{ \
+ if ((p)->ActualLength > 0) { \
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" completing packet 0x%X (%d bytes) on ep : %d \n", \
+ (A_UINT32)(p), (p)->ActualLength, (p)->Endpoint)); \
+ (e)->EpCallBacks.EpRecv((e)->EpCallBacks.pContext, \
+ (p)); \
+ } else { \
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" recycling empty packet \n")); \
+ HTC_RECYCLE_RX_PKT((t), (p)); \
+ } \
+}
+
+#ifdef HTC_EP_STAT_PROFILING
+#define HTC_RX_STAT_PROFILE(t,ep,lookAhead) \
+{ \
+ LOCK_HTC_RX((t)); \
+ INC_HTC_EP_STAT((ep), RxReceived, 1); \
+ if ((lookAhead) != 0) { \
+ INC_HTC_EP_STAT((ep), RxLookAheads, 1); \
+ } \
+ UNLOCK_HTC_RX((t)); \
+}
+#else
+#define HTC_RX_STAT_PROFILE(t,ep,lookAhead)
+#endif
+
+static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target,
+ A_UINT8 *pBuffer,
+ int Length,
+ A_UINT32 *pNextLookAhead,
+ 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)) &&
+ (pNextLookAhead != 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 *)pNextLookAhead)[0] = pLookAhead->LookAhead[0];
+ ((A_UINT8 *)pNextLookAhead)[1] = pLookAhead->LookAhead[1];
+ ((A_UINT8 *)pNextLookAhead)[2] = pLookAhead->LookAhead[2];
+ ((A_UINT8 *)pNextLookAhead)[3] = pLookAhead->LookAhead[3];
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
+ DebugDumpBytes((A_UINT8 *)pNextLookAhead,4,"Next Look Ahead");
+ }
+ }
+ 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 *pNextLookAhead)
+{
+ A_UINT8 temp;
+ A_UINT8 *pBuf;
+ A_STATUS status = A_OK;
+ A_UINT16 payloadLen;
+ A_UINT32 lookAhead;
+
+ pBuf = pPacket->pBuffer;
+
+ 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 (lookAhead != pPacket->HTCReserved) {
+ /* 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! \n"));
+ DebugDumpBytes((A_UINT8 *)&pPacket->HTCReserved,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;
+ }
+
+ /* process trailer data that follows HDR + application payload */
+ status = HTCProcessTrailer(target,
+ (pBuf + HTC_HDR_LENGTH + payloadLen - temp),
+ temp,
+ pNextLookAhead,
+ 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,"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;
+}
+
+/* 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 nextLookAhead = 0;
+ A_STATUS status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (status:%d, ep:%d) \n",
+ pPacket->Status, pPacket->Endpoint));
+
+ 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,&nextLookAhead);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+ /* was there a lookahead for the next packet? */
+ if (nextLookAhead != 0) {
+ A_STATUS nextStatus;
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTCRecvCompleteHandler - next look ahead was non-zero : 0x%X \n",
+ nextLookAhead));
+ /* we have another packet, get the next packet fetch started (pipelined) before
+ * we call into the endpoint's callback, this will start another async request */
+ nextStatus = HTCRecvMessagePendingHandler(target,nextLookAhead,NULL);
+ if (A_EPROTO == nextStatus) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Next look ahead from recv header was INVALID\n"));
+ DebugDumpBytes((A_UINT8 *)&nextLookAhead,
+ 4,
+ "BAD lookahead from lookahead report");
+ }
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
+ ("HTCRecvCompleteHandler - 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);
+ }
+
+ HTC_RX_STAT_PROFILE(target,pEndpoint,nextLookAhead);
+ DO_RCV_COMPLETION(target,pPacket,pEndpoint);
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCRecvCompleteHandler , message fetch failed (status = %d) \n",
+ status));
+ /* recyle this packet */
+ HTC_RECYCLE_RX_PKT(target, pPacket);
+ }
+
+ 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->HTCReserved = 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);
+
+ 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;
+}
+
+/* callback when device layer or lookahead report parsing detects a pending message */
+A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 LookAhead, A_BOOL *pAsyncProc)
+{
+ HTC_TARGET *target = (HTC_TARGET *)Context;
+ A_STATUS status = A_OK;
+ HTC_PACKET *pPacket = NULL;
+ HTC_FRAME_HDR *pHdr;
+ HTC_ENDPOINT *pEndpoint;
+ A_BOOL asyncProc = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler LookAhead:0x%X \n",LookAhead));
+
+ 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;
+ }
+
+ while (TRUE) {
+
+ pHdr = (HTC_FRAME_HDR *)&LookAhead;
+
+ 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->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;
+ }
+
+ pEndpoint = &target->EndPoint[pHdr->EndpointID];
+
+ 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;
+ }
+
+ /* lock RX to get a buffer */
+ LOCK_HTC_RX(target);
+
+ /* 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,
+ pHdr->EndpointID);
+ 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->HTCStateFlags |= HTC_STATE_WAIT_BUFFERS;
+ target->EpWaitingForBuffers = pHdr->EndpointID;
+ status = A_NO_MEMORY;
+ }
+
+ UNLOCK_HTC_RX(target);
+
+ if (A_FAILED(status)) {
+ /* no buffers */
+ break;
+ }
+
+ AR_DEBUG_ASSERT(pPacket->Endpoint == pHdr->EndpointID);
+
+ /* make sure this message can fit in the endpoint buffer */
+ if ((pHdr->PayloadLen + HTC_HDR_LENGTH) > pPacket->BufferLength) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Payload Length Error : header reports payload of: %d, endpoint buffer size: %d \n",
+ pHdr->PayloadLen, pPacket->BufferLength));
+ status = A_EPROTO;
+ break;
+ }
+
+ pPacket->HTCReserved = LookAhead; /* set expected look ahead */
+ /* set the amount of data to fetch */
+ pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
+
+ 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;
+ }
+
+ /* go fetch the packet */
+ status = HTCIssueRecv(target, pPacket);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ 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;
+ }
+
+ /* in the sync case, we process the packet, check lookaheads and then repeat */
+
+ LookAhead = 0;
+ status = HTCProcessRecvHeader(target,pPacket,&LookAhead);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ HTC_RX_STAT_PROFILE(target,pEndpoint,LookAhead);
+ DO_RCV_COMPLETION(target,pPacket,pEndpoint);
+
+ pPacket = NULL;
+
+ if (0 == LookAhead) {
+ break;
+ }
+
+ }
+
+ if (A_NO_MEMORY == status) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ (" Endpoint :%d has no buffers, blocking receiver to prevent overrun.. \n",
+ pHdr->EndpointID));
+ /* try to stop receive at the device layer */
+ DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC);
+ status = A_OK;
+ } else if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Failed to get pending message : LookAhead Value: 0x%X (status = %d) \n",
+ LookAhead, status));
+ if (pPacket != NULL) {
+ /* clean up packet on error */
+ HTC_RECYCLE_RX_PKT(target, pPacket);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n"));
+
+ return status;
+}
+
+/* Makes a buffer available to the HTC module */
+A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint;
+ A_BOOL unblockRecv = FALSE;
+ A_STATUS status = A_OK;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("+- HTCAddReceivePkt: endPointId: %d, buffer: 0x%X, length: %d\n",
+ pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->BufferLength));
+
+ do {
+
+ if (HTC_STOPPING(target)) {
+ status = A_ECANCELED;
+ break;
+ }
+
+ AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
+
+ pEndpoint = &target->EndPoint[pPacket->Endpoint];
+
+ LOCK_HTC_RX(target);
+
+ /* store receive packet */
+ HTC_PACKET_ENQUEUE(&pEndpoint->RxBuffers, pPacket);
+
+ /* check if we are blocked waiting for a new buffer */
+ if (target->HTCStateFlags & HTC_STATE_WAIT_BUFFERS) {
+ if (target->EpWaitingForBuffers == pPacket->Endpoint) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" receiver was blocked on ep:%d, unblocking.. \n",
+ target->EpWaitingForBuffers));
+ target->HTCStateFlags &= ~HTC_STATE_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;
+}
+
+static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
+{
+ HTC_PACKET *pPacket;
+
+ LOCK_HTC_RX(target);
+
+ while (1) {
+ pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
+ 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));
+ /* give the packet back */
+ pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext,
+ pPacket);
+ LOCK_HTC_RX(target);
+ }
+
+ UNLOCK_HTC_RX(target);
+
+
+}
+
+void HTCFlushRecvBuffers(HTC_TARGET *target)
+{
+ HTC_ENDPOINT *pEndpoint;
+ int i;
+
+ /* NOTE: no need to flush endpoint 0, these buffers were
+ * allocated as part of the HTC struct */
+ for (i = ENDPOINT_1; i < ENDPOINT_MAX; i++) {
+ pEndpoint = &target->EndPoint[i];
+ if (pEndpoint->ServiceID == 0) {
+ /* not in use.. */
+ continue;
+ }
+ HTCFlushEndpointRX(target,pEndpoint);
+ }
+
+
+}
+
+
diff --git a/drivers/ar6000/htc/htc_send.c b/drivers/ar6000/htc/htc_send.c
new file mode 100644
index 00000000000..cf0dabef472
--- /dev/null
+++ b/drivers/ar6000/htc/htc_send.c
@@ -0,0 +1,538 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#include "htc_internal.h"
+
+#define DO_EP_TX_COMPLETION(ep,p) \
+{ \
+ (p)->Completion = NULL; \
+ (ep)->EpCallBacks.EpTxComplete((ep)->EpCallBacks.pContext,(p)); \
+}
+
+
+/* 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)); \
+}
+
+/* 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];
+
+
+ if (A_FAILED(pPacket->Status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("HTCSendPktCompletionHandler: request failed (status:%d, ep:%d) \n",
+ pPacket->Status, pPacket->Endpoint));
+ }
+ /* first, fixup the head room we allocated */
+ pPacket->pBuffer += HTC_HDR_LENGTH;
+ /* do completion */
+ DO_EP_TX_COMPLETION(pEndpoint,pPacket);
+}
+
+A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket, A_UINT8 SendFlags)
+{
+ A_STATUS status;
+ A_UINT8 *pHdrBuf;
+ A_BOOL sync = FALSE;
+
+ /* caller always provides headrooom */
+ pPacket->pBuffer -= HTC_HDR_LENGTH;
+ pHdrBuf = pPacket->pBuffer;
+ /* setup frame header */
+ A_SET_UINT16_FIELD(pHdrBuf,HTC_FRAME_HDR,PayloadLen,(A_UINT16)pPacket->ActualLength);
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,Flags,SendFlags);
+ A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,EndpointID, (A_UINT8)pPacket->Endpoint);
+
+ 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;
+}
+
+/* try to send the current packet or a packet at the head of the TX queue,
+ * if there are no credits, the packet remains in the queue. */
+static void HTCTrySend(HTC_TARGET *target,
+ HTC_PACKET *pPacketToSend,
+ HTC_ENDPOINT_ID ep)
+{
+ HTC_PACKET *pPacket;
+ HTC_ENDPOINT *pEndpoint;
+ int creditsRequired;
+ A_UINT8 sendFlags;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (pPkt:0x%X)\n",(A_UINT32)pPacketToSend));
+
+ pEndpoint = &target->EndPoint[ep];
+
+ LOCK_HTC_TX(target);
+
+ if (pPacketToSend != NULL) {
+ /* caller supplied us a packet to queue to the tail of the HTC TX queue before
+ * we check the tx queue */
+ HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue,pPacketToSend);
+ pEndpoint->CurrentTxQueueDepth++;
+ }
+
+ /* now drain the TX queue for transmission as long as we have enough
+ * credits */
+
+ while (1) {
+
+ if (HTC_QUEUE_EMPTY(&pEndpoint->TxQueue)) {
+ /* nothing in the queue */
+ break;
+ }
+
+ sendFlags = 0;
+
+ /* get packet at head, but don't remove it */
+ pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%X , Queue Depth: %d\n",
+ (A_UINT32)pPacket, pEndpoint->CurrentTxQueueDepth));
+
+ /* figure out how many credits this message requires */
+ creditsRequired = pPacket->ActualLength + HTC_HDR_LENGTH;
+ creditsRequired += target->TargetCreditSize - 1;
+ creditsRequired /= target->TargetCreditSize;
+
+ 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 */
+ if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
+ 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);
+ pEndpoint->CurrentTxQueueDepth--;
+
+ INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
+
+ UNLOCK_HTC_TX(target);
+
+ HTCIssueSend(target, pPacket, sendFlags);
+
+ LOCK_HTC_TX(target);
+
+ /* go back and check for more messages */
+ }
+
+ if (pEndpoint->CurrentTxQueueDepth >= pEndpoint->MaxTxQueueDepth) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d, TX queue is full, Depth:%d, Max:%d \n",
+ ep, pEndpoint->CurrentTxQueueDepth, pEndpoint->MaxTxQueueDepth));
+ UNLOCK_HTC_TX(target);
+ /* queue is now full, let caller know */
+ if (pEndpoint->EpCallBacks.EpSendFull != NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Calling driver's send full callback.... \n"));
+ pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext, ep);
+ }
+ } else {
+ UNLOCK_HTC_TX(target);
+ /* queue is now available for new packet, let caller know */
+ if (pEndpoint->EpCallBacks.EpSendAvail)
+ pEndpoint->EpCallBacks.EpSendAvail(pEndpoint->EpCallBacks.pContext, ep);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
+}
+
+/* HTC API - HTCSendPkt */
+A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
+{
+ HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
+ HTC_ENDPOINT *pEndpoint;
+ HTC_ENDPOINT_ID ep;
+ A_STATUS status = A_OK;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
+ ("+HTCSendPkt: Enter endPointId: %d, buffer: 0x%X, length: %d \n",
+ pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->ActualLength));
+
+ ep = pPacket->Endpoint;
+ AR_DEBUG_ASSERT(ep < ENDPOINT_MAX);
+ pEndpoint = &target->EndPoint[ep];
+
+ do {
+
+ if (HTC_STOPPING(target)) {
+ status = A_ECANCELED;
+ pPacket->Status = status;
+ DO_EP_TX_COMPLETION(pEndpoint,pPacket);
+ break;
+ }
+ /* everything sent through this interface is asynchronous */
+ /* fill in HTC completion routines */
+ pPacket->Completion = HTCSendPktCompletionHandler;
+ pPacket->pContext = target;
+
+ HTCTrySend(target, pPacket, ep);
+
+ } while (FALSE);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPkt \n"));
+
+ return status;
+}
+
+
+/* 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 (pEndpoint->CurrentTxQueueDepth > 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n",
+ pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, pEndpoint->CurrentTxQueueDepth));
+ /* 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, NULL, pDistItem->Endpoint);
+ }
+
+ 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;
+ }
+
+ 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;
+
+ /* 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, 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(pPacket);
+ /* add it to the discard pile */
+ HTC_PACKET_ENQUEUE(&discardQueue, pPacket);
+ pEndpoint->CurrentTxQueueDepth--;
+ }
+
+ } 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));
+ DO_EP_TX_COMPLETION(pEndpoint,pPacket);
+ }
+
+}
+
+void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist)
+{
+#ifdef DEBUG
+ HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *)pEPDist->pHTCReserved;
+#endif
+
+ 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", pEndpoint->CurrentTxQueueDepth));
+ 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;
+
+ 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) {
+ /* 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);
+
+}
diff --git a/drivers/ar6000/htc/htc_services.c b/drivers/ar6000/htc/htc_services.c
new file mode 100644
index 00000000000..e5d50d16ddc
--- /dev/null
+++ b/drivers/ar6000/htc/htc_services.c
@@ -0,0 +1,403 @@
+/*
+ *
+ * Copyright (c) 2007 Atheros Communications Inc.
+ * 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.
+ *
+ *
+ *
+ */
+
+#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);
+
+ /* the only control messages we are expecting are NULL messages (credit resports), which should
+ * never get here */
+ 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);
+}
+
+A_STATUS HTCSendSetupComplete(HTC_TARGET *target)
+{
+ HTC_PACKET *pSendPacket = NULL;
+ A_STATUS status;
+ HTC_SETUP_COMPLETE_MSG *pSetupComplete;
+
+ do {
+ /* allocate a packet to send to the target */
+ pSendPacket = HTC_ALLOC_CONTROL_TX(target);
+
+ if (NULL == pSendPacket) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ /* 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;
+ /* send the message */
+ status = HTCIssueSend(target,pSendPacket,0);
+
+ } 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;
+ 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;
+
+ status = HTCIssueSend(target,pSendPacket,0);
+
+ 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 = 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];
+
+ 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;
+ INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBuffers);
+ INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue);
+ /* 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;
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize;
+
+ if (0 == pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
+ pEndpoint->CreditDist.TxCreditsPerMaxMsg = 1;
+ }
+
+ 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);
+ }
+
+}