ひこぽんのーと

覚書と雑記です。

デバイスドライバを書いたこと

事の発端

コントローラのヘタリ

Logitec Wireless Gamepad F710の調子が悪いので、
分解メンテしてみようとしたが、特殊ネジで開けられなかった。
仕方ないので新しいのを買おうと物色していた。

最近、ゲームコントローラが高い。

ワイヤレスだったり、電池内蔵だったり、振動機能があったりして、
昔より高機能化しているのはわかるが、
1つ3000円から5000円位する。
(Sonyの純正PS4コントローラはもっと高い)
その価格が高いか安いかにかかわらず、
使ってみるまで使用感に満足できるかわからないので、
あまり高額なものは買いたくない。

というわけで、PS2のコントローラをPS3、PC用に変換できるケーブルをAmazonで買った。
ノーブランド品の為、説明書もサポートもドライバもない。(紙の箱には入ってた)
でも、送料込みで500円は安いし、PS2コントローラの使用感はよく知っている。
なにより、PS2コントローラは数があるので中古品ジャンクで格安で買える。
f:id:nagamitsu1976:20190903103426j:plain
こんなの。

安物買いの銭失い

送られてきて早速、確認。

PS3

まぁ、動く。挙動がおかしい時も稀にあるけど、使えなくはなかった。

Windows 10

HIDデバイス ゲームコントローラとして認識され、すべてのキーが正しく反応した。
ゲームでは使ってないけど、まぁ、大丈夫であろう。

Linux

HIDデバイス ゲームコントローラとして認識されるが、ボタン配置がデタラメな上、
○、×、□ボタンの信号が認識されず、反応がない。

まさに、おいおい、である。
メインで使う予定のLinuxで使えないのが痛かった。
開発社的にも世間的にも「PS3Windowsで使えるからOKでしょー」なんだろうが、
オレサマ的にはNGな品でした。

諦めきれなかった

目的が達せられなかったので、500円とはいえ損である。
Windowsでまともに使えるだけに悔しかった。
なんとか使う方法を考える日々が続いて、
PS3Windowsでは全ボタンが正しく押せることから、
信号自体はコントローラからLinuxに送られているのではないか、
という、推測のもと、デバイスドライバを作ってみるか、と思いたった。

本編

調査

ドライバなど作ったことが無いので、作り方を調べる必要があるが、
その前に、本当にコントローラの信号が届いているか調べる必要がある。

lsusb

lsusbコマンドでコントローラが認識されているか確認する。
ボタン配置がデタラメなのはわかってるので、
認識されているはずだが、今一度確認。

$ lsusb
......
Bus 002 Device 013: ID 054c:0268 Sony Corp. Batoh Device / PlayStation 3 Controller
......

Sony云々と出るのはご愛嬌。
正式に許可を得た商品ではなさそうだし、PS3で認識される為にIDを偽装しているのだと思う。

hidusb-dump

hidusb-dumpコマンドを使うと、USBデバイスからの送信データが参照できるので、
ボタンのプッシュ/リリース動作で値が変わるかどうかを観察すれば、
信号が来ているかがわかる。
このコマンドはroot権限がないと実行できない。

$ sudo usbhid-dump -a 002:013 -e all
......
002:013:000:STREAM             1567477856.950528
 01 00 00 00 08 00 64 5D 80 64 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 03 EF 14
 00 00 00 00 23 03 77 01 00 02 00 02 00 01 80 02
 00
......

こんな感じでデータが流れ続けた。
つないでいる限りデータが垂れ流されているようだ。
(ちなみに、Logitec Wireless Gamepad F710の場合、
ボタンを押したときのみデータが送信される方式だった。
どっちがいいかは、まぁ、わからんが、後者のほうがエコだよな)

この状態でコントローラのボタンを押すと、
押している間だけバイト値が変わることが
全ボタンで確認できたので、信号は来ている、という確証を得た。

コーディング

前置きが長くなったけど、
Arch Linux上で動く上記コントローラ用のドライバを書いた。
この記事を参考に、ほぼ丸パクリの形で作った。

Linuxでペンタブモジュールを作成しよう。 - memomuteki

このサイトでは対象がペンタブレットだったので、
ゲームパッド用の定義については、別途、こっちのページを参考にした。
また、定数なんかはlinux/input.hを直接見た。

4. Linux Gamepad Specification — The Linux Kernel documentation
Linux Input Subsystemの使い方

USBデバイス カーネルドライバの作り方は割と情報量が多く、
上記以外にも参考になるサイトはいくつかあった。

運用

ロード問題

参考サイトではドライバのロードを以下の手順で行っていた。

  1. usbhidドライバのデタッチ
  2. 自作ドライバの削除(すでにロードされている場合)
  3. 自作ドライバの登録

開発時はこれで問題ないが、
PCを再起動したり、コントローラを抜き差しする度に、
ドライバのロードを行わければならず、日常的な運用では不便すぎる。

modprobe

カーネルドライバを自動ロードする仕組みにmodprobeがあるので、
自作ドライバをmodprobeに登録して自動的にロードするように設定する。
以下、Arch Linuxの場合。

  1. /lib/modules/extramodules-ARCH/にモジュールを置く。
  2. /etc/modprobe.d/blacklist.conf*1に定義を書く。
  3. /etc/mkinitcpio.confのFILEの項に2.のパスを追加する。
  4. depmod -aを実行する。
  5. mkinitcpio -p linuxを実行してinitcpioを再作成する。
  6. 再起動
/etc/modprobe.d/blacklist.conf
/etc/modprobe.d/blacklist.conf
......
# vendor_id:product_id: HID_QUIRK_IGNORE = 4,……
options usbhid quirks=0x054c:0x0268:0x04
......

これを書かないとusbhidドライバがロードされてしまい、
自作ドライバが適用されないので必ず作成する。

/etc/mkinitcpio.conf
/etc/mkinitcpio.conf
......
# FILES
# This setting is similar to BINARIES above, however, files are added
# as-is and are not parsed in any way.  This is useful for config files.
FILES=(/etc/modprobe.d/blacklist.conf)
......

ここにblacklistの定義を追加しないと、blacklistが有効にならない。

結果

コントローラをつなぐ度に自作ドライバがロードされ、
いつでも正しく使えるようになった。

オチ

実は約20年くらい前に買った
PS2 -> PC用 USBジョイスティック変換コネクタをまだ持っていた。
コイツは出来が悪くストレスフルだったので、ずっと使ってなかったのだが、
同じようにドライバを作って使ってみたら、すこぶる快適な動きになった。*2
そのようなわけで、今回買った変換コネクタは引き出しの奥に行くことになり、
結局、500円の無駄な出費になった。

おあとがよろしいようで。

*1:ファイル名は何でもいい

*2:むろん、動作の調整もしたけどね