www.pudn.com > sdio-2.6.18-full.rar > sdio_bt.c
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @file: sdio_bt.c @abstract: Bluetooth SDIO driver #notes: includes OS independent portions @notice: Copyright (c), 2005-2006 Atheros Communications, Inc. * * 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. * * Portions o this code were developed with information supplied from the * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: * * The following conditions apply to the release of the SD simplified specification (“Simplified * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified * Specification may require a license from the SD Card Association or other third parties. * Disclaimers: * The information contained in the Simplified Specification is presented only as a standard * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for * any damages, any infringements of patents or other right of the SD Card Association or any third * parties, which may result from its use. No license is granted by implication, estoppel or otherwise * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall * be construed as an obligation by the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. * * * The initial developers of the original code are Seung Yi and Paul Lever * * sdio@atheros.com * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #include#include #include #include #include "sdio_bt.h" static void BTIRQHandler(PVOID pContext); #define BLOCK_WRITE TRUE #define BLOCK_READ FALSE static SDIO_STATUS ReceiveHciPacket(PBT_HCI_INSTANCE pHci); static void BtTxCompletion(PSDREQUEST pReq); UINT32 SetRequestParam(PBT_HCI_INSTANCE pHci, UINT32 BytesToSend, PSDREQUEST pReq, BOOL Write); /* delete an instance */ void DeleteHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, PBT_HCI_INSTANCE pHci) { SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; SDIO_STATUS status; if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pFuncContext->InstanceSem))) { return; } /* pull it out of the list */ SDListRemove(&pHci->SDList); SemaphorePost(&pFuncContext->InstanceSem); if (!(SDDEVICE_IS_CARD_REMOVED(pHci->pDevice))) { if (pHci->Flags & FLAGS_CARD_IRQ_UNMSK) { SDDEVICE_SET_IRQ_HANDLER(pHci->pDevice, NULL, NULL); /* mask our IRQ */ status = SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_MASK_IRQ,NULL,0); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to unmask IRQ err:%d \n", status)); } else { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: IRQ disabled \n")); } } /* disable the card */ if (pHci->Flags & FLAGS_CARD_ENAB) { ZERO_OBJECT(fData); fData.EnableFlags = SDCONFIG_DISABLE_FUNC; fData.TimeOut = SDBT_ENABLE_DISABLE_TIMEOUT; status = SDLIB_IssueConfig(pHci->pDevice, SDCONFIG_FUNC_ENABLE_DISABLE, &fData, sizeof(fData)); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to disable card: %d\n", status)); } else { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Card Disabled \n")); } } } else { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Card removed \n")); } SDLIB_IssueConfig(pHci->pDevice, SDCONFIG_FUNC_FREE_SLOT_CURRENT, NULL, 0); KernelFree(pHci); } /* create a Hci instance */ PBT_HCI_INSTANCE CreateHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, PSDDEVICE pDevice) { PBT_HCI_INSTANCE pHci = NULL; UINT32 nextTpl; UINT8 tplLength; SDIO_STATUS status = SDIO_STATUS_SUCCESS; struct PKT_RETRY_CONTROL_TUPLE rtcTuple; SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent; ZERO_OBJECT(slotCurrent); ZERO_OBJECT(fData); do { pHci = (PBT_HCI_INSTANCE)KernelAlloc(sizeof(BT_HCI_INSTANCE)); if (NULL == pHci) { break; } ZERO_POBJECT(pHci); SDLIST_INIT(&pHci->SDList); pHci->pDevice = pDevice; pHci->FuncNo = SDDEVICE_GET_SDIO_FUNCNO(pHci->pDevice); DBG_PRINT(SDDBG_TRACE, ("SDIO BT HCI Function Instance: 0x%Xn Fn:%d \n",(INT)pHci, pHci->FuncNo)); if (SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pHci->pDevice) == 0) { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Function does not support Block Mode! \n")); status = SDIO_STATUS_ERROR; break; } /* limit block size to operational block limit or card function capability*/ pHci->MaxBlockSize = min(SDDEVICE_GET_OPER_BLOCK_LEN(pHci->pDevice), SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pHci->pDevice)); /* check if the card support multi-block transfers */ if (!(SDDEVICE_GET_SDIOCARD_CAPS(pHci->pDevice) & SDIO_CAPS_MULTI_BLOCK)) { pHci->Flags |= FLAGS_BYTE_BASIS; DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Byte basis only \n")); /* limit block size to max byte basis */ pHci->MaxBlockSize = min(pHci->MaxBlockSize, (UINT16)SDIO_MAX_LENGTH_BYTE_BASIS); pHci->MaxBlocks = 1; } else { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Multi-block capable \n")); pHci->MaxBlocks = SDDEVICE_GET_OPER_BLOCKS(pHci->pDevice); } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Bytes Per Block: %d bytes, Block Count:%d \n", pHci->MaxBlockSize, pHci->MaxBlocks)); status = SDLIB_GetDefaultOpCurrent(pDevice, &slotCurrent.SlotCurrent); if (!SDIO_SUCCESS(status)) { break; } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Allocating Slot current: %d mA\n", slotCurrent.SlotCurrent)); status = SDLIB_IssueConfig(pDevice, SDCONFIG_FUNC_ALLOC_SLOT_CURRENT, &slotCurrent, sizeof(slotCurrent)); if (!SDIO_SUCCESS((status))) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to allocate slot current %d\n", status)); if (status == SDIO_STATUS_NO_RESOURCES) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Remaining Slot Current: %d mA\n", slotCurrent.SlotCurrent)); } break; } nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pHci->pDevice); tplLength = sizeof(rtcTuple); status = SDLIB_FindTuple(pHci->pDevice, CISTPL_VENDOR, &nextTpl, (PUINT8)&rtcTuple, &tplLength); if (SDIO_SUCCESS(status)) { pHci->Flags |= FLAGS_RTC_SUPPORTED; DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Retry protocol supported \n")); if (!(rtcTuple.SDIO_RetryControl & RTC_READ_ACK_NOT_REQUIRED)) { pHci->Flags |= FLAGS_READ_ACK; DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read Acknowledgements required \n")); } } else { DBG_PRINT(SDDBG_WARN, ("SDIO BT Function: No RTC Tuple, retry not supported \n")); status = SDIO_STATUS_SUCCESS; } /* now enable the card */ fData.EnableFlags = SDCONFIG_ENABLE_FUNC; fData.TimeOut = SDBT_ENABLE_DISABLE_TIMEOUT; status = SDLIB_IssueConfig(pHci->pDevice, SDCONFIG_FUNC_ENABLE_DISABLE, &fData, sizeof(fData)); if (!SDIO_SUCCESS((status))) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to enable function Err:%d\n", status)); break; } pHci->Flags |= FLAGS_CARD_ENAB; if (!(pHci->Flags & FLAGS_BYTE_BASIS)) { /* for cards that support multi-block transfers, set the block size */ status = SDLIB_SetFunctionBlockSize(pHci->pDevice, pHci->MaxBlockSize); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set block size. Err:%d\n", status)); break; } } /* if it was Type-B card, put it in Type-A mode */ if (SDDEVICE_GET_SDIO_FUNC_CLASS(pHci->pDevice) == 0x03) { DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Type B card detected, switch to type A mode \n")); status = WriteRegister(pHci,MODE_STATUS_REG,MODE_STATUS_TYPE_A); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to switch to type A mode \n")); break; } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: TYPE A Mode switched \n")); } if (pHci->Flags & FLAGS_READ_ACK) { status = WriteRegister(pHci,RETRY_CONTROL_STATUS_REG,RETRY_CONTROL_STATUS_USE_ACKS); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set retry control to use acks \n")); break; } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read acks enabled \n")); status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RECV_PACKET_CONTROL_ACK); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set issue read ack \n")); break; } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read ack issued \n")); } SDDEVICE_SET_IRQ_HANDLER(pHci->pDevice,BTIRQHandler,pHci); /* unmask our function IRQ */ status = SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_UNMASK_IRQ,NULL,0); if (!SDIO_SUCCESS((status))) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to unmask IRQ Err:%d\n", status)); break; } pHci->Flags |= FLAGS_CARD_IRQ_UNMSK; /* enable packet ready interrupt */ status = WriteRegister(pHci,INTERRUPT_ENABLE_REG,RCV_PKT_PENDING_ENABLE); if (!SDIO_SUCCESS(status)) { break; } DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: IRQ enabled - ready \n")); #if 0 // test command { UCHAR data[7] = {0x07,0x00,0x00,0x01,0x03,0x10,0x00}; PSDREQUEST pReq = NULL; /* allocate request to send to host controller */ pReq = SDDeviceAllocRequest(pHci->pDevice); if (NULL == pReq) { break; } SetRequestParam(pHci, 7, pReq, TRUE); pReq->Flags &= ~SDREQ_FLAGS_TRANS_ASYNC; pReq->pCompletion =NULL; pReq->pCompleteContext = NULL; pReq->pDataBuffer = data; SDLIB_PrintBuffer(data,7, "Sending first BT command..."); status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_WARN, ("SDIO BT - TEST: Synch CMD53 write failed %d \n", status)); } SDDeviceFreeRequest(pHci->pDevice,pReq); } #endif } while (FALSE); if (!SDIO_SUCCESS(status) && (pHci != NULL)) { DeleteHciInstance(pFuncContext, pHci); pHci = NULL; } return pHci; } /* find an instance associated with the SD device */ PBT_HCI_INSTANCE FindHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, PSDDEVICE pDevice) { SDIO_STATUS status; PSDLIST pItem; PBT_HCI_INSTANCE pHci = NULL; status = SemaphorePendInterruptable(&pFuncContext->InstanceSem); if (!SDIO_SUCCESS(status)) { return NULL; } /* walk the list and find our instance */ SDITERATE_OVER_LIST(&pFuncContext->InstanceList, pItem) { pHci = CONTAINING_STRUCT(pItem, BT_HCI_INSTANCE, SDList); if (pHci->pDevice == pDevice) { /* found it */ break; } pHci = NULL; } SemaphorePost(&pFuncContext->InstanceSem); return pHci; } /* add an instance to our list */ SDIO_STATUS AddHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, PBT_HCI_INSTANCE pHci) { SDIO_STATUS status; status = SemaphorePendInterruptable(&pFuncContext->InstanceSem); if (!SDIO_SUCCESS(status)) { return status; } SDListAdd(&pFuncContext->InstanceList,&pHci->SDList); SemaphorePost(&pFuncContext->InstanceSem); return SDIO_STATUS_SUCCESS; } static void BTIRQHandler(PVOID pContext) { PBT_HCI_INSTANCE pHci; SDIO_STATUS status = SDIO_STATUS_SUCCESS; UINT8 temp; DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT IRQ \n")); pHci = (PBT_HCI_INSTANCE)pContext; while (1) { status = ReadRegister(pHci,INTERRUPT_STATUS_CLEAR_REG,&temp); if (!SDIO_SUCCESS(status)) { /* can't read it for some reason */ DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to read int status, err:%d\n", status)); break; } if (!(temp & RCV_PKT_PENDING)) { break; } /* clear the status */ status = WriteRegister(pHci,INTERRUPT_STATUS_CLEAR_REG,RCV_PKT_PENDING); if (!SDIO_SUCCESS(status)) { /* can't clear it for some reason */ DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to clear int status, err:%d\n", status)); break; } status = ReceiveHciPacket(pHci); if (!SDIO_SUCCESS(status)) { break; } } if (!SDIO_SUCCESS(status)) { /* mask the interrupt if we can't handle them */ DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: FATAL Error detected in IRQ processing , err:%d\n", status)); WriteRegister(pHci,INTERRUPT_ENABLE_REG,0); } /* ack the interrupt */ SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_ACK_IRQ,NULL,0); } /* set the request parameters in a request * This does not set the pDataBuffer field */ UINT32 SetRequestParam(PBT_HCI_INSTANCE pHci, UINT32 BytesToSend, PSDREQUEST pReq, BOOL Write) { UINT32 bytesToTransfer; pReq->Command = CMD53; pReq->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS; if (Write) { /* do write in non-blocking fashion */ /* note, that read-operations are performed in our normal IRQ handler which * allows synchronous operations */ pReq->Flags |= (SDREQ_FLAGS_DATA_WRITE | SDREQ_FLAGS_TRANS_ASYNC); pReq->pCompletion = BtTxCompletion; pReq->pCompleteContext = (PVOID)pHci; } if ((pHci->Flags & FLAGS_BYTE_BASIS) || (BytesToSend < pHci->MaxBlockSize)) { /* byte basis */ bytesToTransfer = min((UINT32)pHci->MaxBlockSize, BytesToSend); SDIO_SET_CMD53_ARG(pReq->Argument, (Write ? CMD53_WRITE : CMD53_READ), pHci->FuncNo, CMD53_BYTE_BASIS, CMD53_FIXED_ADDRESS, (Write ? XMIT_DATA_REG : RECV_DATA_REG), CMD53_CONVERT_BYTE_BASIS_BLK_LENGTH_PARAM(bytesToTransfer)); pReq->BlockCount = 1; pReq->BlockLen = bytesToTransfer; if (pReq->BlockLen < 8) { pReq->Flags |= SDREQ_FLAGS_DATA_SHORT_TRANSFER; } } else { /* block mode */ pReq->BlockLen = pHci->MaxBlockSize; /* get block counts (whole blocks, no partials allowed) */ pReq->BlockCount = min(BytesToSend / (UINT32)pHci->MaxBlockSize, (UINT32)pHci->MaxBlocks); DBG_ASSERT(pReq->BlockCount != 0); /* calculate total transfer to return */ bytesToTransfer = pReq->BlockCount * pReq->BlockLen; /* set argument */ SDIO_SET_CMD53_ARG(pReq->Argument, (Write ? CMD53_WRITE : CMD53_READ), pHci->FuncNo, CMD53_BLOCK_BASIS, CMD53_FIXED_ADDRESS, (Write ? XMIT_DATA_REG : RECV_DATA_REG), CMD53_CONVERT_BLOCK_BASIS_BLK_COUNT_PARAM(pReq->BlockCount)); } return bytesToTransfer; } static INLINE UINT32 AdjustBytesForHC(PBT_HCI_INSTANCE pHci, UINT32 RemainingBytes) { if (pHci->BlockTransferFix) { /* some host controllers (like the PXA25x) do not support 1 and 3 byte transfers * we are trying to avert a 1 or 3 byte block transfer * unfortunately not all SDIO BT cards allow CMD52 operations on the data port * so we have to "look ahead" and prevent a small block transfer by dividing the * current chunk */ if (((RemainingBytes % pHci->MaxBlockSize) == 1) || ((RemainingBytes % pHci->MaxBlockSize) == 3)) { /* divide by two, this should split the remaining bytes into something * manageable */ DBG_PRINT(SDDBG_WARN, ("SDIO BT - adjusting %d bytes (max blocks: %d) \n", RemainingBytes,pHci->MaxBlockSize)); return (RemainingBytes >> 1); } return RemainingBytes; } /* no adjustment required */ return RemainingBytes; } static INLINE void SetUpNextTxBlocTransfer(PBT_HCI_INSTANCE pHci, PSDREQUEST pReq) { pHci->pTxBufferPosition += pHci->TxBytesToTransfer; pHci->TxRemaining -= pHci->TxBytesToTransfer; if (pHci->TxRemaining) { /* set where we are */ pReq->pDataBuffer = pHci->pTxBufferPosition; /* set up parameters for the request */ pHci->TxBytesToTransfer = SetRequestParam(pHci, AdjustBytesForHC(pHci,pHci->TxRemaining), pReq, BLOCK_WRITE); } } static INLINE void ResetCurrentTxPacketTransfer(PBT_HCI_INSTANCE pHci) { pHci->pTxBufferPosition = SDBTHCI_GET_PKT_BUFFER(pHci->pCurrentTxPacket); pHci->TxRemaining = SDBTHCI_GET_PKT_LENGTH(pHci->pCurrentTxPacket); pHci->TxBytesToTransfer = 0; } static void BtTxCompletion(PSDREQUEST pReq) { PBT_HCI_INSTANCE pHci; SDIO_STATUS status; BOOL done = FALSE; pHci = (PBT_HCI_INSTANCE)pReq->pCompleteContext; DBG_ASSERT(pHci != NULL); status = pReq->Status; switch (pHci->TxState) { case TX_BLOCK_PROCESSING: if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: XMIT Failed, Err:%d \n", status)); /* check for retry */ if (pHci->Flags & FLAGS_RTC_SUPPORTED) { pHci->TxRetry--; if (pHci->TxRetry) { pHci->TxState = TX_PACKET_RETRY; /* setup request for async CMD52 */ SDLIB_SetupCMD52RequestAsync((pHci)->FuncNo, XMIT_PACKET_CONTROL_REG, CMD52_DO_WRITE, XMIT_PKT_CONTROL_RETRY, pReq); /* set completion routine */ pReq->pCompletion = BtTxCompletion; pReq->pCompleteContext = (PVOID)pHci; /* submit */ SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); } else { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: XMIT retries exceeded \n")); done = TRUE; break; } } else { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Card does not support retries! \n")); done = TRUE; } } else { /* set it up for the next transfer */ SetUpNextTxBlocTransfer(pHci,pReq); /* are we done? */ if (pHci->TxRemaining) { /* submit request asynchronously */ SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); } else { DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Function: TX Packet 0x%X sent \n", (INT)pHci->pCurrentTxPacket)); done = TRUE; } } break; case TX_PACKET_RETRY: if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set TX packet retry control, Err:%d \n", status)); done = TRUE; break; } if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: CMD52 failed in TX retry control \n")); status = SDIO_STATUS_DEVICE_ERROR; done = TRUE; break; } ResetCurrentTxPacketTransfer(pHci); pHci->TxState = TX_BLOCK_PROCESSING; /* setup block transfer */ SetUpNextTxBlocTransfer(pHci,pReq); /* submit request asynchronously */ SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); break; default: DBG_ASSERT(FALSE); break; } if (done) { OSIndicateHCIPacketTransmitDone(pHci,status); OSFreeSDRequest(pHci, pReq); } } /* send the hci packet asynchronously the caller handles the header and packet queues and sets the * pCurrentPacket field for the packet ready to go out */ SDIO_STATUS SendHciPacket(PBT_HCI_INSTANCE pHci) { SDIO_STATUS status = SDIO_STATUS_PENDING; PSDREQUEST pReq; do { DBG_ASSERT(pHci->pCurrentTxPacket != NULL); pReq = OSAllocSDRequest(pHci); if (NULL == pReq) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: No SD requests remaining \n")); status = SDIO_STATUS_NO_RESOURCES; break; } pHci->TxRetry = SDBT_PKT_RETRIES; ResetCurrentTxPacketTransfer(pHci); pHci->TxState = TX_BLOCK_PROCESSING; /* setup block transfer */ SetUpNextTxBlocTransfer(pHci,pReq); DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Function: BlockLen:%d,BlockCount:%d, Arg:0x%X \n", pReq->BlockLen, pReq->BlockCount,pReq->Argument)); /* submit request asynchronously */ SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); } while (FALSE); if (!SDIO_SUCCESS(status)) { OSIndicateHCIPacketTransmitDone(pHci,status); } return status; } static SDIO_STATUS ReceiveHciPacket(PBT_HCI_INSTANCE pHci) { SDIO_STATUS status; PSDBT_HCI_PACKET pPacket = NULL; UINT8 header[SDIO_BT_TRANSPORT_HEADER_LENGTH]; PSDREQUEST pReq; UINT32 bytesToTransfer; UINT32 remaining; PUINT8 pBufferLoc; pReq = OSAllocSDRequest(pHci); if (NULL == pReq) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: No SD requests remaining \n")); return SDIO_STATUS_NO_RESOURCES; } do{ pReq->pDataBuffer = header; SetRequestParam(pHci,AdjustBytesForHC(pHci,sizeof(header)),pReq,BLOCK_READ); /* go get the header */ status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to get header, Err:%d\n", status)); break; } remaining = SDIO_BT_GET_LENGTH(header); if (remaining <= SDIO_BT_TRANSPORT_HEADER_LENGTH) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Invalid Length:%d \n", remaining)); break; } if (DBG_GET_DEBUG_LEVEL() >= SDBT_DBG_RECEIVE) { SDLIB_PrintBuffer(header,sizeof(header),"SDIO BT, header dump \n"); } switch (SDIO_BT_GET_SERVICEID(header)) { case SDIO_BT_TYPE_A_HCI_EVT: case SDIO_BT_TYPE_A_HCI_ACL: case SDIO_BT_TYPE_A_HCI_SCO: break; default: DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Invalid packet type:%d \n", SDIO_BT_GET_SERVICEID(header))); status = SDIO_STATUS_ERROR; break; } if (!SDIO_SUCCESS(status)) { break; } /* subtract off header */ remaining -= SDIO_BT_TRANSPORT_HEADER_LENGTH; /* get a buffer for this HCI packet */ pPacket = OSAllocHCIRcvPacket(pHci, remaining, SDIO_BT_GET_SERVICEID(header)); if (NULL == pPacket) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to allocate packet \n")); status = SDIO_STATUS_NO_RESOURCES; break; } DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: Getting HCI Packet (type:%d, Length:%d) \n", SDIO_BT_GET_SERVICEID(header),remaining)); pBufferLoc = SDBTHCI_GET_PKT_BUFFER(pPacket); while (remaining) { /* set where we are */ pReq->pDataBuffer = pBufferLoc; /* set up parameters for the request */ bytesToTransfer = SetRequestParam(pHci,AdjustBytesForHC(pHci,remaining),pReq,BLOCK_READ); /* submit request synchronously */ status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Rcv Failed, Err:%d \n", status)); break; } pBufferLoc += bytesToTransfer; remaining -= bytesToTransfer; } if (!SDIO_SUCCESS(status)) { /* free this packet */ OSFreeHciRcvPacket(pHci, pPacket); if (pHci->Flags & FLAGS_RTC_SUPPORTED) { pHci->RxRetry++; if (pHci->RxRetry > MAX_RX_RETRY) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: RX Retry Exceeded \n")); break; } /* set bit to discard this packet, we need to start over */ status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RCV_PKT_CONTROL_RETRY); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to set RCV PKT RETRY, err:%d \n", status)); } } else { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Card does not support retries! \n")); } break; } else { /* reset retry on good packets */ pHci->RxRetry = 0; DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: Packet received --\n")); if (DBG_GET_DEBUG_LEVEL() >= SDBT_DBG_RECEIVE) { SDLIB_PrintBuffer(SDBTHCI_GET_PKT_BUFFER(pPacket), (SDIO_BT_GET_LENGTH(header) - SDIO_BT_TRANSPORT_HEADER_LENGTH), "Received HCI Packet Dump"); } /* indicate the packet */ OSIndicateHCIPacketReceived(pHci, pPacket, (SDIO_BT_GET_LENGTH(header) - SDIO_BT_TRANSPORT_HEADER_LENGTH), SDIO_BT_GET_SERVICEID(header)); /* we no longer own this packet */ pPacket = NULL; if (pHci->Flags & FLAGS_READ_ACK) { /* ack the hardware indicating we pulled the packet out */ status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RECV_PACKET_CONTROL_ACK); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to set ACK Packet, err:%d \n", status)); break; } else { DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: RCV Packet Ack'd \n")); } } } }while(FALSE); OSFreeSDRequest(pHci, pReq); return status; }