replace ad hoc driver loading with a proper driver manager, new DDK, new PnP manager with device matching, asynchronous startup, resource ownership and removal, add shared PCI capability helpers, replace typed, rigid services with a proper service manager, implement proper system shutdown and restart, shutting down unloads drivers and shuts down services, add simple power button, remove heavyweight boot self-tests and SMP stress runs, add monotonic timestamps to kernel debug output, fruitless USB and PS/2 fixes, make test builds opt in.
This commit is contained in:
1 parent
c0ad63c27f
commit
d36f19509c
160 files changed
+6563
-7366
No files matched your search
+156
-42
@@ -99,7 +99,7 @@ static KSTATUS UsbHubEnumerateRootPortWithRetries(USB_HUB_EXTENSION* hub,
|
||||
|
||||
DbgPrint("usb-hub: root port %u enumeration attempt %u failed st=0x%x; retrying\n",
|
||||
port, attempt, last);
|
||||
UsbDelayUs(xhci->Ddk, 50000);
|
||||
UsbDelayUs(50000);
|
||||
}
|
||||
|
||||
if (UsbHubRootPortConnected(xhci, port)) {
|
||||
@@ -126,7 +126,7 @@ static KSTATUS UsbHubEnumerateConnectedRootPort(USB_HUB_EXTENSION* hub,
|
||||
hub->RootPortSlot[port] = USB_HUB_PORT_ENUMERATING;
|
||||
hub->RootPortState[port] = UsbHubPortConnectedDebounce;
|
||||
|
||||
UsbDelayUs(xhci->Ddk, 30000u);
|
||||
UsbDelayUs(30000u);
|
||||
if (!UsbHubRootPortConnected(xhci, port)) {
|
||||
UsbHubClearRootPortFailure(hub, port);
|
||||
hub->RootPortSlot[port] = 0;
|
||||
@@ -393,13 +393,21 @@ static KSTATUS UsbHubResetExternalPort(USB_HUB_EXTENSION* hub, uint8_t port, uin
|
||||
|
||||
USB_HUB_PORT_STATUS ps;
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
bool resetComplete = false;
|
||||
for (uint32_t i = 0; i < 500u; i++) {
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 1000u);
|
||||
UsbDelayUs(1000u);
|
||||
st = UsbHubGetPortStatus(hub, port, &ps);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
if (ps.Change & USB_HUB_PORT_CHANGE_RESET)
|
||||
if (ps.Change & USB_HUB_PORT_CHANGE_RESET) {
|
||||
resetComplete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!resetComplete) {
|
||||
DbgPrint("usb-hub: slot=%u port=%u reset timed out status=%04x change=%04x\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, ps.Status, ps.Change);
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
|
||||
@@ -464,7 +472,7 @@ static void UsbHubHandleExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port)
|
||||
|
||||
hub->ChildSlot[port] = USB_HUB_PORT_ENUMERATING;
|
||||
hub->ChildPortState[port] = UsbHubPortConnectedDebounce;
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 30000u);
|
||||
UsbDelayUs(30000u);
|
||||
|
||||
uint8_t speed = 0;
|
||||
hub->ChildPortState[port] = UsbHubPortResetting;
|
||||
@@ -478,8 +486,12 @@ static void UsbHubHandleExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port)
|
||||
}
|
||||
|
||||
hub->ChildPortState[port] = UsbHubPortEnumerating;
|
||||
DbgPrint("usb-stage: hub-slot=%u port=%u child enumeration begin speed=%u\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, speed);
|
||||
uint8_t slot = 0;
|
||||
st = XhciEnumerateHubPortEx((XHCI_CONTROLLER*)hub->Hcd->ControllerContext, hub, port, speed, &slot);
|
||||
DbgPrint("usb-stage: hub-slot=%u port=%u child enumeration done st=0x%x slot=%u\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, st, slot);
|
||||
if (KSUCCESS(st)) {
|
||||
hub->ChildSlot[port] = slot;
|
||||
hub->ChildPortState[port] = UsbHubPortChildPresent;
|
||||
@@ -580,6 +592,12 @@ static bool UsbHubTakeInitialRootScan(USB_HUB_EXTENSION* hub) {
|
||||
// Implements the hub Worker operation.
|
||||
static void UsbHubWorker(void* arg) {
|
||||
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)arg;
|
||||
if (hub && hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root worker entered ports=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount);
|
||||
}
|
||||
while (hub && hub->WorkerRunning) {
|
||||
KSTATUS st = KeWaitSemaphore(hub->WorkerSemaphore, KE_WAIT_INFINITE);
|
||||
if (!KSUCCESS(st))
|
||||
@@ -588,8 +606,11 @@ static void UsbHubWorker(void* arg) {
|
||||
break;
|
||||
|
||||
if (hub->Kind == UsbHubKindRoot) {
|
||||
if (UsbHubTakeInitialRootScan(hub))
|
||||
(void)UsbHubDoRootPortScan(hub->Hcd);
|
||||
if (UsbHubTakeInitialRootScan(hub)) {
|
||||
KSTATUS scanStatus = UsbHubDoRootPortScan(hub->Hcd);
|
||||
if (!KSUCCESS(scanStatus))
|
||||
DbgPrint("usb-stage: root scan aborted st=0x%x\n", scanStatus);
|
||||
}
|
||||
|
||||
uint8_t port = 0;
|
||||
while (UsbHubPopPendingRootPort(hub, &port))
|
||||
@@ -657,6 +678,12 @@ static KSTATUS UsbHubStartWorker(USB_HUB_EXTENSION* hub) {
|
||||
UsbHubStopWorker(hub);
|
||||
return status;
|
||||
}
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root worker created\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -675,6 +702,8 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
if (!xhci || !xhci->Op)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root scan begin ports=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount);
|
||||
for (uint8_t port = 1; port <= hub->PortCount; port++) {
|
||||
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
|
||||
if (!(portsc & XHCI_PORTSC_CCS))
|
||||
@@ -686,9 +715,13 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u begin portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc);
|
||||
attempted++;
|
||||
uint8_t slot = 0;
|
||||
KSTATUS st = UsbHubEnumerateConnectedRootPort(hub, xhci, port, &slot);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u done st=0x%x slot=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, st, slot);
|
||||
if (KSUCCESS(st)) {
|
||||
hub->RootPortSlot[port] = slot;
|
||||
hub->RootPortState[port] = UsbHubPortChildPresent;
|
||||
@@ -699,27 +732,45 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
}
|
||||
}
|
||||
|
||||
DbgPrint("usb-hub: root-port scan complete; attempted=%u configured=%u\n", attempted, configured);
|
||||
DbgPrint("usb-hub: hcd=%02x:%02x.%u root-port scan complete; attempted=%u configured=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, attempted, configured);
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
|
||||
hub->InitialScanComplete = true;
|
||||
hub->InitialScanInProgress = false;
|
||||
KeReleaseSpinlock(&hub->PendingLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the hub Schedule Root Scan operation.
|
||||
KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd) {
|
||||
if (!hcd || !hcd->RootHubContext)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)hcd->RootHubContext;
|
||||
if (hub->Kind != UsbHubKindRoot || !hub->Started)
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
static SEMAPHORE* UsbHubQueueInitialScan(USB_HUB_EXTENSION* hub) {
|
||||
if (!hub || !hub->Started || !hub->WorkerSemaphore)
|
||||
return (SEMAPHORE*)0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
|
||||
if (!hub->InitialScanComplete && !hub->InitialScanInProgress)
|
||||
bool pending = !hub->InitialScanComplete && !hub->InitialScanInProgress;
|
||||
if (pending)
|
||||
hub->InitialScanPending = true;
|
||||
KeReleaseSpinlock(&hub->PendingLock, flags);
|
||||
|
||||
if (hub->WorkerSemaphore)
|
||||
KeSignalSemaphore(hub->WorkerSemaphore);
|
||||
if (!pending)
|
||||
return (SEMAPHORE*)0;
|
||||
|
||||
ObReferenceObject(&hub->WorkerSemaphore->Header);
|
||||
return hub->WorkerSemaphore;
|
||||
}
|
||||
|
||||
KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd) {
|
||||
if (!hcd || !hcd->RootHubPdo)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&hcd->RootHubLock);
|
||||
hcd->RootScanRequested = true;
|
||||
SEMAPHORE* wake = UsbHubQueueInitialScan((USB_HUB_EXTENSION*)hcd->RootHubContext);
|
||||
KeReleaseSpinlock(&hcd->RootHubLock, flags);
|
||||
|
||||
if (wake) {
|
||||
KeSignalSemaphore(wake);
|
||||
ObDereferenceObject(&wake->Header);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -800,8 +851,10 @@ static void UsbHubExternalChangeComplete(USB_URB* urb, void* context) {
|
||||
return;
|
||||
|
||||
hub->ChangeUrbPending = false;
|
||||
if (!KSUCCESS(urb->Status) || hub->Removed || hub->SurpriseRemoved)
|
||||
if (!KSUCCESS(urb->Status) || hub->Removed || hub->SurpriseRemoved) {
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t actual = urb->ActualLength;
|
||||
if (actual > USB_HUB_CHANGE_BITMAP_BYTES)
|
||||
@@ -813,6 +866,7 @@ static void UsbHubExternalChangeComplete(USB_URB* urb, void* context) {
|
||||
if (byteIndex < actual && (hub->ChangeBitmap[byteIndex] & (1u << bit)))
|
||||
UsbHubQueueExternalPortChange(hub, port);
|
||||
}
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
}
|
||||
|
||||
// Implements the hub Arm External Interrupt operation.
|
||||
@@ -820,6 +874,10 @@ static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub) {
|
||||
if (!hub || !hub->InterruptPipe || !hub->ChangeBitmap || hub->ChangeUrbPending)
|
||||
return;
|
||||
|
||||
KSTATUS refStatus = IoAcquireRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
if (!KSUCCESS(refStatus))
|
||||
return;
|
||||
hub->ChangeUrbPending = true;
|
||||
memset(hub->ChangeBitmap, 0, USB_HUB_CHANGE_BITMAP_BYTES);
|
||||
memset(&hub->ChangeUrb, 0, sizeof(hub->ChangeUrb));
|
||||
hub->ChangeUrb.Function = UrbFunctionInterruptTransfer;
|
||||
@@ -835,11 +893,12 @@ static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub) {
|
||||
hub->ChangeUrb.CompleteContext = hub;
|
||||
|
||||
KSTATUS st = UsbSubmitUrbAsync(hub->LowerDevice, &hub->ChangeUrb);
|
||||
if (st == NTLSTATUS_PENDING) {
|
||||
hub->ChangeUrbPending = true;
|
||||
} else if (!KSUCCESS(st)) {
|
||||
if (st != NTLSTATUS_PENDING || hub->ChangeUrb.Status != NTLSTATUS_PENDING) {
|
||||
hub->ChangeUrbPending = false;
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
DbgPrint("usb-hub: slot=%u interrupt arm failed st=0x%x\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, st);
|
||||
hub->Device ? hub->Device->SlotId : 0,
|
||||
st == NTLSTATUS_PENDING ? hub->ChangeUrb.Status : st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,12 +912,30 @@ static KSTATUS UsbHubStartRootDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSION* h
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
hub->Started = true;
|
||||
device->PnpState = DevicePnpStarted;
|
||||
if (hub->HubPdo)
|
||||
hub->HubPdo->PnpState = DevicePnpStarted;
|
||||
|
||||
DbgPrint("usb-hub: root hub started ports=%u\n", hub->PortCount);
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
hub->Started = true;
|
||||
SEMAPHORE* wake = hub->Hcd->RootScanRequested ? UsbHubQueueInitialScan(hub) : (SEMAPHORE*)0;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-hub: hcd=%02x:%02x.%u root hub started ports=%u scan=%s\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount,
|
||||
wake ? "queued" : "not-queued");
|
||||
|
||||
// Publish the startup checkpoint before waking enumeration. A bad port or
|
||||
// an interrupt storm can otherwise hide which hub started the work.
|
||||
if (wake) {
|
||||
KeSignalSemaphore(wake);
|
||||
ObDereferenceObject(&wake->Header);
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u initial scan wake returned\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -888,10 +965,24 @@ static KSTATUS UsbHubStartExternalDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSIO
|
||||
}
|
||||
hub->PowerOnToPowerGoodMs = desc.bPwrOn2PwrGood ? (uint8_t)(desc.bPwrOn2PwrGood * 2u) : 100u;
|
||||
|
||||
// xHCI needs the hub's Slot Context before routing low/full-speed child
|
||||
// transfers through a high-speed transaction translator.
|
||||
XHCI_CONTROLLER* xhci = hub->Hcd ? (XHCI_CONTROLLER*)hub->Hcd->ControllerContext : (XHCI_CONTROLLER*)0;
|
||||
XHCI_DEVICE* xdev = xhci && hub->Device->SlotId <= XHCI_MAX_TRACKED_SLOTS
|
||||
? &xhci->Devices[hub->Device->SlotId] : (XHCI_DEVICE*)0;
|
||||
if (!xdev || !xdev->Allocated) {
|
||||
UsbHubReleaseDma(hub);
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
st = XhciConfigureHubContext(xhci, xdev, hub->PortCount, desc.wHubCharacteristics);
|
||||
if (!KSUCCESS(st)) {
|
||||
UsbHubReleaseDma(hub);
|
||||
return st;
|
||||
}
|
||||
|
||||
for (uint8_t port = 1; port <= hub->PortCount; port++)
|
||||
(void)UsbHubSetPortFeature(hub, port, USB_HUB_PORT_FEAT_POWER);
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk,
|
||||
(uint32_t)hub->PowerOnToPowerGoodMs * 1000u);
|
||||
UsbDelayUs((uint32_t)hub->PowerOnToPowerGoodMs * 1000u);
|
||||
|
||||
st = UsbHubStartWorker(hub);
|
||||
if (!KSUCCESS(st)) {
|
||||
@@ -971,12 +1062,22 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
device->PnpState = DevicePnpSurpriseRemoved;
|
||||
if (hub) {
|
||||
hub->SurpriseRemoved = true;
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
if (hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
hub->Started = false;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
}
|
||||
hub->WorkerRunning = false;
|
||||
if (hub->ChangeUrbPending)
|
||||
(void)UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (hub->WorkerSemaphore)
|
||||
KeSignalSemaphore(hub->WorkerSemaphore);
|
||||
UsbHubStopWorker(hub);
|
||||
if (hub->ChangeUrbPending) {
|
||||
st = UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove downstream PDOs owned by this hub FDO. The explicit
|
||||
// slot arrays are still used as the hardware lookup table; the
|
||||
@@ -1000,23 +1101,34 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
device->PnpState = DevicePnpDeleted;
|
||||
if (hub) {
|
||||
hub->Removed = true;
|
||||
hub->WorkerRunning = false;
|
||||
if (hub->ChangeUrbPending)
|
||||
(void)UsbCancelUrb(&hub->ChangeUrb);
|
||||
(void)UsbRemoveDeviceObjectChildren(device, false);
|
||||
if (hub->InterruptPipe) {
|
||||
hub->InterruptPipe->Open = false;
|
||||
UsbDereferencePipe(hub->InterruptPipe);
|
||||
hub->InterruptPipe = (USB_PIPE*)0;
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
if (hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
hub->Started = false;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
}
|
||||
hub->WorkerRunning = false;
|
||||
UsbHubStopWorker(hub);
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd && hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
if (hub->ChangeUrbPending) {
|
||||
st = UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
(void)UsbRemoveDeviceObjectChildren(device, false);
|
||||
if (hub->InterruptPipe)
|
||||
hub->InterruptPipe->Open = false;
|
||||
if (hub->RemoveLockReady) {
|
||||
IoReleaseRemoveLockAndWait(&hub->RemoveLock, irp);
|
||||
IoDestroyRemoveLock(&hub->RemoveLock);
|
||||
hub->RemoveLockReady = false;
|
||||
}
|
||||
if (hub->InterruptPipe) {
|
||||
UsbDereferencePipe(hub->InterruptPipe);
|
||||
hub->InterruptPipe = (USB_PIPE*)0;
|
||||
}
|
||||
UsbHubReleaseDma(hub);
|
||||
}
|
||||
|
||||
@@ -1118,7 +1230,9 @@ KSTATUS UsbHubAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje
|
||||
}
|
||||
|
||||
if (kind == UsbHubKindRoot) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
hub->Hcd->RootHubContext = hub;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
DbgPrint("usb-hub: root hub FDO attached\n");
|
||||
} else {
|
||||
DbgPrint("usb-hub: external hub FDO attached slot=%u ep=%02x\n",
|
||||
@@ -1135,5 +1249,5 @@ KSTATUS UsbInitializeHubDriver(void) {
|
||||
gUsbHubDriver.IdTable = &gUsbHubIdTable;
|
||||
gUsbHubDriver.DriverAddDevice = UsbHubAddDevice;
|
||||
gUsbHubDriver.MajorFunction[IRP_MJ_PNP] = UsbHubDispatchPnp;
|
||||
return IoRegisterDriverObject(&gUsbHubDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user