KVM Nerd's home

USB Keyboard Scancode Injector

To automate the switching of KVM switches for my RetroRacks project, I needed a solution. First I thought it would be an idea to use serial connections of the switches, but unfortunately they are only meant for upgrading their firmware, and not to control them. The KVM switches are controlled by keyboard shortcuts, so my next idea was to send automated USB keyboard strokes (“scancodes”) using an additional device which acts like a USB keyboard. Fotunately, the main KVM switch in my setup allows to connect more than one keyboard by using a USB hub connected to the USB keyboard connector, so I could connect the injector this way. The injector, once it is finished, should be integrated into the multi switch, which should also operate it.

Selecting a Device for Sending USB Scancodes

The device needed must be able to work as a USB device (“USB Gadget Mode”) and behave like a standard keyboard via the USB connection. Searching for a solution on the net, my first attention turned to the Raspberry Pi Zero, which supports the USB Gadget Mode. I also found Arduino based solutions, but they seemed to be much more complicated, and I would have had to learn a new platform. I found a Raspberry Pi Zero for less than 6 € on the net, so I decided to give this a try. There were also the “H” and “WH” versions of the Raspberry Pi Zero, but the additional features didn’t help me. Wireless technologies like WLAN and Bluetooth were of no help, because I wanted to install the device within a metal casing, which would not allow it to work.

Setting Up the Raspberry Pi Zero as a Virtual Keyboard

I used this tutorial on randomnerdtutorials.com as a basis, which itself is based on another tutorial on isticktoit.net, and show you a slightly modified and stripped down version of it for my special case.

Install Raspbian Lite

First install Raspbian Lite on your device. I used the version from February 2020 (Release date: 2020-02-13).

Accessing the Raspberry Pi Zero via Serial Console

Because we will set the Raspberry Pi Zero to USB Gadget Mode, we won’t be able to access it via USB. Also it has got no network interfaces, so we will not be able to access it via SSH. To get console access, we can connect to its serial port via GPIO using GPIO 14 and 15 (pins 8 and 10). There are two options to do this:

  • Use a USB-UART with a PL2303/CH340 or similar based chip:
    • Configure it to 3.3 Volts
    • Connect Ground/GND of UART to Ground (e. g. pin 6)
    • Connect RX of UART to GPIO 14 (pin 8)
    • Connect TX of UART to GPIO 15 (pin 10)
    • Do NOT connect 3V3 power!
    • Connnect USB port of UART to computer
    • Device should be something like /dev/ttyUSB0
  • Use a RS-232 to 3.3 Volts converter with a MAX3232 or similar based chip:
    • Connect Ground/GND of converter to Ground (e. g. pin 6)
    • Connect RX of converter to GPIO 14 (pin 8)
    • Connect TX of converter to GPIO 15 (pin 10)
    • Connect VCC of converter to 3V3 power (e. g. pin 1)
    • Connect RS-232 port of converter to computer
    • Device should be something like /dev/ttyS0

Then use a serial terminal client, e. g. PuTTY, configure it to 115200,8,N,1. On Linux, use device name as described above and connect to the Raspberry Pi Zero. Press <Enter> twice if you get no console.

Enabling Modules and Drivers

Boot up Raspbian, login as root and execute the following commands to configure the modules and drivers to be loaded at boot time:

# echo "dtoverlay=dwc2" >> /boot/config.txt
# echo "dwc2" >> /etc/modules
# echo "libcomposite" >> /etc/modules

Creating a USB Gadget Configuration Script

To configure the device as a USB Keyboard, a bootup script has to be written. We call our script usbkbscancodeinjector and place it in /usr/bin.

Then it has to be filled with the following content:

#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p usbkbscancodeinjector
cd usbkbscancodeinjector
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
mkdir -p strings/0x409
echo "fedcba9876543210" > strings/0x409/serialnumber
echo "KVM Nerd" > strings/0x409/manufacturer
echo "USB Keyboard Scancode Injector" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower

# Add functions here
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc
ln -s functions/hid.usb0 configs/c.1/
# End functions

ls /sys/class/udc > UDC

Make it executable with this command:

# chmod a+x /usr/bin/usbkbscancodeinjector

Add the script to /etc/rc.local to let it execute at each boot by adding the following line before the line which contains exit 0 (which itself should be the last line):

/usr/bin/usbkbscancodeinjector # libcomposite configuration

After the next boot it should be ready for some testing!

Testing it

Connect the USB port of the Raspberry Pi Zero to the USB port of a computer, it should detect a new USB hardware. When running Linux, run lsusb -d 1d6b:0104 and it should output an entry like the following:

Bus 007 Device 018: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget

This is our selfmade device!

Next Steps

  • Replace the script with a Python script
  • Make the Python script receive scancodes via I2C
  • Write second Python script which sends the scancodes via I2C from another Raspberry Pi
Last updated on 19 Jul 2020
Published on 23 Apr 2020
Edit on GitHub