/*++ Copyright (c) 1996,1997 Microsoft Corporation Module Name: hid.c Abstract: Human Input Device (HID) minidriver that creates an example device. --*/ #include #include #include #include // // Our descriptors. Normally you'd have to get these from the hardware, if it // was truly a HID device, or you'll have to build your own for non HID devices. // // // The report descriptor completely lays out what read and write packets will look like // and indicates what the semantics are for each field. // HID_REPORT_DESCRIPTOR MyReportDescriptor[] = { 0x05, 0x01, // Usage Page (Generic Desktop), 0x09, 0x06, // Usage (Keyboard), 0xA1, 0x01, // Collection (Application), 0x05, 0x07, // Usage Page (Key Codes); 0x19, 0xE0, // Usage Minimum (224), 0x29, 0xE7, // Usage Maximum (231), 0x15, 0x00, // Logical Minimum (0), 0x25, 0x01, // Logical Maximum (1), 0x75, 0x01, // Report Size (1), 0x95, 0x08, // Report Count (8), 0x81, 0x02, // Input (Data, Variable, Absolute),;Modifier byte 0x95, 0x01, // Report Count (1), 0x75, 0x08, // Report Size (8), 0x81, 0x01, // Input (Constant), ;Reserved byte 0x95, 0x05, // Report Count (5), 0x75, 0x01, // Report Size (1), 0x05, 0x08, // Usage Page (Page# for LEDs), 0x19, 0x01, // Usage Minimum (1), 0x29, 0x05, // Usage Maximum (5), 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report 0x95, 0x01, // Report Count (1), 0x75, 0x03, // Report Size (3), 0x91, 0x01, // Output (Constant), ;LED report padding 0x95, 0x06, // Report Count (6), 0x75, 0x08, // Report Size (8), 0x15, 0x00, // Logical Minimum (0), 0x25, 0x65, // Logical Maximum(101), 0x05, 0x07, // Usage Page (Key Codes), 0x19, 0x00, // Usage Minimum (0), 0x29, 0x65, // Usage Maximum (101), 0x81, 0x00, // Input (Data, Array), ;Key arrays (6 bytes) 0xC0 // End Collection }; // // The HID descriptor has some basic device info and tells how long the report // descriptor is. // USB_HID_DESCRIPTOR MyHidDescriptor = { 0x09, // length of HID descriptor 0x21, // descriptor type == HID 0x0100, // hid spec release 0x00, // country code == Not Specified 0x01, // number of HID class descriptors 0x22, // report descriptor type sizeof(MyReportDescriptor) // total length of report descriptor }; // // This buffer has all of the strings that we define. // UCHAR AStringDescriptor[] = { 4, // length of this string 3, // type == STRING 0x09, 0x00, // language code == ENGLISH 44, 3, 'M',0, 'i',0, 'c',0, 'r',0, 'o',0, 's',0, 'o',0, 'f',0, 't',0, ' ',0, 'C',0, 'o',0, 'r',0, 'p',0, 'o',0, 'r',0, 'a',0, 't',0, 'i',0, 'o',0, 'n',0, 46, 3, 'S',0, 'y',0, 's',0, 't',0, 'e',0, 'm',0, ' ',0, 'C',0, 'o',0, 'n',0, 't',0, 'r',0, 'o',0, 'l',0, ' ',0, 'B',0, 'u',0, 't',0, 't',0, 'o',0, 'n',0, 's',0, 32, 3, 'L',0, 'e',0, 'g',0, 'a',0, 'c',0, 'y',0, ' ',0, 'K',0, 'e',0, 'y',0, 'b',0, 'o',0, 'a',0, 'r',0, 'd',0, }; // // String descriptors are PUSB_STRING_DESCRIPTOR MyStringDescriptor = (PUSB_STRING_DESCRIPTOR) AStringDescriptor; // // No designator descriptors. // PUSB_PHYSICAL_DESCRIPTOR MyPhysicalDescriptor = NULL; // // IO lists // KSPIN_LOCK HidMini_IrpReadLock; KSPIN_LOCK HidMini_IrpWriteLock; LIST_ENTRY HidMini_ReadIrpHead; LIST_ENTRY HidMini_WriteIrpHead; BOOLEAN IsRunning = FALSE; LONG ReadsCompleting = 0; NTSTATUS HidMiniGetHIDDescriptor(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Finds the HID descriptor and copies it into the buffer provided by the Irp. Arguments: DeviceObject - pointer to a device object. Irp - Pointer to Interrupt Request Packet. Return Value: NT status code. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; ULONG bytesToCopy; DBGPrint(("'HIDMINI.SYS: HidMiniGetHIDDescriptor Entry\n")); // // Get a pointer to the current location in the Irp // IrpStack = IoGetCurrentIrpStackLocation(Irp); // // Get a pointer to the device extension // DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); // // Copy device descriptor to HIDCLASS buffer // DBGPrint(("'HIDMINI.SYS: HIDCLASS Buffer = 0x%x, Buffer length = 0x%x\n", Irp->UserBuffer, IrpStack->Parameters.DeviceIoControl.OutputBufferLength)); // // Copy MIN (OutputBufferLength, DeviceExtension->HidDescriptor->bLength) // bytesToCopy = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; if (bytesToCopy > DeviceExtension->HidDescriptor.bLength) { bytesToCopy = DeviceExtension->HidDescriptor.bLength; } DBGPrint(("'HIDMINI.SYS: Copying %d bytes to HIDCLASS buffer\n", bytesToCopy)); RtlCopyMemory((PUCHAR) Irp->UserBuffer, (PUCHAR) &DeviceExtension->HidDescriptor, bytesToCopy); // // Report how many bytes were copied // Irp->IoStatus.Information = bytesToCopy; DBGPrint(("'HIDMINI.SYS: HidMiniGetHIDDescriptor Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniGetReportDescriptor(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Finds the Report descriptor and copies it into the buffer provided by the Irp. Arguments: DeviceObject - pointer to a device object. Irp - Pointer to Interrupt Request Packet. Return Value: NT status code. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; ULONG bytesToCopy; DBGPrint(("'HIDMINI.SYS: HidMiniGetReportDescriptor Entry\n")); // // Get a pointer to the current location in the Irp // IrpStack = IoGetCurrentIrpStackLocation(Irp); // // Get a pointer to the device extension // DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); // // Copy device descriptor to HIDCLASS buffer // DBGPrint(("'HIDMINI.SYS: HIDCLASS Buffer = 0x%x, Buffer length = 0x%x\n", Irp->UserBuffer, IrpStack->Parameters.DeviceIoControl.OutputBufferLength)); // // Copy MIN (OutputBufferLength, DeviceExtension->HidDescriptor->bLength) // bytesToCopy = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; if (bytesToCopy > DeviceExtension->HidDescriptor.wReportLength) { bytesToCopy = DeviceExtension->HidDescriptor.wReportLength; } DBGPrint(("'HIDMINI.SYS: Copying %d bytes to HIDCLASS buffer\n", bytesToCopy)); RtlCopyMemory((PUCHAR) Irp->UserBuffer, (PUCHAR) DeviceExtension->ReportDescriptor, bytesToCopy); // // Report how many bytes were copied // Irp->IoStatus.Information = bytesToCopy; DBGPrint(("'HIDMINI.SYS: HidMiniGetReportDescriptor Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniGetDeviceAttributes(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Fill in the given struct _HID_DEVICE_ATTRIBUTES Arguments: DeviceObject - pointer to a device object. Return Value: NT status code. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; PHID_DEVICE_ATTRIBUTES deviceAttributes; DBGPrint(("'HIDMINI.SYS: HidMiniGetDeviceAttributes Entry\n")); // // Get a pointer to the current location in the Irp // irpStack = IoGetCurrentIrpStackLocation(Irp); // // Get a pointer to the device extension // deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); deviceAttributes = (PHID_DEVICE_ATTRIBUTES) Irp->UserBuffer; ASSERT (sizeof (HID_DEVICE_ATTRIBUTES) == irpStack->Parameters.DeviceIoControl.OutputBufferLength); // // Report how many bytes were copied // Irp->IoStatus.Information = sizeof (HID_DEVICE_ATTRIBUTES); deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES); deviceAttributes->VendorID = HIDMINI_VID; deviceAttributes->ProductID = HIDMINI_PID; deviceAttributes->VersionNumber = HIDMINI_VERSION; DBGPrint(("'HIDMINI.SYS: HidMiniGetAttributes Exit = 0x%x\n", ntStatus)); return ntStatus; } VOID HidMiniIncrementPendingRequestCount(IN PDEVICE_EXTENSION DeviceExtension) /*++ Routine Description: Increments the number of outstanding requests on the DeviceObject with this extension. Arguments: DeviceExtension - the mini driver extension area of the device that is being made busy. Return Value: VOID. --*/ { DeviceExtension->NumPendingRequests++; DBGPrint(("'HIDMINI.SYS: Bumping requests on device extension 0x%08x up to %d\n", DeviceExtension, DeviceExtension->NumPendingRequests)); } VOID HidMiniDecrementPendingRequestCount(IN PDEVICE_EXTENSION DeviceExtension) /*++ Routine Description: Decrements the number of outstanding requests on the DeviceObject with this extension. If we get to zero outstanding IOs, set the device's all requests complete event. Arguments: DeviceExtension - the mini driver extension area of the device that is being made busy. Return Value: VOID. --*/ { ASSERT( DeviceExtension->NumPendingRequests > 0 ); DeviceExtension->NumPendingRequests--; DBGPrint(("'HIDMINI.SYS: Bumping requests on device extension 0x%08x down to %d\n", DeviceExtension, DeviceExtension->NumPendingRequests)); if( DeviceExtension->NumPendingRequests == 0 && DeviceExtension->DeviceState != DEVICE_STATE_RUNNING ){ // // The device state is stopping, and the last outstanding request // has just completed. // DBGPrint(("'HIDMINI.SYS: last request completed, signalling event\n")); KeSetEvent( &DeviceExtension->AllRequestsCompleteEvent, 0, FALSE ); } } NTSTATUS HidMiniReadReport(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Process a read HID packet request. Arguments: DeviceObject - pointer to a device object. Irp - Pointer to Interrupt Request Packet. Return Value: NT status code. --*/ { NTSTATUS ntStatus; PDEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; PVOID ReportBuffer; ULONG ReportTotalSize; PNODE Node; DBGPrint(("'HIDMINI.SYS: HidMiniReadReport Enter\n")); DBGPrint(("'HIDMINI.SYS: DeviceObject = 0x%x\n", DeviceObject)); // // Get a pointer to the device extension. // DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); DBGPrint(("'HIDMINI.SYS: DeviceExtension = 0x%x\n", DeviceExtension)); // // Get Stack location // IrpStack = IoGetCurrentIrpStackLocation(Irp); // // Get the buffer and its size, make sure they're valid. // ReportBuffer = Irp->UserBuffer; ReportTotalSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; DBGPrint(("'HIDMINI.SYS: ReportBuffer = 0x%x, ReportTotalSize = 0x%x\n", ReportBuffer, ReportTotalSize)); if (IsRunning) { if (ReportTotalSize && ReportBuffer){ Node = (PNODE)ExAllocatePool(NonPagedPool, sizeof(NODE)); if (Node) { // // Increase the count of outstanding IOs, mark the Irp pending. // HidMiniIncrementPendingRequestCount(DeviceExtension); IoMarkIrpPending(Irp); // // Hook the Irp onto the pending IO list // Node->Irp = Irp; ExInterlockedInsertTailList(&HidMini_ReadIrpHead, &Node->List, &HidMini_IrpReadLock); ntStatus = STATUS_PENDING; } else { ntStatus = STATUS_NO_MEMORY; } } else { // // No buffer, or buffer of zero size // ntStatus = STATUS_INVALID_PARAMETER; } } else { // // We're shutting down // ntStatus = STATUS_NO_SUCH_DEVICE; } DBGPrint(("'HIDMINI.SYS: HidMiniReadReport Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniReadCompletion(PVOID Context) /*++ Routine Description: HID read packet completion routine. Arguments: Return Value: STATUS_SUCCESS, STATUS_UNSUCCESSFUL. --*/ { PIO_STACK_LOCATION IrpStack; ULONG bytesRead; PDEVICE_EXTENSION deviceExtension; PNODE IrpNode; PIRP Irp; PDEVICE_OBJECT DeviceObject; // // ReadBuffer is a list of keyboard reports that can be transmitted as a // result for a read request. // static int ReadIndex = 0; static int Rest = 0; static UCHAR *ReadBuffer[] = { "\x02\x00\x0D\0\0\0\0\0", // J "\x00\x00\x04\0\0\0\0\0", // a "\x00\x00\x06\0\0\0\0\0", // c "\x00\x00\x0E\0\0\0\0\0", // k "\x00\x00\x07\0\0\0\0\0", // d "\x00\x00\x04\0\0\0\0\0", // a "\x00\x00\x1A\0\0\0\0\0", // w "\x00\x00\x16\0\0\0\0\0", // s "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x0F\0\0\0\0\0", // l "\x00\x00\x12\0\0\0\0\0", // o "\x00\x00\x19\0\0\0\0\0", // v "\x00\x00\x08\0\0\0\0\0", // e "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x10\0\0\0\0\0", // m "\x00\x00\x1C\0\0\0\0\0", // y "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x05\0\0\0\0\0", // b "\x00\x00\x0C\0\0\0\0\0", // i "\x00\x00\x0A\0\0\0\0\0", // g "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x16\0\0\0\0\0", // s "\x00\x00\x13\0\0\0\0\0", // p "\x00\x00\x0B\0\0\0\0\0", // h "\x00\x00\x1C\0\0\0\0\0", // y "\x00\x00\x11\0\0\0\0\0", // n "\x00\x00\x1B\0\0\0\0\0", // x "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x12\0\0\0\0\0", // o "\x00\x00\x09\0\0\0\0\0", // f "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x14\0\0\0\0\0", // q "\x00\x00\x18\0\0\0\0\0", // u "\x00\x00\x04\0\0\0\0\0", // a "\x00\x00\x15\0\0\0\0\0", // r "\x00\x00\x17\0\0\0\0\0", // t "\x00\x00\x1D\0\0\0\0\0", // z "\x00\x00\x37\0\0\0\0\0", // . "\x00\x00\x2C\0\0\0\0\0", // "\x00\x00\x2C\0\0\0\0\0", // NULL, }; DBGPrint(("'HIDMINI.SYS: HidMiniReadCompletion Enter\n")); // // Free workitem that started us, check to see if we're already completing reads // ExFreePool((PWORK_QUEUE_ITEM)Context); ASSERT(ReadsCompleting > 0); if (InterlockedDecrement(&ReadsCompleting)) { return STATUS_SUCCESS; } // // Loop around completing Irps. When we run out, break // out of the loop. // while (IsRunning) { // // Make sure we don't overrun our list of replies. If we get to the end of it, // we'll exit this loop early and let the IRPs queue up again. // if (!ReadBuffer[ReadIndex]) { ReadIndex = 0; } // // Any Irps to complete? // IrpNode = (PNODE)ExInterlockedRemoveHeadList(&HidMini_ReadIrpHead, &HidMini_IrpReadLock); if (IrpNode) { // // Find all the pieces // Irp = IrpNode->Irp; IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceObject = IrpStack->DeviceObject; deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION( DeviceObject ); HidMiniDecrementPendingRequestCount( deviceExtension ); // // Get the bytes read and store in the status block // bytesRead = 8; if (bytesRead > IrpStack->Parameters.DeviceIoControl.OutputBufferLength) { bytesRead = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; } if (Rest) { RtlCopyMemory((PUCHAR) Irp->UserBuffer, "\x00\x00\0\0\0\0\0\0", bytesRead); } else { RtlCopyMemory((PUCHAR) Irp->UserBuffer, (PUCHAR)ReadBuffer[ReadIndex++], bytesRead); } Rest ^= 1; Irp->IoStatus.Information = bytesRead; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGPrint(("'HIDMINI.SYS: Read report DeviceObject (%x) completed, %d bytes!\n", DeviceObject, bytesRead )); // // Free up the Node // ExFreePool(IrpNode); if (!Rest) { break; } } else { // // No Irps // break; } } return STATUS_SUCCESS; } NTSTATUS HidMiniWriteReport(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Process a write HID packet request. This is also how we start up the read completion process. Arguments: DeviceObject - pointer to a device object. Irp - Pointer to Interrupt Request Packet. Return Value: NT status code. --*/ { NTSTATUS ntStatus; PDEVICE_EXTENSION DeviceExtension; PNODE Node; PWORK_QUEUE_ITEM StartCompletingReads; DBGPrint(("'HIDMINI.SYS: HidMiniWriteReport Enter\n")); DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); if (IsRunning) { Node = (PNODE)ExAllocatePool(NonPagedPool, sizeof(NODE)); StartCompletingReads = (PWORK_QUEUE_ITEM) ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM)); if (Node && StartCompletingReads) { // // Queue up a work item to start completing reads. // ASSERT(ReadsCompleting >= 0); InterlockedIncrement(&ReadsCompleting); ExInitializeWorkItem(StartCompletingReads, HidMiniReadCompletion, (PVOID)StartCompletingReads); ExQueueWorkItem(StartCompletingReads, DelayedWorkQueue); // // Increase the count of outstanding IOs, mark the Irp pending. // HidMiniIncrementPendingRequestCount(DeviceExtension); IoMarkIrpPending(Irp); // // Hook the Irp onto the pending IO list // Node->Irp = Irp; ExInterlockedInsertTailList(&HidMini_WriteIrpHead, &Node->List, &HidMini_IrpWriteLock); ntStatus = STATUS_PENDING; // // Give the write completion code a kick // HidMiniWriteCompletion(); } else { ntStatus = STATUS_NO_MEMORY; } } else { // // We're shutting down // ntStatus = STATUS_NO_SUCH_DEVICE; } DBGPrint(("'HIDMINI.SYS: HidMiniWriteReport Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniWriteCompletion(VOID) /*++ Routine Description: Complete processing a write HID packet request. Arguments: Return Value: NT status code. --*/ { PIO_STACK_LOCATION IrpStack; PDEVICE_EXTENSION deviceExtension; PNODE IrpNode; PIRP Irp; PDEVICE_OBJECT DeviceObject; // // Loop around completing Irps. When we run out, break // out of the loop. // while (IsRunning) { // // Any Irps to complete? // IrpNode = (PNODE)ExInterlockedRemoveHeadList(&HidMini_WriteIrpHead, &HidMini_IrpWriteLock); if (IrpNode) { // // Find all the pieces // Irp = IrpNode->Irp; IrpStack = IoGetCurrentIrpStackLocation(Irp); DeviceObject = IrpStack->DeviceObject; deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION( DeviceObject ); // // Do something here... // // // Finish off the IRP // HidMiniDecrementPendingRequestCount( deviceExtension ); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGPrint(("'HIDMINI.SYS: Write report DeviceObject (%x) completed\n", DeviceObject)); // // Free up the Node // ExFreePool(IrpNode); } else { // // No Irps // break; } } return STATUS_SUCCESS; } NTSTATUS HidMiniGetStringDescriptor( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Get the device string descriptor, if any. Arguments: DeviceObject - pointer to a device object. Irp - Pointer to Interrupt Request Packet. Return Value: NT status code. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; ULONG bytesToCopy; DBGPrint(("'HIDMINI.SYS: HidMiniGetStringDescriptor Enter\n")); DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject); IrpStack = IoGetCurrentIrpStackLocation(Irp); // // Get the buffer size to write into // bytesToCopy = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // // Make sure we have a buffer and it has some space // if (Irp->UserBuffer && bytesToCopy){ // // Adjust the size to the amount we have to write // if (bytesToCopy > sizeof(AStringDescriptor)) { bytesToCopy = sizeof(AStringDescriptor); } DBGPrint(("'HIDMINI.SYS: Copying %d bytes to STRING buffer\n", bytesToCopy)); RtlCopyMemory((PUCHAR) Irp->UserBuffer, (PUCHAR) DeviceExtension->StringDescriptor, bytesToCopy); // // Report how many bytes were copied // Irp->IoStatus.Information = bytesToCopy; } else { ntStatus = STATUS_INVALID_USER_BUFFER; } Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGPrint(("'HIDMINI.SYS: HidMiniGetStringDescriptor Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniOpenCollection(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Called when a HIDCLASS client opens this collection Arguments: DeviceObject - Pointer to class device object. IrpStack - Pointer to Interrupt Request Packet. Return Value: STATUS_SUCCESS, STATUS_PENDING. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; DBGPrint(("'HIDMINI.SYS: HidMiniOpenCollection Enter\n")); Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGPrint(("'HIDMINI.SYS: HidMiniOpenCollection Exit = 0x%x\n", ntStatus)); return ntStatus; } NTSTATUS HidMiniCloseCollection(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) /*++ Routine Description: Called when a HIDCLASS client closes this collection Arguments: DeviceObject - Pointer to class device object. IrpStack - Pointer to Interrupt Request Packet. Return Value: STATUS_SUCCESS, STATUS_PENDING. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; DBGPrint(("'HIDMINI.SYS: HidMiniCloseCollection Enter\n")); Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); DBGPrint(("'HIDMINI.SYS: HidMiniCloseCollection Exit = 0x%x\n", ntStatus)); return ntStatus; }