|
|
/****************************** Module Header ******************************\
* Module Name: hidevice.c * * Copyright (c) 1985 - 2000, Microsoft Corporation * * This module handles HID inputs * * History: * 2000-02-16 HiroYama \***************************************************************************/
/*
* HidDeviceStartStop() needs to be called after both the process devlice request list * and the global TLC list are fully updated. * Each deletion of addition should only recalc the reference count of each devicetype info * and UsagePage-only req-list, but does not actively changes the actual read state of * each device. * * The device should start if: * - cDirectRequest > 0. * This device type is in the inclusion list, so no matter the other ref counts are, * the device needs to be read. * - or, cUsagePageRequest > cExcludeRequest * if UsagePage inclusion exceeds the exclude request count, this device needs to be read. * * The device should stop if: * - uDrecoutRequest == 0 && cUsagePageRequest <= cExcludeRequest * No process specifies this device in the inclusion list. * Exclude count exceeds the UP only request. * * The above consideration assumes, in a single process, specific UsagePage/Usage only appears * either in inclusion list or exclusion list, but not both. * * N.b. No need to maintain the global *exclusion* list. * Each DeviceTLCInfo has three ref counter: * - cDirectRequest * - cUsagePageRequest * - cExcludeRequest * plus, cDevices. * * N.b. Legit number of exclusive requests in TLCInfo is, * cExclusive - cExclusiveOrphaned. * */
#include "precomp.h"
#pragma hdrstop
#ifdef GENERIC_INPUT
#define API_PROLOGUE(type, err) \
type retval; \ type errval = err
#define API_ERROR(lasterr) \
retval = errval; \ if (lasterr) { \ UserSetLastError(lasterr); \ } \ goto error_exit
#define API_CLEANUP() \
goto error_exit; \ error_exit: \
#define API_EPILOGUE() \
return retval
#define StubExceptionHandler(fSetLastError) W32ExceptionHandler((fSetLastError), RIP_WARNING)
#ifdef GI_SINK
HID_COUNTERS gHidCounters; #endif
#if DBG
/*
* Quick sneaky way for the memory leak check. */ struct HidAllocateCounter { int cHidData; int cHidDesc; int cTLCInfo; int cPageOnlyRequest; int cProcessDeviceRequest; int cProcessRequestTable; int cHidSinks; int cKbdSinks; int cMouseSinks; } gHidAllocCounters;
int gcAllocHidTotal;
#define DbgInc(a) do { UserAssert(gHidAllocCounters.a >= 0 && gcAllocHidTotal >= 0); ++gHidAllocCounters.a; ++gcAllocHidTotal; } while (FALSE)
#define DbgDec(a) do { --gHidAllocCounters.a; --gcAllocHidTotal; UserAssert(gHidAllocCounters.a >= 0 && gcAllocHidTotal >= 0); } while (FALSE)
#define DbgFreInc(a) do { DbgInc(a); ++gHidCounters.a; } while (FALSE)
#define DbgFreDec(a) do { DbgDec(a); --gHidCounters.a; } while (FALSE)
#else
#define DbgInc(a)
#define DbgDec(a)
#define DbgFreInc(a) do { ++gHidCounters.a; } while (FALSE)
#define DbgFreDec(a) do { --gHidCounters.a; } while (FALSE)
#endif
/*
* Short helpers */ __inline BOOL IsKeyboardDevice(USAGE usagePage, USAGE usage) { return usagePage == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_KEYBOARD; }
__inline BOOL IsMouseDevice(USAGE usagePage, USAGE usage) { return usagePage == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE; }
__inline BOOL IsLegacyDevice(USAGE usagePage, USAGE usage) { BOOL fRet = FALSE;
switch (usagePage) { case HID_USAGE_PAGE_GENERIC: switch (usage) { case HID_USAGE_GENERIC_KEYBOARD: case HID_USAGE_GENERIC_MOUSE: fRet = TRUE; } } UserAssert(fRet == (IsKeyboardDevice(usagePage, usage) || IsMouseDevice(usagePage, usage))); return fRet; }
/*
* Debug helpers */ #if DBG
/***************************************************************************\
* CheckupHidLeak * * Check if there is any leaked memory. * This one should be called after the pDeviceInfo and all process cleanup. \***************************************************************************/ void CheckupHidLeak(void) { UserAssert(gHidAllocCounters.cHidData == 0); UserAssert(gHidAllocCounters.cHidDesc == 0); UserAssert(gHidAllocCounters.cTLCInfo == 0); UserAssert(gHidAllocCounters.cPageOnlyRequest == 0); UserAssert(gHidAllocCounters.cProcessDeviceRequest == 0); UserAssert(gHidAllocCounters.cProcessRequestTable == 0);
#ifdef GI_SINK
UserAssert(gHidCounters.cKbdSinks == (DWORD)gHidAllocCounters.cKbdSinks); UserAssert(gHidCounters.cMouseSinks == (DWORD)gHidAllocCounters.cMouseSinks); UserAssert(gHidCounters.cHidSinks == (DWORD)gHidAllocCounters.cHidSinks);
UserAssert(gHidAllocCounters.cKbdSinks == 0); UserAssert(gHidAllocCounters.cMouseSinks == 0); UserAssert(gHidAllocCounters.cHidData == 0);
UserAssert(gHidCounters.cKbdSinks == 0); UserAssert(gHidCounters.cMouseSinks == 0); UserAssert(gHidCounters.cHidSinks == 0); #endif
}
void CheckupHidCounter(void) { PLIST_ENTRY pList;
UserAssert(gHidAllocCounters.cHidData >= 0); UserAssert(gHidAllocCounters.cHidDesc >= 0); UserAssert(gHidAllocCounters.cTLCInfo >= 0); UserAssert(gHidAllocCounters.cPageOnlyRequest >= 0); UserAssert(gHidAllocCounters.cProcessDeviceRequest >= 0); UserAssert(gHidAllocCounters.cProcessRequestTable >= 0);
#ifdef GI_SINK
UserAssert(gHidCounters.cKbdSinks == (DWORD)gHidAllocCounters.cKbdSinks); UserAssert(gHidCounters.cMouseSinks == (DWORD)gHidAllocCounters.cMouseSinks); UserAssert(gHidCounters.cHidSinks == (DWORD)gHidAllocCounters.cHidSinks);
UserAssert((int)gHidAllocCounters.cKbdSinks >= 0); UserAssert((int)gHidAllocCounters.cMouseSinks >= 0); UserAssert((int)gHidAllocCounters.cHidData >= 0);
UserAssert((int)gHidCounters.cKbdSinks >= 0); UserAssert((int)gHidCounters.cMouseSinks >= 0); UserAssert((int)gHidCounters.cHidSinks >= 0); #endif
/*
* Checkup TLC Info */ for (pList = gHidRequestTable.TLCInfoList.Flink; pList != &gHidRequestTable.TLCInfoList; pList = pList->Flink) { PHID_TLC_INFO pTLCInfo = CONTAINING_RECORD(pList, HID_TLC_INFO, link);
UserAssert((int)pTLCInfo->cDevices >= 0); UserAssert((int)pTLCInfo->cDirectRequest >= 0); UserAssert((int)pTLCInfo->cUsagePageRequest >= 0); UserAssert((int)pTLCInfo->cExcludeRequest >= 0); UserAssert((int)pTLCInfo->cExcludeOrphaned >= 0); }
#ifdef GI_SINK
/*
* Checkup process request tables. */ for (pList = gHidRequestTable.ProcessRequestList.Flink; pList != &gHidRequestTable.ProcessRequestList; pList = pList->Flink) { PPROCESS_HID_TABLE pHidTable = CONTAINING_RECORD(pList, PROCESS_HID_TABLE, link);
UserAssert((int)pHidTable->nSinks >= 0); } #endif
}
/***************************************************************************\
* DBGValidateHidRequestIsNew * * Make sure there's no deviceinfo that has this UsagePage/Usage. \***************************************************************************/ void DBGValidateHidRequestIsNew( USAGE UsagePage, USAGE Usage) { PDEVICEINFO pDeviceInfo;
CheckDeviceInfoListCritIn();
if (IsLegacyDevice(UsagePage, Usage)) { return; }
for (pDeviceInfo = gpDeviceInfoList; pDeviceInfo; pDeviceInfo = pDeviceInfo->pNext) { if (pDeviceInfo->type == DEVICE_TYPE_HID) { UserAssert(pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage != UsagePage || pDeviceInfo->hid.pHidDesc->hidpCaps.Usage != Usage); } } }
/***************************************************************************\
* DBGValidateHidReqNotInList * * Make sure this request is not in ppi->pHidTable \***************************************************************************/ void DBGValidateHidReqNotInList( PPROCESSINFO ppi, PPROCESS_HID_REQUEST pHid) { PLIST_ENTRY pList;
for (pList = ppi->pHidTable->InclusionList.Flink; pList != &ppi->pHidTable->InclusionList; pList = pList->Flink) { const PPROCESS_HID_REQUEST pHidTmp = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid != pHidTmp); }
for (pList = ppi->pHidTable->UsagePageList.Flink; pList != &ppi->pHidTable->UsagePageList; pList = pList->Flink) { const PPROCESS_HID_REQUEST pHidTmp = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid != pHidTmp); }
for (pList = ppi->pHidTable->ExclusionList.Flink; pList != &ppi->pHidTable->ExclusionList; pList = pList->Flink) { const PPROCESS_HID_REQUEST pHidTmp = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid != pHidTmp); } }
#else
/*
* NOT DBG */ #define CheckupHidCounter()
#define DBGValidateHidReqNotInList(ppi, pHid)
#endif // DBG
/*
* Function prototypes */ PHID_PAGEONLY_REQUEST SearchHidPageOnlyRequest( USHORT usUsagePage);
PHID_TLC_INFO SearchHidTLCInfo( USHORT usUsagePage, USHORT usUsage);
void FreeHidPageOnlyRequest( PHID_PAGEONLY_REQUEST pPOReq);
void ClearProcessTableCache( PPROCESS_HID_TABLE pHidTable);
/***************************************************************************\
* HidDeviceTypeNoReference \***************************************************************************/ __inline BOOL HidTLCInfoNoReference(PHID_TLC_INFO pTLCInfo) { /*
* Orphaned Exclusive requests are always less then cExclusive. */ UserAssert(pTLCInfo->cExcludeRequest >= pTLCInfo->cExcludeOrphaned);
/*
* Hacky, but a bit faster than comparing 0 with each counter. */ return (pTLCInfo->cDevices | pTLCInfo->cDirectRequest | pTLCInfo->cExcludeRequest | pTLCInfo->cUsagePageRequest) == 0; }
/***************************************************************************\
* HidDeviceStartStop: * * This routine has to be called after the global request list is fully updated. \***************************************************************************/ VOID HidDeviceStartStop() { PDEVICEINFO pDeviceInfo;
/*
* The caller has to ensure being in the device list critical section. */ CheckDeviceInfoListCritIn();
/*
* Walk through the list, and start or stop the HID device accordingly. */ for (pDeviceInfo = gpDeviceInfoList; pDeviceInfo; pDeviceInfo = pDeviceInfo->pNext) { if (pDeviceInfo->type == DEVICE_TYPE_HID) { PHID_TLC_INFO pTLCInfo = pDeviceInfo->hid.pTLCInfo;
UserAssert(pTLCInfo);
if (HidTLCActive(pTLCInfo)) { if (pDeviceInfo->handle == 0) { TAGMSG3(DBGTAG_PNP, "HidTLCActive: starting pDevInfo=%p (%x, %x)", pDeviceInfo, pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage, pDeviceInfo->hid.pHidDesc->hidpCaps.Usage); RequestDeviceChange(pDeviceInfo, GDIAF_STARTREAD, TRUE); } } else { UserAssert(pTLCInfo->cDirectRequest == 0 && pTLCInfo->cUsagePageRequest <= HidValidExclusive(pTLCInfo)); if (pDeviceInfo->handle) { TAGMSG3(DBGTAG_PNP, "HidTLCActive: stopping pDevInfo=%p (%x, %x)", pDeviceInfo, pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage, pDeviceInfo->hid.pHidDesc->hidpCaps.Usage); RequestDeviceChange(pDeviceInfo, GDIAF_STOPREAD, TRUE); } } } } }
/***************************************************************************\
* AllocateAndLinkHidTLCInfo * * Allocates DeviceTypeRequest and link it to the global device type request list. * * N.b. the caller has the responsibility to manage the appropriate link count. \***************************************************************************/ PHID_TLC_INFO AllocateAndLinkHidTLCInfo(USHORT usUsagePage, USHORT usUsage) { PHID_TLC_INFO pTLCInfo; PLIST_ENTRY pList;
CheckDeviceInfoListCritIn();
UserAssert(!IsLegacyDevice(usUsagePage, usUsage));
/*
* Make sure this device type is not in the global device request list. */ UserAssert(SearchHidTLCInfo(usUsagePage, usUsage) == NULL);
pTLCInfo = UserAllocPoolZInit(sizeof *pTLCInfo, TAG_PNP); if (pTLCInfo == NULL) { RIPMSG0(RIP_WARNING, "AllocateAndLinkHidTLCInfoList: failed to allocate."); return NULL; }
DbgInc(cTLCInfo);
pTLCInfo->usUsagePage = usUsagePage; pTLCInfo->usUsage = usUsage;
/*
* Link it in. */ InsertHeadList(&gHidRequestTable.TLCInfoList, &pTLCInfo->link);
/*
* Set the correct counter of UsagePage-only request. */ for (pList = gHidRequestTable.UsagePageList.Flink; pList != &gHidRequestTable.UsagePageList; pList = pList->Flink) { PHID_PAGEONLY_REQUEST pPoReq = CONTAINING_RECORD(pList, HID_PAGEONLY_REQUEST, link);
if (pPoReq->usUsagePage == usUsagePage) { pTLCInfo->cUsagePageRequest = pPoReq->cRefCount; break; } }
/*
* The caller is responsible for the further actions, including: * 1) increments appropriate refcount in this strucutre, or * 2) check & start read if this is allocated though the SetRawInputDevice API. * etc. */
return pTLCInfo; }
/***************************************************************************\
* FreeHidTLCInfo. * * Make sure that no one is interested in this device type before * calling this function. \***************************************************************************/ VOID FreeHidTLCInfo(PHID_TLC_INFO pTLCInfo) { CheckDeviceInfoListCritIn();
DbgDec(cTLCInfo);
UserAssert(pTLCInfo->cDevices == 0); UserAssert(pTLCInfo->cDirectRequest == 0); UserAssert(pTLCInfo->cUsagePageRequest == 0); UserAssert(pTLCInfo->cExcludeRequest == 0); UserAssert(pTLCInfo->cExcludeOrphaned == 0);
RemoveEntryList(&pTLCInfo->link);
UserFreePool(pTLCInfo); }
/***************************************************************************\
* SearchHidTLCInfo * * Simply searches the UsagePage/Usage in the global device type request list. \***************************************************************************/ PHID_TLC_INFO SearchHidTLCInfo(USHORT usUsagePage, USHORT usUsage) { PLIST_ENTRY pList;
CheckDeviceInfoListCritIn();
for (pList = gHidRequestTable.TLCInfoList.Flink; pList != &gHidRequestTable.TLCInfoList; pList = pList->Flink) { PHID_TLC_INFO pTLCInfo = CONTAINING_RECORD(pList, HID_TLC_INFO, link);
UserAssert(!IsLegacyDevice(pTLCInfo->usUsagePage, pTLCInfo->usUsage));
if (pTLCInfo->usUsagePage == usUsagePage && pTLCInfo->usUsage == usUsage) { return pTLCInfo; } }
return NULL; }
/***************************************************************************\
* FixupHidPageOnlyRequest * * After the page-only request is freed, fix up the reference counter in * DeviceTypeRequest. If there's no reference, this function also frees * the DeviceTypeRequest. \***************************************************************************/ void SetHidPOCountToTLCInfo(USHORT usUsagePage, DWORD cRefCount, BOOL fFree) { PLIST_ENTRY pList;
CheckDeviceInfoListCritIn();
fFree = (fFree && cRefCount == 0);
for (pList = gHidRequestTable.TLCInfoList.Flink; pList != &gHidRequestTable.TLCInfoList;) { PHID_TLC_INFO pTLCInfo = CONTAINING_RECORD(pList, HID_TLC_INFO, link);
pList = pList->Flink;
if (pTLCInfo->usUsagePage == usUsagePage) { pTLCInfo->cUsagePageRequest = cRefCount; if (fFree && HidTLCInfoNoReference(pTLCInfo)) { /*
* Currently there's no devices of this type attached to the system, * and nobody is interested in this type of device any more. * We can free it now. */ FreeHidTLCInfo(pTLCInfo); } } } }
/***************************************************************************\
* AllocateAndLinkHidPageOnlyRequest * * Allocates the page-only request and link it in the global request list. * The caller is responsible for setting the proper link count. \***************************************************************************/ PHID_PAGEONLY_REQUEST AllocateAndLinkHidPageOnlyRequest(USHORT usUsagePage) { PHID_PAGEONLY_REQUEST pPOReq;
CheckDeviceInfoListCritIn();
/*
* Make sure this PageOnly request is not in the global PageOnly request list. */ UserAssert((pPOReq = SearchHidPageOnlyRequest(usUsagePage)) == NULL);
pPOReq = UserAllocPoolZInit(sizeof(*pPOReq), TAG_PNP); if (pPOReq == NULL) { RIPMSG0(RIP_WARNING, "AllocateAndLinkHidPageOnlyRequest: failed to allocate."); return NULL; }
DbgInc(cPageOnlyRequest);
pPOReq->usUsagePage = usUsagePage;
/*
* Link it in */ InsertHeadList(&gHidRequestTable.UsagePageList, &pPOReq->link);
return pPOReq; }
/***************************************************************************\
* FreeHidPageOnlyRequest * * Frees the page-only request in the global request list. * The caller is responsible for setting the proper link count. \***************************************************************************/ void FreeHidPageOnlyRequest(PHID_PAGEONLY_REQUEST pPOReq) { CheckDeviceInfoListCritIn();
UserAssert(pPOReq->cRefCount == 0);
RemoveEntryList(&pPOReq->link);
UserFreePool(pPOReq);
DbgDec(cPageOnlyRequest); }
/***************************************************************************\
* SearchHidPageOnlyRequest * * Searches the page-only request in the global request list. * The caller is responsible for setting the proper link count. \***************************************************************************/ PHID_PAGEONLY_REQUEST SearchHidPageOnlyRequest(USHORT usUsagePage) { PLIST_ENTRY pList;
for (pList = gHidRequestTable.UsagePageList.Flink; pList != &gHidRequestTable.UsagePageList; pList = pList->Flink) { PHID_PAGEONLY_REQUEST pPOReq = CONTAINING_RECORD(pList, HID_PAGEONLY_REQUEST, link);
if (pPOReq->usUsagePage == usUsagePage) { return pPOReq; } }
return NULL; }
/***************************************************************************\
* SearchProcessHidRequestInclusion * * Searches specific TLC in the per-process inclusion request. \***************************************************************************/ __inline PPROCESS_HID_REQUEST SearchProcessHidRequestInclusion( PPROCESS_HID_TABLE pHidTable, USHORT usUsagePage, USHORT usUsage) { PLIST_ENTRY pList;
UserAssert(pHidTable); // the caller has to validate this
for (pList = pHidTable->InclusionList.Flink; pList != &pHidTable->InclusionList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
if (pHid->usUsagePage == usUsagePage && pHid->usUsage == usUsage) { return pHid; } } return NULL; }
/***************************************************************************\
* SearchProcessHidRequestUsagePage * * Searches specific page-only TLC in the per-process page-only request. \***************************************************************************/ __inline PPROCESS_HID_REQUEST SearchProcessHidRequestUsagePage( PPROCESS_HID_TABLE pHidTable, USHORT usUsagePage) { PLIST_ENTRY pList;
UserAssert(pHidTable); // the caller has to validate this
for (pList = pHidTable->UsagePageList.Flink; pList != &pHidTable->UsagePageList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
if (pHid->usUsagePage == usUsagePage /*&& pHid->usUsage == usUsage*/) { return pHid; } } return NULL; }
/***************************************************************************\
* SearchProcessHidRequestExclusion * * Searches specifc TLC in the per-process exclusion list. \***************************************************************************/ __inline PPROCESS_HID_REQUEST SearchProcessHidRequestExclusion( PPROCESS_HID_TABLE pHidTable, USHORT usUsagePage, USHORT usUsage) { PLIST_ENTRY pList;
UserAssert(pHidTable); // the caller has to validate this
for (pList = pHidTable->ExclusionList.Flink; pList != &pHidTable->ExclusionList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid->spwndTarget == NULL);
if (pHid->usUsagePage == usUsagePage && pHid->usUsage == usUsage) { return pHid; } } return NULL; }
/***************************************************************************\
* SearchProcessHidRequest * * Search per-process HID request list * * Returns the pointer and the flag to indicate which list the request is in. * N.b. this function performs the simple search, should not be used * to judge whether or not the TLC is requested by the process. \***************************************************************************/ PPROCESS_HID_REQUEST SearchProcessHidRequest( PPROCESSINFO ppi, USHORT usUsagePage, USHORT usUsage, PDWORD pdwFlags ) { PPROCESS_HID_REQUEST pReq;
if (ppi->pHidTable == NULL) { return NULL; }
pReq = SearchProcessHidRequestInclusion(ppi->pHidTable, usUsagePage, usUsage); if (pReq) { *pdwFlags = HID_INCLUDE; return pReq; }
if (usUsage == 0) { pReq = SearchProcessHidRequestUsagePage(ppi->pHidTable, usUsagePage); if (pReq) { *pdwFlags = HID_PAGEONLY; return pReq; } }
pReq = SearchProcessHidRequestExclusion(ppi->pHidTable, usUsagePage, usUsage); if (pReq) { *pdwFlags = HID_EXCLUDE; return pReq; }
*pdwFlags = 0;
return NULL; }
/***************************************************************************\
* InProcessDeviceTypeRequestTable * * Check if the device type is in the per-process device request list. * This routine considers the returns TRUE if UsagePage/Usage is requested * by the process. \***************************************************************************/ PPROCESS_HID_REQUEST InProcessDeviceTypeRequestTable( PPROCESS_HID_TABLE pHidTable, USHORT usUsagePage, USHORT usUsage) { PPROCESS_HID_REQUEST phr = NULL; PPROCESS_HID_REQUEST phrExclusive = NULL; UserAssert(pHidTable);
/*
* Firstly check if this is in the inclusion list. */ if ((phr = SearchProcessHidRequestInclusion(pHidTable, usUsagePage, usUsage)) != NULL) { if (CONTAINING_RECORD(pHidTable->InclusionList.Flink, PROCESS_HID_REQUEST, link) != phr) { /*
* Relink this phr to the list head for MRU list */ RemoveEntryList(&phr->link); InsertHeadList(&pHidTable->InclusionList, &phr->link); } goto yes_this_is_requested; }
/*
* Secondly, check if this is in the UsagePage list. */ if ((phr = SearchProcessHidRequestUsagePage(pHidTable, usUsagePage)) == NULL) { /*
* If this UsagePage is not requested, we don't need * to process the input. */ return NULL; } if (CONTAINING_RECORD(pHidTable->UsagePageList.Flink, PROCESS_HID_REQUEST, link) != phr) { /*
* Relink this phr to the list head for MRU list */ RemoveEntryList(&phr->link); InsertHeadList(&pHidTable->UsagePageList, &phr->link); }
/*
* Lastly, check the exclusion list. * If it's not in the exclusion list, this device is * considered as requested by this process. */ if ((phrExclusive = SearchProcessHidRequestExclusion(pHidTable, usUsagePage, usUsage)) != NULL) { /*
* The device in the UsagePage request, but * rejected as in the Exclusion list. */ if (CONTAINING_RECORD(pHidTable->ExclusionList.Flink, PROCESS_HID_REQUEST, link) != phrExclusive) { /*
* Relink this phr to the list head for MRU list */ RemoveEntryList(&phrExclusive->link); InsertHeadList(&pHidTable->ExclusionList, &phrExclusive->link); } return NULL; }
yes_this_is_requested: UserAssert(phr); /*
* The device is in UsagePage list, and is not rejected by exslucion list. */ return phr; }
/***************************************************************************\
* AllocateHidProcessRequest * * The caller has the responsibility to put this in the appropriate list. \***************************************************************************/ PPROCESS_HID_REQUEST AllocateHidProcessRequest( USHORT usUsagePage, USHORT usUsage) { PPROCESS_HID_REQUEST pHidReq;
pHidReq = UserAllocPoolWithQuota(sizeof(PROCESS_HID_REQUEST), TAG_PNP); if (pHidReq == NULL) { return NULL; }
DbgInc(cProcessDeviceRequest);
/*
* Initialize the contents */ pHidReq->usUsagePage = usUsagePage; pHidReq->usUsage = usUsage; pHidReq->ptr = NULL; pHidReq->spwndTarget = NULL; pHidReq->fExclusiveOrphaned = FALSE; #ifdef GI_SINK
pHidReq->fSinkable = FALSE; #endif
return pHidReq; }
/***************************************************************************\
* DerefIncludeRequest * \***************************************************************************/ void DerefIncludeRequest( PPROCESS_HID_REQUEST pHid, PPROCESS_HID_TABLE pHidTable, BOOL fLegacyDevice, BOOL fFree) { if (fLegacyDevice) { /*
* Legacy devices are not associated with TLCInfo. */ UserAssert(pHid->pTLCInfo == NULL);
// N.b. NoLegacy flag is set afterwards
/*
* If mouse is being removed, clear the captureMouse * flag. */ if (pHidTable->fCaptureMouse) { if (IsMouseDevice(pHid->usUsagePage, pHid->usUsage)) { pHidTable->fCaptureMouse = FALSE; } } if (pHidTable->fNoHotKeys) { if (IsKeyboardDevice(pHid->usUsagePage, pHid->usUsage)) { pHidTable->fNoHotKeys = FALSE; } } if (pHidTable->fAppKeys) { if (IsKeyboardDevice(pHid->usUsagePage, pHid->usUsage)) { pHidTable->fAppKeys = FALSE; } } } else { /*
* HID devices. * Decrement the counters in HidDeviceTypeRequest. */ UserAssert(pHid->pTLCInfo); UserAssert(pHid->pTLCInfo == SearchHidTLCInfo(pHid->usUsagePage, pHid->usUsage));
if (--pHid->pTLCInfo->cDirectRequest == 0 && fFree) { if (HidTLCInfoNoReference(pHid->pTLCInfo)) { /*
* Currently there's no devices of this type attached to the system, * and nobody is interested in this type of device any more. * We can free it now. */ FreeHidTLCInfo(pHid->pTLCInfo); } } }
#ifdef GI_SINK
if (pHid->fSinkable) { pHid->fSinkable = FALSE; if (!fLegacyDevice) { --pHidTable->nSinks; UserAssert(pHidTable->nSinks >= 0); // LATER: when nSinks is changed to DWORD, remove those assertions
DbgFreDec(cHidSinks); } } #endif
}
/***************************************************************************\
* DerefPageOnlyRequest * \***************************************************************************/ void DerefPageOnlyRequest( PPROCESS_HID_REQUEST pHid, PPROCESS_HID_TABLE pHidTable, const BOOL fFree) { /*
* Decrement the ref count in the global pageonly list. */ UserAssert(pHid->pPORequest); UserAssert(pHid->pPORequest == SearchHidPageOnlyRequest(pHid->usUsagePage)); UserAssert(pHid->usUsage == 0); UserAssert(!IsLegacyDevice(pHid->usUsagePage, pHid->usUsage)); UserAssert(pHid->pPORequest->cRefCount >= 1);
--pHid->pPORequest->cRefCount; /*
* Update the POCount in TLCInfo. Does not free them if fFree is false. */ SetHidPOCountToTLCInfo(pHid->usUsagePage, pHid->pPORequest->cRefCount, fFree);
/*
* If refcount is 0 and the caller wants it freed, do it now. */ if (pHid->pPORequest->cRefCount == 0 && fFree) { FreeHidPageOnlyRequest(pHid->pPORequest); pHid->pPORequest = NULL; } #ifdef GI_SINK
if (pHid->fSinkable) { pHid->fSinkable = FALSE; --pHidTable->nSinks; UserAssert(pHidTable->nSinks >= 0); DbgFreDec(cHidSinks); } /*
* The legacy sink flags in pHidTable is calc'ed later. */ #endif
}
/***************************************************************************\
* DerefExcludeRequest * \***************************************************************************/ void DerefExcludeRequest( PPROCESS_HID_REQUEST pHid, BOOL fLegacyDevice, BOOL fFree) { /*
* Remove Exclude request. */ #ifdef GI_SINK
UserAssert(pHid->fSinkable == FALSE); UserAssert(pHid->spwndTarget == NULL); #endif
if (!fLegacyDevice) { UserAssert(pHid->pTLCInfo); UserAssert(pHid->pTLCInfo == SearchHidTLCInfo(pHid->usUsagePage, pHid->usUsage));
if (pHid->fExclusiveOrphaned) { /*
* This is a orphaned exclusive request. */ --pHid->pTLCInfo->cExcludeOrphaned; } if (--pHid->pTLCInfo->cExcludeRequest == 0 && fFree && HidTLCInfoNoReference(pHid->pTLCInfo)) { /*
* If all the references are gone, let's free this TLCInfo. */ FreeHidTLCInfo(pHid->pTLCInfo); } } else { /*
* Legacy devices are not associated with TLCInfo. */ UserAssert(pHid->pTLCInfo == NULL); /*
* Legacy devices cannot be orphaned exclusive request. */ UserAssert(pHid->fExclusiveOrphaned == FALSE); } }
/***************************************************************************\
* FreeHidProcessRequest * * Frees the per-process request. * This routine only manupilates the reference count of the global request list, so * the caller has to call HidDeviceStartStop(). \***************************************************************************/ void FreeHidProcessRequest( PPROCESS_HID_REQUEST pHid, DWORD dwFlags, PPROCESS_HID_TABLE pHidTable) { BOOL fLegacyDevice = IsLegacyDevice(pHid->usUsagePage, pHid->usUsage);
CheckDeviceInfoListCritIn(); // the caller has to ensure it's in the device list crit.
/*
* Unlock the target window. */ Unlock(&pHid->spwndTarget);
if (dwFlags == HID_INCLUDE) { DerefIncludeRequest(pHid, pHidTable, fLegacyDevice, TRUE); } else if (dwFlags == HID_PAGEONLY) { DerefPageOnlyRequest(pHid, pHidTable, TRUE); } else if (dwFlags == HID_EXCLUDE) { DerefExcludeRequest(pHid, fLegacyDevice, TRUE); } else { UserAssert(FALSE); }
RemoveEntryList(&pHid->link);
DbgDec(cProcessDeviceRequest);
CheckupHidCounter();
UserFreePool(pHid); }
/***************************************************************************\
* AllocateProcessHidTable * * The caller has to assign the returned table to ppi. \***************************************************************************/ PPROCESS_HID_TABLE AllocateProcessHidTable(void) { PPROCESS_HID_TABLE pHidTable;
TAGMSG1(DBGTAG_PNP, "AllocateProcessHidTable: ppi=%p", PpiCurrent());
pHidTable = UserAllocPoolWithQuotaZInit(sizeof *pHidTable, TAG_PNP); if (pHidTable == NULL) { return NULL; }
DbgInc(cProcessRequestTable);
InitializeListHead(&pHidTable->InclusionList); InitializeListHead(&pHidTable->UsagePageList); InitializeListHead(&pHidTable->ExclusionList);
#ifdef GI_SINK
InsertHeadList(&gHidRequestTable.ProcessRequestList, &pHidTable->link); #endif
/*
* Increment the number of process that are HID aware. * When the process goes away, this gets decremented. */ ++gnHidProcess;
return pHidTable; }
/***************************************************************************\
* FreeProcesHidTable * \***************************************************************************/ void FreeProcessHidTable(PPROCESS_HID_TABLE pHidTable) { BOOL fUpdate; UserAssert(pHidTable);
CheckCritIn(); CheckDeviceInfoListCritIn();
TAGMSG2(DBGTAG_PNP, "FreeProcessHidTable: cleaning up pHidTable=%p (possibly ppi=%p)", pHidTable, PpiCurrent());
fUpdate = !IsListEmpty(&pHidTable->InclusionList) || !IsListEmpty(&pHidTable->UsagePageList) || !IsListEmpty(&pHidTable->ExclusionList);
/*
* Unlock the target window for legacy devices. */ Unlock(&pHidTable->spwndTargetKbd); Unlock(&pHidTable->spwndTargetMouse);
while (!IsListEmpty(&pHidTable->InclusionList)) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pHidTable->InclusionList.Flink, PROCESS_HID_REQUEST, link); FreeHidProcessRequest(pHid, HID_INCLUDE, pHidTable); }
while (!IsListEmpty(&pHidTable->UsagePageList)) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pHidTable->UsagePageList.Flink, PROCESS_HID_REQUEST, link); FreeHidProcessRequest(pHid, HID_PAGEONLY, pHidTable); }
while (!IsListEmpty(&pHidTable->ExclusionList)) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pHidTable->ExclusionList.Flink, PROCESS_HID_REQUEST, link); UserAssert(pHid->spwndTarget == NULL); FreeHidProcessRequest(pHid, HID_EXCLUDE, pHidTable); }
#ifdef GI_SINK
UserAssert(pHidTable->nSinks == 0); RemoveEntryList(&pHidTable->link);
/*
* Those flags should have been cleared on the * thread destruction. */ UserAssert(pHidTable->fRawKeyboardSink == FALSE); UserAssert(pHidTable->fRawMouseSink == FALSE); CheckupHidCounter(); #endif
UserFreePool(pHidTable);
/*
* Decrement the number of process that are HID aware. */ --gnHidProcess;
DbgDec(cProcessRequestTable);
if (fUpdate) { HidDeviceStartStop(); } }
/***************************************************************************\
* DestroyProcessHidRequests * * Upon process termination, force destroy process hid requests. \***************************************************************************/ void DestroyProcessHidRequests(PPROCESSINFO ppi) { PPROCESS_HID_TABLE pHidTable;
CheckCritIn(); EnterDeviceInfoListCrit();
#if DBG
/*
* Check out if there's a pwndTarget in the HidTable list. * These should be unlocked by the time the last * threadinfo is destroyed. */ UserAssert(ppi->pHidTable->spwndTargetMouse == NULL); UserAssert(ppi->pHidTable->spwndTargetKbd == NULL);
#ifdef GI_SINK
UserAssert(ppi->pHidTable->fRawKeyboardSink == FALSE); UserAssert(ppi->pHidTable->fRawMouseSink == FALSE); #endif
{ PPROCESS_HID_TABLE pHidTableTmp = ppi->pHidTable; PLIST_ENTRY pList;
for (pList = pHidTableTmp->InclusionList.Flink; pList != &pHidTableTmp->InclusionList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid->spwndTarget == NULL); }
for (pList = pHidTableTmp->UsagePageList.Flink; pList != &pHidTableTmp->UsagePageList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid->spwndTarget == NULL); }
for (pList = pHidTableTmp->ExclusionList.Flink; pList != &pHidTableTmp->ExclusionList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
UserAssert(pHid->spwndTarget == NULL); } } #endif
pHidTable = ppi->pHidTable; ppi->pHidTable = NULL; FreeProcessHidTable(pHidTable); LeaveDeviceInfoListCrit(); }
/***************************************************************************\
* DestroyThreadHidObjects * * When a thread is going away, destroys thread-related Hid objects. \***************************************************************************/ void DestroyThreadHidObjects(PTHREADINFO pti) { PPROCESS_HID_TABLE pHidTable = pti->ppi->pHidTable; PLIST_ENTRY pList;
UserAssert(pHidTable);
/*
* If the target windows belong to this thread, * unlock them now. */ if (pHidTable->spwndTargetKbd && GETPTI(pHidTable->spwndTargetKbd) == pti) { RIPMSG2(RIP_WARNING, "DestroyThreadHidObjects: raw keyboard is requested pwnd=%p by pti=%p", pHidTable->spwndTargetKbd, pti); Unlock(&pHidTable->spwndTargetKbd); pHidTable->fRawKeyboard = pHidTable->fNoLegacyKeyboard = FALSE; #ifdef GI_SINK
if (pHidTable->fRawKeyboardSink) { DbgFreDec(cKbdSinks); pHidTable->fRawKeyboardSink = FALSE; } #endif
} if (pHidTable->spwndTargetMouse && GETPTI(pHidTable->spwndTargetMouse) == pti) { RIPMSG2(RIP_WARNING, "DestroyThreadHidObjects: raw mouse is requested pwnd=%p by pti=%p", pHidTable->spwndTargetMouse, pti); Unlock(&pHidTable->spwndTargetMouse); pHidTable->fRawMouse = pHidTable->fNoLegacyMouse = FALSE; #ifdef GI_SINK
if (pHidTable->fRawMouseSink) { DbgFreDec(cMouseSinks); pHidTable->fRawMouseSink = FALSE; } #endif
}
/*
* Free up the cached input type, in case it's for the current thread. * LATER: clean this up only pLastRequest belongs to this thread. */ ClearProcessTableCache(pHidTable);
CheckCritIn(); EnterDeviceInfoListCrit();
/*
* Delete all process device requests that have * a target window belongs to this thread. */ for (pList = pHidTable->InclusionList.Flink; pList != &pHidTable->InclusionList;) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link); pList = pList->Flink;
if (pHid->spwndTarget && GETPTI(pHid->spwndTarget) == pti) { RIPMSG4(RIP_WARNING, "DestroyThreadHidObjects: HID inc. request (%x,%x) pwnd=%p pti=%p", pHid->usUsagePage, pHid->usUsage, pHid->spwndTarget, pti); FreeHidProcessRequest(pHid, HID_INCLUDE GI_SINK_PARAM(pHidTable)); } }
for (pList = pHidTable->UsagePageList.Flink; pList != &pHidTable->UsagePageList;) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link); pList = pList->Flink;
if (pHid->spwndTarget && GETPTI(pHid->spwndTarget) == pti) { RIPMSG4(RIP_WARNING, "DestroyThreadHidObjects: HID page-only request (%x,%x) pwnd=%p pti=%p", pHid->usUsagePage, pHid->usUsage, pHid->spwndTarget, pti); FreeHidProcessRequest(pHid, HID_PAGEONLY GI_SINK_PARAM(pHidTable)); } }
for (pList = pHidTable->ExclusionList.Flink; pList != &pHidTable->ExclusionList;) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link); pList = pList->Flink;
UserAssert(pHid->spwndTarget == NULL);
if (pHid->spwndTarget && GETPTI(pHid->spwndTarget) == pti) { RIPMSG4(RIP_WARNING, "DestroyThreadHidObjects: HID excl. request (%x,%x) pwnd=%p pti=%p", pHid->usUsagePage, pHid->usUsage, pHid->spwndTarget, pti); FreeHidProcessRequest(pHid, HID_EXCLUDE GI_SINK_PARAM(pHidTable)); } } LeaveDeviceInfoListCrit(); }
/***************************************************************************\
* InitializeHidRequestList * * Global request list initialization \***************************************************************************/ void InitializeHidRequestList() { InitializeListHead(&gHidRequestTable.TLCInfoList); InitializeListHead(&gHidRequestTable.UsagePageList); #ifdef GI_SINK
InitializeListHead(&gHidRequestTable.ProcessRequestList); #endif
}
/***************************************************************************\
* CleanupHidRequestList * * Global HID requests cleanup * * See Win32kNtUserCleanup. * N.b. This rountine is supposed to be called before cleaning up * the deviceinfo list. \***************************************************************************/ void CleanupHidRequestList() { PLIST_ENTRY pList;
CheckDeviceInfoListCritIn();
pList = gHidRequestTable.TLCInfoList.Flink; while (pList != &gHidRequestTable.TLCInfoList) { PHID_TLC_INFO pTLCInfo = CONTAINING_RECORD(pList, HID_TLC_INFO, link);
/*
* The contents may be freed later, so get the next link as the first thing. */ pList = pList->Flink;
/*
* Set the process reference counter to zero, so that the FreeDeviceInfo() later can actually free * this device request. */ pTLCInfo->cDirectRequest = pTLCInfo->cUsagePageRequest = pTLCInfo->cExcludeRequest = pTLCInfo->cExcludeOrphaned = 0;
if (pTLCInfo->cDevices == 0) { /*
* If this has zero deviceinfo reference, it can be directly freed here. */ FreeHidTLCInfo(pTLCInfo); } }
/*
* Free PageOnly list. * Since this list is not referenced from DeviceInfo, it's safe to directly free it here. */ while (!IsListEmpty(&gHidRequestTable.UsagePageList)) { PHID_PAGEONLY_REQUEST pPOReq = CONTAINING_RECORD(gHidRequestTable.UsagePageList.Flink, HID_PAGEONLY_REQUEST, link);
/*
* Set the process reference count to zero. */ pPOReq->cRefCount = 0; /*
* No need to fixup the HidTLCInfo's page-only request * count, as allthe TLCInfo has been freed already. */ FreeHidPageOnlyRequest(pPOReq); } }
/***************************************************************************\
* GetOperationMode * * This function converts the RAWINPUTDEVICE::dwFlags to the internal * operation mode. \***************************************************************************/ __inline DWORD GetOperationMode( PCRAWINPUTDEVICE pDev, BOOL fLegacyDevice) { DWORD dwFlags = 0;
UNREFERENCED_PARAMETER(fLegacyDevice);
/*
* Prepare the information */ if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_PAGEONLY) { UserAssert(pDev->usUsage == 0); /*
* The app want all the Usage in this UsagePage. */ dwFlags = HID_PAGEONLY; } else if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_EXCLUDE) { UserAssert(pDev->usUsage != 0); UserAssert(pDev->hwndTarget == NULL); UserAssert((pDev->dwFlags & RIDEV_INPUTSINK) == 0); dwFlags = HID_EXCLUDE; } else if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_INCLUDE || RIDEV_EXMODE(pDev->dwFlags) == RIDEV_NOLEGACY) { UserAssert(pDev->usUsage != 0);
/*
* NOLEGACY can be only specified for the legacy devices. */ UserAssertMsg2(RIDEV_EXMODE(pDev->dwFlags) == RIDEV_INCLUDE || fLegacyDevice, "RIDEV_NOLEGACY is specified for non legacy device (%x,%x)", pDev->usUsagePage, pDev->usUsage); dwFlags = HID_INCLUDE; } else { UserAssert(FALSE); }
return dwFlags; }
/***************************************************************************\
* SetLegacyDeviceFlags * * This function sets or resets the NoLegacy flags and CaptureMouse flag * when processing each request. \***************************************************************************/ void SetLegacyDeviceFlags( PPROCESS_HID_TABLE pHidTable, PCRAWINPUTDEVICE pDev) { UserAssert(IsLegacyDevice(pDev->usUsagePage, pDev->usUsage));
if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_INCLUDE || RIDEV_EXMODE(pDev->dwFlags) == RIDEV_NOLEGACY) { if (IsKeyboardDevice(pDev->usUsagePage, pDev->usUsage)) { pHidTable->fNoLegacyKeyboard = (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_NOLEGACY); pHidTable->fNoHotKeys = ((pDev->dwFlags & RIDEV_NOHOTKEYS) != 0); pHidTable->fAppKeys = ((pDev->dwFlags & RIDEV_APPKEYS) != 0); } else if (IsMouseDevice(pDev->usUsagePage, pDev->usUsage)) { pHidTable->fNoLegacyMouse = RIDEV_EXMODE(pDev->dwFlags) == RIDEV_NOLEGACY; pHidTable->fCaptureMouse = (pDev->dwFlags & RIDEV_CAPTUREMOUSE) != 0; } } }
/***************************************************************************\
* InsertProcRequest * * This function inserts the ProcRequest into ppi->pHidTable. * This function also maintains the reference counter of TLCInfo and * PORequest. \***************************************************************************/ BOOL InsertProcRequest( PPROCESSINFO ppi, PCRAWINPUTDEVICE pDev, PPROCESS_HID_REQUEST pHid, #if DBG
PPROCESS_HID_REQUEST pHidOrg, #endif
DWORD dwFlags, BOOL fLegacyDevice, PWND pwnd) { /*
* Update the global list. */ if (dwFlags == HID_INCLUDE) { if (!fLegacyDevice) { PHID_TLC_INFO pTLCInfo = SearchHidTLCInfo(pHid->usUsagePage, pHid->usUsage); if (pTLCInfo == NULL) { UserAssert(pHidOrg == NULL); #if DBG
DBGValidateHidRequestIsNew(pHid->usUsagePage, pHid->usUsage); #endif
/*
* There is no such device type request allocated yet. * Create a new one now. */ pTLCInfo = AllocateAndLinkHidTLCInfo(pHid->usUsagePage, pHid->usUsage); if (pTLCInfo == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "AddNewProcDeviceRequest: failed to allocate pTLCInfo."); return FALSE; } } pHid->pTLCInfo = pTLCInfo; ++pTLCInfo->cDirectRequest; }
/*
* Lock the target window. */ Lock(&pHid->spwndTarget, pwnd);
/*
* Link it in. */ InsertHeadList(&ppi->pHidTable->InclusionList, &pHid->link);
TAGMSG2(DBGTAG_PNP, "AddNewProcDeviceRequest: include (%x, %x)", pHid->usUsagePage, pHid->usUsage);
} else if (dwFlags == HID_PAGEONLY) { PHID_PAGEONLY_REQUEST pPOReq = SearchHidPageOnlyRequest(pHid->usUsagePage);
if (pPOReq == NULL) { UserAssert(pHidOrg == NULL); /*
* Create a new one. */ pPOReq = AllocateAndLinkHidPageOnlyRequest(pHid->usUsagePage); if (pPOReq == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "AddNewProcDeviceRequest: failed to allocate pPOReq"); return FALSE; }
} pHid->pPORequest = pPOReq; ++pPOReq->cRefCount;
/*
* Update the page-only refcount in TLCInfo */ SetHidPOCountToTLCInfo(pHid->usUsagePage, pPOReq->cRefCount, FALSE);
/*
* Lock the target window. */ Lock(&pHid->spwndTarget, pwnd);
/*
* Link it in. */ InsertHeadList(&ppi->pHidTable->UsagePageList, &pHid->link);
TAGMSG2(DBGTAG_PNP, "AddNewProcDeviceRequest: pageonly (%x, %x)", pHid->usUsagePage, pHid->usUsage);
} else if (dwFlags == HID_EXCLUDE) { /*
* Add new Exclude request... * N.b. this may become orphaned exclusive request later. * For now let's pretend if it's a legit exclusive request. */ if (!fLegacyDevice) { PHID_TLC_INFO pTLCInfo = SearchHidTLCInfo(pHid->usUsagePage, pHid->usUsage);
if (pTLCInfo == NULL) { UserAssert(pHidOrg == NULL); #if DBG
DBGValidateHidRequestIsNew(pHid->usUsagePage, pHid->usUsage); #endif
pTLCInfo = AllocateAndLinkHidTLCInfo(pHid->usUsagePage, pHid->usUsage); if (pTLCInfo == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "AddNewProcDeviceRequest: failed to allocate pTLCInfo for exlusion"); return FALSE; } } pHid->pTLCInfo = pTLCInfo; ++pTLCInfo->cExcludeRequest; UserAssert(pHid->fExclusiveOrphaned == FALSE);
UserAssert(pHid->spwndTarget == NULL); // This is a new allocation, should be no locked pwnd.
}
/*
* Link it in. */ InsertHeadList(&ppi->pHidTable->ExclusionList, &pHid->link);
TAGMSG2(DBGTAG_PNP, "AddNewProcDeviceRequest: exlude (%x, %x)", pHid->usUsagePage, pHid->usUsage); }
/*
* After this point, as pHid is already linked in pHidTable, * no simple return is allowed, without a legit cleanup. */
#ifdef GI_SINK
/*
* Set the sinkable flag. */ if (pDev->dwFlags & RIDEV_INPUTSINK) { /*
* Exclude request cannot be a sink. This should have been * checked in the validation code by now. */ UserAssert(RIDEV_EXMODE(pDev->dwFlags) != RIDEV_EXCLUDE); /*
* Sink request should specify the target hwnd. * The validation is supposed to check it beforehand. */ UserAssert(pwnd);
UserAssert(ppi->pHidTable->nSinks >= 0); // LATER
if (!fLegacyDevice) { /*
* We count the sink for the non legacy devices only, so that * we can save clocks to walk through the request list. */ if (!pHid->fSinkable) { ++ppi->pHidTable->nSinks; DbgFreInc(cHidSinks); } } /*
* Set this request as sink. */ pHid->fSinkable = TRUE; } #endif
return TRUE; }
/***************************************************************************\
* RemoveProcRequest * * This function temporarily removes the ProcRequest from pHidTable * and global TLCInfo / PORequest. This function also updates the * reference counters in TLCInfo / PORequest. The sink counter in * pHidTable is also updated. \***************************************************************************/ void RemoveProcRequest( PPROCESSINFO ppi, PPROCESS_HID_REQUEST pHid, DWORD dwFlags, BOOL fLegacyDevice) { /*
* Unlock the target window. */ Unlock(&pHid->spwndTarget);
switch (dwFlags) { case HID_INCLUDE: DerefIncludeRequest(pHid, ppi->pHidTable, fLegacyDevice, FALSE); break; case HID_PAGEONLY: DerefPageOnlyRequest(pHid, ppi->pHidTable, FALSE); break; case HID_EXCLUDE: DerefExcludeRequest(pHid, fLegacyDevice, FALSE); }
RemoveEntryList(&pHid->link); }
/***************************************************************************\
* SetProcDeviceRequest * * This function updates the ProcHidRequest based on RAWINPUTDEVICE. * This function also sets some of the legacy device flags, such as * NoLegacy or CaptureMouse / NoDefSystemKeys. \***************************************************************************/ BOOL SetProcDeviceRequest( PPROCESSINFO ppi, PCRAWINPUTDEVICE pDev, PPROCESS_HID_REQUEST pHidOrg, DWORD dwFlags) { PPROCESS_HID_REQUEST pHid = pHidOrg; BOOL fLegacyDevice = IsLegacyDevice(pDev->usUsagePage, pDev->usUsage); PWND pwnd; DWORD dwOperation;
TAGMSG3(DBGTAG_PNP, "SetProcDeviceRequest: processing (%x, %x) to ppi=%p", pDev->usUsagePage, pDev->usUsage, ppi);
CheckDeviceInfoListCritIn();
if (pDev->hwndTarget) { pwnd = ValidateHwnd(pDev->hwndTarget); if (pwnd == NULL) { RIPMSG2(RIP_WARNING, "SetProcDeviceRequest: hwndTarget (%p) in pDev (%p) is bogus", pDev->hwndTarget, pDev); return FALSE; } } else { pwnd = NULL; }
dwOperation = GetOperationMode(pDev, fLegacyDevice); if (dwFlags == 0) { UserAssert(pHid == NULL); } else { UserAssert(pHid); }
if (pHid == NULL) { /*
* If this is a new request for this TLC, allocate it here. */ pHid = AllocateHidProcessRequest(pDev->usUsagePage, pDev->usUsage); if (pHid == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "SetRawInputDevices: failed to allocate pHid."); goto error_exit; } }
/*
* Firstly remove this guy temporarily from the list. */ if (pHidOrg) { UserAssert(pHidOrg->usUsagePage == pDev->usUsagePage && pHidOrg->usUsage == pDev->usUsage); RemoveProcRequest(ppi, pHidOrg, dwFlags, fLegacyDevice); pHid = pHidOrg; }
if (!InsertProcRequest(ppi, pDev, pHid, #if DBG
pHidOrg, #endif
dwOperation, fLegacyDevice, pwnd)) { /*
* The error case in InsertProcRequest should be TLCInfo * allocation error, so it couldn't be legacy devices. */ UserAssert(!fLegacyDevice); goto error_exit; }
if (fLegacyDevice) { SetLegacyDeviceFlags(ppi->pHidTable, pDev); }
/*
* Succeeded. */ return TRUE;
error_exit: if (pHid) { /*
* Let's make sure it's not in the request list. */ DBGValidateHidReqNotInList(ppi, pHid);
/*
* Free this error-prone request. */ UserFreePool(pHid); } return FALSE; }
/***************************************************************************\
* HidRequestValidityCheck * \***************************************************************************/ BOOL HidRequestValidityCheck( const PRAWINPUTDEVICE pDev) { PWND pwnd = NULL;
if (pDev->dwFlags & ~RIDEV_VALID) { RIPERR1(ERROR_INVALID_FLAGS, RIP_WARNING, "HidRequestValidityCheck: invalid flag %x", pDev->dwFlags); return FALSE; }
if (pDev->usUsagePage == 0) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: usUsagePage is 0"); return FALSE; }
/*
* If hwndTarget is specified, validate it here. */ if (pDev->hwndTarget) { pwnd = ValidateHwnd(pDev->hwndTarget); }
/*
* Reject invalid CaptureMouse / NoSystemKeys flags. */ #if (RIDEV_CAPTUREMOUSE != RIDEV_NOHOTKEYS)
#error The value of RIDEV_CAPTUREMOUSE and RIDEV_NOSYSTEMKEYS should match.
#endif
if (pDev->dwFlags & RIDEV_CAPTUREMOUSE) { if (IsMouseDevice(pDev->usUsagePage, pDev->usUsage)) { if (RIDEV_EXMODE(pDev->dwFlags) != RIDEV_NOLEGACY || pwnd == NULL || GETPTI(pwnd)->ppi != PpiCurrent()) { RIPERR4(ERROR_INVALID_FLAGS, RIP_WARNING, "HidRequestValidityCheck: invalid request (%x,%x) dwf %x hwnd %p " "found for RIDEV_CAPTUREMOUSE", pDev->usUsagePage, pDev->usUsage, pDev->dwFlags, pDev->hwndTarget); return FALSE; } } else if (!IsKeyboardDevice(pDev->usUsagePage, pDev->usUsage)) { RIPERR4(ERROR_INVALID_FLAGS, RIP_WARNING, "HidRequestValidityCheck: invalid request (%x,%x) dwf %x hwnd %p " "found for RIDEV_CAPTUREMOUSE", pDev->usUsagePage, pDev->usUsage, pDev->dwFlags, pDev->hwndTarget); return FALSE; } } if (pDev->dwFlags & RIDEV_APPKEYS) { if (!IsKeyboardDevice(pDev->usUsagePage, pDev->usUsage) || (RIDEV_EXMODE(pDev->dwFlags) != RIDEV_NOLEGACY)) { RIPERR4(ERROR_INVALID_FLAGS, RIP_WARNING, "HidRequestValidityCheck: invalid request (%x,%x) dwf %x hwnd %p " "found for RIDEV_APPKEYS", pDev->usUsagePage, pDev->usUsage, pDev->dwFlags, pDev->hwndTarget); return FALSE; } }
/*
* RIDEV_REMOVE only takes PAGEONLY or ADD_OR_MODIFY. */ if ((pDev->dwFlags & RIDEV_MODEMASK) == RIDEV_REMOVE) { // LATER: too strict?
if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_EXCLUDE || RIDEV_EXMODE(pDev->dwFlags) == RIDEV_NOLEGACY) { RIPERR0(ERROR_INVALID_FLAGS, RIP_WARNING, "HidRequestValidityCheck: remove and (exlude or nolegacy)"); return FALSE; } if (pDev->hwndTarget != NULL) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: hwndTarget is specified for remove operation."); return FALSE; } }
/*
* Check EXMODE */ switch (RIDEV_EXMODE(pDev->dwFlags)) { case RIDEV_EXCLUDE: #ifdef GI_SINK
if (pDev->dwFlags & RIDEV_INPUTSINK) { RIPERR2(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: Exclude request cannot have RIDEV_INPUTSINK for UP=%x, U=%x", pDev->usUsagePage, pDev->usUsage); return FALSE; } /* FALL THROUGH */ #endif
case RIDEV_INCLUDE: if (pDev->usUsage == 0) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: usUsage is 0 without RIDEV_PAGEONLY for UP=%x", pDev->usUsagePage); return FALSE; } break; case RIDEV_PAGEONLY: if (pDev->usUsage != 0) { RIPERR2(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: UsagePage-only has Usage UP=%x, U=%x", pDev->usUsagePage, pDev->usUsage); return FALSE; } break; case RIDEV_NOLEGACY: if (!IsLegacyDevice(pDev->usUsagePage, pDev->usUsage)) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: NOLEGACY is specified to non legacy device."); return FALSE; } break; default: RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: invalid exmode=%x", RIDEV_EXMODE(pDev->dwFlags)); return FALSE; }
/*
* Check if pDev->hwndTarget is a valid handle. */ if (RIDEV_EXMODE(pDev->dwFlags) == RIDEV_EXCLUDE) { #ifdef GI_SINK
if (pDev->dwFlags & RIDEV_INPUTSINK) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: input sink is specified for exclude."); return FALSE; } #endif
if (pDev->hwndTarget != NULL) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: hwndTarget %p cannot be specified for exlusion.", pDev->hwndTarget); return FALSE; } } else { if (pDev->hwndTarget && pwnd == NULL) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: hwndTarget %p is invalid.", pDev->hwndTarget); return FALSE; } if (pwnd && GETPTI(pwnd)->ppi != PpiCurrent()) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: pwndTarget %p belongs to different process", pwnd); return FALSE; } #ifdef GI_SINK
if ((pDev->dwFlags & RIDEV_INPUTSINK) && pwnd == NULL) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "HidRequestValidityCheck: RIDEV_INPUTSINK requires hwndTarget"); return FALSE; } #endif
}
return TRUE; }
/***************************************************************************\
* ClearProcessTableCache * * Clear up the input type cache in the process request table. \***************************************************************************/ void ClearProcessTableCache(PPROCESS_HID_TABLE pHidTable) { pHidTable->pLastRequest = NULL; pHidTable->UsagePageLast = pHidTable->UsageLast = 0; }
/***************************************************************************\
* AdjustLegacyDeviceFlags * * Adjust the request and sink flags for legacy devices in the process * request table, as the last thing in RegisterRawInputDevices. * N.b. sink and raw flags need to be set at the last thing in * RegsiterRawInputDevices, as it may be implicitly requested through the * page-only request. * Also this function sets up the target window for legacy devices. \***************************************************************************/ void AdjustLegacyDeviceFlags(PPROCESSINFO ppi) { PPROCESS_HID_TABLE pHidTable = ppi->pHidTable; PPROCESS_HID_REQUEST phr;
/*
* Adjust the keyboard sink flag and target window. */ if (phr = InProcessDeviceTypeRequestTable(pHidTable, HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD)) {
TAGMSG1(DBGTAG_PNP, "AdjustLegacyDeviceFlags: raw keyboard is requested in ppi=%p", ppi); pHidTable->fRawKeyboard = TRUE;
#ifdef GI_SINK
UserAssert(!phr->fSinkable || phr->spwndTarget); if (pHidTable->fRawKeyboardSink != phr->fSinkable) { TAGMSG2(DBGTAG_PNP, "AdjustLegacyDeviceFlags: kbd prevSink=%x newSink=%x", pHidTable->fRawKeyboardSink, phr->fSinkable); if (phr->fSinkable) { DbgFreInc(cKbdSinks); } else { DbgFreDec(cKbdSinks); } pHidTable->fRawKeyboardSink = phr->fSinkable; } #endif
Lock(&pHidTable->spwndTargetKbd, phr->spwndTarget); } else { TAGMSG1(DBGTAG_PNP, "AdjustLegacyDeviceFlags: raw keyboard is NOT requested in ppi=%p", ppi); pHidTable->fRawKeyboard = pHidTable->fNoLegacyKeyboard = FALSE; pHidTable->fNoHotKeys = FALSE; pHidTable->fAppKeys = FALSE; #ifdef GI_SINK
if (pHidTable->fRawKeyboardSink) { DbgFreDec(cKbdSinks); TAGMSG0(DBGTAG_PNP, "AdjustLegacyDeviceFlags: kbd prevSink was true"); } pHidTable->fRawKeyboardSink = FALSE; #endif
Unlock(&pHidTable->spwndTargetKbd); }
/*
* Adjust the mouse sink flags and target window. */ if (phr = InProcessDeviceTypeRequestTable(pHidTable, HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE)) {
TAGMSG1(DBGTAG_PNP, "AdjustLegacyDeviceFlags: raw mouse is requested in ppi=%p", ppi); pHidTable->fRawMouse = TRUE; #ifdef GI_SINK
UserAssert(!phr->fSinkable || phr->spwndTarget); if (pHidTable->fRawMouseSink != phr->fSinkable) { TAGMSG2(DBGTAG_PNP, "AdjustLegacyDeviceFlags: mouse prevSink=%x newSink=%x", pHidTable->fRawMouseSink, phr->fSinkable); if (phr->fSinkable) { DbgFreInc(cMouseSinks); } else { DbgFreDec(cMouseSinks); } pHidTable->fRawMouseSink = phr->fSinkable; } #endif
Lock(&pHidTable->spwndTargetMouse, phr->spwndTarget); } else { TAGMSG1(DBGTAG_PNP, "AdjustLegacyDeviceFlags: raw mouse is NOT requested in ppi=%p", ppi); pHidTable->fRawMouse = pHidTable->fNoLegacyMouse = pHidTable->fCaptureMouse = FALSE; #ifdef GI_SINK
if (pHidTable->fRawMouseSink) { TAGMSG0(DBGTAG_PNP, "AdjustLegacyDeviceFlags: mouse prevSink was true"); DbgFreDec(cMouseSinks); } pHidTable->fRawMouseSink = FALSE; #endif
Unlock(&pHidTable->spwndTargetMouse); }
#if DBG
/*
* Check NoLegacy and CaptureMouse legitimacy. */ if (!pHidTable->fNoLegacyMouse) { UserAssert(!pHidTable->fCaptureMouse); } #endif
}
/***************************************************************************\
* CleanupFreedTLCInfo * * This routine clears the TLCInfo and PageOnlyReq that are no longer * ref-counted. \***************************************************************************/ VOID CleanupFreedTLCInfo() { PLIST_ENTRY pList;
/*
* The caller has to ensure being in the device list critical section. */ CheckDeviceInfoListCritIn();
/*
* Walk through the list, free the TLCInfo if it's not ref-counted. */ for (pList = gHidRequestTable.TLCInfoList.Flink; pList != &gHidRequestTable.TLCInfoList;) { PHID_TLC_INFO pTLCInfo = CONTAINING_RECORD(pList, HID_TLC_INFO, link);
/*
* Get the next link, before this gets freed. */ pList = pList->Flink;
if (HidTLCInfoNoReference(pTLCInfo)) { TAGMSG3(DBGTAG_PNP, "CleanupFreedTLCInfo: freeing TLCInfo=%p (%x, %x)", pTLCInfo, pTLCInfo->usUsagePage, pTLCInfo->usUsage); FreeHidTLCInfo(pTLCInfo); } }
/*
* Walk though the Page-only request list, free it if it's not ref-counted. */ for (pList = gHidRequestTable.UsagePageList.Flink; pList != &gHidRequestTable.UsagePageList; ) { PHID_PAGEONLY_REQUEST pPOReq = CONTAINING_RECORD(pList, HID_PAGEONLY_REQUEST, link);
/*
* Get the next link before it's freed. */ pList = pList->Flink;
if (pPOReq->cRefCount == 0) { FreeHidPageOnlyRequest(pPOReq); } } }
/***************************************************************************\
* FixupOrphanedExclusiveRequests * * Adjust the exclusiveness counter in the global TLC info. * Sometimes there's orphaned exclusive request that really should not take * global effect. \***************************************************************************/ void FixupOrphanedExclusiveRequests(PPROCESSINFO ppi) { PLIST_ENTRY pList; PPROCESS_HID_TABLE pHidTable = ppi->pHidTable;
for (pList = pHidTable->ExclusionList.Flink; pList != &pHidTable->ExclusionList; pList = pList->Flink) { PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
if (IsLegacyDevice(pHid->usUsagePage, pHid->usUsage)) { UserAssert(pHid->fExclusiveOrphaned == FALSE); } else { PPROCESS_HID_REQUEST pPageOnly;
UserAssert(pHid->spwndTarget == NULL); UserAssert(pHid->pTLCInfo);
/*
* Search if we have the page-only request for this UsagePage. */ pPageOnly = SearchProcessHidRequestUsagePage(pHidTable, pHid->usUsagePage); if (pPageOnly) { /*
* OK, corresponding page-only request is found, this one * is not orphaned. */ if (pHid->fExclusiveOrphaned) { /*
* This request was previously orphaned, but not any more. */ UserAssert(pHid->pTLCInfo->cExcludeOrphaned >= 1); --pHid->pTLCInfo->cExcludeOrphaned; pHid->fExclusiveOrphaned = FALSE; } } else { /*
* This one is orphaned. Let's check the previous state * to see if we need to fix up the counter(s). */ if (!pHid->fExclusiveOrphaned) { /*
* This request was not orphaned, but unfortunately * due to removal of page request or some other reasons, * becoming an orphan. */ ++pHid->pTLCInfo->cExcludeOrphaned; pHid->fExclusiveOrphaned = TRUE; } } UserAssert(pHid->pTLCInfo->cExcludeRequest >= pHid->pTLCInfo->cExcludeOrphaned); } } }
/***************************************************************************\
* _RegisterRawInputDevices * * API helper \***************************************************************************/ BOOL _RegisterRawInputDevices( PCRAWINPUTDEVICE cczpRawInputDevices, UINT uiNumDevices) { PPROCESSINFO ppi; UINT i;
API_PROLOGUE(BOOL, FALSE);
ppi = PpiCurrent(); UserAssert(ppi); UserAssert(uiNumDevices > 0); // should have been checked in the stub
CheckDeviceInfoListCritOut(); EnterDeviceInfoListCrit();
if (ppi->pHidTable) { /*
* Clear the last active UsagePage/Usage, so that * the next read operation will check the updated * request list. */ ClearProcessTableCache(ppi->pHidTable); }
/*
* Firstly validate all the device request. */ for (i = 0; i < uiNumDevices; ++i) { RAWINPUTDEVICE ridDev;
ridDev = cczpRawInputDevices[i];
/*
* Validity check */ if (!HidRequestValidityCheck(&ridDev)) { /*
* Indicate no real change has made. */ i = 0;
/*
* LastError is already set in the above function, * so let's specify zero here. */ API_ERROR(0); } }
/*
* If the process hid request table is not yet allocated, allocate it now. */ if (ppi->pHidTable == NULL) { ppi->pHidTable = AllocateProcessHidTable(); if (ppi->pHidTable == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "_RegisterRawInputDevices: failed to allocate table"); API_ERROR(0); } }
UserAssert(ppi->pHidTable);
for (i = 0; i < uiNumDevices; ++i) { PPROCESS_HID_REQUEST pHid; DWORD dwFlags;
/*
* Check if the requested device type is already in our process hid req list here, * for it's commonly used in the following cases. */ pHid = SearchProcessHidRequest( ppi, cczpRawInputDevices[i].usUsagePage, cczpRawInputDevices[i].usUsage, &dwFlags);
if ((cczpRawInputDevices[i].dwFlags & RIDEV_MODEMASK) == RIDEV_ADD_OR_MODIFY) { if (!SetProcDeviceRequest( ppi, cczpRawInputDevices + i, pHid, dwFlags)) { API_ERROR(0); } } else { /*
* Remove this device, if it's in the list */ if (pHid) { TAGMSG4(DBGTAG_PNP, "_RegisterRawInputDevices: removing type=%x (%x, %x) from ppi=%p", RIDEV_EXMODE(cczpRawInputDevices[i].dwFlags), cczpRawInputDevices[i].usUsagePage, cczpRawInputDevices[i].usUsage, ppi); FreeHidProcessRequest(pHid, dwFlags GI_SINK_PARAM(ppi->pHidTable)); } else { RIPMSG3( RIP_WARNING, "_RegisterRawInputDevices: removing... TLC (%x,%x) is not registered in ppi=%p, but just ignore it", cczpRawInputDevices[i].usUsagePage, cczpRawInputDevices[i].usUsage, ppi); } } }
/*
* Now that we finished updating the process device request and the global request list, * start/stop each device. */ retval = TRUE;
/*
* API cleanup portion */ API_CLEANUP();
if (ppi->pHidTable) { /*
* Adjust the legacy flags in pHidTable. */ AdjustLegacyDeviceFlags(ppi);
/*
* Check if there's orphaned exclusive requests. */ FixupOrphanedExclusiveRequests(ppi);
/*
* Make sure the cache is cleared right. */ UserAssert(ppi->pHidTable->pLastRequest == NULL); UserAssert(ppi->pHidTable->UsagePageLast == 0); UserAssert(ppi->pHidTable->UsageLast == 0);
/*
* Free TLCInfo that are no longer ref-counted. */ CleanupFreedTLCInfo();
/*
* Start or stop reading the HID devices. */ HidDeviceStartStop(); }
CheckupHidCounter();
LeaveDeviceInfoListCrit();
API_EPILOGUE(); }
/***************************************************************************\
* SortRegisteredDevices * * API helper: * This function sorts the registered raw input devices by the shell sort. * O(n^1.2) * N.b. if the array is in the user-mode, this function may raise * an exception, which is supposed to be handled by the caller. \***************************************************************************/
__inline BOOL IsRawInputDeviceLarger( const PRAWINPUTDEVICE pRid1, const PRAWINPUTDEVICE pRid2) { return (DWORD)MAKELONG(pRid1->usUsage, pRid1->usUsagePage) > (DWORD)MAKELONG(pRid2->usUsage, pRid2->usUsagePage); }
void SortRegisteredDevices( PRAWINPUTDEVICE cczpRawInputDevices, const int iSize) { int h;
if (iSize <= 0) { // give up!
return; }
// Calculate starting block size.
for (h = 1; h < iSize / 9; h = 3 * h + 1) { UserAssert(h > 0); }
while (h > 0) { int i;
for (i = h; i < iSize; ++i) { RAWINPUTDEVICE rid = cczpRawInputDevices[i]; int j;
for (j = i - h; j >= 0 && IsRawInputDeviceLarger(&cczpRawInputDevices[j], &rid); j -= h) { cczpRawInputDevices[j + h] = cczpRawInputDevices[j]; } if (i != j + h) { cczpRawInputDevices[j + h] = rid; } } h /= 3; }
#if DBG
// verify
{ int i;
for (i = 1; i < iSize; ++i) { UserAssert(cczpRawInputDevices[i - 1].usUsagePage <= cczpRawInputDevices[i].usUsagePage || cczpRawInputDevices[i - 1].usUsage <= cczpRawInputDevices[i].usUsage); } } #endif
}
/***************************************************************************\
* _GetRegisteredRawInputDevices * * API helper \***************************************************************************/ UINT _GetRegisteredRawInputDevices( PRAWINPUTDEVICE cczpRawInputDevices, PUINT puiNumDevices) { API_PROLOGUE(UINT, (UINT)-1); PPROCESSINFO ppi; UINT uiNumDevices; UINT nDevices = 0;
CheckDeviceInfoListCritOut(); EnterDeviceInfoListCrit();
ppi = PpiCurrent(); UserAssert(ppi);
if (ppi->pHidTable == NULL) { nDevices = 0; } else { PLIST_ENTRY pList;
for (pList = ppi->pHidTable->InclusionList.Flink; pList != &ppi->pHidTable->InclusionList; pList = pList->Flink) { ++nDevices; } TAGMSG2(DBGTAG_PNP, "_GetRawInputDevices: ppi %p # inclusion %x", ppi, nDevices); for (pList = ppi->pHidTable->UsagePageList.Flink; pList != &ppi->pHidTable->UsagePageList; pList = pList->Flink) { ++nDevices; } TAGMSG1(DBGTAG_PNP, "_GetRawInputDevices: # pageonly+inclusion %x", nDevices); for (pList = ppi->pHidTable->ExclusionList.Flink; pList != &ppi->pHidTable->ExclusionList; pList = pList->Flink) { ++nDevices; } TAGMSG1(DBGTAG_PNP, "_GetRawInputDevices: # total hid request %x", nDevices);
/*
* Check Legacy Devices. */ UserAssert(ppi->pHidTable->fRawKeyboard || !ppi->pHidTable->fNoLegacyKeyboard); UserAssert(ppi->pHidTable->fRawMouse || !ppi->pHidTable->fNoLegacyMouse);
TAGMSG1(DBGTAG_PNP, "_GetRawInputDevices: # request including legacy devices %x", nDevices); }
if (cczpRawInputDevices == NULL) { /*
* Return the number of the devices in the per-process device list. */ try { ProbeForWrite(puiNumDevices, sizeof(UINT), sizeof(DWORD)); *puiNumDevices = nDevices; retval = 0; } except (StubExceptionHandler(TRUE)) { API_ERROR(0); } } else { try { ProbeForRead(puiNumDevices, sizeof(UINT), sizeof(DWORD)); uiNumDevices = *puiNumDevices; if (uiNumDevices == 0) { /*
* Non-NULL buffer is specified, but the buffer size is 0. * To probe the buffer right, this case is treated as an error. */ API_ERROR(ERROR_INVALID_PARAMETER); } ProbeForWriteBuffer(cczpRawInputDevices, uiNumDevices, sizeof(DWORD)); } except (StubExceptionHandler(TRUE)) { API_ERROR(0); }
if (ppi->pHidTable == NULL) { retval = 0; } else { PLIST_ENTRY pList; UINT i;
if (uiNumDevices < nDevices) { try { ProbeForWrite(puiNumDevices, sizeof(UINT), sizeof(DWORD)); *puiNumDevices = nDevices; API_ERROR(ERROR_INSUFFICIENT_BUFFER); } except (StubExceptionHandler(TRUE)) { API_ERROR(0); } }
try { for (i = 0, pList = ppi->pHidTable->InclusionList.Flink; pList != &ppi->pHidTable->InclusionList && i < uiNumDevices; pList = pList->Flink, ++i) { RAWINPUTDEVICE device; PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
device.dwFlags = 0; #ifdef GI_SINK
device.dwFlags |= (pHid->fSinkable ? RIDEV_INPUTSINK : 0); #endif
device.usUsagePage = pHid->usUsagePage; device.usUsage = pHid->usUsage; device.hwndTarget = HW(pHid->spwndTarget); if ((IsKeyboardDevice(pHid->usUsagePage, pHid->usUsage) && ppi->pHidTable->fNoLegacyKeyboard) || (IsMouseDevice(pHid->usUsagePage, pHid->usUsage) && ppi->pHidTable->fNoLegacyMouse)) { device.dwFlags |= RIDEV_NOLEGACY; } if (IsKeyboardDevice(pHid->usUsagePage, pHid->usUsage) && ppi->pHidTable->fNoHotKeys) { device.dwFlags |= RIDEV_NOHOTKEYS; } if (IsKeyboardDevice(pHid->usUsagePage, pHid->usUsage) && ppi->pHidTable->fAppKeys) { device.dwFlags |= RIDEV_APPKEYS; } if (IsMouseDevice(pHid->usUsagePage, pHid->usUsage) && ppi->pHidTable->fCaptureMouse) { device.dwFlags |= RIDEV_CAPTUREMOUSE; } cczpRawInputDevices[i] = device;
} for (pList = ppi->pHidTable->UsagePageList.Flink; pList != &ppi->pHidTable->UsagePageList && i < uiNumDevices; pList = pList->Flink, ++i) { RAWINPUTDEVICE device; PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
device.dwFlags = RIDEV_PAGEONLY; #ifdef GI_SINK
device.dwFlags |= (pHid->fSinkable ? RIDEV_INPUTSINK : 0); #endif
device.usUsagePage = pHid->usUsagePage; device.usUsage = pHid->usUsage; device.hwndTarget = HW(pHid->spwndTarget); cczpRawInputDevices[i] = device; } for (pList = ppi->pHidTable->ExclusionList.Flink; pList != &ppi->pHidTable->ExclusionList && i < uiNumDevices; pList = pList->Flink, ++i) { RAWINPUTDEVICE device; PPROCESS_HID_REQUEST pHid = CONTAINING_RECORD(pList, PROCESS_HID_REQUEST, link);
device.dwFlags = RIDEV_EXCLUDE; #ifdef GI_SINK
UserAssert(pHid->fSinkable == FALSE); #endif
device.usUsagePage = pHid->usUsagePage; device.usUsage = pHid->usUsage; device.hwndTarget = NULL; cczpRawInputDevices[i] = device; }
/*
* Sort the array by UsagePage and Usage. */ SortRegisteredDevices(cczpRawInputDevices, (int)nDevices);
retval = nDevices; } except (StubExceptionHandler(TRUE)) { API_ERROR(0); } } }
API_CLEANUP();
LeaveDeviceInfoListCrit();
API_EPILOGUE(); }
/***************************************************************************\
* AllocateHidDesc * * HidDesc allocation \***************************************************************************/ PHIDDESC AllocateHidDesc(PUNICODE_STRING pustrName, PVOID pPreparsedData, PHIDP_CAPS pCaps, PHID_COLLECTION_INFORMATION pHidCollectionInfo) { PHIDDESC pHidDesc;
CheckCritIn();
if (pPreparsedData == NULL) { RIPMSG0(RIP_ERROR, "AllocateHidDesc: pPreparsedData is NULL."); return NULL; }
if (pCaps->InputReportByteLength == 0) { RIPMSG2(RIP_WARNING, "AllocateHidDesc: InputReportByteLength for (%02x, %02x).", pCaps->UsagePage, pCaps->Usage); return NULL; }
pHidDesc = UserAllocPoolZInit(sizeof(HIDDESC), TAG_HIDDESC); if (pHidDesc == NULL) { // Failed to allocate.
RIPMSG1(RIP_WARNING, "AllocateHidDesc: failed to allocated hiddesc. name='%ws'", pustrName->Buffer); return NULL; }
DbgInc(cHidDesc);
/*
* Allocate the input buffer used by the asynchronouse I/O */ pHidDesc->hidpCaps = *pCaps; pHidDesc->pInputBuffer = UserAllocPoolNonPaged(pHidDesc->hidpCaps.InputReportByteLength * MAXIMUM_ITEMS_READ, TAG_PNP); TAGMSG1(DBGTAG_PNP, "AllocateHidDesc: pInputBuffer=%p", pHidDesc->pInputBuffer); if (pHidDesc->pInputBuffer == NULL) { RIPMSG1(RIP_WARNING, "AllocateHidDesc: failed to allocate input buffer (size=%x)", pHidDesc->hidpCaps.InputReportByteLength); FreeHidDesc(pHidDesc); return NULL; }
pHidDesc->pPreparsedData = pPreparsedData; pHidDesc->hidCollectionInfo = *pHidCollectionInfo;
TAGMSG1(DBGTAG_PNP, "AllocateHidDesc: returning %p", pHidDesc);
return pHidDesc;
UNREFERENCED_PARAMETER(pustrName); }
/***************************************************************************\
* FreeHidDesc * * HidDesc destruction \***************************************************************************/ void FreeHidDesc(PHIDDESC pDesc) { CheckCritIn(); UserAssert(pDesc);
TAGMSG2(DBGTAG_PNP | RIP_THERESMORE, "FreeHidDesc entered for (%x, %x)", pDesc->hidpCaps.UsagePage, pDesc->hidpCaps.Usage); TAGMSG1(DBGTAG_PNP, "FreeHidDesc: %p", pDesc);
if (pDesc->pInputBuffer) { UserFreePool(pDesc->pInputBuffer); #if DBG
pDesc->pInputBuffer = NULL; #endif
}
if (pDesc->pPreparsedData) { UserFreePool(pDesc->pPreparsedData); #if DBG
pDesc->pPreparsedData = NULL; #endif
}
UserFreePool(pDesc);
DbgDec(cHidDesc); }
/***************************************************************************\
* AllocateHidData * * HidData allocation * * This function simply calls the HMAllocateObject function. * The rest of the initialization is the responsibility of the caller. \***************************************************************************/ PHIDDATA AllocateHidData( HANDLE hDevice, DWORD dwType, DWORD dwSize, // size of the actual data, not including RAWINPUTHEADER
WPARAM wParam, PWND pwnd) { PHIDDATA pHidData; PTHREADINFO pti;
CheckCritIn();
#if DBG
if (dwType == RIM_TYPEMOUSE) { UserAssert(dwSize == sizeof(RAWMOUSE)); } else if (dwType == RIM_TYPEKEYBOARD) { UserAssert(dwSize == sizeof(RAWKEYBOARD)); } else if (dwType == RIM_TYPEHID) { UserAssert(dwSize > FIELD_OFFSET(RAWHID, bRawData)); } else { UserAssert(FALSE); } #endif
/*
* N.b. The following code is copied from WakeSomeone to determine * which thread will receive the message. * When the code in WakeSomeone changes, the following code should be changed too. * This pti is required for the HIDDATA is specified as thread owned * for some reasons for now. This may be changed later. * * I think having similar duplicated code in pretty far places is not * really a good idea, or HIDDATA may not suit to be thread owned (perhaps * it'll be more clear in the future enhanced model). By making it * thead owned, we don't have to modify the thread cleanup code... * However, I don't see clear advantage other than that. For now, * let's make it thread owned and we'll redo the things later... (hiroyama) */ UserAssert(gpqForeground); UserAssert(gpqForeground && gpqForeground->ptiKeyboard);
if (pwnd) { pti = GETPTI(pwnd); } else { pti = PtiKbdFromQ(gpqForeground); }
UserAssert(pti);
/*
* Allocate the handle. * The next code assumes HIDDATA := HEAD + RAWINPUT. */ pHidData = (PHIDDATA)HMAllocObject(pti, NULL, (BYTE)TYPE_HIDDATA, dwSize + FIELD_OFFSET(HIDDATA, rid.data));
/*
* Recalc the size of RAWINPUT structure. */ dwSize += FIELD_OFFSET(RAWINPUT, data);
if (pHidData) { DbgInc(cHidData);
/*
* Initialize some common part. */ pHidData->spwndTarget = NULL; Lock(&pHidData->spwndTarget, pwnd); pHidData->rid.header.dwSize = dwSize; pHidData->rid.header.dwType = dwType; pHidData->rid.header.hDevice = hDevice; pHidData->rid.header.wParam = wParam; #if LOCK_HIDDEVICEINFO
/*
* do hDevice locking here... */ #endif
}
return pHidData; }
/***************************************************************************\
* FreeHidData * * HidData destruction \***************************************************************************/ void FreeHidData(PHIDDATA pData) { CheckCritIn(); if (!HMMarkObjectDestroy(pData)) { RIPMSG2(RIP_ERROR, "FreeHidData: HIDDATA@%p cannot be destroyed now: cLock=%x", pData, pData->head.cLockObj); return; }
Unlock(&pData->spwndTarget);
HMFreeObject(pData);
DbgDec(cHidData); }
/*
* HID device info creation */
/***************************************************************************\
* xxxHidGetCaps * * Get the interface through IRP and call hidparse.sys!HidP_GetCaps. * (ported from wdm/dvd/class/codguts.c) \***************************************************************************/ NTSTATUS xxxHidGetCaps( IN PDEVICE_OBJECT pDeviceObject, IN PHIDP_PREPARSED_DATA pPreparsedData, OUT PHIDP_CAPS pHidCaps) { NTSTATUS status; KEVENT event; IO_STATUS_BLOCK iosb; PIRP irp; PIO_STACK_LOCATION pIrpStackNext; PHID_INTERFACE_HIDPARSE pHidInterfaceHidParse; PHIDP_GETCAPS pHidpGetCaps = NULL;
CheckCritIn(); CheckDeviceInfoListCritIn();
pHidInterfaceHidParse = UserAllocPoolNonPaged(sizeof *pHidInterfaceHidParse, TAG_PNP); if (pHidInterfaceHidParse == NULL) { RIPMSG0(RIP_WARNING, "xxxHidGetCaps: failed to allocate pHidInterfaceHidParse"); return STATUS_INSUFFICIENT_RESOURCES; } pHidInterfaceHidParse->Size = sizeof *pHidInterfaceHidParse; pHidInterfaceHidParse->Version = 1;
//
// LATER: check out this comment
// There is no file object associated with this Irp, so the event may be located
// on the stack as a non-object manager object.
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pDeviceObject, NULL, 0, NULL, &event, &iosb); if (irp == NULL) { RIPMSG0(RIP_WARNING, "xxxHidGetCaps: failed to allocate Irp."); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
irp->RequestorMode = KernelMode; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; pIrpStackNext = IoGetNextIrpStackLocation(irp); UserAssert(pIrpStackNext);
//
// Create an interface query out of the irp.
//
pIrpStackNext->MinorFunction = IRP_MN_QUERY_INTERFACE; pIrpStackNext->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_HID_INTERFACE_HIDPARSE; pIrpStackNext->Parameters.QueryInterface.Size = sizeof *pHidInterfaceHidParse; pIrpStackNext->Parameters.QueryInterface.Version = 1; pIrpStackNext->Parameters.QueryInterface.Interface = (PINTERFACE)pHidInterfaceHidParse; pIrpStackNext->Parameters.QueryInterface.InterfaceSpecificData = NULL;
status = IoCallDriver(pDeviceObject, irp);
if (status == STATUS_PENDING) { //
// This waits using KernelMode, so that the stack, and therefore the
// event on that stack, is not paged out.
//
TAGMSG1(DBGTAG_PNP, "HidQueryInterface: pending for devobj=%p", pDeviceObject); LeaveDeviceInfoListCrit(); LeaveCrit(); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); EnterCrit(); EnterDeviceInfoListCrit(); status = iosb.Status; } if (status == STATUS_SUCCESS) { UserAssert(pHidInterfaceHidParse->HidpGetCaps); status = pHidInterfaceHidParse->HidpGetCaps(pPreparsedData, pHidCaps); } else { RIPMSG1(RIP_WARNING, "xxxHidGetCaps: failed to get pHidpCaps for devobj=%p", pDeviceObject); }
Cleanup: UserFreePool(pHidInterfaceHidParse);
return status; }
/***************************************************************************\
* GetDeviceObjectPointer * * Description: * This routine returns a pointer to the device object specified by the * object name. It also returns a pointer to the referenced file object * that has been opened to the device that ensures that the device cannot * go away. * To close access to the device, the caller should dereference the file * object pointer. * * Arguments: * ObjectName - Name of the device object for which a pointer is to be * returned. * DesiredAccess - Access desired to the target device object. * ShareAccess - Supplies the types of share access that the caller would like * to the file. * FileObject - Supplies the address of a variable to receive a pointer * to the file object for the device. * DeviceObject - Supplies the address of a variable to receive a pointer * to the device object for the specified device. * Return Value: * The function value is a referenced pointer to the specified device * object, if the device exists. Otherwise, NULL is returned. \***************************************************************************/ NTSTATUS GetDeviceObjectPointer( IN PUNICODE_STRING ObjectName, IN ACCESS_MASK DesiredAccess, IN ULONG ShareAccess, OUT PFILE_OBJECT *FileObject, OUT PDEVICE_OBJECT *DeviceObject) { PFILE_OBJECT fileObject; OBJECT_ATTRIBUTES objectAttributes; HANDLE fileHandle; IO_STATUS_BLOCK ioStatus; NTSTATUS status;
/*
* Initialize the object attributes to open the device. */ InitializeObjectAttributes(&objectAttributes, ObjectName, OBJ_KERNEL_HANDLE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL);
status = ZwOpenFile(&fileHandle, DesiredAccess, &objectAttributes, &ioStatus, ShareAccess, FILE_NON_DIRECTORY_FILE);
if (NT_SUCCESS(status)) { /*
* The open operation was successful. Dereference the file handle * and obtain a pointer to the device object for the handle. */ status = ObReferenceObjectByHandle(fileHandle, 0, *IoFileObjectType, KernelMode, (PVOID *)&fileObject, NULL); if (NT_SUCCESS(status)) { *FileObject = fileObject;
/*
* Get a pointer to the device object for this file. */ *DeviceObject = IoGetRelatedDeviceObject(fileObject); } ZwClose(fileHandle); }
return status; }
/***************************************************************************\
* HidCreateDeviceInfo * \***************************************************************************/ PHIDDESC HidCreateDeviceInfo(PDEVICEINFO pDeviceInfo) { NTSTATUS status; PFILE_OBJECT pFileObject; PDEVICE_OBJECT pDeviceObject; IO_STATUS_BLOCK iob; PHIDDESC pHidDesc = NULL; PBYTE pPreparsedData = NULL; HIDP_CAPS caps; PHID_TLC_INFO pTLCInfo; HID_COLLECTION_INFORMATION hidCollection; KEVENT event; PIRP irp;
UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID);
CheckCritIn(); CheckDeviceInfoListCritIn();
BEGINATOMICCHECK();
TAGMSG0(DBGTAG_PNP, "HidCreateDeviceInfo");
status = GetDeviceObjectPointer(&pDeviceInfo->ustrName, FILE_READ_DATA, FILE_SHARE_READ, &pFileObject, &pDeviceObject);
if (!NT_SUCCESS(status)) { RIPMSGF1(RIP_WARNING, "failed to get the device object pointer. stat=%x", status); goto CleanUp0; }
/*
* Reference the device object. */ UserAssert(pDeviceObject); ObReferenceObject(pDeviceObject); /*
* Remove the reference IoGetDeviceObjectPointer() has put * on the file object. */ UserAssert(pFileObject); ObDereferenceObject(pFileObject);
KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_HID_GET_COLLECTION_INFORMATION, pDeviceObject, NULL, 0, // No Input buffer
&hidCollection, sizeof(hidCollection), // Output buffer
FALSE, // no internal device control
&event, &iob);
if (irp == NULL) { RIPMSGF0(RIP_WARNING, "failed to build IRP 1"); goto CleanUpDeviceObject; }
status = IoCallDriver(pDeviceObject, irp); if (status == STATUS_PENDING) { TAGMSGF0(DBGTAG_PNP, "pending IRP 1."); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = iob.Status; }
if (status != STATUS_SUCCESS) { RIPMSGF0(RIP_WARNING, "IoCallDriver failed!"); goto CleanUpDeviceObject; }
/*
* Get the preparsed data for this device */ pPreparsedData = UserAllocPoolNonPaged(hidCollection.DescriptorSize, TAG_PNP); if (pPreparsedData == NULL) { RIPMSGF0(RIP_WARNING, "failed to allocate preparsed data."); goto CleanUpDeviceObject; }
KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_HID_GET_COLLECTION_DESCRIPTOR, pDeviceObject, NULL, 0, // No input buffer
pPreparsedData, hidCollection.DescriptorSize, // Output
FALSE, &event, &iob); if (irp == NULL) { RIPMSGF0(RIP_WARNING, "failed to build IRP 2"); goto CleanUpPreparsedData; }
status = IoCallDriver(pDeviceObject, irp); if (status == STATUS_PENDING) { RIPMSGF0(RIP_WARNING, "pending 2."); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = iob.Status; }
if (status != STATUS_SUCCESS) { RIPMSGF1(RIP_WARNING, "failed IoCallDriver(2). st=%x", status); goto CleanUpPreparsedData; }
/*
* Get the HID Caps, check it, and store it in HIDDESC. */ status = xxxHidGetCaps(pDeviceObject, (PHIDP_PREPARSED_DATA)pPreparsedData, &caps); if (status != HIDP_STATUS_SUCCESS) { RIPMSGF2(RIP_WARNING, "failed to get caps for devobj=%p. status=%x", pDeviceObject, status); goto CleanUpPreparsedData; }
TAGMSGF2(DBGTAG_PNP | RIP_THERESMORE, "UsagePage=%x, Usage=%x", caps.UsagePage, caps.Usage); TAGMSG2(DBGTAG_PNP, "InputReportByteLength=0x%x, FeatureByteLengt=0x%x", caps.InputReportByteLength, caps.FeatureReportByteLength);
/*
* Check the UsagePage/Usage to reject mice and keyboard devices as HID */ if (caps.UsagePage == HID_USAGE_PAGE_GENERIC) { switch (caps.Usage) { case HID_USAGE_GENERIC_KEYBOARD: case HID_USAGE_GENERIC_MOUSE: case HID_USAGE_GENERIC_POINTER: case HID_USAGE_GENERIC_SYSTEM_CTL: // LATER: what is this really?
TAGMSGF2(DBGTAG_PNP, "(%x, %x) will be ignored.", caps.UsagePage, caps.Usage); goto CleanUpPreparsedData; } } #ifdef OBSOLETE
else if (caps.UsagePage == HID_USAGE_PAGE_CONSUMER) { TAGMSGF0(DBGTAG_PNP, "Consumer device, ignored."); goto CleanUpPreparsedData; } #endif
pHidDesc = AllocateHidDesc(&pDeviceInfo->ustrName, pPreparsedData, &caps, &hidCollection); if (pHidDesc == NULL) { TAGMSGF2(DBGTAG_PNP, "AllocateHidDesc returned NULL for (%x, %x)", caps.UsagePage, caps.Usage); goto CleanUpPreparsedData; }
/*
* Check if there's already a HID request for this type of device. */ pTLCInfo = SearchHidTLCInfo(caps.UsagePage, caps.Usage); if (pTLCInfo) { /*
* Found the one. */ TAGMSGF3(DBGTAG_PNP, "Usage (%x, %x) is already allocated at pTLCInfo=%p.", caps.UsagePage, caps.Usage, pTLCInfo); } else { /*
* HID request for this device type is not yet created, * so create it now. */ pTLCInfo = AllocateAndLinkHidTLCInfo(caps.UsagePage, caps.Usage); if (pTLCInfo == NULL) { RIPMSGF1(RIP_WARNING, "failed to allocate pTLCInfo for DevInfo=%p. Bailing out.", pDeviceInfo); goto CleanUpHidDesc; } TAGMSGF3(DBGTAG_PNP, "HidRequest=%p allocated for (%x, %x)", pTLCInfo, caps.UsagePage, caps.Usage); } /*
* Increment the device ref count of the Hid Request. */ ++pTLCInfo->cDevices; TAGMSGF3(DBGTAG_PNP, "new cDevices of (%x, %x) is 0x%x", caps.UsagePage, caps.Usage, pTLCInfo->cDevices);
/*
* Link the Hid request to pDeviceInfo. */ pDeviceInfo->hid.pTLCInfo = pTLCInfo;
UserAssert(pHidDesc != NULL); ObDereferenceObject(pDeviceObject); goto Succeeded;
CleanUpHidDesc: UserAssert(pHidDesc); FreeHidDesc(pHidDesc); pHidDesc = NULL; /*
* The ownership of pPreparsedData was passed to pHidDesc, * so it's freed in FreeHidDesc. To avoid the double * free, let's skip to the next cleanup code. */ goto CleanUpDeviceObject;
CleanUpPreparsedData: UserAssert(pPreparsedData); UserFreePool(pPreparsedData);
CleanUpDeviceObject: UserAssert(pDeviceObject); ObDereferenceObject(pDeviceObject);
CleanUp0: UserAssert(pHidDesc == NULL);
Succeeded: ENDATOMICCHECK();
return pHidDesc; }
/***************************************************************************\
* HidIsRequestedByThisProcess * * Returns TRUE if the device type is requested by the process. * This routines looks up the cached device type for faster processing. * * N.b. this routine also updates the cache locally. \***************************************************************************/
PPROCESS_HID_REQUEST HidIsRequestedByThisProcess( PDEVICEINFO pDeviceInfo, PPROCESS_HID_TABLE pHidTable) { PPROCESS_HID_REQUEST phr; USAGE usUsagePage, usUsage;
if (pHidTable == NULL) { TAGMSG0(DBGTAG_PNP, "ProcessHidInput: the process is not HID aware."); return FALSE; }
usUsagePage = pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage; usUsage = pDeviceInfo->hid.pHidDesc->hidpCaps.Usage;
if (pHidTable->UsagePageLast == usUsagePage && pHidTable->UsageLast == usUsage) { /*
* The same device type as the last input. */ UserAssert(pHidTable->UsagePageLast && pHidTable->UsageLast); UserAssert(pHidTable->pLastRequest); return pHidTable->pLastRequest; }
phr = InProcessDeviceTypeRequestTable(pHidTable, usUsagePage, usUsage); if (phr) { pHidTable->UsagePageLast = usUsagePage; pHidTable->UsageLast = usUsage; pHidTable->pLastRequest = phr; } return phr; }
#ifdef GI_SINK
BOOL PostHidInput( PDEVICEINFO pDeviceInfo, PQ pq, PWND pwnd, WPARAM wParam) { DWORD dwSizeData = (DWORD)pDeviceInfo->hid.pHidDesc->hidpCaps.InputReportByteLength; DWORD dwLength = (DWORD)pDeviceInfo->iosb.Information; DWORD dwSize; DWORD dwCount; PHIDDATA pHidData;
UserAssert(dwSizeData != 0); #if DBG
if (dwLength > dwSizeData) { TAGMSG2(DBGTAG_PNP, "PostHidInput: multiple input; %x / %x", pDeviceInfo->iosb.Information, dwSizeData); } #endif
/*
* Validate the input length. */ if (dwLength % dwSizeData != 0) { /*
* Input report has invalid length. */ RIPMSG0(RIP_WARNING, "PostHidInput: multiple input: unexpected report size."); return FALSE; } dwCount = dwLength / dwSizeData; UserAssert(dwCount <= MAXIMUM_ITEMS_READ); if (dwCount == 0) { RIPMSG0(RIP_WARNING, "PostHidInput: dwCount == 0"); return FALSE; } UserAssert(dwSizeData * dwCount == dwLength);
/*
* Calculate the required size for RAWHID. */ dwSize = FIELD_OFFSET(RAWHID, bRawData) + dwLength;
/*
* Allocate the input data handle. */ pHidData = AllocateHidData(PtoH(pDeviceInfo), RIM_TYPEHID, dwSize, wParam, pwnd); if (pHidData == NULL) { RIPMSG0(RIP_WARNING, "PostHidInput: failed to allocate HIDDATA."); return FALSE; }
/*
* Fill the data in. */ pHidData->rid.data.hid.dwSizeHid = dwSizeData; pHidData->rid.data.hid.dwCount = dwCount; RtlCopyMemory(pHidData->rid.data.hid.bRawData, pDeviceInfo->hid.pHidDesc->pInputBuffer, dwLength);
#if DBG
{ PBYTE pSrc = pDeviceInfo->hid.pHidDesc->pInputBuffer; PBYTE pDest = pHidData->rid.data.hid.bRawData; DWORD dwCountTmp = 0;
while ((ULONG)(pSrc - (PBYTE)pDeviceInfo->hid.pHidDesc->pInputBuffer) < dwLength) { TAGMSG3(DBGTAG_PNP, "PostHidInput: storing %x th message from %p to %p", dwCountTmp, pSrc, pDest);
pSrc += dwSizeData; pDest += dwSizeData; ++dwCountTmp; }
UserAssert(pHidData->rid.data.hid.dwCount == dwCountTmp); } #endif
/*
* All the data are ready to fly. */ if (!PostInputMessage(pq, pwnd, WM_INPUT, wParam, (LPARAM)PtoH(pHidData), 0, 0)) { /*
* Failed to post the message, hHidData needs to be freed. */ RIPMSG2(RIP_WARNING, "PostInputMessage: failed to post WM_INPUT (%p) to pq=%p", wParam, pq); FreeHidData(pHidData); return FALSE; } return TRUE; }
/***************************************************************************\
* ProcessHidInput (RIT) * * Called from InputAPC for all input from HID devices. \***************************************************************************/
VOID ProcessHidInput(PDEVICEINFO pDeviceInfo) { PPROCESSINFO ppiForeground = NULL; BOOL fProcessed = FALSE;
TAGMSG1(DBGTAG_PNP, "ProcessHidInput: pDeviceInfo=%p", pDeviceInfo); CheckCritOut(); UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID);
if (!NT_SUCCESS(pDeviceInfo->iosb.Status)) { RIPMSG1(RIP_WARNING, "ProcessHidInput: unsuccessful input apc. status=%x", pDeviceInfo->iosb.Status); return; }
EnterCrit();
TAGMSG2(DBGTAG_PNP, "ProcessHidInput: max:%x info:%x", pDeviceInfo->hid.pHidDesc->hidpCaps.InputReportByteLength, pDeviceInfo->iosb.Information);
UserAssert(pDeviceInfo->handle);
if (gpqForeground == NULL) { TAGMSG0(DBGTAG_PNP, "ProcessHidInput: gpqForeground is NULL."); } else { PWND pwnd = NULL; PPROCESS_HID_REQUEST pHidRequest;
UserAssert(PtiKbdFromQ(gpqForeground) != NULL); ppiForeground = PtiKbdFromQ(gpqForeground)->ppi;
pHidRequest = HidIsRequestedByThisProcess(pDeviceInfo, ppiForeground->pHidTable); if (pHidRequest) { PQ pq = gpqForeground;
pwnd = pHidRequest->spwndTarget;
if (pwnd) { /*
* Adjust the foreground queue, if the app specified * the target window. */ pq = GETPTI(pwnd)->pq; }
if (pwnd && TestWF(pwnd, WFINDESTROY)) { /*
* If the target window is in destroy, let's not post * a message, it's just waste of time. */ goto check_sinks; }
if (PostHidInput(pDeviceInfo, pq, pwnd, RIM_INPUT)) { fProcessed = TRUE; } } else { /*
* No request for this device from the foreground process. */ TAGMSG3(DBGTAG_PNP, "ProcessHidInput: (%x, %x) is ignored for ppi=%p.", pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage, pDeviceInfo->hid.pHidDesc->hidpCaps.Usage, PtiKbdFromQ(gpqForeground)->ppi); } }
check_sinks: #ifdef LATER
/*
* Check if multiple process requests this type of devices. */ if (IsSinkRequestedFor(pDeviceInfo)) #endif
{ /*
* Walk through the global sink list and find the sinkable request. */ PLIST_ENTRY pList = gHidRequestTable.ProcessRequestList.Flink;
for (; pList != &gHidRequestTable.ProcessRequestList; pList = pList->Flink) { PPROCESS_HID_TABLE pProcessHidTable = CONTAINING_RECORD(pList, PROCESS_HID_TABLE, link); PPROCESS_HID_REQUEST pHidRequest;
UserAssert(pProcessHidTable); if (pProcessHidTable->nSinks <= 0) { /*
* No sinkable request in this table. */ continue; }
pHidRequest = HidIsRequestedByThisProcess(pDeviceInfo, pProcessHidTable); if (pHidRequest) { PWND pwnd;
UserAssert(pHidRequest->spwndTarget);
if (!pHidRequest->fSinkable) { /*
* It's not a sink. */ continue; }
pwnd = pHidRequest->spwndTarget;
if (GETPTI(pwnd)->ppi == ppiForeground) { /*
* We should have already processed this guy. */ continue; }
if (pwnd->head.rpdesk != grpdeskRitInput) { /*
* This guy belongs to the other desktop, let's skip it. */ continue; } if (TestWF(pwnd, WFINDESTROY) || TestWF(pwnd, WFDESTROYED)) { /*
* The window is being destroyed, let's save some time. */ continue; }
/*
* OK, this guy has the right to receive the sink input. */ TAGMSG2(DBGTAG_PNP, "ProcessRequestList: posting SINK to pwnd=%p pq=%p", pwnd, GETPTI(pwnd)->pq); if (!PostHidInput(pDeviceInfo, GETPTI(pwnd)->pq, pwnd, RIM_INPUTSINK)) { /*
* Something went bad... let's bail out. */ break; } fProcessed = TRUE; } } }
if (fProcessed) { /*
* Exit the video power down mode. */ if (glinp.dwFlags & LINP_POWERTIMEOUTS) { /*
* Call video driver here to exit power down mode. */ TAGMSG0(DBGTAG_Power, "Exit video power down mode"); DrvSetMonitorPowerState(gpDispInfo->pmdev, PowerDeviceD0); }
/*
* Prevents power off: * LATER: devices with possible chattering??? */ glinp.dwFlags = (glinp.dwFlags & ~(LINP_INPUTTIMEOUTS | LINP_INPUTSOURCES)) | LINP_KEYBOARD; glinp.timeLastInputMessage = gpsi->dwLastRITEventTickCount = NtGetTickCount(); /*
* N.b. when win32k starts to support HID input injection, * timeLastInputMessage should only be set after checking gbBlockSendInputResets * and the injection flag. */ CLEAR_SRVIF(SRVIF_LASTRITWASKEYBOARD); }
LeaveCrit(); }
#else
// without SINK
/***************************************************************************\
* ProcessHidInput (RIT) * * Called from InputAPC for all input from HID devices. \***************************************************************************/
VOID ProcessHidInput(PDEVICEINFO pDeviceInfo) { TAGMSG1(DBGTAG_PNP, "ProcessHidInput: pDeviceInfo=%p", pDeviceInfo); CheckCritOut(); UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID);
if (!NT_SUCCESS(pDeviceInfo->iosb.Status)) { RIPMSG1(RIP_WARNING, "ProcessHidInput: unsuccessful input apc. status=%x", pDeviceInfo->iosb.Status); return; }
EnterCrit();
TAGMSG2(DBGTAG_PNP, "ProcessHidInput: max:%x info:%x", pDeviceInfo->hid.pHidDesc->hidpCaps.InputReportByteLength, pDeviceInfo->iosb.Information);
UserAssert(pDeviceInfo->handle); if (gpqForeground == NULL) { RIPMSG0(RIP_WARNING, "ProcessHidInput: gpqForeground is NULL, bailing out."); } else { PWND pwnd = NULL; PPROCESSINFO ppi; PPROCESS_HID_REQUEST pHidRequest;
UserAssert(PtiKbdFromQ(gpqForeground) != NULL); ppi = PtiKbdFromQ(gpqForeground)->ppi;
pHidRequest = HidIsRequestedByThisProcess(pDeviceInfo, ppi->pHidTable); if (pHidRequest) { /*
* The foreground thread has requested the raw input from this type of device. */ PHIDDATA pHidData; DWORD dwSizeData; // size of each report
DWORD dwSize; // size of HIDDATA
DWORD dwCount; // number of report
DWORD dwLength; // length of all input reports
PQ pq;
pwnd = pHidRequest->spwndTarget;
pq = gpqForeground;
if (pwnd) { /*
* Adjust the foreground queue, if the app specified * the target window. */ pq = GETPTI(pwnd)->pq; }
if (pwnd && TestWF(pwnd, WFINDESTROY)) { /*
* If the target window is in destroy, let's not post * a message, it's just waste of time. */ goto exit; }
dwSizeData = (DWORD)pDeviceInfo->hid.pHidDesc->hidpCaps.InputReportByteLength; UserAssert(dwSizeData != 0);
dwLength = (DWORD)pDeviceInfo->iosb.Information; #if DBG
if (dwLength > dwSizeData) { TAGMSG2(DBGTAG_PNP, "ProcessHidInput: multiple input; %x / %x", pDeviceInfo->iosb.Information, dwSizeData); } #endif
/*
* Validate the input length. */ if (dwLength % dwSizeData != 0) { /*
* Input report has invalid length. */ RIPMSG0(RIP_WARNING, "ProcessHidInput: multiple input: unexpected report size."); goto exit; } dwCount = dwLength / dwSizeData; UserAssert(dwCount <= MAXIMUM_ITEMS_READ); if (dwCount == 0) { RIPMSG0(RIP_WARNING, "ProcessHidInput: dwCount == 0"); goto exit; } UserAssert(dwSizeData * dwCount == dwLength);
/*
* Calculate the required size for RAWHID. */ dwSize = FIELD_OFFSET(RAWHID, bRawData) + dwLength;
/*
* Allocate the input data handle. */ pHidData = AllocateHidData(PtoH(pDeviceInfo), RIM_TYPEHID, dwSize, RIM_INPUT, pwnd); if (pHidData == NULL) { RIPMSG0(RIP_WARNING, "ProcessHidInput: failed to allocate HIDDATA."); goto exit; }
/*
* Fill the data in. */ pHidData->rid.data.hid.dwSizeHid = dwSizeData; pHidData->rid.data.hid.dwCount = dwCount; RtlCopyMemory(pHidData->rid.data.hid.bRawData, pDeviceInfo->hid.pHidDesc->pInputBuffer, dwLength);
#if DBG
{ PBYTE pSrc = pDeviceInfo->hid.pHidDesc->pInputBuffer; PBYTE pDest = pHidData->rid.data.hid.bRawData; DWORD dwCountTmp = 0;
while ((ULONG)(pSrc - (PBYTE)pDeviceInfo->hid.pHidDesc->pInputBuffer) < dwLength) { TAGMSG3(DBGTAG_PNP, "ProcessHidInput: storing %x th message from %p to %p", dwCountTmp, pSrc, pDest);
pSrc += dwSizeData; pDest += dwSizeData; ++dwCountTmp; }
UserAssert(pHidData->rid.data.hid.dwCount == dwCountTmp); } #endif
/*
* All the data are ready to fly. */ if (!PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUT, (LPARAM)PtoH(pHidData), 0, 0)) { /*
* Failed to post the message, hHidData needs to be freed. */ FreeHidData(pHidData); }
/*
* Prevents power off: * LATER: devices with possible chattering??? */ glinp.dwFlags &= ~(LINP_INPUTTIMEOUTS | LINP_INPUTSOURCES); glinp.timeLastInputMessage = gpsi->dwLastRITEventTickCount = NtGetTickCount(); if (gpsi->dwLastRITEventTickCount - gpsi->dwLastSystemRITEventTickCountUpdate > SYSTEM_RIT_EVENT_UPDATE_PERIOD) { SharedUserData->LastSystemRITEventTickCount = gpsi->dwLastRITEventTickCount; gpsi->dwLastSystemRITEventTickCountUpdate = gpsi->dwLastRITEventTickCount; }
CLEAR_SRVIF(SRVIF_LASTRITWASKEYBOARD); } else { /*
* No request for this device from the foreground process. */ TAGMSG3(DBGTAG_PNP, "ProcessHidInput: (%x, %x) is ignored for ppi=%p.", pDeviceInfo->hid.pHidDesc->hidpCaps.UsagePage, pDeviceInfo->hid.pHidDesc->hidpCaps.Usage, PtiKbdFromQ(gpqForeground)->ppi); } }
exit: LeaveCrit(); } #endif // GI_SINK
#endif // GENERIC_INPUT
|