前回の記事ではレポートディスクリプタの大まかな見方を学びました。それでは今回はマウスのレポートディスクリプタを改造して、その構成について実践的に学びましょう。
マウスのレポートディスクリプタ・レポート
まず、マウスのレポートディスクリプタをご覧いにれましょう。見やすいように改行とコメントを入れています。
const uint8_t __attribute__((aligned(2))) ReportDescriptorForMouse[50] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
// Byte 0, bit 0~2 :: Mouse Buttons
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
// Byte 0, bit 3~7: Constant(0)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
// Byte 1, displacement X
0x09, 0x30, // USAGE (X)
// Byte 2, displacement Y
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
レポートは 3bytes で、1byte 目に X 変位、2byte 目に Y 変位を代入して送信すれば良い、とわかります。今回は簡単のためにマウスボタンは無視しておきます。
なので、ゆっくりとマウスを下に動かしたいときはこんなコードを書けば良いのです。
__attribute__((aligned(2)))
volatile uint8_t report[3];
void usb_hid_mouse(void) {
...
report[0] = 0;
report[1] = 0;
report[2] = 5; // Y方向に5だけ移動
}
そうすると、こんな感じで動きます↓
改造しよう
では、ここで X 変位と Y 変位を入れ替えたいと思います。通常は usb_hid_mouse
関数内で 1byte 目と 2bytes 目を入れ替えれば良いのですが、今回は素直にそうせず、レポートディスクリプタを改造してみたいと思います。
const uint8_t __attribute__((aligned(2))) ReportDescriptorForMouse[50] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
// Byte 0, bit 0~2 :: Mouse Buttons
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
// Byte 0, bit 3~7: Constant(0)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
// Byte 1, displacement Y
0x09, 0x31, // USAGE (Y)
// Byte 2, displacement X
0x09, 0x30, // USAGE (X)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
これで動かしてみましょう。確かに横方向に動いています。
〆
ということで、検証回でした。前の記事で「マウスのレポートディスクリプタは必ず同じ構成でなければならないのか?」と言った気がしますが、答えは必ずしもレポートディスクリプタの順番は同じである必要はないということでした。つまりレポートディスクリプタの記述どおりにデータの受信および処理が行われると考えて良さそうです。
これでまた一つ USB について賢くなることが出来ました。多分今後もしばらく USB ネタばかりを投下すると思いますが、しばらくお付き合いください。