HID Minidriver: How Does Input Data Flow? RRS feed

  • Question

  • I'm trying to write a UMDF HID minidriver that takes one of my company's "generic" 64-channel analog input devices and presents it as a gamepad. I based it on the VHIDMini2 sample. Everything seems to be working… except I have no control over the actual data. How does the data flow for inputs?

    I expected IOCTL_UMDF_HID_GET_INPUT_REPORT to be the main answer, but apparently not. I've also modified IOCTL_HID_READ_REPORT to immediately respond by calling GetInputReport, instead of waiting, but to no avail. Modifications to the descriptors to play games with the logical range seem to have no effect, but it's possible the logical range is being ignored.

    (link excluded on new forum account)

    The ultimate goal is to be able to use this in Brainsalt Media's Conductor; I have version 2019.5.27.5712. However, this is simply a DirectInput client, and any old DirectInput client should work. No matter what I put in the input report, it always reads the first channel at 50% and every other channel at 0%. Presumably this data is coming from somewhere else in the driver.

    Monday, August 5, 2019 9:12 PM

All replies

  • In theory, an application calls HidD_GetInputReport, which gets converted to IOCTL_HID_GET_INPUT_REPORT, which the reflector passes to you as IOCTL_UMDF_HID_GET_INPUT_REPORT.

    Are you seeing any ioctl calls in your driver?  I'd be printing out the ioctl codes to try to match them up with the IOCTL_HID or IOCTL_UMDF sets.  Have you set all the registry values you need to set to have a kernel driver on top of you?

    Tim Roberts | Driver MVP Emeritus | Providenza & Boekelheide, Inc.

    Tuesday, August 6, 2019 3:42 AM
  • Currently trying to determine whether I'm seeing any IOCTL calls in the driver… In our older (WDM) drivers I'd log to the registry, but I seem to be misusing the WDF registry functions. My registry writes aren't working from even DriverEntry or AddDevice. But the device appears in Device Manager and generates a child device based on the descriptors I set, so I know it's at least getting that far.

    I've been assuming I've been getting IOCTLs, since IOCTL_HID_GET_DEVICE_DESCRIPTOR and IOCTL_HID_GET_REPORT_DESCRIPTOR seem to be functioning, but this sample may include another way to fetch descriptors.

    I saw in VHIDMini2 some registry values to have a kernel driver on top, I left those alone, and Device Manager does show MSHIDUMDF.sys and WUDFRd.sys getting loaded for my device.
    Tuesday, August 6, 2019 7:53 PM
  • For better logging options, take a look at WDK10-Samples\General\Tracing\TraceDriver and WDK10-Samples\network\wlan\WDI\PLATFORM\NDIS6\SDIO\N6Sdio_main.c

    The former demonstrates how to use WPP tracing, and the latter how to use the TraceLogging APIs


    Azius Developer Training Windows device driver, internals, security, & forensics training and consulting. Blog at

    Thursday, August 8, 2019 12:58 AM
  • Okay, I'm definitely getting IOCTL_UMDF_HID_GET_INPUT_REPORT. I tried a wide variety of techniques to get something out of it and almost nothing worked, but I can reliably INT 3 under host/target debugging. (OutputDebugStringA seems to do nothing even in a debug build, but I may just be using it wrong.)

    I'm definitely not accustomed to UMDF.
    Saturday, August 10, 2019 12:43 AM
  • OutputDebugString is not same as WPP or TraceLogging. Remember that UM drivers run in session 0, like other services.

    Setting up WPP is (or was) a chore... maybe now with TraceLoggiong it is easier.

    /* I used to just call DbgPrintEx[WithPrefix] from usermode.... until Microsoft broke it in Win10. With good intentions, hopefully. */

    -- pa

    Saturday, August 10, 2019 1:11 AM
  • The TraceLogging APIs were clearly designed with data logging in mind, but you can use them as an equivalent of DbgPrintEx, but it will take some knowledge of how ETW works (or read all the TraceLogging docs and look at TraceLoggingProvider.h).

    Personally, I find using WPP for debug output to be easier. I posted the WPP macros that I use here. Copy the posted definition into a file named something like WPP_Tracing.h and include it in all your .C/.CPP files. Add the following lines to the beginning of that file, and then replace the GUID with your own GUID (using GUIDGen or in Visual Studio: Tools->Create GUID). Change the WPP_DEFINE_BIT lines to be whatever criteria you want to use for enabling or disabling tracing. I have a separate bit for each file in my drivers, but you can partition it any way you like. Leave the ALL_COMPONENTS and TRACE_ROUTINE lines.

    // Define the tracing flags
    // Tracing GUID for this driver is {151EBE3F-2010-40A4-827D-36AD69048292}
    #define WPP_CONTROL_GUIDS											\
    	WPP_DEFINE_CONTROL_GUID(										\
    		mydrivername_TraceGuid, (151EBE3F, 2010, 40A4, 827D, 36AD69048292),	\
    		WPP_DEFINE_BIT(DRIVER)										\
    		WPP_DEFINE_BIT(DEVICE)										\
    		WPP_DEFINE_BIT(HW)											\
    		WPP_DEFINE_BIT(IO)											\
    		WPP_DEFINE_BIT(XFER)										\

    At the beginning of each routine, I start with TRACE_ENTER (); and the last line before the return is TRACE_EXIT (); This will allow you to see a call trace in the debugger. For values that you want to output, use TRACE_{INFO|WARN|ERROR|VERBOSE} (<one of the names in the WPP_DEFINE_BIT macros>, "printf format string", params,...), e.g. TRACE_ERROR (DEVICE, "Error returned from IoCallDriver, status = %!STATUS!, status). The "%!STATUS!" decodes the NTSTATUS value into its name, which is quite handy and a big time saver. In the beginning of DriverEntry, do this: WPP_INIT_TRACING (Wdm_driver_object, Registry_path), and at the end of your driver cleanup/unload routine (and the failure path in DriverEntry) do this: WPP_CLEANUP (wdm_driver_object).

    In Visual Studio, go into your driver's properties and set Wpp Tracing->General->Run Wpp Tracing = Yes, set Wpp Tracing->Search and Formatting->Specify Module Name = yourdrivername, Wpp Tracing->File Options->Scan Configuration Data = Wpp_tracing.h (or whatever you named it). Note, Visual Studio will run WPP before the C++ compiler so if there are syntax errors then WPP will output errors. They usually contain the line number, so it isn't too hard to fix. You'll see lots of red squiggles on lines containing WPP macros because Visual Studio isn't very smart. WPP will be run on each of your source files, and it will create a file with a .TMH extension in your project's intermediate output directory. That .TMH file will have to be included in the source file, e.g. for file Device.cpp, WPP will create Device.TMH, so in Device.cpp add: #include "Device.tmh". In the driver's property page, in C/C++->General, add $(IntDir) to the beginning of the "Additional Include Directories" property, so the compiler can find the .TMH file.

    To enable WPP tracing, on the TARGET machine run the following PowerShell script (change the GUID to match what is in your WPP_Tracing.h file), which creates a persistent "auto logger", and then reboot the target. The bitmask passed to Add-EtwTraceProvider as the -MatchAnyKeyword parameter matches the bits defined with the WPP_DEFINE_BIT macro. The script enables everything by default. You may change which bits are enabled at any time, even while the driver is running.

    # Create an ETW autologger that will be enabled after the next system boot
    # This script must be run with Admin privileges on the target system
    # Brian Catlin
    # Always create a new session GUID to identify this logger
    $session_guid = "{$((New-Guid).Guid)}"
    # Flags for New-AutologgerConfig's -LogFileMode parameter
    # From EvnTrace.h
    $EVENT_TRACE_FILE_MODE_NONE		= 0x00000000
    $EVENT_TRACE_SECURE_MODE		= 0x00000080
    $EVENT_TRACE_REAL_TIME_MODE		= 0x00000100
    $EVENT_TRACE_ADD_HEADER_MODE		= 0x00001000
    $EVENT_TRACE_RELOG_MODE			= 0x00010000
    $EVENT_TRACE_PRIVATE_IN_PROC		= 0x00020000
    $EVENT_TRACE_KD_FILTER_MODE		= 0x00080000
    $EVENT_TRACE_USE_PAGED_MEMORY           = 0x01000000
    $EVENT_TRACE_BLOCKING_MODE		= 0x20000000
    # Values unique to this logger
    $logger_name = 'mydriver'
    $our_guid = '{151EBE3F-2010-40A4-877D-36AD69048292}'
    $log_file = 'C:\Target\mydriver\mydriver.etl'
    # Create the persistent logger
    New-AutologgerConfig `
        -BufferSize 1024 `
        -FlushTimer 1 `
        -Guid $session_guid `
        -LocalFilePath $log_file `
        -LogFileMode $log_file_mode `
        -MaximumBuffers 64 `
        -MaximumFileSize 100 `
        -Name $logger_name `
        -Start Enabled
    # Add our WPP trace provider to the logger
    Add-EtwTraceProvider `
        -AutologgerName $logger_name `
        -Guid $our_guid `
        -Level 0xff `
        -MatchAnyKeyword 0xFFFFFFFFL
    WPP tracing depends upon the EXACT .PDB file for the code running, so hold onto your PDB file for any driver your distribute. One of the cool features of WPP tracing is that if you haven't enabled your trace provider (using the script), then there is almost no overhead so you can leave the tracing statements in your driver. If a problem comes up in the field, you can send your client the script to enable the driver's WPP provider and they can send you back the .ETL file. The .PDB file is needed to decode the .ETL file, because WPP doesn't log strings (just message codes and parameters, which makes it fast and efficient). I like TraceView++ for viewing .ETL files, but you can use TraceView (in the WDK).

    You should see your WPP trace output in WinDBG without doing anything. Optionally, in WinDBG you can load the WMITrace debugger extension (.load wmitrace) to examine (!strdump and !logdump) and modify the trace sessions.


    Azius Developer Training Windows device driver, internals, security, & forensics training and consulting. Blog at

    Saturday, August 10, 2019 2:13 AM
  • > OutputDebugString is not same as WPP or TraceLogging.
    Yeah, I tried a bunch of things friday…

    > Remember that UM drivers run in session 0, like other services.
    The issue is more, I keep finding "new" implications of that. FMOs by name don't seem to work, that surprised the heck outta me.

    > /* I used to just call DbgPrintEx[WithPrefix] from usermode.... until Microsoft broke it in Win10. With good intentions, hopefully. */
    Yeah, once I got host/target up that was the first thing I tried, since people on the Web said it worked. Unfortunately, our customer picked Win10…

    • Edited by DariaAIO Tuesday, August 13, 2019 8:05 PM Formatting
    Monday, August 12, 2019 4:54 PM
  • > Personally, I find using WPP for debug output to be easier. […]
    I had tried to slap in a random WPP implementation I found on the Web, it looked rather different… So trying yours is first up today, thanks!
    • Edited by DariaAIO Tuesday, August 13, 2019 8:06 PM Quote Clarity
    Monday, August 12, 2019 4:57 PM
  • > You should see your WPP trace output in WinDBG without doing anything.
    That seems to be working now, thanks!

    Tuesday, August 13, 2019 11:46 PM