| Index: chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c
|
| diff --git a/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c b/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..17894f2a184e24eb06634270c089f2110d5330f0
|
| --- /dev/null
|
| +++ b/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox_hciuart.c
|
| @@ -0,0 +1,1217 @@
|
| +//------------------------------------------------------------------------------
|
| +// <copyright file="ar6k_prot_hciUart.c" company="Atheros">
|
| +// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
|
| +//
|
| +// This program is free software; you can redistribute it and/or modify
|
| +// it under the terms of the GNU General Public License version 2 as
|
| +// published by the Free Software Foundation;
|
| +//
|
| +// Software distributed under the License is distributed on an "AS
|
| +// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
| +// implied. See the License for the specific language governing
|
| +// rights and limitations under the License.
|
| +//
|
| +//
|
| +//------------------------------------------------------------------------------
|
| +//==============================================================================
|
| +// Protocol module for use in bridging HCI-UART packets over the GMBOX interface
|
| +//
|
| +// Author(s): ="Atheros"
|
| +//==============================================================================
|
| +#include "a_config.h"
|
| +#include "athdefs.h"
|
| +#include "a_types.h"
|
| +#include "a_osapi.h"
|
| +#include "../htc_debug.h"
|
| +#include "hif.h"
|
| +#include "htc_packet.h"
|
| +#include "ar6k.h"
|
| +#include "hci_transport_api.h"
|
| +#include "gmboxif.h"
|
| +
|
| +#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
|
| +
|
| +typedef struct {
|
| + HCI_TRANSPORT_CONFIG_INFO HCIConfig;
|
| + A_BOOL HCIAttached;
|
| + A_BOOL HCIStopped;
|
| + A_UINT32 RecvStateFlags;
|
| + A_UINT32 SendStateFlags;
|
| + HCI_TRANSPORT_PACKET_TYPE WaitBufferType;
|
| + HTC_PACKET_QUEUE SendQueue; /* write queue holding HCI Command and ACL packets */
|
| + HTC_PACKET_QUEUE HCIACLRecvBuffers; /* recv queue holding buffers for incomming ACL packets */
|
| + HTC_PACKET_QUEUE HCIEventBuffers; /* recv queue holding buffers for incomming event packets */
|
| + AR6K_DEVICE *pDev;
|
| + A_MUTEX_T HCIRxLock;
|
| + A_MUTEX_T HCITxLock;
|
| + int CreditsMax;
|
| + int CreditsConsumed;
|
| + int CreditsAvailable;
|
| + int CreditSize;
|
| + int CreditsCurrentSeek;
|
| + int SendProcessCount;
|
| +} GMBOX_PROTO_HCI_UART;
|
| +
|
| +#define LOCK_HCI_RX(t) A_MUTEX_LOCK(&(t)->HCIRxLock);
|
| +#define UNLOCK_HCI_RX(t) A_MUTEX_UNLOCK(&(t)->HCIRxLock);
|
| +#define LOCK_HCI_TX(t) A_MUTEX_LOCK(&(t)->HCITxLock);
|
| +#define UNLOCK_HCI_TX(t) A_MUTEX_UNLOCK(&(t)->HCITxLock);
|
| +
|
| +#define DO_HCI_RECV_INDICATION(p,pt) \
|
| +{ AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI: Indicate Recv on packet:0x%X status:%d len:%d type:%d \n", \
|
| + (A_UINT32)(pt),(pt)->Status, A_SUCCESS((pt)->Status) ? (pt)->ActualLength : 0, HCI_GET_PACKET_TYPE(pt))); \
|
| + (p)->HCIConfig.pHCIPktRecv((p)->HCIConfig.pContext, (pt)); \
|
| +}
|
| +
|
| +#define DO_HCI_SEND_INDICATION(p,pt) \
|
| +{ AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Indicate Send on packet:0x%X status:%d type:%d \n", \
|
| + (A_UINT32)(pt),(pt)->Status,HCI_GET_PACKET_TYPE(pt))); \
|
| + (p)->HCIConfig.pHCISendComplete((p)->HCIConfig.pContext, (pt)); \
|
| +}
|
| +
|
| +static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous);
|
| +
|
| +static void HCIUartCleanup(GMBOX_PROTO_HCI_UART *pProtocol)
|
| +{
|
| + A_ASSERT(pProtocol != NULL);
|
| +
|
| + A_MUTEX_DELETE(&pProtocol->HCIRxLock);
|
| + A_MUTEX_DELETE(&pProtocol->HCITxLock);
|
| +
|
| + A_FREE(pProtocol);
|
| +}
|
| +
|
| +static A_STATUS InitTxCreditState(GMBOX_PROTO_HCI_UART *pProt)
|
| +{
|
| + A_STATUS status;
|
| + int credits;
|
| + int creditPollCount = CREDIT_POLL_COUNT;
|
| + A_BOOL gotCredits = FALSE;
|
| +
|
| + pProt->CreditsConsumed = 0;
|
| +
|
| + do {
|
| +
|
| + if (pProt->CreditsMax != 0) {
|
| + /* we can only call this only once per target reset */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI: InitTxCreditState - already called! \n"));
|
| + A_ASSERT(FALSE);
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + /* read the credit counter. At startup the target will set the credit counter
|
| + * to the max available, we read this in a loop because it may take
|
| + * multiple credit counter reads to get all credits */
|
| +
|
| + while (creditPollCount) {
|
| +
|
| + credits = 0;
|
| +
|
| + status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + if (!gotCredits && (0 == credits)) {
|
| + creditPollCount--;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: credit is 0, retrying (%d) \n",creditPollCount));
|
| + A_MDELAY(HCI_DELAY_PER_INTERVAL_MS);
|
| + continue;
|
| + } else {
|
| + gotCredits = TRUE;
|
| + }
|
| +
|
| + if (0 == credits) {
|
| + break;
|
| + }
|
| +
|
| + pProt->CreditsMax += credits;
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + if (0 == creditPollCount) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("** HCI : Failed to get credits! GMBOX Target was not available \n"));
|
| + status = A_ERROR;
|
| + break;
|
| + }
|
| +
|
| + /* now get the size */
|
| + status = DevGMboxReadCreditSize(pProt->pDev, &pProt->CreditSize);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + if (A_SUCCESS(status)) {
|
| + pProt->CreditsAvailable = pProt->CreditsMax;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI : InitTxCreditState - credits avail: %d, size: %d \n",
|
| + pProt->CreditsAvailable, pProt->CreditSize));
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +static A_STATUS CreditsAvailableCallback(void *pContext, int Credits, A_BOOL CreditIRQEnabled)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
|
| + A_BOOL enableCreditIrq = FALSE;
|
| + A_BOOL disableCreditIrq = FALSE;
|
| + A_BOOL doPendingSends = FALSE;
|
| + A_STATUS status = A_OK;
|
| +
|
| + /** this callback is called under 2 conditions:
|
| + * 1. The credit IRQ interrupt was enabled and signaled.
|
| + * 2. A credit counter read completed.
|
| + *
|
| + * The function must not assume that the calling context can block !
|
| + */
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback (Credits:%d, IRQ:%s) \n",
|
| + Credits, CreditIRQEnabled ? "ON" : "OFF"));
|
| +
|
| + LOCK_HCI_RX(pProt);
|
| +
|
| + do {
|
| +
|
| + if (0 == Credits) {
|
| + if (!CreditIRQEnabled) {
|
| + /* enable credit IRQ */
|
| + enableCreditIrq = TRUE;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: current credit state, consumed:%d available:%d max:%d seek:%d\n",
|
| + pProt->CreditsConsumed,
|
| + pProt->CreditsAvailable,
|
| + pProt->CreditsMax,
|
| + pProt->CreditsCurrentSeek));
|
| +
|
| + pProt->CreditsAvailable += Credits;
|
| + A_ASSERT(pProt->CreditsAvailable <= pProt->CreditsMax);
|
| + pProt->CreditsConsumed -= Credits;
|
| + A_ASSERT(pProt->CreditsConsumed >= 0);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state, consumed:%d available:%d max:%d seek:%d\n",
|
| + pProt->CreditsConsumed,
|
| + pProt->CreditsAvailable,
|
| + pProt->CreditsMax,
|
| + pProt->CreditsCurrentSeek));
|
| +
|
| + if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
|
| + /* we have enough credits to fullfill at least 1 packet waiting in the queue */
|
| + pProt->CreditsCurrentSeek = 0;
|
| + pProt->SendStateFlags &= ~HCI_SEND_WAIT_CREDITS;
|
| + doPendingSends = TRUE;
|
| + if (CreditIRQEnabled) {
|
| + /* credit IRQ was enabled, we shouldn't need it anymore */
|
| + disableCreditIrq = TRUE;
|
| + }
|
| + } else {
|
| + /* not enough credits yet, enable credit IRQ if we haven't already */
|
| + if (!CreditIRQEnabled) {
|
| + enableCreditIrq = TRUE;
|
| + }
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + UNLOCK_HCI_RX(pProt);
|
| +
|
| + if (enableCreditIrq) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Enabling credit count IRQ...\n"));
|
| + /* must use async only */
|
| + status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_ENABLE, PROC_IO_ASYNC);
|
| + } else if (disableCreditIrq) {
|
| + /* must use async only */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Disabling credit count IRQ...\n"));
|
| + status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_DISABLE, PROC_IO_ASYNC);
|
| + }
|
| +
|
| + if (doPendingSends) {
|
| + HCITrySend(pProt, NULL, FALSE);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback \n"));
|
| + return status;
|
| +}
|
| +
|
| +static INLINE void NotifyTransportFailure(GMBOX_PROTO_HCI_UART *pProt, A_STATUS status)
|
| +{
|
| + if (pProt->HCIConfig.TransportFailure != NULL) {
|
| + pProt->HCIConfig.TransportFailure(pProt->HCIConfig.pContext, status);
|
| + }
|
| +}
|
| +
|
| +static void FailureCallback(void *pContext, A_STATUS Status)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
|
| +
|
| + /* target assertion occured */
|
| + NotifyTransportFailure(pProt, Status);
|
| +}
|
| +
|
| +static void StateDumpCallback(void *pContext)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("============ HCIUart State ======================\n"));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("RecvStateFlags : 0x%X \n",pProt->RecvStateFlags));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendStateFlags : 0x%X \n",pProt->SendStateFlags));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("WaitBufferType : %d \n",pProt->WaitBufferType));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendQueue Depth : %d \n",HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsMax : %d \n",pProt->CreditsMax));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsConsumed : %d \n",pProt->CreditsConsumed));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsAvailable : %d \n",pProt->CreditsAvailable));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("==================================================\n"));
|
| +}
|
| +
|
| +static A_STATUS HCIUartMessagePending(void *pContext, A_UINT8 LookAheadBytes[], int ValidBytes)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
|
| + A_STATUS status = A_OK;
|
| + int totalRecvLength = 0;
|
| + HCI_TRANSPORT_PACKET_TYPE pktType = HCI_PACKET_INVALID;
|
| + A_BOOL recvRefillCalled = FALSE;
|
| + A_BOOL blockRecv = FALSE;
|
| + HTC_PACKET *pPacket = NULL;
|
| +
|
| + /** caller guarantees that this is a fully block-able context (synch I/O is allowed) */
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCIUartMessagePending Lookahead Bytes:%d \n",ValidBytes));
|
| +
|
| + LOCK_HCI_RX(pProt);
|
| +
|
| + do {
|
| +
|
| + if (ValidBytes < 3) {
|
| + /* not enough for ACL or event header */
|
| + break;
|
| + }
|
| +
|
| + if ((LookAheadBytes[0] == HCI_UART_ACL_PKT) && (ValidBytes < 5)) {
|
| + /* not enough for ACL data header */
|
| + break;
|
| + }
|
| +
|
| + switch (LookAheadBytes[0]) {
|
| + case HCI_UART_EVENT_PKT:
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
|
| + LookAheadBytes[1], LookAheadBytes[2]));
|
| + totalRecvLength = LookAheadBytes[2];
|
| + totalRecvLength += 3; /* add type + event code + length field */
|
| + pktType = HCI_EVENT_TYPE;
|
| + break;
|
| + case HCI_UART_ACL_PKT:
|
| + totalRecvLength = (LookAheadBytes[4] << 8) | LookAheadBytes[3];
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI ACL: conn:0x%X length: %d \n",
|
| + ((LookAheadBytes[2] & 0xF0) << 8) | LookAheadBytes[1], totalRecvLength));
|
| + totalRecvLength += 5; /* add type + connection handle + length field */
|
| + pktType = HCI_ACL_TYPE;
|
| + break;
|
| + default:
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",LookAheadBytes[0]));
|
| + status = A_EPROTO;
|
| + break;
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + if (pProt->HCIConfig.pHCIPktRecvAlloc != NULL) {
|
| + UNLOCK_HCI_RX(pProt);
|
| + /* user is using a per-packet allocation callback */
|
| + pPacket = pProt->HCIConfig.pHCIPktRecvAlloc(pProt->HCIConfig.pContext,
|
| + pktType,
|
| + totalRecvLength);
|
| + LOCK_HCI_RX(pProt);
|
| +
|
| + } else {
|
| + HTC_PACKET_QUEUE *pQueue;
|
| + /* user is using a refill handler that can refill multiple HTC buffers */
|
| +
|
| + /* select buffer queue */
|
| + if (pktType == HCI_ACL_TYPE) {
|
| + pQueue = &pProt->HCIACLRecvBuffers;
|
| + } else {
|
| + pQueue = &pProt->HCIEventBuffers;
|
| + }
|
| +
|
| + if (HTC_QUEUE_EMPTY(pQueue)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
|
| + ("** HCI pkt type: %d has no buffers available calling allocation handler \n",
|
| + pktType));
|
| + /* check for refill handler */
|
| + if (pProt->HCIConfig.pHCIPktRecvRefill != NULL) {
|
| + recvRefillCalled = TRUE;
|
| + UNLOCK_HCI_RX(pProt);
|
| + /* call the re-fill handler */
|
| + pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
|
| + pktType,
|
| + 0);
|
| + LOCK_HCI_RX(pProt);
|
| + /* check if we have more buffers */
|
| + pPacket = HTC_PACKET_DEQUEUE(pQueue);
|
| + /* fall through */
|
| + }
|
| + } else {
|
| + pPacket = HTC_PACKET_DEQUEUE(pQueue);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
|
| + ("HCI pkt type: %d now has %d recv buffers left \n",
|
| + pktType, HTC_PACKET_QUEUE_DEPTH(pQueue)));
|
| + }
|
| + }
|
| +
|
| + if (NULL == pPacket) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
|
| + ("** HCI pkt type: %d has no buffers available stopping recv...\n", pktType));
|
| + /* this is not an error, we simply need to mark that we are waiting for buffers.*/
|
| + pProt->RecvStateFlags |= HCI_RECV_WAIT_BUFFERS;
|
| + pProt->WaitBufferType = pktType;
|
| + blockRecv = TRUE;
|
| + break;
|
| + }
|
| +
|
| + if (totalRecvLength > (int)pPacket->BufferLength) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI-UART pkt: %d requires %d bytes (%d buffer bytes avail) ! \n",
|
| + LookAheadBytes[0], totalRecvLength, pPacket->BufferLength));
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + UNLOCK_HCI_RX(pProt);
|
| +
|
| + /* locks are released, we can go fetch the packet */
|
| +
|
| + do {
|
| +
|
| + if (A_FAILED(status) || (NULL == pPacket)) {
|
| + break;
|
| + }
|
| +
|
| + /* do this synchronously, we don't need to be fast here */
|
| + pPacket->Completion = NULL;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI : getting recv packet len:%d hci-uart-type: %s \n",
|
| + totalRecvLength, (LookAheadBytes[0] == HCI_UART_EVENT_PKT) ? "EVENT" : "ACL"));
|
| +
|
| + status = DevGMboxRead(pProt->pDev, pPacket, totalRecvLength);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + if (pPacket->pBuffer[0] != LookAheadBytes[0]) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not contain expected packet type: %d ! \n",
|
| + pPacket->pBuffer[0]));
|
| + status = A_EPROTO;
|
| + break;
|
| + }
|
| +
|
| + if (pPacket->pBuffer[0] == HCI_UART_EVENT_PKT) {
|
| + /* validate event header fields */
|
| + if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
|
| + (pPacket->pBuffer[2] != LookAheadBytes[2])) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
|
| + DebugDumpBytes(LookAheadBytes, 3, "Expected HCI-UART Header");
|
| + DebugDumpBytes(pPacket->pBuffer, 3, "** Bad HCI-UART Header");
|
| + status = A_EPROTO;
|
| + break;
|
| + }
|
| + } else if (pPacket->pBuffer[0] == HCI_UART_ACL_PKT) {
|
| + /* validate acl header fields */
|
| + if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
|
| + (pPacket->pBuffer[2] != LookAheadBytes[2]) ||
|
| + (pPacket->pBuffer[3] != LookAheadBytes[3]) ||
|
| + (pPacket->pBuffer[4] != LookAheadBytes[4])) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
|
| + DebugDumpBytes(LookAheadBytes, 5, "Expected HCI-UART Header");
|
| + DebugDumpBytes(pPacket->pBuffer, 5, "** Bad HCI-UART Header");
|
| + status = A_EPROTO;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* adjust buffer to move past packet ID */
|
| + pPacket->pBuffer++;
|
| + pPacket->ActualLength = totalRecvLength - 1;
|
| + pPacket->Status = A_OK;
|
| + /* indicate packet */
|
| + DO_HCI_RECV_INDICATION(pProt,pPacket);
|
| + pPacket = NULL;
|
| +
|
| + /* check if we need to refill recv buffers */
|
| + if ((pProt->HCIConfig.pHCIPktRecvRefill != NULL) && !recvRefillCalled) {
|
| + HTC_PACKET_QUEUE *pQueue;
|
| + int watermark;
|
| +
|
| + if (pktType == HCI_ACL_TYPE) {
|
| + watermark = pProt->HCIConfig.ACLRecvBufferWaterMark;
|
| + pQueue = &pProt->HCIACLRecvBuffers;
|
| + } else {
|
| + watermark = pProt->HCIConfig.EventRecvBufferWaterMark;
|
| + pQueue = &pProt->HCIEventBuffers;
|
| + }
|
| +
|
| + if (HTC_PACKET_QUEUE_DEPTH(pQueue) < watermark) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
|
| + ("** HCI pkt type: %d watermark hit (%d) current:%d \n",
|
| + pktType, watermark, HTC_PACKET_QUEUE_DEPTH(pQueue)));
|
| + /* call the re-fill handler */
|
| + pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
|
| + pktType,
|
| + HTC_PACKET_QUEUE_DEPTH(pQueue));
|
| + }
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + /* check if we need to disable the reciever */
|
| + if (A_FAILED(status) || blockRecv) {
|
| + DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_DISABLE, PROC_IO_SYNC);
|
| + }
|
| +
|
| + /* see if we need to recycle the recv buffer */
|
| + if (A_FAILED(status) && (pPacket != NULL)) {
|
| + HTC_PACKET_QUEUE queue;
|
| +
|
| + if (A_EPROTO == status) {
|
| + DebugDumpBytes(pPacket->pBuffer, totalRecvLength, "Bad HCI-UART Recv packet");
|
| + }
|
| + /* recycle packet */
|
| + HTC_PACKET_RESET_RX(pPacket);
|
| + INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
|
| + HCI_TransportAddReceivePkts(pProt,&queue);
|
| + NotifyTransportFailure(pProt,status);
|
| + }
|
| +
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCIUartMessagePending \n"));
|
| +
|
| + return status;
|
| +}
|
| +
|
| +static void HCISendPacketCompletion(void *Context, HTC_PACKET *pPacket)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)Context;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion (pPacket:0x%X) \n",(A_UINT32)pPacket));
|
| +
|
| + if (A_FAILED(pPacket->Status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Send Packet (0x%X) failed: %d , len:%d \n",
|
| + (A_UINT32)pPacket, pPacket->Status, pPacket->ActualLength));
|
| + }
|
| +
|
| + DO_HCI_SEND_INDICATION(pProt,pPacket);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion \n"));
|
| +}
|
| +
|
| +static A_STATUS SeekCreditsSynch(GMBOX_PROTO_HCI_UART *pProt)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + int credits;
|
| + int retry = 100;
|
| +
|
| + while (TRUE) {
|
| + credits = 0;
|
| + status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| + LOCK_HCI_TX(pProt);
|
| + pProt->CreditsAvailable += credits;
|
| + if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
|
| + pProt->CreditsCurrentSeek = 0;
|
| + UNLOCK_HCI_TX(pProt);
|
| + break;
|
| + }
|
| + UNLOCK_HCI_TX(pProt);
|
| + retry--;
|
| + if (0 == retry) {
|
| + status = A_EBUSY;
|
| + break;
|
| + }
|
| + A_MDELAY(20);
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +static A_STATUS HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + int transferLength;
|
| + int creditsRequired, remainder;
|
| + A_UINT8 hciUartType;
|
| + A_BOOL synchSendComplete = FALSE;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCITrySend (pPacket:0x%X) %s \n",(A_UINT32)pPacket,
|
| + Synchronous ? "SYNC" :"ASYNC"));
|
| +
|
| + LOCK_HCI_TX(pProt);
|
| +
|
| + /* increment write processing count on entry */
|
| + pProt->SendProcessCount++;
|
| +
|
| + do {
|
| +
|
| + if (pProt->HCIStopped) {
|
| + status = A_ECANCELED;
|
| + break;
|
| + }
|
| +
|
| + if (pPacket != NULL) {
|
| + /* packet was supplied */
|
| + if (Synchronous) {
|
| + /* in synchronous mode, the send queue can only hold 1 packet */
|
| + if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
|
| + status = A_EBUSY;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + if (pProt->SendProcessCount > 1) {
|
| + /* another thread or task is draining the TX queues */
|
| + status = A_EBUSY;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
|
| +
|
| + } else {
|
| + /* see if adding this packet hits the max depth (asynchronous mode only) */
|
| + if ((pProt->HCIConfig.MaxSendQueueDepth > 0) &&
|
| + ((HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue) + 1) >= pProt->HCIConfig.MaxSendQueueDepth)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("HCI Send queue is full, Depth:%d, Max:%d \n",
|
| + HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue),
|
| + pProt->HCIConfig.MaxSendQueueDepth));
|
| + /* queue will be full, invoke any callbacks to determine what action to take */
|
| + if (pProt->HCIConfig.pHCISendFull != NULL) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + ("HCI : Calling driver's send full callback.... \n"));
|
| + if (pProt->HCIConfig.pHCISendFull(pProt->HCIConfig.pContext,
|
| + pPacket) == HCI_SEND_FULL_DROP) {
|
| + /* drop it */
|
| + status = A_NO_RESOURCE;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
|
| + }
|
| +
|
| + }
|
| +
|
| + if (pProt->SendStateFlags & HCI_SEND_WAIT_CREDITS) {
|
| + break;
|
| + }
|
| +
|
| + if (pProt->SendProcessCount > 1) {
|
| + /* another thread or task is draining the TX queues */
|
| + break;
|
| + }
|
| +
|
| + /***** beyond this point only 1 thread may enter ******/
|
| +
|
| + /* now drain the send queue for transmission as long as we have enough
|
| + * credits */
|
| + while (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
|
| +
|
| + pPacket = HTC_PACKET_DEQUEUE(&pProt->SendQueue);
|
| +
|
| + switch (HCI_GET_PACKET_TYPE(pPacket)) {
|
| + case HCI_COMMAND_TYPE:
|
| + hciUartType = HCI_UART_COMMAND_PKT;
|
| + break;
|
| + case HCI_ACL_TYPE:
|
| + hciUartType = HCI_UART_ACL_PKT;
|
| + break;
|
| + default:
|
| + status = A_EINVAL;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Got head packet:0x%X , Type:%d Length: %d Remaining Queue Depth: %d\n",
|
| + (A_UINT32)pPacket, HCI_GET_PACKET_TYPE(pPacket), pPacket->ActualLength,
|
| + HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
|
| +
|
| + transferLength = 1; /* UART type header is 1 byte */
|
| + transferLength += pPacket->ActualLength;
|
| + transferLength = DEV_CALC_SEND_PADDED_LEN(pProt->pDev, transferLength);
|
| +
|
| + /* figure out how many credits this message requires */
|
| + creditsRequired = transferLength / pProt->CreditSize;
|
| + remainder = transferLength % pProt->CreditSize;
|
| +
|
| + if (remainder) {
|
| + creditsRequired++;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Creds Required:%d Got:%d\n",
|
| + creditsRequired, pProt->CreditsAvailable));
|
| +
|
| + if (creditsRequired > pProt->CreditsAvailable) {
|
| + if (Synchronous) {
|
| + /* in synchronous mode we need to seek credits in synchronously */
|
| + pProt->CreditsCurrentSeek = creditsRequired;
|
| + UNLOCK_HCI_TX(pProt);
|
| + status = SeekCreditsSynch(pProt);
|
| + LOCK_HCI_TX(pProt);
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| + /* fall through and continue processing this send op */
|
| + } else {
|
| + /* not enough credits, queue back to the head */
|
| + HTC_PACKET_ENQUEUE_TO_HEAD(&pProt->SendQueue,pPacket);
|
| + /* waiting for credits */
|
| + pProt->SendStateFlags |= HCI_SEND_WAIT_CREDITS;
|
| + /* provide a hint to reduce attempts to re-send if credits are dribbling back
|
| + * this hint is the short fall of credits */
|
| + pProt->CreditsCurrentSeek = creditsRequired;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: packet:0x%X placed back in queue. head packet needs: %d credits \n",
|
| + (A_UINT32)pPacket, pProt->CreditsCurrentSeek));
|
| + pPacket = NULL;
|
| + UNLOCK_HCI_TX(pProt);
|
| +
|
| + /* schedule a credit counter read, our CreditsAvailableCallback callback will be called
|
| + * with the result */
|
| + DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_ASYNC, NULL);
|
| +
|
| + LOCK_HCI_TX(pProt);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* caller guarantees some head room */
|
| + pPacket->pBuffer--;
|
| + pPacket->pBuffer[0] = hciUartType;
|
| +
|
| + pProt->CreditsAvailable -= creditsRequired;
|
| + pProt->CreditsConsumed += creditsRequired;
|
| + A_ASSERT(pProt->CreditsConsumed <= pProt->CreditsMax);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state: consumed:%d available:%d max:%d\n",
|
| + pProt->CreditsConsumed, pProt->CreditsAvailable, pProt->CreditsMax));
|
| +
|
| + UNLOCK_HCI_TX(pProt);
|
| +
|
| + /* write it out */
|
| + if (Synchronous) {
|
| + pPacket->Completion = NULL;
|
| + pPacket->pContext = NULL;
|
| + } else {
|
| + pPacket->Completion = HCISendPacketCompletion;
|
| + pPacket->pContext = pProt;
|
| + }
|
| +
|
| + status = DevGMboxWrite(pProt->pDev,pPacket,transferLength);
|
| + if (Synchronous) {
|
| + synchSendComplete = TRUE;
|
| + } else {
|
| + pPacket = NULL;
|
| + }
|
| +
|
| + LOCK_HCI_TX(pProt);
|
| +
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + pProt->SendProcessCount--;
|
| + A_ASSERT(pProt->SendProcessCount >= 0);
|
| + UNLOCK_HCI_TX(pProt);
|
| +
|
| + if (Synchronous) {
|
| + A_ASSERT(pPacket != NULL);
|
| + if (A_SUCCESS(status) && (!synchSendComplete)) {
|
| + status = A_EBUSY;
|
| + A_ASSERT(FALSE);
|
| + LOCK_HCI_TX(pProt);
|
| + if (pPacket->ListLink.pNext != NULL) {
|
| + /* remove from the queue */
|
| + HTC_PACKET_REMOVE(&pProt->SendQueue,pPacket);
|
| + }
|
| + UNLOCK_HCI_TX(pProt);
|
| + }
|
| + } else {
|
| + if (A_FAILED(status) && (pPacket != NULL)) {
|
| + pPacket->Status = status;
|
| + DO_HCI_SEND_INDICATION(pProt,pPacket);
|
| + }
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HCITrySend: \n"));
|
| + return status;
|
| +}
|
| +
|
| +static void FlushSendQueue(GMBOX_PROTO_HCI_UART *pProt)
|
| +{
|
| + HTC_PACKET *pPacket;
|
| + HTC_PACKET_QUEUE discardQueue;
|
| +
|
| + INIT_HTC_PACKET_QUEUE(&discardQueue);
|
| +
|
| + LOCK_HCI_TX(pProt);
|
| +
|
| + if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->SendQueue);
|
| + }
|
| +
|
| + UNLOCK_HCI_TX(pProt);
|
| +
|
| + /* discard packets */
|
| + while (!HTC_QUEUE_EMPTY(&discardQueue)) {
|
| + pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
|
| + pPacket->Status = A_ECANCELED;
|
| + DO_HCI_SEND_INDICATION(pProt,pPacket);
|
| + }
|
| +
|
| +}
|
| +
|
| +static void FlushRecvBuffers(GMBOX_PROTO_HCI_UART *pProt)
|
| +{
|
| + HTC_PACKET_QUEUE discardQueue;
|
| + HTC_PACKET *pPacket;
|
| +
|
| + INIT_HTC_PACKET_QUEUE(&discardQueue);
|
| +
|
| + LOCK_HCI_RX(pProt);
|
| + /*transfer list items from ACL and event buffer queues to the discard queue */
|
| + if (!HTC_QUEUE_EMPTY(&pProt->HCIACLRecvBuffers)) {
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIACLRecvBuffers);
|
| + }
|
| + if (!HTC_QUEUE_EMPTY(&pProt->HCIEventBuffers)) {
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIEventBuffers);
|
| + }
|
| + UNLOCK_HCI_RX(pProt);
|
| +
|
| + /* now empty the discard queue */
|
| + while (!HTC_QUEUE_EMPTY(&discardQueue)) {
|
| + pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
|
| + pPacket->Status = A_ECANCELED;
|
| + DO_HCI_RECV_INDICATION(pProt,pPacket);
|
| + }
|
| +
|
| +}
|
| +
|
| +/*** protocol module install entry point ***/
|
| +
|
| +A_STATUS GMboxProtocolInstall(AR6K_DEVICE *pDev)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + GMBOX_PROTO_HCI_UART *pProtocol = NULL;
|
| +
|
| + do {
|
| +
|
| + pProtocol = A_MALLOC(sizeof(GMBOX_PROTO_HCI_UART));
|
| +
|
| + if (NULL == pProtocol) {
|
| + status = A_NO_MEMORY;
|
| + break;
|
| + }
|
| +
|
| + A_MEMZERO(pProtocol, sizeof(*pProtocol));
|
| + pProtocol->pDev = pDev;
|
| + INIT_HTC_PACKET_QUEUE(&pProtocol->SendQueue);
|
| + INIT_HTC_PACKET_QUEUE(&pProtocol->HCIACLRecvBuffers);
|
| + INIT_HTC_PACKET_QUEUE(&pProtocol->HCIEventBuffers);
|
| + A_MUTEX_INIT(&pProtocol->HCIRxLock);
|
| + A_MUTEX_INIT(&pProtocol->HCITxLock);
|
| +
|
| + } while (FALSE);
|
| +
|
| + if (A_SUCCESS(status)) {
|
| + LOCK_AR6K(pDev);
|
| + DEV_GMBOX_SET_PROTOCOL(pDev,
|
| + HCIUartMessagePending,
|
| + CreditsAvailableCallback,
|
| + FailureCallback,
|
| + StateDumpCallback,
|
| + pProtocol);
|
| + UNLOCK_AR6K(pDev);
|
| + } else {
|
| + if (pProtocol != NULL) {
|
| + HCIUartCleanup(pProtocol);
|
| + }
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +/*** protocol module uninstall entry point ***/
|
| +void GMboxProtocolUninstall(AR6K_DEVICE *pDev)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
|
| +
|
| + if (pProtocol != NULL) {
|
| +
|
| + /* notify anyone attached */
|
| + if (pProtocol->HCIAttached) {
|
| + A_ASSERT(pProtocol->HCIConfig.TransportRemoved != NULL);
|
| + pProtocol->HCIConfig.TransportRemoved(pProtocol->HCIConfig.pContext);
|
| + pProtocol->HCIAttached = FALSE;
|
| + }
|
| +
|
| + HCIUartCleanup(pProtocol);
|
| + DEV_GMBOX_SET_PROTOCOL(pDev,NULL,NULL,NULL,NULL,NULL);
|
| + }
|
| +
|
| +}
|
| +
|
| +static A_STATUS NotifyTransportReady(GMBOX_PROTO_HCI_UART *pProt)
|
| +{
|
| + HCI_TRANSPORT_PROPERTIES props;
|
| + A_STATUS status = A_OK;
|
| +
|
| + do {
|
| +
|
| + A_MEMZERO(&props,sizeof(props));
|
| +
|
| + /* HCI UART only needs one extra byte at the head to indicate the packet TYPE */
|
| + props.HeadRoom = 1;
|
| + props.TailRoom = 0;
|
| + props.IOBlockPad = pProt->pDev->BlockSize;
|
| + if (pProt->HCIAttached) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI: notifying attached client to transport... \n"));
|
| + A_ASSERT(pProt->HCIConfig.TransportReady != NULL);
|
| + status = pProt->HCIConfig.TransportReady(pProt,
|
| + &props,
|
| + pProt->HCIConfig.pContext);
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + return status;
|
| +}
|
| +
|
| +/*********** HCI UART protocol implementation ************************************************/
|
| +
|
| +HCI_TRANSPORT_HANDLE HCI_TransportAttach(void *HTCHandle, HCI_TRANSPORT_CONFIG_INFO *pInfo)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProtocol = NULL;
|
| + AR6K_DEVICE *pDev;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportAttach \n"));
|
| +
|
| + pDev = HTCGetAR6KDevice(HTCHandle);
|
| +
|
| + LOCK_AR6K(pDev);
|
| +
|
| + do {
|
| +
|
| + pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
|
| +
|
| + if (NULL == pProtocol) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not installed! \n"));
|
| + break;
|
| + }
|
| +
|
| + if (pProtocol->HCIAttached) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol already attached! \n"));
|
| + break;
|
| + }
|
| +
|
| + A_MEMCPY(&pProtocol->HCIConfig, pInfo, sizeof(HCI_TRANSPORT_CONFIG_INFO));
|
| +
|
| + A_ASSERT(pProtocol->HCIConfig.pHCIPktRecv != NULL);
|
| + A_ASSERT(pProtocol->HCIConfig.pHCISendComplete != NULL);
|
| +
|
| + pProtocol->HCIAttached = TRUE;
|
| +
|
| + } while (FALSE);
|
| +
|
| + UNLOCK_AR6K(pDev);
|
| +
|
| + if (pProtocol != NULL) {
|
| + /* TODO ... should we use a worker? */
|
| + NotifyTransportReady(pProtocol);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach (0x%X) \n",(A_UINT32)pProtocol));
|
| + return (HCI_TRANSPORT_HANDLE)pProtocol;
|
| +}
|
| +
|
| +void HCI_TransportDetach(HCI_TRANSPORT_HANDLE HciTrans)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| + AR6K_DEVICE *pDev = pProtocol->pDev;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportDetach \n"));
|
| +
|
| + LOCK_AR6K(pDev);
|
| + if (!pProtocol->HCIAttached) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not attached! \n"));
|
| + UNLOCK_AR6K(pDev);
|
| + return;
|
| + }
|
| + pProtocol->HCIAttached = FALSE;
|
| + UNLOCK_AR6K(pDev);
|
| +
|
| + HCI_TransportStop(HciTrans);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach \n"));
|
| +}
|
| +
|
| +A_STATUS HCI_TransportAddReceivePkts(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET_QUEUE *pQueue)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| + A_STATUS status = A_OK;
|
| + A_BOOL unblockRecv = FALSE;
|
| + HTC_PACKET *pPacket;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCI_TransportAddReceivePkt \n"));
|
| +
|
| + LOCK_HCI_RX(pProt);
|
| +
|
| + do {
|
| +
|
| + if (pProt->HCIStopped) {
|
| + status = A_ECANCELED;
|
| + break;
|
| + }
|
| +
|
| + pPacket = HTC_GET_PKT_AT_HEAD(pQueue);
|
| +
|
| + if (NULL == pPacket) {
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv packet added, type :%d, len:%d num:%d \n",
|
| + HCI_GET_PACKET_TYPE(pPacket), pPacket->BufferLength, HTC_PACKET_QUEUE_DEPTH(pQueue)));
|
| +
|
| + if (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) {
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIEventBuffers, pQueue);
|
| + } else if (HCI_GET_PACKET_TYPE(pPacket) == HCI_ACL_TYPE) {
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIACLRecvBuffers, pQueue);
|
| + } else {
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + if (pProt->RecvStateFlags & HCI_RECV_WAIT_BUFFERS) {
|
| + if (pProt->WaitBufferType == HCI_GET_PACKET_TYPE(pPacket)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv was blocked on packet type :%d, unblocking.. \n",
|
| + pProt->WaitBufferType));
|
| + pProt->RecvStateFlags &= ~HCI_RECV_WAIT_BUFFERS;
|
| + pProt->WaitBufferType = HCI_PACKET_INVALID;
|
| + unblockRecv = TRUE;
|
| + }
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + UNLOCK_HCI_RX(pProt);
|
| +
|
| + if (A_FAILED(status)) {
|
| + while (!HTC_QUEUE_EMPTY(pQueue)) {
|
| + pPacket = HTC_PACKET_DEQUEUE(pQueue);
|
| + pPacket->Status = A_ECANCELED;
|
| + DO_HCI_RECV_INDICATION(pProt,pPacket);
|
| + }
|
| + }
|
| +
|
| + if (unblockRecv) {
|
| + DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_ASYNC);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCI_TransportAddReceivePkt \n"));
|
| +
|
| + return A_OK;
|
| +}
|
| +
|
| +A_STATUS HCI_TransportSendPkt(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET *pPacket, A_BOOL Synchronous)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| +
|
| + return HCITrySend(pProt,pPacket,Synchronous);
|
| +}
|
| +
|
| +void HCI_TransportStop(HCI_TRANSPORT_HANDLE HciTrans)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStop \n"));
|
| +
|
| + LOCK_AR6K(pProt->pDev);
|
| + if (pProt->HCIStopped) {
|
| + UNLOCK_AR6K(pProt->pDev);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
|
| + return;
|
| + }
|
| + pProt->HCIStopped = TRUE;
|
| + UNLOCK_AR6K(pProt->pDev);
|
| +
|
| + /* disable interrupts */
|
| + DevGMboxIRQAction(pProt->pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
|
| + FlushSendQueue(pProt);
|
| + FlushRecvBuffers(pProt);
|
| +
|
| + /* signal bridge side to power down BT */
|
| + DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_OFF, BTOFF_TIMEOUT_MS);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
|
| +}
|
| +
|
| +A_STATUS HCI_TransportStart(HCI_TRANSPORT_HANDLE HciTrans)
|
| +{
|
| + A_STATUS status;
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStart \n"));
|
| +
|
| + /* set stopped in case we have a problem in starting */
|
| + pProt->HCIStopped = TRUE;
|
| +
|
| + do {
|
| +
|
| + status = InitTxCreditState(pProt);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + status = DevGMboxIRQAction(pProt->pDev, GMBOX_ERRORS_IRQ_ENABLE, PROC_IO_SYNC);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| + /* enable recv */
|
| + status = DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_SYNC);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| + /* signal bridge side to power up BT */
|
| + status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_ON, BTON_TIMEOUT_MS);
|
| +
|
| + if (A_FAILED(status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI_TransportStart : Failed to trigger BT ON \n"));
|
| + break;
|
| + }
|
| +
|
| + /* we made it */
|
| + pProt->HCIStopped = FALSE;
|
| +
|
| + } while (FALSE);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStart \n"));
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS HCI_TransportEnableDisableAsyncRecv(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| + return DevGMboxIRQAction(pProt->pDev,
|
| + Enable ? GMBOX_RECV_IRQ_ENABLE : GMBOX_RECV_IRQ_DISABLE,
|
| + PROC_IO_SYNC);
|
| +
|
| +}
|
| +
|
| +A_STATUS HCI_TransportRecvHCIEventSync(HCI_TRANSPORT_HANDLE HciTrans,
|
| + HTC_PACKET *pPacket,
|
| + int MaxPollMS)
|
| +{
|
| + GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
|
| + A_STATUS status = A_OK;
|
| + A_UINT8 lookAhead[8];
|
| + int bytes;
|
| + int totalRecvLength;
|
| +
|
| + MaxPollMS = MaxPollMS / 16;
|
| +
|
| + if (MaxPollMS < 2) {
|
| + MaxPollMS = 2;
|
| + }
|
| +
|
| + while (MaxPollMS) {
|
| +
|
| + bytes = sizeof(lookAhead);
|
| + status = DevGMboxRecvLookAheadPeek(pProt->pDev,lookAhead,&bytes);
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + if (bytes < 3) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI recv poll got bytes: %d, retry : %d \n",
|
| + bytes, MaxPollMS));
|
| + A_MDELAY(16);
|
| + MaxPollMS--;
|
| + continue;
|
| + }
|
| +
|
| + totalRecvLength = 0;
|
| + switch (lookAhead[0]) {
|
| + case HCI_UART_EVENT_PKT:
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
|
| + lookAhead[1], lookAhead[2]));
|
| + totalRecvLength = lookAhead[2];
|
| + totalRecvLength += 3; /* add type + event code + length field */
|
| + break;
|
| + default:
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",lookAhead[0]));
|
| + status = A_EPROTO;
|
| + break;
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + pPacket->Completion = NULL;
|
| + status = DevGMboxRead(pProt->pDev,pPacket,totalRecvLength);
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + pPacket->pBuffer++;
|
| + pPacket->ActualLength = totalRecvLength - 1;
|
| + pPacket->Status = A_OK;
|
| + break;
|
| + }
|
| +
|
| + if (MaxPollMS == 0) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI recv poll timeout! \n"));
|
| + status = A_ERROR;
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +#endif //ATH_AR6K_ENABLE_GMBOX
|
| +
|
|
|