I spent the last few nights hacking on the keyboard firmware, and posted my branch here:
The changes are pretty extensive, because I split the single “Keyboard” source file into a half dozen modules so I could understand them better. (I know this will be annoying to the authors. I’m sorry.) Then I added a bunch of features to the display and the hidraw interface (/dev/hidraw2) which added about 2KB to the binary image and 400 bytes to the RAM usage (now 53% and 52%, respectively).
Here’s a list of the enhancements I made:
switched out the 6x8 font for an Apple-II-inspired font (128 ASCII characters only)
you can switch back to the original with a #define at the top of display.c
added a larger 8x16 font (bizcat) that can be selected by the hidraw interface
compressed the MNT reform logo, and pulled it out of the font, but kept it animated (because that looks cool)
new hidraw commands:
UXPW: display the battery status as if the user pressed circle-B
UXST: display the keyboard status as if the user pressed circle-S
WCLR: clear the OLED display
FONT0: select the default 6x8 font (21x4 characters)
FONT1: select the bizcat 8x16 font (16x2 characters)
WRIT (text): write to the OLED display as if it were a scrolling terminal, with extra escape codes listed below
WBIT (u16 LE offset) (bytes): write a bitmap directly into the 512-byte display memory, starting at offset
WRLE (u16 LE offset) (bytes): write RLE-encoded data into the 512-byte display memory, starting at offset
special escape codes in WRIT to make it easier to write a daemon that displays battery status, using byte 255 (0xff) as an escape:
I would love if some or all of these enhancements were pulled into the primary MNT driver, but I understand that I may have just moved the code around too much. Hopefully others will find this useful though.
@robey Great work! I was able to build and flash the firmware by following these steps:
Clone your forked repo
Install the dependencies you outlined in your previous post, ie: apt install gcc-avr avr-libc dfu-programmer
In the repository’s reform2-keyboard-fw directory, run make
Remove top keyboard cover plate
Switch program switch to ON (as specified in Reform manual here
Push the reset button (keyboard turns off)
Plug in external keybord and execute ./flash.sh as root
All of that seemed to do the trick. I am able to see the “new” animation, as well as run some of the example commands.
One issue I’m having – and this comes down to my lack of knowledge in these matters – is that I do not have permission to write to /dev/hidraw2 as my regular user. I have to run the example commands as root in order to get them to work. Is there some way to change the permissions for this?
Excellent that you got that far. Flashing the keyboard firmware was a bit scary for me.
I just noticed that the name of the “hidraw” file seems to depend on whether you booted with a USB keyboard plugged in. Now that I booted without it (just using the builtin keyboard), it’s back to /dev/hidraw0.
Opening all read access for all hidraw devices seems like it might be a security risk. For example, does this allow any process on the system to snoop on all keys typed (including login passwords), including on external USB keyboards?
I’d suggest:
adding ATTR{idVendor}=="xxxx" ATTR{idProduct}=="yyyy" clauses to limit it to just the Reform’s built-in keyboard (sorry I don’t know the vendor/product codes, still waiting for my Reform),
changing to MODE="0660", and
creating and using a group specifically for this purpose (in case plugdev is used by other things).
@devkev Good thinking. I tried several variations of this at first but couldn’t get any of them to work. I assume it’s because I was pulling out the wrong information.
Here is what it reported from lsusb on my system:
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0451:8140 Texas Instruments, Inc. TUSB8041 4-Port Hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 03eb:2041 Atmel Corp. LUFA Mouse Demo Application
Bus 001 Device 003: ID 03eb:2042 Atmel Corp. LUFA Keyboard Demo Application
Bus 001 Device 002: ID 0451:8142 Texas Instruments, Inc. TUSB8041 4-Port Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
I assumed it was the third to last, but got conflicting information onlne about what counts as the vendor and what counts as the id.
Based on when I’ve done similar rules in the past for USB devices, the first four hex bytes (ie. 03eb) are definitely the vendor, and the latter ones (ie. 2042) are the product.
But udev rules can be tricky to get right, and I’ve never done any for hidraw devices. The main thing is that the listed attributes have to match the actual hidraw device node itself, or a single ancestor node in the udev tree. I would expect the KERNEL and SUBSYSTEM clauses to match the hidraw device, and the ATTR{idVendor/idProduct} to match the node of the USB device itself. But maybe that doesn’t work for some reason (eg. some separation in the kernel between the hidraw device, and the usb device). That said, I can see rules in /lib/udev/rules.d/* on my current system which use all 4 of these clauses.
The usual way to debug these things is to run something like sudo udevadm info -a -n hidraw0, and study the output carefully to try to figure out why it isn’t matching.
You might also want to add something like SYMLINK+="hidraw-internal-keyboard", so that scripts have a stable name to use (regardless of whether the actual device is hidraw0, hidraw, …).
I made a few more changes/“enhancements” which I’ve pushed to my mirror. The main thing is I hooked up the keyboard backlight PWM to an interrupt and use that to count 32KHz “ticks”. This let me fix the low-battery indicator to flash at a lower frequency that’s independent of how fast the keyboard scan is.
With some help from Patrick Georgi on the fediverse, I got the keyboard scan itself down to about 350 usec, which means we can scan for keypresses once every millisecond, and allow the CPU to idle between scans, which may save an extremely tiny amount of power. (However, the ATmega32U4 is using so little power, I doubt it matters. It just made me feel better and less wasteful.)
I also added a README that documents all the stuff I described at the top of this thread.
Assuming this has very little chance of being merged into the primary git repo, would it make sense for me to re-fork this into just the keyboard firmware? The reform repo contains a lot of other code that I’m not modifying.
I’m also unsure on how to merge into the stock firmware as I’ve got my version to enter power-down state when the computer is shut off (by the menu) or a selection in the menu. Helping to greatly reduce the draw when the system is off.
Currently waiting on mntmn to have some free time as well for the corresponding LPC half as I admit that’s a bit over my head. But combined these should greatly reduce the battery drain in the off state so overdischarge shouldn’t be as much of a problem
I did try creating an account on source.mnt.re though that’s still not approved so not sure the “official way” to submit pull requests and such. I’ll likely throw mine on github. Would be nice to combine all these updates together. And maybe make some customized ones, or at least get some of the core features into the stock firmware.
Another simple patch I did that you may want to consider to yours as well is adding a bool flag for when the battery status is being displayed on the oled and every second or so recalling that routine to update and draw it again.
I tried out the power-off feature (after merging) and it works great – very cool idea. (I think it might be using a 2 second WDT instead of 1 second, though, unless I misread the spec.)
Oops you’re right, not sure why I set WDT0. That’d explain why it was feeling longer than 1s. I was thinking it was just since the WDT timer is very imprecise. Yeah it’s set to 2s right now. I’ll fixed that. Though even 2s doesn’t seem too long to have to hold it.