727 lines
24 KiB
C
727 lines
24 KiB
C
/*++
|
|
*
|
|
* Component: hidserv.exe
|
|
* File: pnp.c
|
|
* Purpose: routines to support pnp hid devices.
|
|
*
|
|
* Copyright (C) Microsoft Corporation 1997,1998. All rights reserved.
|
|
*
|
|
* WGJ
|
|
--*/
|
|
|
|
#include "hidserv.h"
|
|
#include <cfgmgr32.h>
|
|
#include <tchar.h>
|
|
|
|
BOOL
|
|
OpenHidDevice (
|
|
IN HDEVINFO HardwareDeviceInfo,
|
|
IN PSP_DEVICE_INTERFACE_DATA DeviceInfoData,
|
|
IN OUT PHID_DEVICE *HidDevice
|
|
);
|
|
|
|
BOOL
|
|
RebuildHidDeviceList (
|
|
void
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Do the required PnP things in order to find, the all the HID devices in
|
|
the system at this time.
|
|
--*/
|
|
{
|
|
HDEVINFO hardwareDeviceInfo;
|
|
SP_DEVICE_INTERFACE_DATA deviceInfoData;
|
|
PHID_DEVICE hidDeviceInst;
|
|
GUID hidGuid;
|
|
DWORD i=0;
|
|
PHID_DEVICE pCurrent, pTemp;
|
|
|
|
HidD_GetHidGuid (&hidGuid);
|
|
|
|
TRACE(("Getting class devices"));
|
|
|
|
//
|
|
// Open a handle to the plug and play dev node.
|
|
//
|
|
hardwareDeviceInfo = SetupDiGetClassDevs (
|
|
&hidGuid,
|
|
NULL, // Define no enumerator (global)
|
|
NULL, // Define no
|
|
(DIGCF_PRESENT | // Only Devices present
|
|
DIGCF_DEVICEINTERFACE)); // Function class devices.
|
|
|
|
if (!hardwareDeviceInfo) {
|
|
TRACE(("Get class devices failed"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Take a wild guess to start
|
|
//
|
|
deviceInfoData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
|
|
|
|
TRACE(("Marking existing devnodes"));
|
|
// Unmark all existing nodes. They will be remarked if the device still exists.
|
|
pCurrent = (PHID_DEVICE)HidDeviceList.pNext;
|
|
while (pCurrent) {
|
|
pCurrent->Active = FALSE;
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
TRACE(("Entering loop"));
|
|
while (TRUE) {
|
|
|
|
TRACE(("Enumerating device interfaces"));
|
|
if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, //HDEVINFO
|
|
0, // No care about specific PDOs //PSP_DEVINFO_DATA
|
|
&hidGuid, // LPGUID
|
|
i, //DWORD MemberIndex
|
|
&deviceInfoData)) { //PSP_DEVICE_INTERFACE_DATA
|
|
|
|
TRACE(("Got an item"));
|
|
if (!OpenHidDevice (hardwareDeviceInfo, &deviceInfoData, &hidDeviceInst)) {
|
|
TRACE(("Open hid device failed"));
|
|
} else {
|
|
if (StartHidDevice(hidDeviceInst)) {
|
|
TRACE(("Start hid device succeeded."));
|
|
InsertTailList((PLIST_NODE)&HidDeviceList, (PLIST_NODE)hidDeviceInst);
|
|
} else {
|
|
WARN(("Failed to start hid device. (%x)", hidDeviceInst));
|
|
HidFreeDevice(hidDeviceInst);
|
|
}
|
|
}
|
|
} else {
|
|
DWORD error = GetLastError();
|
|
if (ERROR_NO_MORE_ITEMS == error) {
|
|
TRACE(("No more items. Exitting"));
|
|
break;
|
|
} else {
|
|
WARN(("Unexpected error getting device interface: 0x%xh", error));
|
|
}
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
TRACE(("Removing unmarked device nodes"));
|
|
// RemoveUnmarkedNodes();
|
|
pCurrent = (PHID_DEVICE)HidDeviceList.pNext;
|
|
while (pCurrent) {
|
|
pTemp = pCurrent->pNext;
|
|
if (!pCurrent->Active) {
|
|
INFO(("Device (DevInst = %x) is gone.", pCurrent->DevInst));
|
|
RemoveEntryList((PLIST_NODE)&HidDeviceList, (PLIST_NODE)pCurrent);
|
|
StopHidDevice(pCurrent); // this frees pCurrent
|
|
}
|
|
pCurrent = pTemp;
|
|
}
|
|
|
|
TRACE(("Destroying device info list"));
|
|
SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
HidFreeDevice(PHID_DEVICE HidDevice)
|
|
{
|
|
PHID_DATA data;
|
|
UCHAR j;
|
|
|
|
HidD_FreePreparsedData (HidDevice->Ppd);
|
|
|
|
data = HidDevice->InputData;
|
|
|
|
//
|
|
// Release the button data
|
|
//
|
|
for (j = 0; j < HidDevice->Caps.NumberLinkCollectionNodes; j++, data++) {
|
|
LocalFree(data->ButtonData.PrevUsages);
|
|
LocalFree(data->ButtonData.Usages);
|
|
}
|
|
|
|
LocalFree(HidDevice->InputData);
|
|
LocalFree(HidDevice->InputReportBuffer);
|
|
LocalFree(HidDevice);
|
|
}
|
|
|
|
BOOL
|
|
OpenHidDevice (
|
|
IN HDEVINFO HardwareDeviceInfo,
|
|
IN PSP_DEVICE_INTERFACE_DATA DeviceInfoData,
|
|
IN OUT PHID_DEVICE *HidDevice
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
Given the HardwareDeviceInfo, representing a handle to the plug and
|
|
play information, and deviceInfoData, representing a specific hid device,
|
|
open that device and fill in all the relivant information in the given
|
|
HID_DEVICE structure.
|
|
|
|
return if the open and initialization was successfull or not.
|
|
|
|
--*/
|
|
{
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA functionClassDeviceData = NULL;
|
|
SP_DEVINFO_DATA DevInfoData;
|
|
ULONG predictedLength = 0;
|
|
ULONG requiredLength = 0;
|
|
UCHAR i = 0;
|
|
PHID_DATA data = NULL;
|
|
PHIDP_BUTTON_CAPS pButtonCaps = NULL;
|
|
PHIDP_VALUE_CAPS pValueCaps = NULL;
|
|
USHORT numCaps;
|
|
PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes = NULL;
|
|
PHID_DEVICE hidDevice = NULL;
|
|
|
|
WCHAR buf[512];
|
|
CONFIGRET cr = CR_SUCCESS;
|
|
DEVINST devInst, parentDevInst;
|
|
DWORD len = 0;
|
|
|
|
if (!(hidDevice = LocalAlloc (LPTR, sizeof (HID_DEVICE)))) {
|
|
//
|
|
// Alloc failed. Drop out of the loop and let the device list
|
|
// get deleted.
|
|
//
|
|
WARN(("Alloc HID_DEVICE struct failed."));
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE(("Creating Device Node (%x)", hidDevice));
|
|
//
|
|
// allocate a function class device data structure to receive the
|
|
// goods about this particular device.
|
|
//
|
|
SetupDiGetDeviceInterfaceDetail (
|
|
HardwareDeviceInfo,
|
|
DeviceInfoData,
|
|
NULL, // probing so no output buffer yet
|
|
0, // probing so output buffer length of zero
|
|
&requiredLength,
|
|
NULL); // get the specific dev-node
|
|
|
|
|
|
predictedLength = requiredLength;
|
|
// sizeof (SP_FNCLASS_DEVICE_DATA) + 512;
|
|
|
|
if (!(functionClassDeviceData = LocalAlloc (LPTR, predictedLength))) {
|
|
WARN(("Allocation failed, our of resources!"));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
functionClassDeviceData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoData.DevInst = 0;
|
|
|
|
//
|
|
// Retrieve the information from Plug and Play.
|
|
//
|
|
if (! SetupDiGetDeviceInterfaceDetail (
|
|
HardwareDeviceInfo,
|
|
DeviceInfoData, // PSP_DEVICE_INTERFACE_DATA
|
|
functionClassDeviceData, // PSP_DEVICE_INTERFACE_DETAIL_DATA
|
|
predictedLength,
|
|
&requiredLength,
|
|
&DevInfoData)) { //PSP_DEVINFO_DATA
|
|
WARN(("SetupDiGetDeviceInterfaceDetail failed"));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
INFO(("Just got interface detail for %S", functionClassDeviceData->DevicePath));
|
|
hidDevice->DevInst = DevInfoData.DevInst;
|
|
|
|
//
|
|
// <HACK>
|
|
//
|
|
// Find out it this is a set of speakers with buttons on it. This is for
|
|
// but 136800. We want to only emit WM_APPCOMMANDs for speakers, not the
|
|
// VK. This is because certain games leave the opening movie scene when
|
|
// you press any key, so if someone presses volume down on their speakers
|
|
// it will leave the scene. They just want that to affect volume.
|
|
//
|
|
cr = CM_Get_Parent(&parentDevInst,
|
|
DevInfoData.DevInst,
|
|
0);
|
|
//
|
|
// We need to get the grandparent, then get the child, to make sure that
|
|
// we get the first child in the set. From there, if the child we've got
|
|
// is the same parent of the devnode that we started with, we want to
|
|
// look at its sibling. But if the devnode we've got is different from
|
|
// the parent, then we've got the right one to look at!
|
|
//
|
|
if (cr == CR_SUCCESS) {
|
|
cr = CM_Get_Parent(&devInst,
|
|
parentDevInst,
|
|
0);
|
|
}
|
|
if (cr == CR_SUCCESS) {
|
|
cr = CM_Get_Child(&devInst,
|
|
devInst,
|
|
0);
|
|
}
|
|
|
|
if (cr == CR_SUCCESS) {
|
|
if (devInst == parentDevInst) {
|
|
//
|
|
// Only look at the first sibling, because this covers all sets
|
|
// of speakers currently on the market.
|
|
//
|
|
cr = CM_Get_Sibling(&devInst,
|
|
devInst,
|
|
0);
|
|
}
|
|
|
|
if (cr == CR_SUCCESS) {
|
|
len = sizeof(buf);
|
|
cr = CM_Get_DevNode_Registry_Property(devInst,
|
|
CM_DRP_CLASS,
|
|
NULL,
|
|
buf,
|
|
&len,
|
|
0);
|
|
if (cr == CR_SUCCESS) {
|
|
if (lstrcmpi(TEXT("MEDIA"), buf) == 0) {
|
|
hidDevice->Speakers = TRUE;
|
|
}
|
|
}
|
|
} // else - definitely not speakers
|
|
}
|
|
//
|
|
// </HACK>
|
|
//
|
|
|
|
// Do we already have this device open?
|
|
{
|
|
PHID_DEVICE pCurrent = (PHID_DEVICE)HidDeviceList.pNext;
|
|
|
|
while (pCurrent) {
|
|
if (pCurrent->DevInst == hidDevice->DevInst) break;
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
if (pCurrent) {
|
|
// Yes. Mark it and bail on the new node.
|
|
pCurrent->Active = TRUE;
|
|
INFO(("Device (DevInst = %x) already open.", DevInfoData.DevInst));
|
|
goto OpenHidDeviceError;
|
|
} else {
|
|
// No. Mark the new node and continue.
|
|
INFO(("Device (DevInst = %x) is new.", DevInfoData.DevInst));
|
|
hidDevice->Active = TRUE;
|
|
}
|
|
}
|
|
|
|
hidDevice->HidDevice = CreateFile (
|
|
functionClassDeviceData->DevicePath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, // no SECURITY_ATTRIBUTES structure
|
|
OPEN_EXISTING, // No special create flags
|
|
FILE_FLAG_OVERLAPPED, // Do overlapped read/write
|
|
NULL); // No template file
|
|
|
|
if (INVALID_HANDLE_VALUE == hidDevice->HidDevice) {
|
|
INFO(("CreateFile failed - %x (%S)", GetLastError(), functionClassDeviceData->DevicePath));
|
|
goto OpenHidDeviceError;
|
|
} else {
|
|
INFO(("CreateFile succeeded Handle(%x) - %S", hidDevice->HidDevice, functionClassDeviceData->DevicePath));
|
|
}
|
|
|
|
if (!HidD_GetPreparsedData (hidDevice->HidDevice, &hidDevice->Ppd)) {
|
|
WARN(("HidD_GetPreparsedData failed"));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
if (!HidD_GetAttributes (hidDevice->HidDevice, &hidDevice->Attributes)) {
|
|
WARN(("HidD_GetAttributes failed"));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
if (!HidP_GetCaps (hidDevice->Ppd, &hidDevice->Caps)) {
|
|
WARN(("HidP_GetCaps failed"));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
// ***Instructive comment from KenRay:
|
|
// At this point the client has a choice. It may chose to look at the
|
|
// Usage and Page of the top level collection found in the HIDP_CAPS
|
|
// structure. In this way it could just use the usages it knows about.
|
|
// If either HidP_GetUsages or HidP_GetUsageValue return an error then
|
|
// that particular usage does not exist in the report.
|
|
// This is most likely the preferred method as the application can only
|
|
// use usages of which it already knows.
|
|
// In this case the app need not even call GetButtonCaps or GetValueCaps.
|
|
|
|
|
|
// If this is a collection we care about, continue. Else, we get out now.
|
|
if (hidDevice->Caps.UsagePage != HIDSERV_USAGE_PAGE) {
|
|
TRACE(("This device is not for us (%x)", hidDevice));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
//
|
|
// setup Input Data buffers.
|
|
//
|
|
TRACE(("NumberLinkCollectionNodes = %d", hidDevice->Caps.NumberLinkCollectionNodes));
|
|
{
|
|
ULONG numNodes = hidDevice->Caps.NumberLinkCollectionNodes;
|
|
|
|
if (!(LinkCollectionNodes = LocalAlloc(LPTR, hidDevice->Caps.NumberLinkCollectionNodes*sizeof(HIDP_LINK_COLLECTION_NODE)))) {
|
|
WARN(("LinkCollectionNodes alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
HidP_GetLinkCollectionNodes(LinkCollectionNodes,
|
|
&numNodes,
|
|
hidDevice->Ppd);
|
|
for (i=0; i<hidDevice->Caps.NumberLinkCollectionNodes; i++) {
|
|
INFO(("Link Collection [%d] Type = %x, Alias = %x", i, LinkCollectionNodes[i].CollectionType, LinkCollectionNodes[i].IsAlias));
|
|
INFO(("Link Collection [%d] Page = %x, Usage = %x", i, LinkCollectionNodes[i].LinkUsagePage, LinkCollectionNodes[i].LinkUsage));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate memory to hold on input report
|
|
//
|
|
|
|
if (!(hidDevice->InputReportBuffer = (PCHAR)
|
|
LocalAlloc (LPTR, hidDevice->Caps.InputReportByteLength * sizeof (CHAR)))) {
|
|
WARN(("InputReportBuffer alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
//
|
|
// Allocate memory to hold the button and value capabilities.
|
|
// NumberXXCaps is in terms of array elements.
|
|
//
|
|
if (!(pButtonCaps = (PHIDP_BUTTON_CAPS)
|
|
LocalAlloc (LPTR, hidDevice->Caps.NumberInputButtonCaps*sizeof (HIDP_BUTTON_CAPS)))) {
|
|
WARN(("buttoncaps alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
if (!(pValueCaps = (PHIDP_VALUE_CAPS)
|
|
LocalAlloc (LPTR, hidDevice->Caps.NumberInputValueCaps*sizeof (HIDP_VALUE_CAPS)))) {
|
|
WARN(("valuecaps alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
//
|
|
// Have the HidP_X functions fill in the capability structure arrays.
|
|
//
|
|
numCaps = hidDevice->Caps.NumberInputButtonCaps;
|
|
TRACE(("NumberInputButtonCaps = %d", numCaps));
|
|
HidP_GetButtonCaps (HidP_Input,
|
|
pButtonCaps,
|
|
&numCaps,
|
|
hidDevice->Ppd);
|
|
|
|
numCaps = hidDevice->Caps.NumberInputValueCaps;
|
|
TRACE(("NumberInputValueCaps = %d", numCaps));
|
|
HidP_GetValueCaps (HidP_Input,
|
|
pValueCaps,
|
|
&numCaps,
|
|
hidDevice->Ppd);
|
|
|
|
TRACE(("Buttons:"));
|
|
for (i=0; i<hidDevice->Caps.NumberInputButtonCaps; i++) {
|
|
TRACE(("UsagePage = 0x%x", pButtonCaps[i].UsagePage));
|
|
TRACE(("LinkUsage = 0x%x", pButtonCaps[i].LinkUsage));
|
|
TRACE(("LinkUsagePage = 0x%x\n", pButtonCaps[i].LinkUsagePage));
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to hold the struct _HID_DATA structures.
|
|
//
|
|
hidDevice->InputDataLength = hidDevice->Caps.NumberLinkCollectionNodes +
|
|
hidDevice->Caps.NumberInputValueCaps;
|
|
if (!(hidDevice->InputData = data = (PHID_DATA)
|
|
LocalAlloc (LPTR, hidDevice->InputDataLength * sizeof (HID_DATA)))) {
|
|
WARN(("InputData alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
|
|
TRACE(("InputDataLength = %d", hidDevice->InputDataLength));
|
|
|
|
//
|
|
// Fill in the button data
|
|
// Group button sets by link collection.
|
|
//
|
|
for (i = 0; i < hidDevice->Caps.NumberLinkCollectionNodes; i++, data++) {
|
|
data->IsButtonData = TRUE;
|
|
data->LinkUsage = LinkCollectionNodes[i].LinkUsage;
|
|
data->UsagePage = LinkCollectionNodes[i].LinkUsagePage;
|
|
if (i)
|
|
data->LinkCollection = i;
|
|
else
|
|
data->LinkCollection = HIDP_LINK_COLLECTION_ROOT;
|
|
INFO(("Button Link Usage = %x", data->LinkUsage));
|
|
INFO(("Button Link Usage Page = %x", data->UsagePage));
|
|
INFO(("Button Link Collection = %x", data->LinkCollection));
|
|
data->Status = HIDP_STATUS_SUCCESS;
|
|
data->ButtonData.MaxUsageLength = HidP_MaxUsageListLength (
|
|
HidP_Input,
|
|
hidDevice->Caps.UsagePage,
|
|
hidDevice->Ppd);
|
|
//make room for the terminator
|
|
data->ButtonData.MaxUsageLength++;
|
|
if (!(data->ButtonData.Usages = (PUSAGE_AND_PAGE)
|
|
LocalAlloc (LPTR, data->ButtonData.MaxUsageLength * sizeof (USAGE_AND_PAGE)))) {
|
|
WARN(("Usages alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
if (!(data->ButtonData.PrevUsages = (PUSAGE_AND_PAGE)
|
|
LocalAlloc (LPTR, data->ButtonData.MaxUsageLength * sizeof (USAGE_AND_PAGE)))) {
|
|
WARN(("PrevUsages alloc failed."));
|
|
goto OpenHidDeviceError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill in the value data
|
|
//
|
|
for (i = 0; i < hidDevice->Caps.NumberInputValueCaps; i++, data++) {
|
|
if (pValueCaps[i].IsRange) {
|
|
WARN(("Can't handle value ranges!!"));
|
|
}
|
|
data->IsButtonData = FALSE;
|
|
data->LinkUsage = pValueCaps[i].LinkUsage;
|
|
data->UsagePage = pValueCaps[i].LinkUsagePage;
|
|
if (pValueCaps[i].LinkCollection)
|
|
data->LinkCollection = pValueCaps[i].LinkCollection;
|
|
else
|
|
data->LinkCollection = HIDP_LINK_COLLECTION_ROOT;
|
|
INFO(("Value Link Usage = %x", data->LinkUsage));
|
|
INFO(("Value Link Usage Page = %x", data->UsagePage));
|
|
INFO(("Value Link Collection = %x", data->LinkCollection));
|
|
INFO(("Value LogicalMin = %x", pValueCaps[i].LogicalMin));
|
|
INFO(("Value LogicalMax = %x", pValueCaps[i].LogicalMax));
|
|
data->ValueData.LogicalRange = pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
|
|
data->Status = HIDP_STATUS_SUCCESS;
|
|
data->ValueData.Usage = pValueCaps[i].NotRange.Usage;
|
|
}
|
|
|
|
LocalFree(pButtonCaps);
|
|
LocalFree(pValueCaps);
|
|
LocalFree(LinkCollectionNodes);
|
|
LocalFree(functionClassDeviceData);
|
|
|
|
*HidDevice = hidDevice;
|
|
return TRUE;
|
|
OpenHidDeviceError:
|
|
if (data) {
|
|
for (i = 0; i < hidDevice->Caps.NumberLinkCollectionNodes; i++, data++) {
|
|
if (data->ButtonData.Usages) {
|
|
LocalFree(data->ButtonData.Usages);
|
|
}
|
|
if (data->ButtonData.PrevUsages) {
|
|
LocalFree(data->ButtonData.PrevUsages);
|
|
}
|
|
}
|
|
LocalFree(data);
|
|
}
|
|
|
|
if (pValueCaps) {
|
|
LocalFree(pValueCaps);
|
|
}
|
|
if (pButtonCaps) {
|
|
LocalFree (pButtonCaps);
|
|
}
|
|
if (hidDevice->InputReportBuffer) {
|
|
LocalFree (hidDevice->InputReportBuffer);
|
|
}
|
|
if (LinkCollectionNodes) {
|
|
LocalFree (LinkCollectionNodes);
|
|
}
|
|
if (hidDevice->Ppd) {
|
|
HidD_FreePreparsedData (hidDevice->Ppd);
|
|
}
|
|
if (hidDevice->HidDevice &&
|
|
hidDevice->HidDevice != INVALID_HANDLE_VALUE) {
|
|
CloseHandle (hidDevice->HidDevice);
|
|
}
|
|
if (functionClassDeviceData) {
|
|
LocalFree (functionClassDeviceData);
|
|
}
|
|
LocalFree (hidDevice);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
StartHidDevice(
|
|
PHID_DEVICE pHidDevice
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
Create a work thread to go with the new hid device. This thread lives
|
|
as long as the associated hid device is open.
|
|
--*/
|
|
{
|
|
//
|
|
// Init read sync objects
|
|
//
|
|
pHidDevice->ReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!pHidDevice->ReadEvent) {
|
|
WARN(("Failed creating read event."));
|
|
return FALSE;
|
|
}
|
|
|
|
pHidDevice->CompletionEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (!pHidDevice->CompletionEvent) {
|
|
CloseHandle(pHidDevice->ReadEvent);
|
|
|
|
WARN(("Failed creating read event."));
|
|
return FALSE;
|
|
}
|
|
|
|
// event handle for overlap.
|
|
pHidDevice->Overlap.hEvent = pHidDevice->CompletionEvent;
|
|
|
|
//
|
|
// Create hid work thread
|
|
//
|
|
pHidDevice->fThreadEnabled = TRUE;
|
|
|
|
pHidDevice->ThreadHandle =
|
|
CreateThread(
|
|
NULL, // pointer to thread security attributes
|
|
0, // initial thread stack size, in bytes (0 = default)
|
|
HidThreadProc, // pointer to thread function
|
|
pHidDevice, // argument for new thread
|
|
0, // creation flags
|
|
&pHidDevice->ThreadId // pointer to returned thread identifier
|
|
);
|
|
|
|
if (!pHidDevice->ThreadHandle) {
|
|
CloseHandle(pHidDevice->ReadEvent);
|
|
CloseHandle(pHidDevice->CompletionEvent);
|
|
|
|
WARN(("Failed creating hid work thread."));
|
|
return FALSE;
|
|
}
|
|
|
|
// Register device nofication for this file handle
|
|
// This only required for NT5
|
|
{
|
|
DEV_BROADCAST_HANDLE DevHdr;
|
|
ZeroMemory(&DevHdr, sizeof(DevHdr));
|
|
DevHdr.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
|
DevHdr.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
DevHdr.dbch_handle = pHidDevice->HidDevice;
|
|
|
|
pHidDevice->hNotify =
|
|
RegisterDeviceNotification( hWndHidServ,
|
|
&DevHdr,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
|
|
if (!pHidDevice->hNotify) {
|
|
WARN(("RegisterDeviceNotification failure (%x).", GetLastError()));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start the read
|
|
//
|
|
SetEvent(pHidDevice->ReadEvent);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
StopHidDevice(
|
|
PHID_DEVICE pHidDevice
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
Eaxh device has a thread that needs to be cleaned up when the device
|
|
is "stopped". Here we signal the thread to exit and clean up.
|
|
--*/
|
|
{
|
|
HANDLE hThreadHandle;
|
|
DWORD dwResult;
|
|
|
|
TRACE(("StopHidDevice (%x)", pHidDevice));
|
|
// without a device, nothing can be done.
|
|
if (!pHidDevice) return FALSE;
|
|
|
|
// Doing this here prevents us from seeing
|
|
// DBT_DEVICEQUERYREMOVEFAILED since the notify handle
|
|
// is gone. However, this is acceptable since there is
|
|
// nothing useful we will do in response to that event
|
|
// anyway.
|
|
UnregisterDeviceNotification(pHidDevice->hNotify);
|
|
hThreadHandle = pHidDevice->ThreadHandle;
|
|
|
|
//
|
|
// Allow the hid work thread to exit.
|
|
//
|
|
pHidDevice->fThreadEnabled = FALSE;
|
|
|
|
// Signal the read event, in case thread is waiting there
|
|
INFO(("Set Read Event."));
|
|
SetEvent(pHidDevice->ReadEvent);
|
|
INFO(("Waiting for work thread to exit..."));
|
|
WaitForSingleObject(hThreadHandle, INFINITE);
|
|
|
|
TRACE(("StopHidDevice (%x) done.", pHidDevice));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DestroyHidDeviceList(
|
|
void
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
Unlike a rebuild, all devices here are closed so the process can
|
|
exit.
|
|
--*/
|
|
{
|
|
PHID_DEVICE pNext, pCurrent = (PHID_DEVICE)HidDeviceList.pNext;
|
|
while (pCurrent) {
|
|
|
|
RemoveEntryList((PLIST_NODE)&HidDeviceList, (PLIST_NODE)pCurrent);
|
|
pNext = pCurrent->pNext;
|
|
StopHidDevice(pCurrent);
|
|
|
|
pCurrent = pNext;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DestroyDeviceByHandle(
|
|
HANDLE hDevice
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
Here we need to remove a specific device.
|
|
--*/
|
|
{
|
|
PHID_DEVICE pCurrent = (PHID_DEVICE)HidDeviceList.pNext;
|
|
|
|
while (pCurrent) {
|
|
|
|
if (hDevice == pCurrent->HidDevice) {
|
|
RemoveEntryList((PLIST_NODE)&HidDeviceList, (PLIST_NODE)pCurrent);
|
|
#if WIN95_BUILD
|
|
//
|
|
// Can't do the UnregisterDeviceNotification in the same context
|
|
// as when we receive the WM_DEVICECHANGE DBT_REMOVEDEVICECOMPLETE
|
|
// for a DBT_DEVTYP_HANDLE
|
|
//
|
|
PostMessage(hWndHidServ, WM_HIDSERV_STOP_DEVICE, 0, (LPARAM)pCurrent);
|
|
#else
|
|
StopHidDevice(pCurrent);
|
|
#endif // WIN95_BUILD
|
|
break;
|
|
}
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|