From 30295c8936f3a426985e99bebb17d72bd7847910 Mon Sep 17 00:00:00 2001 From: Vipin Mehta Date: Wed, 1 Sep 2010 12:06:33 -0700 Subject: staging: add ath6kl driver for AR6003 chip AR6003 is a single stream, SDIO based 802.11 chipset from Atheros optimized for mobile and embedded devices. ath6kl is a cfg80211 driver for AR6003 and supports both the station and AP mode of operation. Station mode supports 802.11 a/b/g/n with HT20 on 2.4/5GHz and HT40 only on 5GHz. Some of the other features include WPA/WPA2, WPS, WMM, WMM-PS, and BT coexistence. AP mode can be operated only in b/g mode with support for a subset of features mentioned above. The driver supports cfg80211 but comes with its own set of wext ioctls which have historically supported some of our customers with features like BT 3.0 and AP mode of operation. For further details, please refer to: http://wireless.kernel.org/en/users/Drivers/ath6kl The driver requires firmware that runs on the chip's network processor. The majority of it is stored in ROM. The binaries that are downloaded and executed from RAM are as follows: 1) Patch against the code in ROM for bug fixes and feature enhancements. 2) Code to copy the data from the OTP region of the memory into RAM. 3) Calibration file carrying board specific data. The above files need to be present in the directory '/lib/firmware/ath6k/AR6003/hw2.0/' for the driver to initialize the chip upon enumeration. The files can be downloaded from the link specified at the following location: http://wireless.kernel.org/en/users/Drivers/ath6kl#Download This driver is only provided in the interim while we work on the mac80211 replacement, ath6k. Once the mac80211 driver achieves feature parity with the ath6kl driver, the ath6kl will be deprecated and removed from staging. Signed-off-by: Vipin Mehta Signed-off-by: Greg Kroah-Hartman --- .../ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c | 1280 ++++++++++++++++++++ 1 file changed, 1280 insertions(+) create mode 100644 drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c (limited to 'drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c') diff --git a/drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c b/drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c new file mode 100644 index 00000000000..db6d30c113b --- /dev/null +++ b/drivers/staging/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c @@ -0,0 +1,1280 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) 2007-2010 Atheros Corporation. All rights reserved. +// +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// +//------------------------------------------------------------------------------ +//============================================================================== +// Protocol module for use in bridging HCI-UART packets over the GMBOX interface +// +// Author(s): ="Atheros" +//============================================================================== +#include "a_config.h" +#include "athdefs.h" +#include "a_types.h" +#include "a_osapi.h" +#include "../htc_debug.h" +#include "hif.h" +#include "htc_packet.h" +#include "ar6k.h" +#include "hci_transport_api.h" +#include "gmboxif.h" +#include "ar6000_diag.h" +#include "hw/apb_map.h" +#include "hw/mbox_reg.h" + +#ifdef ATH_AR6K_ENABLE_GMBOX +#define HCI_UART_COMMAND_PKT 0x01 +#define HCI_UART_ACL_PKT 0x02 +#define HCI_UART_SCO_PKT 0x03 +#define HCI_UART_EVENT_PKT 0x04 + +#define HCI_RECV_WAIT_BUFFERS (1 << 0) + +#define HCI_SEND_WAIT_CREDITS (1 << 0) + +#define HCI_UART_BRIDGE_CREDIT_SIZE 128 + +#define CREDIT_POLL_COUNT 256 + +#define HCI_DELAY_PER_INTERVAL_MS 10 +#define BTON_TIMEOUT_MS 500 +#define BTOFF_TIMEOUT_MS 500 +#define BAUD_TIMEOUT_MS 1 +#define BTPWRSAV_TIMEOUT_MS 1 + +typedef struct { + HCI_TRANSPORT_CONFIG_INFO HCIConfig; + A_BOOL HCIAttached; + A_BOOL HCIStopped; + A_UINT32 RecvStateFlags; + A_UINT32 SendStateFlags; + HCI_TRANSPORT_PACKET_TYPE WaitBufferType; + HTC_PACKET_QUEUE SendQueue; /* write queue holding HCI Command and ACL packets */ + HTC_PACKET_QUEUE HCIACLRecvBuffers; /* recv queue holding buffers for incomming ACL packets */ + HTC_PACKET_QUEUE HCIEventBuffers; /* recv queue holding buffers for incomming event packets */ + AR6K_DEVICE *pDev; + A_MUTEX_T HCIRxLock; + A_MUTEX_T HCITxLock; + int CreditsMax; + int CreditsConsumed; + int CreditsAvailable; + int CreditSize; + int CreditsCurrentSeek; + int SendProcessCount; +} GMBOX_PROTO_HCI_UART; + +#define LOCK_HCI_RX(t) A_MUTEX_LOCK(&(t)->HCIRxLock); +#define UNLOCK_HCI_RX(t) A_MUTEX_UNLOCK(&(t)->HCIRxLock); +#define LOCK_HCI_TX(t) A_MUTEX_LOCK(&(t)->HCITxLock); +#define UNLOCK_HCI_TX(t) A_MUTEX_UNLOCK(&(t)->HCITxLock); + +#define DO_HCI_RECV_INDICATION(p,pt) \ +{ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI: Indicate Recv on packet:0x%lX status:%d len:%d type:%d \n", \ + (unsigned long)(pt),(pt)->Status, A_SUCCESS((pt)->Status) ? (pt)->ActualLength : 0, HCI_GET_PACKET_TYPE(pt))); \ + (p)->HCIConfig.pHCIPktRecv((p)->HCIConfig.pContext, (pt)); \ +} + +#define DO_HCI_SEND_INDICATION(p,pt) \ +{ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Indicate Send on packet:0x%lX status:%d type:%d \n", \ + (unsigned long)(pt),(pt)->Status,HCI_GET_PACKET_TYPE(pt))); \ + (p)->HCIConfig.pHCISendComplete((p)->HCIConfig.pContext, (pt)); \ +} + +static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous); + +static void HCIUartCleanup(GMBOX_PROTO_HCI_UART *pProtocol) +{ + A_ASSERT(pProtocol != NULL); + + A_MUTEX_DELETE(&pProtocol->HCIRxLock); + A_MUTEX_DELETE(&pProtocol->HCITxLock); + + A_FREE(pProtocol); +} + +static A_STATUS InitTxCreditState(GMBOX_PROTO_HCI_UART *pProt) +{ + A_STATUS status; + int credits; + int creditPollCount = CREDIT_POLL_COUNT; + A_BOOL gotCredits = FALSE; + + pProt->CreditsConsumed = 0; + + do { + + if (pProt->CreditsMax != 0) { + /* we can only call this only once per target reset */ + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI: InitTxCreditState - already called! \n")); + A_ASSERT(FALSE); + status = A_EINVAL; + break; + } + + /* read the credit counter. At startup the target will set the credit counter + * to the max available, we read this in a loop because it may take + * multiple credit counter reads to get all credits */ + + while (creditPollCount) { + + credits = 0; + + status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits); + + if (A_FAILED(status)) { + break; + } + + if (!gotCredits && (0 == credits)) { + creditPollCount--; + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: credit is 0, retrying (%d) \n",creditPollCount)); + A_MDELAY(HCI_DELAY_PER_INTERVAL_MS); + continue; + } else { + gotCredits = TRUE; + } + + if (0 == credits) { + break; + } + + pProt->CreditsMax += credits; + } + + if (A_FAILED(status)) { + break; + } + + if (0 == creditPollCount) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, + ("** HCI : Failed to get credits! GMBOX Target was not available \n")); + status = A_ERROR; + break; + } + + /* now get the size */ + status = DevGMboxReadCreditSize(pProt->pDev, &pProt->CreditSize); + + if (A_FAILED(status)) { + break; + } + + } while (FALSE); + + if (A_SUCCESS(status)) { + pProt->CreditsAvailable = pProt->CreditsMax; + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI : InitTxCreditState - credits avail: %d, size: %d \n", + pProt->CreditsAvailable, pProt->CreditSize)); + } + + return status; +} + +static A_STATUS CreditsAvailableCallback(void *pContext, int Credits, A_BOOL CreditIRQEnabled) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext; + A_BOOL enableCreditIrq = FALSE; + A_BOOL disableCreditIrq = FALSE; + A_BOOL doPendingSends = FALSE; + A_STATUS status = A_OK; + + /** this callback is called under 2 conditions: + * 1. The credit IRQ interrupt was enabled and signaled. + * 2. A credit counter read completed. + * + * The function must not assume that the calling context can block ! + */ + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback (Credits:%d, IRQ:%s) \n", + Credits, CreditIRQEnabled ? "ON" : "OFF")); + + LOCK_HCI_TX(pProt); + + do { + + if (0 == Credits) { + if (!CreditIRQEnabled) { + /* enable credit IRQ */ + enableCreditIrq = TRUE; + } + break; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: current credit state, consumed:%d available:%d max:%d seek:%d\n", + pProt->CreditsConsumed, + pProt->CreditsAvailable, + pProt->CreditsMax, + pProt->CreditsCurrentSeek)); + + pProt->CreditsAvailable += Credits; + A_ASSERT(pProt->CreditsAvailable <= pProt->CreditsMax); + pProt->CreditsConsumed -= Credits; + A_ASSERT(pProt->CreditsConsumed >= 0); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state, consumed:%d available:%d max:%d seek:%d\n", + pProt->CreditsConsumed, + pProt->CreditsAvailable, + pProt->CreditsMax, + pProt->CreditsCurrentSeek)); + + if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) { + /* we have enough credits to fullfill at least 1 packet waiting in the queue */ + pProt->CreditsCurrentSeek = 0; + pProt->SendStateFlags &= ~HCI_SEND_WAIT_CREDITS; + doPendingSends = TRUE; + if (CreditIRQEnabled) { + /* credit IRQ was enabled, we shouldn't need it anymore */ + disableCreditIrq = TRUE; + } + } else { + /* not enough credits yet, enable credit IRQ if we haven't already */ + if (!CreditIRQEnabled) { + enableCreditIrq = TRUE; + } + } + + } while (FALSE); + + UNLOCK_HCI_TX(pProt); + + if (enableCreditIrq) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Enabling credit count IRQ...\n")); + /* must use async only */ + status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_ENABLE, PROC_IO_ASYNC); + } else if (disableCreditIrq) { + /* must use async only */ + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Disabling credit count IRQ...\n")); + status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_DISABLE, PROC_IO_ASYNC); + } + + if (doPendingSends) { + HCITrySend(pProt, NULL, FALSE); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback \n")); + return status; +} + +static INLINE void NotifyTransportFailure(GMBOX_PROTO_HCI_UART *pProt, A_STATUS status) +{ + if (pProt->HCIConfig.TransportFailure != NULL) { + pProt->HCIConfig.TransportFailure(pProt->HCIConfig.pContext, status); + } +} + +static void FailureCallback(void *pContext, A_STATUS Status) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext; + + /* target assertion occured */ + NotifyTransportFailure(pProt, Status); +} + +static void StateDumpCallback(void *pContext) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext; + + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("============ HCIUart State ======================\n")); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("RecvStateFlags : 0x%X \n",pProt->RecvStateFlags)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendStateFlags : 0x%X \n",pProt->SendStateFlags)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("WaitBufferType : %d \n",pProt->WaitBufferType)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendQueue Depth : %d \n",HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue))); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsMax : %d \n",pProt->CreditsMax)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsConsumed : %d \n",pProt->CreditsConsumed)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsAvailable : %d \n",pProt->CreditsAvailable)); + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("==================================================\n")); +} + +static A_STATUS HCIUartMessagePending(void *pContext, A_UINT8 LookAheadBytes[], int ValidBytes) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext; + A_STATUS status = A_OK; + int totalRecvLength = 0; + HCI_TRANSPORT_PACKET_TYPE pktType = HCI_PACKET_INVALID; + A_BOOL recvRefillCalled = FALSE; + A_BOOL blockRecv = FALSE; + HTC_PACKET *pPacket = NULL; + + /** caller guarantees that this is a fully block-able context (synch I/O is allowed) */ + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCIUartMessagePending Lookahead Bytes:%d \n",ValidBytes)); + + LOCK_HCI_RX(pProt); + + do { + + if (ValidBytes < 3) { + /* not enough for ACL or event header */ + break; + } + + if ((LookAheadBytes[0] == HCI_UART_ACL_PKT) && (ValidBytes < 5)) { + /* not enough for ACL data header */ + break; + } + + switch (LookAheadBytes[0]) { + case HCI_UART_EVENT_PKT: + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n", + LookAheadBytes[1], LookAheadBytes[2])); + totalRecvLength = LookAheadBytes[2]; + totalRecvLength += 3; /* add type + event code + length field */ + pktType = HCI_EVENT_TYPE; + break; + case HCI_UART_ACL_PKT: + totalRecvLength = (LookAheadBytes[4] << 8) | LookAheadBytes[3]; + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI ACL: conn:0x%X length: %d \n", + ((LookAheadBytes[2] & 0xF0) << 8) | LookAheadBytes[1], totalRecvLength)); + totalRecvLength += 5; /* add type + connection handle + length field */ + pktType = HCI_ACL_TYPE; + break; + default: + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",LookAheadBytes[0])); + status = A_EPROTO; + break; + } + + if (A_FAILED(status)) { + break; + } + + if (pProt->HCIConfig.pHCIPktRecvAlloc != NULL) { + UNLOCK_HCI_RX(pProt); + /* user is using a per-packet allocation callback */ + pPacket = pProt->HCIConfig.pHCIPktRecvAlloc(pProt->HCIConfig.pContext, + pktType, + totalRecvLength); + LOCK_HCI_RX(pProt); + + } else { + HTC_PACKET_QUEUE *pQueue; + /* user is using a refill handler that can refill multiple HTC buffers */ + + /* select buffer queue */ + if (pktType == HCI_ACL_TYPE) { + pQueue = &pProt->HCIACLRecvBuffers; + } else { + pQueue = &pProt->HCIEventBuffers; + } + + if (HTC_QUEUE_EMPTY(pQueue)) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("** HCI pkt type: %d has no buffers available calling allocation handler \n", + pktType)); + /* check for refill handler */ + if (pProt->HCIConfig.pHCIPktRecvRefill != NULL) { + recvRefillCalled = TRUE; + UNLOCK_HCI_RX(pProt); + /* call the re-fill handler */ + pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext, + pktType, + 0); + LOCK_HCI_RX(pProt); + /* check if we have more buffers */ + pPacket = HTC_PACKET_DEQUEUE(pQueue); + /* fall through */ + } + } else { + pPacket = HTC_PACKET_DEQUEUE(pQueue); + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("HCI pkt type: %d now has %d recv buffers left \n", + pktType, HTC_PACKET_QUEUE_DEPTH(pQueue))); + } + } + + if (NULL == pPacket) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("** HCI pkt type: %d has no buffers available stopping recv...\n", pktType)); + /* this is not an error, we simply need to mark that we are waiting for buffers.*/ + pProt->RecvStateFlags |= HCI_RECV_WAIT_BUFFERS; + pProt->WaitBufferType = pktType; + blockRecv = TRUE; + break; + } + + if (totalRecvLength > (int)pPacket->BufferLength) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI-UART pkt: %d requires %d bytes (%d buffer bytes avail) ! \n", + LookAheadBytes[0], totalRecvLength, pPacket->BufferLength)); + status = A_EINVAL; + break; + } + + } while (FALSE); + + UNLOCK_HCI_RX(pProt); + + /* locks are released, we can go fetch the packet */ + + do { + + if (A_FAILED(status) || (NULL == pPacket)) { + break; + } + + /* do this synchronously, we don't need to be fast here */ + pPacket->Completion = NULL; + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI : getting recv packet len:%d hci-uart-type: %s \n", + totalRecvLength, (LookAheadBytes[0] == HCI_UART_EVENT_PKT) ? "EVENT" : "ACL")); + + status = DevGMboxRead(pProt->pDev, pPacket, totalRecvLength); + + if (A_FAILED(status)) { + break; + } + + if (pPacket->pBuffer[0] != LookAheadBytes[0]) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not contain expected packet type: %d ! \n", + pPacket->pBuffer[0])); + status = A_EPROTO; + break; + } + + if (pPacket->pBuffer[0] == HCI_UART_EVENT_PKT) { + /* validate event header fields */ + if ((pPacket->pBuffer[1] != LookAheadBytes[1]) || + (pPacket->pBuffer[2] != LookAheadBytes[2])) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n")); + DebugDumpBytes(LookAheadBytes, 3, "Expected HCI-UART Header"); + DebugDumpBytes(pPacket->pBuffer, 3, "** Bad HCI-UART Header"); + status = A_EPROTO; + break; + } + } else if (pPacket->pBuffer[0] == HCI_UART_ACL_PKT) { + /* validate acl header fields */ + if ((pPacket->pBuffer[1] != LookAheadBytes[1]) || + (pPacket->pBuffer[2] != LookAheadBytes[2]) || + (pPacket->pBuffer[3] != LookAheadBytes[3]) || + (pPacket->pBuffer[4] != LookAheadBytes[4])) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n")); + DebugDumpBytes(LookAheadBytes, 5, "Expected HCI-UART Header"); + DebugDumpBytes(pPacket->pBuffer, 5, "** Bad HCI-UART Header"); + status = A_EPROTO; + break; + } + } + + /* adjust buffer to move past packet ID */ + pPacket->pBuffer++; + pPacket->ActualLength = totalRecvLength - 1; + pPacket->Status = A_OK; + /* indicate packet */ + DO_HCI_RECV_INDICATION(pProt,pPacket); + pPacket = NULL; + + /* check if we need to refill recv buffers */ + if ((pProt->HCIConfig.pHCIPktRecvRefill != NULL) && !recvRefillCalled) { + HTC_PACKET_QUEUE *pQueue; + int watermark; + + if (pktType == HCI_ACL_TYPE) { + watermark = pProt->HCIConfig.ACLRecvBufferWaterMark; + pQueue = &pProt->HCIACLRecvBuffers; + } else { + watermark = pProt->HCIConfig.EventRecvBufferWaterMark; + pQueue = &pProt->HCIEventBuffers; + } + + if (HTC_PACKET_QUEUE_DEPTH(pQueue) < watermark) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV, + ("** HCI pkt type: %d watermark hit (%d) current:%d \n", + pktType, watermark, HTC_PACKET_QUEUE_DEPTH(pQueue))); + /* call the re-fill handler */ + pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext, + pktType, + HTC_PACKET_QUEUE_DEPTH(pQueue)); + } + } + + } while (FALSE); + + /* check if we need to disable the reciever */ + if (A_FAILED(status) || blockRecv) { + DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_DISABLE, PROC_IO_SYNC); + } + + /* see if we need to recycle the recv buffer */ + if (A_FAILED(status) && (pPacket != NULL)) { + HTC_PACKET_QUEUE queue; + + if (A_EPROTO == status) { + DebugDumpBytes(pPacket->pBuffer, totalRecvLength, "Bad HCI-UART Recv packet"); + } + /* recycle packet */ + HTC_PACKET_RESET_RX(pPacket); + INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket); + HCI_TransportAddReceivePkts(pProt,&queue); + NotifyTransportFailure(pProt,status); + } + + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCIUartMessagePending \n")); + + return status; +} + +static void HCISendPacketCompletion(void *Context, HTC_PACKET *pPacket) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)Context; + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion (pPacket:0x%lX) \n",(unsigned long)pPacket)); + + if (A_FAILED(pPacket->Status)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Send Packet (0x%lX) failed: %d , len:%d \n", + (unsigned long)pPacket, pPacket->Status, pPacket->ActualLength)); + } + + DO_HCI_SEND_INDICATION(pProt,pPacket); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion \n")); +} + +static A_STATUS SeekCreditsSynch(GMBOX_PROTO_HCI_UART *pProt) +{ + A_STATUS status = A_OK; + int credits; + int retry = 100; + + while (TRUE) { + credits = 0; + status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits); + if (A_FAILED(status)) { + break; + } + LOCK_HCI_TX(pProt); + pProt->CreditsAvailable += credits; + pProt->CreditsConsumed -= credits; + if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) { + pProt->CreditsCurrentSeek = 0; + UNLOCK_HCI_TX(pProt); + break; + } + UNLOCK_HCI_TX(pProt); + retry--; + if (0 == retry) { + status = A_EBUSY; + break; + } + A_MDELAY(20); + } + + return status; +} + +static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous) +{ + A_STATUS status = A_OK; + int transferLength; + int creditsRequired, remainder; + A_UINT8 hciUartType; + A_BOOL synchSendComplete = FALSE; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCITrySend (pPacket:0x%lX) %s \n",(unsigned long)pPacket, + Synchronous ? "SYNC" :"ASYNC")); + + LOCK_HCI_TX(pProt); + + /* increment write processing count on entry */ + pProt->SendProcessCount++; + + do { + + if (pProt->HCIStopped) { + status = A_ECANCELED; + break; + } + + if (pPacket != NULL) { + /* packet was supplied */ + if (Synchronous) { + /* in synchronous mode, the send queue can only hold 1 packet */ + if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) { + status = A_EBUSY; + A_ASSERT(FALSE); + break; + } + + if (pProt->SendProcessCount > 1) { + /* another thread or task is draining the TX queues */ + status = A_EBUSY; + A_ASSERT(FALSE); + break; + } + + HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket); + + } else { + /* see if adding this packet hits the max depth (asynchronous mode only) */ + if ((pProt->HCIConfig.MaxSendQueueDepth > 0) && + ((HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue) + 1) >= pProt->HCIConfig.MaxSendQueueDepth)) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("HCI Send queue is full, Depth:%d, Max:%d \n", + HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue), + pProt->HCIConfig.MaxSendQueueDepth)); + /* queue will be full, invoke any callbacks to determine what action to take */ + if (pProt->HCIConfig.pHCISendFull != NULL) { + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("HCI : Calling driver's send full callback.... \n")); + if (pProt->HCIConfig.pHCISendFull(pProt->HCIConfig.pContext, + pPacket) == HCI_SEND_FULL_DROP) { + /* drop it */ + status = A_NO_RESOURCE; + break; + } + } + } + + HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket); + } + + } + + if (pProt->SendStateFlags & HCI_SEND_WAIT_CREDITS) { + break; + } + + if (pProt->SendProcessCount > 1) { + /* another thread or task is draining the TX queues */ + break; + } + + /***** beyond this point only 1 thread may enter ******/ + + /* now drain the send queue for transmission as long as we have enough + * credits */ + while (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) { + + pPacket = HTC_PACKET_DEQUEUE(&pProt->SendQueue); + + switch (HCI_GET_PACKET_TYPE(pPacket)) { + case HCI_COMMAND_TYPE: + hciUartType = HCI_UART_COMMAND_PKT; + break; + case HCI_ACL_TYPE: + hciUartType = HCI_UART_ACL_PKT; + break; + default: + status = A_EINVAL; + A_ASSERT(FALSE); + break; + } + + if (A_FAILED(status)) { + break; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Got head packet:0x%lX , Type:%d Length: %d Remaining Queue Depth: %d\n", + (unsigned long)pPacket, HCI_GET_PACKET_TYPE(pPacket), pPacket->ActualLength, + HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue))); + + transferLength = 1; /* UART type header is 1 byte */ + transferLength += pPacket->ActualLength; + transferLength = DEV_CALC_SEND_PADDED_LEN(pProt->pDev, transferLength); + + /* figure out how many credits this message requires */ + creditsRequired = transferLength / pProt->CreditSize; + remainder = transferLength % pProt->CreditSize; + + if (remainder) { + creditsRequired++; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Creds Required:%d Got:%d\n", + creditsRequired, pProt->CreditsAvailable)); + + if (creditsRequired > pProt->CreditsAvailable) { + if (Synchronous) { + /* in synchronous mode we need to seek credits in synchronously */ + pProt->CreditsCurrentSeek = creditsRequired; + UNLOCK_HCI_TX(pProt); + status = SeekCreditsSynch(pProt); + LOCK_HCI_TX(pProt); + if (A_FAILED(status)) { + break; + } + /* fall through and continue processing this send op */ + } else { + /* not enough credits, queue back to the head */ + HTC_PACKET_ENQUEUE_TO_HEAD(&pProt->SendQueue,pPacket); + /* waiting for credits */ + pProt->SendStateFlags |= HCI_SEND_WAIT_CREDITS; + /* provide a hint to reduce attempts to re-send if credits are dribbling back + * this hint is the short fall of credits */ + pProt->CreditsCurrentSeek = creditsRequired; + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: packet:0x%lX placed back in queue. head packet needs: %d credits \n", + (unsigned long)pPacket, pProt->CreditsCurrentSeek)); + pPacket = NULL; + UNLOCK_HCI_TX(pProt); + + /* schedule a credit counter read, our CreditsAvailableCallback callback will be called + * with the result */ + DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_ASYNC, NULL); + + LOCK_HCI_TX(pProt); + break; + } + } + + /* caller guarantees some head room */ + pPacket->pBuffer--; + pPacket->pBuffer[0] = hciUartType; + + pProt->CreditsAvailable -= creditsRequired; + pProt->CreditsConsumed += creditsRequired; + A_ASSERT(pProt->CreditsConsumed <= pProt->CreditsMax); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state: consumed:%d available:%d max:%d\n", + pProt->CreditsConsumed, pProt->CreditsAvailable, pProt->CreditsMax)); + + UNLOCK_HCI_TX(pProt); + + /* write it out */ + if (Synchronous) { + pPacket->Completion = NULL; + pPacket->pContext = NULL; + } else { + pPacket->Completion = HCISendPacketCompletion; + pPacket->pContext = pProt; + } + + status = DevGMboxWrite(pProt->pDev,pPacket,transferLength); + if (Synchronous) { + synchSendComplete = TRUE; + } else { + pPacket = NULL; + } + + LOCK_HCI_TX(pProt); + + } + + } while (FALSE); + + pProt->SendProcessCount--; + A_ASSERT(pProt->SendProcessCount >= 0); + UNLOCK_HCI_TX(pProt); + + if (Synchronous) { + A_ASSERT(pPacket != NULL); + if (A_SUCCESS(status) && (!synchSendComplete)) { + status = A_EBUSY; + A_ASSERT(FALSE); + LOCK_HCI_TX(pProt); + if (pPacket->ListLink.pNext != NULL) { + /* remove from the queue */ + HTC_PACKET_REMOVE(&pProt->SendQueue,pPacket); + } + UNLOCK_HCI_TX(pProt); + } + } else { + if (A_FAILED(status) && (pPacket != NULL)) { + pPacket->Status = status; + DO_HCI_SEND_INDICATION(pProt,pPacket); + } + } + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HCITrySend: \n")); + return status; +} + +static void FlushSendQueue(GMBOX_PROTO_HCI_UART *pProt) +{ + HTC_PACKET *pPacket; + HTC_PACKET_QUEUE discardQueue; + + INIT_HTC_PACKET_QUEUE(&discardQueue); + + LOCK_HCI_TX(pProt); + + if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) { + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->SendQueue); + } + + UNLOCK_HCI_TX(pProt); + + /* discard packets */ + while (!HTC_QUEUE_EMPTY(&discardQueue)) { + pPacket = HTC_PACKET_DEQUEUE(&discardQueue); + pPacket->Status = A_ECANCELED; + DO_HCI_SEND_INDICATION(pProt,pPacket); + } + +} + +static void FlushRecvBuffers(GMBOX_PROTO_HCI_UART *pProt) +{ + HTC_PACKET_QUEUE discardQueue; + HTC_PACKET *pPacket; + + INIT_HTC_PACKET_QUEUE(&discardQueue); + + LOCK_HCI_RX(pProt); + /*transfer list items from ACL and event buffer queues to the discard queue */ + if (!HTC_QUEUE_EMPTY(&pProt->HCIACLRecvBuffers)) { + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIACLRecvBuffers); + } + if (!HTC_QUEUE_EMPTY(&pProt->HCIEventBuffers)) { + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIEventBuffers); + } + UNLOCK_HCI_RX(pProt); + + /* now empty the discard queue */ + while (!HTC_QUEUE_EMPTY(&discardQueue)) { + pPacket = HTC_PACKET_DEQUEUE(&discardQueue); + pPacket->Status = A_ECANCELED; + DO_HCI_RECV_INDICATION(pProt,pPacket); + } + +} + +/*** protocol module install entry point ***/ + +A_STATUS GMboxProtocolInstall(AR6K_DEVICE *pDev) +{ + A_STATUS status = A_OK; + GMBOX_PROTO_HCI_UART *pProtocol = NULL; + + do { + + pProtocol = A_MALLOC(sizeof(GMBOX_PROTO_HCI_UART)); + + if (NULL == pProtocol) { + status = A_NO_MEMORY; + break; + } + + A_MEMZERO(pProtocol, sizeof(*pProtocol)); + pProtocol->pDev = pDev; + INIT_HTC_PACKET_QUEUE(&pProtocol->SendQueue); + INIT_HTC_PACKET_QUEUE(&pProtocol->HCIACLRecvBuffers); + INIT_HTC_PACKET_QUEUE(&pProtocol->HCIEventBuffers); + A_MUTEX_INIT(&pProtocol->HCIRxLock); + A_MUTEX_INIT(&pProtocol->HCITxLock); + + } while (FALSE); + + if (A_SUCCESS(status)) { + LOCK_AR6K(pDev); + DEV_GMBOX_SET_PROTOCOL(pDev, + HCIUartMessagePending, + CreditsAvailableCallback, + FailureCallback, + StateDumpCallback, + pProtocol); + UNLOCK_AR6K(pDev); + } else { + if (pProtocol != NULL) { + HCIUartCleanup(pProtocol); + } + } + + return status; +} + +/*** protocol module uninstall entry point ***/ +void GMboxProtocolUninstall(AR6K_DEVICE *pDev) +{ + GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev); + + if (pProtocol != NULL) { + + /* notify anyone attached */ + if (pProtocol->HCIAttached) { + A_ASSERT(pProtocol->HCIConfig.TransportRemoved != NULL); + pProtocol->HCIConfig.TransportRemoved(pProtocol->HCIConfig.pContext); + pProtocol->HCIAttached = FALSE; + } + + HCIUartCleanup(pProtocol); + DEV_GMBOX_SET_PROTOCOL(pDev,NULL,NULL,NULL,NULL,NULL); + } + +} + +static A_STATUS NotifyTransportReady(GMBOX_PROTO_HCI_UART *pProt) +{ + HCI_TRANSPORT_PROPERTIES props; + A_STATUS status = A_OK; + + do { + + A_MEMZERO(&props,sizeof(props)); + + /* HCI UART only needs one extra byte at the head to indicate the packet TYPE */ + props.HeadRoom = 1; + props.TailRoom = 0; + props.IOBlockPad = pProt->pDev->BlockSize; + if (pProt->HCIAttached) { + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI: notifying attached client to transport... \n")); + A_ASSERT(pProt->HCIConfig.TransportReady != NULL); + status = pProt->HCIConfig.TransportReady(pProt, + &props, + pProt->HCIConfig.pContext); + } + + } while (FALSE); + + return status; +} + +/*********** HCI UART protocol implementation ************************************************/ + +HCI_TRANSPORT_HANDLE HCI_TransportAttach(void *HTCHandle, HCI_TRANSPORT_CONFIG_INFO *pInfo) +{ + GMBOX_PROTO_HCI_UART *pProtocol = NULL; + AR6K_DEVICE *pDev; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportAttach \n")); + + pDev = HTCGetAR6KDevice(HTCHandle); + + LOCK_AR6K(pDev); + + do { + + pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev); + + if (NULL == pProtocol) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not installed! \n")); + break; + } + + if (pProtocol->HCIAttached) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol already attached! \n")); + break; + } + + A_MEMCPY(&pProtocol->HCIConfig, pInfo, sizeof(HCI_TRANSPORT_CONFIG_INFO)); + + A_ASSERT(pProtocol->HCIConfig.pHCIPktRecv != NULL); + A_ASSERT(pProtocol->HCIConfig.pHCISendComplete != NULL); + + pProtocol->HCIAttached = TRUE; + + } while (FALSE); + + UNLOCK_AR6K(pDev); + + if (pProtocol != NULL) { + /* TODO ... should we use a worker? */ + NotifyTransportReady(pProtocol); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach (0x%lX) \n",(unsigned long)pProtocol)); + return (HCI_TRANSPORT_HANDLE)pProtocol; +} + +void HCI_TransportDetach(HCI_TRANSPORT_HANDLE HciTrans) +{ + GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)HciTrans; + AR6K_DEVICE *pDev = pProtocol->pDev; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportDetach \n")); + + LOCK_AR6K(pDev); + if (!pProtocol->HCIAttached) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not attached! \n")); + UNLOCK_AR6K(pDev); + return; + } + pProtocol->HCIAttached = FALSE; + UNLOCK_AR6K(pDev); + + HCI_TransportStop(HciTrans); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach \n")); +} + +A_STATUS HCI_TransportAddReceivePkts(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET_QUEUE *pQueue) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + A_STATUS status = A_OK; + A_BOOL unblockRecv = FALSE; + HTC_PACKET *pPacket; + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCI_TransportAddReceivePkt \n")); + + LOCK_HCI_RX(pProt); + + do { + + if (pProt->HCIStopped) { + status = A_ECANCELED; + break; + } + + pPacket = HTC_GET_PKT_AT_HEAD(pQueue); + + if (NULL == pPacket) { + status = A_EINVAL; + break; + } + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv packet added, type :%d, len:%d num:%d \n", + HCI_GET_PACKET_TYPE(pPacket), pPacket->BufferLength, HTC_PACKET_QUEUE_DEPTH(pQueue))); + + if (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) { + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIEventBuffers, pQueue); + } else if (HCI_GET_PACKET_TYPE(pPacket) == HCI_ACL_TYPE) { + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIACLRecvBuffers, pQueue); + } else { + status = A_EINVAL; + break; + } + + if (pProt->RecvStateFlags & HCI_RECV_WAIT_BUFFERS) { + if (pProt->WaitBufferType == HCI_GET_PACKET_TYPE(pPacket)) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv was blocked on packet type :%d, unblocking.. \n", + pProt->WaitBufferType)); + pProt->RecvStateFlags &= ~HCI_RECV_WAIT_BUFFERS; + pProt->WaitBufferType = HCI_PACKET_INVALID; + unblockRecv = TRUE; + } + } + + } while (FALSE); + + UNLOCK_HCI_RX(pProt); + + if (A_FAILED(status)) { + while (!HTC_QUEUE_EMPTY(pQueue)) { + pPacket = HTC_PACKET_DEQUEUE(pQueue); + pPacket->Status = A_ECANCELED; + DO_HCI_RECV_INDICATION(pProt,pPacket); + } + } + + if (unblockRecv) { + DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_ASYNC); + } + + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCI_TransportAddReceivePkt \n")); + + return A_OK; +} + +A_STATUS HCI_TransportSendPkt(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET *pPacket, A_BOOL Synchronous) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + + return HCITrySend(pProt,pPacket,Synchronous); +} + +void HCI_TransportStop(HCI_TRANSPORT_HANDLE HciTrans) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStop \n")); + + LOCK_AR6K(pProt->pDev); + if (pProt->HCIStopped) { + UNLOCK_AR6K(pProt->pDev); + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n")); + return; + } + pProt->HCIStopped = TRUE; + UNLOCK_AR6K(pProt->pDev); + + /* disable interrupts */ + DevGMboxIRQAction(pProt->pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC); + FlushSendQueue(pProt); + FlushRecvBuffers(pProt); + + /* signal bridge side to power down BT */ + DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_OFF, BTOFF_TIMEOUT_MS); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n")); +} + +A_STATUS HCI_TransportStart(HCI_TRANSPORT_HANDLE HciTrans) +{ + A_STATUS status; + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStart \n")); + + /* set stopped in case we have a problem in starting */ + pProt->HCIStopped = TRUE; + + do { + + status = InitTxCreditState(pProt); + + if (A_FAILED(status)) { + break; + } + + status = DevGMboxIRQAction(pProt->pDev, GMBOX_ERRORS_IRQ_ENABLE, PROC_IO_SYNC); + + if (A_FAILED(status)) { + break; + } + /* enable recv */ + status = DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_SYNC); + + if (A_FAILED(status)) { + break; + } + /* signal bridge side to power up BT */ + status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_ON, BTON_TIMEOUT_MS); + + if (A_FAILED(status)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI_TransportStart : Failed to trigger BT ON \n")); + break; + } + + /* we made it */ + pProt->HCIStopped = FALSE; + + } while (FALSE); + + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStart \n")); + + return status; +} + +A_STATUS HCI_TransportEnableDisableAsyncRecv(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + return DevGMboxIRQAction(pProt->pDev, + Enable ? GMBOX_RECV_IRQ_ENABLE : GMBOX_RECV_IRQ_DISABLE, + PROC_IO_SYNC); + +} + +A_STATUS HCI_TransportRecvHCIEventSync(HCI_TRANSPORT_HANDLE HciTrans, + HTC_PACKET *pPacket, + int MaxPollMS) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + A_STATUS status = A_OK; + A_UINT8 lookAhead[8]; + int bytes; + int totalRecvLength; + + MaxPollMS = MaxPollMS / 16; + + if (MaxPollMS < 2) { + MaxPollMS = 2; + } + + while (MaxPollMS) { + + bytes = sizeof(lookAhead); + status = DevGMboxRecvLookAheadPeek(pProt->pDev,lookAhead,&bytes); + if (A_FAILED(status)) { + break; + } + + if (bytes < 3) { + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI recv poll got bytes: %d, retry : %d \n", + bytes, MaxPollMS)); + A_MDELAY(16); + MaxPollMS--; + continue; + } + + totalRecvLength = 0; + switch (lookAhead[0]) { + case HCI_UART_EVENT_PKT: + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n", + lookAhead[1], lookAhead[2])); + totalRecvLength = lookAhead[2]; + totalRecvLength += 3; /* add type + event code + length field */ + break; + default: + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",lookAhead[0])); + status = A_EPROTO; + break; + } + + if (A_FAILED(status)) { + break; + } + + pPacket->Completion = NULL; + status = DevGMboxRead(pProt->pDev,pPacket,totalRecvLength); + if (A_FAILED(status)) { + break; + } + + pPacket->pBuffer++; + pPacket->ActualLength = totalRecvLength - 1; + pPacket->Status = A_OK; + break; + } + + if (MaxPollMS == 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI recv poll timeout! \n")); + status = A_ERROR; + } + + return status; +} + +#define LSB_SCRATCH_IDX 4 +#define MSB_SCRATCH_IDX 5 +A_STATUS HCI_TransportSetBaudRate(HCI_TRANSPORT_HANDLE HciTrans, A_UINT32 Baud) +{ + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + HIF_DEVICE *pHIFDevice = (HIF_DEVICE *)(pProt->pDev->HIFDevice); + A_UINT32 scaledBaud, scratchAddr; + A_STATUS status = A_OK; + + /* Divide the desired baud rate by 100 + * Store the LSB in the local scratch register 4 and the MSB in the local + * scratch register 5 for the target to read + */ + scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * LSB_SCRATCH_IDX); + scaledBaud = (Baud / 100) & LOCAL_SCRATCH_VALUE_MASK; + status = ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud); + scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * MSB_SCRATCH_IDX); + scaledBaud = ((Baud / 100) >> (LOCAL_SCRATCH_VALUE_MSB+1)) & LOCAL_SCRATCH_VALUE_MASK; + status |= ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud); + if (A_OK != status) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to set up baud rate in scratch register!")); + return status; + } + + /* Now interrupt the target to tell it about the baud rate */ + status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BAUD_SET, BAUD_TIMEOUT_MS); + if (A_OK != status) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to tell target to change baud rate!")); + } + + return status; +} + +A_STATUS HCI_TransportEnablePowerMgmt(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable) +{ + A_STATUS status; + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans; + + if (Enable) { + status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_PWR_SAV_ON, BTPWRSAV_TIMEOUT_MS); + } else { + status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_PWR_SAV_OFF, BTPWRSAV_TIMEOUT_MS); + } + + if (A_FAILED(status)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to enable/disable HCI power management!\n")); + } else { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI power management enabled/disabled!\n")); + } + + return status; +} + +#endif //ATH_AR6K_ENABLE_GMBOX + -- cgit v1.2.3