NOTE: No Model M keyboards were harmed in this project. That would be horrible. The modifications detailed herein are completely reversible.
UPGRADING THE MODEL M KEYBOARD CONTROLLER
I decided to put a Zero in my beloved Space Saving Model M keyboard: that's the sort of thing everyone does, right? It shouldn't be too hard - stuff the Zero in there (there's lots of room!), and connect it to the keyboard's controller board using this circuit. Why would I do this, you ask? Because, using the Pi Zero, I could. I'd figure out what I could use it for later!
Then I had an epiphany: the Zero has enough GPIOs to completely REPLACE the keyboard controller. I'd just need to write a kernel module to scan the keyswitch matrix. Since I'd already done about half the necessary work on a previous project, it shouldn't take too long. And as an added bonus, it would consume less than half the current of the original keyboard controller. That would make battery operation possible. And any project that uses 24 GPIOs on a Pi has got to be awesome, right? I could even spin it as "upgrading the Model M keyboard controller" instead of the rather pedestrian "Pi stuffed inside of a keyboard". Bonus!
WRITING A KERNEL MODULE
So I started by writing a kernel module. I decided to write it as a generic switch matrix handling module, in case other people might find it useful. Maybe someone will want to put a Pi inside of a Commodore 64 or something? (Comments are in the source if you want to do this.) Of course I used my favourite switch de-bounce routine. I got basic matrix scanning with n-key rollover working, and started on ghost key lock-out. Then I had a terrible thought: the Linux kernel probably already has a switch matrix module. And sure enough, a quick search turned up the matrix_keypad
kernel module.
Oh no, I thought, all that work was for naught!
I went through the matrix_keypad
module code to see how it worked. As far as I could tell, it used a simple time delay to de-bounce the switches, not a robust filtering algorithm like mine. And I couldn't find any ghost key lock-out. It gave me the feeling that it was written for small switch matrices, not a full keyboard. It did have the advantage of being interrupt-on-change driven, unlike mine which uses a regular timer tick. So the crisis was averted - my module was definitely better for a full keyboard! I added ghost key lockout to my module, double-checked the translation table, and it was done. I made a device tree overlay for it, and added a reference to it in /boot/config.txt
.
IT'S COMPLICATED
Now that the keyboard was working I really needed to figure out what I would use it for. I could use it as a USB keyboard using the USB HID gadget driver. Or a Bluetooth keyboard using a Bluetooth dongle. Or even a WiFi keyboard using a WiFi dongle, so I wouldn't have to add Bluetooth to my Pi 2. (These last two would require batteries of course.) Or even all three, with the mode determined by what was plugged into the USB port! But in the end I decided to concentrate on first making it a simple USB keyboard. Then I wouldn't have to worry about batteries.
That meant I'd need to use the g_hid (HID gadget) kernel module. I thought it'd be simple to get it working, but upon researching it, I found that it needed a data block in HID report format sent to it's device file. And those reports are completely different from how Linux reports keyboard events. The Linux event system sends key-up and key-down events, and the HID report consist of the modifier key states (in a bitmap) followed by the keycodes of up to 6 keys that are currently pressed. These reports are sent only when there is a change in key state. So I wrote a userspace program (called event2hid
) to get key events, map which keys are down, and scan this map (when there was a change) to generate and write a properly formatted HID report. Oh, and also translate the key codes since they're completely different!
On top of that, the g_hid
kernel module didn't work! It...
If you put a decent sized battery in it, you could just leave the Pi running all the time and have it charge over USB if possible. I'm sure there are flaws with that plan, but can't think of them. Any thoughts?