キーボードドライバ実装
電源投入後の通信時系列 全体
https://gyazo.com/07d6931ad44c0ea16ff18bed65c54e2c
D0 = SDA
D1 = SCL
D2 = IRQ
電源投入直後のSDAだけ振れてるやつはBoot ROMのDUART(Mux未設定のため)
拡大 1
https://gyazo.com/4f6f706d0b2de83027c83a8cceea5972
通信1 404ms ごろ
通信2 508ms ごろ
拡大 2
https://gyazo.com/2d52ad27577dd86e5196df889ca1674b
通信3 612ms ごろ
通信4 924ms ごろ
拡大 3
https://gyazo.com/eeee181e5cf2718ff16987fb4ddf2eef
通信5 7120ms ごろ
通信6 8744ms ごろ
拡大 4
https://gyazo.com/30857a42a1e32f3d583ce63706d57b3f
通信7 11540ms ごろ
通信内容
リテラルはすべて16進
Write と Read のアドレスはすべて 0x28
以下のダンプの書式は {Write bytes} → {Read bytes} と簡略化する
レジスタ番号を Write して Read すると値が出る
Write が必ず先立つ
コマンド番号的な意味を含むものがあるらしく、Write のみの場合がある
No. 1$ \ t=404\,\mathrm{[ms]}
0x00 (Status) → 0x40 (起動中?)
No. 2$ \ t=508\,\mathrm{[ms]}
0x00 (Status) → 0x40 (起動中?)
100ms delay してリトライ?
No. 3$ \ t=612\,\mathrm{[ms]}
0x00 (Status) → 0x00 (通常動作?)
0x0A (Reset Status) → 0x01 (サスペンド無効)
0x01 (Port Status) → 0x20 (USBDETECTEVENT, USBFNEVENT, USBEVENT をセット)
100ms delay してリトライ?
Status Register のビットが折れているのを察知してからサスペンド無効化とポート設定?を行っている
No. 4$ \ t=924\,\mathrm{[ms]}
0x40 → 0x10
0x41 → 0x00
0x48 → 0x40
0x40, 0x41, 0x48 の Write に副作用があるかは不明
No. 5$ \ t=7120\,\mathrm{[ms]}
0x00 (Status) → 0x00 (通常動作?)
(10ms later)
0x00 (Status) → 0x00 (通常動作?)
0x04 (Key Event) → 0x01, 0x8A (1 key, 0x0A のリリース)
0x01 (Port Status) → 0x20 (イベントをセット)
(10ms later)
0x0A (Reset Status) → 0x01 (サスペンド無効)
(5ms later)
0x00 (Status) → 0x00 (通常動作?)
0x0A (Reset Status) → 0x01 (サスペンド無効)
0x01 (Port Status) → 0x20 (イベントをセット)
0x88, 0x08 (?)
(1ms later)
0x82, 0x05 (?)
ロゴ→画面が暗転と同時に上記I2C通信→その後時計設定画面へ
No. 6$ \ t=8744\,\mathrm{[ms]}
0x00 (Status) → 0x00 (通常動作?)
24ms later
0x0A (Reset Status) → 0x01 (サスペンド無効)
何かを設定している?
No. 7$ \ t=11540\,\mathrm{[ms]}
0x82, 0x02 (?)
0x82, 0x03 (?)
決定キーを押した時の波形
全体
https://gyazo.com/eb8464822d4dd5bcdcb98193a5d216e9
割り込みが起きて、I2Cの通信中にフラグが下がる
拡大
https://gyazo.com/f01e6c4ac0ce79939b62fc2cf12f5d0c
https://gyazo.com/461a165006ea6b28ec76cb73591ea4af
https://gyazo.com/4a022150f2f022a4e5367ee75d877823
https://gyazo.com/f82330328546c6e602efa607b17631fa
決定キーを押した時
0x04 → 0x01, 0x23
決定キーを離した時
0x04 → 0x01, 0x63
Aキーを押した時
0x04 → 0x01, 0x03
Aキーを離した時
0x04 → 0x01, 0x43
プロトコル
Writeで送るデータは必ずしもレジスタ番号ではないっぽいので、Writeする時の先頭1バイトをニーモニックと呼ぶことにする
ニーモニックのみ・ニーモニック + 引数・ニーモニック + Read の3パターンがある
table: 見つかっているニーモニック一覧
ニーモニック 意味 W データ長 W enum R データ長 R enum
0x00 Status 0 1 0x40 = No, 0x00 = Yes
0x01 Port Status 0 1 0x20 = Set event, 0x00 = Reset event
0x04 Key Event 0 2
0x0A Reset Status 0 1 0x01 = Not suspended
0x0B Direct Key ? ? ?
0x40 ? 0 1 0x10 = ?
0x41 ? 0 1 0x00 = ?
0x48 ? 0 1 0x40 = ?
0x82 ? 1 0x05, 0x02, 0x03
0x88 ? 1 0x08
table:キーコード
[ビット] 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
[意味] ? ? ? ? ? ? ? ? ? 押フラグ Key {5} Key {4} Key {3} Key {2} Key {1} Key {0}
キーの読み出し方
必ず 0x04 を 2 Bytes (Word) read する
table:Read 1回目
# Byte[0] Byte[1]
意味 N(イベント数) キーコード1
N = 1 の場合
キーコードが 0x00 → イベントなし
キーコードが 0x00 以外 → キーコード
N = 2 の場合
追加でもう一回 Read する
2回目の Read にはキーコードとダミーの値が入る
ダミーの値にはいろいろなものがあるが、法則性は不明
table:Read 2回目(N = 2)
# Byte[0] Byte[1]
意味 キーコード2 ダミー
N = 3 の場合
追加でもう一回 Read する
2回目の Read には 2 つのキーコードが入る
table:Read 2回目(N = 3)
# Byte[0] Byte[1]
意味 キーコード2 キーコード3
N > 3 の場合
キーコード1が特定のキーの組み合わせで、一意のコードを返すように見える
N=3の場合と同じくキーコード3までは有効なキーコードに見えるが、それ以降はinvalidに見える
キーコード
Q: 0x02
W: 0x08
E: 0x0e
R: 0x12
T: 0x16
Y: 0x1e
U: 0x26
I: 0x2c
O: 0x27
P: 0x2d
A: 0x03
S: 0x09
D: 0x0f
F: 0x13
G: 0x17
H: 0x1f
J: 0x20
K: 0x28
L: 0x2e
Left Shift: 0x05
Z: 0x04
X: 0x0a
C: 0x10
V: 0x14
B: 0x18
N: 0x21
M: 0x29
Minus: 0x2f
Delete: 0x31
PgUp: 0x0b
PgDown: 0x0c
Switch characters: 0x06
Symbols: 0x19
Back: 0x1b
Space: 0x1c
Enter: 0x23
Left: 0x1a
Up: 0x22
Down: 0x2a
Right: 0x30
ハードウェアイベントに対応するキーコードの仕様
PW-SH1にて調査
キースイッチの他にスイッチや状態変化によってもイベントが発生する。
bit7 : 1=キーボード以外のイベント
bit6.4 : 不明 ( 000b )
bit3..1 : イベント(スイッチ)コード
bit0: 0=on / 1=off
イベントコード
タブレット形に開ききる途中 : 3
USB給電あり : 5
蓋が完全に閉まったまたは完全にタブレット形になった : 6
閉まりかけている途中 : 7
LCDを速く閉じた時
N=3 {0x8E, 0x8C, 0x86}
0x8E: 1 000 111 0 = 閉まりかけている途中 + ON
0x8C: 1 000 110 0 = 蓋が完全に閉まった + ON
0x86: 1 000 011 0 = タブレット形に開ききる途中 + ON
LCDを速く開いた時
N=3 {0x8F, 0x8D, 0x87}
0x8F: 1 000 111 1 = 閉まりかけている途中 + OFF
0x8D: 1 000 110 1 = 蓋が完全に閉まった + OFF
0x87: 1 000 011 1 = タブレット形に開ききる途中 + OFF
LCDをゆっくり閉じた時
5mm くらいで、N=2 {0x8E, 0x86}
0x8E: 1 000 111 0 = 閉まりかけている途中 + ON
0x86: 1 000 011 0 = タブレット形に開ききる途中 + ON
完全に閉じると N=1 {0x8C}
0x8C: 1 000 110 0 = 蓋が完全に閉まった + ON
LCDをゆっくり開いた時
5mm くらいで N=1 {0x8D}
0x8D: 1 000 110 1 = 蓋が完全に閉まった + OFF
10mm くらいで N=2 {0x8F, 0x87}
0x8F: 1 000 111 1 = 閉まりかけている途中 + OFF
0x87: 1 000 011 1 = タブレット形に開ききる途中 + OFF
背合わせに閉じた時(タブレットon)
N=1 {0x8C}
0x8C: 1 000 110 0 = 蓋が完全に閉まった + ON
背合わせから開いた時(タブレットoff)
N=1 {0x8D}
0x8D: 1 000 110 1 = 蓋が完全に閉まった + OFF
USBのVBUSに+5Vを接続 : N=1{0x8A}
USBのVBUSから+5Vを切断 : N=1{0x8B}
D+,D-,IDが未接続の充電ケーブルでも同様のためVBUSの電源電圧に連動するように思える
puhitaku.icon ID は PCB 上でも未接続なので想定動作
VBUSから給電充電している状態でVBUSを切断、再接続しても給電充電状態に戻らないため、給電はMCUで管理しており再接続後は給電開始コマンドをMCUに送る必要があるように思える。
puhitaku.icon これは追加解析が必要そう
Sx3 以降
マトリクスへの出力(バンク選択): GPIO Bank 2 16 〜 2 21, Bank 4 8
マトリクスからの入力: GPIO Bank 4 0 〜 7
競合状態の回避
ボタンの bouncing のために、割り込みハンドラが処理している間に再び割り込みが発生しうる
どうやら IRQF_ONESHOT を指定すれば大丈夫っぽい?
Debounce
単に busy loop で sleep をかましていたのではカーネルが全停止してしまうのでここに従って non-atomic なスリープ usleep_rangeを使用…と思ったら atomic なコンテキスト(割り込みハンドラ)の下ではカーネルのスケジューラを使用した sleep はだめらしい、そりゃそうか code:error
96.013371 BUG: scheduling while atomic: ly/223/0x00010001 96.024226 CPU: 0 PID: 223 Comm: ly Not tainted 5.4.99-00274-gc5d1c601ca3-dirty #27 96.032554 Hardware name: Freescale MXS (Device Tree) そもそも割り込みの入り方が謎なので(押している間無限に割り込みが立つ)、poll して読む作戦に変更