📄 bthsdio.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
#include "bthsdio.h"
#include <ceddk.h>
// Bluetooth SDIO registers
#define REG_DATAPORT 0x00 // Send/Recv data
#define REG_PCRRT 0x10 // Read Packet Control
#define REG_PCWRT 0x11 // Write Packet Control
#define REG_RTC 0x12 // Retry Control
#define REG_INTRD 0x13 // Interrupt
#define REG_ENINTRD 0x14 // Interrupt Enable
#define REG_MDSTAT 0x20 // Bluetooth Mode Status
// SDIO registers
#define REG_CCCR_CARD_CAP 0x08
// BT card types
#define BT_CARD_TYPE_A 0x02
#define BT_CARD_TYPE_B 0x03
// Constants
#define DEFAULT_HOST_BLOCK_SIZE 512
#define DEFAULT_HOST_NUM_BLOCKS 8
#define DEFAULT_SDIO_FUNCTION_RETRIES 5
#define DEFAULT_SDIO_FUNCTION_RETRY_TIMEOUT 1000
CSdioDevice::CSdioDevice()
{
m_hDevice = 0;
m_dwState = DETACHED;
m_ucFunction = 0;
m_fInterruptConnected = FALSE;
m_pRegPath = NULL;
m_hReadPacketEvent = NULL;
m_hIOStoppedEvent = NULL;
m_hRecvCompleteEvent = NULL;
m_usBlockLen = 0;
m_usBTCardType = 0;
m_fBlockMode = FALSE;
m_fCancelIO = FALSE;
m_hBusAccess = NULL;
m_cpsCurrent = D0;
m_fGoTo4BitModeOnResume = FALSE;
// SVSRefObj always starts at 1. For our purposes, we want
// it to initially be 0
ASSERT(m_refIO.GetRefCount() == 1);
m_refIO.SetSignal(SignalRefCountZero, (PVOID)this);
m_refIO.DelRef();
}
CSdioDevice::~CSdioDevice()
{
if (m_hReadPacketEvent) {
CloseHandle(m_hReadPacketEvent);
}
if (m_hIOStoppedEvent) {
CloseHandle(m_hIOStoppedEvent);
}
if (m_hRecvCompleteEvent) {
CloseHandle(m_hRecvCompleteEvent);
}
}
/*
This method is called to initialize the object.
*/
BOOL CSdioDevice::Init(void)
{
BOOL fRetVal = TRUE;
if (! m_hReadPacketEvent) {
m_hReadPacketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == m_hReadPacketEvent) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
}
if (! m_hIOStoppedEvent) {
m_hIOStoppedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == m_hIOStoppedEvent) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
}
if (! m_hRecvCompleteEvent) {
m_hRecvCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == m_hRecvCompleteEvent) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
}
exit:
return fRetVal;
}
/*
This method is called when a card is inserted into the host controller. This method will
initialize the SDIO card.
*/
BOOL CSdioDevice::Attach(DWORD dwContext)
{
BOOL fRetVal = TRUE;
SDCARD_CLIENT_REGISTRATION_INFO clientInfo;
SD_IO_FUNCTION_ENABLE_INFO functionEnable;
SDIO_CARD_INFO sdioInfo;
Init();
m_hDevice = SDGetDeviceHandle(dwContext, &m_pRegPath);
if (NULL == m_hDevice) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
SDRegisterDebugZones(m_hDevice, m_pRegPath);
memset(&clientInfo, 0, sizeof(clientInfo));
wcscpy(clientInfo.ClientName, TEXT("Bluetooth Card"));
if (! SD_API_SUCCESS(SDRegisterClient(m_hDevice, this, &clientInfo))) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
if (! SD_API_SUCCESS(SDIOConnectInterrupt(m_hDevice, SDIOIsrCallBack))) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
m_hBusAccess = CreateBusAccessHandle((LPCTSTR) dwContext);
m_fInterruptConnected = TRUE;
functionEnable.Interval = DEFAULT_SDIO_FUNCTION_RETRY_TIMEOUT;
functionEnable.ReadyRetryCount = DEFAULT_SDIO_FUNCTION_RETRIES;
if (! SD_API_SUCCESS(SDSetCardFeature(m_hDevice, SD_IO_FUNCTION_ENABLE, &functionEnable, sizeof(functionEnable)))) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
if (! SD_API_SUCCESS(SDCardInfoQuery(m_hDevice, SD_INFO_SDIO, &sdioInfo, sizeof(sdioInfo)))) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
m_ucFunction = sdioInfo.FunctionNumber;
m_dwState = ATTACHED;
exit:
if (m_dwState != ATTACHED) {
Detach();
}
else if(g_Data.pfnHCICallback && !g_Data.fStopHardware) {
g_Data.pfnHCICallback(DEVICE_UP, NULL);
}
return fRetVal;
}
/*
This method is called when the card is ejected from the host controller. Anything that
was initialized in Attach() must be deinitialized.
*/
void CSdioDevice::Detach(void)
{
if ((m_dwState == ATTACHED) && g_Data.pfnHCICallback && !g_Data.fStopHardware) {
g_Data.pfnHCICallback(DEVICE_DOWN, NULL);
}
m_dwState = SHUTDOWN_IN_PROGRESS;
// Signal IO to stop. Detach() must wait until IO
// has completed to return.
SetEvent(m_hReadPacketEvent);
WaitForSingleObject(m_hIOStoppedEvent, INFINITE);
if (m_hBusAccess) {
CloseBusAccessHandle(m_hBusAccess);
m_hBusAccess = NULL;
}
if (m_fInterruptConnected) {
SDIODisconnectInterrupt(m_hDevice);
m_fInterruptConnected = FALSE;
}
if (m_pRegPath) {
SDFreeMemory(m_pRegPath);
m_pRegPath = NULL;
}
m_dwState = DETACHED;
}
/*
This is a static method which just calls a method on the object passed in pv.
*/
void CSdioDevice::SignalRefCountZero(void *pv)
{
CSdioDevice* pInst = (CSdioDevice*)pv;
pInst->SignalRefCountZero_Int();
}
/*
This method is called when m_refIO becomes zero. If a detach is in progress
this should signal that all IO has completed. At this point no new IO will begin
since m_dwState will equal SHUTDOWN_IN_PROGRESS.
*/
void CSdioDevice::SignalRefCountZero_Int(void)
{
if ((m_dwState == SHUTDOWN_IN_PROGRESS) || m_fCancelIO) {
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::SignalRefCountZero - IO completed, detach can proceed.\n"));
SetEvent(m_hIOStoppedEvent);
}
}
/*
This method initializes Bluetooth card in preparation for read/write.
*/
BOOL CSdioDevice::OpenConnection(void)
{
BOOL fRetVal = TRUE;
UCHAR ucRegVal;
m_refIO.AddRef();
m_fCancelIO = FALSE; // reset
if (m_dwState != ATTACHED) {
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::OpenConnection - currently not attached, aborting.\n"));
fRetVal = FALSE;
goto exit;
}
if (! GetMaxBlockLen()) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
// Is block mode supported?
if (! SD_API_SUCCESS(SDGetRegister(REG_CCCR_CARD_CAP, 0, &ucRegVal, 1))) {
fRetVal = FALSE;
goto exit;
}
m_fBlockMode = (ucRegVal & 0x2) >> 1; // SMB is bit 1
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] Block Mode: %d Max Block Size: %d\n", m_fBlockMode, m_usBlockLen));
if (! InitializeBTRegisters()) {
ASSERT(0);
fRetVal = FALSE;
goto exit;
}
exit:
m_refIO.DelRef();
if (! fRetVal) {
CloseConnection();
}
return fRetVal;
}
/*
This method cleans up anything required from OpenConnection.
*/
void CSdioDevice::CloseConnection(void)
{
m_fCancelIO = TRUE;
if (m_refIO.GetRefCount()) {
// Signal IO to stop
SetEvent(m_hReadPacketEvent);
}
}
/*
This method waits for an event to be signalled which indicates a packet is ready. It reads the packet
header followed by the rest of the packet.
*/
BOOL CSdioDevice::ReadPacket(unsigned char* pBuffer, unsigned int* pcbBuffer, BYTE* pbType)
{
BOOL fRetVal = TRUE;
unsigned int cHeader = SDIO_HEADER_SIZE;
SD_TRANSPORT_HEADER header;
UCHAR ucRegVal;
m_refIO.AddRef();
if (m_dwState != ATTACHED) {
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::ReadPacket - currently not attached, aborting.\n"));
fRetVal = FALSE;
goto exit;
}
WaitForSingleObject(m_hReadPacketEvent, INFINITE);
if ((m_dwState != ATTACHED) || m_fCancelIO) {
fRetVal = FALSE;
goto exit;
}
// Get SDIO header
if (! SD_API_SUCCESS(SDRecv(pBuffer, &cHeader))) {
fRetVal = FALSE;
goto exit;
}
ASSERT(cHeader == SDIO_HEADER_SIZE);
ASSERT(sizeof(header) == SDIO_HEADER_SIZE);
#if defined (BT_USE_CELOG)
if (g_Data.fCeLog) {
CELOGDATAFLAGGED(TRUE, CELID_RAW_UCHAR, pBuffer, cHeader, 0, CELZONE_ALWAYSON, CELOG_FLAG_RAW_IN);
}
#endif
memcpy(&header, pBuffer, sizeof(header));
if ((header.u.AsULONG & 0xFFFFFF) <= 4) {
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::ReadPacket - Received a zero-length packet.\n"));
fRetVal = FALSE;
goto exit;
}
// SDIO service ID maps directly to HCI_TYPE
*pcbBuffer = (header.u.AsULONG & 0xFFFFFF) - SDIO_HEADER_SIZE;
*pbType = (BYTE)header.u.AsUCHAR.ServiceID;
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::ReadPacket - type=%d size=%d\n", *pbType, *pcbBuffer));
if (*pcbBuffer > PACKET_SIZE_R) {
IFDBG(DebugOut (DEBUG_ERROR, L"[SDIO] CSdioDevice::ReadPacket - Header is specifying invalid size of data. Bailing...\n", *pbType, *pcbBuffer));
fRetVal = FALSE;
goto exit;
}
// Get the payload
if (! SD_API_SUCCESS(SDRecv(pBuffer, pcbBuffer))) {
fRetVal = FALSE;
goto exit;
}
#if defined (BT_USE_CELOG)
if (g_Data.fCeLog) {
CELOGDATAFLAGGED(TRUE, CELID_RAW_UCHAR, pBuffer, *pcbBuffer, 0, CELZONE_ALWAYSON, CELOG_FLAG_RAW_IN);
}
#endif
// Acknowledge the packet
ucRegVal = 0x00;
if (! SD_API_SUCCESS(SDSetRegister(REG_PCRRT, m_ucFunction, &ucRegVal, 1))) {
fRetVal = FALSE;
goto exit;
}
exit:
m_refIO.DelRef();
return fRetVal;
}
/*
This method just calls down to SDSend to write packet to SDIO card.
*/
BOOL CSdioDevice::WritePacket(unsigned char* pBuffer, int cbBuffer)
{
BOOL fRetVal = TRUE;
m_refIO.AddRef();
if (m_dwState != ATTACHED) {
IFDBG(DebugOut (DEBUG_HCI_TRANSPORT, L"[SDIO] CSdioDevice::WritePacket - currently not attached, aborting.\n"));
fRetVal = FALSE;
goto exit;
}
fRetVal = SD_API_SUCCESS(SDSend(pBuffer, cbBuffer));
exit:
m_refIO.DelRef();
return fRetVal;
}
/*
This is a static method which just calls a method on the object passed in pContext.
*/
SD_API_STATUS CSdioDevice::SDIOIsrCallBack(SD_DEVICE_HANDLE hDevice, PVOID pContext)
{
CSdioDevice* pInst = (CSdioDevice*) pContext;
return pInst->SDIOIsrCallback_Int();
}
/*
This method is called when an interrupt occurs. It checks if data is ready to
be transferred from the card and if so, sets an event to indicate this.
*/
SD_API_STATUS CSdioDevice::SDIOIsrCallback_Int(void)
{
SD_API_STATUS status = SD_API_STATUS_SUCCESS;
UCHAR ucRegValue;
m_refIO.AddRef();
if (m_dwState != ATTACHED) {
ASSERT(0); // not attached
goto exit;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -