MVPen (Pegasus Mobile NoteTaker) の解析 (4)
MVPen をLinux から使う試みシリーズ。Linux で HID デバイスを操る方法について。
※注意※ MVPen が故障する可能性があります
Linux の USB (HID) インタフェース
Linux からアクセスできる USB (HID) まわりへのデバイスノードのパスには下記のようなものがあります。
/sys/bus/usb/devices/*
- 詳細不明
/sys/class/usb*/*
- 実質下記の
/dev
デバイスへのリンクと思われる
- 実質下記の
/dev/bus/usb/*/*
- 一番ローレベルの USB としてのデバイスインタフェース
ioctl()
インタフェース
/dev/usb/hiddev*
/dev/input/*
sysfs 経由のインタフェースについて詳細がわからないので,/dev
デバイスノードインタフェースについてそれぞれ簡単に説明します。
/dev/bus/usb/*/*
インタフェース
/dev/bus/usb/*/*
を操る ioctl()
インタフェースの詳細についてドキュメントが見当たらなかったのですが,libusb の Linux port はこのインタフェースを下位で使っています((なので libusb のソースを読むと /dev/bus/usb/*/*
の ioctl()
プロトコルについて,ある程度わかります。))。素直にこれを使うべきでしょう。
さらに libusb を下位とする libhid なるライブラリもあります。つまりこいつは後述する /dev/usb/hiddev*
インタフェースを使っているわけではありません。
/dev/usb/hiddev*
インタフェース(hiddev インタフェース)
汎用 HID デバイス用のドライバで作成されるインタフェースです。詳しくは後述します。
「Linux HID」で検索して最初のほうにでてくる http://www.frogmouth.net/hid-doco/linux-hid.html に一応の説明はありますが,ほとんど内容はありません。Linux カーネルドキュメントの usb/hiddev.txt
とヘッダファイルである linux/hiddev.h
を読み解く必要があります。それにしても情報量は少ないので,あとは試行錯誤をくりかえして理解することになります。
また,先達の Network UPS Tools の nut-2.0.5/drivers/hidups.c も参考になります*1。
/dev/input/*
インタフェース
マウス,キーボードなどの入力デバイス向けの抽象化された(といっても所詮カーネルインタフェースなので高度な API があるわけではありませんが)インタフェースです。
前述の http://www.frogmouth.net/hid-doco/linux-hid.html に説明があります。
MVPen ではこのインタフェースは使わないので省略します。
hiddev インタフェースを使ううえで理解しておくべきこと
ほんとうは最初 libhid を使って MVPen をあやつろうと思っていたのですが,うまくいかなかったので hiddev インタフェースを使うことにしました。libhid 側でなにかおかしなことになっているようですが,libusb 自身に問題はなさそうだったので,余力があれば libusb でコードを書きたいと思います。そのほうが portable になりますしね。
さてさて,hiddev インタフェースについて。なるべく単純な API にするべくこうなったのかもしれませんが,一般的な HID 用 API にはない概念があります。それが「field」と「usage index」です。
たとえば,下記のような Report Descriptor があるとします。
struct MousePointerReport { /* Report Size: 8 Report Count: 1 Usage: 0x38 (Wheel) Input */ byte usage38; /* Wheel */ /* UsagePage: Generic Desktop */ /* Report Size: 16 Report Count: 2 Usage: 0x30 (X) Usage: 0x31 (Y) Input */ word usage30; /* X */ word usage31; /* Y */ /* UsagePage: Button */ /* Report Size: 1 Report Count: 8 Usage Minimum: 0x01 (Button1) Usage Maximum: 0x08 (Button8) Input */ bit:1 usage01; /* Button1 */ bit:1 usage02; /* Button2 */ bit:1 usage03; /* Button3 */ bit:1 usage04; /* Button4 */ bit:1 usage05; /* Button5 */ bit:1 usage06; /* Button6 */ bit:1 usage07; /* Button7 */ bit:1 usage08; /* Button8 */ /* UsagePage: Vendor */ /* Report Size: 8 Report Count: 16 Usage: 0x01 (Vendor#1) Input */ byte usage01[16]; /* Vendor#1 x 16 */ };
これの number of fields はいくつでしょうか?
1 + 1+1 + 8 + 16 = 27 ?
こたえは,4 です。端的にいうと,Input
(や Output
)がある数だけ field があることになります。そしてその field の中の各 Usage Data を usage index で指定することになります。
各フィールドについて個別に見ていきましょうか。
/* Report Size: 8 Report Count: 1 Usage: 0x38 (Wheel) Input */ byte usage38; /* Wheel */
説明するまでもありませんね。8 bit×1つのデータ(Usage)を定義しています。これはこのレポートのなかで独立した1つのフィールドとして扱われます。
/* UsagePage: Generic Desktop */ /* Report Size: 16 Report Count: 2 Usage: 0x30 (X) Usage: 0x31 (Y) Input */ word usage30; /* X */ word usage31; /* Y */
Usage をいくつか指定したあとに Input がくると,各 Usage のデータを定義していることになります。これも1つのフィールドです。usage index = 0 が Usage#0x30(X),usage index = 1 のデータが Usage#0x31(Y)になります。
/* UsagePage: Button */ /* Report Size: 1 Report Count: 8 Usage Minimum: 0x01 (Button1) Usage Maximum: 0x08 (Button8) Input */ bit:1 usage01; /* Button1 */ bit:1 usage02; /* Button2 */ bit:1 usage03; /* Button3 */ bit:1 usage04; /* Button4 */ bit:1 usage05; /* Button5 */ bit:1 usage06; /* Button6 */ bit:1 usage07; /* Button7 */ bit:1 usage08; /* Button8 */
マウスのボタンやゲームパッドのボタンのように同じ用途のデータが数多くある場合,さきほどのように Usage をいくつも連ねていってもいいのですが,指定子が長くなってしまいますし面倒です。なので,「Usage Minimum」と「Usage Maximum」を指定すると,その範囲の Usage Type に対応する Usage Data が定義されるようになっています。このような Report Descriptor の場合でもフィールドとしては1つになります。
/* UsagePage: Vendor */ /* Report Size: 8 Report Count: 16 Usage: 0x01 (Vendor#1) Input */ byte usage01[16]; /* Vendor#1 x 16 */
あるいは単一の Usage に対応するデータが,上記のような「配列」のようになっている場合があります。この場合だと 8 bit×16個のデータです。この場合でもフィールドは1つになります。usage index は各配列のインデックスに対応します(これまでと異なり,usage code はすべて同じになります)。
これらの field や usage index という概念は,HIDIOCGUSAGE
や HIDIOCSUSAGE
で使われる struct hiddev_usage_ref にでてきます。ヘッダファイルより抜粋します。
struct hiddev_usage_ref {
__u32 report_type;
__u32 report_id;
__u32 field_index;
__u32 usage_index;
__u32 usage_code;
__s32 value;
};
このように,各 (Usage) Data に対して,field_index
と usage_index
(と usage_code
)が定義されていることがわかります。具体的な使い方は次回以降説明します。
本質的ではないことで長くなってしまいました。なんとかお盆休みまでには終えたいのですが。
*1:残念ながら?trunk バージョンでは libhid & libusb を使うように変わったぽいのですが