Author Topic: Hacking HHKB Professional Classic  (Read 9919 times)

0 Members and 1 Guest are viewing this topic.

Offline hasu

  • Thread Starter
  • Posts: 3321
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Hacking HHKB Professional Classic
« 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
- 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
Quote

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
« Last Edit: Wed, 06 May 2020, 22:59:43 by hasu »

Offline hasu

  • Thread Starter
  • Posts: 3321
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Hacking HHKB Professional Classic
« Reply #1 on: Wed, 06 May 2020, 23:00:00 »
Crack open your HHKB and void warranty!


Offline crsayen

  • Posts: 4
Re: Hacking HHKB Professional Classic
« Reply #2 on: Thu, 07 May 2020, 00:28:41 »
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!

Offline crsayen

  • Posts: 4
Re: Hacking HHKB Professional Classic
« Reply #3 on: Thu, 07 May 2020, 07:50:12 »
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

USBDriver.cs
https://gist.github.com/crsayen/ffea66d1fbfa5d91e929ffdfd4f02d69

KeyboardLibrary.cs
https://gist.github.com/crsayen/e06c3650306a01232521e336e45ca134
« Last Edit: Thu, 07 May 2020, 09:31:41 by crsayen »

Offline Gondolindrim

  • Posts: 603
  • Location: Gondolin
    • My GitHub
Re: Hacking HHKB Professional Classic
« Reply #4 on: Thu, 07 May 2020, 09:03:39 »
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
A pessimist will tell you the cup is half empty. An optimist will tell you the cup is half full. An engineer will tell you it's exactly twice the size it needs to be.

Online Applet

  • Posts: 432
  • Location: Sweden
Re: Hacking HHKB Professional Classic
« Reply #5 on: Thu, 07 May 2020, 09:34:08 »
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!

Offline crsayen

  • Posts: 4
Re: Hacking HHKB Professional Classic
« Reply #6 on: Thu, 07 May 2020, 10:21:33 »
Quote
- Firmware update protocol of PFU tool

Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
    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.

Offline vffems2529

  • Posts: 4
Re: Hacking HHKB Professional Classic
« Reply #7 on: Thu, 07 May 2020, 11:32:20 »
Does this mean there is a chance of programmability of these boards (Hybrid/Classic) using VIA?
How can the community help with these efforts?

Offline power_ranger

  • Posts: 1
Re: Hacking HHKB Professional Classic
« Reply #8 on: Sun, 27 December 2020, 13:38:20 »
Really awesome work! So is it possible to port QMK/TMK to the stock controller of Pro Classic?

Offline hasu

  • Thread Starter
  • Posts: 3321
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Hacking HHKB Professional Classic
« Reply #9 on: Sun, 27 December 2020, 16:57:56 »
Yes

Offline vladimir108

  • Posts: 140
Re: Hacking HHKB Professional Classic
« Reply #10 on: Mon, 11 January 2021, 13:42:34 »
Really awesome work! So is it possible to port QMK/TMK to the stock controller of Pro Classic?

Yes

Amazing! Wow!

Offline tiydal

  • Posts: 56
  • Location: UK
Re: Hacking HHKB Professional Classic
« Reply #11 on: Sat, 20 March 2021, 17:13:48 »
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.

Offline GoatMaster

  • Formerly Franatic89
  • Posts: 154
  • Location: Germany
Re: Hacking HHKB Professional Classic
« Reply #12 on: Thu, 25 March 2021, 06:09:23 »
Are there any news on this?

Still waiting to buy the HHKB Professional Classic until I can use it with QMK.

Offline liangalv

  • Posts: 35
Re: Hacking HHKB Professional Classic
« Reply #13 on: Thu, 03 June 2021, 17:46:41 »
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)

Offline Mistah

  • Posts: 55
Re: Hacking HHKB Professional Classic
« Reply #14 on: Tue, 08 June 2021, 08:30:30 »
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