714 lines
23 KiB
C
714 lines
23 KiB
C
/*
|
|
********************************************************************************
|
|
*
|
|
* VXDCLNT.C
|
|
*
|
|
*
|
|
* VXDCLNT - Sample Ring-0 HID device mapper for Memphis
|
|
*
|
|
* Copyright 1997 Microsoft Corp.
|
|
*
|
|
* (ep)
|
|
*
|
|
********************************************************************************
|
|
*/
|
|
|
|
|
|
#define INITGUID
|
|
|
|
#include "vxdclnt.h"
|
|
|
|
|
|
|
|
|
|
deviceContext *firstDevice = NULL, *lastDevice = NULL;
|
|
VMM_SEMAPHORE shutdownSemaphore = (VMM_SEMAPHORE)NULL;
|
|
BOOL ShutDown = FALSE;
|
|
|
|
/*
|
|
* Import function pointers
|
|
*/
|
|
t_pHidP_GetUsageValue pHidP_GetUsageValue = NULL;
|
|
t_pHidP_GetScaledUsageValue pHidP_GetScaledUsageValue = NULL;
|
|
t_pHidP_SetUsages pHidP_SetUsages = NULL;
|
|
t_pHidP_GetUsages pHidP_GetUsages = NULL;
|
|
t_pHidP_MaxUsageListLength pHidP_MaxUsageListLength = NULL;
|
|
t_pIoGetDeviceClassAssociations pIoGetDeviceClassAssociations = NULL;
|
|
t_pHidP_GetCaps pHidP_GetCaps = NULL;
|
|
t_pHidP_GetValueCaps pHidP_GetValueCaps = NULL;
|
|
|
|
|
|
#ifdef DEBUG
|
|
UINT dbgOpt = 0;
|
|
#endif
|
|
|
|
/*
|
|
* GetImportFunctionPtrs
|
|
*
|
|
* Set global pointers to imported functions from HIDPARSE and NTKERN.
|
|
*/
|
|
BOOL GetImportFunctionPtrs()
|
|
{
|
|
static BOOL haveAllPtrs = FALSE;
|
|
|
|
if (!haveAllPtrs){
|
|
pHidP_GetUsageValue = (t_pHidP_GetUsageValue)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetUsageValue", NULL);
|
|
pHidP_GetScaledUsageValue = (t_pHidP_GetScaledUsageValue)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetScaledUsageValue", NULL);
|
|
pHidP_GetUsages = (t_pHidP_GetUsages)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetUsages", NULL);
|
|
pHidP_SetUsages = (t_pHidP_SetUsages)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_SetUsages", NULL);
|
|
pHidP_MaxUsageListLength = (t_pHidP_MaxUsageListLength)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_MaxUsageListLength", NULL);
|
|
pIoGetDeviceClassAssociations = (t_pIoGetDeviceClassAssociations)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"ntpnp.sys", "IoGetDeviceClassAssociations", NULL);
|
|
pHidP_GetCaps = (t_pHidP_GetCaps)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetCaps", NULL);
|
|
pHidP_GetValueCaps = (t_pHidP_GetValueCaps)
|
|
_PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetValueCaps", NULL);
|
|
|
|
if ( pHidP_GetUsageValue &&
|
|
pHidP_GetScaledUsageValue &&
|
|
pHidP_GetUsages &&
|
|
pHidP_SetUsages &&
|
|
pHidP_MaxUsageListLength &&
|
|
pIoGetDeviceClassAssociations &&
|
|
pHidP_GetCaps &&
|
|
pHidP_GetValueCaps){
|
|
|
|
haveAllPtrs = TRUE;
|
|
}
|
|
}
|
|
|
|
return haveAllPtrs;
|
|
}
|
|
|
|
|
|
/*
|
|
* WStrLen
|
|
*
|
|
*/
|
|
ULONG WStrLen(PWCHAR str)
|
|
{
|
|
ULONG result = 0;
|
|
while (*str++){
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* NewDevice
|
|
*
|
|
*
|
|
*/
|
|
deviceContext *NewDevice( HANDLE devHandle,
|
|
PHIDP_CAPS caps,
|
|
PHIDP_PREPARSED_DATA desc,
|
|
UINT descSize,
|
|
PWCHAR deviceFileName)
|
|
{
|
|
deviceContext *newDevice;
|
|
|
|
DBGOUT(("NewDevice()"));
|
|
|
|
newDevice = (deviceContext *)_HeapAllocate(sizeof(deviceContext), 0);
|
|
if (newDevice){
|
|
NTSTATUS ntstat;
|
|
ULONG valueCapsLen;
|
|
|
|
DBGOUT(("Allocated new device @ %xh ", (UINT)newDevice));
|
|
|
|
RtlZeroMemory(newDevice, sizeof(deviceContext));
|
|
|
|
newDevice->devHandle = devHandle;
|
|
newDevice->readPending = FALSE;
|
|
newDevice->next = NULL;
|
|
|
|
RtlCopyMemory( (PVOID)&newDevice->deviceFileName,
|
|
(PVOID)deviceFileName,
|
|
(WStrLen(deviceFileName)*sizeof(WCHAR))+sizeof(UNICODE_NULL));
|
|
RtlCopyMemory((PVOID)&newDevice->hidCapabilities, (PVOID)caps, sizeof(HIDP_CAPS));
|
|
ExInitializeWorkItem(&newDevice->workItemRead, WorkItemCallback_Read, newDevice);
|
|
ExInitializeWorkItem(&newDevice->workItemWrite, WorkItemCallback_Write, newDevice);
|
|
|
|
/*
|
|
* Allocate space for the device descriptor.
|
|
*/
|
|
newDevice->hidDescriptor = (PHIDP_PREPARSED_DATA)_HeapAllocate(descSize, 0);
|
|
if (newDevice->hidDescriptor){
|
|
RtlCopyMemory((PVOID)newDevice->hidDescriptor, (PVOID)desc, descSize);
|
|
}
|
|
else {
|
|
DBGERR(("_HeapAllocate for HID descriptor failed in NewDevice()"));
|
|
goto _deviceInitError;
|
|
}
|
|
|
|
newDevice->writeReportQueueSemaphore = Create_Semaphore(0);
|
|
if (!newDevice->writeReportQueueSemaphore){
|
|
goto _deviceInitError;
|
|
}
|
|
|
|
/*
|
|
* Allocate space for the device report.
|
|
*/
|
|
newDevice->report = (PUCHAR)_HeapAllocate(newDevice->hidCapabilities.InputReportByteLength, 0);
|
|
if (!newDevice->report){
|
|
DBGERR(("_HeapAllocate for report buffer failed in NewDevice()"));
|
|
goto _deviceInitError;
|
|
}
|
|
|
|
/*
|
|
* Figure out the length of the buttons value
|
|
* and allocate a buffer for reading the buttons.
|
|
*/
|
|
newDevice->buttonListLength = pHidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_BUTTON, newDevice->hidDescriptor);
|
|
DBGOUT(("Button values list length = %d.", newDevice->buttonListLength));
|
|
if (newDevice->buttonListLength){
|
|
newDevice->buttonValues = (PUSAGE) _HeapAllocate(newDevice->buttonListLength * sizeof (USAGE), 0);
|
|
if (newDevice->buttonValues){
|
|
RtlZeroMemory(newDevice->buttonValues, newDevice->buttonListLength);
|
|
}
|
|
else {
|
|
DBGERR(("HeapAlloc failed for button values buffer"));
|
|
goto _deviceInitError;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate the array of value-caps.
|
|
*/
|
|
valueCapsLen = caps->NumberInputValueCaps;
|
|
if (valueCapsLen){
|
|
newDevice->valueCaps = (PHIDP_VALUE_CAPS)_HeapAllocate(valueCapsLen*sizeof(HIDP_VALUE_CAPS), 0);
|
|
if (!newDevice->valueCaps){
|
|
DBGERR(("HeapAlloc failed for value caps"));
|
|
goto _deviceInitError;
|
|
}
|
|
|
|
ntstat = pHidP_GetValueCaps(HidP_Input, newDevice->valueCaps, &valueCapsLen, desc);
|
|
if (NT_SUCCESS(ntstat)){
|
|
/*
|
|
* Read valueCaps structure for information about the types of
|
|
* values returned by the device.
|
|
*/
|
|
|
|
}
|
|
else {
|
|
DBGERR(("HidP_GetValueCaps failed with %xh", ntstat));
|
|
goto _deviceInitError;
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("value caps length = 0!"));
|
|
goto _deviceInitError;
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("_HeapAllocate failed in NewDevice()"));
|
|
goto _deviceInitError;
|
|
}
|
|
|
|
return newDevice;
|
|
|
|
_deviceInitError:
|
|
if (newDevice){
|
|
if (newDevice->hidDescriptor){
|
|
_HeapFree(newDevice->hidDescriptor, 0);
|
|
}
|
|
if (newDevice->report){
|
|
_HeapFree(newDevice->report, 0);
|
|
}
|
|
if (newDevice->buttonValues){
|
|
_HeapFree(newDevice->buttonValues, 0);
|
|
}
|
|
if (newDevice->valueCaps){
|
|
_HeapFree(newDevice->valueCaps, 0);
|
|
}
|
|
_HeapFree(newDevice, 0);
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* EnqueueDevice
|
|
*
|
|
*/
|
|
VOID EnqueueDevice(deviceContext *device)
|
|
{
|
|
if (lastDevice){
|
|
lastDevice->next = device;
|
|
lastDevice = device;
|
|
}
|
|
else {
|
|
firstDevice = lastDevice = device;
|
|
}
|
|
device->next = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* DequeueDevice
|
|
*
|
|
*/
|
|
VOID DequeueDevice(deviceContext *device)
|
|
{
|
|
deviceContext *prevDevice, *thisDevice;
|
|
|
|
thisDevice = firstDevice;
|
|
prevDevice = NULL;
|
|
while (thisDevice){
|
|
if (thisDevice == device){
|
|
if (prevDevice){
|
|
prevDevice->next = thisDevice->next;
|
|
if (!thisDevice->next){
|
|
lastDevice = prevDevice;
|
|
}
|
|
}
|
|
else {
|
|
if (thisDevice->next){
|
|
firstDevice = thisDevice->next;
|
|
}
|
|
else {
|
|
firstDevice = lastDevice = NULL;
|
|
}
|
|
}
|
|
|
|
thisDevice->next = NULL;
|
|
break;
|
|
}
|
|
else {
|
|
prevDevice = thisDevice;
|
|
thisDevice = thisDevice->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* DestroyDevice
|
|
*
|
|
* Destroy the device context.
|
|
* This function assumes the device context has already been dequeued
|
|
* from the global list headed by firstDevice.
|
|
*
|
|
*/
|
|
VOID DestroyDevice(deviceContext *device)
|
|
{
|
|
DBGOUT(("==> DestroyDevice()"));
|
|
|
|
/*
|
|
* Modify the device's internal workItem to do a close instead of a read.
|
|
* Then queue the work item so that NtClose is called on a worker thread.
|
|
*/
|
|
ExInitializeWorkItem(&device->workItemClose, WorkItemCallback_Close, device);
|
|
_NtKernQueueWorkItem(&device->workItemClose, DelayedWorkQueue);
|
|
|
|
DBGOUT(("<== DestroyDevice()"));
|
|
}
|
|
|
|
|
|
/*
|
|
* WorkItemCallback_Close
|
|
*
|
|
*/
|
|
VOID WorkItemCallback_Close(PVOID context)
|
|
{
|
|
deviceContext *device = (deviceContext *)context;
|
|
|
|
DBGOUT(("==> WorkItemCallback_Close()"));
|
|
|
|
_NtKernClose(device->devHandle);
|
|
|
|
if (device->hidDescriptor){
|
|
_HeapFree(device->hidDescriptor, 0);
|
|
}
|
|
if (device->report){
|
|
_HeapFree(device->report, 0);
|
|
}
|
|
if (device->buttonValues){
|
|
_HeapFree(device->buttonValues, 0);
|
|
}
|
|
if (device->valueCaps){
|
|
_HeapFree(device->valueCaps, 0);
|
|
}
|
|
if (device->writeReportQueueSemaphore){
|
|
Destroy_Semaphore(device->writeReportQueueSemaphore);
|
|
}
|
|
|
|
_HeapFree(device, 0);
|
|
|
|
DBGOUT(("<== WorkItemCallback_Close()"));
|
|
}
|
|
|
|
|
|
/*
|
|
* TryDestroyAll
|
|
*
|
|
* Destroy all devices which don't have a pending read.
|
|
*/
|
|
VOID TryDestroyAll()
|
|
{
|
|
deviceContext *thisDevice;
|
|
|
|
DBGOUT(("=> TryDestroyAll()"));
|
|
|
|
thisDevice = firstDevice;
|
|
while (thisDevice){
|
|
deviceContext *nextDevice = thisDevice->next; // hold the next ptr in case we dequeue
|
|
|
|
if (!thisDevice->readPending){
|
|
/*
|
|
* No read pending on this device; we can shut it down.
|
|
*/
|
|
DequeueDevice(thisDevice);
|
|
DestroyDevice(thisDevice);
|
|
}
|
|
|
|
thisDevice = nextDevice;
|
|
}
|
|
|
|
if (!firstDevice){
|
|
/*
|
|
* All reads are complete and all devices have been destroyed.
|
|
* If a shutdown is suspended, shutdown now.
|
|
*/
|
|
if (shutdownSemaphore){
|
|
Signal_Semaphore_No_Switch(shutdownSemaphore);
|
|
}
|
|
}
|
|
|
|
DBGOUT(("<= TryDestroyAll()"));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* HandleShutdown
|
|
*
|
|
*
|
|
*/
|
|
VOID _cdecl HandleShutdown(VOID)
|
|
{
|
|
/*
|
|
* Just set a flag. Wait for read completion to close the device handles.
|
|
*/
|
|
DBGOUT(("==> HandleShutdown"));
|
|
TryDestroyAll();
|
|
|
|
if (firstDevice && !ShutDown){
|
|
|
|
/*
|
|
* There are still reads pending.
|
|
* Wait for all reads to complete before returning.
|
|
*/
|
|
|
|
ShutDown = TRUE;
|
|
|
|
shutdownSemaphore = Create_Semaphore(0);
|
|
if (shutdownSemaphore){
|
|
|
|
Wait_Semaphore(shutdownSemaphore, 0);
|
|
Destroy_Semaphore(shutdownSemaphore);
|
|
}
|
|
}
|
|
|
|
DBGOUT(("<== HandleShutdown"));
|
|
}
|
|
|
|
|
|
/*
|
|
* HandleNewDevice
|
|
*
|
|
*/
|
|
VOID _cdecl HandleNewDevice(VOID)
|
|
{
|
|
DBGOUT(("==> HandleNewDevice"));
|
|
|
|
/*
|
|
* See if there are any new device devices to connect.
|
|
*/
|
|
ConnectNTDeviceDrivers();
|
|
|
|
DBGOUT(("<== HandleNewDevice"));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* DeviceHasBeenOpened
|
|
*
|
|
* BUGBUG - there's got to be a better way of checking for this.
|
|
*
|
|
*/
|
|
BOOL DeviceHasBeenOpened(PWCHAR deviceFileName, UINT nameWChars)
|
|
{
|
|
deviceContext *device;
|
|
UINT nameLen = (nameWChars*sizeof(WCHAR))+sizeof(UNICODE_NULL);
|
|
|
|
for (device = firstDevice; device; device = device->next){
|
|
if (memcmp(deviceFileName, device->deviceFileName, nameLen) == 0){
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ConnectNTDeviceDrivers
|
|
*
|
|
*
|
|
*/
|
|
VOID ConnectNTDeviceDrivers()
|
|
{
|
|
WORK_QUEUE_ITEM *workItemOpen;
|
|
|
|
workItemOpen = _HeapAllocate(sizeof(WORK_QUEUE_ITEM), 0);
|
|
if (workItemOpen){
|
|
|
|
/*
|
|
* Initialize the workItem and
|
|
* pass the workItem itself as the context so that it can be freed later.
|
|
*/
|
|
ExInitializeWorkItem(workItemOpen, WorkItemCallback_Open, workItemOpen);
|
|
|
|
DBGOUT(("==> ConnectNTDeviceDrivers() - queueing work item to call "));
|
|
/*
|
|
* Queue a work item to do the open; this way we'll be on a worker thread
|
|
* instead of (possibly) the NTKERN thread when we call NtCreateFile().
|
|
* This prevents a contention bug.
|
|
*/
|
|
_NtKernQueueWorkItem(workItemOpen, DelayedWorkQueue);
|
|
}
|
|
|
|
DBGOUT(("<== ConnectNTDeviceDrivers()"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* WorkItemCallback_Open
|
|
*
|
|
* Do the actual work of opening the device.
|
|
*
|
|
*/
|
|
VOID WorkItemCallback_Open(PVOID context)
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS ntStatus;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING FileName;
|
|
PWSTR symbolicLinkList = NULL;
|
|
PWSTR symbolicLink;
|
|
|
|
DBGOUT(("==> WorkItemCallback_Open()"));
|
|
|
|
/*
|
|
* The context is just the workItem itself, which can now be freed.
|
|
*/
|
|
ASSERT(context);
|
|
_HeapFree(context, 0);
|
|
|
|
|
|
/*
|
|
* Get pointers to all our import functions at once.
|
|
*/
|
|
if (!GetImportFunctionPtrs()){
|
|
DBGERR(("ERROR: Failed to get import functions."));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get a multi-string (separated by unicode NULL characters)
|
|
* of symbolic link names to the input-class devices.
|
|
*/
|
|
ntStatus = pIoGetDeviceClassAssociations( (EXTERN_C GUID *)&GUID_CLASS_INPUT,
|
|
NULL,
|
|
0,
|
|
(PWSTR *)&symbolicLinkList);
|
|
if (NT_ERROR(ntStatus) || !symbolicLinkList) {
|
|
DBGERR(("pIoGetDeviceClassAssociations failed"));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Go through all the device paths
|
|
*/
|
|
symbolicLink = symbolicLinkList;
|
|
while ((WCHAR)*symbolicLink){
|
|
HANDLE deviceHandle;
|
|
ULONG fileNameWChars;
|
|
PWCHAR fileName;
|
|
deviceContext *newDevice = NULL;
|
|
|
|
|
|
/*
|
|
* Get a pointer to to the next device name and step the multi-string pointer.
|
|
*/
|
|
fileName = symbolicLink;
|
|
fileNameWChars = WStrLen(fileName);
|
|
symbolicLink += fileNameWChars+1;
|
|
|
|
/*
|
|
* Make sure we don't already have this device open.
|
|
* This can happen because we check for new device on every PNP_NEW_DEVNODE msg.
|
|
*/
|
|
if (DeviceHasBeenOpened(fileName, fileNameWChars)){
|
|
DBGOUT(("This device is already open, skipping ..."));
|
|
}
|
|
else {
|
|
FileName.Buffer = fileName;
|
|
FileName.Length = fileNameWChars*sizeof(WCHAR);
|
|
FileName.MaximumLength = FileName.Length + sizeof(UNICODE_NULL);
|
|
|
|
/*
|
|
* Initialize an object-attribute structure with this filename.
|
|
*/
|
|
InitializeObjectAttributes(&Obja, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
/*
|
|
* Try to open the device.
|
|
*/
|
|
DBGOUT(("Opening HID Device : (unicode name @%xh, %xh wchars)", (UINT)fileName, (UINT)fileNameWChars));
|
|
|
|
ntStatus = _NtKernCreateFile(
|
|
&deviceHandle,
|
|
(GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES),
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
|
FILE_OPEN,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
if (NT_SUCCESS(ntStatus)){
|
|
HID_COLLECTION_INFORMATION hidColInfo;
|
|
|
|
DBGOUT(("Opened some device (handle=%xh), calling _NtKernDeviceIoControl", (UINT)deviceHandle));
|
|
|
|
ntStatus = _NtKernDeviceIoControl(
|
|
deviceHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_HID_GET_COLLECTION_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&hidColInfo,
|
|
sizeof(HID_COLLECTION_INFORMATION));
|
|
if (NT_SUCCESS(ntStatus)){
|
|
PHIDP_PREPARSED_DATA phidDescriptor;
|
|
|
|
phidDescriptor = (PHIDP_PREPARSED_DATA)_HeapAllocate(hidColInfo.DescriptorSize, 0);
|
|
|
|
if (phidDescriptor){
|
|
|
|
ntStatus = _NtKernDeviceIoControl(
|
|
deviceHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
|
|
NULL,
|
|
0,
|
|
phidDescriptor,
|
|
hidColInfo.DescriptorSize);
|
|
if (NT_SUCCESS(ntStatus)){
|
|
HIDP_CAPS hidCaps;
|
|
|
|
ntStatus = pHidP_GetCaps(phidDescriptor, (PHIDP_CAPS)&hidCaps);
|
|
|
|
if (NT_SUCCESS(ntStatus)){
|
|
|
|
DBGOUT(("Opened HID device successfully, report size is %d.", (UINT)hidCaps.InputReportByteLength));
|
|
|
|
/*
|
|
* <<COMPLETE>>
|
|
*
|
|
* Check hidCaps.UsagePage and hidCaps.Usage to verify that this is a device
|
|
* that you want to drive.
|
|
*/
|
|
|
|
if (hidCaps.InputReportByteLength == 0){
|
|
DBGERR(("ERROR: Report size is zero!"));
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* Take all the information we have for this device and bundle
|
|
* it into a context.
|
|
*/
|
|
newDevice = NewDevice(deviceHandle,
|
|
(PHIDP_CAPS)&hidCaps,
|
|
phidDescriptor,
|
|
hidColInfo.DescriptorSize,
|
|
fileName);
|
|
if (newDevice){
|
|
/*
|
|
* Add this device to our global list.
|
|
*/
|
|
EnqueueDevice(newDevice);
|
|
|
|
/*
|
|
* Then start the first async read in the device device.
|
|
*/
|
|
DispatchNtReadFile(newDevice);
|
|
}
|
|
else {
|
|
DBGERR(("NewDevice() failed"));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("pHidP_GetCaps failed"));
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("_NtKernDeviceIoControl (#2) failed"));
|
|
}
|
|
|
|
_HeapFree(phidDescriptor, 0);
|
|
}
|
|
else {
|
|
DBGERR(("HeapAlloc failed"));
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("_NtKernDeviceIoControl failed"));
|
|
}
|
|
|
|
if (!newDevice){
|
|
DBGERR(("Device init failed -- calling _NtKernClose() on device handle"));
|
|
_NtKernClose(deviceHandle);
|
|
}
|
|
|
|
}
|
|
else {
|
|
DBGERR(("_NtKernCreateFile failed to open this Device (ntStatus=%xh)", (UINT)ntStatus));
|
|
}
|
|
}
|
|
}
|
|
|
|
// BUGBUG ExFreePool(symbolicLinkList);
|
|
|
|
DBGOUT(("<== WorkItemCallback_Open()"));
|
|
}
|
|
|