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 | ( ) | | <o>
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