How to use keyboard/mouse for PS4 part3 -Btstack is GOD-

How to use keyboard/mouse for PS4 part1 -Sniffing HCI-UART- - aki33524’s blog

How to use keyboard/mouse for PS4 part2 -How does Bluetooth work?- - aki33524’s blog

Part2で少し話に出したが、Btstackを使ってみると一気に進捗が出た。Bluezなんて一生使わねえぞ。

Bluez vs Btstack

Bluezは現在Linuxで採用されているBluetoothプロトコルスタックである。標準であるからには優れたものだと思っていたが実際にはそびえ立つクソだった。料理を作ろうとしたらチェーンソーを渡されてしかもたまに突然歯が逆回転したりするような感じ。

Btstackはとてもシンプルな組み込み向けのシンプルなプロトコルスタックである。組み込み向けとは言うものの、libusbをインストールすることでUNIX-basedの環境にも対応するし、daemonもあるようだ。料理を作ろうとしたら小さなナイフを幾つか渡された感じ。決して高機能ではないし、ナイフの選択には少しばかり注意が必要だが思い通りの動作が出来る。

Btstackは組み込み向けであるからして、kernelの機能を使わない。その分デバッグや細かい制御がとても簡単に行える。ドキュメントも存在しているし、例示も充実している。Keyboardエミュレータまで存在するのは驚いた。Bluezが何故クソかと言えば、これらが全て満たされないからだ。kernelに依存するのは良いが、それが謎の挙動をするのだからクソでしか無い。ドキュメントは本当に一切無いし、最も良い例示はbluetoothdのコードである。加えて難解なkernelのコードを読む必要もある。

もちろんBluezだって良いところはある。どうやらBluezは様々なデバイスをホスト側で扱うことに注力しているようだ。加えて、ユーザーがGUIで簡単に設定できるようにしており(むしろCUIはなおざりらしい)、一般ユーザーが使う分には便利。今回は用途が違った。

Btstack

BtstackはBluetoothの仕様に従い階層化したイベント駆動で動く。各階層のやり取りは独自の(仮想化した)HCI packetを使って行うようだ。パケットログが若干汚くなるがそこまで気にならないから良しとしよう。多分ログに保存しない方法もあるはず。

正しい作法なのかわからないが、実装されているコードは極めて簡素であるから自分でカスタマイズした。DS4はHuman Interface Device(HID)プロトコルでやり取りする。Btstackのhid_device.cはL2CAPのDatapacketに関して一切イベントを送出しない。しかし、DS4で接続した直後に飛んでくるGET_REPORTに答えなければ入力待ち状態にならないため、新しいイベントを送出するように書き換えた。

SDP

SDPは適切に設定すれば勝手に分割されて送信される。これはPart1で作ったsnifferで取り出したSDPデータを分割した(HIDとDIDの2つのserviceが入っており、1つずつ設定する必要があるため)。これはWikiに載っている内容と少し食い違っているからで、ProductIDから違う。新しいバージョンのコントローラーはUSBによるキー入力に対応しており、別のデバイスとして認識されるようだ。

GET_REPORT

先述したように、PS4に接続した直後に飛んでくるGET_REPORTに答えなければならない。Wikiに載っている情報はおそらく以前のバージョンのコントローラー(Aug 3 2013)で、実際にsniffするとこの箇所が2016になっていた。ちょうど発売時期と一致する。

http://www.psdevwiki.com/ps4/DS4-BT#0x06

0x06と0xa3に応答するのはいかにも大切そうだが、あまり解析されていない0x02のreportを返すのも大切だった。これを返して初めてコントローラーは応答待ちになる。これもWikiのデータとは食い違っていた。これがコントローラーそれぞれによって違うのか基板レベルなのかは謎。

http://www.psdevwiki.com/ps4/DS4-BT#0x02

Demo

www.youtube.com

ThinkpadBluetoothでDS4として登録する。その後数秒おきに◯ボタンを押している。最後に注目して欲しいのだが接続が解除されている。これはThinkpadから解除したのではなくPS4側から解除されている。

実はPS4はライセンスされたコントローラーしか使えない仕組みが導入されている。次パートではそれについて解説する。もし新しいコントローラーで仕様変わってたら死ねる……

と思ってたけど今冷静に動画見たらこれはただ単に自分で接続切ってるだけだった……

www.youtube.com

◯を一回だけ押して後は上下を交互に押し続けるようにした。どうやらchallengeに応答しないと入力が無視されるだけで接続が切られるわけでは無いようだ。

そもそも何故動的に入力を作ってないかというと、CRCをCでパッと書くのが面倒だから(Pythonで10000メッセージくらい作って流してる)。

Bluetooth Class

Bluetoothは幾つかの消費電力の区分でクラス分けされている。どうやらX220に内蔵されているBluetoothはClass-3なのかとても弱くて接続が安定しなかった。今日Class-1(なんと理論上100mまで通信可能)を買ってきたら安定した。

DS4で使われているAR3002はSupports both Class-2 (up to +4 dBm) and Class-1 (up to +10 dBm) operationとあり比較的強い電波強度である。しかしClass-1を名乗るには100mW以上必要だと理解していたけど10mWまでに見える、どういうことだ。100mWは上限であって、Class-2以上であれば良いようだ。

DS4をペアリングする時にえらく認識されるのが速いのはClassだけが理由ではない。DS4は起動時にいろいろな初期化を行っており、その際にスキャンされやすい(その代わりに消費電力も上がる)設定をする。Bluez向けのコードではこれを実装していたがBtstack向けではまだやっていない。とは言え書かなくても問題無いらしい。Bluez向けはこれを書いた後に不安定になった気もするのであまり手を出したくないという理由もある。

追記