2016年2月22日月曜日

【Linux】 usb-skeleton.c moduleが動かなくて深くはまった話

今回はLinux moduleを少し弄ったこちらの記事の続報みたいなものです.

こちらの記事の【準備】に書いたようにしてkernel sourceをinstallすると、usb module開発の練習用のファイルが中に入っています.(わたしの現環境はKonalinux3.0-64bit kernel-3.16です)
その練習ファイルはここにあるusb-skeleton.cという名前です.
/usr/src/linux-source-3.16/drivers/usb/usb-skeleton.c
これをコンパイルするとできるのはusb-skeleton.koというmoduleです.こいつをkernelに組み込むと、USBデバイスにBULK read/writeができますよという有難いものです.まずはこれを動かしてみようと思ったわたしであった.

ところが、これがなかなか動かなかった.

【問題】
コンパイルは出来た.kernelへの組み込みも出来た.だがしかし、たとえばユーザープログラムのopen()関数をcallしても、このusb-skeleton moduleへオーバーライドされないんですよ.替わりに、usbtest moduleへ飛んでいるらしかった.Linux moduleに全然詳しくないわたしとしては大いに悩みました.一週間ぐらい悩んでいたと思う.

【usb-skeleton.cコンパイル及びkernelへの組み込み方法】
以下はrootで作業してます.

usb-skeleton.cを一部変更します.上の方にあるこの2行です.何の数字なのかは後で説明します.
 #define USB_SKEL_VENDOR_ID      0x04b4
 #define USB_SKEL_PRODUCT_ID    0x8613

Makefileはこれです.
 obj-m := usb-skeleton.o
 all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
make一発で usb-skeleton.ko が生成されます.(Kernel sourceが必要です)

後に使うdepmodとmodprobeの都合により、systemの某所にコピーしときます.
 # cp usb-skeleton.ko /lib/modules/3.16.0-4-amd64

kernelへのmoduleの登録と削除はこれで行います.
 # insmod usb-skeleton.ko  登録
 # rmmod usb-skeleton.ko  削除

dmesgで状況確認すると、skeletonという新しいdriver(=module)が組み込まれたことを確認できます.
 # dmesg|tail
 usbcore: registered new interface driver skeleton

kernelへの組み込みはこれで完了といってよいのだろうと思いますが、もうひとつ小技が必要です.
 # depmod
これはどういう役目なのか?
USBデバイスの場合vendorID/productIDというのがあります.このVID/PIDと、特定のmoduleとの対応リストを作り直す役目です.skeletonというdriver(=module)はLinuxマシンにとって初物ですからこの作業を必要とします.

運よくスムースに進行すればこれでOKOKだったのですが、わたしはここからがドツボでした.

【ユーザープログラム】
まずは動かしてみるだけでいいので、こんな簡単なものとします.USBデバイスをopenして"1234"を送信するだけ./dev/bus/usb/003/020はわたしの環境においては、とあるUSBデバイスを挿したときにこうだった、というだけの理由です.

#include <string.h>
#include <fcntl.h>

char buf[]="1234";

void main( void )
{
  int fd;
  fd = open("/dev/bus/usb/003/020",O_RDWR );
  write(fd,buf,strlen(buf));
  close(fd);
}

【使ったUSBデバイス】
ez-usbのプリント基板です.ディスクリプタにez-usbデフォルトのVID/PID=04B4/8613をセットしてあります.上でusb-skeleton.cを修正した2行はVID/PIDのことだったのです.後でこれが諸悪の根源だったのですが.
詳細までは説明しませんが、open()関数をcallすると、usb-skeleton.cの中に記述したskel_open()がcallされます.
そのリンクの流れはこうです.
 open()をcall → /dev/bus/usb/003/020 → VID/PID=04B4/8613 →
          → skeleton module → usb-skeleton.ko → skel_open()
VID/PIDがskeleton moduleにリンクするのはdepmodのおかげなので、depmodを怠るとリンクしないのはこのせいです.

ところがわたしの場合はskel_open()が呼び出されませんでした.どこかでミスリンクが生じていました.

【udevというもの】
Linuxのkernelバージョンによって頻繁に変わっているのですが、使用中のKernel3.16では、USBのhotplugの検出等にudevというのが使われています.

hotplugには①USBデバイスの素性と、②デバイスに適合するmoduleの③対応表が必要です.
①USBデバイスの素性はぶっちゃけVID/PIDです.下手にVID/PIDを変えてしまうと認識されなくなってしまうのはそのせいです.
③対応表はdepmodで生成するのでした.

では②moduleにはどんな識別情報が格納されているのでしょうか?
ちょっと乱暴ですが、バイナリファイルのusb-skeleton.koを開いてその中にある「alias」という文字を検索すると、こんな文字が存在します.
 alias=usb:v04B4p8613d*dc*dsc*dp*ic*isc*ip*in*
ここに、usb、04b4、8613 というmoduleの適合相手が格納されています.後続の*はさらなる素性限定の余地を示唆しますが細部については知りません.

それでわたしが直面している問題は、対応表がなにかおかしいんじゃね?という状況と考えられます.

対応表をチェックしますと、、、
 # modprobe -c | grep v04B4p8613
 alias usb:v04B4p8613d*dc*dsc*dp*ic*isc*ip*in* usbtest
 alias usb:v04B4p8613d*dc*dsc*dp*ic*isc*ip*in* usb_skeleton
おやおや~? 所期のusb_skeleton moduleだけでなく、usbtest moduleがVID/PID=04B4/8613に適合していると言っている.このせいでusbtestにリンクしちゃっていると推測されました.

それならば、udevの設定ファイルであるところの、
 /lib/udev/rules.d/*.rules
をどこか書き換えることでusbtestへのリンケージを止めるコトができるのではないか?と思いました.でもそれは出来ませんでした.
udevやdepmodはさほど根源的存在ではなく、根源はxxx.koファイルそのものであるように思われました.すなわち、udevやdepmodが利用したり生成したりするデータベースファイルは、xxx.koファイルを全てなめて生成しているようでした.

だとすると、usbtest moduleそのものが怪しいかと、素性を調べてみました.
usbtest moduleはここにありました.
 /lib/modules/3.16.0-4-amd64/kernel/drivers/usb/misc/usbtest.ko
aliasを検索すると様々なデバイスへの対応が出るわ出るわ、
 alias=usb:v0525pA4A3d*dc*dsc*dp*ic*isc*ip*in*    ←なんとZaurusらしい
 alias=usb:v0525pA4A4d*dc*dsc*dp*ic*isc*ip*in*
 alias=usb:v0525pA4A0d*dc*dsc*dp*ic*isc*ip*in*
 alias=usb:vFFF0pFFF0d*dc*dsc*dp*ic*isc*ip*in*    ←fooかな?
 alias=usb:v04B4p8613d*dc*dsc*dp*ic*isc*ip*in*    ←ez-usb
 alias=usb:v0547p0080d*dc*dsc*dp*ic*isc*ip*in*     ←別のez-usb
 alias=usb:v0547p2235d*dc*dsc*dp*ic*isc*ip*in*
なんとVID/PID=04B4/8613がusbtest.koの中から見つかりました.
つまりusbtest moduleはVID/PID=04B4/8613に対応するmoduleだったのです.
usbtest moduleが何の役目なのかは知りませんが、usb-skeleton moduleでオーバーライドされるのをusbtestがブロックしていた原因はここにあったのでした.
この発見までに一週間かかったわけでした.(>_<);

usbtestもmoduleですからソースコードからPID=8613を除去して再コンパイルすりゃOKのはずですがそれはめんどくさいので却下とします.

【USBデバイスで逃げる】
VID/PID=04B4/8613である必要はありません.usbtestの呪いから逃れられればVID/PIDは何でもいいです.
ez-usbのFLASHを焼くために「USB Control Center」というwindowsアプリを使っています.windowsのデバドラは VID/PID=04B4/1004 であっても動いてます.(.infを弄ったからかも)
なので、ez-usbのVID/PID=04B4/1004に変更しました.

【usb-skeleton.cの再コンパイル】
USBデバイスのPIDを変更したのに合わせて、usb-skeleton.cもPID=1004に変更します.
 #define USB_SKEL_VENDOR_ID      0x04b4
 #define USB_SKEL_PRODUCT_ID    0x1004

再度コンパイルして、kernelに登録して、depmodして、USBデバイスを接続します.

【udev再び】
USBデバイスのPIDを変更して再接続してみたら、新しい動きがありました.

usbtestにリンクしていたと考えられるVID/PID=04B4/8613のときには、udevによって登録されるデバイスは、/dev/bus/usb/003/020 でした.奥深くて嫌なかんじ.

VID/PID=04B4/1004 に変えてみたら、/dev/skel0 が現れるようになりました.これじゃなくっちゃいけないよね.

【ユーザープログラム再び】
/dev/bus/usb/003/020 ではなく、/dev/skel0 で良くなりました.

#include <string.h>
#include <fcntl.h>

char buf[]="1234";

void main( void )
{
  int fd;
  fd = open("/dev/skel0",O_RDWR );
  write(fd,buf,strlen(buf));
  close(fd);
}

【ユーザープログラムの動作確認】
ez-usbの、EP1にて、"1234"を受信したことを確認しました.

write()で一体どこのEPにOUTされるんだろ?と不思議でしたが、結果的には宛先はEP1でした.ez-usbの設定を、EP1 OUT、EP2 IN にしてあるので、デフォルトでEP1OUTに向けて送信されたんだろうとしか今は云えません.

好みのEPにデータを送信する方法と、好みのEPからデータを受信する方法は、これから調べます.きっとurbを触る必要があるんだろう.
追記: USBデバイス接続時に自動でcallされるskel_probe()関数において、最初に見つかったEPを使うとされていました.そういうところは練習用ですね.


PID=8613にしてやられた、という悲しいお話は以上でおしまいです.

#元はioctl()について知りたいだけだったのだが発散著しいこの有様

エイメン

人気ブÍグランキングへ

0 件のコメント:

コメントを投稿