
USB Linux Gadgetを使ってRaspberry PiをUSBキーボードと認識させてみた
-
2022年12月22日
こんにちは。主に課金周りのサーバーとそのバックシステムを担当しているKです。
皆さんUSB Linux Gadgetをご存知でしょうか。USB Linux GadgetはUDC(USB Device Controller)を備えたデバイスであり、USBホストに接続してシリアルポートやストレージ機能などを追加で拡張することができます。
今回はUSB Linux Gadgetを使ってRaspberry PiをUSBキーボードと認識させて遊んでみました。
準備したもの
- MacBook
- Raspberry Pi 4 Model B : OS Linux raspberrypi 5.15.32-v8+
- Windows11
やってみたこと
Raspberry PiにUSB Linux Gadgetを設定してWindowsにキーボードとして認識させます。
その後、MacからRaspberry Piにキーコードを送ってHIDを通してWindowsにタイプした結果を表示させます。
かんたんな事前知識
USB機器は一見ひとつの機器でも内部的には複数の機能をもつものがあります。これを実現するためにUSBデバイスがどのような仕様のものなのかを、テーブルの形で階層的に保持しています。これをディスクリプタと呼びます。ディスクリプタにはいくつか種類があります。
-
- デバイス・ディスクリプタ
- 一番上のルートに位置するディスクリプタ、ベンダIDやプロダクトIDなどを設定します。
- コンフィグレーション・ディスクリプタ
- インターフェース・ディスクリプタとエンドポイント・ディスクリプタの長さや消費電力を設定します。
- インターフェース・ディスクリプタ
- ここでインターフェースとしての仕様を設定します。ここに今回の目的であるキーボードであることを設定します。
- エンドポイント・ディスクリプタ
- エンドポイントの番号や転送モード、パケットサイズなどを設定します。
- ストリング・ディスクリプタ
- デバイスに対する製品名などの文字情報を設定します。これらの情報は任意に設定でき、ホストに接続したときに表示されます。言語ごとに決められたサブディレクトリに構築します。
- デバイス・ディスクリプタ
各ディスクリプタの仕様についてはこちらをご参照ください。
今回の機器構成
最初の準備
Raspberry PiにUSB OTGドライバとComposite USB Gadgetsを読み込ませます。
/boot/config.txtに以下を追加します。
dtoverlay=dwc2
/boot/cmdline.txtに以下を追加します。
modules-load=dwc2,libcomposite
間違って記入するとRaspberry Piが立ち上がらなくなるので注意しましょう。
手順
以下、Raspberry Piでの作業です。
デバイスの制御のためにlibcompositeを読み込み、configfsのディレクトリに移動します。
$ modprobe libcomposite
$ cd /sys/kernel/config/usb_gadget/
ガジェットのディレクトリを作成し、移動します。
$ mkdir -p g1
$ cd g1
configfsに設定を書いていきます。各ガジェットには、ベンダーIDと製品IDを登録する必要があります。ベンダーIDと製品IDは各メーカーで個別に決められていますが、今回は以下のものを使います。各ベンダの情報はUSB Information for Developersに記載されています。
デバイス・ディスクリプタの設定の設定をします。
$ echo 0x1d6b > idVendor # USB IFが割り当てます。今回はLinux Foundationを選択
$ echo 0x0104 > idProduct # ベンダが割り当てます。今回はMultifunction Composite Gadgetを選択
$ echo 0x0100 > bcdDevice # BCD表現のデバイスのリリース番号 v1.0.0にしておきます
$ echo 0x0200 > bcdUSB # BCD表現のUSB仕様リリース番号 USB2.0にします。
ストリング・ディスクリプタを作成します。
$ mkdir strings/0x409
$ echo "12345678" > strings/0x409/serialnumber
$ echo "KeyboardTestCCN." > strings/0x409/manufacturer
$ echo "Generic USB Keyboard" > strings/0x409/product
Functionsの設定をし、レポートディスクリプタを設定します。レポートディスクリプタはデバイスからホストに通知するデータフォーマットを定義するものです。こちらを参考にします。
$ mkdir -p functions/hid.usb0
$ echo 1 > functions/hid.usb0/protocol # キーボード
$ echo 0 > functions/hid.usb0/subclass # subclass設定しない
$ echo 8 > functions/hid.usb0/report_length
$ echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc
コンフィグレーション・ディスクリプタを設定します。
$ mkdir -p configs/c.1
$ echo 250 > configs/c.1/MaxPower
$ mkdir -p configs/c.1/strings/0x409
$ echo "Config 1" > configs/c.1/strings/0x409/configuration
$ ln -s functions/hid.usb0 configs/c.1
最後にガジェットを有効化します。後にデバイスにキーコードを送るために権限をつけておきます。
$ ls /sys/class/udc > UDC
$ chmod 777 /dev/hidg0
以上で設定は完了です。Raspberry Piを再起動するとすべての設定が消えてしまいますので、スクリプト化しておくかserviceに登録することをおすすめします。
Windowsでキーボードとして認識されたことを確認する
WindowsでRaspberry Piをキーボードとして認識しているか確認します。
USBデバイスをUSB Device Tree Viewerで見るとちゃんと認識されていました。設定したディスクリプタも確認できます。
Raspberry Piでキーコードを送ってみる
ちゃんとキーボードとして動作するのかやってみます。
以下のコマンドで”a”を押しっぱなしにするキーコードを送ってみます。
$ echo -ne "\0\0\x4\0\0\0\0\0" > /dev/hidg0
あらかじめWindowsでは”a”を表示させるためにエディタを開いてカーソルを置いておきました。キーコードを入力後、エディタをみるとaがずっと入力され続けている様子が見れます。
キーを離すキーコードを送ると入力が止まります。
$ echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0
おまけ
Raspberry Pi 4 model Bから電源アダプターがUSB-Type Cに変更されています。最初、WindowsにUSBで接続してそのまま給電できるかと思ったのですが電圧が足りずOSが起動しませんでした。しかしWindowsとはUSB TypeCの口を利用して繋ぎたいのでどうしたものかと考えた結果、GPIOから給電させてみたらいけるのではないかとやってみましたら無事起動しました。以下のコマンドで電圧不足をみてlogがでていなかったので足りたのでしょう。
$ journalctl -xe | grep "Under-voltage"
まとめ
Raspberry PiをLinux USB Gadgetを使ってキーボードにしてみました。どのように有効活用するのかはアイデア次第でいろいろ考えられるかと思います。キーボード以外にもゲームコントローラーにもストレージにもできるようです。
この記事がRaspberry Piを触る方のきっかけになれば幸いです。
ココネでは一緒に働く仲間を募集中です。
ご興味のある方は、ぜひこちらの採用特設サイトをご覧ください。