Hello, I like physical buttons, knobs, dials. As an extension of that, I quite enjoy messing with input devices. of course, that includes keyboards. For the longest time, I used a unicomp pc122 clone with buckling springs. But recently I traded it in for a pinnacle DEKO fast action keyboard. W a n t S o m e --------------------------- F F F F F F F A S T ------------------- A C T I O N ? -------------------- Right, so, this thing has the obiquious cherry switches, which, while inferior to the buckling springs of the unicomp, it is vastly easier to find them fancy custom keycaps for these switches. By default, the DEKO keeb comes with cherry MX black switches but they are easy enough to switch out should you want. It has (quite a lot) more buttons than the PC122 (which already has more than your standard 102 key keyboard). On top of that, the majority of those extra keys have individually controllable LEDs. AND on top of THAT, it has a built-in VFD display. This is reminicent of the old, more well-known logitech g15 keyboard, which came with a built-in LCD screen, but let's face it - vacuum fluorescence is way cooler than liquid crystals :) All of the extra LEDS and the VFD are controllable by sending PS2 commands to the keyboard (right, it's a PS2 keyboard, none of that USB nonesense. hardware interrupts, baby!) Before I dig into the technical details on this thing, I'd like to point out just how nice it is to have dedicated buttons to launch various applications, control window manager functions, etc,... Iconify (minimize), Maximize, Close, various tiling ops, etc, are just so much quicker to execute from a keystroke, especially if it's not a layered combination of keys but literally a single button press. Definitively no need for none of that rodent crap (sorrynotsorry Rob Pike). Moreover, all the extra keys come in handy for applications like blender, where it's useful to map various operations like edge and loop cuts to dedicated keys, or send cursor to origin, send cursor to selected, etc... no more navigating through menus or having to memorize combinations of key sequences. Imagine showing the position in 3d space of the selected object in blender on the vfd screen at all times, and the number of tri's. I just love this stuff. .````.---_ |o| A bunch of years ago (I think I phlogged' . `. about it at the time) at vcfmw we went to .___ \ |o| go visit the vector general that created ..... / \ |, the Death Star trench animation in the | ( ) | | Star Wars films. The Vector General was a \___/ | graphics terminal, which they had attached............../ to a PDP11. They were using this thing for . . . .` CAD and 3d modeling, all the way back in `.. . ._.` /\ 1969! It supported a light pen, and had ``````` | || | physical buttons for rotating/panning the |=-\[..]/-=| model etc,... Two things stuck with me --/'``'\-- from having actually had the chance to use this thing: 1) Our modern software is not better, but actually much worse at many things. 3d graphics is not new. Obviousy, we now have texturing, lighting, etc,... and hardware that can handle an insane amount of triangles. But the actual modeling workflow back then seemed a lot more intuitive. 2) Having physical buttons to rot/pan/tilt PHYSICAL knobs is actually REALLY handy and overall pretty damn awesome. So yes, knobs buttons, dials, sliders, they are good things to have! Physical things you can touch with a dedicated real meaning. All of this also contributes to how I loathe using mobile phones or even laptops. I like to do my computing on something that feels like an actual machine. A computer ought to be an art installation! :) Anyhow! Back to this keyboard... we hit a snag, right? How DO we send these fancy PS2 commands to the keyboard? After all, it's not like the OS just exposes the ps2 bus, does it? Right, typically, no, it doesn't. On GNU+Linux it can sort of be exposed, via the serio_raw interface, but it comes with the caveat that for as long as /dev/serio_raw exists, you can't actually type on the keyboard. You see, the Linux kernel serio driver is used for all communication to the ps2 bus, but only one device can have access to a bus at a time. So either the atkbd keyboard driver has access to it so you can type, or the serio_raw driver has access to it so you can send your commands, but not both at the same time. Nevertheless, you can sort-of-kind-of get around that problem with a little shell script: .==[ BEGIN SOURCE DUMP: vfdwrite.sh ]==============================. #!/usr/bin/env bash set -e # Helper functions ------------------------------------------------- function load_module { set +e lsmod|grep serio_raw > /dev/null set -e if [ $? == 1 ]; then modprobe serio_raw fi } function get_serio_port { grep -H atkbd /sys/bus/serio/devices/*/uevent |\ awk -F '/devices/' '{print $2}' |\ cut -d/ -f1 |\ tr -d '[a-zA-Z]' } function get_serio_raw_dev { ls -1v /dev/serio_raw* | tail -n1 } function enable_serio_raw { PORT="${1}" echo -n "serio_raw" > /sys/bus/serio/devices/serio${PORT}/drvctl } function disable_serio_raw { PORT="${1}" echo -n "atkbd" > /sys/bus/serio/devices/serio${PORT}/drvctl } function vfd_write { DEV="${1}" INPUT_STR=$2 OUTCMD="\xe4\x01" for (( i=0; i<${#INPUT_STR}; i++ )); do OUTCMD="${OUTCMD}\xe6${INPUT_STR:$i:1}" done echo -e -n "${OUTCMD}" > ${DEV} } # Entry point ------------------------------------------------------ INPUT_STR="$@" if [ "${UID}" -ne 0 ]; then echo "WARNING: you probably should be running this as root." echo "Will likely fail." fi load_module PORT="$(get_serio_port)" enable_serio_raw "${PORT}" RAW_DEV=$(get_serio_raw_dev) vfd_write "${RAW_DEV}" "${INPUT_STR}" disable_serio_raw "${PORT}" /etc/local.d/setkeys.start xmodmap ~/.xmodmap `==[ END SOURCE DUMP ]=============================================' So, what's with the last 2 lines? WELL, it turns out, when you detach the keyboard, and then reattach it, we lose the kernel keymap set, and X also loses it's keyboard map. So we have to reload them. In order for some of the 'extra' keys to register on Linux I use setkeys in /etc/local.d/setkeys.start to map them to... something. And then in turn, I map those somethings to other things in my ~/.xmodmap Here's what setkeys.start looks like: .==[ BEGIN SOURCE DUMP: setkeyus.start ]===========================. #!/usr/bin/env bash # Menu key setkeycodes 6c 127 # Super key setkeycodes 6d 125 # etc... `==[ END SOURCE DUMP ]=============================================' All of this is kinda hacky, but it was good enough for me to live with for a while. The one sad thing about this setup is that when the linux keyboard driver retakes control after unloading serio_raw it re-inits the keyboard, which in turn turns off all the LEDS, so that effectively made the LEDS uncontrollable on Linux. Thankfully the text on the VFD persists at least. So, I already knew that at some point, I was going to have to write a driver. [FF] Fast Forward to a couple of weeks ago. I've been wanting to move my main desktop machine away from GNU+Linux to FreeBSD for a while now, and I finally started the work on that. .___. | | There is a couple of reasons why I want | U | to move my main desktop to FreeBSD. The | N |= biggest reason being that I've been kind | H | of getting more and more increasingly | I | dissapointed with the GNU+Linux direction | N | and communities as a whole. Most GNU+Linux | G | conversation and development seems to have | E | been taken over by pro-corporate opensource | D | people, dead set on scorched earth when it | | comes to replacing tried and true /working/ | R | tools with shitty 'modern' implementations. | A | It started with systemd, then pulseaudio, | N | then for whatever reason, ifconfig had to | T | be replaced with 'ip', a tool with less | | usability and undeniably worse output, now | W | wayland, and fuck iptables too I guess. The | A | list just goes on and on. Instead of fixing | R | problems and tech debt in existing working | N | software, these kids' egos are just so | I | inflated that they think that THEIR version | N |= is going to fix all the problems without | G | re-implementing all the pitfalls. Whatever. |___| All of this is driven by companies like ' ' redhat who dictate the direction of a lot of this stuff. That's not to say that there isn't problems with the things they replace, but just because something has problems, that doesn't mean you have to replace it. You fix those problems over time. That's what software maintenance is. But maintenance is not exciting, I guess. Writing something new is more fun, especially if you can slap your name on it, whatever,... I'm just venting at this point, as I like to do. :) The problem is of course beyond all that. It's cultural. The BSD's in general move a bit slower, and tend to have less of a "let's just throw some shit over the wall" vibe when it comes to their releases. Usually there's actually proper documentation included with packages, etc,... and the tooling is often more coherent and in general just... nicer. Of course, there's also less people contributing, which means less hardware support, and less packages, and more hoops to jump through to try and get stuff working that you need to interact with the norms. :P But since I'm not having to worry about work crap anymore lately, I really don't have anything keeping me from switching anylonger. Moreover, a lot of people I interact with online are users of some flavour of BSD. This includes screwtape and prahou who also want to use my 3d engine, so as such it is better for a BSD to be my main OS, so I can make sure everything always compiles nicely on it. Out of all of the BSD flavours FreeBSD is probably the most popular, and the most Linux-like which is good and bad. Good for hardware support, bad because it's quicker to inherit Linux' flaws. For me it strikes a usable middle ground. I wanted to go for NetBSD first, but my video card gets no hardware acceleration on it at all, which is problematic given that I'm Moreover, the linux-emulation working on a 3d engine. will come in handy when I do have to interact with the other side. The irony that the BSD's tend to not use copyleft, yet tend to have a more hacker-friendly community is not lost on me. I'm not the first one to notice or complain about this change in culture on the Linux-side, and not the first to notice the strengthening corporate grip on the ecosystem, which, to be fair has always been there. Linus had always embraced and sought out commercial use. Not that I'm against commercial use in of itself, but I am against huge corporations hijacking communities and manipulating the conversations and abusing 'open source' for free labour. This is why I like copyleft. At least with the GPL, anything they grab and change, they have to release back into the wild. That of course is also broken now that AI copyright laundring is a thing. In any event, many other hackers have fled to the bsd's over the years, yet the irony is not lost on me that the BSD's have historically been anti-copyleft. It's quite a strange situation we find ourselves in now. And It isn't exactly great. Someone ought to write a fresh copyleft- licensed BSD perhaps. ;) Or maybe we need a second HURD, done properly this time. Or maybe all this computer stuff was just a huge mistake and I should just go learn woodworking or something, whateverrrrr... in the end, I did finally end up installing FreeBSD on this machine, aaand: The first thing I noticed was that many of the 'extra' keys on my fancy deko keyboard were not sending keycodes. Not in X, and not in the vt console. Indeed, kbdscan was giving me nothing|nada for a lot of the keys. That's a big problem, because I rely on a lot of those keys for my normal workflow. As such, I started work on a driver for the deko keyboard. It took quite a bit of reverse engineering, trying to figure out how the FreeBSD kernel handles PS2 keyboards. And it's not all pretty. (FWIW it's not all pretty in Linux either). PS2 keyboards on both freebsd and linux are handled by a driver called 'atkbd'. I guess both keyboards with an AT connector and keyboards with a PS2 connector are handled by the same thing. And the name ought to be a clue towards how old and crufty some of this code is going to be. Diving in, the first thing I see on top of the atkbd.c file is a 1999 copyright - this is going to be fun. In the Linux kernel, the atkbd driver uses the serio facility to communicate with the ps2 port. Looking over the atkbd source I saw no equivalent on FreeBSD. Instead, the atkbd driver uses the atkbdc (the at keyboard controller driver) bus to communicate over the ps2 bus. And atkbdc in turn uses the bus_space (1) functions to talk to the port. Okay. Not too bad so far. We can work with this. I can write an atkbd replacement and still hook it into atkbdc to communicate with the ps2 bus. Another thing I noticed right away was this bit of code: .==[ BEGIN SOURCE DUMP: atkbd.c ]==================================. switch (state->ks_prefix) { case 0x00: /* normal scancode */ switch(scancode) { // .... stuff case 0xE0: case 0xE1: state->ks_prefix = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ state->ks_prefix = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; // .... more stuff default: /* ignore everything else */ goto next_code; } break; `==[ END SOURCE DUMP ]=============================================' So, it turns out, all of the keys that weren't working on this keyboard were keys that send a 0xE0 prefix. The FreeBSD atkbd driver has a bunch of hard-coded cases for some keys with this prefix, but obviously not any for this funky keyboard, and anything not handled by their hard-coded cases gets... ignored. So there was just no chance of me ever seeing a keycode for these keys in userland. I copied atkbd.c into my own driver, recompiled my kernel with atkbd disabled, and made sure my custom module loads on boot. Then I added some extra case statemens for some of my special keys. Aaannd... N O P E Nothing. Didn't work. After a bunch of debugging, I found that the function returns before ever even reaching this case statement. What's going on?! Welll.... it returns here: .==[ BEGIN SOURCE DUMP: atkbd.c ]==================================. /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; `==[ END SOURCE DUMP ]=============================================' So we are in raw mode? I wrote a little userland C program that dumps the keyboard mode, and another that switches it to raw mode. If I switch the keyboard to raw mode from userland, All keys just produce garbage. If I read back what mode the keyboard is in, it is NOT in raw mode. HUH. so userland thinks the keyboard is NOT in raw mode, yet my atkbd driver clearly thinks it is. Well. It turns there's more to the puzzle of how keyboards are hanled on FreeBSD. Quite a bit more. You see, atkbd and atkbdc do not act alone. Nay! They are in turn hooked into the kbd facility and attached to a kbdmux. The kbdmux man page (2) states: "The kbdmux keyboard driver switches all slave keyboards into K_RAW mode. Thus all slave keyboards attached to the kbdmux keyboard share the same state. The kbdmux keyboard is logically equivalent to one keyboard with lots of duplicated keys." AH. Okay, so that kind of makes sense. atkbd is in raw mode, but the kbdmux is not. That explains the mismatch between what we see in the atkbd driver versus userspace. Having a quick glance into kbdmux.c I quickly noticed that it duplicates a lot of the atkbdc code, including our big switch statement with the hardcoded cases for the 0xE0 prefixed keys. That's.... a problem. Certainly I don't want to go modify both atkbd AND kbdmux. The keyboard code being spread out all over the kernel like that with hardcoded keycodes was giving me a sinking feeling that writing a driver for this keyboard was going to be very difficult if not impossible. Certainly it would be near impossible to make a clean self-contained module. That's what I thought until.... I had an idea. What if, we tell the kbdmux a little white lie. Instead of passing it the raw scancode, we can pass it a manipulated scan code, with the prefixed keys already translated to a single byte keycode and then recomposed into a scancode. That would bypass the 0xE0 case and would avoid the keys being ignored. Finally progress. I got this working, and kbdscan now finally saw unique keycodes for my 'extra' 0xE0 prefixed keys. But as soon as I fired up xorg, I was back to not having scancodes again for those keys, even if I mapped them with kbdcontrol and an appropriate keymap. What's going on? Turns out there is ANOTHER mechanism that wants to translate keycodes into scancodes: evdev. On FreeBSD evdev is the thing that feeds events to libinput. And libinput is typically the thing that xorg and wayland get their events from for keyboard, mouse, and other input devices. Looking into atkbd.c (and my new driver) we see that there's some special code that generates evdev events: .==[ BEGIN SOURCE DUMP: atkbd.c ]==================================. #ifdef EVDEV_SUPPORT // ... stuff keycode = evdev_scancode2key(&state->ks_evdev_state, scancode); if (keycode != KEY_RESERVED) { evdev_push_event(state->ks_evdev, EV_KEY, (uint16_t)keycode, scancode & 0x80 ? 0 : 1); evdev_sync(state->ks_evdev); } // ... more stuff #endif `==[ END SOURCE DUMP ]=============================================' scancode 2 key.... sigh... so, yeah. evdev has it's own key codes, distinct from the vt key codes, and I had to add more code to translate each special key to a unique evdev key code. After that, it still didn't work until I added evdev_support_key calls for every special key in the bit where it sets up the evdev events as well. But after THAT I finally got all keys to produce unique scancodes in xorg. What. A. Ride. And after all that, honestly, it was relatively trivial to write up some code to handle the VFD display and the extra leds. Thanks to the kbd facility in FreeBSD, those drivers can 'search' for the keyboard by keyboard driver name, get a handle to the keyboard state, which has a mutex we can lock to prevent polling while we send commands to the port. It's actually kind of nice. Simpler than the serio setup on Linux, yet very usable. My dekovfd driver sets up a /dev/dekovfd device which you can write to in order to write to the display, or you can cat it to read back what's on the display. The dekoleds driver sets up a /dev/dekoleds device, which has some ioctl's to set led status for all the extra leds. They can be set to on, off, or blinking. I wrote up a userland utility to control the leds, so you can control them in shell scripts, and I included some sample C programs which use the ioctl. All in all, while the keyboard handling code is a bit of a mess, I found that writing device drivers for FreeBSD is actually quite a pleasant experience compared to Linux. The bsd.kmod.mk Makefile makes setting up a build pipeline extremely easy. The module loader not needing an exact match between kernel and the module like on Linux made my life easier as it led to less complete kernel recompiling. The various driver macro's like DRIVER_MODULE and KEYBOARD_DRIVER make registering your drivers super easy. Obviously, it would be better if there were no hard-coded scancode handling in the kernel, and if that same code wasn't duplicated a bunch of times (Oh, I forgot to mention, that same atkbd code is also duplicated AGAIN in the bhyve code whaaaaaaaa), and I think a lot of that code has some cobwebs and organic growth on it, but given that inspite of that, I was still able to write a small self contained module, it isn't all that bad really. It would be nice if there were some type of syntax to handle multi-byte scancodes in the keymaps themselves, so no kernel hacking would be required. It would also be nice if there was a userland-only way to send commands to the ps2 port. But I think, in the end, having proper drivers now is 10000% nicer than the hacky shell script crap I had going on in Linux. That concludes my FreeBSD kernel driver / keyboard / rant story. :) If you would like to take a stab at FreeBSD driver development, they actually have really good offical documentation on all that! Check these out: * https://docs.freebsd.org/en/books/arch-handbook/driverbasics/ * https://wiki.freebsd.org/CDevModule I also recorded a video demonstration where I go over some of this, and demo what the keyboard looks like when using the driver. It is on SDF toobnix here: * https://toobnix.org/w/dXMkWYitfejhPsxoydpCts The source code for the drivers can be found here: * https://linkerror.com:11175/jns/dekodrv/src/branch/main/dekokbd ___________________________________________________________________ 1: https://man.freebsd.org/cgi/man.cgi?query=bus_space 2: https://man.freebsd.org/cgi/man.cgi?kbdmux