Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customise USB Compact Keyboard Firmware #32

Open
agoode opened this issue Jan 6, 2016 · 161 comments
Open

Customise USB Compact Keyboard Firmware #32

agoode opened this issue Jan 6, 2016 · 161 comments

Comments

@agoode
Copy link

agoode commented Jan 6, 2016

(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
Copy link
Owner

lentinj commented Jan 6, 2016

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

@agoode
Copy link
Author

agoode commented Jan 6, 2016

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
Copy link

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:

  • Lenovo's firmware upgrade tool tp_compact_usb_kb_with_trackpoint_fw.exe file is a modified version of Sonix's SONiX_USB_MCU_ISP_Tool_V2.3.0.6.exe (BinDiff shows a big percentage of exactly matching functions), you can see it in a resource editor and when comparing side by side:
    resource fun
  • Inside Lenovo's tool is a BINARY resource which is an executable originally named LenovoTest.exe (Sonix's tool has no such thing)
  • This executable does absolutely nothing, but appended to the end is a 24892 bytes file that appears to be an SN8 file (albeit encypted with a simple XOR key 0x5A)
  • When using Sonix's SN8 C Studio with a template for the SN8F2288 chip combined with the C2AsmDemo source code you can get it to produce an SN8 file of similar size with similar contents (most of the header appears to match).
  • The function that decodes (part of) the SN8 file is located at 0x40E080 in Lenovo's tool for people that might be interested in looking further.
  • A disassembler is available for the SN8F2288 instruction set called dissn8.

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
Copy link

dal00 commented May 10, 2018

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

@mrexodia
Copy link

@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
Copy link

dal00 commented May 11, 2018

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

@mrexodia
Copy link

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
Copy link

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

@aiju
Copy link

aiju commented Jun 21, 2018

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
Copy link

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

@aiju
Copy link

aiju commented Jun 21, 2018

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
Copy link

Kwarf commented Aug 24, 2018

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
Copy link

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
Copy link

tbjornli commented May 3, 2019

Did you guys make any progress with this?

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

@mrexodia
Copy link

mrexodia commented May 3, 2019

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
Copy link

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
Copy link

mrexodia commented May 6, 2019 via email

@vpelletier
Copy link

vpelletier commented May 8, 2019

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

  • general microcontroler settings (clock source frequency, clock ratios, ...) as these cannot be modified by reflashing and need to be known to respect timing constraints
  • port settings & what is connected on which (4 pins are still resisting my attempts at the moment)
  • keyboard matrix (scancodes for each cell)
  • structure of i²c messages exchanged with the mouse controler
  • flash general layout (where is built-in flasher, where is persistent storage and what is stored)
  • flashing protocol (I may be able to provide one implemented in python)
    (maybe a bit more ?)

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
Copy link

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
Copy link

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
Copy link

vpelletier commented May 12, 2019

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
Copy link

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
Copy link

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
Copy link

vpelletier commented May 22, 2019

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
Copy link

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
Copy link

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
Copy link

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
Copy link

vpelletier commented May 26, 2019

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:

  • internal pull-up is not described as controlled by pin direction. This would make sense if all pins were open-drain (so the chip could either be driving high or pulling low through the pull-up), but only 4 pins are documented as having an extra open-drain configuration. Having totem-pole outputs and pull-ups makes no sense to me.
  • it is not specified whether reading a port bit reads the drive bit from register or actually samples pin state: on an open-drain the pin could be set to let the line float (1) but reading from that same pin could produce a 0 if anything else is pulling the line low.

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:

  • instructions work (I'm sure there are a few bugs left - like interrupts firing while a certain instruction is running). I have implemented them at instruction granularity level (and not cpu cycle), as I do not know what each cycle does in multi-cycle instructions and it is easier to implement interrupts this way.
  • non-memory registers are implemented (ex: writing to PECMD triggers the right action, writing to WATCHDOG resets the watchdog counter, ...)
  • @YZ implemented, raises an exception if YZ points at @YZ (I suspect that on original chip this would be either a constant value or a "hidden" normal ram cell). I also assume pointing YZ to a register and accessing @YZ does the same as accessing that register.
  • call stack is implemented, but I do not know which address the µC pushes (return address or call address). I assume return address, as it makes interrupts easier to implement.
  • interrupts & reset are implemented
  • clocks are implemented (Fhosc & Flosc), with all power modes supported (but untested)
  • watchdog is implemented (12-bits counter on Flosc)
  • timers are implemented and work, but no buzzer/external counter support for TCx.
  • dummy ADC, MSP, UART, PWM, SIO placeholders. I realized that SIO is really I²C without the name. It is even on the same port as original firmware uses for bit-banging implementation, just not on the same pins. No idea why they didn't use it.
  • incomplete USB implementation (no way for script to do anything with the bus yet)
  • flash erase & program works (only tested on the config save page managed by original firmware)
  • ports are implemented, without interrupt support

On scriptability level, it is terse:

  • one-instruction step function
  • write & read watchers
  • current cycle number
  • current run time
  • overhead is huge (around 1 second to simulate 10ms of cpu time). Improving this is very low on the priority list (feature completeness comes before, as does correctness on details). I will try pypy before trying to optimize the code.

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
Copy link

vpelletier commented May 27, 2019

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
Copy link

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 can get some keycodes but not all, and it seems to depend on what key presses happened before, or what delay between presses. As each keypress needs thousands of instructions to run, I did not debug step-by-step so far. I suspect a bug in Port class and/or related impedance-retrieval functions.
  • USB endpoints are behaving differently from the real keyboard: on real keyboard transfers are only ACK'ed by device when there is something to send: either a key press, or a key release. In my test code, there seems to be a pump-priming effect, where the first key press causes a report to be sent but after that they never stop. Which means reports and keypresses get out of sync, and I can't be sure what key state the report corresponds to. Adding super-long sleeps (to let µC run and hopefully stabilise) does not seem to help.

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

@stngiam
Copy link

stngiam commented Nov 27, 2022

I was referring to the issue mentioned by @trevor403, @sanderboom and @federvieh where Fn-[Up/Down/Left/Right]Arrow doesn't do anything after doing the remapping of keys. #32 (comment)
My solution was to map Fn to RightControl instead of mapping it to LeftControl. That way, pressing Fn-Up or Fn-Shift-Up gets interpreted as Ctrl-Up or Ctrl-Shift-Up. LeftCtrl remains Control so there are now two control keys on the left hand side of the keyboard.

Works for me but your preferences may differ.

@alem0n
Copy link

alem0n commented Nov 28, 2022

This is what I have concluded based on #issuecomment-687659110, #issuecomment-750739628, #issuecomment-1273517862 hope it is useful.
The data block 0xF5 at 0x740BA represents the Fn key
The data block 0xBA at 0x74004 represents the left Ctrl key
The data block 0xBE at 0x7400C represents the right Ctrl key
The data block 0xBC at 0x7409E represents the Alt key on the right

@kocane
Copy link

kocane commented Dec 10, 2022

@elnahir did you get any further on this? I've bought a couple of SK-8855 well knowing that the ctrl and fn wasn't swapable but I'd love to do it, still.

@MichalLebeda
Copy link

Received II wireless version today... you know how I feel. Which model number is possible to remap (don't care about it being wired). Does KU-1255 have problems with the middle button?

@nahuel
Copy link

nahuel commented Mar 4, 2023

Another possible path is flash the kbd with a QMK / ZMK / BlueMicro_BLE firmware, so you not only get the ctrl/fn swap, but also all the programable features of these firmwares. Is better than reverse engineering the Lenovo code. Check https://github.com/SonixQMK

@KlosiakMK
Copy link

Another possible path is flash the kbd with a QMK / ZMK / BlueMicro_BLE firmware

Have you tried to flash your Lenovo Thinkpad Trackpoint Keyboard II with any firmware from the site you mentioned? I was tying to find there anything which would fit to this keyboard but with no luck.

@nahuel
Copy link

nahuel commented Mar 5, 2023

@KlosiakMK I didn't tried because currently I don't have a Thinkpad USB Compact keyboard. Looking better, the SN8F2288 doesn't seems to be supported by SonixQMK, only chips of the SN32* family :( https://sonixqmk.github.io//SonixDocs/#status

But the Thinkpad Trackpoint Keyboard II is based on another chip who seems to be well supported by BlueMicro_BLE firmware, lets continue the talk for that keyboard here: #53

@savagegreed
Copy link

savagegreed commented Mar 20, 2023

@leekyoung010

To find the address of the "Left Windows" key, i locate the value "B9" in the ROM at the Keyboard Maxtrix section, and found its address to be 0x740B0
I think other keys can be done in a similar fashion, so im leaving this for others to try.

Replacing default keys
from
1<Fn, Left Ctrl, Left Windows>
2<Home,End>
3<PgUp,PgDown>
to
1<Left Ctrl, Left Windows, Fn>
2<PgUp,PgDown>
3<Home,End>

Ref https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
Universal Serial Bus(USB) HID Usage Tables V1.12 Page 59

UsageName -------Hex ------- Xored with 0x5A
LeftCtrl--------------E0--------------BA
FN-------------------AF--------------F5
GUI key--------------E3--------------B9
PgUp-----------------4B--------------11
PgDown--------------4E--------------14
Home----------------4A--------------10
End-------------------4D--------------17

Key--------------Address--------------Value Before--------------Value After
Ctrl--------------0x74004--------------0xBA------------------------0xB9
Fn--------------0x740BA--------------0xF5--------------------------0xBA
GUI--------------0x740B0--------------0xB9-------------------------0xF5
pup--------------0x740CC--------------0x11-------------------------0x10
pdown--------------0x740CE--------------0x14-------------------------0x17
home--------------0x74084--------------0x10-------------------------0x11
end----------------0x740B8--------------0x17-------------------------0x14

@zhanghongcq
Copy link

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

* Download the firmware update tool for FW ver. 3.30 from Lenovo's homepage (sha256sum of `tp_compact_usb_kb_with_trackpoint_fw.exe` is `7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4`)

* Open the file in the hex editor of your choice

* Change position 0x74004 (475140 decimal) from 0xba to 0xf5 (this changes the function of the "LeftControl" key to "Fn"

* Change position 0x740BA (475322 decimal) from 0xf5 to 0xba (this changes the function of the "Fn" key to "LeftControl"

* The sha256sum of the modified file is `123143092dab578550c87a62526b07a6c5f06c047f2455be87971aa51577e300`

* Use this file to update your keyboard's firmware

* It worked for me, but I will not guarantee that it won't brick your keyboard. Use at your own risk!

More detailed description:

* I used the info in this issue and https://github.com/vpelletier/dissn8 to get a human readable assembler file of the firmware

* After a lot of to and fro I found out that there is a table in the ROM at word 0x05a0 that translates the keyboard matrix to Usage IDs as specified in "Universal Serial Bus HID Usage Tables", rev. 1.12 (October 28, 2004), table 12 from the USB Implementer's forum.

* The actual translation is done by the function at word 0x0186 (label `func_0186` when using the aforementioned disassembler.  Handling of the "Fn" key is at label `func_0186_015f` and following.

* But for swapping "Fn" and "LeftControl" we need to modify the translation table at word 0x05a0:
  
  * The "Fn" key is encoded as 0x00af at word 0x0615, i.e. byte 0xc2a (0x740BA in the .exe), since it is XORed with 0x5a the value in the .exe is 0xf5. To make the "Fn" key work as "LeftControl", it must be changed to 0xe0 ^ 0x5a = 0xba.
  * The "LeftControl" key is encoded as 0x00e0 at word 0x05ba, i.e. 0xb74 bytes (0x74004 in the .exe), since it is XORed with 0x5a the value in the .exe is 0xba. To make the "LeftControl" key work as "Fn", it must be changed to 0xaf ^ 0x5a = 0xf5.

Can anyone lend a hand, please? I've successfully followed @federvieh's instructions to modify the firmware file hoping to swap Fn and Ctrl on my SK-8855 keyboard. However, when I try to run the modified .exe, I get an "Error: Can't Find USB HID Device!!" message.

Has anyone stumbled across this issue or does anyone know how to get the file to execute successfully? Thanks in advance.

image

It need modify the PID & VID at the position from 794b0H to 9747H by the your actual PID&VID, Attention: Need XOR key 0x5A.
image

@kocane
Copy link

kocane commented May 22, 2023

@zhanghongcq thanks for the feedback on this issue, I'm also one of the guys who'd love to swap the FN/CTRL on a SK-8855 although I'm by no means good at modifying firmware/drivers like this. Have you gotten it to work on a SK-8855 yourself?

@zhanghongcq
Copy link

zhanghongcq commented May 24, 2023 via email

@kocane
Copy link

kocane commented May 24, 2023

@zhanghongcq ah I must've misunderstood. The trouble is that there is no firmware released for the SK-8855.

So I suppose it's hopeless.

@slv087
Copy link

slv087 commented Dec 3, 2023

hey hey,
@zhanghongcq can you please elaborate this part ?
#32 (comment)

My PID is 60EE and VID is 17EF, and I can not fully understand how to translate it the positions you mention.
image

Thank you!

@zhanghongcq
Copy link

zhanghongcq commented Dec 4, 2023 via email

@slv087
Copy link

slv087 commented Dec 4, 2023 via email

@zhanghongcq
Copy link

zhanghongcq commented Dec 5, 2023 via email

@slv087
Copy link

slv087 commented Dec 6, 2023

My model is KC-1957. Seems to the newest one. Completely wireless keyboard - 1x bluetooth connection and 1x 2.4 ghz with dongle. Usb-C wire is only for charging.
Of course there is no firmware for it.

Thank you @zhanghongcq for trying to help out.

@digitalsignalperson
Copy link

digitalsignalperson commented Dec 19, 2023

Would it also be possible to add Home and End to respectively Fn+ArrowLeft and Fn+ArrowRight?

this is a super handy & super common shortcut among developers. I cannot belive how this thinkpad keyboard dosen't support it. My disappointment is immeasurable, and my day is ruined.

If someone could implement/reverse-engineer that, a lot of us would be beyond grateful. 🙏

Wishlist:

  • Fn+ArrowLeft = Home
  • Fn+ArrowRight = End
  • Fn+ArrowUp = Page Up
  • Fn+ArrowDown = Page Down

Even though we have PgUp/PgDn right next to the arrow keys, it's muscle memory to use Fn+arrow keys with it being pretty standard on other keyboards. And then those dedicated PgUp/PgDn keys could be remapped to something else.

It sounds like some other thinkpad keyboards do behave like this. I wonder if there are similarities between firmwares that could be cross-referenced.

But dang no firmware for the Trackpoint Keyboard II eh...

The Fn key is also purely on the keyboard side? I see no event with libinput debug-events or evtest

@hydroEng
Copy link

I thought I would get used to the original fn / ctrl positioning but my left finger was hating me. Thanks so much for this thread on the easy fix.

On a related note, does anyone know if there are physical keycaps available that I can replace the fn / ctrl keys with to match the modification? (I guess I can use stickers if it comes down to it)

@runoono
Copy link

runoono commented Mar 13, 2024

I didnt see it here so I wanna also put this here,
if you follow both #32 (comment) and #32 (comment)

you start with the firmware sha256 of 7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4 and you end up with 58eb6f62dadcf34f14da0238cd2d691a41070c18eb179a03a3da0005ac9fa1d9

also I wanna thank everyone in this thread, really made this keyboard perfect for me, so perfect that I'm gonna end up buying another one

@alexpdp7
Copy link

alexpdp7 commented Apr 2, 2024

The hexedit firmware update worked for me with:

Bus 001 Device 004: ID 17ef:6047 Lenovo ThinkPad Compact Keyboard with TrackPoint

Just a quick question to confirm.

I'm noticing that C-x C-s (hold ctrl, then press x, then press s) does not register correctly in Emacs if I do it "fast". Is this another instance of the ghosting issue above?

@GalaxyUay
Copy link

GalaxyUay commented May 11, 2024

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

* Download the firmware update tool for FW ver. 3.30 from Lenovo's homepage (sha256sum of `tp_compact_usb_kb_with_trackpoint_fw.exe` is `7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4`)

* Open the file in the hex editor of your choice

* Change position 0x74004 (475140 decimal) from 0xba to 0xf5 (this changes the function of the "LeftControl" key to "Fn"

* Change position 0x740BA (475322 decimal) from 0xf5 to 0xba (this changes the function of the "Fn" key to "LeftControl"

* The sha256sum of the modified file is `123143092dab578550c87a62526b07a6c5f06c047f2455be87971aa51577e300`

* Use this file to update your keyboard's firmware

* It worked for me, but I will not guarantee that it won't brick your keyboard. Use at your own risk!

More detailed description:

* I used the info in this issue and https://github.com/vpelletier/dissn8 to get a human readable assembler file of the firmware

* After a lot of to and fro I found out that there is a table in the ROM at word 0x05a0 that translates the keyboard matrix to Usage IDs as specified in "Universal Serial Bus HID Usage Tables", rev. 1.12 (October 28, 2004), table 12 from the USB Implementer's forum.

* The actual translation is done by the function at word 0x0186 (label `func_0186` when using the aforementioned disassembler.  Handling of the "Fn" key is at label `func_0186_015f` and following.

* But for swapping "Fn" and "LeftControl" we need to modify the translation table at word 0x05a0:
  
  * The "Fn" key is encoded as 0x00af at word 0x0615, i.e. byte 0xc2a (0x740BA in the .exe), since it is XORed with 0x5a the value in the .exe is 0xf5. To make the "Fn" key work as "LeftControl", it must be changed to 0xe0 ^ 0x5a = 0xba.
  * The "LeftControl" key is encoded as 0x00e0 at word 0x05ba, i.e. 0xb74 bytes (0x74004 in the .exe), since it is XORed with 0x5a the value in the .exe is 0xba. To make the "LeftControl" key work as "Fn", it must be changed to 0xaf ^ 0x5a = 0xf5.

Hi, my thinkpad compact keyboard was made in 27 october 2021, FN swapped to left CTRL works!

But can you share the checksum 256 of you file for the FN swapped to right CTRL?

For some reason Ctrl+Shift+Up works for me, Fn swapped to Left Ctrl.

@valentt
Copy link

valentt commented May 27, 2024

I have Lenovo Trackpad Bluetooth Keyboard II: Lenovo Trackpad Bluetooth Keyboard II

Any chance of getting firmware for switching positions of Fn and CTRL ?

@Licarde
Copy link

Licarde commented Jul 4, 2024

Great solution!
I also use the IME for Japanese input, and I have added an IME on/off key with the following modification.

Key	ID	XORwith5A

R Alt	E6	BC
Caps	39	63
LANG1	90	CA	IME on
LANG2	91	CB	IME off

At 0x7409E
Caps => LANG2
0x63 => 0xCB

At 0x7409E
R Alt => LANG1
0xBC => 0xCA

@ratticon
Copy link

Awesome, thanks!

@maxidev
Copy link

maxidev commented Oct 25, 2024

For aynone wondering, yes it DOES work for SPANISH variation, here's my fw updated: https://filebin.net/93g9timf1uf33xyd/tp_compact_usb_kb_with_trackpoint_fw_upd.zip

@sashokbg
Copy link

Hello I have just tested the FN / CTR swap method on model KU-1255 rev W02 from 2024 and it worked properly.

@lepokle
Copy link

lepokle commented Jan 15, 2025

Hi @valentt , did you try it, and did it worked for that model?
Thanks!

@Dieco98
Copy link

Dieco98 commented Jan 19, 2025

Hi @valentt , did you try it, and did it worked for that model? Thanks!

Have you found anything for the Thinkpad Trackpoint II (KC-1957)?

@ZZyy-678
Copy link

I bricked my ku-1255 when I tried to update the newest firmware. What should I do to recover? T_T

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests