Getting usb device descriptor from upper filter driver (kmdf)

I am developing an upper filter driver for USB devices, registered as an upper filter in the {36fc9e60-...-444553540000} USB class GUID, using the KMDF framework. My goal is to read the device descriptor during EvtWdfDevicePrepareHardware to obtain the device's class, subclass, and serial number.

To achieve this, I plan to send an IRP with the IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION control code to the lower PDO (obtained using WdfFdoInitWdmGetPhysicalDevice). I prepared the UsbDescriptorRequest structure and implemented the code to read the device descriptor, including the VID and PID.

However, in my current implementation, the VID and PID values retrieved are invalid. I’ve reviewed the code but haven’t identified the issue. Below is the relevant code. Can you help pinpoint the bug?

NTSTATUS status;
PDEVICE_OBJECT pdo;
IO_STATUS_BLOCK ioStatusBlock;
KEVENT event;
PIRP irp;
UCHAR Buffer[256];
USB_DESCRIPTOR_REQUEST descriptorRequest = { 0 };
ULONG descriptorRequestSize = sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_DEVICE_DESCRIPTOR);
PUSB_DEVICE_DESCRIPTOR deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)&descriptorRequest.Data[0];`

// Initialize the descriptor request
descriptorRequest.ConnectionIndex = 1; // Port index
descriptorRequest.SetupPacket.wValue = USB_DEVICE_DESCRIPTOR_TYPE << 8; // Device Descriptor
descriptorRequest.SetupPacket.wIndex = 0;
descriptorRequest.SetupPacket.wLength = sizeof(USB_DEVICE_DESCRIPTOR);

// Initialize synchronization event
KeInitializeEvent(&event, NotificationEvent, FALSE);

// Build the IRP
irp = IoBuildDeviceIoControlRequest(
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
dev_context->pdo,
&descriptorRequest,
sizeof(USB_DESCRIPTOR_REQUEST),
&descriptorRequest,
descriptorRequestSize,
TRUE,
&event,
&ioStatusBlock
);

if (!irp) {
KdPrint(("Failed to build IRP\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}

// Send the IRP
status = IoCallDriver(pdo, irp);

if (status == STATUS_PENDING) {
// Wait for completion
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatusBlock.Status;
}

if (NT_SUCCESS(status)) {
// Parse the USB Device Descriptor
KdPrint(("USB VID: 0x%04x, PID: 0x%04x\n",
deviceDescriptor->idVendor,
deviceDescriptor->idProduct));
} else {
KdPrint(("Failed to send IOCTL to PDO. Status: 0x%x\n", status));
}

If you are talking to the USB hub driver, then that request should be OK. How do you know your connection number? Have you tried others? You can also send that ioctl from user mode That's what usbview does.

Depending on your location in the stack, you can also send an URB directly, with IOCTL_INTERNAL_USB_SUBMIT_URB_IOCTL with URB_CONTROL_DESCRIPTOR_REQUEST.

1 Like

Thank you for your attention.

Since my driver is positioned as an upper filter for USB devices, I believe I am communicating with the USB hub driver (lower pdo of stack). Is that correct?

Regarding your question, "How do you know your connection number?" — if you mean 'connection index' which port of usb hub so i saw from usbview in target system (the device connected to port 1) so i set ConnectionIndex to 1)

Based on my understanding (from MSDN documentation and a post you made), I believe I cant send a URB directly (to wdfusbdevice from upper filter. also using WdfUsbTargetDeviceCreate or WdfUsbTargetDeviceCreateWithParameters, as both returned a "status not supported" error.

If by "directly" you mean I can send an IRP with the IOCTL_INTERNAL_USB_SUBMIT_URB IOCTL, could you explain what "directly" refers to in this context?

Well, no. It depends. If you call WdfUsbTargetDeviceCreate, then you are sending to the driver for the device you are filtering, right? You are sitting ABOVE that device.

1 Like

I use WdfFdoInitWdmGetPhysicalDevice to retrieve the PDO. I already read which WdfUsbTargetDeviceCreate is not supported in an upper filter driver, is that correct (Additionally, when I call it, it fails with the status error 'not supported.')

You always have to bear in mind, "what is the interface I'm crossing here?" WdfUsbTargetDeviceCreate works when you are sitting on top of a driver that expects URBs, which includes the generic USB drivers (HCD, usbhub, usbccgp). If you are the upper filter to a function driver, then the driver immediately below you doesn't understand URBs, so that API is not useful.

Thank you, but my issue remains unresolved. Was the output buffer provided to IoBuildDeviceIoControlRequest correct? I suspect there's something wrong in this code.

irp = IoBuildDeviceIoControlRequest( IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, dev_context->pdo, &descriptorRequest, sizeof(USB_DESCRIPTOR_REQUEST), &descriptorRequest, descriptorRequestSize, TRUE, &event, &ioStatusBlock );

This code is wrong:

USB_DESCRIPTOR_REQUEST descriptorRequest = { 0 };
ULONG descriptorRequestSize = sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_DEVICE_DESCRIPTOR);
PUSB_DEVICE_DESCRIPTOR deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)&descriptorRequest.Data[0];

You are only allocating enough room for the descriptorRequest, but not for the descriptor itself. You need to allocate that from a pool:

ULONG descriptorRequestSize = sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_DEVICE_DESCRIPTOR);
PUSB_DESCRIPTOR_REQUEST pDescriptorRequest = (PUSB_DESCRIPTOR_REQUEST) ExAllocPool2( POOL_FLAG_NONPAGED, descriptorRequestSize, 0);
PUSB_DEVICE_DESCRIPTOR deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)pDescriptorRequest.Data[0];

and remember to ExFreePool the request later.