www.pudn.com > WINCEOS.zip > bthid.cxx
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/**
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
Abstract:
Windows CE Bluetooth stack layer sample
**/
#include
#include
#include
#include
#include
#include
#include
#if defined (UNDER_CE)
#include
#endif
#include
#include
#include "bthidpriv.h"
#include "hidpkt.h"
#include "hidsdp.h"
#include "bthid.h"
#include "hidconf.h"
#define DEBUG_LAYER_TRACE 0x00000040
DECLARE_DEBUG_VARS();
#define BTH_PSM_CONTROL 0x11
#define BTH_PSM_INTERRUPT 0x13
#define BTH_MTU_MIN 48
#define BTH_MTU_MAX 672
#define BTH_MAX_FRAGMENTS 256
#define BTH_PARSER_TIMEOUT 3600000
#define BTH_CONNECTION_TIMEOUT 120000
#define BTH_WIRE_TIMEOUT 5000
#define CALL_L2CAP_LINK_SETUP 0x11
#define NONE 0x00
#define CONNECTED 0x01
#define CONFIG_REQ_DONE 0x02
#define CONFIG_IND_DONE 0x04
#define UP 0x07
#define LINK_ERROR 0x80
#define HID_RECONNECT_INTERVAL 20000
#ifdef DEBUG
// Need this for hidmdd module
DBGPARAM dpCurSettings = {
_T("BTHHID"),
{
_T("Errors"), _T("Warnings"), _T("Init"), _T("Function"),
_T("HID Data"), _T("Comments"), _T(""), _T(""),
_T("Parsing"), _T(""), _T(""), _T(""),
_T(""), _T(""), _T(""), _T("BTH Client")
},
0x3
};
#endif
struct Link {
Link *pNext;
BD_ADDR b;
unsigned short psm;
unsigned short cid;
unsigned int fStage;
unsigned int fIncoming : 1;
unsigned short mtu;
};
struct SCall {
SCall *pNext;
Link *pLink;
HANDLE hEvent;
int iResult;
unsigned short psm;
unsigned int fWhat : 8;
unsigned int fComplete : 1;
unsigned int fAutoClean : 1;
unsigned char l2cap_id;
BD_BUFFER *pBuffer;
};
struct HidDevice : public IBTHHIDReportHandler {
HidDevice *pNext;
BD_ADDR b;
union {
struct {
// BTH
unsigned int fEncrypt : 1;
unsigned int fAuthenticate : 1;
unsigned int fHaveControl : 1;
unsigned int fHaveInterrupt : 1;
// HID
unsigned int fReconnectInitiate : 1;
unsigned int fNormallyConnectable : 1;
unsigned int fVirtualCable : 1;
// Service
unsigned int fIncoming : 1;
unsigned int fAuthSpinned : 1;
unsigned int fTrans : 1;
};
unsigned int flags;
};
HANDLE hevtTransFree;
HANDLE hevtTrans;
int cTransWait;
BTHHIDPacket *pktTransResp;
BTHHIDPacket *pktControl;
BTHHIDPacket *pktInterrupt;
SVSCookie ckDeviceTimeout;
SVSCookie ckConnectionTimeout;
PVOID pvNotifyParameter;
BLOB blobReportDescriptor;
HidDevice (BD_ADDR *pba) {
pNext = 0;
b = *pba;
flags = 0;
hevtTrans = CreateEvent (NULL, FALSE, FALSE, NULL);
hevtTransFree = CreateEvent (NULL, TRUE, FALSE, NULL);
cTransWait = 0;
pktTransResp = pktControl = pktInterrupt = NULL;
ckDeviceTimeout = 0;
ckConnectionTimeout = 0;
pvNotifyParameter = NULL;
memset(&blobReportDescriptor, 0, sizeof(BLOB));
}
~HidDevice (void) {
SetEvent (hevtTransFree);
CloseHandle (hevtTransFree);
SetEvent (hevtTrans);
CloseHandle (hevtTrans);
if (pvNotifyParameter)
HidMdd_Notifications(HID_MDD_CLOSE_DEVICE, 0, pvNotifyParameter);
if (pktTransResp) {
pktTransResp->ReleasePayload ();
delete pktTransResp;
}
if (pktControl) {
pktControl->ReleasePayload ();
delete pktControl;
}
if (pktInterrupt) {
pktInterrupt->ReleasePayload ();
delete pktInterrupt;
}
if (blobReportDescriptor.pBlobData)
g_funcFree (blobReportDescriptor.pBlobData, g_pvFreeData);
}
void *operator new (size_t iSize);
void operator delete(void *ptr);
int FillPersistentParameters (int fIncoming);
// IBTHHIDReportHandler overrides
int SetIdle(unsigned char bIdle);
int GetIdle(unsigned char* pbIdle);
int SetProtocol(E_BTHID_PROTOCOLS protocol);
int GetReport(int iReportID, E_BTHID_REPORT_TYPES type, PCHAR pBuffer, int cbBuffer, PDWORD pcbTransfered, int iTimeout);
int SetReport(E_BTHID_REPORT_TYPES type, PCHAR pBuffer, int cbBuffer, int iTimeout);
};
int hiddev_CloseDriverInstance (void);
static int hiddev_ConfigInd (void *pUserContext, unsigned char id, unsigned short cid, unsigned short usOutMTU, unsigned short usInFlushTO, struct btFLOWSPEC *pInFlow, int cOptNum, struct btCONFIGEXTENSION **pExtendedOptions);
static int hiddev_ConnectInd (void *pUserContext, BD_ADDR *pba, unsigned short cid, unsigned char id, unsigned short psm);
static int hiddev_DataUpInd (void *pUserContext, unsigned short cid, BD_BUFFER *pBuffer);
static int hiddev_DisconnectInd (void *pUserContext, unsigned short cid, int iError);
static int hiddev_lStackEvent (void *pUserContext, int iEvent, void *pEventContext);
static int hiddev_lCallAborted (void *pCallContext, int iError);
static int hiddev_ConfigReq_Out (void *pCallContext, unsigned short usResult, unsigned short usInMTU, unsigned short usOutFlushTO, struct btFLOWSPEC *pOutFlow, int cOptNum, struct btCONFIGEXTENSION **pExtendedOptions);
static int hiddev_ConfigResponse_Out (void *pCallContext, unsigned short result);
static int hiddev_ConnectReq_Out (void *pCallContext, unsigned short cid, unsigned short result, unsigned short status);
static int hiddev_ConnectResponse_Out (void *pCallContext, unsigned short result);
static int hiddev_DataDown_Out (void *pCallContext, unsigned short result);
static int hiddev_Ping_Out (void *pCallContext, BD_ADDR *pba, unsigned char *pOutBuffer, unsigned short size);
static int hiddev_Disconnect_Out (void *pCallContext, unsigned short result);
#if defined (BTHHID_QUEUE)
static DWORD WINAPI HidReportHandler (LPVOID lpNull);
#endif
static int WritePacket (void *lpHidDevice, BTHHIDPacket *pSource, int iTimeout, BTHHIDPacket **ppRes, int iChannel);
static int HIDConnect_Int(BD_ADDR *pba, unsigned short usPSM, unsigned short *pusCid);
static int HIDCloseCID_Int (unsigned short usCID);
class HIDDEV : public SVSSynch, public SVSRefObj {
public:
Link *pLinks;
SCall *pCalls;
HidDevice *pHIDs;
unsigned int fIsRunning : 1;
unsigned int fConnected : 1;
HANDLE hL2CAP;
L2CAP_INTERFACE l2cap_if;
int cHeaders;
int cTrailers;
FixedMemDescr *pfmdLinks;
FixedMemDescr *pfmdCalls;
FixedMemDescr *pfmdHIDs;
#if defined (BTHHID_QUEUE)
HANDLE hthReports;
SVSQueue qHidReports;
HANDLE hevtReports;
#endif
HANDLE hthReconnect;
HANDLE hReconnectEvent;
SVSThreadPool *pSchedule;
HIDDEV (void) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: new HIDDEV\n"));
pLinks = NULL;
pCalls = NULL;
pHIDs = NULL;
cHeaders = 0;
cTrailers = 0;
fIsRunning = FALSE;
fConnected = FALSE;
hL2CAP = NULL;
memset (&l2cap_if, 0, sizeof(l2cap_if));
pfmdLinks = pfmdCalls = pfmdHIDs = NULL;
pSchedule = NULL;
if (! (hReconnectEvent = CreateEvent (NULL, FALSE, FALSE, NULL)))
return;
#if defined (BTHHID_QUEUE)
hthReports = NULL;
hevtReports = NULL;
if (! qHidReports.IsQueueInitialized())
return;
if (! (hevtReports = CreateEvent (NULL, FALSE, FALSE, NULL)))
return;
#endif
if (! (pSchedule = new SVSThreadPool))
return;
if (! (pfmdLinks = svsutil_AllocFixedMemDescr (sizeof(Link), 10)))
return;
if (! (pfmdHIDs = svsutil_AllocFixedMemDescr (sizeof(HidDevice), 10)))
return;
if (! (pfmdCalls = svsutil_AllocFixedMemDescr (sizeof(SCall), 10)))
return;
L2CAP_EVENT_INDICATION lei;
memset (&lei, 0, sizeof(lei));
lei.l2ca_ConfigInd = hiddev_ConfigInd;
lei.l2ca_ConnectInd = hiddev_ConnectInd;
lei.l2ca_DataUpInd = hiddev_DataUpInd;
lei.l2ca_DisconnectInd = hiddev_DisconnectInd;
lei.l2ca_StackEvent = hiddev_lStackEvent;
L2CAP_CALLBACKS lc;
memset (&lc, 0, sizeof(lc));
lc.l2ca_CallAborted = hiddev_lCallAborted;
lc.l2ca_ConfigReq_Out = hiddev_ConfigReq_Out;
lc.l2ca_ConfigResponse_Out = hiddev_ConfigResponse_Out;
lc.l2ca_ConnectReq_Out = hiddev_ConnectReq_Out;
lc.l2ca_ConnectResponse_Out = hiddev_ConnectResponse_Out;
lc.l2ca_DataDown_Out = hiddev_DataDown_Out;
lc.l2ca_Ping_Out = hiddev_Ping_Out;
lc.l2ca_Disconnect_Out = hiddev_Disconnect_Out;
if (ERROR_SUCCESS != L2CAP_EstablishDeviceContext (this, L2CAP_PSM_MULTIPLE, &lei, &lc, &l2cap_if, &cHeaders, &cTrailers, &hL2CAP))
return;
int iRes = ERROR_SUCCESS;
__try {
int iRet = 0;
unsigned short usPSMin = BTH_PSM_CONTROL;
iRes = l2cap_if.l2ca_ioctl (hL2CAP, BTH_STACK_IOCTL_RESERVE_PORT, sizeof(usPSMin), (char *)&usPSMin, 0, NULL, &iRet);
if (iRes == ERROR_SUCCESS) {
usPSMin = BTH_PSM_INTERRUPT;
iRes = l2cap_if.l2ca_ioctl (hL2CAP, BTH_STACK_IOCTL_RESERVE_PORT, sizeof(usPSMin), (char *)&usPSMin, 0, NULL, &iRet);
}
} __except (1) {
iRes = ERROR_EXCEPTION_IN_SERVICE;
}
if (iRes == ERROR_SUCCESS)
fIsRunning = TRUE;
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: new HIDDEV successful\n"));
}
~HIDDEV (void) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: delete HIDDEV\n"));
fIsRunning = FALSE;
fConnected = FALSE;
#if defined (BTHHID_QUEUE)
if (hevtReports) {
HANDLE h = hevtReports;
hevtReports = NULL;
SetEvent (h);
CloseHandle (h);
}
if (hthReports) {
if (WAIT_OBJECT_0 != WaitForSingleObject (hthReports, 15000))
TerminateThread (hthReports, 0);
CloseHandle (hthReports);
}
#endif
CloseHandle (hReconnectEvent);
if (pSchedule)
delete pSchedule;
if (hL2CAP)
L2CAP_CloseDeviceContext (hL2CAP);
if (pfmdHIDs)
svsutil_ReleaseFixedNonEmpty (pfmdHIDs);
if (pfmdLinks)
svsutil_ReleaseFixedNonEmpty (pfmdLinks);
if (pfmdCalls)
svsutil_ReleaseFixedNonEmpty (pfmdCalls);
}
};
static HIDDEV *gpState = NULL;
static BYTE gbUnplugHeader = 0;
//
// Auxiliary code
//
void *HidDevice::operator new (size_t iSize) {
SVSUTIL_ASSERT (iSize == sizeof(HidDevice));
void *pRes = svsutil_GetFixed (gpState->pfmdHIDs);
return pRes;
}
void HidDevice::operator delete(void *ptr) {
svsutil_FreeFixed (ptr, gpState->pfmdHIDs);
}
static HIDDEV *CreateNewState (void) {
return new HIDDEV;
}
static SCall *AllocCall (int fWhat, Link *pLink) {
SCall *pCall = (SCall *)svsutil_GetFixed (gpState->pfmdCalls);
if (! pCall)
return NULL;
memset (pCall, 0, sizeof(*pCall));
pCall->pLink = pLink;
pCall->fWhat = fWhat;
pCall->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (NULL == pCall->hEvent) {
svsutil_FreeFixed (pCall, gpState->pfmdCalls);
return NULL;
}
if (! gpState->pCalls)
gpState->pCalls = pCall;
else {
SCall *pLast = gpState->pCalls;
while (pLast->pNext)
pLast = pLast->pNext;
pLast->pNext = pCall;
}
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Allocated call 0x%08x what = 0x%02x\n", pCall, fWhat));
return pCall;
}
static void DeleteCall (SCall *pCall) {
if (pCall == gpState->pCalls)
gpState->pCalls = pCall->pNext;
else {
SCall *pParent = gpState->pCalls;
while (pParent && (pParent->pNext != pCall))
pParent = pParent->pNext;
if (! pParent) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: call not in list in DeleteCall!\n"));
return;
}
pParent->pNext = pCall->pNext;
}
CloseHandle (pCall->hEvent);
svsutil_FreeFixed (pCall, gpState->pfmdCalls);
}
static SCall *VerifyCall (SCall *pCall) {
SCall *p = gpState->pCalls;
while (p && (p != pCall))
p = p->pNext;
return p;
}
static SCall *FindCall (unsigned int fOp) {
SCall *p = gpState->pCalls;
while (p && (p->fWhat != fOp))
p = p->pNext;
return p;
}
static SCall *FindCall (Link *pLink, unsigned int fOp) {
SCall *p = gpState->pCalls;
while (p && ((p->pLink != pLink) || (p->fWhat != fOp)))
p = p->pNext;
return p;
}
static HidDevice *VerifyDevice (HidDevice *pDev) {
HidDevice *pD = gpState->pHIDs;
while (pD && (pD != pDev))
pD = pD->pNext;
return pD;
}
static HidDevice *FindDevice (BD_ADDR *pba) {
HidDevice *pD = gpState->pHIDs;
while (pD && (pD->b != *pba))
pD = pD->pNext;
return pD;
}
static void DeleteDevice (HidDevice *pDev) {
if (pDev == gpState->pHIDs)
gpState->pHIDs = pDev->pNext;
else {
HidDevice *p = gpState->pHIDs;
while (p->pNext != pDev)
p = p->pNext;
p->pNext = pDev->pNext;
}
delete pDev;
// Signal Reconnect thread that a device has been deleted
SetEvent(gpState->hReconnectEvent);
}
static Link *VerifyLink (Link *pLink) {
Link *p = gpState->pLinks;
while (p && (p != pLink))
p = p->pNext;
return p;
}
static Link *FindLink (BD_ADDR *pba) {
Link *pL = gpState->pLinks;
while (pL && (pL->b != *pba))
pL = pL->pNext;
return pL;
}
static Link *FindLink (BD_ADDR *pba, unsigned short psm) {
Link *pL = gpState->pLinks;
while (pL && ((pL->b != *pba) || (pL->psm != psm)))
pL = pL->pNext;
return pL;
}
static Link *FindLink (unsigned short cid) {
Link *p = gpState->pLinks;
while (p && (p->cid != cid))
p = p->pNext;
return p;
}
static void DisconnectDevice (HidDevice *pDev, BOOL fSendVCD) {
for ( ; ; ) {
Link *pLink = FindLink (&pDev->b);
if (! pLink)
return;
if (fSendVCD && pDev->fVirtualCable) {
// Send VIRTUAL_CABLE_DISCONNECT
BTHHIDPacket packet;
packet.SetHeader(gbUnplugHeader);
(void) WritePacket (pDev, &packet, 0, NULL, BTH_PSM_CONTROL);
}
HIDCloseCID_Int (pLink->cid);
}
}
static DWORD WINAPI HidReconnectThread (LPVOID lpArg) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Started reconnect thread\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
while (1) {
DWORD dwTimeout;
if (! gpState->fIsRunning) {
break;
}
HANDLE h = gpState->hReconnectEvent;
if (gpState->pHIDs)
dwTimeout = HID_RECONNECT_INTERVAL;
else
dwTimeout = INFINITE;
gpState->AddRef ();
gpState->Unlock ();
DWORD dwWait = WaitForSingleObject(h, dwTimeout);
gpState->Lock ();
gpState->DelRef ();
if (WAIT_TIMEOUT == dwWait) {
HidDevice *pD = gpState->pHIDs;
while (pD) {
if ((!pD->fHaveControl || !pD->fHaveInterrupt) && !pD->fReconnectInitiate && pD->fNormallyConnectable) {
int iRes = ERROR_SUCCESS;
unsigned short cidControl = 0;
if (!pD->fHaveControl) {
gpState->AddRef ();
gpState->Unlock ();
iRes = HIDConnect_Int (&pD->b, BTH_PSM_CONTROL, &cidControl);
gpState->Lock ();
gpState->DelRef ();
}
if (iRes == ERROR_SUCCESS) {
unsigned short cidInterrupt = 0;
if (!pD->fHaveInterrupt) {
gpState->AddRef ();
gpState->Unlock ();
iRes = HIDConnect_Int (&pD->b, BTH_PSM_INTERRUPT, &cidInterrupt);
if (iRes != ERROR_SUCCESS) {
if (cidControl)
HIDCloseCID_Int (cidControl);
}
gpState->Lock ();
gpState->DelRef ();
}
}
}
pD = pD->pNext;
}
}
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
static DWORD WINAPI DeviceTimeout (LPVOID lpArg) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Timeout on hid device\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
HidDevice *pDev = VerifyDevice ((HidDevice *)lpArg);
if (pDev && pDev->ckDeviceTimeout && (! FindLink (&pDev->b))) {
DeleteDevice(pDev);
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
static DWORD WINAPI ConnectionTimeout (LPVOID lpArg) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Connection Timeout on hid device\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
HidDevice *pDev = VerifyDevice ((HidDevice *)lpArg);
if (pDev && pDev->ckConnectionTimeout && (! (pDev->fHaveControl && pDev->fHaveInterrupt))) {
pDev->ckConnectionTimeout = 0;
DisconnectDevice (pDev, TRUE);
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
static void DeleteLink (Link *pLink) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: delete link for bd_addr %04x%08x cid 0x%04x\n", pLink->b.NAP, pLink->b.SAP, pLink->cid));
if (pLink == gpState->pLinks)
gpState->pLinks = pLink->pNext;
else {
Link *pParent = gpState->pLinks;
while (pParent && (pParent->pNext != pLink))
pParent = pParent->pNext;
if (! pParent) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: link to be deleted not in list\n"));
return;
}
pParent->pNext = pLink->pNext;
}
SCall *pC = gpState->pCalls;
while (pC) {
if (pC->pLink == pLink) {
if (pC->fAutoClean) {
DeleteCall (pC);
pC = gpState->pCalls;
continue;
} else if (! pC->fComplete) {
pC->pLink = NULL;
pC->fComplete = TRUE;
pC->iResult = ERROR_CONNECTION_UNAVAIL;
SetEvent (pC->hEvent);
} else {
pC->pLink = NULL;
if (pC->iResult == ERROR_SUCCESS)
pC->iResult = ERROR_CONNECTION_UNAVAIL;
}
}
pC = pC->pNext;
}
HidDevice *pDev = FindDevice (&pLink->b);
if (pDev) {
if (pLink->psm == BTH_PSM_CONTROL)
pDev->fHaveControl = FALSE;
else
pDev->fHaveInterrupt = FALSE;
if (! pDev->ckDeviceTimeout)
pDev->ckDeviceTimeout = gpState->pSchedule->ScheduleEvent (DeviceTimeout, pDev, BTH_PARSER_TIMEOUT);
}
svsutil_FreeFixed (pLink, gpState->pfmdLinks);
}
static void HID_BufferFree (BD_BUFFER *pBuf) {
if (! pBuf->fMustCopy)
g_funcFree (pBuf, g_pvFreeData);
}
static void HID_BufferFreeArray (BD_BUFFER **ppArray, int carray) {
for (int i = 0 ; i < carray ; ++i)
HID_BufferFree (ppArray[i]);
}
static BD_BUFFER *HID_BufferAlloc (int cSize) {
SVSUTIL_ASSERT (cSize > 0);
BD_BUFFER *pRes = (BD_BUFFER *)g_funcAlloc (cSize + sizeof (BD_BUFFER), g_pvAllocData);
pRes->cSize = cSize;
pRes->cEnd = pRes->cSize;
pRes->cStart = 0;
pRes->fMustCopy = FALSE;
pRes->pFree = HID_BufferFree;
pRes->pBuffer = (unsigned char *)(pRes + 1);
return pRes;
}
static BD_BUFFER *HID_BufferCopy (BD_BUFFER *pBuffer) {
BD_BUFFER *pRes = HID_BufferAlloc (pBuffer->cSize);
pRes->cSize = pBuffer->cSize;
pRes->cStart = pBuffer->cStart;
pRes->cEnd = pBuffer->cEnd;
pRes->fMustCopy = FALSE;
pRes->pFree = HID_BufferFree;
pRes->pBuffer = (unsigned char *)(pRes + 1);
memcpy (pRes->pBuffer, pBuffer->pBuffer, pRes->cSize);
return pRes;
}
static DWORD WINAPI StackDown (LPVOID lpVoid) { // Attention - must increment ref count before calling this!
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Stack Down\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
gpState->DelRef ();
if ((! gpState->fIsRunning) || (! gpState->fConnected)) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
gpState->fConnected = FALSE;
Link *pLink = gpState->pLinks;
while (gpState->pLinks)
DeleteLink (gpState->pLinks);
SCall *pC = gpState->pCalls;
while (pC) {
if (pC->fAutoClean) {
DeleteCall (pC);
pC = gpState->pCalls;
continue;
} else if (! pC->fComplete) {
pC->pLink = NULL;
pC->fComplete = TRUE;
pC->iResult = ERROR_CONNECTION_UNAVAIL;
SetEvent (pC->hEvent);
} else {
pC->pLink = NULL;
if (pC->iResult == ERROR_SUCCESS)
pC->iResult = ERROR_CONNECTION_UNAVAIL;
}
pC = pC->pNext;
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
static DWORD WINAPI StackUp (LPVOID lpVoid) { // Attention - must increment ref count before calling this!
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Stack Up\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
gpState->DelRef ();
if ((! gpState->fIsRunning) || gpState->fConnected) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
gpState->fConnected = TRUE;
gpState->Unlock ();
return ERROR_SUCCESS;
}
static HidDevice *MakeNewDevice (BD_ADDR *pba, int fIncoming) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: MakeNewDevice %04x%08x\n", pba->NAP, pba->SAP));
HidDevice *pDev = new HidDevice (pba);
if (! pDev)
return NULL;
if (! pDev->FillPersistentParameters (fIncoming)) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: MakeNewDevice %04x%08x fails to retrieve pairing information\n", pba->NAP, pba->SAP));
delete pDev;
return NULL;
}
pDev->pNext = gpState->pHIDs;
gpState->pHIDs = pDev;
pDev->fIncoming = fIncoming;
// Signal Reconnect thread that we added a device.
SetEvent(gpState->hReconnectEvent);
return pDev;
}
static DWORD WINAPI AuthenticateDevice (LPVOID lpArg) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: authentication request on hid device\n"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
SCall *pCall = VerifyCall ((SCall *)lpArg);
HidDevice *pDev = pCall ? FindDevice (&pCall->pLink->b) : NULL;
if (! pDev) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: authentication request on nonexistent hid device\n"));
gpState->Unlock ();
return ERROR_SUCCESS;
}
int fAuth = pDev->fAuthenticate;
int fEncr = pDev->fEncrypt;
int fIncoming = pDev->fIncoming;
unsigned char id = pCall->l2cap_id;
unsigned short cid = pCall->pLink->cid;
BD_ADDR b = pDev->b;
BT_ADDR bt = SET_NAP_SAP (b.NAP, b.SAP);
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: authentication request on hid device %04x%08x cid 0x%04x auth: %d, encr: %d, incoming: %d\n", b.NAP, b.SAP, fAuth, fEncr, fIncoming));
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_ConfigReq_In pCallbackConfig = gpState->l2cap_if.l2ca_ConfigReq_In;
L2CA_ConnectResponse_In pCallbackConnect = gpState->l2cap_if.l2ca_ConnectResponse_In;
gpState->Unlock ();
if (fIncoming) {
__try {
pCallbackConnect (hL2CAP, NULL, &b, id, cid, 1, 1);
} __except (1) {
}
}
int iErr = ERROR_SUCCESS;
if (fAuth)
iErr = BthAuthenticate (&bt);
if (fEncr && (iErr == ERROR_SUCCESS))
iErr = BthSetEncryption (&bt, TRUE);
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
pDev = VerifyDevice (pDev);
if (! pDev) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: authentication request : device disappeared enroute\n"));
gpState->Unlock ();
return ERROR_SUCCESS;
}
gpState->Unlock ();
if (fIncoming) {
unsigned short result = 0;
if (iErr != ERROR_SUCCESS)
result = 3;
__try {
pCallbackConnect (hL2CAP, NULL, &b, id, cid, result, 0);
} __except (1) {
}
if (result == 0) {
iErr = ERROR_INTERNAL_ERROR;
__try {
iErr = pCallbackConfig (hL2CAP, pCall, cid, BTH_MTU_MAX, 0xffff, NULL, 0, NULL);
} __except (1) {
}
}
if (iErr != ERROR_SUCCESS)
hiddev_lCallAborted (pCall, iErr);
} else {
if (iErr == ERROR_SUCCESS) {
iErr = ERROR_INTERNAL_ERROR;
__try {
iErr = pCallbackConfig (hL2CAP, pCall, cid, BTH_MTU_MAX, 0xffff, NULL, 0, NULL);
} __except (1) {
}
} else
HIDCloseCID_Int (cid);
if (iErr != ERROR_SUCCESS)
hiddev_lCallAborted (pCall, iErr);
}
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: authentication request completes with status %d\n", iErr));
return ERROR_SUCCESS;
}
static int WritePacket (void *lpHidDevice, BTHHIDPacket *pSource, int iTimeout, BTHHIDPacket **ppRes, int iChannel) {
int iRes = ERROR_SUCCESS;
for ( ; ; ) {
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
HidDevice *pDev = VerifyDevice ((HidDevice *)lpHidDevice);
if (! pDev) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
HANDLE hevtComplete = pDev->hevtTransFree;
if (pDev->fTrans) { // We don't have the transaction
gpState->Unlock ();
if (iTimeout == 0)
return ERROR_TIMEOUT;
DWORD dwTicks = GetTickCount ();
WaitForSingleObject (hevtComplete, iTimeout);
if (iTimeout != INFINITE) {
iTimeout -= GetTickCount () - dwTicks;
if (iTimeout < 0)
iTimeout = 0;
}
continue;
}
Link *pLink = FindLink (&pDev->b, iChannel);
if (! pLink) {
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
BD_BUFFER *frags[BTH_MAX_FRAGMENTS];
int cfrags = 0;
pSource->SetMTU(pLink->mtu);
for (cfrags = 0 ; cfrags < BTH_MAX_FRAGMENTS ; ++cfrags) {
int cChunk = 0;
pSource->GetPayloadChunk (NULL, 0, &cChunk);
frags[cfrags] = HID_BufferAlloc (cChunk + gpState->cHeaders + gpState->cTrailers);
if (! frags[cfrags]) {
HID_BufferFreeArray (frags, cfrags);
gpState->Unlock ();
return ERROR_OUTOFMEMORY;
}
frags[cfrags]->cStart = gpState->cHeaders;
frags[cfrags]->cEnd = frags[cfrags]->cSize - gpState->cTrailers;
if (! pSource->GetPayloadChunk(frags[cfrags]->pBuffer + frags[cfrags]->cStart, cChunk, &cChunk))
break;
}
if (cfrags == BTH_MAX_FRAGMENTS) {
HID_BufferFreeArray (frags, cfrags);
gpState->Unlock ();
return ERROR_OUTOFMEMORY;
}
++cfrags;
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_DataDown_In pCallback = gpState->l2cap_if.l2ca_DataDown_In;
unsigned short usCID = pLink->cid;
HANDLE hevt = pDev->hevtTrans;
pDev->fTrans = TRUE; // We own the transaction
ResetEvent (hevtComplete);
gpState->Unlock ();
iRes = ERROR_SUCCESS;
int iSent = 0;
__try {
for (iSent = 0 ; iSent < cfrags ; ++iSent) {
if (ERROR_SUCCESS != (iRes = pCallback (hL2CAP, NULL, usCID, frags[iSent])))
break;
}
} __except (1) {
iRes = ERROR_EXCEPTION_IN_SERVICE;
}
if (iRes == ERROR_SUCCESS)
WaitForSingleObject (hevt, iTimeout);
if (! gpState) {
SetEvent (hevtComplete);
return ERROR_SERVICE_NOT_ACTIVE;
}
gpState->Lock ();
if (iRes != ERROR_SUCCESS)
HID_BufferFreeArray (frags + iSent, cfrags - iSent);
SetEvent (hevtComplete);
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
if (! VerifyDevice (pDev)) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
BTHHIDPacket *pPacket = pDev->pktTransResp;
pDev->pktTransResp = NULL;
pDev->fTrans = FALSE; // Clear the transaction
if (! pPacket) {
gpState->Unlock ();
return iRes == ERROR_SUCCESS ? ERROR_TIMEOUT : iRes;
}
if (ppRes)
*ppRes = pPacket;
else {
pPacket->ReleasePayload ();
delete pPacket;
}
gpState->Unlock ();
break;
}
return iRes;
}
int HidDevice::FillPersistentParameters (int fIncoming) {
DWORD dwDeviceFlags = 0;
unsigned char *psdp = NULL;
unsigned int csdp = 0;
if (! GetDeviceConfig (&b, &dwDeviceFlags, &psdp, &csdp)) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: FillPersistentParameters %04x%08x - bonding does not exist\n", b.NAP, b.SAP));
return FALSE;
}
if (fIncoming && ((dwDeviceFlags & HIDCONF_FLAGS_ACTIVE) == 0)) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: FillPersistentParameters %04x%08x - device is not activated\n", b.NAP, b.SAP));
g_funcFree (psdp, g_pvFreeData);
return FALSE;
}
if (dwDeviceFlags & HIDCONF_FLAGS_AUTH)
fAuthenticate = TRUE;
if (dwDeviceFlags & HIDCONF_FLAGS_ENCRYPT)
fEncrypt = TRUE;
BTHHIDSdpParser sdpParser;
int iErr = sdpParser.Start (psdp, csdp);
BOOL bb = FALSE;
if (iErr == ERROR_SUCCESS)
iErr = sdpParser.GetHIDReconnectInitiate(&bb);
if (iErr == ERROR_SUCCESS) {
fReconnectInitiate = bb;
iErr = sdpParser.GetHIDNormallyConnectable(&bb);
if (iErr == ERROR_SUCCESS)
fNormallyConnectable = bb;
iErr = ERROR_SUCCESS;
}
if (iErr == ERROR_SUCCESS) {
iErr = sdpParser.GetHIDVirtualCable(&bb);
}
if (iErr == ERROR_SUCCESS) {
fVirtualCable = bb;
iErr = sdpParser.GetHIDReportDescriptor(&blobReportDescriptor);
}
if (iErr == ERROR_SUCCESS) {
// Now we have the size. Allocate and requery.
SVSUTIL_ASSERT(blobReportDescriptor.cbSize > 0);
blobReportDescriptor.pBlobData = (unsigned char *)g_funcAlloc (blobReportDescriptor.cbSize, g_pvAllocData);
iErr = sdpParser.GetHIDReportDescriptor(&blobReportDescriptor);
}
sdpParser.End ();
if (iErr != ERROR_SUCCESS) {
IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: FillPersistentParameters %04x%08x - parsing SDP record failed\n", b.NAP, b.SAP));
}
g_funcFree (psdp, g_pvFreeData);
return iErr == ERROR_SUCCESS;
}
static int ProcessHidPacket (BTHHIDPacket *pPacket, HidDevice *pDev) {
if (pPacket->GetReportType () == BTHID_REPORT_INPUT) {
BYTE* pPayload;
int cbPayload;
if (pDev->pvNotifyParameter) {
pPacket->GetPayload(&pPayload, &cbPayload);
if (ERROR_SUCCESS != HidMdd_ProcessInterruptReport(pPayload, cbPayload, pDev->pvNotifyParameter)) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID ProcessHidPacket : Error processing interrupt report - disconnecting device.\n"));
DisconnectDevice (pDev, TRUE);
}
}
} else {
if (pPacket->GetHeader() == gbUnplugHeader) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID ReportHandler : virtual disconnect\n"));
DisconnectDevice (pDev, FALSE);
} else {
if (! pDev->fTrans) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH Device: protocol error - control packet w/o transaction. Aborting...\n"));
DisconnectDevice (pDev, TRUE);
} else {
if (pDev->pktTransResp) { // Transaction violated
IFDBG(DebugOut (DEBUG_ERROR, L"BTH Device: protocol error - multiple control packets for transaction. Aborting...\n"));
DisconnectDevice (pDev, TRUE);
} else {
pDev->pktTransResp = pPacket;
SetEvent (pDev->hevtTrans);
return FALSE; // Don't destroy the packet
}
}
}
}
return TRUE;
}
//
// L2CAP stuff
//
static int hiddev_DataUpInd (void *pUserContext, unsigned short cid, BD_BUFFER *pBuffer) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Data up on channel 0x%04x %d bytes\n", cid, BufferTotal (pBuffer)));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
Link *pLink = FindLink (cid);
HidDevice *pDev = pLink ? FindDevice (&pLink->b) : NULL;
BTHHIDPacket *pPacket = pDev ? (pLink->psm == BTH_PSM_CONTROL ? pDev->pktControl : pDev->pktInterrupt) : NULL;
if (pDev && (! pPacket)) {
pPacket = new BTHHIDPacket;
if (pPacket) {
pPacket->SetMTU (pLink->mtu);
pPacket->SetOwner (pDev);
// Store this packet away. In some cases, we will need to buffer up
// several fragments before passing data up to HID layer.
if (pLink->psm == BTH_PSM_CONTROL) {
pPacket->SetReportType(BTHID_REPORT_FEATURE);
pDev->pktControl = pPacket;
} else {
pPacket->SetReportType(BTHID_REPORT_INPUT);
pDev->pktInterrupt = pPacket;
}
}
}
#if defined (DEBUG) || defined (_DEBUG)
if (pPacket) {
SVSUTIL_ASSERT (pPacket->GetOwner() == pDev);
} else {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: Data up - no packet\n"));
}
#endif
if (pPacket) {
// If this function returns 0 we can indicate the packet up to HID layer. Otherwise, we have
// to recv more data before indicating up.
if (! pPacket->AddPayloadChunk(pBuffer->pBuffer + pBuffer->cStart, BufferTotal (pBuffer))) {
if (pLink->psm == BTH_PSM_CONTROL)
pDev->pktControl = NULL;
else
pDev->pktInterrupt = NULL;
#if defined (BTHHID_QUEUE)
if (! gpState->qHidReports.Put (pPacket)) {
pPacket->ReleasePayload();
delete pPacket;
}
SetEvent (gpState->hevtReports);
#else
int fRelease = ProcessHidPacket (pPacket, pDev);
if (fRelease) {
pPacket->ReleasePayload();
delete pPacket;
}
#endif
}
}
if (pBuffer->pFree)
pBuffer->pFree (pBuffer);
gpState->Unlock ();
return ERROR_SUCCESS;
}
static int hiddev_DisconnectInd (void *pUserContext, unsigned short cid, int iError) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Disconnect indicator on channel 0x%04x\n", cid));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
Link *pLink = FindLink (cid);
if (! pLink) {
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
SCall *pC = gpState->pCalls;
while (pC) {
if (pC->pLink == pLink) {
if (pC->fAutoClean) {
DeleteCall (pC);
pC = gpState->pCalls;
continue;
} else if (! pC->fComplete) {
pC->fComplete = TRUE;
pC->iResult = ERROR_CONNECTION_UNAVAIL;
SetEvent (pC->hEvent);
}
}
pC = pC->pNext;
}
DeleteLink (pLink);
gpState->Unlock ();
return ERROR_SUCCESS;
}
static int hiddev_lStackEvent (void *pUserContext, int iEvent, void *pEventContext) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Stack event (L2CAP) %d\n", iEvent));
if (! gpState)
return ERROR_SUCCESS;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SUCCESS;
}
LPTHREAD_START_ROUTINE p = NULL;
if (iEvent == BTH_STACK_DISCONNECT)
hiddev_CloseDriverInstance ();
else if (iEvent == BTH_STACK_DOWN)
p = StackDown;
else if (iEvent == BTH_STACK_UP)
p = StackUp;
HANDLE h = p ? CreateThread (NULL, 0, p, NULL, 0, NULL) : NULL;
if (h) {
CloseHandle (h);
gpState->AddRef ();
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
static int hiddev_lCallAborted (void *pCallContext, int iError) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Aborted call 0x%08x error %d\n", pCallContext, iError));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
SCall *pCall = VerifyCall ((SCall *)pCallContext);
if (! pCall) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: Aborted call not found!\n"));
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
SVSUTIL_ASSERT (! pCall->fComplete);
SVSUTIL_ASSERT (! pCall->pBuffer);
Link *pLink = pCall->pLink;
if (pCall->fAutoClean)
DeleteCall (pCall);
else {
pCall->iResult = iError;
pCall->fComplete = TRUE;
pCall->pLink = NULL;
SetEvent (pCall->hEvent);
}
unsigned short disconnect_cid = 0;
if (pLink) {
if ((pLink->fStage & UP) != UP) {
if (pLink->fStage & CONNECTED)
disconnect_cid = pLink->cid;
else
DeleteLink (pLink);
} else {
disconnect_cid = pLink->cid;
pLink->fStage |= LINK_ERROR;
}
}
gpState->Unlock ();
if (disconnect_cid)
HIDCloseCID_Int (disconnect_cid);
return ERROR_SUCCESS;
}
static int hiddev_ConfigInd (void *pUserContext, unsigned char id, unsigned short cid, unsigned short usOutMTU, unsigned short usInFlushTO, struct btFLOWSPEC *pInFlow, int cOptNum, struct btCONFIGEXTENSION **pExtendedOptions) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Config request indicator on ch 0x%04x id %d MTU %d flush 0x%04x, flow: %s\n", cid, id, usOutMTU, usInFlushTO, pInFlow ? L"yes" : L"no"));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
Link *pLink = FindLink (cid);
SCall *pCall = pLink ? FindCall (pLink, CALL_L2CAP_LINK_SETUP) : NULL;
if ((! pCall) || pCall->fComplete) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: config setup call not found!\n"));
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
pCall->l2cap_id = id;
SVSUTIL_ASSERT (pCall->fWhat == CALL_L2CAP_LINK_SETUP);
SVSUTIL_ASSERT (! pCall->fComplete);
SVSUTIL_ASSERT (VerifyLink (pCall->pLink));
SVSUTIL_ASSERT (! pCall->pBuffer);
int fAccept = FALSE;
// if ((usInFlushTO == 0xffff) && (! pInFlow)) {
pCall->iResult = ERROR_SUCCESS;
pCall->pLink->fStage |= CONFIG_IND_DONE;
pCall->pLink->mtu = usOutMTU ? usOutMTU : L2CAP_MTU;
fAccept = TRUE;
if (pLink->fStage == UP) {
pCall->fComplete = TRUE;
pCall->iResult = ERROR_SUCCESS;
SetEvent (pCall->hEvent);
if (pCall->fAutoClean)
DeleteCall (pCall);
HidDevice *pDev = FindDevice (&pLink->b);
if (pDev) {
if (pLink->psm == BTH_PSM_CONTROL)
pDev->fHaveControl = TRUE;
else
pDev->fHaveInterrupt = TRUE;
if (pDev->fHaveControl && pDev->fHaveInterrupt) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: device %04x%08x connected!\n", pDev->b.NAP, pDev->b.SAP));
if (pDev->ckConnectionTimeout) {
gpState->pSchedule->UnScheduleEvent (pDev->ckConnectionTimeout);
pDev->ckConnectionTimeout = 0;
}
} else if (! pDev->ckConnectionTimeout)
pDev->ckConnectionTimeout = gpState->pSchedule->ScheduleEvent (ConnectionTimeout, pDev, BTH_CONNECTION_TIMEOUT);
} else {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: could not find HID device - connection not accepted!\n"));
fAccept = FALSE;
}
}
// } else {
// IFDBG(DebugOut (DEBUG_WARN, L"BTH HID: not accepting config because of flow control parameters.\n"));
// }
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_ConfigResponse_In pCallback = gpState->l2cap_if.l2ca_ConfigResponse_In;
gpState->Unlock ();
__try {
pCallback (hL2CAP, NULL, id, cid, fAccept ? 0 : 2, usOutMTU, 0xffff, NULL, 0, NULL);
} __except (1) {
}
if (! fAccept)
hiddev_lCallAborted (pCall, ERROR_CONNECTION_ABORTED);
return ERROR_SUCCESS;
}
static int hiddev_ConfigReq_Out (void *pCallContext, unsigned short usResult, unsigned short usOutMTU, unsigned short usOutFlushTO, struct btFLOWSPEC *pOutFlow, int cOptNum, struct btCONFIGEXTENSION **pExtendedOptions) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Config req out for call 0x%08x result 0x%04x mtu %d flush 0x%04x, flow %s\n", pCallContext, usResult, usOutMTU, usOutFlushTO, pOutFlow ? L"yes" : L"no" ));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
SCall *pCall = VerifyCall ((SCall *)pCallContext);
if ((! pCall) || pCall->fComplete) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: config setup call not found!\n"));
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
SVSUTIL_ASSERT (pCall->fWhat == CALL_L2CAP_LINK_SETUP);
SVSUTIL_ASSERT (! pCall->fComplete);
SVSUTIL_ASSERT (VerifyLink (pCall->pLink));
SVSUTIL_ASSERT (! pCall->pBuffer);
if (usResult == 0) {
Link *pLink = pCall->pLink;
SVSUTIL_ASSERT (! (pLink->fStage & CONFIG_REQ_DONE));
SVSUTIL_ASSERT (pLink->fStage & CONNECTED);
SVSUTIL_ASSERT (pLink->cid);
SVSUTIL_ASSERT (pLink->psm);
pLink->fStage |= CONFIG_REQ_DONE;
if (usOutMTU)
pLink->mtu = usOutMTU;
if (pLink->fStage == UP) {
pCall->fComplete = TRUE;
pCall->iResult = ERROR_SUCCESS;
SetEvent (pCall->hEvent);
if (pCall->fAutoClean)
DeleteCall (pCall);
HidDevice *pDev = FindDevice (&pLink->b);
if (pDev) {
if (pLink->psm == BTH_PSM_CONTROL)
pDev->fHaveControl = TRUE;
else
pDev->fHaveInterrupt = TRUE;
if (pDev->fHaveControl && pDev->fHaveInterrupt) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: device %04x%08x connected!\n", pDev->b.NAP, pDev->b.SAP));
if (pDev->ckConnectionTimeout) {
gpState->pSchedule->UnScheduleEvent (pDev->ckConnectionTimeout);
pDev->ckConnectionTimeout = 0;
}
} else if (! pDev->ckConnectionTimeout)
pDev->ckConnectionTimeout = gpState->pSchedule->ScheduleEvent (ConnectionTimeout, pDev, BTH_CONNECTION_TIMEOUT);
} else {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: could not find HID device - connection not accepted!\n"));
gpState->Unlock ();
hiddev_lCallAborted (pCallContext, ERROR_CONNECTION_ABORTED);
return ERROR_SUCCESS;
}
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
gpState->Unlock ();
hiddev_lCallAborted (pCallContext, ERROR_CONNECTION_ABORTED);
return ERROR_SUCCESS;
}
static int hiddev_ConnectInd (void *pUserContext, BD_ADDR *pba, unsigned short cid, unsigned char id, unsigned short psm) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Connect indicator from %04x%08x ch 0x%04x id %d psm 0x%04x\n", pba->NAP, pba->SAP, cid, id, psm));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
unsigned short result = 0;
unsigned short status = 0;
HidDevice *pDev = FindDevice (pba);
if (! pDev) {
pDev = MakeNewDevice (pba, TRUE);
if (pDev) {
DWORD cbMaxInputReport;
// Pass the report descriptor off to the MDD
if ((NULL == pDev->pvNotifyParameter) &&
(HidMdd_Attach(
(HID_PDD_HANDLE) pDev,
pDev->blobReportDescriptor.pBlobData,
pDev->blobReportDescriptor.cbSize,
0,
0,
0,
0,
&pDev->pvNotifyParameter,
&cbMaxInputReport) == FALSE)) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: Failed to initialize HID Mdd\n"));
return ERROR_NOT_CONNECTED;
}
}
}
if (! pDev) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: connect indication: could not make device\n"));
result = 4;
} else if (pDev->ckDeviceTimeout) {
gpState->pSchedule->UnScheduleEvent (pDev->ckDeviceTimeout);
pDev->ckDeviceTimeout = 0;
}
SCall *pCall = NULL;
if (pDev) {
Link *pLink = (Link *)svsutil_GetFixed (gpState->pfmdLinks);
pCall = pLink ? AllocCall (CALL_L2CAP_LINK_SETUP, pLink) : NULL;
if (pCall) {
pCall->fAutoClean = TRUE;
pCall->l2cap_id = id;
pLink->b = *pba;
pLink->cid = cid;
pLink->fStage = CONNECTED;
pLink->mtu = 0;
pLink->psm = psm;
pLink->fIncoming = TRUE;
pLink->pNext = gpState->pLinks;
gpState->pLinks = pLink;
result = 0;
} else {
if (pLink)
svsutil_FreeFixed (pLink, gpState->pfmdLinks);
result = 4;
}
} else {
if (result == 0)
result = 2;
}
if ((result == 0) && (pDev->fEncrypt || pDev->fAuthenticate) && (! pDev->fAuthSpinned)) {
pDev->fAuthSpinned = TRUE;
gpState->pSchedule->ScheduleEvent (AuthenticateDevice, pCall, 0);
gpState->Unlock ();
return ERROR_SUCCESS;
}
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_ConnectResponse_In pCallbackConnect = gpState->l2cap_if.l2ca_ConnectResponse_In;
L2CA_ConfigReq_In pCallbackConfig = gpState->l2cap_if.l2ca_ConfigReq_In;
gpState->Unlock ();
__try {
pCallbackConnect (hL2CAP, NULL, pba, id, cid, result, status);
} __except (1) {
}
if (result == 0) {
int iRes = ERROR_INTERNAL_ERROR;
__try {
iRes = pCallbackConfig (hL2CAP, pCall, cid, BTH_MTU_MAX, 0xffff, NULL, 0, NULL);
} __except (1) {
}
if (iRes != ERROR_SUCCESS)
hiddev_lCallAborted (pCall, iRes);
}
return ERROR_SUCCESS;
}
static int hiddev_ConnectReq_Out (void *pCallContext, unsigned short cid, unsigned short result, unsigned short status) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Connect out for call 0x%08x ch 0x%04x result = 0x%04x status 0x%04x\n", pCallContext, cid, result, status));
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
SCall *pCall = VerifyCall ((SCall *)pCallContext);
if ((! pCall) || pCall->fComplete) {
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
SVSUTIL_ASSERT (pCall->fWhat == CALL_L2CAP_LINK_SETUP);
SVSUTIL_ASSERT (! pCall->fComplete);
SVSUTIL_ASSERT (VerifyLink (pCall->pLink));
SVSUTIL_ASSERT (! pCall->pBuffer);
if (result) {
if (result != 1) {
pCall->fComplete = TRUE;
pCall->iResult = ERROR_CONNECTION_REFUSED;
SetEvent (pCall->hEvent);
}
gpState->Unlock ();
return ERROR_SUCCESS;
}
HidDevice *pDev = FindDevice (&pCall->pLink->b);
if (! pDev) {
pCall->fComplete = TRUE;
pCall->iResult = ERROR_CONNECTION_REFUSED;
SetEvent (pCall->hEvent);
gpState->Unlock ();
HIDCloseCID_Int (cid);
return ERROR_SUCCESS;
}
Link *pLink = pCall->pLink;
SVSUTIL_ASSERT (pLink->fStage == NONE);
SVSUTIL_ASSERT (! pLink->cid);
SVSUTIL_ASSERT (pLink->psm);
pLink->fStage = CONNECTED;
pLink->cid = cid;
if ((pDev->fEncrypt || pDev->fAuthenticate) && (! pDev->fAuthSpinned)) {
pDev->fAuthSpinned = TRUE;
gpState->pSchedule->ScheduleEvent (AuthenticateDevice, pCallContext, 0);
gpState->Unlock ();
return ERROR_SUCCESS;
}
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_ConfigReq_In pCallback = gpState->l2cap_if.l2ca_ConfigReq_In;
gpState->Unlock ();
int iRes = ERROR_INTERNAL_ERROR;
__try {
iRes = pCallback (hL2CAP, pCallContext, cid, BTH_MTU_MAX, 0xffff, NULL, 0, NULL);
} __except (1) {
}
if (iRes != ERROR_SUCCESS)
hiddev_lCallAborted (pCallContext, iRes);
return ERROR_SUCCESS;
}
static int hiddev_DataDown_Out (void *pCallContext, unsigned short result) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: Data down call 0x%08x result %d\n", pCallContext, result));
return ERROR_SUCCESS;
}
static int hiddev_Ping_Out (void *pCallContext, BD_ADDR *pba, unsigned char *pOutBuffer, unsigned short size) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: hiddev_Ping_Out call 0x%08x result %d\n", pCallContext));
return ERROR_SUCCESS;
}
// These are just stubs - they do nothing
static int hiddev_ConfigResponse_Out (void *pCallContext, unsigned short result) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: ConfigResponse out call 0x%08x, result %d\n", pCallContext, result));
return ERROR_SUCCESS;
}
static int hiddev_ConnectResponse_Out (void *pCallContext, unsigned short result) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: ConnectResponse out call 0x%08x, result %d\n", pCallContext, result));
return ERROR_SUCCESS;
}
static int hiddev_Disconnect_Out (void *pCallContext, unsigned short result) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: disconnect out call 0x%08x, result %d\n", pCallContext, result));
return ERROR_SUCCESS;
}
//
// Init stuff
//
static int hiddev_CreateDriverInstance (void) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"+hiddev_CreateDriverInstance\n"));
if (gpState) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CreateDriverInstance : ERROR_ALREADY_INITIALIZED\n"));
return ERROR_ALREADY_INITIALIZED;
}
if (! BthPktInitAllocator ())
return ERROR_OUTOFMEMORY;
BTHID_Header_Parameter packetVirtualUnplug;
packetVirtualUnplug.bRawHeader = 0;
packetVirtualUnplug.control_p.bControlOp = BTHID_CONTROL_VIRTUAL_CABLE_UNPLUG;
gbUnplugHeader = (BTHID_HID_CONTROL << 4) | packetVirtualUnplug.bRawHeader;
gpState = CreateNewState ();
if ((! gpState) || (! gpState->fIsRunning)) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CreateDriverInstance : ERROR_OUTOFMEMORY\n"));
if (gpState)
delete gpState;
gpState = NULL;
return ERROR_OUTOFMEMORY;
}
#if defined (BTHHID_QUEUE)
gpState->hthReports = CreateThread (NULL, 0, HidReportHandler, NULL, 0, NULL);
if (gpState->hthReports)
SetThreadPriority (gpState->hthReports, THREAD_PRIORITY_HIGHEST);
#endif
gpState->hthReconnect = CreateThread (NULL, 0, HidReconnectThread, NULL, 0, NULL);
if (! gpState->hthReconnect) {
IFDBG(DebugOut (DEBUG_ERROR, L"-hiddev_CreateDriverInstance : Failed to create reconnect thread.\n"));
}
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CreateDriverInstance : ERROR_SUCCESS\n"));
return ERROR_SUCCESS;
}
static int hiddev_CloseDriverInstance (void) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"+hiddev_CloseDriverInstance\n"));
if (! gpState) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CloseDriverInstance : ERROR_SERVICE_NOT_ACTIVE\n"));
return ERROR_SERVICE_NOT_ACTIVE;
}
gpState->Lock ();
if (! gpState->fIsRunning) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CloseDriverInstance : ERROR_SERVICE_NOT_ACTIVE\n"));
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
gpState->fIsRunning = FALSE;
SetEvent(gpState->hReconnectEvent);
while (gpState->GetRefCount () > 1) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"Waiting for ref count in hiddev_CloseDriverInstance\n"));
gpState->Unlock ();
Sleep (200);
gpState->Lock ();
}
while (gpState->pCalls) {
SetEvent (gpState->pCalls->hEvent);
gpState->pCalls->iResult = ERROR_CANCELLED;
gpState->pCalls = gpState->pCalls->pNext;
}
while (gpState->pLinks) {
unsigned short cid = gpState->pLinks->cid;
gpState->Unlock ();
gpState->l2cap_if.l2ca_Disconnect_In (gpState->hL2CAP, NULL, cid);
gpState->Lock ();
gpState->pLinks = gpState->pLinks->pNext;
}
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"-hiddev_CloseDriverInstance : ERROR_SUCCESS\n"));
if (gpState->hthReconnect)
CloseHandle (gpState->hthReconnect);
gpState->Unlock ();
delete gpState;
gpState = NULL;
BthPktFreeAllocator ();
return ERROR_SUCCESS;
}
#if defined (BTHHID_QUEUE)
//
// HID reports processing
//
static DWORD WINAPI HidReportHandler (LPVOID lpNull) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID ReportHandler : started\n"));
for ( ; ; ) {
if (! gpState)
return 0;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
break;
}
HANDLE hEvent = gpState->hevtReports;
gpState->Unlock ();
WaitForSingleObject (hEvent, INFINITE);
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
break;
}
while (! gpState->qHidReports.IsEmpty()) {
BTHHIDPacket *pPacket = (BTHHIDPacket *)gpState->qHidReports.Get ();
HidDevice *pDev = VerifyDevice ((HidDevice *)pPacket->GetOwner());
int fRelease = TRUE;
if (pDev)
fRelease = ProcessHidPacket (pPacket, pDev);
else {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH Device: Device not found for a packet...\n"));
}
if (fRelease) {
pPacket->ReleasePayload();
delete pPacket;
}
}
gpState->Unlock ();
}
return 0;
}
#endif
//
// Internal API section
//
static int HIDConnect_Int
(
BD_ADDR *pba,
unsigned short usPSM,
unsigned short *pusCid
) {
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
Link *pLink = (Link *)svsutil_GetFixed (gpState->pfmdLinks);
memset (pLink, 0, sizeof (*pLink));
pLink->cid = 0;
pLink->b = *pba;
pLink->fStage = NONE;
pLink->fIncoming = FALSE;
pLink->psm = usPSM;
pLink->pNext = gpState->pLinks;
gpState->pLinks = pLink;
SCall *pCall = AllocCall (CALL_L2CAP_LINK_SETUP, pLink);
if (! pCall) {
gpState->Unlock ();
return ERROR_OUTOFMEMORY;
}
HANDLE hEvent = pCall->hEvent;
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_ConnectReq_In pCallbackConnect = gpState->l2cap_if.l2ca_ConnectReq_In;
L2CA_ConfigReq_In pCallbackConfig = gpState->l2cap_if.l2ca_ConfigReq_In;
int iRes = ERROR_INTERNAL_ERROR;
gpState->Unlock ();
__try {
iRes = pCallbackConnect (hL2CAP, pCall, usPSM, pba);
} __except (1) {
}
if (iRes == ERROR_SUCCESS)
WaitForSingleObject (hEvent, INFINITE);
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (gpState->fIsRunning) {
pCall = VerifyCall (pCall);
pLink = VerifyLink (pLink);
} else {
pCall = NULL;
pLink = NULL;
iRes = ERROR_SERVICE_NOT_ACTIVE;
}
if (iRes == ERROR_SUCCESS) {
if (pCall && pCall->fComplete)
iRes = pCall->iResult;
else
iRes = ERROR_TIMEOUT;
}
if (pCall)
DeleteCall (pCall);
else
iRes = ERROR_INTERNAL_ERROR;
if ((! pLink) || (pLink->fStage & LINK_ERROR) || (iRes != ERROR_SUCCESS) || (pLink->fStage != UP)) {
unsigned short cid_disconnect = 0;
if (pLink) {
if ((iRes == ERROR_SUCCESS) && (pLink->fStage != UP))
iRes = ERROR_INTERNAL_ERROR;
cid_disconnect = pLink->cid;
} else
iRes = ERROR_INTERNAL_ERROR;
gpState->Unlock ();
if (cid_disconnect)
HIDCloseCID_Int (cid_disconnect);
return iRes;
}
*pusCid = pLink->cid;
gpState->Unlock ();
return ERROR_SUCCESS;
}
static int HIDCloseCID_Int (unsigned short usCID) {
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
Link *pLink = FindLink (usCID);
if (! pLink) {
gpState->Unlock ();
return ERROR_NOT_FOUND;
}
unsigned short cid = pLink->cid;
DeleteLink (pLink);
HANDLE hL2CAP = gpState->hL2CAP;
L2CA_Disconnect_In pCallback = gpState->l2cap_if.l2ca_Disconnect_In;
gpState->Unlock ();
__try {
pCallback (hL2CAP, NULL, cid);
} __except (1) {
}
return ERROR_SUCCESS;
}
//
// Hid parser interface
//
//
// Parser interface
//
int HidDevice::SetIdle(unsigned char bIdle) {
// Initialize the packets
BTHHIDPacket requestPacket;
requestPacket.SetHeader(BTHID_SET_IDLE << 4);
requestPacket.SetPayload(&bIdle, sizeof(bIdle));
return WritePacket (this, &requestPacket, BTH_WIRE_TIMEOUT, NULL, BTH_PSM_CONTROL);
}
int HidDevice::GetIdle(unsigned char* pbIdle) {
BTHHIDPacket requestPacket;
requestPacket.SetHeader(BTHID_GET_IDLE << 4);
BTHHIDPacket *pPacket = NULL;
int iErr = WritePacket (this, &requestPacket, BTH_WIRE_TIMEOUT, &pPacket, BTH_PSM_CONTROL);
if (iErr == ERROR_SUCCESS) {
// Check if the request succeeded
BYTE bHeader = pPacket->GetHeader();
if ((bHeader >> 4) == BTHID_DATA) {
// Request succeeded, get the payload to the caller
BYTE *pPacketPayload;
int cbPacketPayload;
pPacket->GetPayload(&pPacketPayload, &cbPacketPayload);
if (cbPacketPayload == 1) {
*pbIdle = *pPacketPayload;
} else
iErr = ERROR_INVALID_DATA;
} else
iErr = ERROR_INVALID_DATA;
pPacket->ReleasePayload ();
delete pPacket;
}
return iErr;
}
int HidDevice::SetProtocol(E_BTHID_PROTOCOLS protocol) {
// Initialize the packets
BTHHIDPacket requestPacket;
BTHID_Header_Parameter param;
// Initialize the packets
param.bRawHeader = 0;
param.setprotocol_p.bProtocol = protocol;
requestPacket.SetHeader((BTHID_SET_PROTOCOL << 4) | param.bRawHeader);
return WritePacket (this, &requestPacket, BTH_WIRE_TIMEOUT, NULL, BTH_PSM_CONTROL);
}
int HidDevice::SetReport(E_BTHID_REPORT_TYPES type, PCHAR pBuffer, int cbBuffer, int iTimeout) {
// Initialize the packets
BTHHIDPacket requestPacket;
BTHID_Header_Parameter SetReport_param;
// Initialize the packets
SetReport_param.bRawHeader = 0;
SetReport_param.setreport_p.bReportType = type;
// Start by sending BTHIDSetReport_Request down the pipe.
requestPacket.SetHeader((BTHID_SET_REPORT << 4) | SetReport_param.bRawHeader);
requestPacket.SetPayload((PBYTE) pBuffer, cbBuffer);
return WritePacket (this, &requestPacket, iTimeout, NULL, BTH_PSM_CONTROL);
}
int HidDevice::GetReport(int iReportID, E_BTHID_REPORT_TYPES type, PCHAR pBuffer, int cbBuffer, PDWORD pcbTransfered, int iTimeout) {
// Initialize the packets
BTHHIDPacket requestPacket;
BTHID_Header_Parameter GetReport_param;
// Initialize the packets
GetReport_param.bRawHeader = 0;
GetReport_param.getreport_p.bReportType = type;
GetReport_param.getreport_p.bSize = 0;
// Start by sending BTHIDGetReport_Request down the pipe.
requestPacket.SetHeader((BTHID_GET_REPORT << 4) | GetReport_param.bRawHeader);
BTHHIDPacket *pPacket = NULL;
int iErr = WritePacket (this, &requestPacket, iTimeout, &pPacket, BTH_PSM_CONTROL);
if (iErr == ERROR_SUCCESS) {
// Check if the request succeeded
BYTE bHeader = pPacket->GetHeader();
if ((bHeader >> 4) == BTHID_DATA) {
// Request succeeded, get the payload to the caller
BYTE *pPacketPayload;
int cbPacketPayload;
pPacket->GetPayload(&pPacketPayload, &cbPacketPayload);
if (cbBuffer >= cbPacketPayload) {
memcpy(pBuffer, pPacketPayload, cbPacketPayload);
*pcbTransfered = cbPacketPayload;
} else
iErr = ERROR_INSUFFICIENT_BUFFER;
} else
iErr = ERROR_INVALID_DATA;
pPacket->ReleasePayload ();
delete pPacket;
}
return iErr;
}
//
// Main API section
//
static int HIDConnect (BT_ADDR *pbt) {
BD_ADDR *pba = (BD_ADDR *)pbt;
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
HidDevice *pDev = FindDevice (pba);
if (! pDev) {
pDev = MakeNewDevice (pba, FALSE);
}
if (pDev && pDev->ckDeviceTimeout) {
gpState->pSchedule->UnScheduleEvent (pDev->ckDeviceTimeout);
pDev->ckDeviceTimeout = 0;
}
gpState->Unlock ();
if (! pDev)
return ERROR_CONNECTION_REFUSED;
unsigned short cidControl = 0;
int iRes = HIDConnect_Int (pba, BTH_PSM_CONTROL, &cidControl);
if (iRes != ERROR_SUCCESS)
return iRes;
DWORD cbMaxInputReport;
// Pass the report descriptor off to the MDD
if ((NULL == pDev->pvNotifyParameter) &&
(HidMdd_Attach(
(HID_PDD_HANDLE) pDev,
pDev->blobReportDescriptor.pBlobData,
pDev->blobReportDescriptor.cbSize,
0,
0,
0,
0,
&pDev->pvNotifyParameter,
&cbMaxInputReport) == FALSE)) {
IFDBG(DebugOut (DEBUG_ERROR, L"BTH HID: Failed to initialize HID Mdd\n"));
return ERROR_NOT_CONNECTED;
}
unsigned short cidInterrupt = 0;
iRes = HIDConnect_Int (pba, BTH_PSM_INTERRUPT, &cidInterrupt);
if (iRes != ERROR_SUCCESS) {
HIDCloseCID_Int (cidControl);
return iRes;
}
return ERROR_SUCCESS;
}
static int HIDVerify (unsigned char *ucSDP, unsigned int cSDP) {
if (! gpState)
return ERROR_SERVICE_NOT_ACTIVE;
gpState->Lock ();
if (! gpState->fIsRunning) {
gpState->Unlock ();
return ERROR_SERVICE_NOT_ACTIVE;
}
BTHHIDSdpParser sdpParser;
int iErr = sdpParser.Start (ucSDP, cSDP);
BOOL bb = FALSE;
if (iErr == ERROR_SUCCESS)
iErr = sdpParser.GetHIDReconnectInitiate(&bb);
if (iErr == ERROR_SUCCESS)
iErr = sdpParser.GetHIDNormallyConnectable(&bb);
if (iErr == ERROR_SUCCESS)
iErr = sdpParser.GetHIDVirtualCable(&bb);
// Pass in an empty structure to retrieve the size of the blob
BLOB blobReportDescriptor = {0, NULL};
if (iErr == ERROR_SUCCESS)
iErr = sdpParser.GetHIDReportDescriptor(&blobReportDescriptor);
if (iErr == ERROR_SUCCESS) {
// Now we have the size. Allocate and requery.
SVSUTIL_ASSERT(blobReportDescriptor.cbSize > 0);
blobReportDescriptor.pBlobData = (unsigned char *)g_funcAlloc (blobReportDescriptor.cbSize, g_pvAllocData);
iErr = sdpParser.GetHIDReportDescriptor(&blobReportDescriptor);
if (iErr == ERROR_SUCCESS)
g_funcFree (blobReportDescriptor.pBlobData, g_pvFreeData);
}
sdpParser.End ();
if (iErr != ERROR_SUCCESS) {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: HIDVerify - parsing SDP record failed\n"));
} else {
IFDBG(DebugOut (DEBUG_LAYER_TRACE, L"BTH HID: HIDVerify - successfully parsed SDP record\n"));
}
gpState->Unlock ();
return iErr;
}
//
// Marshaling code
//
#define TASK_CONNECT 1
#define TASK_VERIFY 2
struct Task {
unsigned int what;
union {
struct {
BT_ADDR b;
} Connect;
struct {
unsigned int cSDP;
unsigned char ucSDP[1];
} Verify;
};
};
static DWORD WINAPI ExecThread (LPVOID lpTask) {
Task *pTask = (Task *)lpTask;
DWORD dwError = ERROR_CALL_NOT_IMPLEMENTED;
if (pTask->what == TASK_CONNECT)
dwError = HIDConnect (&pTask->Connect.b);
else if (pTask->what == TASK_VERIFY)
dwError = HIDVerify (pTask->Verify.ucSDP, pTask->Verify.cSDP);
return dwError;
}
static int Exec (Task *pTask) {
HANDLE hThread = CreateThread (NULL, 0, ExecThread, pTask, 0, NULL);
if (! hThread)
return GetLastError ();
WaitForSingleObject (hThread, INFINITE);
DWORD dwCode;
if (! GetExitCodeThread (hThread, &dwCode))
dwCode = GetLastError ();
CloseHandle (hThread);
return dwCode;
}
#if defined (UNDER_CE)
//
// Driver service funcs...
//
extern "C" BOOL WINAPI DllMain( HANDLE hInstDll, DWORD fdwReason, LPVOID lpvReserved) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls ((HMODULE)hInstDll);
svsutil_Initialize ();
break;
case DLL_PROCESS_DETACH:
svsutil_DeInitialize ();
break;
}
return HidMdd_DllEntry(fdwReason);
}
#if defined (SDK_BUILD)
HANDLE OpenEvent (DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName) {
return CreateEvent (NULL, TRUE, FALSE, lpName);
}
#endif
static DWORD WINAPI StackInitialize (LPVOID lpUnused) {
for (int i = 0 ; i < 100 ; ++i) {
HANDLE hBthStackInited = OpenEvent (EVENT_ALL_ACCESS, FALSE, BTH_NAMEDEVENT_STACK_INITED);
if (hBthStackInited) {
DWORD dwRes = WaitForSingleObject (hBthStackInited, INFINITE);
CloseHandle (hBthStackInited);
if (WAIT_OBJECT_0 == dwRes)
return hiddev_CreateDriverInstance ();
}
Sleep (1000);
}
return ERROR_TIMEOUT;
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// EXECUTION THREAD: Client-application!
// These functions are only executed on the caller's thread
// i.e. the thread belongs to the client application
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// @func PVOID | BHI_Init | Device initialization routine
// @parm DWORD | dwInfo | Info passed to RegisterDevice
// @rdesc Returns a DWORD which will be passed to Open & Deinit or NULL if
// unable to initialize the device.
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
extern "C" DWORD BHI_Init (LPCWSTR lpszDevKey) {
DebugInit();
HANDLE hBthStackInited = OpenEvent (EVENT_ALL_ACCESS, FALSE, BTH_NAMEDEVENT_STACK_INITED);
if (hBthStackInited) {
DWORD dwRes = WaitForSingleObject (hBthStackInited, 0);
CloseHandle (hBthStackInited);
if (WAIT_OBJECT_0 == dwRes)
if (hiddev_CreateDriverInstance () == ERROR_SUCCESS)
goto exit;
}
CloseHandle (CreateThread (NULL, 0, StackInitialize, NULL, 0, NULL));
exit:
return HidMdd_Init(lpszDevKey);
}
// @func PVOID | BHI_Deinit | Device deinitialization routine
// @parm DWORD | dwData | value returned from BHI_Init call
// @rdesc Returns TRUE for success, FALSE for failure.
// @remark Routine exported by a device driver. "PRF" is the string
// passed in as lpszType in RegisterDevice
extern "C" BOOL BHI_Deinit(DWORD dwData) {
hiddev_CloseDriverInstance ();
DebugDeInit();
return HidMdd_Deinit(dwData);
}
// @func PVOID | BHI_Open | Device open routine
// @parm DWORD | dwData | value returned from BHI_Init call
// @parm DWORD | dwAccess | requested access (combination of GENERIC_READ
// and GENERIC_WRITE)
// @parm DWORD | dwShareMode | requested share mode (combination of
// FILE_SHARE_READ and FILE_SHARE_WRITE)
// @rdesc Returns a DWORD which will be passed to Read, Write, etc or NULL if
// unable to open device.
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
extern "C" DWORD BHI_Open (DWORD dwData, DWORD dwAccess, DWORD dwShareMode) {
return HidMdd_Open(dwData, dwAccess, dwShareMode);
}
// @func BOOL | BHI_Close | Device close routine
// @parm DWORD | dwOpenData | value returned from BHI_Open call
// @rdesc Returns TRUE for success, FALSE for failure
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
extern "C" BOOL BHI_Close (DWORD dwData) {
return HidMdd_Close(dwData);
}
// @func DWORD | BHI_Write | Device write routine
// @parm DWORD | dwOpenData | value returned from BHI_Open call
// @parm LPCVOID | pBuf | buffer containing data
// @parm DWORD | len | maximum length to write [IN BYTES, NOT WORDS!!!]
// @rdesc Returns -1 for error, otherwise the number of bytes written. The
// length returned is guaranteed to be the length requested unless an
// error condition occurs.
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
//
extern "C" DWORD BHI_Write (DWORD dwData, LPCVOID pInBuf, DWORD dwInLen) {
return -1;
}
// @func DWORD | BHI_Read | Device read routine
// @parm DWORD | dwOpenData | value returned from BHI_Open call
// @parm LPVOID | pBuf | buffer to receive data
// @parm DWORD | len | maximum length to read [IN BYTES, not WORDS!!]
// @rdesc Returns 0 for end of file, -1 for error, otherwise the number of
// bytes read. The length returned is guaranteed to be the length
// requested unless end of file or an error condition occurs.
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
//
extern "C" DWORD BHI_Read (DWORD dwData, LPVOID pBuf, DWORD Len) {
return -1;
}
// @func DWORD | BHI_Seek | Device seek routine
// @parm DWORD | dwOpenData | value returned from BHI_Open call
// @parm long | pos | position to seek to (relative to type)
// @parm DWORD | type | FILE_BEGIN, FILE_CURRENT, or FILE_END
// @rdesc Returns current position relative to start of file, or -1 on error
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
extern "C" DWORD BHI_Seek (DWORD dwData, long pos, DWORD type) {
return (DWORD)-1;
}
// @func void | BHI_PowerUp | Device powerup routine
// @comm Called to restore device from suspend mode. You cannot call any
// routines aside from those in your dll in this call.
extern "C" void BHI_PowerUp (void) {
return;
}
// @func void | BHI_PowerDown | Device powerdown routine
// @comm Called to suspend device. You cannot call any routines aside from
// those in your dll in this call.
extern "C" void BHI_PowerDown (void) {
return;
}
// @func BOOL | BHI_IOControl | Device IO control routine
// @parm DWORD | dwOpenData | value returned from BHI_Open call
// @parm DWORD | dwCode | io control code to be performed
// @parm PBYTE | pBufIn | input data to the device
// @parm DWORD | dwLenIn | number of bytes being passed in
// @parm PBYTE | pBufOut | output data from the device
// @parm DWORD | dwLenOut |maximum number of bytes to receive from device
// @parm PDWORD | pdwActualOut | actual number of bytes received from device
// @rdesc Returns TRUE for success, FALSE for failure
// @remark Routine exported by a device driver. "PRF" is the string passed
// in as lpszType in RegisterDevice
extern "C" BOOL BHI_IOControl(DWORD dwData, DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut)
{
int iError = ERROR_SUCCESS;
switch (dwCode) {
case BTHHID_IOCTL_HIDConnect:
if (dwLenIn == sizeof(BT_ADDR)) {
Task *pTask = (Task *)LocalAlloc (LMEM_FIXED, sizeof(Task));
if (pTask) {
pTask->what = TASK_CONNECT;
memcpy (&pTask->Connect.b, pBufIn, sizeof(pTask->Connect.b));
iError = Exec(pTask);
LocalFree (pTask);
} else
iError = ERROR_OUTOFMEMORY;
} else
iError = ERROR_INVALID_PARAMETER;
break;
case BTHHID_IOCTL_HIDDisconnect:
if (dwLenIn == sizeof(BT_ADDR)) {
gpState->Lock ();
HidDevice *pDev = FindDevice ((BD_ADDR*)pBufIn);
if (pDev) {
DisconnectDevice(pDev, TRUE);
DeleteDevice(pDev);
}
gpState->Unlock ();
}
else
iError = ERROR_INVALID_PARAMETER;
break;
case BTHHID_IOCTL_HIDVerify:
if (dwLenIn > 0) {
Task *pTask = (Task *)LocalAlloc (LMEM_FIXED, sizeof(Task) + dwLenIn);
if (pTask) {
pTask->what = TASK_VERIFY;
memcpy (pTask->Verify.ucSDP, pBufIn, dwLenIn);
pTask->Verify.cSDP = dwLenIn;
iError = Exec(pTask);
LocalFree (pTask);
} else
iError = ERROR_OUTOFMEMORY;
} else
iError = ERROR_INVALID_PARAMETER;
break;
case BTHHID_IOCTL_HidSendControl:
if (dwLenIn > sizeof(BT_ADDR)) {
gpState->Lock ();
HidDevice* pDev = FindDevice((BD_ADDR*)pBufIn);
gpState->Unlock ();
if (pDev) {
BTHHIDPacket requestPacket;
requestPacket.AddPayloadChunk(pBufIn+sizeof(BT_ADDR), dwLenIn - sizeof(BT_ADDR));
iError = WritePacket (pDev, &requestPacket, BTH_WIRE_TIMEOUT, NULL, BTH_PSM_CONTROL);
}
else
iError = ERROR_INVALID_PARAMETER;
} else
iError = ERROR_INVALID_PARAMETER;
break;
case BTHHID_IOCTL_HidSendInterrupt:
if (dwLenIn > sizeof(BT_ADDR)) {
gpState->Lock ();
HidDevice* pDev = FindDevice((BD_ADDR*)pBufIn);
gpState->Unlock ();
if (pDev) {
BTHHIDPacket requestPacket;
requestPacket.AddPayloadChunk(pBufIn+sizeof(BT_ADDR), dwLenIn - sizeof(BT_ADDR));
iError = WritePacket (pDev, &requestPacket, BTH_WIRE_TIMEOUT, NULL, BTH_PSM_INTERRUPT);
}
else
iError = ERROR_INVALID_PARAMETER;
} else
iError = ERROR_INVALID_PARAMETER;
break;
default:
IFDBG(DebugOut (DEBUG_WARN, L"Unknown control code %d\n", dwCode));
iError = ERROR_CALL_NOT_IMPLEMENTED;
}
if (iError != ERROR_SUCCESS) {
SetLastError(iError);
return FALSE;
}
HidMdd_IOControl(dwData, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut);
return TRUE;
}
#if defined(UNDER_CE) && CE_MAJOR_VER < 0x0003
extern "C" int _isctype(int c, int mask) {
if ((c < 0) || (c > 0xff))
return 0;
return iswctype((wchar_t)c,(wctype_t)mask);
}
#endif //defined(UNDER_CE) && CE_MAJOR_VER < 0x0003
#endif // UNDER_CE
// Get an Input, Output, or Feature report from the device. This is not to be
// be used for recurring interrupt reports. The PDD calls into the MDD with
// HidMdd_ProcessReport for that.
DWORD
WINAPI
HidPdd_GetReport(
HID_PDD_HANDLE hPddDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
PDWORD pcbTransferred,
DWORD dwTimeout,
BYTE bReportID
)
{
HidDevice* pDev = (HidDevice*) hPddDevice;
PREFAST_DEBUGCHK(pDev);
return pDev->GetReport(bReportID, (E_BTHID_REPORT_TYPES)type, pbBuffer, cbBuffer, pcbTransferred, dwTimeout);
}
// Set an Input, Output, or Feature report on the device.
DWORD
WINAPI
HidPdd_SetReport(
HID_PDD_HANDLE hPddDevice,
HIDP_REPORT_TYPE type,
PCHAR pbBuffer,
DWORD cbBuffer,
DWORD dwTimeout,
BYTE bReportID
)
{
HidDevice* pDev = (HidDevice*) hPddDevice;
PREFAST_DEBUGCHK(pDev);
return pDev->SetReport((E_BTHID_REPORT_TYPES)type, pbBuffer, cbBuffer, dwTimeout);
}
DWORD
WINAPI
HidPdd_GetString(
HID_PDD_HANDLE hPddDevice,
HID_STRING_TYPE stringType,
DWORD dwIdx, // Only used with stringType == HID_STRING_INDEXED
LPWSTR pszBuffer, // Set to NULL to get character count
DWORD cchBuffer, // Count of chars that will fit into pszBuffer
// including the NULL terminator.
PDWORD pcchActual // Count of chars in the string NOT including
// the NULL terminator
)
{
HidDevice* pDev = (HidDevice*) hPddDevice;
DEBUGCHK(pDev);
// TODO: Get HID strings
if (pcchActual)
*pcchActual = 0;
return ERROR_SUCCESS;
}
// Get the idle rate for a specific report.
DWORD
WINAPI
HidPdd_GetIdle(
HID_PDD_HANDLE hPddDevice,
PDWORD pdwIdle,
DWORD dwReportID
)
{
HidDevice* pDev = (HidDevice*) hPddDevice;
PREFAST_DEBUGCHK(pDev);
return pDev->GetIdle((unsigned char*)pdwIdle);
}
// Set the idle rate for a specific report. The idle rate is expressed in
// 4 ms increments, so to set idle rate of 20 ms, bDuration should be 5.
// bDuration of 0 means infinite. bReportID of 0 means to apply to all
// reports.
DWORD
WINAPI
HidPdd_SetIdle(
HID_PDD_HANDLE hPddDevice,
DWORD dwDuration,
DWORD dwReportID
)
{
HidDevice* pDev = (HidDevice*) hPddDevice;
PREFAST_DEBUGCHK(pDev);
return pDev->SetIdle((unsigned char)dwDuration);
}
// Commands the PDD to initiate some activity or configuration.
DWORD
WINAPI
HidPdd_IssueCommand(
HID_PDD_HANDLE hPddDevice, // PDD function parameter
DWORD dwMsg, // Notification message
WPARAM wParam // Message parameter
)
{
// We do not support remote wake or any other command
return ERROR_SUCCESS;
}