/****************************** 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