You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3541 lines
117 KiB
3541 lines
117 KiB
/****************************** 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
|