Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


[WDF] Getting USB Device Descriptor (in upperfilter driver)

rumble06rumble06 Member Posts: 28

I have a keyboard UpperFilter driver (that supports both PS/2 and HID/USB keyboards) and I'm trying to use as much info I can get about the devices as possible to identify them. For PS/2 I'm just using the HardwareID, but for USB keyboards I want to also use the Serial Number (if provided, as most manufacturers don't). I see that the easiest way is to get it using WdfUsbTargetDeviceGetDeviceDescriptor, however for that I need to create an WDFUSBDEVICE. I'm trying to useWdfUsbTargetDeviceCreateWithParametersto create an WDFUSBDEVICE, however the function fails with code 0xC0000010 (STATUS_INVALID_DEVICE_REQUEST). The docs of the method says it can be used in EvtDeviceAdd (and that is where I first tried to use it), however the docs for WdfUsbTargetDeviceCreate says it can only be used in EvtDevicePrepareHardware, so I moved my method there thinking this may be the reason. However, I also get the same status here. The device is a normal USB keyboard, the machine is a VM running inside VMWare with the keyboard added to vmware usb passtrough functionality. This is my code:

        WDF_USB_DEVICE_CREATE_CONFIG UsbDeviceConfig;

        // Create WDFUSBDEVICE object
        WDF_USB_DEVICE_CREATE_CONFIG_INIT(&UsbDeviceConfig, USBD_CLIENT_CONTRACT_VERSION_602);

        NTSTATUS UsbDeviceCreateStatus = WdfUsbTargetDeviceCreateWithParameters(hDevice, &UsbDeviceConfig, WDF_NO_OBJECT_ATTRIBUTES, &filterExt->UsbDevice);
        if (!NT_SUCCESS(UsbDeviceCreateStatus)) {
            KdPrint(("WdfUsbTargetDeviceCreateWithParameters failed with status 0x%x\n", UsbDeviceCreateStatus));

            // If failed to create an USB Device, handle device as non-usb (PS/2)
            filterExt->UsbDevice = NULL;
        }

I can't seem to find an explanation of why the device creation would fail except vmware usb passthrough, however I do not have any testing physical PC on which I could test to see if this is the problem, so before finding a spare harddrive to put a copy of windows on it and to test it on my own PC, I thought it's better to ask here as the problem may be another one that I do not see and someone else could point it to me.

Comments

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,157

    This is a KMDF driver.... not a UMDF driver?

    Enable WDF Verifier, set Verbose logging, and dump the log with !WDFKD.WDFLOGDUMP

    P

    Peter Viscarola
    OSR
    @OSRDrivers

  • rumble06rumble06 Member Posts: 28

    @Peter_Viscarola_(OSR) said:
    This is a KMDF driver.... not a UMDF driver?

    Enable WDF Verifier, set Verbose logging, and dump the log with !WDFKD.WDFLOGDUMP

    P

    Yes, it is a KMDF driver (it's a upper class filter for keyboards and as per-microsoft documentation, UMDF can't be used to write a class filter driver).

    Log dumped using wdflogdump(posted it on pastebin because it wouldn't let me to put it here as it is too long): https://pastebin.com/TZ7aEapd

  • Doron_HolanDoron_Holan Member - All Emails Posts: 10,534
    Kbdhid as the FDO does not accept USB IO requests. It only accepts HID and kbdclass requests. And even if you sent the request directly to the PDO it would not work as it’s a HID PDO, not a USB enumerated PDO. You can’t get a USB descriptor from where you are in the stack.
    d
  • rumble06rumble06 Member Posts: 28

    @Doron_Holan said:
    Kbdhid as the FDO does not accept USB IO requests. It only accepts HID and kbdclass requests. And even if you sent the request directly to the PDO it would not work as it’s a HID PDO, not a USB enumerated PDO. You can’t get a USB descriptor from where you are in the stack.

    So there is no method I can get the SerialNumber (if the device has one) from here? You're saying that it accepts HID requests, does that mean I can use WdfRequestSend to send an IOCTL_HID_GET_SERIALNUMBER_STRING request down and hopefully getting a S/N for the device (I guess there is no point in sending an IOCTL_INTERNAL_USB_SUBMIT_URB to get an usb device descriptor, as you said it doesn't accepts USB IO requests)?

  • Doron_HolanDoron_Holan Member - All Emails Posts: 10,534

    I misspoke slightly. kbdhid doesn't allow incoming HID requests either, only keyboard class IOCTLs are processed. Technically you could send IOCTL_HID_GET_SERIALNUMBER_STRING directly to the PDO, but if HIDCLASS requires that you have a valid file object to send this IOCTL, you won't be able to open that file object. But perhaps this is all too low level. Why not query for the DEVICE_CAPABILITIES, see if UniqueID is set to TRUE. If it is, query the device instance ID and you will get your unique identifier.

    d
  • rumble06rumble06 Member Posts: 28

    @Doron_Holan said:
    I misspoke slightly. kbdhid doesn't allow incoming HID requests either, only keyboard class IOCTLs are processed. Technically you could send IOCTL_HID_GET_SERIALNUMBER_STRING directly to the PDO, but if HIDCLASS requires that you have a valid file object to send this IOCTL, you won't be able to open that file object. But perhaps this is all too low level. Why not query for the DEVICE_CAPABILITIES, see if UniqueID is set to TRUE. If it is, query the device instance ID and you will get your unique identifier.

    Thanks for your info and help. When querying for instance ID (IRP_MN_QUERY_ID) when UniqueID is set to FALSE, isn't the OS supposed to give a unique ID based on the current usb port the device is plugged in? Because when the UniqueID is FALSE, what I get from IRP_MN_QUERY_ID is (null). I haven't yet tested with a keyboard that has the UniqueID set to TRUE because I'm still searching for one that does.

    I have this to get and print the result of the IRP:

    // print device capabilities
    [...]
    WDF_REQUEST_COMPLETION_PARAMS completionParams;
    WDF_REQUEST_COMPLETION_PARAMS_INIT(&completionParams);
    WdfRequestGetCompletionParams(Request, &completionParams);
    
    KdPrint(("Instance get status: 0x%x, Instance id: %ws\n", completionParams.IoStatus.Status, completionParams.IoStatus.Information));
    

    And this is printed:

    dev_cap: Address 1, SurpriseRemoval: TRUE, Unique ID: FALSE 
    Instance get status: 0x0, Instance id: (null)
    

    As you see, the IRP finish successfully (if I do the same IRP for a PS/2 keyboard, it will return with an error status code and not with 0x0), however the instance id is printed as being (null). Why does this happen? Does InstanceID only gets generated when there are two devices of the same type plugged in and may this be the reason I'm getting it as null?

  • Doron_HolanDoron_Holan Member - All Emails Posts: 10,534
    Post the code that queries the ID, perhaps there is a mistake
    d
  • rumble06rumble06 Member Posts: 28
    edited October 31

    @Doron_Holan said:
    Post the code that queries the ID, perhaps there is a mistake

            // Instance id
            {
                WDFREQUEST Request;
                WDF_REQUEST_SEND_OPTIONS options;
                WDF_REQUEST_SEND_OPTIONS_INIT(&options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
    
                WDFIOTARGET target = WdfDeviceGetIoTarget(s->WdfDevice);
    
                NTSTATUS instStatus;
    
                IO_STACK_LOCATION stack;
                RtlZeroMemory(&stack, sizeof(stack));
    
                stack.MajorFunction = IRP_MJ_PNP;
                stack.MinorFunction = IRP_MN_QUERY_ID;
                stack.Parameters.QueryId.IdType = BusQueryInstanceID;
    
                instStatus = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, target, &Request);
                if (NT_SUCCESS(instStatus)) {
    
                    WDF_REQUEST_REUSE_PARAMS reuse;
                    WDF_REQUEST_REUSE_PARAMS_INIT(&reuse, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
    
                    WdfRequestReuse(Request, &reuse);
    
                    WdfRequestWdmFormatUsingStackLocation(Request, &stack);
    
                    if (WdfRequestSend(Request, target, &options) == TRUE) {
                        instStatus = WdfRequestGetStatus(Request);
                        if (NT_SUCCESS(instStatus)) {
                            WDF_REQUEST_COMPLETION_PARAMS completionParams;
                            WDF_REQUEST_COMPLETION_PARAMS_INIT(&completionParams);
                            WdfRequestGetCompletionParams(Request, &completionParams);
    
                            KdPrint(("Instance get status: 0x%x, Instance id: %ws\n", completionParams.IoStatus.Status, completionParams.IoStatus.Information));
                        }
                        else {
                            KdPrint(("instanceid failed with status 0x%x\n", instStatus));
                        }
                    }
                    else {
                        KdPrint(("INstanceID WdfRequestSend failed\n"));
                    }
    
                }
            }
    

    May it be because I haven't allocated any memory for the output buffer? I saw that every IRPs that returns something say to allocate an output buffer, but the docs for this one didn't, the only thing said is that the output string can be found in IoStatus.Information

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,694

    This is a brand-new request, so I don't understand the call to WdfRequestReuse, but it shouldn't have any effect on the results.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • rumble06rumble06 Member Posts: 28
    > @Tim_Roberts said:
    > This is a brand-new request, so I don't understand the call to WdfRequestReuse, but it shouldn't have any effect on the results.

    I read on the internet that any PnP request must be initialized with STATUS_NOT_SUPPORTED and with WdfRequestReuse is the only way I found to set a status to it
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,157

    I would have used WdfRequestSetInformarion.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,694

    Ordinarily, KMDF takes care of all of these arcane details automatically. I haven't found it in the source yet, but I'd be surprised if it wasn't doing that for you.

    And, Peter, WdfRequestSetInformation doesn't set the Status value...

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,157
    edited November 1

    Sorry, OP and thanks Tim... reading too fast. My bad!

    So, let’s try again: I’d use WdfRequestWdmGetIrp!

    Gad... I hope I got that right this time :o

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Writing WDF Drivers 7 Dec 2020 LIVE ONLINE
Internals & Software Drivers 25 Jan 2021 LIVE ONLINE
Developing Minifilters 8 March 2021 LIVE ONLINE