lentinj / tp-compact-keyboard

Fn-Lock switcher for ThinkPad Compact Bluetooth Keyboard with TrackPoint
GNU General Public License v2.0
345 stars 34 forks source link

Customise USB Compact Keyboard Firmware #32

Open agoode opened 8 years ago

agoode commented 8 years ago

(I am only half joking here)

The keyboard has an upgradable firmware and a documented microcontroller: https://support.lenovo.com/us/en/documents/pd026745 http://www.sonix.com.tw/article-en-1002-3048

It would be great to have a keyboard with a working middle mouse button. A completely clean room firmware would allow this.

As a bonus, it would be great to implement proper negative inertia, which has disappeared from newer TrackPoint devices: http://blogs.epfl.ch/icenet/documents/Ykt3Eext.pdf

lentinj commented 8 years ago

Interesting. Did you try upgrading your own keyboard? Notice any different behaviour other than the listed key combination not working?

agoode commented 8 years ago

Yes, I did update it. No visible changes. I tried sending it some new parameters from Linux but it still has the annoying middle-button behaviors.

mrexodia commented 6 years ago

So I have been doing a little reverse engineering (I'm getting this keyboard tomorrow and just wanted to tinker a little bit) and these are my findings so far:

My plan is to figure out how the SN8 file format works (probably it just directly flashes certain regions to the ROM) and create disassembler cores for IDA/Binary Ninja to be able to reverse engineer the Lenovo firmware.

dal00 commented 6 years ago

@mrexodia Did you have time to do some more work on this?

mrexodia commented 6 years ago

@dal00 I did do some work on this but recently I didn’t have much time. I came in contact with @vpelletier from https://github.com/vpelletier/dissn8 and he in fact reversed most of the firmware.

At the moment I’m working on an emulator for the whole chip to do debugging on changes to the firmware without risking to brick my board. This is coming along nicely and at the moment I’m implementing USB support inside qemu.

It’s mostly a project I work on during flights though so don’t expect too much.

Also, doing a clean room firmware could be possible but is a significant effort without benefits other than having an open source firmware. My plan is to just hook in the real firmware and replace the keyboard routine with something more to my liking...

dal00 commented 6 years ago

@mrexodia Cool, thanks for the update! What kind of changes do you have in mind?

mrexodia commented 6 years ago

Hi,

Mostly I want to change mappings for Fn+something to arbitrary keycodes (F13 and certain other keys that no keyboard has for a personal project) and also allow swapping the Fn/Ctrl keys (although I got used to that by now).

Probably I could have made my changes by now but I’m just doing the emulator stuff to learn 😀

mrexodia commented 6 years ago

Here is some progress on an interactive disassembler/emulator for the platform: https://github.com/mrexodia/SN8F2288_gui

aiju commented 6 years ago

I figured out how to disable the automatic hardware "scrolling". If you start with version 3.30 of the firmware (SHA-256 7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4) and write 0x5A5A to locations 476076, 475710, 475718 and 481870 (in decimal), you should get a file with SHA-256 7fd326d15862211932ce73965c2ba2b86d87b918a54f25d7af37eef1b29d27ba.

If you use that to flash the firmware, it functions as a normal three button mouse would (obviously, one can still do software fake mouse wheel shenanigans if one wants to). I think it still intersperses some scrolling reports depending on the mode, but all the actual scrolling calculations should be disabled.

This seems to work fine with Windows (with, and presumably without, the Lenovo driver), but Linux (the lenovo_hid driver, I think) seems to ignore the middle mouse button events. I mostly intend to use the keyboard with Windows (all my Linux machines are actual Thinkpads :)), so I'm not going to debug this further.

Some obvious NB:

  1. I cannot, and will not, guarantee this won't brick your keyboard. Use at your own risk.
  2. I cannot, and will not, provide patched binaries (for reasons of copyright).
mrexodia commented 6 years ago

Can you elaborate on the process to come to this conclusion for the patch?

aiju commented 6 years ago

I used dissn8 and looked for code that writes to the USB endpoint FIFO and from there figured out which memory locations are used to hold button state, and from there which places check for middle mouse button. (I'm not really sure to what extent I can legally share specifics about this...)

Kwarf commented 5 years ago

I really want to swap the Fn and Ctrl keys. It's crazy that this isn't possible with the default firmware, since their laptops have the feature to swap. It's increcibly hard to press Ctrl+Shift with the current (ISO) layout.

Do you know if this would even be possible with just a FW mod, or if the key matrix is designed so that some combinations wouldn't register? I just want to know if there's any point in looking into this, or if i should just do the HW mod.

mrexodia commented 5 years ago

From what I looked at when disassembling the keyboard, it is just a laptop keyboard glued to a plastic body (you can remove the body without removing any screws if you want to look inside yourself). This tells me that there is no problem with the design of the key matrix.

It is possible to modify the firmware to switch the Ctrl and Fn keys, but I'm hesitant of trying because I don't want to brick the keyboard. Recently I implemented interrupts and timers is my emulator and I can now observe pins being read/written, but no more time 😄

tbjornli commented 5 years ago

Did you guys make any progress with this?

I really want to be able to swap the Fn and Ctrl keys as well.

mrexodia commented 5 years ago

This is the closest to progress I got: https://github.com/mrexodia/SN8F2288_gui

Personally I just got used to it and had to drop the project for time reasons...

vpelletier commented 5 years ago

I improved dissn8 some more, the biggest addition being the ability to generate global function call graph and per-function branch graphs (in graphviz "dot" format), closely second to per-address read/write/set/clear access tracking.

I also continued my firmware analysis a bit further, but still haven't made up my mind about releasing the dissn8 .cfg file I write firmware-specific findings to: there is no code in it but symbolic names I came up with for ram & rom addresses, and some comments. IANAL, but this does not look "clean room" enough to me. In any case, the firmware can be fully disassembled with the chip declaration file only, all the (few) jump types the CPU has are supported. The only things missing in dissn8 are ram bank selection (which this firmware doesn't use, and given how it uses B0 and non-B0 instruction variants for the same variables it looks like the compiler used does not support them itself) and possible direct program counter assignments as opposed to increments (which this firmware also doesn't use).

Just a cautionary word: firmware flashing is purely handled by the firmware itself, the chip does not on its own assist in getting the device on the USB bus. So the next step after a bad flash will very likely be to poke at whatever JTAG capabilities the chip may have (I have not looked into this myself). Given how obscure the code is in some places (I think I found a periodic event occurring every 14.5 hours... that can't be right), combined with the at places obscure documentation of the chip itself (timer source can be external or internal, internal being "fcpu or fclk" but without any indication of which it really is - and there is a factor of 2 to 8 between these two...), this means I would be very surprised if someone achieves extended firmware modifications without bricking a few.

mrexodia commented 5 years ago

I did not get very deep into it, but the risk of bricking was why I started writing an emulator. Most things appear to be working and the next step would be to actually support USB with qemu or similar...

With regards to bricking though, perhaps it is possible to always run the firmware in the “kernel” component that is responsible for handling flashing (I went through many Chinese sites looking for example source code and it looks like there is some kind of support for detecting failed flashes and then falling back). To jump to the normal operation it could be made so that it only happens if a specific hid packet is received, meaning that if you mess up nothing would persist making it less likely to brick.

You might like my gui by the way, I only did a minimal implementation and it is already very comfortable to comment/label (compatible with your cfg file format). It’s no IDA, but gets the job done better than manually reading in a text editor and is very hackable :)

Not sure if I implemented cross reference support yet, but it should also be very easy...

On Tue, 7 May 2019 at 00:48, Vincent Pelletier notifications@github.com wrote:

I improved dissn8 some more, the biggest addition being the ability to generate global function call graph and per-function branch graphs (in graphviz "dot" format), closely second to per-address read/write/set/clear access tracking.

I also continued my firmware analysis a bit further, but still haven't made up my mind about releasing the dissn8 .cfg file I write firmware-specific findings to: there is no code in it but symbolic names I came up with for ram & rom addresses, and some comments. IANAL, but this does not look "clean room" enough to me. In any case, the firmware can be fully disassembled with the chip declaration file only, all the (few) jump types the CPU has are supported. The only things missing in dissn8 are ram bank selection (which this firmware doesn't use, and given how it uses B0 and non-B0 instruction variants for the same variables it looks like the compiler used does not support them itself) and possible direct program counter assignments as opposed to increments (which this firmware also doesn't use).

Just a cautionary word: firmware flashing is purely handled by the firmware itself, the chip does not on its own assist in getting the device on the USB bus. So the next step after a bad flash will very likely be to poke at whatever JTAG capabilities the chip may have (I have not looked into this myself). Given how obscure the code is in some places (I think I found a periodic event occurring every 14.5 hours... that can't be right), combined with the at places obscure documentation of the chip itself (timer source can be external or internal, internal being "fcpu or fclk" but without any indication of which it really is - and there is a factor of 2 to 8 between these two...), this means I would be very surprised if someone achieves extended firmware modifications without bricking a few.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/lentinj/tp-compact-keyboard/issues/32#issuecomment-489815142, or mute the thread https://github.com/notifications/unsubscribe-auth/AASYFGNANNMK2LT5CWN47HDPUCYVVANCNFSM4BX7FQBA .

vpelletier commented 5 years ago

I am making enough progress on the analysis that I think I can prepare a description for a clean-room reimplementation:

The rest should be available already: CPU doc, USB HID doc. A libre assembler tool should be easy to implement. A libre C compiler should be a lot more effort, and I doubt it would be worth it (unless we have an sdcc dev in the audience maybe ? But then it will be another arch costing in maintenance for very few known device types...). So the implementation will likely have to be done in pure assembler.

To give a very general idea of the amount of effort needed, the proprietary firmware has around 6500 assembler instructions (flasher included). This chip's assembly dialect is very simple (a bit simpler than the 8051, to give a more widely known example). I believe this would require a few weeks of work, maybe a few days in collaborative sprint mode [EDIT: "days" is very optimistic, let's leave it at "weeks"].

Is anyone motivated to take this on, and non-tainted by the proprietary implementation ?

Fun discovery of the day: if you press "return" while plugging the keyboard, it boots in flash mode. It's still under firmware control, but that means that if we do not touch the early initialisation code & flashing program, we can have a quite fail-safe setup for experimenting.

vpelletier commented 5 years ago

Fun discovery of the day: if you press "return" while plugging the keyboard, it boots in flash mode. It's still under firmware control, but that means that if we do not touch the early initialisation code & flashing program, we can have a quite fail-safe setup for experimenting.

Sadly, that's not possible... Early initialisation code calls functions well past this offset, before checking key state. Also, erase pages are huge compared to how fast the code progresses in initialising everything, so custom firmware would get an already initialised state with interrupts enabled, and would have to carefuly place stub functions to satisfy early init, and tread around the memory offsets it uses...

Such frankenware seems much more dangerous than reimplementing the key polling ourselves early in the init.

vpelletier commented 5 years ago

I have most of the clean-room spec written. I still feel there is something fishy with i²c, and the chip at the other end seems famously undocumented (custom ASIC ?), so I have to check some more before they are ready.

I still would like to hear that someone is motivated to work on the implementation part of the firmware.

In addition, I would also like to hear from anyone willing to review the clearoom spec without being candidate for implementation, in order to advise on what is missing and what shouldn't be there (ex: I feel the keyboard matrix may be too much, as it can be deduced during implementation by using the keyboard - although some cells will not be found until enough layouts have been tested).

I have written a flashing tool, and have verified with actual hardware all except the dangerous commands. I ordered a second keyboard to not brick the one I use (non-local layout). I do not intend to release it until I have used it myself, so it's not in my repository yet.

vpelletier commented 5 years ago

Curiosity killed the... mouse.

Below, a cautionary tale for the curious:

<my life> I was snooping around the i²c bus, which has no probe pad. So I soldered 2 wires to the pull-up resistors on SCL and SDA. Which are surface-mount components. One successful measure later, and I was tearing down my setup.

Then a brainfart hit, and I burned myself with the soldering iron. The recoil caused SCL's pull-up to lift. No way to get solder back under it. I removed it completely, resoldered the pads, and the resistor suddenly decided it wanted to travel at high speed to somewhere.

So I discovered a new feature of the firmware: if it cannot initialise the mouse chip (via i²c), it reboots the keyboard. So it won't work as a mouseless keyboard until I get my hands on what seems to be a 3.3k 1005 and somehow succeed in my first intentional SMC solder. </my life>

The good thing is that now I'm less scared of trying my flashing tool, as boot-time flashing mode is still available (RETURN press on plug is checked before mouse init).

vpelletier commented 5 years ago

The good thing is that now I'm less scared of trying my flashing tool, as boot-time flashing mode is still available (RETURN press on plug is checked before mouse init).

Done. flashsn8 pushed to my repository. Works-for-me state. No warranty, use at your own risk. Tested on linux with python-libusb1 installed (as of Debian sid).

vpelletier commented 5 years ago

A libre assembler tool should be easy to implement.

Done, pushed to my repository.

It is very crude, error reporting is abysmal (what's a line number ?), but it's able to assemble the source produced by dissn8, and end up with the same binary image as dissn8's input and should be able to cope with some style variations.

So the libre toolchain is now bootstrapped.

vpelletier commented 5 years ago

So it won't work as a mouseless keyboard until I get my hands on what seems to be a 3.3k 1005 and somehow succeed in my first intentional SMC solder.

Success !

It was hard, and I had to wipe the board clean of burned flux twice, but I eventually succeeded. I suspect I lifted the pad along with the original resistor, and had to make a solder bridge to a nearby via.

Now that board, which sustained a few erase/program cycles with my tool (with the original firmware), is back in working order. And I now have a spare keyboard in another layout - and could confirm I could swap the key matrices between both.

I'm currently busy writing asm libraries for the chip. I've written flasher interfacing, a bit-banging I²C and am progressing on USB implementation - but I did not test much yet. I²C is a pain to check even in the original toolchain's simulator, as it does not have an accurate port emulation. I expanded the features of the assembler enough to be able to work on modular sources (ram allocation, symbol exports/imports, anonymous jump targets).

EDIT: Just pushed the - still very crude and 95% untested - asm libraries in my repository.

saveman71 commented 5 years ago

Hey Vincent!

Just wanted to chip in and say I'm following attentively your progress, as I'm writing this from my very own compact USB keyboard.

I really appreciate – and judging by the reactions on your comments, I'm not alone – that you continue to report your progress regularly, so cheers!

vpelletier commented 5 years ago

Thanks a lot for the support, it really helps keeping my motivation !

I will try to push this as much as I can, although I'm still worried that I cannot on my own implement the final firmware: I am tainted by my analysis of the proprietary implementation. Even though keyboard reading is not rocket science, copyright is copyright, and I do not want to do all this work (as fun as it may be), then push a firmware just to have it taken down by a DMCA claim. So I'm implementing from specs as much as I can when specs exist (and am fairly confident I am not reproducing idiosyncrasies of the proprietary firmware, although there are only so many ways to implement a USB SETUP packet dispatcher), but will need some external help to tie it all together with the board- & device-specific stuff.

On a related motivational note, I realized that one feature I miss from old thinkpad keyboards is the Fn+up/down/left/right key combo for stop/play|pause/previous/next multimedia keys. A custom firmware would bring these back.

Now that I have a bunch of untested ASM code around, my next goal is to test it automatically, and for this I need a scriptable emulator (ex: run until this address is reached in at most this many cycles, then flip that port bit, then run until that address is reached...). I'm not yet sure how to go: there is @mrexodia 's emulator but I am not familiar enough with C++ to add scripting to it. Or I could implement an emulator in python, but this will also take quite some time as I have to start from scratch.

mrexodia commented 5 years ago

With regards to the emulator, in the end it didn't take me very long to implement emulation of the instructions. The main issue for me is implementing the I2C/USB/timers/interrupts because I'm not at all familiar with stuff at a level this low...

I like the progress a lot though!

vpelletier commented 5 years ago

With regards to the emulator, in the end it didn't take me very long to implement emulation of the instructions.

I started implementing one and I confirm instructions is not the hardest part.

The main issue for me is implementing the I2C/USB/timers/interrupts

This is indeed the hardest part.

Ports are also harder than they seem on the surface: is the port driving the line (high/low), does it have an internal pull-up, is there an external pull-up, is the line pulled low externally... I find the SN8F2288 pin schema in the spec underwhelming:

So far I could implement pins by following my own assumptions (read samples from line, pull-up disabled on output pins), but I have not verified on the actual hardware (...because I do not have any firmware code nearly ready for a first flash). Each pin is basically a voltage divider circuit, and I compare computed voltage to logic thresholds. I'm sure it can be simplified (binary voltage & binary impedance ?) but I'm happy that this model is enough to get a bit-banging I²C implementation able to communicate.

Simulator progress:

On scriptability level, it is terse:

I also implemented (separately) board-specific IOs: key matrix, minimal I²C slave understanding the same protocol as the real one (...all constant bytes considered magic, I have no idea on their meaning). With that board I am able currently to at least reach a successful mouse init (about 200ms of run-time in the original firmware, including 50ms of flash page erase). I still have to test the key matrix. USB is not implemented at all (need in-CPU-emulator support for this). I²C slave implementation is ugly, with code duplication - I failed a lot on this part.

On to the next: USB support. Once working and I could get a few HID reports from the original firmware I will probably push the simulator.

EDIT: Woops, I pinged "yz" user when talking about the register. Sorry.

vpelletier commented 5 years ago

Once working and I could get a few HID reports from the original firmware I will probably push the simulator.

I did not go all the way to HID reports yet, but USB EP0 is working. I pushed the simulators for the CPU and the board. Next, HID reports.

On my machine (i5-5200U @ 2.20GHz) the very basic test scenario I committed (which simulates around 152ms of µC time) takes 6.7s with python2.7 and 1.9s with pypy 7.0.0 .

And a sneak peek at the output of current test scenario, which simulates USB enumeration & configuration and ends as soon as mouse init over I²C is over:

$ pypy ./ku1255_sim.py board_fw.bin
./simsn8.py:1105: UserWarning: Ignoring write to 0x00a5: 0x00
  value,
./simsn8.py:1114: UserWarning: Ignoring write to 0x00c3: 0x00
  value,
./simsn8.py:1114: UserWarning: Ignoring write to 0x00d3: 0xff
  value,
./simsn8.py:1114: UserWarning: Ignoring write to 0x00e3: 0xff
  value,
pre-address device desc: 12 01 00 02 00 00 00 08
post-address device desc: 12 01 00 02 00 00 00 08
full device desc: 12 01 00 02 00 00 00 08 ef 17 47 60 30 03 01 02 00 01
config desc head: 09 02 3b 00 02 01 00 a0
len 59
config desc: 09 02 3b 00 02 01 00 a0 32 09 04 00 00 01 03 01 01 00 09 21 00 01 00 01 22 51 00 07 05 81 03 3f 00 0a 09 04 01 00 01 03 01 02 00 09 21 00 01 00 01 22 d3 00 07 05 82 03 3f 00 0a
1
0
   103.834ms start condition
Received write address, asserting SDA
Received data byte 0xfc
   104.042ms stop condition
   150.874ms start condition
Received read address, asserting SDA
Sending 0x80
CPU ACK
Sending 0x00
CPU ACK
Sending 0x00
CPU ACK
Sending 0x00
CPU ACK
Sending 0x00
CPU NACK
   151.498ms stop condition
   152.124ms start condition
Received write address, asserting SDA
Received data byte 0xc4
vpelletier commented 5 years ago

A very short progress report this time: I can get an HID report from mouse (but I did not test very intensely either), but key matrix is full of bugs:

I think I need to step back for a while to stop running in circles.

vpelletier commented 5 years ago

Progress at last !

I could finally identify several mistakes in voltage & impedance computation for the key matrix, two mistakes in instructions emulation, and finally I can get HID reports for key events, pressing all keys one by one.

One thing worries me though: I could not find in the spec how the µC behaves when receiving an enabled interrupt while already in the interrupt handler (ex: FT0IEN is set but FGIE is cleared). It is hard for me to imagine that interrupt would just be lost, but I can't be sure, and I don't know how/when it gets retriggered. Early firmwares should avoid relying one more than one interrupt source.

mrexodia commented 5 years ago

I implemented this in my emulator iirc. From the manual there was something like

if the interrupt is enabled, and no other interrupt is in progress

This seems to be done by the GIE register.

Specifically:

Once interrupt service is executed, the GIE bit in STKP register will clear to “0” for stopping other interrupt request. On the contrast, when interrupt service exits, the GIE bit will set to “1” to accept the next interrupts’ request.

vpelletier commented 5 years ago

True. I may be looking too hard for ambguity, but "next interrupt" can mean "next trigger at the source" (and answer my question) or just "next to be handled" and the ambiguity remains. The latter could be true if the interrupts are level-triggered rather than edge-triggered.

My very limited knowledge in FPGA/HDL is insufficient to help me make a guess.

At least, I tried in vendor's emulator to set an IRQ flag with corresponding IEN and GIE set, and it did not trigger the interrupt handler. So either there is a hidden register set by actual interrupt sources which would somehow be cleared (when entering interrupt handler ? when firmware-visible IRQ flag is cleared ?) or the IRQ is really lost.

One thing which annoys me especially if the IRQ is lost is that the USB device in the µC sets a flag when receiving a CONTROL USB packet which makes the EP0 buffer immutable from USB until after firmware cleared that flag. Losing corresponding interrupt makes the device stuck. Maybe original firmware relies on another interrupt firing "soon" after, letting the USB interrupt be found (and this time losing whatever just triggered, which would be TC1). But that would seem like a big hack. Even with interrupt re-firing I'm already facing issues where some random-ish key presses take much longer than others to get reported on USB - and I do not know why yet.

Currently I'm trying to test every single instruction on original emulator, taking its behaviour as gospel and checking how mine reacts. So far it let me find that I am not incrementing PC in the correct sequence during instruction execution: it needs to be incremented before the instruction runs, and if instruction has another increment (conditionals) then that must be done separately later.

mrexodia commented 5 years ago

From when I was implementing interrupts I remember that there was a timer interrupt that was fired constantly. I guess if the USB interrupt disappears that timer would indeed pick up the USB interrupt. Not entirely sure though, I just observed a timer being hit frequently, but I didn't get far in startup because I didn't implement any voltage/usb/i2c stuff.

vpelletier commented 5 years ago

Once interrupt service is executed, the GIE bit in STKP register will clear to “0” for stopping other interrupt request. On the contrast, when interrupt service exits, the GIE bit will set to “1” to accept the next interrupts’ request.

I found another hint that your understanding is correct:

If the T1IEN = 0, the trigger event T1IRQ is still set to be "1". Moreover, the system won’t execute interrupt vector even when the T1IEN is set to be "1"

This phrasing is missing on several interrupt descriptions, and on some where it is present *IEN is replaced by *IRQ, which is probably a bad copy/paste/replace. But I believe this means "if this interrupt source was disabled when it triggered, enabling it later will not cause interrupt handler execution". Which very much looks like edge-triggered interrupts, and I guess this sentence is likely to also apply to GIEN.

So I still have a bug somewhere in my simulator, as simulation dies on second USB standard request (setAddress, but I think it's really just because it's the second, or because of a race against TC1).

vpelletier commented 5 years ago

Currently I'm trying to test every single instruction

Done. It did find a few minor bugs: stack overflow warning was off-by-one (cough), SBC was not using the carry flag properly, nibble carry was very broken, logical operations reading from memory and writing to A were taking one cycle too much.

There are remaining uncertainties about how instructions explicitly affect PCL, so it's better to avoid using it outside of B0ADD PCL, A (jump tables), and even then the table must begin and end within the same PCH value. I get the feeling of how vendor's simulator behaves, but I do not know if this perfectly mirrors what the hardware is doing.

Having an enabled timer just as a missed-interrupts garbage collector seems a good idea. Original firmware takes quite some time between getting on the USB bus and enabling a timer, which was what caused my simulation to get stuck (some USB request getting into a large timeout whenever the interrupt lost the race).

I discovered that vendor's assembler does a few extra checks on when PCL can be read and apparently when/how {,B0}BTS{0,1} can be used. The details are implemented in MacroAssembler_MCU.dll and I do not think I can have the patience of finding my way through it (disassembled x86 is a whole other beast to me, especially if it turns out to come from C++ - I'm not familiar with the language in its human-readable form, and even less in its CPU-readable form).

Next step: finding bugs in asmlib.

sanderboom commented 4 years ago

Just wanted to say that this is an amazing effort! A lot of respect for the skills to hack on this, unfortunately I don't have enough experience with this corner of tech to contribute. Most important to me would be support for swapping Fn and Ctrl. Would it also be possible to add Home and End to respectively Fn+ArrowLeft and Fn+ArrowRight? Anyhow, keep up the good stuff and thanks a lot for even making an effort :tada:

bkueppers commented 4 years ago

Just wanted to join in... I also started working on a custom firmware based on this article (https://hohlerde.org/rauch/elektronik/projekte/tpkbd-fix/, in German)... At the moment I'm trying to set up a working environment.

Swapping the FN and CTRL keys would be astonishing for the keyboard!

Any progress on this so far by anyone? Then I would not have to do it myself :D

aiju commented 4 years ago

I suspect the FN+CTRL swap can be done by hexediting the normal firmware binary (like my middle mouse button change).

bkueppers commented 4 years ago

@aiju Never thought about that, actually... How Did you figure out which values had to be replaced?

aiju commented 4 years ago

Study the disassembly to find a place to modify. (See my comment above) Then I had a script that replaced the bytes in the .exe and reran the disassembly and diffed against the original, to make sure I'm changing the bytes I intend to change. It took me some trial and error to get it working. I was worried about bricking it but I took the risk and it worked out fine. (But, of course, the risk is real; be careful)

trevor403 commented 4 years ago

Currently Fn+Arrow{Up,Down,Left,Right} does nothing. It is really helpful to be able to do Fn+LeftArrow for HomeKey and Fn+RightArrow for EndKey Is there anyway to achieve this using the asmlib firmware?

I will look through IDA tonight for this feature. Maybe it already exists as an option in the stock firmware. It would be a lot easier to enable by sending an hid command.

vpelletier commented 4 years ago

Is there anyway to achieve this using the asmlib firmware?

As the name tries to imply, dissn8 asmlib is just a library of functions for the chip. It does not implement a full keyboard firmware, but a keyboard firmware should be possible to implement from it. I have sadly burned out on this project so I did not finish the USB part of the library (which is by far the most complicated part) so even asmlib itself will need to be debugged while implementing the firmware.

What needs to be done outside of the scope of asmlib is something like:

And beyond this, some minor compatibility work/care to take:

Then the fun options can be implemented: more key combos, no flash mode access from normal firmware (IOW without the power-up time magic key pressed), restoring multimedia keys or home/end on arrow keys, ...

All this is a lot of work. I tried to document the board surrounding the sn8 chip (in the most verifiable way: the ku1255_sim.py emulator, able to run the original firmware). There are uncertainties about the accuracy of my emulation of the chip though: maybe the spec is too vague, maybe I misread it, maybe the vendor's emulator is not precise enough and instruction subcycles are not accurately represented... I suspect a few keyboards will die in the process of writing a better firmware, unless done extraordinarily carefully and defensively. Bricking is a very real risk with this chip. Once the firmware cannot jump on its own to the re-flashing entry point (flasher from on_chip_flasher.asm) or the flashing functions are damaged (they are not protected against flashing), unbricking will require desoldering the chip and figuring out the (AFAIK) undocumented pin-level bit-banging flash protocol (which should not be extremely complex, but can quickly get frustrating).

Lastly, a few recommendations to minimize bricking risk when writing a firmware for this chip:

Marisa-Chan commented 4 years ago

For people who brick their devices I do programmer on arduino https://github.com/Marisa-Chan/sn8fisp

federvieh commented 3 years ago

Thanks to the amazing work of @vpelletier and @mrexodia I managed to swap Fn and Left Control in the firmware. Here's the TLDR:

More detailed description:

ovelny commented 3 years ago

@federvieh Can confirm your instructions worked for the azerty version of the keyboard too. I've been looking for a way to swap Fn <-> Left ctrl since months now, thanks a lot for your hard work and those of @vpelletier and @mrexodia !

saveman71 commented 3 years ago

THANK YOU! Same as @ovelny I confirm it works on my AZERTY keyboard, I can at last rest my numb left little finger that has done so many weird acrobatics just to be able to do "Ctrl+Shift" for 3 years now. Can I send you guys some beer money somehow?

EDIT: in case you were wondering why it was more of a problem for an AZERTY layout:

lenovo-nonsense

With the QWERTY layout the shift bar is wider so it's less of a problem, and I'm just noticing this now :sweat_smile:

I'm sure the people who made that decision had never seen/used the AZERTY layout...

tbjornli commented 3 years ago

Would these instructions also work for the ThinkPad Compact Bluetooth Keyboard as well you think?

vpelletier commented 3 years ago

@tbjornli : AFAIK the bluetooth variant has a completely different controller (ARM ?), so it will require a separate disassembly and analysis. The advantage is that the tools for this should already exist, and will be of higher quality than what I wrote.

About key layouts, the firmware is indeed layout-independent, the layout is purely implemented by the physical key matrix. The firmware has all possible key events in its key lookup table, some entries being unreachable depending on used layout.

sanderboom commented 3 years ago

Truly impressive work!

I just received the ThinkPad TrackPoint Keyboard II. Maybe people here like to know:

The obvious question: would the instructions by @federvieh work for this particular model as well?