1 //------------------------------------------------------------------------------
2 // <copyright file="ar6k_prot_hciUart.c" company="Atheros">
3 // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved.
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Protocol module for use in bridging HCI-UART packets over the GMBOX interface
23 // Author(s): ="Atheros"
24 //==============================================================================
29 #include "../htc_debug.h"
31 #include "htc_packet.h"
33 #include "hci_transport_api.h"
35 #include "ar6000_diag.h"
36 #include "hw/apb_map.h"
37 #include "hw/mbox_reg.h"
39 #ifdef ATH_AR6K_ENABLE_GMBOX
40 #define HCI_UART_COMMAND_PKT 0x01
41 #define HCI_UART_ACL_PKT 0x02
42 #define HCI_UART_SCO_PKT 0x03
43 #define HCI_UART_EVENT_PKT 0x04
45 #define HCI_RECV_WAIT_BUFFERS (1 << 0)
47 #define HCI_SEND_WAIT_CREDITS (1 << 0)
49 #define HCI_UART_BRIDGE_CREDIT_SIZE 128
51 #define CREDIT_POLL_COUNT 256
53 #define HCI_DELAY_PER_INTERVAL_MS 10
54 #define BTON_TIMEOUT_MS 500
55 #define BTOFF_TIMEOUT_MS 500
56 #define BAUD_TIMEOUT_MS 1
57 #define BTPWRSAV_TIMEOUT_MS 1
60 HCI_TRANSPORT_CONFIG_INFO HCIConfig;
63 A_UINT32 RecvStateFlags;
64 A_UINT32 SendStateFlags;
65 HCI_TRANSPORT_PACKET_TYPE WaitBufferType;
66 HTC_PACKET_QUEUE SendQueue; /* write queue holding HCI Command and ACL packets */
67 HTC_PACKET_QUEUE HCIACLRecvBuffers; /* recv queue holding buffers for incomming ACL packets */
68 HTC_PACKET_QUEUE HCIEventBuffers; /* recv queue holding buffers for incomming event packets */
76 int CreditsCurrentSeek;
78 } GMBOX_PROTO_HCI_UART;
80 #define LOCK_HCI_RX(t) A_MUTEX_LOCK(&(t)->HCIRxLock);
81 #define UNLOCK_HCI_RX(t) A_MUTEX_UNLOCK(&(t)->HCIRxLock);
82 #define LOCK_HCI_TX(t) A_MUTEX_LOCK(&(t)->HCITxLock);
83 #define UNLOCK_HCI_TX(t) A_MUTEX_UNLOCK(&(t)->HCITxLock);
85 #define DO_HCI_RECV_INDICATION(p, pt) \
87 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, \
88 ("HCI: Indicate Recv on packet:0x%lX status:%d len:%d type:%d \n", \
89 (unsigned long)(pt), \
91 !(pt)->Status ? (pt)->ActualLength : 0, \
92 HCI_GET_PACKET_TYPE(pt))); \
93 (p)->HCIConfig.pHCIPktRecv((p)->HCIConfig.pContext, (pt)); \
96 #define DO_HCI_SEND_INDICATION(p,pt) \
97 { AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Indicate Send on packet:0x%lX status:%d type:%d \n", \
98 (unsigned long)(pt),(pt)->Status,HCI_GET_PACKET_TYPE(pt))); \
99 (p)->HCIConfig.pHCISendComplete((p)->HCIConfig.pContext, (pt)); \
102 static int HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous);
104 static void HCIUartCleanup(GMBOX_PROTO_HCI_UART *pProtocol)
106 A_ASSERT(pProtocol != NULL);
108 A_MUTEX_DELETE(&pProtocol->HCIRxLock);
109 A_MUTEX_DELETE(&pProtocol->HCITxLock);
114 static int InitTxCreditState(GMBOX_PROTO_HCI_UART *pProt)
118 int creditPollCount = CREDIT_POLL_COUNT;
119 A_BOOL gotCredits = FALSE;
121 pProt->CreditsConsumed = 0;
125 if (pProt->CreditsMax != 0) {
126 /* we can only call this only once per target reset */
127 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI: InitTxCreditState - already called! \n"));
133 /* read the credit counter. At startup the target will set the credit counter
134 * to the max available, we read this in a loop because it may take
135 * multiple credit counter reads to get all credits */
137 while (creditPollCount) {
141 status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
147 if (!gotCredits && (0 == credits)) {
149 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: credit is 0, retrying (%d) \n",creditPollCount));
150 A_MDELAY(HCI_DELAY_PER_INTERVAL_MS);
160 pProt->CreditsMax += credits;
167 if (0 == creditPollCount) {
168 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
169 ("** HCI : Failed to get credits! GMBOX Target was not available \n"));
174 /* now get the size */
175 status = DevGMboxReadCreditSize(pProt->pDev, &pProt->CreditSize);
184 pProt->CreditsAvailable = pProt->CreditsMax;
185 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI : InitTxCreditState - credits avail: %d, size: %d \n",
186 pProt->CreditsAvailable, pProt->CreditSize));
192 static int CreditsAvailableCallback(void *pContext, int Credits, A_BOOL CreditIRQEnabled)
194 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
195 A_BOOL enableCreditIrq = FALSE;
196 A_BOOL disableCreditIrq = FALSE;
197 A_BOOL doPendingSends = FALSE;
200 /** this callback is called under 2 conditions:
201 * 1. The credit IRQ interrupt was enabled and signaled.
202 * 2. A credit counter read completed.
204 * The function must not assume that the calling context can block !
207 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback (Credits:%d, IRQ:%s) \n",
208 Credits, CreditIRQEnabled ? "ON" : "OFF"));
215 if (!CreditIRQEnabled) {
216 /* enable credit IRQ */
217 enableCreditIrq = TRUE;
222 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: current credit state, consumed:%d available:%d max:%d seek:%d\n",
223 pProt->CreditsConsumed,
224 pProt->CreditsAvailable,
226 pProt->CreditsCurrentSeek));
228 pProt->CreditsAvailable += Credits;
229 A_ASSERT(pProt->CreditsAvailable <= pProt->CreditsMax);
230 pProt->CreditsConsumed -= Credits;
231 A_ASSERT(pProt->CreditsConsumed >= 0);
233 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state, consumed:%d available:%d max:%d seek:%d\n",
234 pProt->CreditsConsumed,
235 pProt->CreditsAvailable,
237 pProt->CreditsCurrentSeek));
239 if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
240 /* we have enough credits to fullfill at least 1 packet waiting in the queue */
241 pProt->CreditsCurrentSeek = 0;
242 pProt->SendStateFlags &= ~HCI_SEND_WAIT_CREDITS;
243 doPendingSends = TRUE;
244 if (CreditIRQEnabled) {
245 /* credit IRQ was enabled, we shouldn't need it anymore */
246 disableCreditIrq = TRUE;
249 /* not enough credits yet, enable credit IRQ if we haven't already */
250 if (!CreditIRQEnabled) {
251 enableCreditIrq = TRUE;
257 UNLOCK_HCI_TX(pProt);
259 if (enableCreditIrq) {
260 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Enabling credit count IRQ...\n"));
261 /* must use async only */
262 status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_ENABLE, PROC_IO_ASYNC);
263 } else if (disableCreditIrq) {
264 /* must use async only */
265 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Disabling credit count IRQ...\n"));
266 status = DevGMboxIRQAction(pProt->pDev, GMBOX_CREDIT_IRQ_DISABLE, PROC_IO_ASYNC);
269 if (doPendingSends) {
270 HCITrySend(pProt, NULL, FALSE);
273 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+CreditsAvailableCallback \n"));
277 static INLINE void NotifyTransportFailure(GMBOX_PROTO_HCI_UART *pProt, int status)
279 if (pProt->HCIConfig.TransportFailure != NULL) {
280 pProt->HCIConfig.TransportFailure(pProt->HCIConfig.pContext, status);
284 static void FailureCallback(void *pContext, int Status)
286 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
288 /* target assertion occured */
289 NotifyTransportFailure(pProt, Status);
292 static void StateDumpCallback(void *pContext)
294 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
296 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("============ HCIUart State ======================\n"));
297 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("RecvStateFlags : 0x%X \n",pProt->RecvStateFlags));
298 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendStateFlags : 0x%X \n",pProt->SendStateFlags));
299 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("WaitBufferType : %d \n",pProt->WaitBufferType));
300 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("SendQueue Depth : %d \n",HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
301 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsMax : %d \n",pProt->CreditsMax));
302 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsConsumed : %d \n",pProt->CreditsConsumed));
303 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("CreditsAvailable : %d \n",pProt->CreditsAvailable));
304 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("==================================================\n"));
307 static int HCIUartMessagePending(void *pContext, A_UINT8 LookAheadBytes[], int ValidBytes)
309 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)pContext;
311 int totalRecvLength = 0;
312 HCI_TRANSPORT_PACKET_TYPE pktType = HCI_PACKET_INVALID;
313 A_BOOL recvRefillCalled = FALSE;
314 A_BOOL blockRecv = FALSE;
315 HTC_PACKET *pPacket = NULL;
317 /** caller guarantees that this is a fully block-able context (synch I/O is allowed) */
319 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCIUartMessagePending Lookahead Bytes:%d \n",ValidBytes));
325 if (ValidBytes < 3) {
326 /* not enough for ACL or event header */
330 if ((LookAheadBytes[0] == HCI_UART_ACL_PKT) && (ValidBytes < 5)) {
331 /* not enough for ACL data header */
335 switch (LookAheadBytes[0]) {
336 case HCI_UART_EVENT_PKT:
337 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
338 LookAheadBytes[1], LookAheadBytes[2]));
339 totalRecvLength = LookAheadBytes[2];
340 totalRecvLength += 3; /* add type + event code + length field */
341 pktType = HCI_EVENT_TYPE;
343 case HCI_UART_ACL_PKT:
344 totalRecvLength = (LookAheadBytes[4] << 8) | LookAheadBytes[3];
345 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI ACL: conn:0x%X length: %d \n",
346 ((LookAheadBytes[2] & 0xF0) << 8) | LookAheadBytes[1], totalRecvLength));
347 totalRecvLength += 5; /* add type + connection handle + length field */
348 pktType = HCI_ACL_TYPE;
351 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",LookAheadBytes[0]));
360 if (pProt->HCIConfig.pHCIPktRecvAlloc != NULL) {
361 UNLOCK_HCI_RX(pProt);
362 /* user is using a per-packet allocation callback */
363 pPacket = pProt->HCIConfig.pHCIPktRecvAlloc(pProt->HCIConfig.pContext,
369 HTC_PACKET_QUEUE *pQueue;
370 /* user is using a refill handler that can refill multiple HTC buffers */
372 /* select buffer queue */
373 if (pktType == HCI_ACL_TYPE) {
374 pQueue = &pProt->HCIACLRecvBuffers;
376 pQueue = &pProt->HCIEventBuffers;
379 if (HTC_QUEUE_EMPTY(pQueue)) {
380 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
381 ("** HCI pkt type: %d has no buffers available calling allocation handler \n",
383 /* check for refill handler */
384 if (pProt->HCIConfig.pHCIPktRecvRefill != NULL) {
385 recvRefillCalled = TRUE;
386 UNLOCK_HCI_RX(pProt);
387 /* call the re-fill handler */
388 pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
392 /* check if we have more buffers */
393 pPacket = HTC_PACKET_DEQUEUE(pQueue);
397 pPacket = HTC_PACKET_DEQUEUE(pQueue);
398 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
399 ("HCI pkt type: %d now has %d recv buffers left \n",
400 pktType, HTC_PACKET_QUEUE_DEPTH(pQueue)));
404 if (NULL == pPacket) {
405 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
406 ("** HCI pkt type: %d has no buffers available stopping recv...\n", pktType));
407 /* this is not an error, we simply need to mark that we are waiting for buffers.*/
408 pProt->RecvStateFlags |= HCI_RECV_WAIT_BUFFERS;
409 pProt->WaitBufferType = pktType;
414 if (totalRecvLength > (int)pPacket->BufferLength) {
415 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI-UART pkt: %d requires %d bytes (%d buffer bytes avail) ! \n",
416 LookAheadBytes[0], totalRecvLength, pPacket->BufferLength));
423 UNLOCK_HCI_RX(pProt);
425 /* locks are released, we can go fetch the packet */
429 if (status || (NULL == pPacket)) {
433 /* do this synchronously, we don't need to be fast here */
434 pPacket->Completion = NULL;
436 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI : getting recv packet len:%d hci-uart-type: %s \n",
437 totalRecvLength, (LookAheadBytes[0] == HCI_UART_EVENT_PKT) ? "EVENT" : "ACL"));
439 status = DevGMboxRead(pProt->pDev, pPacket, totalRecvLength);
445 if (pPacket->pBuffer[0] != LookAheadBytes[0]) {
446 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not contain expected packet type: %d ! \n",
447 pPacket->pBuffer[0]));
452 if (pPacket->pBuffer[0] == HCI_UART_EVENT_PKT) {
453 /* validate event header fields */
454 if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
455 (pPacket->pBuffer[2] != LookAheadBytes[2])) {
456 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
457 DebugDumpBytes(LookAheadBytes, 3, "Expected HCI-UART Header");
458 DebugDumpBytes(pPacket->pBuffer, 3, "** Bad HCI-UART Header");
462 } else if (pPacket->pBuffer[0] == HCI_UART_ACL_PKT) {
463 /* validate acl header fields */
464 if ((pPacket->pBuffer[1] != LookAheadBytes[1]) ||
465 (pPacket->pBuffer[2] != LookAheadBytes[2]) ||
466 (pPacket->pBuffer[3] != LookAheadBytes[3]) ||
467 (pPacket->pBuffer[4] != LookAheadBytes[4])) {
468 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** HCI buffer does not match lookahead! \n"));
469 DebugDumpBytes(LookAheadBytes, 5, "Expected HCI-UART Header");
470 DebugDumpBytes(pPacket->pBuffer, 5, "** Bad HCI-UART Header");
476 /* adjust buffer to move past packet ID */
478 pPacket->ActualLength = totalRecvLength - 1;
479 pPacket->Status = A_OK;
480 /* indicate packet */
481 DO_HCI_RECV_INDICATION(pProt,pPacket);
484 /* check if we need to refill recv buffers */
485 if ((pProt->HCIConfig.pHCIPktRecvRefill != NULL) && !recvRefillCalled) {
486 HTC_PACKET_QUEUE *pQueue;
489 if (pktType == HCI_ACL_TYPE) {
490 watermark = pProt->HCIConfig.ACLRecvBufferWaterMark;
491 pQueue = &pProt->HCIACLRecvBuffers;
493 watermark = pProt->HCIConfig.EventRecvBufferWaterMark;
494 pQueue = &pProt->HCIEventBuffers;
497 if (HTC_PACKET_QUEUE_DEPTH(pQueue) < watermark) {
498 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
499 ("** HCI pkt type: %d watermark hit (%d) current:%d \n",
500 pktType, watermark, HTC_PACKET_QUEUE_DEPTH(pQueue)));
501 /* call the re-fill handler */
502 pProt->HCIConfig.pHCIPktRecvRefill(pProt->HCIConfig.pContext,
504 HTC_PACKET_QUEUE_DEPTH(pQueue));
510 /* check if we need to disable the reciever */
511 if (status || blockRecv) {
512 DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_DISABLE, PROC_IO_SYNC);
515 /* see if we need to recycle the recv buffer */
516 if (status && (pPacket != NULL)) {
517 HTC_PACKET_QUEUE queue;
519 if (A_EPROTO == status) {
520 DebugDumpBytes(pPacket->pBuffer, totalRecvLength, "Bad HCI-UART Recv packet");
523 HTC_PACKET_RESET_RX(pPacket);
524 INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
525 HCI_TransportAddReceivePkts(pProt,&queue);
526 NotifyTransportFailure(pProt,status);
530 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCIUartMessagePending \n"));
535 static void HCISendPacketCompletion(void *Context, HTC_PACKET *pPacket)
537 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)Context;
538 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion (pPacket:0x%lX) \n",(unsigned long)pPacket));
540 if (pPacket->Status) {
541 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Send Packet (0x%lX) failed: %d , len:%d \n",
542 (unsigned long)pPacket, pPacket->Status, pPacket->ActualLength));
545 DO_HCI_SEND_INDICATION(pProt,pPacket);
547 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCISendPacketCompletion \n"));
550 static int SeekCreditsSynch(GMBOX_PROTO_HCI_UART *pProt)
558 status = DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_SYNC, &credits);
563 pProt->CreditsAvailable += credits;
564 pProt->CreditsConsumed -= credits;
565 if (pProt->CreditsAvailable >= pProt->CreditsCurrentSeek) {
566 pProt->CreditsCurrentSeek = 0;
567 UNLOCK_HCI_TX(pProt);
570 UNLOCK_HCI_TX(pProt);
582 static int HCITrySend(GMBOX_PROTO_HCI_UART *pProt, HTC_PACKET *pPacket, A_BOOL Synchronous)
586 int creditsRequired, remainder;
588 A_BOOL synchSendComplete = FALSE;
590 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HCITrySend (pPacket:0x%lX) %s \n",(unsigned long)pPacket,
591 Synchronous ? "SYNC" :"ASYNC"));
595 /* increment write processing count on entry */
596 pProt->SendProcessCount++;
600 if (pProt->HCIStopped) {
601 status = A_ECANCELED;
605 if (pPacket != NULL) {
606 /* packet was supplied */
608 /* in synchronous mode, the send queue can only hold 1 packet */
609 if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
615 if (pProt->SendProcessCount > 1) {
616 /* another thread or task is draining the TX queues */
622 HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
625 /* see if adding this packet hits the max depth (asynchronous mode only) */
626 if ((pProt->HCIConfig.MaxSendQueueDepth > 0) &&
627 ((HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue) + 1) >= pProt->HCIConfig.MaxSendQueueDepth)) {
628 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("HCI Send queue is full, Depth:%d, Max:%d \n",
629 HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue),
630 pProt->HCIConfig.MaxSendQueueDepth));
631 /* queue will be full, invoke any callbacks to determine what action to take */
632 if (pProt->HCIConfig.pHCISendFull != NULL) {
633 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
634 ("HCI : Calling driver's send full callback.... \n"));
635 if (pProt->HCIConfig.pHCISendFull(pProt->HCIConfig.pContext,
636 pPacket) == HCI_SEND_FULL_DROP) {
638 status = A_NO_RESOURCE;
644 HTC_PACKET_ENQUEUE(&pProt->SendQueue,pPacket);
649 if (pProt->SendStateFlags & HCI_SEND_WAIT_CREDITS) {
653 if (pProt->SendProcessCount > 1) {
654 /* another thread or task is draining the TX queues */
658 /***** beyond this point only 1 thread may enter ******/
660 /* now drain the send queue for transmission as long as we have enough
662 while (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
664 pPacket = HTC_PACKET_DEQUEUE(&pProt->SendQueue);
666 switch (HCI_GET_PACKET_TYPE(pPacket)) {
667 case HCI_COMMAND_TYPE:
668 hciUartType = HCI_UART_COMMAND_PKT;
671 hciUartType = HCI_UART_ACL_PKT;
683 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Got head packet:0x%lX , Type:%d Length: %d Remaining Queue Depth: %d\n",
684 (unsigned long)pPacket, HCI_GET_PACKET_TYPE(pPacket), pPacket->ActualLength,
685 HTC_PACKET_QUEUE_DEPTH(&pProt->SendQueue)));
687 transferLength = 1; /* UART type header is 1 byte */
688 transferLength += pPacket->ActualLength;
689 transferLength = DEV_CALC_SEND_PADDED_LEN(pProt->pDev, transferLength);
691 /* figure out how many credits this message requires */
692 creditsRequired = transferLength / pProt->CreditSize;
693 remainder = transferLength % pProt->CreditSize;
699 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: Creds Required:%d Got:%d\n",
700 creditsRequired, pProt->CreditsAvailable));
702 if (creditsRequired > pProt->CreditsAvailable) {
704 /* in synchronous mode we need to seek credits in synchronously */
705 pProt->CreditsCurrentSeek = creditsRequired;
706 UNLOCK_HCI_TX(pProt);
707 status = SeekCreditsSynch(pProt);
712 /* fall through and continue processing this send op */
714 /* not enough credits, queue back to the head */
715 HTC_PACKET_ENQUEUE_TO_HEAD(&pProt->SendQueue,pPacket);
716 /* waiting for credits */
717 pProt->SendStateFlags |= HCI_SEND_WAIT_CREDITS;
718 /* provide a hint to reduce attempts to re-send if credits are dribbling back
719 * this hint is the short fall of credits */
720 pProt->CreditsCurrentSeek = creditsRequired;
721 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: packet:0x%lX placed back in queue. head packet needs: %d credits \n",
722 (unsigned long)pPacket, pProt->CreditsCurrentSeek));
724 UNLOCK_HCI_TX(pProt);
726 /* schedule a credit counter read, our CreditsAvailableCallback callback will be called
728 DevGMboxReadCreditCounter(pProt->pDev, PROC_IO_ASYNC, NULL);
735 /* caller guarantees some head room */
737 pPacket->pBuffer[0] = hciUartType;
739 pProt->CreditsAvailable -= creditsRequired;
740 pProt->CreditsConsumed += creditsRequired;
741 A_ASSERT(pProt->CreditsConsumed <= pProt->CreditsMax);
743 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HCI: new credit state: consumed:%d available:%d max:%d\n",
744 pProt->CreditsConsumed, pProt->CreditsAvailable, pProt->CreditsMax));
746 UNLOCK_HCI_TX(pProt);
750 pPacket->Completion = NULL;
751 pPacket->pContext = NULL;
753 pPacket->Completion = HCISendPacketCompletion;
754 pPacket->pContext = pProt;
757 status = DevGMboxWrite(pProt->pDev,pPacket,transferLength);
759 synchSendComplete = TRUE;
770 pProt->SendProcessCount--;
771 A_ASSERT(pProt->SendProcessCount >= 0);
772 UNLOCK_HCI_TX(pProt);
775 A_ASSERT(pPacket != NULL);
776 if (!status && (!synchSendComplete)) {
780 if (pPacket->ListLink.pNext != NULL) {
781 /* remove from the queue */
782 HTC_PACKET_REMOVE(&pProt->SendQueue,pPacket);
784 UNLOCK_HCI_TX(pProt);
787 if (status && (pPacket != NULL)) {
788 pPacket->Status = status;
789 DO_HCI_SEND_INDICATION(pProt,pPacket);
793 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HCITrySend: \n"));
797 static void FlushSendQueue(GMBOX_PROTO_HCI_UART *pProt)
800 HTC_PACKET_QUEUE discardQueue;
802 INIT_HTC_PACKET_QUEUE(&discardQueue);
806 if (!HTC_QUEUE_EMPTY(&pProt->SendQueue)) {
807 HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->SendQueue);
810 UNLOCK_HCI_TX(pProt);
812 /* discard packets */
813 while (!HTC_QUEUE_EMPTY(&discardQueue)) {
814 pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
815 pPacket->Status = A_ECANCELED;
816 DO_HCI_SEND_INDICATION(pProt,pPacket);
821 static void FlushRecvBuffers(GMBOX_PROTO_HCI_UART *pProt)
823 HTC_PACKET_QUEUE discardQueue;
826 INIT_HTC_PACKET_QUEUE(&discardQueue);
829 /*transfer list items from ACL and event buffer queues to the discard queue */
830 if (!HTC_QUEUE_EMPTY(&pProt->HCIACLRecvBuffers)) {
831 HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIACLRecvBuffers);
833 if (!HTC_QUEUE_EMPTY(&pProt->HCIEventBuffers)) {
834 HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&discardQueue,&pProt->HCIEventBuffers);
836 UNLOCK_HCI_RX(pProt);
838 /* now empty the discard queue */
839 while (!HTC_QUEUE_EMPTY(&discardQueue)) {
840 pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
841 pPacket->Status = A_ECANCELED;
842 DO_HCI_RECV_INDICATION(pProt,pPacket);
847 /*** protocol module install entry point ***/
849 int GMboxProtocolInstall(AR6K_DEVICE *pDev)
852 GMBOX_PROTO_HCI_UART *pProtocol = NULL;
856 pProtocol = A_MALLOC(sizeof(GMBOX_PROTO_HCI_UART));
858 if (NULL == pProtocol) {
859 status = A_NO_MEMORY;
863 A_MEMZERO(pProtocol, sizeof(*pProtocol));
864 pProtocol->pDev = pDev;
865 INIT_HTC_PACKET_QUEUE(&pProtocol->SendQueue);
866 INIT_HTC_PACKET_QUEUE(&pProtocol->HCIACLRecvBuffers);
867 INIT_HTC_PACKET_QUEUE(&pProtocol->HCIEventBuffers);
868 A_MUTEX_INIT(&pProtocol->HCIRxLock);
869 A_MUTEX_INIT(&pProtocol->HCITxLock);
875 DEV_GMBOX_SET_PROTOCOL(pDev,
876 HCIUartMessagePending,
877 CreditsAvailableCallback,
883 if (pProtocol != NULL) {
884 HCIUartCleanup(pProtocol);
891 /*** protocol module uninstall entry point ***/
892 void GMboxProtocolUninstall(AR6K_DEVICE *pDev)
894 GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
896 if (pProtocol != NULL) {
898 /* notify anyone attached */
899 if (pProtocol->HCIAttached) {
900 A_ASSERT(pProtocol->HCIConfig.TransportRemoved != NULL);
901 pProtocol->HCIConfig.TransportRemoved(pProtocol->HCIConfig.pContext);
902 pProtocol->HCIAttached = FALSE;
905 HCIUartCleanup(pProtocol);
906 DEV_GMBOX_SET_PROTOCOL(pDev,NULL,NULL,NULL,NULL,NULL);
911 static int NotifyTransportReady(GMBOX_PROTO_HCI_UART *pProt)
913 HCI_TRANSPORT_PROPERTIES props;
918 A_MEMZERO(&props,sizeof(props));
920 /* HCI UART only needs one extra byte at the head to indicate the packet TYPE */
923 props.IOBlockPad = pProt->pDev->BlockSize;
924 if (pProt->HCIAttached) {
925 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HCI: notifying attached client to transport... \n"));
926 A_ASSERT(pProt->HCIConfig.TransportReady != NULL);
927 status = pProt->HCIConfig.TransportReady(pProt,
929 pProt->HCIConfig.pContext);
937 /*********** HCI UART protocol implementation ************************************************/
939 HCI_TRANSPORT_HANDLE HCI_TransportAttach(void *HTCHandle, HCI_TRANSPORT_CONFIG_INFO *pInfo)
941 GMBOX_PROTO_HCI_UART *pProtocol = NULL;
944 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportAttach \n"));
946 pDev = HTCGetAR6KDevice(HTCHandle);
952 pProtocol = (GMBOX_PROTO_HCI_UART *)DEV_GMBOX_GET_PROTOCOL(pDev);
954 if (NULL == pProtocol) {
955 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not installed! \n"));
959 if (pProtocol->HCIAttached) {
960 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol already attached! \n"));
964 A_MEMCPY(&pProtocol->HCIConfig, pInfo, sizeof(HCI_TRANSPORT_CONFIG_INFO));
966 A_ASSERT(pProtocol->HCIConfig.pHCIPktRecv != NULL);
967 A_ASSERT(pProtocol->HCIConfig.pHCISendComplete != NULL);
969 pProtocol->HCIAttached = TRUE;
975 if (pProtocol != NULL) {
976 /* TODO ... should we use a worker? */
977 NotifyTransportReady(pProtocol);
980 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach (0x%lX) \n",(unsigned long)pProtocol));
981 return (HCI_TRANSPORT_HANDLE)pProtocol;
984 void HCI_TransportDetach(HCI_TRANSPORT_HANDLE HciTrans)
986 GMBOX_PROTO_HCI_UART *pProtocol = (GMBOX_PROTO_HCI_UART *)HciTrans;
987 AR6K_DEVICE *pDev = pProtocol->pDev;
989 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportDetach \n"));
992 if (!pProtocol->HCIAttached) {
993 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("GMBOX protocol not attached! \n"));
997 pProtocol->HCIAttached = FALSE;
1000 HCI_TransportStop(HciTrans);
1001 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportAttach \n"));
1004 int HCI_TransportAddReceivePkts(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET_QUEUE *pQueue)
1006 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1008 A_BOOL unblockRecv = FALSE;
1009 HTC_PACKET *pPacket;
1011 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HCI_TransportAddReceivePkt \n"));
1017 if (pProt->HCIStopped) {
1018 status = A_ECANCELED;
1022 pPacket = HTC_GET_PKT_AT_HEAD(pQueue);
1024 if (NULL == pPacket) {
1029 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv packet added, type :%d, len:%d num:%d \n",
1030 HCI_GET_PACKET_TYPE(pPacket), pPacket->BufferLength, HTC_PACKET_QUEUE_DEPTH(pQueue)));
1032 if (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) {
1033 HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIEventBuffers, pQueue);
1034 } else if (HCI_GET_PACKET_TYPE(pPacket) == HCI_ACL_TYPE) {
1035 HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pProt->HCIACLRecvBuffers, pQueue);
1041 if (pProt->RecvStateFlags & HCI_RECV_WAIT_BUFFERS) {
1042 if (pProt->WaitBufferType == HCI_GET_PACKET_TYPE(pPacket)) {
1043 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" HCI recv was blocked on packet type :%d, unblocking.. \n",
1044 pProt->WaitBufferType));
1045 pProt->RecvStateFlags &= ~HCI_RECV_WAIT_BUFFERS;
1046 pProt->WaitBufferType = HCI_PACKET_INVALID;
1053 UNLOCK_HCI_RX(pProt);
1056 while (!HTC_QUEUE_EMPTY(pQueue)) {
1057 pPacket = HTC_PACKET_DEQUEUE(pQueue);
1058 pPacket->Status = A_ECANCELED;
1059 DO_HCI_RECV_INDICATION(pProt,pPacket);
1064 DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_ASYNC);
1067 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HCI_TransportAddReceivePkt \n"));
1072 int HCI_TransportSendPkt(HCI_TRANSPORT_HANDLE HciTrans, HTC_PACKET *pPacket, A_BOOL Synchronous)
1074 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1076 return HCITrySend(pProt,pPacket,Synchronous);
1079 void HCI_TransportStop(HCI_TRANSPORT_HANDLE HciTrans)
1081 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1083 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStop \n"));
1085 LOCK_AR6K(pProt->pDev);
1086 if (pProt->HCIStopped) {
1087 UNLOCK_AR6K(pProt->pDev);
1088 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
1091 pProt->HCIStopped = TRUE;
1092 UNLOCK_AR6K(pProt->pDev);
1094 /* disable interrupts */
1095 DevGMboxIRQAction(pProt->pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
1096 FlushSendQueue(pProt);
1097 FlushRecvBuffers(pProt);
1099 /* signal bridge side to power down BT */
1100 DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_OFF, BTOFF_TIMEOUT_MS);
1102 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStop \n"));
1105 int HCI_TransportStart(HCI_TRANSPORT_HANDLE HciTrans)
1108 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1110 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HCI_TransportStart \n"));
1112 /* set stopped in case we have a problem in starting */
1113 pProt->HCIStopped = TRUE;
1117 status = InitTxCreditState(pProt);
1123 status = DevGMboxIRQAction(pProt->pDev, GMBOX_ERRORS_IRQ_ENABLE, PROC_IO_SYNC);
1129 status = DevGMboxIRQAction(pProt->pDev, GMBOX_RECV_IRQ_ENABLE, PROC_IO_SYNC);
1134 /* signal bridge side to power up BT */
1135 status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BT_ON, BTON_TIMEOUT_MS);
1138 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI_TransportStart : Failed to trigger BT ON \n"));
1143 pProt->HCIStopped = FALSE;
1147 AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HCI_TransportStart \n"));
1152 int HCI_TransportEnableDisableAsyncRecv(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable)
1154 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1155 return DevGMboxIRQAction(pProt->pDev,
1156 Enable ? GMBOX_RECV_IRQ_ENABLE : GMBOX_RECV_IRQ_DISABLE,
1161 int HCI_TransportRecvHCIEventSync(HCI_TRANSPORT_HANDLE HciTrans,
1162 HTC_PACKET *pPacket,
1165 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1167 A_UINT8 lookAhead[8];
1169 int totalRecvLength;
1171 MaxPollMS = MaxPollMS / 16;
1173 if (MaxPollMS < 2) {
1179 bytes = sizeof(lookAhead);
1180 status = DevGMboxRecvLookAheadPeek(pProt->pDev,lookAhead,&bytes);
1186 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI recv poll got bytes: %d, retry : %d \n",
1193 totalRecvLength = 0;
1194 switch (lookAhead[0]) {
1195 case HCI_UART_EVENT_PKT:
1196 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HCI Event: %d param length: %d \n",
1197 lookAhead[1], lookAhead[2]));
1198 totalRecvLength = lookAhead[2];
1199 totalRecvLength += 3; /* add type + event code + length field */
1202 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("**Invalid HCI packet type: %d \n",lookAhead[0]));
1211 pPacket->Completion = NULL;
1212 status = DevGMboxRead(pProt->pDev,pPacket,totalRecvLength);
1218 pPacket->ActualLength = totalRecvLength - 1;
1219 pPacket->Status = A_OK;
1223 if (MaxPollMS == 0) {
1224 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI recv poll timeout! \n"));
1231 #define LSB_SCRATCH_IDX 4
1232 #define MSB_SCRATCH_IDX 5
1233 int HCI_TransportSetBaudRate(HCI_TRANSPORT_HANDLE HciTrans, A_UINT32 Baud)
1235 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1236 HIF_DEVICE *pHIFDevice = (HIF_DEVICE *)(pProt->pDev->HIFDevice);
1237 A_UINT32 scaledBaud, scratchAddr;
1240 /* Divide the desired baud rate by 100
1241 * Store the LSB in the local scratch register 4 and the MSB in the local
1242 * scratch register 5 for the target to read
1244 scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * LSB_SCRATCH_IDX);
1245 scaledBaud = (Baud / 100) & LOCAL_SCRATCH_VALUE_MASK;
1246 status = ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud);
1247 scratchAddr = MBOX_BASE_ADDRESS | (LOCAL_SCRATCH_ADDRESS + 4 * MSB_SCRATCH_IDX);
1248 scaledBaud = ((Baud / 100) >> (LOCAL_SCRATCH_VALUE_MSB+1)) & LOCAL_SCRATCH_VALUE_MASK;
1249 status |= ar6000_WriteRegDiag(pHIFDevice, &scratchAddr, &scaledBaud);
1250 if (A_OK != status) {
1251 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to set up baud rate in scratch register!"));
1255 /* Now interrupt the target to tell it about the baud rate */
1256 status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_BAUD_SET, BAUD_TIMEOUT_MS);
1257 if (A_OK != status) {
1258 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to tell target to change baud rate!"));
1264 int HCI_TransportEnablePowerMgmt(HCI_TRANSPORT_HANDLE HciTrans, A_BOOL Enable)
1267 GMBOX_PROTO_HCI_UART *pProt = (GMBOX_PROTO_HCI_UART *)HciTrans;
1270 status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_PWR_SAV_ON, BTPWRSAV_TIMEOUT_MS);
1272 status = DevGMboxSetTargetInterrupt(pProt->pDev, MBOX_SIG_HCI_BRIDGE_PWR_SAV_OFF, BTPWRSAV_TIMEOUT_MS);
1276 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to enable/disable HCI power management!\n"));
1278 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HCI power management enabled/disabled!\n"));
1284 #endif //ATH_AR6K_ENABLE_GMBOX