Going to the driver level

A project log for Cheap Windows Jog/keyboard controller for CNCs

Cheap <$5 usb numpad to use for key macros and CNC job controllers. Primarily I'm using it to extend a remote for jogging around my G0704

charliexcharliex 04/23/2015 at 18:050 Comments


So we looked at the basic ways with hooks and RAWINPUT.

Windows has filter drivers, these basically sit on top of the existing drivers and can modify what the base driver does, simply a filter.

MSDN Filter Drivers

Software Needed

We will be looking at upper device level filters. As I mentioned previously MSDN is a great resource, what we're looking for now is the DDK or as its known now the WDK, Windows Driver Kit, this needs to be installed alongside Visual Studio 2013, grab it here.

WDK Downloads

The Windows 8 WDK works for Windows 7, which is what i'm using.

Debugger Notes

Grab all that, set it up. Note the remote debugging client, these are very useful for drive development, since we're at level where you're expected to know whats going on and be able to debug system exceptions, so a second machine or a VM is very useful. VMWare for instance has a Visual Studio plugin to help remote debugging inside a guest OS, great for low level work.

I also use a USB kernel debug cable for a second physical box, where i can't use or a VM isn't good enough.

Setting up a Kernel Debugger

You can use serial, firewire or USB for kernel debugging, but we likely won't need it for this.

Back to MSDN

Microsoft supplies code examples for almost every windows substem, and this no exception. We're looking at filter drivers, and there are ones for a PS/2 keyboard (kbdfilt) and a HID Mouse (firefly)

kbdfilt This is an upper device filter driver sample for PS/2 keyboard. This driver layers in between the KbdClass driver and i8042prt driver and hooks the callback routine that moves keyboard inputs from the port driver to class driver. In its current state, it only hooks into the keyboard packet report chain, the keyboard initialization function, and the keyboard ISR, but does not do any processing of the data that it sees. (The hooking of the initialization function and ISR is only available in the i8042prt stack.) With additions to this current filter-only code base, the filter could conceivably add, remove, or modify input as needed.

firefly Firefly is a KMDF-based filter driver for a HID device. Along with illustrating how to write a filter driver, this sample shows how to use remote I/O target interfaces to open a HID collection in kernel-mode and send IOCTL requests to set and get feature reports, as well as how an application can use WMI interfaces to send commands to a filter driver.

KMDF is Kernel Mode Driver Framework , WMI is Windows Management Instrumentation.

X64 Driver Signing and test sign mode

The WDK samples are setup to self sign, but X64 windows won't like it unless you boot in TESTSIGNING mode, which means adding a flag to the loader. will test sign as well, and switch on testsign.

The reason i posted this entry before it was finished, is because even though i have my own cert, i forgot to sign it before installing it on this PC , and promptly killed my keyboard off, since it required a reboot to install it. Should be using a VM. You can run the virtual keyboard, which lets you get keypresses again if you do dev on a single box.

Setting up VMWare Player for driver debugging

After installing WDK into VS2013, setup a VMware Player with a wIn 7 x64, set a username/password and then under the driver/test menu in VS configure a new machine. Tell it the name of the VM ( make sure sharing is on in windows, firewall on too ) copy over and install the remote test sign tools, run those then run the auto config in VS

C:\Program Files (x86)\Windows Kits\8.1\Remote\ they're in this folder by default

Windows 8.x is really needed to use the network debugging features in VS2013.

VMWare by default doesn't pass in HID class devices, so to attach a keyboard or mouse directly to the VM , you'd need to add these to the VMX file

usb.generic.allowHID = "TRUE"
usb.generic.allowLastHID = "TRUE"

Then reboot or have the VM powered off. Now you'll be able to attach any HID devices directly to the VM , just don't attach your primary keyboard/mouse ;)

The kbdfiltr gets added to the UpperFilters registry key. Note that this adds the filter to ALL keyboard class devices, you can also add it to one specific device.



kbfilttr has to be before kbdclass. Just make sure its available and working before rebooting, otherwise the HID/keyboard system will fail to load. Though you can still use the old trick of loading the virtual keyboard.

Easiest way to add is use .reg file with

Windows Registry Editor Version 5.00

"DisplayName"="WDK Keyboard Filter Example"
"Group"="Keyboard Port"




then put the kbfiltr.sys in system32\drivers\

reboot the VM and if the keyboard is still working, run dbgview from sysinternals and press the numlock or caps lock a few times and you should see debug messages.

if not, check Test Mode appears in the bottom right of the desktop, and then the windows system log for issues with the driver.

Log of num lock press and disconnect, reconnect of my numpad. I added some more debug logs to my kbdfiltr source code

Entered: KbFilter_EvtIoInternalDeviceControl
Enter: FilterEvtDeviceAdd 
Entered: KbFiltr_CreateRawPdo
Entered: KbFilter_EvtIoInternalDeviceControl
Entered: KbFilter_EvtIoInternalDeviceControl
Entered: KbFilter_EvtIoInternalDeviceControl
Entered: KbFilter_EvtIoInternalDeviceControl
Entered: KbFilter_EvtIoInternalDeviceControl
Entered: KbFilter_EvtIoInternalDeviceControl

I made a desktop shortcut in the VM to the drivers folder, then i could just drag it across, you can also deploy from VS. Currently i just reboot it each change but we ought to be able to start and stop the driver to force a reload.

Ok so now if you're upto where I am, we're ready to do key substitution, lets do the Q to Z again.

open up kbflitr.c and look for KbFilter_ServiceCallback function

to make it easier since this is scan codes, add this DebugPrint to after this line

   devExt = FilterGetData ( hDevice );

DebugPrint ( ( "KbFilter_ServiceCallback 0x%x \n", InputDataStart->MakeCode  ) );
now we'll see each scan code up/down/repeat as the make code.

Scan Code List

add this after the DebugPrint, we won't get too fancy to start off with.

    switch ( InputDataStart->MakeCode ) {

        case 0x010:// 'Q'
            InputDataStart->MakeCode = 0x2C; // 'Z'
build it and if no errors, deploy it to the VM drivers folder and reboot it. ?(note to self document driver stop/start...)

Now open notepad and dbgview, and press the Q key. You should get something like this

I pressed Q 4 times, then W 4 times, then Z 4 times.

So there we go, keyboard filter, intercepting keys at the kernel level and replacing them.

All i have to do now is map the scan codes for my numpad and only install the filter driver on that device, and we're done!

Make codes

        Numpad make codes in hex

         789  - 47 48 49
         456  - 4b 4c 4d
         124  - 4f 50 51
         00.  - 52 52 53 (middle is marked as 000)
         /*-  - 35 37 4a
         +    - 4e
         BKS  - e
         ENT  - 1c

I'll map those to my CNC's keys and add modifiers, remember since we're dealing with scan codes here, ctrl shift alt etc don't mean anything yet. they're just scan codes same as (almost) any other key.