geekhack
geekhack Projects => Making Stuff Together! => Topic started by: hasu on Wed, 06 May 2020, 22:31:03
-
This is a thread to share info and discuss about Hacking HHKB Professional Classic(and Hybrid possibly).
I'll keep this first post updated for useful resources regularly. Post your findings!
Topics I'm for one mainly interested.
- Open source firmware for stock controller
- Firmware update protocol of PFU tool (https://happyhackingkb.com/download/)
- Custom controller and its firmware
HHKB Professional Classic PD-KB401W Internals
Let's start with my findings and photos:
https://gist.github.com/tmk/e0f9f49db619413d1cf45e1aecaea52e
https://imgur.com/a/p9dWvM0
More
HHKB Professional Classic PD-KB401W
===================================
2019-12-14
TODO
----
Photo album
-----------
https://imgur.com/a/p9dWvM0
STM32L072RB
-----------
LQFP64 with 128KB Flash(2-bank)
AN4767 On-the-fly firmware update for dual bank STM32
AN3156 USB DFU protocol used in the STM32 bootloader
4 bytes per word
32 words/128 bytes per page
32 pages/4K byes per sector
Memory:
0x0000 0000 Memory space mapped to Flash(0x0800 0000), System memory(0x1FF0 0000) or SRAM
0x0000 0000 top of stack address
0x0000 0004 start of codes reset handler
0x0008 0000 Memory space mapped to EEPROM(0x0808 0000)
0x0800 0000 Flash 128KB
0x0800 0000 Bank1 Flash 64KB
0x0800 FFFF
0x0801 0000 Bank2 Flash 64KB
0x0801 FFFF
0x0808 0000 EEPROM 6K
0x0808 0000 Bank1 EEPROM 3KB
0x0808 0BFF
0x0808 0C00 Bank2 EEPROM 3KB
0x0808 17FF
0x1FF0 0000 System memory 8K - Bootloader
0x1FF0 1FFF
0x1FF8 0000 Option bytes 32 bytes
0x1FF8 001f
0x1FF8 0020 Factory option byte 96 bytes
0x1FF8 007F
0x2000 0000 SRAM 20K
0x2000 4FFF SRAM 0x2000 20c0/0x2000 20b8
0x4000 0000 Peripherals
0x5000 0000 IOPORT
Keymap format in Flash/EEPROM:
------------------------------
Keymap is comprised of 128 bytes and defines 61 keys in first 64 bytes, followed by 64-byte '0x00'.
This is dump of 256 bytes from eeprom, for example. It define 2 layers for HHKB mode.
00000000: 00 8a e6 2c e2 8b 01 e5 38 37 36 10 11 05 19 06 ...,....876.....
00000010: 1b 1d e1 28 34 33 0f 0e 0d 0b 0a 09 07 16 04 e0 ...(43..........
00000020: 4c 30 2f 13 12 0c 18 1c 17 15 08 1a 14 2b 35 31 L0/..........+51
00000030: 2e 2d 27 26 25 24 23 22 21 20 1f 1e 29 00 00 00 .-'&%$#"! ..)...
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000080: 00 78 e6 2c e2 8b 01 e5 51 4e 4d 10 11 05 19 06 .x.,....QNM.....
00000090: 1b 1d e1 28 4f 50 4b 4a 0d 0b 0a 09 07 16 04 e0 ...(OPKJ........
000000a0: 2a 30 52 48 47 46 18 1c 17 15 08 1a 14 2b 4c 49 *0RHGF.......+LI
000000b0: 45 44 43 42 41 40 3f 3e 3d 3c 3b 3a 29 00 00 00 EDCBA@?>=<;...
000000c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0 1 2 3 4 5 6 7 8 9 A B C D E F
----------------------------------------------------------------
? RGu RAl spc LAl LGu Fn RSh / . , m n b v c
x z Lsh Ret ' ; l k j h g f d s a Ctl
Del ] [ p o i u y t r e w q Tab ` \
= - 0 9 8 7 6 5 4 3 2 1 Esc ? ? ?
Boot Sequence
-------------
The device is configured with BFB2=0, nBOOT1=1 in option bytes and BOOT0 pin is pulled low(BOOT0=0) and it should boot at Bank1 on Flash memory.
With HHKB STM32 standard Dual bank boot sequence is not enabled(BFB2=0), it boots always from Bank1 and firmware on Bank1 checks Bank2 firmware inegrity and execute it somehow probably.
If Bank2 firmware is not valid Bank1 firmware continues to run anyway instead.
Bank1: original firmware and not updated? firmware linked with 0x0800 0000
Bank2: The latest firmware which usually runs and updated by Bank1 firmeware. firmware linked with 0x0801 0000
PFU firmeare is not compatible with STM32 dual-bank boot sequence by System bootloader
RM0376 3.3.2
When the BFB2 bit is set and the boot pins are configured to boot from Flash memory
(BOOT0 = 0 and BOOT1 = x), the device maps the System memory at address 0. Bootloader runs after reset and check Bank2 first and then Bank1.
When Bank2 is valid Bootloader sets UFB in SYSCFG_CFGR1 to map Bank2 at address 0x0800 0000(bank swap) and jump there.
When Bank2 is not valid and Bank1 is valid Bootloader keeps UFB 0 and jump to Bank1 at 0x0800 0000.
When both banks are not valid Bootloader continues to run for progamming Flash memory.
On-the-fly live update
----------------------
The goal of the live field upgrade is to make the transition to a new code version without
going through the system reset.
Codes in both banks are normally linked to start at address 0x0800 0000: AN4767 3.1.1
By bank swap Bank1 or Bank2 can be mapped at 0x0800 0000 depending on UFB setting.
Concerns about transition of execution from one bank to another: AN4767 4
Both banks have unmodifiable and fixed code section which keeps stack at the lowest as possible to make the transition.
Remember that having identical source code does not implicate that identical binary codes
will be generated. It is better to generate a library to be linked to all future versions, or
preserve an object file, or (perhaps) even carefully edit the resulting binary
Dip Switch
----------
SW1,SW2: Keymap mode
00 HHK: Henkan and Muhenkan on Gui keys, Disables Volume and Power key, Enables Stop key(Cancel)
10 WIN: Disables Volume and Power key, Disables Stop key
01 MAC: Enables Volume and Power key, Disables Stop key
11 N/A: Inhibited.
SW3: Delete/Backspace
SW4: Left Super/Fn
SW5: Alt/Super
SW6: Power saving(Remote wakeup)
Internals
---------
Switch board:
1.6mm thick PCB
CNL1: Connector I-PEX CABLINE-VS 30pin, receptacle: 20455-030E-99, https://www.i-pex.com/product/cabline-vs
U1, U2: LW051A TSSOP 16pin, TI SN74LV4051A, http://www.ti.com/lit/ds/symlink/sn74lv4051a.pdf
U3: Non populated
U4: AYO7A48 10pin, OPA2373AID, OPAmp, https://www.ti.com/lit/ds/symlink/opa2373.pdf
Q8: MOSFET switching 21-Signal
Controller board:
2.0mm thick PCB
Z2 STM32L072RBT6 https://www.st.com/resource/en/datasheet/stm32l072rb.pdf
ADC: AN2668
Bootloader: AN2606
Z2.1 VDD
Z2.2 PC13 (Power Switch for Battery?)
Z2.3 PC14 (Power Switch TPS2065D for Switch board)
Z2.4 PC15 (pull down with 10K)
Z2.5 (Xtal) NP
Z2.6 (Xtal) NP
Z2.7 NRST (to CN3)
Z2.8 PC0 (pull down with 10K)
Z2.9 PC1 (pull down with 10K)
Z2.10 PC2 ?? for JP layout?
Z2.11 PC3 (pull down with 10K)
Z2.14 PA0 (pull down with 10K)
Z2.15 PA1 Signal input
Z2.16 PA2 (pull down with 10K)
Z2.17 PA3 (DIPSW-1)
Z2.18 VSS
Z2.19 VDD
Z2.20 PA4 (DIPSW-2)
Z2.21 PA5 (DIPSW-3)
Z2.22 PA6 (DIPSW-4)
Z2.23 PA7 (DIPSW-5)
Z2.24 PC4 (DIPSW-6)
Z2.25 PC5 (Push button for Bluetooth)
Z2.26 PB0 Row drive
Z2.27 PB1 Row drive
Z2.28 PB2 Row drive
Z2.29 PB10 Row drive
Z2.30 PB11 (Row drive for JP layout?)
Z2.31 VSS
Z2.32 VDD
Z2.33 PB12 COL A
Z2.34 PB13 COL B
Z2.35 PB14 COL C
Z2.36 PB15 U1 ~EN
Z2.37 PC6 U2 ~EN
Z2.38 PC7 OPAmp EN
Z2.39 PC8 Q8.5-gate capacitor discharge
Z2.40 PC9 ??
Z2.41 PA8 ~LED1
Z2.42 PA9 ~LED2
Z2.43 PA10 ??
Z2.44 (USB_DM)
Z2.45 (USB_DP)
Z2.46 (SWIO to CN3)
Z2.47 VSS
Z2.48 VDD_USB 3.3V from regulator
Z2.49 SWCLK (to CN3)
Z2.50 PA15 (to Bluetooth module) pull up with 51K R130
Z2.51 PC10 (pull down with 10K)
Z2.52 PC11 (pull down with 10K)
Z2.53 PC12 (pull down with 10K)
Z2.54 PD2 (to Bluetooth module) pull down with 51K R83 module reset??
Z2.55 PB3 (to Bluetooth module) pull up with 51K R127
Z2.56 PB4 (to Bluetooth module) pull up with 51K R128
Z2.57 PB5 (to Bluetooth module) pull up with 51K R129
Z2.58 PB6 (to Bluetooth module) pull up with 51K R131
Z2.59 PB7 (Battery Voltage Monitor)
Z2.60 BOOT0 pull down with 10K R6
Z2.61 PB8 (Battery Voltage Monitor)
Z2.62 PB9 (Battery Voltage Monitor)
Z2.63 VSS
Z2.64 VDD
Z7 V20 1GY, Voltage Regulator 3.3V
Z8 ?? Voltage monitor for Z7?
Z6 2065 SOT-23-5 TI TPS2065D http://www.ti.com/lit/ds/symlink/tps2065d.pdf
R85 0Ohm connects USB Power(3.3V regulated) and Battery Power for USB only Classic
Z15 Not populated. Power switch between USB power and Battery power
CN5 Not populated. Connector for battery
CN3 SWD Z2.7(NRST), Z2.49(SWCLK), Z2.46(SWDIO)
Z3 Reset IC connected to Z2.7(NRST)
Z5 Boost converter for Battery power
Z12 Voltage Monitor to shutdown Z5 when low voltage?
Z17, Z10, Z11 Voltage Monitor for Battery
Z4 Buck converter?
Switch board connector Controller board connector
1 Row drive 30 Z2.26 PB0
2 U1-4051A.INH(Turns on output with Lo.) 29 Z2.36 PB15
3 U1,U2-4051A.B 28 Z2.34 PB13
4 Row drive 27 Z2.27 PB1
5 U1,U2-4051A.C 26 Z2.35 PB14
6 Row drive 25 Z2.28 PB2
7 U1,U2-4051A.A 24 Z2.33 PB12
8 Row drive 23 Z2.29 PB10
9 U2-4051A.INH(Turns on output with Lo.) 22 Z2.37 PC6
10 NC? Row for JP layout?? 21 Z2.30 PB11(not controlled)
11 ~LED1.Kathode 20 Z2.41 PA8 thr Q6 Digital-Tr
12 ~LED2.Kathode 19 Z2.42 PA9 thr Q2 Digital-Tr
13 Q12.2-gate not populated. ?? Not controlled 18 Z2.40 PC9
14 U4.5,6(OPAmp EnableA,B) 17 Z2.38 PC7
15 NC? 16 Z2.43 PA10 with 510K pull-up
16 Q8.5-gate discharge? before row drive 15 Z2.39 PC8
17 NC?(for JP layout?) 14 Z2.10 PC2
18 GND 13 GND
19,20 AGND 11,12 AGND=GND
21 Signal from OPAmp 10 Z2.15 PA1
22,23 AGND 8,9 AGND=GND
24-26 GND 5-7 GND
27-29 VCC(3.3V) 2-4 Switched by Z6(TPS2065D) Z2.3
30 LED Power(3.3V): through R43, R44 to LED1.Anode, LED2.Anode 1 VCC 3.3V
Scan Key Matrix
===============
Scan matrix takes 4.8ms per 20ms
for (15 colums)
for (4 rows)
Row Drive
Sense keys
Matrix on PCB:
U1 U2
Y0 Y3 Y1 Y2 Y4 Y5 Y6 Y7 Y3 Y0 Y1 Y2 Y4 Y6 Y5
0 1 2 3 4 5 6 7 8 9 a b c d e
------------------------------------------------------------
PB1 Esc 1 2 3 4 5 6 7 8 9 0 - = \ `
PB2 Tab q w e r t g y u i o p [ ] Del
PB10 Ctl a s d c v b h j k l ; ' Ret Fn
PB0 LSh LAl z x LGu f spc n m , . RGu / RSh RAl
Matrix Scan order:
U1 U2
Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y0 Y1 Y2 Y3 Y4 Y5 Y6
0 2 3 1 4 5 6 7 9 a b 8 c e d
-----------------------------------------------------------
PB0 LSh z x LAl LGu f spc n , . RGu m / RAl RSh
PB1 Esc 2 3 1 4 5 6 7 9 0 - 8 = ` \
PB2 Tab w e q r t g y i o p u [ Del ]
PB10 Ctl s d a c v b h k l ; j ' Fn Ret
Col Select
----------
It takes 4.8ms to scan all keys on 4x15 matrix
| |20ms
___ _________________ ............. ___ _____
U1 |_______________| |_______________|
_______________ _______________
U2___| |_________________ ............. ___| |______
| U1.enable |2.5ms
| U2.enable |2.3ms
____ _______ _________
C |_______| |_______|
____ ___ ___ ___ _____
B |___| |___| |___| |___|
_ _ _ _ _ _ _
A______| |_| |_| |_| |_| |_| |_| |_____
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6
| |320us
| |4.8ms
Row Drive
---------
Wave form of drive: https://i.imgur.com/AHe1m27.png
Four rows driven: https://i.imgur.com/paKkbNF.png
_ 3.3V _
| | | |
| | | |
| \ | \
ROW0____| \___________________| \____
| |20us
| |80us
| |320us
_ _
| | | |
| | | |
| \ | \
ROW1__________| \___________________| \____
_ _
| | | |
| | | |
| \ | \
ROW2________________| \___________________| \____
_ _
| | | |
| | | |
| \ | \
ROW3______________________| \___________________| \____
| COL0 | COL1 | ... COL14 |
| |4.8ms
A row driven around 4.8ms(320us*15): https://i.imgur.com/Pg2XIUa.png
A row driven for 15 columns and output of OpAmp: https://i.imgur.com/6cQ6uIg.png
Sense key
---------
0. Select a column
1. Enable OpAmp
2. Discharge capcitor
Sample capacitor C57 retaining voltage of previous key and is dischared?
3. Drive a row
Signal is amplified and stored in capacitor C57?
4. Sensed by Controller
5. Shutdown OpAmp
6. loop 2-5 for rows
Signal for discarging capacitor: https://i.imgur.com/aRtHkPi.png
|| || || || discharge
|| || || ||
|| || || ||
Q8.g __||____||____||____||_____
||7us
Row drive and output from OpAmp: https://i.imgur.com/I91TQpL.png
Row drive and output from OpAmp: https://i.imgur.com/2g2jq8d.png
Row drive and output from OpAmp: https://i.imgur.com/scxqnAv.png
_ _ _ _
| | | | | | | |
| | | | | | | |
| \ | \ | \ | \
ROWn ___| \_| \_| \_| \_
| |20us
| |80us
____ 2.3V(depressed)
| |
| |
| | 600mV(normal)
Sense_--| |-----------------_ output from OpAmp
OPAmp_____ ___ ___ ___ _ enable
| | | | | | | |
| | | | | | | |
| | | | | | | |
|_| |_| |_| |_| shutdown
| |22us
| |53us
USB info
========
/var/log/kern.log:
Dec 14 11:46:49 desk kernel: [774252.335808] usb 3-3.2: new full-speed USB device number 35 using xhci_hcd
Dec 14 11:46:49 desk kernel: [774252.452770] usb 3-3.2: New USB device found, idVendor=04fe, idProduct=0020
Dec 14 11:46:49 desk kernel: [774252.452774] usb 3-3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
Dec 14 11:46:49 desk kernel: [774252.452776] usb 3-3.2: Product: HHKB-Classic
Dec 14 11:46:49 desk kernel: [774252.452778] usb 3-3.2: Manufacturer: PFU Limited
Dec 14 11:46:49 desk kernel: [774252.475927] input: PFU Limited HHKB-Classic as /devices/pci0000:00/0000:00:08.1/0000:38:00.3/usb3/3-3/3-3.2/3-3.2:1.0/0003:04FE:0020.01A4/input/input323
Dec 14 11:46:49 desk kernel: [774252.536337] hid-generic 0003:04FE:0020.01A4: input,hidraw2: USB HID v1.11 Keyboard [PFU Limited HHKB-Classic] on usb-0000:38:00.3-3.2/input0
Dec 14 11:46:49 desk kernel: [774252.543236] input: PFU Limited HHKB-Classic as /devices/pci0000:00/0000:00:08.1/0000:38:00.3/usb3/3-3/3-3.2/3-3.2:1.1/0003:04FE:0020.01A5/input/input324
Dec 14 11:46:49 desk kernel: [774252.600164] hid-generic 0003:04FE:0020.01A5: input,hidraw3: USB HID v1.11 Keyboard [PFU Limited HHKB-Classic] on usb-0000:38:00.3-3.2/input1
Dec 14 11:46:49 desk kernel: [774252.604075] hid-generic 0003:04FE:0020.01A6: hiddev1,hidraw4: USB HID v1.11 Device [PFU Limited HHKB-Classic] on usb-0000:38:00.3-3.2/input2
USB Intefaces
-------------
0: Boot Keyboard EP1IN(6KRO)
1: NKRO Keyboard and Consumer keys EP2IN
Reprot ID 1: Consumer Media keys
Reprot ID 2: NKRO Keyboard(Not used)
Reprot ID 3: Consumer Application Launch/Control keys(Not used)
2: Vendor specific EP3IN, EP4OUT
for keymap/firmware update
USB Descriptor
--------------
https://gist.github.com/tmk/5f22878a7ddca01e9174e5d6224395d2
DFU Bootloader
==============
To kick up System bootloader plugin with pulling up BOOT0 pin.
$ dfu-util -l
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Found DFU: [0483:df11] ver=2200, devnum=87, cfg=1, intf=0, path="3-3.2", alt=2, name="@DATA Memory /0x08080000/2*3Ke", serial="164738450000"
Found DFU: [0483:df11] ver=2200, devnum=87, cfg=1, intf=0, path="3-3.2", alt=1, name="@Option Bytes /0x1FF80000/01*032 e", serial="164738450000"
Found DFU: [0483:df11] ver=2200, devnum=87, cfg=1, intf=0, path="3-3.2", alt=0, name="@Internal Flash /0x08000000/1536*128g", serial="164738450000"
Flash
-----
Get Flash content(128KB) through bootloader.
$ dfu-util -a 0 -s 0x08000000 -U hhkb-flash.bin
It is comprised of two memory bank.
First part 64KB
00000 unknown. Programming interface for update keymap and firmware?
: :
0ffff
Second part 64KB
10000 identical to bin part of HHKB401_FW_A426.hfb
: :
1ffff
Firwmware is common for both US and JP layout and has different keymaps for them.
US keymaps is located around 0x0000cd20 while JP is around 0x0000c300.
Version of original firmware
0x0000 c100 0b 04 01 06 B4.1.6 (bank1)
0x0001 c150 0a 04 02 06 A4.2.6 (bank2)
EEPROM
------
It seems like keymap is stored in EEPROM. DIP switch key configuration affects on keymap in EEPROM.
It seems to have key sense calibration data in EEPROM also. After EEPROM broken accidentally some keys never been released. Restoring original EEPROM data cured the issue.
keymaps(128 byte per layer):
0x0000 HHK default
0x0080 HHK Fn
0x0100 MAC default
0x0180 MAC Fn
0x0200 WIN default
0x0280 WIN Fn
Other infos:
0x0300 Keyboard ID and version(A4.2.6)
00000300: aa aa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000310: 50 44 2d 4b 42 34 30 31 57 00 00 00 00 00 00 00 PD-KB401W.......
00000320: 00 00 00 00 41 30 00 00 43 31 38 4c 30 30 30 32 ....A0..C18L0002
00000330: 38 39 00 00 00 00 00 00 0a 04 02 06 00 00 00 00 89..............
00000340: 0b 04 01 06 00 00 00 00 00 00 00 00 00 00 00 00 ................
Get EEPROM content through DFU bootloader.
$ dfu-util -a 2 -s 0x08080000 -U hhkb-eeprom.bin
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Opening DFU capable USB device...
ID 0483:df11
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #2 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "DATA Memory "
Limiting upload to end of memory segment, 6144 bytes
Upload [=========================] 100% 6144 bytes
Upload done.
Option Btyes
------------
The Option bytes are automatically loaded during the boot. They are used to set the content of the FLASH_OPTR
and FLASH_WRPROTx registers. RM0376 3.8
32-byte data
$ dfu-util -a 1 -s 0x1FF80000:32 -U hhkb-opt.bin
00000000: aa 00 55 ff 70 80 8f 7f 00 00 ff ff 00 00 ff ff ..U.p...........
00000010: 00 00 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................
FLASH_OPTR: RM0376 3.7.8
0x8070 00aa
nBOOT1: 1
BOR_LEV:0 BOR OFF
BFB2: 0
WPRRMOD:0
RDPROT: 0xAA Protection Level 0
FLASH_WRPROT1: RM0376 3.7.9
0x0000 0000
FLASH_WRPROT2
0x---- 0000
48-bit: flash memroy sector protection configuration up to 192KB(48*4K sector)
0: sector not protected
1: sector protected
HHKB Keymap/Firwamre Update
===========================
Keyboard firmware supports the update feature with interface2 ep3 and 4.
Protocol
--------
Host command format: 0xaa,0xaa,cmd(1-byte),0x00,len(1-byte, length of data),data...
Keyboard response format: 0x55,0x55,cmd(1-byte)
command byte:
01 echo AA,AA,01,00,01,00
response:
55,55,01,00
02 read ID and version AA,AA,02,00,00
response:
55,55,02,00,00,39,(eeprom data at 0x310) - confirmed.
model name and serial number are picked from eeprom at 0x310 while firmware
version come from firmware code itself.
The last byte at end of data indicates integrity of A(Bank2) and the byte
turns 0x01 when the firmware is broken.
eemprom data:
00000310: 50 44 2d 4b 42 34 30 31 57 00 00 00 00 00 00 00 PD-KB401W.......
00000320: 00 00 00 00 41 30 00 00 43 31 38 4c 30 30 30 32 ....A0..C18L0002
00000330: 38 39 00 00 00 00 00 00 0a 04 02 06 00 00 00 00 89..............
00000340: 0b 04 01 06 00 00 00 00 00 .........
Note that version info of 0a 04 02 06 and 0b 04 01 06 in eeprom are not used.
It means firmware version 4.2.6 in A(Bank2) and 4.1.6 in B(Bank1).
05 read DIP Switch AA,AA,05,00,00
response:
55,55,05,00,00,0c,DIP1,DIP2,DIP3,DIP4,DIP5,DIP6,00,00,00,00,00,00
(00: OFF, 01: ON) - confirmed.
06 key config AA,AA,06,00,00
response:
55,55,06,00,00,00(HHK) or 55,55,06,00,00,01(MAC) or 55,55,06,00,00,02(WIN)
87 read keymap(two layers) AA,AA,87,00,02,(key config:[0|1|2]),(layer:[0|1])
response:
55,55,87,00,41,3a,(eeprom data)
55,55,87,00,82,3a,(eeprom data)
55,55,87,00,c3,0c,(eeprom data)
where keymap size: 128 bytes(0x3a+0x3a+0c)
eeprom data: HHK layer.0 at 0x0000
HHK layer.1 at 0x0080
MAC layer.0 at 0x0100
MAC layer.1 at 0x0180
WIN layer.0 at 0x0200
WIN layer.1 at 0x0280
e0 firmware update start? e0,00,00
e1 ?? e1,00,08,22,00,01,00,aa,d7,00,00
e2 e2,00,len,lsb,msb,data(firmware binary) where lsb and msb is sequence number
e3 firmware update end? e3,00,00
HHKB firmware file format .hfb
------------------------------
HHKB401_FW_A426.hfb - Firmware file for PD-KB401 downloaded from PFU site at 2019-12-11
The firmware is identical to 2nd part 64KB of hhkb-flash.bin.
Size: 65570 bytes = 64KB bin file(65536) + pre/postfix 34 bytes
"8f 03" part of Prefix can be located at 0x0370 in eeprom
Prefix:
aa d7 8f 03
Bin(64KB)
Postfix:
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 41 48 48 58 30 31 0a 04 02 06 ff ff ff ff ff ff
.......AHHX01..........
Description below includes vague speculation.
HHKB800_FW_A036.hfb - for Hybrid PD-KB800
Size: 282672 bytes 276KB(282624) + pre/postfix 48bytes???
Prefix:
00000000: 16 c8 ae ad
Postfix:
00045020: 41 48 55 58 30 31 0a 00 03 06 ff ff ff ff ff ff AHUX01..........
+-----------+
| 64KB | firmware for STM32???
+-----------+
| |
| 148KB |
| |
+-----------+
| 64KB |
+-----------+
HHKB800_FW_JP_A236.hfb - for Hybrid JP PD-KB820
Prefix:
00000000: 48 ad 05 fc
Postfix:
00045020: 41 48 4a 58 30 31 0a 02 03 06 ff ff ff ff ff ff AHJX01..........
Useful resources:
Remapping the 2019 HHKB Classic by crsayen
https://geekhack.org/index.php?topic=106001.0
-
Crack open your HHKB and void warranty!
(https://i.imgur.com/lG2ScHq.jpg)
-
Awesome work! I cracked open my board earlier today and started looking at the firmware in ghidra. It looks like you are way ahead of me!
I will be spending some time on this tomorrow.
Thanks for sharing your findings!
-
I have a lot of "source" code for a certain keymapping tool. I am adding some relevant files here.
if anyone wants to see more files, download dotPeek and open the exe.
KeyboardDriver.cs
https://gist.github.com/crsayen/58ad65038e463c13fb9caa566fea2ce2 (https://gist.github.com/crsayen/58ad65038e463c13fb9caa566fea2ce2)
USBDriver.cs
https://gist.github.com/crsayen/ffea66d1fbfa5d91e929ffdfd4f02d69 (https://gist.github.com/crsayen/ffea66d1fbfa5d91e929ffdfd4f02d69)
KeyboardLibrary.cs
https://gist.github.com/crsayen/e06c3650306a01232521e336e45ca134 (https://gist.github.com/crsayen/e06c3650306a01232521e336e45ca134)
-
I'm curious as to why they went with a dual-bank MCU this time. Honestly I don't think it's needed for this application, but maybe a little bit more hacking into the firmware might tell us
Nice job as always, Hasu
-
Very exciting stuff! It would be fantastic if it is possible to run open source software TMK/QMK on the stock controller :D
Thanks for the work hasu!
-
- Firmware update protocol of PFU tool (https://happyhackingkb.com/download/)
private async Task<bool> FirmupSend(uint dataNumber, byte[] data)
{
KeyboardDriver keyboardDriver1 = this;
System.Diagnostics.Debug.WriteLine("[FirmupSend] 1");
dataNumber -= 2U;
data = ((IEnumerable<byte>) data).Skip<byte>(2).ToArray<byte>();
System.Diagnostics.Debug.WriteLine("[FirmupSend] 3");
uint numberOfPacket = (uint) ((int) dataNumber + 57 - 1) / 57U;
uint completedByteLength = 0;
for (ushort packetNumber = 0; (uint) packetNumber < numberOfPacket; ++packetNumber)
{
KeyboardDriver keyboardDriver = keyboardDriver1;
System.Diagnostics.Debug.WriteLine(string.Format("[FirmupSend] 4 ({0}", (object) packetNumber));
uint num1 = (uint) packetNumber * 57U;
uint num2 = (uint) (((int) packetNumber + 1) * 57);
if (dataNumber <= num2)
num2 = dataNumber;
uint length = num2 - num1;
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 226;
input[4] = (byte) (length + 2U);
byte[] packetNumberByte = keyboardDriver1.toolUtility.ConvertUshortToBytes(packetNumber);
input[5] = packetNumberByte[0];
input[6] = packetNumberByte[1];
for (int index = 0; (long) index < (long) length; ++index)
input[7 + index] = data[(long) num1 + (long) index];
byte[] output = new byte[0];
System.Diagnostics.Debug.WriteLine(string.Format("[FirmupSend] 5 ({0}", (object) packetNumber));
await Task.Run((Action) (() => output = keyboardDriver.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 226,
(byte) 0,
(byte) 0,
(byte) 2
}, "0xE2")));
System.Diagnostics.Debug.WriteLine(string.Format("[FirmupSend] 6 ({0}", (object) packetNumber));
if (output == null)
return false;
if ((int) output[6] != (int) packetNumberByte[0] || (int) output[7] != (int) packetNumberByte[1])
{
System.Diagnostics.Debug.WriteLine(string.Format("Keyboard Communication Error 0xE2 (format error)"));
System.Diagnostics.Debug.WriteLine(string.Format("receiveData = {0}", (object) BitConverter.ToString(output)));
return false;
}
completedByteLength += length;
int completedRate = 10 + (int) (completedByteLength * 80U / dataNumber);
keyboardDriver1.eventAggregator.GetEvent<NortificateFirmupProgressEvent>().Publish(new NortificateFirmupProgressEventEntity(completedRate));
packetNumberByte = (byte[]) null;
}
System.Diagnostics.Debug.WriteLine("[FirmupSend] 7");
return true;
}
This looks like it gives a good idea of how the data is sent. Prior to sending firmware data, the tool sends this:
private bool FirmupStart(uint firmSize, byte[] crc)
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 225;
input[3] = (byte) 0;
input[4] = (byte) 8;
byte[] bytes = this.toolUtility.ConvertUintToBytes(firmSize);
for (int index = 0; index < 4; ++index)
input[5 + index] = bytes[index];
input[9] = crc[0];
input[10] = crc[1];
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 225,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE1") != null;
}
and to finish the firmware upload:
private bool FirmupEnd()
{
byte[] input = new byte[ConstDefinition.BufferSizeUSB];
input[0] = input[1] = (byte) 170;
input[2] = (byte) 227;
return this.WriteReadCheck(input, new byte[6]
{
(byte) 85,
(byte) 85,
(byte) 227,
(byte) 0,
(byte) 0,
(byte) 0
}, "0xE3") != null;
}
I am looking into whether we can just trick the tool into uploading custom firmware for us.
If not, I will modify it to do so.
-
Does this mean there is a chance of programmability of these boards (Hybrid/Classic) using VIA?
How can the community help with these efforts?
-
Really awesome work! So is it possible to port QMK/TMK to the stock controller of Pro Classic?
-
Yes
-
Really awesome work! So is it possible to port QMK/TMK to the stock controller of Pro Classic?
Yes
Amazing! Wow!
-
Really awesome work! So is it possible to port QMK/TMK to the stock controller of Pro Classic?
Yes
This is really exciting! Doing great work for the community here.
-
Are there any news on this?
Still waiting to buy the HHKB Professional Classic until I can use it with QMK.
-
Has work on the controller been abandoned due to the JSON work around? I’m still looking to get a classic to avoid the battery bump with a bluetooth capable HHKB. (Plus QMK compatibility helps)
-
Is anyone currently working on flashing custom firmware on the existing controller? Interested in helping out, but have had cold feet on actually buying one because my workflow is helplessly dependent on QMK
-
I think I'm typing this from the first HHKB Professional Classic running qmk. There is still a lot of work to do, but the basics are working.
The current code:
https://github.com/Duncaen/qmk_firmware/tree/hhkb_classic
I'm currently flashing the firmware by shorting BOOT0 to get into the DFU bootloader, which is a bit cumbersome.
I don't think there is a way to make it boot into the dfu bootloader without doing that so I might need to come up with a better solution.
Maybe some flash mode while qmk is running similar to the original firmware, maybe also using the dual bank mechanism.
I'm still trying to find out what the best values to use are for the activation point of the switches is and if it would be better with per key values which I think the original firmware does, the gist from hasu has some info about calibration data in EEPROM.
-
Great job!
Let us know if you have any your new find or correction on my note.
-
Here are the most interesting addresses in the eeprom:
0x08080000 u8[128] keymap_hhk_layer_0
0x08080080 u8[128] keymap_hhk_layer_1
0x08080100 u8[128] keymap_mac_layer_0
0x08080180 u8[128] keymap_mac_layer_1
0x08080200 u8[128] keymap_win_layer_0
0x08080280 u8[128] keymap_win_layer_1
0x08080300 u16 keymap_eeprom # 0xAAAA to load keymap from eeprom, otherwise default keymaps from flash are used.
0x08080310 u8[64] keyboard_info
0x08080350 u16 keyboard_info_eeprom # 0xAAAA to load info from keyboard otherwise data from flash is used.
0x08080360 bool boot_bank2
0x08080370 u16 bank2_crc16
# crc16(-ccitt?) polynomial=0x1021 initial_value=0xFFFF reverse_input=True reverse_output=True final_xor_value=0x0000
0x08081100 u16[128] actuation_points_0
0x08081200 u16[128] actuation_points_1
# If the first value in actuation_points_1 is zero all calibration is disabled.
# My eeprom only the first value is 1 everything else is 0, actuation_points_0 contains the actual data.
# When sensing a key, the firmware adds both values for a key; actuation_points_0[key]+from actuation_points_0[key]
0x08081330 u8[4] firmware_version_0 # boot
0x08081338 u8[4] firmware_version_1 # bank2
I've been using this dapboot branch https://github.com/Duncaen/dapboot/tree/hhkb (https://github.com/Duncaen/dapboot/tree/hhkb) as second stage dfu bootloader which works well to flash changes without having to open the keyboard. My qmk branch has a `keyboards/hhkb/classic/dapboot` keyboard with the right ldscript and the right configuration to make lshift+rshift+esc reset into dapboot.
I did waste quite some time with trying to get qmk boot on the "second bank" flashing with the original firmware on bank1, but I was unable to actually get it working because the bank 1 firmware changes the clocks which for some reason makes chibios unable to initialize and without a debugger I'm not really able to see what is actually going wrong.
The firmware files basically just have a 2 crc16 at the beginning of the file, the first one is checked by the keymap tool and include the trailing 0xFF and the "compatible model" things that are easy to follow thanks to the decompiled keymap tool.
The second crc16 is just for the firmware binary and is what the keyboard checks after finishing the firmware update and then is written to the eeprom, this checksum is being checked each boot and it will not switch to bank2 if it doesn't match.
There also seems to be some hidden key combination in the original firmware, not sure if this is documented somewhere and/or actually works, holding `fn+rshift+v` should after some time send the firmware version and then some bytes from ram I don't really know what they are.
-
I'm currently flashing the firmware by shorting BOOT0 to get into the DFU bootloader, which is a bit cumbersome.
I'm currently trying to flash your firmware to my PD-KB401, but I can't figure out how to get the board into recovery mode as there doesn't seem to be any labels for a BOOT0 pin to get it into recovery mode. Is it by any chance that I'd be right in assuming it's labelled PSW2 on the board with the USB C port on it?
EDIT:
I looked up the pinout and saw that BOOT0 is on pin 60, if this is the right pin for the bootloader, what pin should I short it with / what should I use on the other end to short it?
-
I'm currently flashing the firmware by shorting BOOT0 to get into the DFU bootloader, which is a bit cumbersome.
I'm currently trying to flash your firmware to my PD-KB401, but I can't figure out how to get the board into recovery mode as there doesn't seem to be any labels for a BOOT0 pin to get it into recovery mode. Is it by any chance that I'd be right in assuming it's labelled PSW2 on the board with the USB C port on it?
EDIT:
I looked up the pinout and saw that BOOT0 is on pin 60, if this is the right pin for the bootloader, what pin should I short it with / what should I use on the other end to short it?
R6 and R85 as seen on this picture from hasu https://i.imgur.com/oTwQLPg.jpeg. I just used a jumper wire without soldering, I plugged the usb cable half in, positioned the jumper wire so that they press against the right side of R6 and R85 with one hand and then plugged in usb with the other hand, this takes some tries to get right the first time, if the keyboard boots into the dfu bootloader the led will be orange.
Edit: Make sure you make backups of the flash and eeprom with dfu, I would also be interested in looking at you eeprom to compare the actuation points.
-
I'm currently flashing the firmware by shorting BOOT0 to get into the DFU bootloader, which is a bit cumbersome.
I'm currently trying to flash your firmware to my PD-KB401, but I can't figure out how to get the board into recovery mode as there doesn't seem to be any labels for a BOOT0 pin to get it into recovery mode. Is it by any chance that I'd be right in assuming it's labelled PSW2 on the board with the USB C port on it?
EDIT:
I looked up the pinout and saw that BOOT0 is on pin 60, if this is the right pin for the bootloader, what pin should I short it with / what should I use on the other end to short it?
R6 and R85 as seen on this picture from hasu https://i.imgur.com/oTwQLPg.jpeg. I just used a jumper wire without soldering, I plugged the usb cable half in, positioned the jumper wire so that they press against the right side of R6 and R85 with one hand and then plugged in usb with the other hand, this takes some tries to get right the first time, if the keyboard boots into the dfu bootloader the led will be orange.
Edit: Make sure you make backups of the flash and eeprom with dfu, I would also be interested in looking at you eeprom to compare the actuation points.
Unfortunately, in trying to get the board to use your QMK firmware, it's been flashed and now can't enter the bootloader (no orange light and won't appear with in dfu-buddy or dfu-util, and for some reason when I ran dfu-util to dump the firmware, it just dumped an empty file so my HHKB has now been rendered useless... Hopefully with it being less than 2 weeks old I can refund it from Amazon and get a new one...
Edit: Guess the jumper I was using must've mislodged itself a little bit, I managed to get it back into DFU mode but I unfortunately still have no way to restore the original firmware in lieu of QMK not working
Edit 2: I just realised in my original post I said my board was the PD-KB401, but it's actually the PD-KB401W, I don't think that'd have caused any issues but on the off chance it has I don't suppose you've got any advice do you?
Edit 3: I tried flashing this using dfu to no avail https://origin.pfultd.com/downloads/hhkb/HHKB410_FW_A429.hfb
-
Item return is not fair for Amazon or saler in this situ. Warranty is void when you peel its seal and open case... I know you are joking.
You can download official firmware image from PFU site. Flashing it onto both bank1 and 2 'may' work. If not you need original bank1 image but it is not available publicly unfortunately.
(EDIT: you can get binary image from hfb file: dd bs=1 skip=4 count=65536 if=HHKB410_FW_A429.hfb > firm.bin )
You should have made backup images and retain safely before removing flash memory, anyway.
-
Unfortunately after running the dd command to extract the firmware, flashing it onto the board using dfu-util -d 0483:df11 -a 0 -s 0x08000000 -D firm.bin or dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D firm.bin did not restore functionality to the board. Unfortunately I'm at a complete loss as to what I can do to fix it as the firmware file seems to be the correct one for the board. Running dfu-util -l returns the following (now, cannot confirm what it said before I flashed anything as I didn't make a backup of the output out of stupidity.
Found DFU: [0483:df11] ver=2200, devnum=9, cfg=1, intf=0, path="4-2.2", alt=2, name="@DATA Memory /0x08080000/2*3Ke", serial="154730420000"
Found DFU: [0483:df11] ver=2200, devnum=9, cfg=1, intf=0, path="4-2.2", alt=1, name="@Option Bytes /0x1FF80000/01*032 e", serial="154730420000"
Found DFU: [0483:df11] ver=2200, devnum=9, cfg=1, intf=0, path="4-2.2", alt=0, name="@Internal Flash /0x08000000/1536*128g", serial="154730420000"
Interestingly though, when running dfu-util -d 0483:df11 -a 0 -s 0x08000000 -U boardfirm.bin to get the firmware running on the board, it seems to error out at 64% consistently, and then reset the board out of DFU mode with the following output.
Opening DFU capable USB device...
Device ID 0483:df11
Device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Interface #0 ...
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "Internal Flash "
Non-valid multiplier 'g', interpreted as type identifier instead
Limiting upload to end of memory segment, 196608 bytes
Upload [================ ] 64% 126976 bytesdfuse_upload: libusb_control_transfer returned -9 (LIBUSB_ERROR_PIPE)
-
I think the firmware from PFU was built for Bank2(0x0801_0000) and it may not work on Bank1.
Note that you will need to use command like this to flash onto Bank2. (I didn't confirm this on mine, though.)
$ dfu-util -a 0 -s 0x08010000 -D firm.bin
This is md5sum of firm.bin just for reference.
$ md5sum firm.bin
ea90ed357441693940be241bb34468ae firm.bin
And this is my outputs from dfu-util. So your DFU bootloader seems to work normally.
$ dfu-util -l
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Found DFU: [0483:df11] ver=2200, devnum=35, cfg=1, intf=0, path="5-1", alt=2, name="@DATA Memory /0x08080000/2*3Ke", serial="164738450000"
Found DFU: [0483:df11] ver=2200, devnum=35, cfg=1, intf=0, path="5-1", alt=1, name="@Option Bytes /0x1FF80000/01*032 e", serial="164738450000"
Found DFU: [0483:df11] ver=2200, devnum=35, cfg=1, intf=0, path="5-1", alt=0, name="@Internal Flash /0x08000000/1536*128g", serial="164738450000"
$ dfu-util -a 0 -s 0x08000000 -U hhkb_flash.bin
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Opening DFU capable USB device...
ID 0483:df11
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "Internal Flash "
dfu-util: Non-valid multiplier 'g', interpreted as type identifier instead
Limiting upload to end of memory segment, 196608 bytes
Upload [================ ] 64% 126976 bytesdfu-util: dfuse_upload: libusb_control_msg returned -4
$ ls -l hhkb_flash.bin
-rw-rw-r-- 1 noname noname 131072 Feb 19 23:00 hhkb_flash.bin
-
Flashing it to bank2 worked and restored functionality to the board! Thank you so much for the help. I'm gonna continue to try to get QMK working however I've made sure to make backups of all three of the DFU's that list finds.
-
Following in the steps of Duncaen, I'm typing this on what could possibly be the second HHKB running QMK! I'm running dapboot as my bootloader provided by Duncaen, and am running a QMK firmware with some userspace code from QMK.
-
Someone did HHKB classic conversion apparently. Looks like they never managed to solder Bt module to the existing spot so it's just the entirely new controller board https://item.taobao.com/item.htm?spm=a21n57.1.0.0.578f523csL2FmJ&id=715088039960&ns=1&abbucket=9#detail
[attachimg=1]
-
Hey everyone, hope you're all well. Any news/ updates on the whole programming the new hhkbs thing?
My entire workflow depends on QMK programmability and macros :-[ :llama: