ここまで来たら、次なる目標は HID コンポジットデバイスです。有用性とかは置いておいて、取り敢えずマウス + キーボード なデバイスを作ります。
本記事ではそこまで詳しく説明しません。あくまでも備忘録。
キーボードのレポートディスクリプタ
こちらも探していたら見つかった、というか、サンプルをそのまま出力したと言ったほうが適切でしょうか。
実は USB.org が HID Descriptor Tool なるものを配布しているようで、こちらからキーボード用のサンプルファイルを用いました。.h
の配列形式で出力してくれるので嬉しいです。そして出力を若干弄りました(アラインメントを)。
const uint8_t __attribute__((aligned(2))) ReportDescriptorForKeyBoard[63] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
コンポジット用のコンフィギュレーションディスクリプタ
bNumInterfaces
を 2 にして、あとはずらずらと2つ分のディスクリプタを並べてあげるだけです。マウスは EP1 の Interrupt Transfer、キーボードは EP2 の Interrupt Transfer としました。
// For USB HID Composite Device
const uint8_t __attribute__((aligned(2))) conf_desc_keyb_mouse[] = {
// Configuration Descriptor
CONFIG_DESCRIPTOR_LENGTH,
CONFIG_DESCRIPTOR_TYPE,
59 & 0xFF, // .wTotalLength
59 >> 8, // same above
2, // .bNumInterfaces
1, // .bConfigurationValue,
0, // .iConfiguration
0x80, // .bmAttributes
0x32, // MaxPower
// Interface Descriptor(Mouse)
INTERFACE_DESCRIPTOR_LENGTH,
INTERFACE_DESCRIPTOR_TYPE,
0, // bInterfaceNumber
0, // bAlternateSetting
1, // bNumEndpoints
0x03, // .bInterfaceClass
0x01, // .bInterfaceSubClass(Boot Interface Subclass)
0x02, // bInterfaceProtocol(Mouse)
0, // iInterface
// HID Class Descriptor
0x09, // bLength
0x21, // bDescriptorType
0x00, // bcdHID
0x10, // L> 1.00
0, // bCountryCode
1, // bNumDescriptors
0x22, // bDescriptorType
50, // wDescriptorLength(The length of Report Desc)
0x00,
// Endpoint Descriptor
ENDPOINT_DESCRIPTOR_LENGTH,
ENDPOINT_DESCRIOTPR_TYPE,
0x81, // .bEndpointAddress(IN, EP1)
0x03, // Interrupt Transfer
64, // .wMacPacketSize
0,
0x0A, // bInterval
// Interface Descriptor(Keyboard)
INTERFACE_DESCRIPTOR_LENGTH,
INTERFACE_DESCRIPTOR_TYPE,
1, // bInterfaceNumber
0, // bAlternateSetting
1, // bNumEndpoints
0x03, // .bInterfaceClass
0x01, // .bInterfaceSubClass(Boot Interface Subclass)
0x01, // bInterfaceProtocol(KeyBoard)
0, // iInterface
// HID Class Descriptor
0x09, // bLength
0x21, // bDescriptorType
0x00, // bcdHID
0x10, // L> 1.00
0, // bCountryCode
1, // bNumDescriptors
0x22, // bDescriptorType
63, // wDescriptorLength(The length of Report Desc)
0x00,
// Endpoint Descriptor
ENDPOINT_DESCRIPTOR_LENGTH,
ENDPOINT_DESCRIOTPR_TYPE,
0x82, // .bEndpointAddress(IN, EP2)
0x03, // Interrupt Transfer
64, // .wMacPacketSize
0,
0x0A // bInterval
};
また、キーボードはレポートに使うキーコードというのがあって、これで押したキーを判別します。これは ASCII 文字コードと全く異なるものなので、逐一対応表を作る必要がある気がします。キーコードはこちらを参照。
動いた
以下の動画みたいな感じです。起動時に素数表を作るシェル芸のコマンドを入力し、その後マウスポインタで星を描きます。何に使うんですかねこれ。
キーボードの実装も出来たので、気分が乗ればフットスイッチやオレオレキーボードの作成したいですね。例えば
- コピペ用スイッチ
- メール用ヘッダ入力スイッチ
- ○○さん\nお世話になっております。M2のxxです。\n\n表題の件につきまして、...
- 音ゲー用スイッチ
- 弐寺 Infinitas 用のコントローラとか...?マイクロスイッチが高そう
- HID ジョイスティックの実装もいいかもね
- ブログ用定型句入力スイッチ
- pre ブロック作成
- 鉄道の発車メロディースイッチ
- スイッチ入力をマイコンの GPIO に入れて、それを送るのもいいかも
夢があふれますね。僕は怠け者なので多分やりませんが、構想は立てておきましたw
〆
ということで、密かに決めていた第2の目標、コンポジットデバイスの実装があっさりと完了してしまいました!
次は CDC で USB Virtual COM Port... といきたいところですが、研究の進捗がヤヴァいのでまた今度という感じになりますね。