USBデバイスとしてのタブレット

卒研で諦めたお絵描きソフトへのリベンジということで。
卒研の時点ではWinTabを使用していたが、今回はUSBデバイスとしてのタブレットを直接libusbで制御するという方向性とした。
使用しているタブレット(Favo4)はUSB-HIDクラスなので、各種デスクリプタをまず観察する。
http://storage.osdev.info/pub/idmjt/diaryimage/0810/neta081025l1.txt
このリストはlsusb -vで得られるが、通常、カーネルドライバがattachされている状況、要するに通常の使用状況ではレポートデスクリプタを観察することができない。
というのも、レポートデスクリプタは通常のデスクリプタではなく、クラス独自のデスクリプタで、クラス独自の機能性はアタッチされているドライバにのみ提供されているため。
幸い、LinuxはUSBに対するカーネルドライバを任意のタイミングで(しかもインターフェース単位で)detachできるため、何らかの手段でdetachすれば良い。
libusbはその目的に使用するusb_detach_kernel_driver_npを備えている。

プログラムを書くのが面倒ならlibhidによって提供されるlibhid-detach-deviceを使えば良いが、通常のシチュエーションではこの機能性は自身のアプリケーションにも含めたいだろう。
パケットフォーマットの詳細はlinux-wacomカーネルモジュールwacom_sys.c*1を参照する。レポートデスクリプタは残念ながらあまり有効な情報を与えない。
簡単に言えば、favo4は以下の情報を提供する。

  • (先頭にリポートIDの"2"。1バイト。)
  • 16bitのX(0-16704),Y(0-12064)*2
  • 9bitの筆圧(0-511)
  • 3bitのホイール(-3-+3) - これだけがrelativeであり、読み取りの度にリセットされる。
  • 1bitの各種ボタンと接触判定
  • 1bit?のペン先と消しゴムの判定

libusbによる読み取り

タブレットを読むための作業は2つある。

正直WinTabの300倍は簡単だと思う。もちろん、実際のレポートのデータフォーマットは特定のタブレットに依存することになるため移植性の問題は生じるが。
libusbの代わりにlibhidを考慮する価値があるかもしれない。
読み取りにlibusbを使う場合の重要な考慮点としては、『8バイトぴったり読み取る必要がある』という点。というのも、USBの規格ではパケットのサイズを積極的に提供する方法が無く、事前にホスト側がパケットサイズに関する知識を持つことを前提とした仕組みになっている。
もし、usb_bulk_readのような関数に8以上のサイズを渡したのなら、そのサイズを満たすまで関数はブロックすることになる*3
Bootプロトコルとは簡略化したパケットを送信するモードで、主にBIOS(のUSBデバイスエミュレーション)によって使用されることを期待している。もしXやWindowsタブレットドライバが存在していて、制御を横取りしたり、BIOS以外によるリセットを行うようなシチュエーションであればプロトコルの切り替えを行わなくても最初からReportプロトコルでパケットを送信してくる公算が高い。

*1:Xのモジュールは、カーネルモジュールによってパースされたデータを対象に動作するため、デバイスを直接操作する気があるならカーネルモジュールを読む必要がある。しかし、パケットを目で見て解析するのも十分に容易だろう。

*2:当然、最大値はタブレットのサイズに依存する。

*3:もっとも、多くのデバイスは『ある一定未満のサイズのパケットをデータ終端と見なす』というルールを持っているので、例えばNICのようなUSBデバイスでは受信するデータサイズを予期する必要は無い。