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