usb hubs, hotplug support, unplug support, saving log files to disk, smt
This commit is contained in:
1 parent
b838f8d83d
commit
27b31f62b6
39 files changed
+6294
-735
No files matched your search
@@ -125,7 +125,7 @@ void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
|
||||
|
||||
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
|
||||
static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue) {
|
||||
if (slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS) return;
|
||||
@@ -158,11 +158,10 @@ void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
|
||||
|
||||
|
||||
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
|
||||
if (xhci->HidDeviceCount == 0)
|
||||
if (!xhci || xhci->HidDeviceCount == 0)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
XhciArmAllHid(xhci);
|
||||
DbgPrint("usb: HID interrupt input armed devices=%u\n", xhci->HidDeviceCount);
|
||||
DbgPrint("usb: HID interrupt URBs active devices=%u\n", xhci->HidDeviceCount);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -365,3 +364,412 @@ KSTATUS UsbHidBind(IO_PUBLISHED_DEVICE* pub, void* context) {
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PDO/FDO boot HID function driver path.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
DRIVER_OBJECT gUsbHidDriver;
|
||||
|
||||
static const IO_DEVICE_ID gUsbHidIds[] = {
|
||||
{ IoBusUsb, { "USB\\CLASS_03&SUBCLASS_01&PROT_01", sizeof("USB\\CLASS_03&SUBCLASS_01&PROT_01") - 1 } },
|
||||
{ IoBusUsb, { "USB\\CLASS_03&SUBCLASS_01&PROT_02", sizeof("USB\\CLASS_03&SUBCLASS_01&PROT_02") - 1 } },
|
||||
};
|
||||
|
||||
static const IO_DRIVER_ID_TABLE gUsbHidIdTable = {
|
||||
gUsbHidIds,
|
||||
sizeof(gUsbHidIds) / sizeof(gUsbHidIds[0]),
|
||||
};
|
||||
|
||||
static const USB_ENDPOINT_DESCRIPTOR* UsbHidFindInterruptInPdo(const USB_PDO_EXTENSION* ext) {
|
||||
if (!ext) return (const USB_ENDPOINT_DESCRIPTOR*)0;
|
||||
|
||||
for (uint8_t i = 0; i < ext->EndpointCount; i++) {
|
||||
const USB_ENDPOINT_DESCRIPTOR* ep = &ext->Endpoints[i];
|
||||
uint8_t type = ep->bmAttributes & USB_ENDPOINT_ATTR_TYPE_MASK;
|
||||
bool in = (ep->bEndpointAddress & USB_ENDPOINT_DIR_IN) != 0;
|
||||
if (type == USB_ENDPOINT_TYPE_INTERRUPT && in)
|
||||
return ep;
|
||||
}
|
||||
return (const USB_ENDPOINT_DESCRIPTOR*)0;
|
||||
}
|
||||
|
||||
|
||||
static void UsbHidProcessBootKeyboardReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) {
|
||||
if (!ext || len < 8) return;
|
||||
|
||||
uint8_t modifiers = report[0];
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
uint8_t mask = (uint8_t)(1u << bit);
|
||||
bool wasDown = (ext->LastModifiers & mask) != 0;
|
||||
bool isDown = (modifiers & mask) != 0;
|
||||
if (wasDown != isDown)
|
||||
XhciSubmitHidKey(XhciHidModifierKey(bit), isDown);
|
||||
}
|
||||
|
||||
const uint8_t* keys = &report[2];
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
uint8_t oldKey = ext->LastKeys[i];
|
||||
if (oldKey > 1 && !XhciHidKeyInReport(keys, oldKey))
|
||||
XhciSubmitHidKey((INPUT_KEY)oldKey, false);
|
||||
}
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
uint8_t newKey = keys[i];
|
||||
if (newKey > 1 && !XhciHidKeyInReport(ext->LastKeys, newKey))
|
||||
XhciSubmitHidKey((INPUT_KEY)newKey, true);
|
||||
}
|
||||
|
||||
ext->LastModifiers = modifiers;
|
||||
memcpy(ext->LastKeys, keys, sizeof(ext->LastKeys));
|
||||
}
|
||||
|
||||
static void UsbHidProcessBootMouseReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) {
|
||||
if (!ext || len < 3) return;
|
||||
|
||||
int dx = (int)(int8_t)report[1];
|
||||
int dy = (int)(int8_t)report[2];
|
||||
int wheel = (len >= 4) ? (int)(int8_t)report[3] : 0;
|
||||
uint8_t buttons = report[0] & 0x07u;
|
||||
|
||||
if (dx || dy || wheel || buttons != ext->LastButtons) {
|
||||
INPUT_MOUSE_EVENT ev;
|
||||
ev.dx = dx;
|
||||
ev.dy = dy;
|
||||
ev.wheel = wheel;
|
||||
ev.buttons = buttons;
|
||||
KeInputSubmitMouse(&ev);
|
||||
}
|
||||
|
||||
ext->LastButtons = buttons;
|
||||
}
|
||||
|
||||
static KSTATUS UsbHidArmInterrupt(USB_HID_EXTENSION* ext);
|
||||
|
||||
static void UsbHidInterruptComplete(USB_URB* urb, void* context) {
|
||||
USB_HID_EXTENSION* ext = (USB_HID_EXTENSION*)context;
|
||||
if (!ext || urb != &ext->InterruptUrb)
|
||||
return;
|
||||
|
||||
ext->InterruptPending = false;
|
||||
if (ext->SurpriseRemoved || ext->Removed || !ext->Started)
|
||||
return;
|
||||
|
||||
if (KSUCCESS(urb->Status)) {
|
||||
uint16_t length = (uint16_t)urb->ActualLength;
|
||||
if (length > ext->ReportLength) length = ext->ReportLength;
|
||||
|
||||
if (ext->Keyboard)
|
||||
UsbHidProcessBootKeyboardReport(ext, ext->ReportBuffer, length);
|
||||
else if (ext->Mouse)
|
||||
UsbHidProcessBootMouseReport(ext, ext->ReportBuffer, length);
|
||||
} else if (urb->Status != STATUS_CANCELLED) {
|
||||
DbgPrint("usb: HID interrupt URB failed slot=%u if=%u ep=%02x st=0x%x\n",
|
||||
ext->Device ? ext->Device->SlotId : 0,
|
||||
ext->InterfaceNumber,
|
||||
ext->EndpointAddress,
|
||||
urb->Status);
|
||||
}
|
||||
|
||||
if (!ext->SurpriseRemoved && !ext->Removed)
|
||||
(void)UsbHidArmInterrupt(ext);
|
||||
}
|
||||
|
||||
static KSTATUS UsbHidSendBootProtocol(USB_HID_EXTENSION* ext) {
|
||||
if (!ext || !ext->LowerDevice || !ext->Device)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
USB_URB urb;
|
||||
memset(&urb, 0, sizeof(urb));
|
||||
urb.Function = UrbFunctionControlTransfer;
|
||||
urb.Direction = UsbTransferNoData;
|
||||
urb.Device = ext->Device;
|
||||
urb.Setup.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
urb.Setup.bRequest = USB_REQ_SET_PROTOCOL;
|
||||
urb.Setup.wValue = 0;
|
||||
urb.Setup.wIndex = ext->InterfaceNumber;
|
||||
urb.Setup.wLength = 0;
|
||||
|
||||
KSTATUS st = UsbSubmitUrbSync(ext->LowerDevice, &urb);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("usb: HID SET_PROTOCOL boot failed slot=%u if=%u st=0x%x\n",
|
||||
ext->Device->SlotId, ext->InterfaceNumber, st);
|
||||
return st;
|
||||
}
|
||||
|
||||
memset(&urb, 0, sizeof(urb));
|
||||
urb.Function = UrbFunctionControlTransfer;
|
||||
urb.Direction = UsbTransferNoData;
|
||||
urb.Device = ext->Device;
|
||||
urb.Setup.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
urb.Setup.bRequest = USB_REQ_SET_IDLE;
|
||||
urb.Setup.wValue = 0;
|
||||
urb.Setup.wIndex = ext->InterfaceNumber;
|
||||
urb.Setup.wLength = 0;
|
||||
|
||||
st = UsbSubmitUrbSync(ext->LowerDevice, &urb);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("usb: HID SET_IDLE failed slot=%u if=%u st=0x%x; continuing\n",
|
||||
ext->Device->SlotId, ext->InterfaceNumber, st);
|
||||
}
|
||||
|
||||
DbgPrint("usb: HID boot protocol selected slot=%u if=%u proto=%u\n",
|
||||
ext->Device->SlotId, ext->InterfaceNumber, ext->Protocol);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS UsbHidArmInterrupt(USB_HID_EXTENSION* ext) {
|
||||
if (!ext || !ext->Started || ext->SurpriseRemoved || ext->Removed)
|
||||
return STATUS_OBJECT_DEAD;
|
||||
if (!ext->LowerDevice || !ext->InterruptPipe || !ext->ReportBuffer || !ext->ReportBufferPhys)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (ext->InterruptPending)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
memset(ext->ReportBuffer, 0, ext->ReportLength);
|
||||
memset(&ext->InterruptUrb, 0, sizeof(ext->InterruptUrb));
|
||||
ext->InterruptUrb.Function = UrbFunctionInterruptTransfer;
|
||||
ext->InterruptUrb.Direction = UsbTransferIn;
|
||||
ext->InterruptUrb.Device = ext->Device;
|
||||
ext->InterruptUrb.Pipe = ext->InterruptPipe;
|
||||
ext->InterruptUrb.Buffer = ext->ReportBuffer;
|
||||
ext->InterruptUrb.BufferPhys = ext->ReportBufferPhys;
|
||||
ext->InterruptUrb.Length = ext->ReportLength;
|
||||
ext->InterruptUrb.Complete = UsbHidInterruptComplete;
|
||||
ext->InterruptUrb.CompleteContext = ext;
|
||||
|
||||
KSTATUS st = UsbSubmitUrbAsync(ext->LowerDevice, &ext->InterruptUrb);
|
||||
if (st != STATUS_PENDING && !KSUCCESS(st))
|
||||
return st;
|
||||
if (ext->InterruptUrb.Status != STATUS_PENDING && ext->InterruptUrb.Status != STATUS_SUCCESS)
|
||||
return ext->InterruptUrb.Status;
|
||||
|
||||
ext->InterruptPending = true;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext) {
|
||||
if (!device || !ext || !ext->Device || !ext->InterfacePdo)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (ext->Started)
|
||||
return STATUS_SUCCESS;
|
||||
if (ext->SurpriseRemoved || ext->Removed)
|
||||
return STATUS_OBJECT_DEAD;
|
||||
|
||||
USB_PDO_EXTENSION* pdoExt = (USB_PDO_EXTENSION*)ext->InterfacePdo->DeviceExtension;
|
||||
if (!pdoExt || pdoExt->Kind != UsbPdoInterface)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
const USB_ENDPOINT_DESCRIPTOR* ep = UsbHidFindInterruptInPdo(pdoExt);
|
||||
if (!ep)
|
||||
return STATUS_NOT_FOUND;
|
||||
|
||||
ext->Keyboard = (pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD);
|
||||
ext->Mouse = (pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_MOUSE);
|
||||
ext->InterfaceNumber = pdoExt->InterfaceDescriptor.bInterfaceNumber;
|
||||
ext->Protocol = pdoExt->InterfaceDescriptor.bInterfaceProtocol;
|
||||
ext->EndpointAddress = ep->bEndpointAddress;
|
||||
ext->MaxPacketSize = ep->wMaxPacketSize;
|
||||
ext->Interval = ep->bInterval;
|
||||
|
||||
uint16_t reportLength = ext->MaxPacketSize & 0x07FFu;
|
||||
if (reportLength == 0) reportLength = ext->Keyboard ? 8u : 4u;
|
||||
if (ext->Keyboard && reportLength < 8) reportLength = 8;
|
||||
if (ext->Mouse && reportLength < 3) reportLength = 3;
|
||||
if (reportLength > XHCI_MAX_HID_REPORT) reportLength = XHCI_MAX_HID_REPORT;
|
||||
ext->ReportLength = reportLength;
|
||||
|
||||
ext->ReportBuffer = (uint8_t*)UsbAllocateDmaBuffer(ext->Device, 1, &ext->ReportBufferPhys);
|
||||
if (!ext->ReportBuffer)
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS st = UsbCreatePipe(ext->Device,
|
||||
UsbPipeInterrupt,
|
||||
ext->EndpointAddress,
|
||||
ext->MaxPacketSize,
|
||||
ext->Interval,
|
||||
&ext->InterruptPipe);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
st = UsbHidSendBootProtocol(ext);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
memset(ext->LastKeys, 0, sizeof(ext->LastKeys));
|
||||
ext->LastModifiers = 0;
|
||||
ext->LastButtons = 0;
|
||||
|
||||
KeInputRegisterDevice(ext->Keyboard ? "usb-hid-keyboard" : "usb-hid-mouse", 0, &ext->InputDeviceId);
|
||||
|
||||
ext->Started = true;
|
||||
device->PnpState = DevicePnpStarted;
|
||||
if (ext->InterfacePdo)
|
||||
ext->InterfacePdo->PnpState = DevicePnpStarted;
|
||||
|
||||
XHCI_CONTROLLER* xhci = ext->Device && ext->Device->Hcd ?
|
||||
(XHCI_CONTROLLER*)ext->Device->Hcd->ControllerContext : (XHCI_CONTROLLER*)0;
|
||||
|
||||
st = UsbHidArmInterrupt(ext);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
if (xhci)
|
||||
xhci->HidDeviceCount++;
|
||||
|
||||
DbgPrint("usb: HID boot %s bound slot=%u if=%u ep=%02x maxpkt=%u interval=%u input=%u urb\n",
|
||||
ext->Keyboard ? "keyboard" : "mouse",
|
||||
ext->Device->SlotId,
|
||||
ext->InterfaceNumber,
|
||||
ext->EndpointAddress,
|
||||
ext->MaxPacketSize,
|
||||
ext->Interval,
|
||||
ext->InputDeviceId);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (!device || !irp)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
|
||||
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
USB_HID_EXTENSION* ext = (USB_HID_EXTENSION*)device->DeviceExtension;
|
||||
KSTATUS st = STATUS_SUCCESS;
|
||||
|
||||
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
|
||||
case IRP_MN_START_DEVICE:
|
||||
st = UsbHidStartDevice(device, ext);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
device->PnpState = DevicePnpRemovePending;
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
|
||||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||||
if (!ext || !ext->SurpriseRemoved)
|
||||
device->PnpState = DevicePnpStarted;
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
device->PnpState = DevicePnpSurpriseRemoved;
|
||||
if (ext) {
|
||||
ext->SurpriseRemoved = true;
|
||||
ext->Started = false;
|
||||
if (ext->InterruptPending)
|
||||
(void)UsbCancelUrb(&ext->InterruptUrb);
|
||||
}
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE: {
|
||||
device->PnpState = DevicePnpDeleted;
|
||||
if (ext) {
|
||||
ext->Removed = true;
|
||||
ext->Started = false;
|
||||
if (ext->InterruptPending)
|
||||
(void)UsbCancelUrb(&ext->InterruptUrb);
|
||||
if (ext->InterruptPipe) {
|
||||
ext->InterruptPipe->Open = false;
|
||||
UsbDereferencePipe(ext->InterruptPipe);
|
||||
ext->InterruptPipe = (USB_PIPE*)0;
|
||||
}
|
||||
if (ext->Device && ext->Device->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)ext->Device->Hcd->ControllerContext;
|
||||
if (xhci && xhci->HidDeviceCount)
|
||||
xhci->HidDeviceCount--;
|
||||
}
|
||||
if (ext->RemoveLockReady) {
|
||||
IoReleaseRemoveLockAndWait(&ext->RemoveLock, irp);
|
||||
IoDestroyRemoveLock(&ext->RemoveLock);
|
||||
ext->RemoveLockReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS lower = ext && ext->LowerDevice ? IoForwardIrpToLower(device, irp) : STATUS_SUCCESS;
|
||||
if (ext && ext->LowerDevice) {
|
||||
IoDetachDevice(device);
|
||||
ext->LowerDevice = (DEVICE_OBJECT*)0;
|
||||
}
|
||||
IoDeleteDevice(device);
|
||||
if (lower == STATUS_NOT_SUPPORTED) {
|
||||
IoCompleteRequest(irp, STATUS_SUCCESS, 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
if (lower != STATUS_PENDING && !KSUCCESS(lower)) {
|
||||
IoCompleteRequest(irp, lower, 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
return lower;
|
||||
}
|
||||
|
||||
default:
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
|
||||
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
USB_PDO_EXTENSION* pdoExt = (USB_PDO_EXTENSION*)physicalDeviceObject->DeviceExtension;
|
||||
if (!pdoExt || pdoExt->Kind != UsbPdoInterface || !pdoExt->Device)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (pdoExt->InterfaceDescriptor.bInterfaceClass != USB_CLASS_HID ||
|
||||
pdoExt->InterfaceDescriptor.bInterfaceSubClass != USB_HID_SUBCLASS_BOOT)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (pdoExt->InterfaceDescriptor.bInterfaceProtocol != USB_HID_PROTOCOL_KEYBOARD &&
|
||||
pdoExt->InterfaceDescriptor.bInterfaceProtocol != USB_HID_PROTOCOL_MOUSE)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (!UsbHidFindInterruptInPdo(pdoExt))
|
||||
return STATUS_NOT_FOUND;
|
||||
|
||||
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
|
||||
KSTATUS st = IoCreateDevice(driver, sizeof(USB_HID_EXTENSION), &fdo);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
(void)IoSetDeviceName(fdo,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD
|
||||
? SYS_SV_LITERAL("usb-hid-keyboard")
|
||||
: SYS_SV_LITERAL("usb-hid-mouse"));
|
||||
|
||||
USB_HID_EXTENSION* ext = (USB_HID_EXTENSION*)fdo->DeviceExtension;
|
||||
ext->Self = fdo;
|
||||
ext->InterfacePdo = physicalDeviceObject;
|
||||
ext->Device = pdoExt->Device;
|
||||
st = IoInitializeRemoveLock(&ext->RemoveLock);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoDeleteDevice(fdo);
|
||||
return st;
|
||||
}
|
||||
ext->RemoveLockReady = true;
|
||||
|
||||
ext->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
|
||||
if (!ext->LowerDevice) {
|
||||
IoDestroyRemoveLock(&ext->RemoveLock);
|
||||
ext->RemoveLockReady = false;
|
||||
IoDeleteDevice(fdo);
|
||||
return STATUS_FAILURE;
|
||||
}
|
||||
|
||||
DbgPrint("usb: HID FDO attached slot=%u if=%u proto=%u\n",
|
||||
ext->Device->SlotId,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceNumber,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceProtocol);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS UsbInitializeHidDriver(void) {
|
||||
memset(&gUsbHidDriver, 0, sizeof(gUsbHidDriver));
|
||||
IoInitializeDriverObject(&gUsbHidDriver, SYS_SV_LITERAL("usb-hid"));
|
||||
gUsbHidDriver.IdTable = &gUsbHidIdTable;
|
||||
gUsbHidDriver.DriverAddDevice = UsbHidAddDevice;
|
||||
gUsbHidDriver.MajorFunction[IRP_MJ_PNP] = UsbHidDispatchPnp;
|
||||
return IoRegisterDriverObject(&gUsbHidDriver);
|
||||
}
|
||||
Reference in new issue
Block a user