このメモは、ある USB デバイス (BT ドングル) に不要なドライバ (usbfs) が bind されてしまう問題を調査する過程のメモだ。肝心の問題はまだ解決していない。udev の kmod load $env{MODALIAS}
以外でドライバが bind される事が分かったら続きを書く。
lsusb による接続状態の表示
Linux から見ると USB デバイスは以下のような情報を持つらしい。
- Linux マシンは複数の Bus を持つ。
- 各 Bus は Port を持つ。
- 各 Port がさらに Port を持つ事もある。
- USB デバイスは各 Bus ごとに一意の Device を持つ。
- USB デバイスごとに vendor:product の形でデバイスを特定する ID を持つ。
- USB デバイスは複数の If を持つ。
- 各 If ごとにデバイスの機能を特定する Class がある。
Linux マシンに繋がった USB 機器の物理的な接続状態を見るには `lsusb -t' を使う。
# lsusb -t
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 2: Dev 3, If 0, Class=Hub, Driver=hub/7p, 12M
|__ Port 1: Dev 4, If 0, Class=Wireless, Driver=usbfs, 12M
|__ Port 1: Dev 4, If 1, Class=Wireless, Driver=, 12M
|__ Port 2: Dev 5, If 1, Class=Wireless, Driver=, 12M
|__ Port 2: Dev 5, If 0, Class=Wireless, Driver=usbfs, 12M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/6p, 480M
|__ Port 1: Dev 2, If 0, Class=Video, Driver=uvcvideo, 480M
|__ Port 1: Dev 2, If 1, Class=Video, Driver=uvcvideo, 480M
これで以下の事が分かる。
- このマシンには Bus 01 と Bus 02 の二つの USB Bus が接続されている。
- 多分この Bus というのは USB バスコントローラに対応している(要調査?)
- Bus 02 は Port 1 と Port 2 を持つ。
- 多分この Port というのは USB ポートに対応している(要調査?)
- Bus 02 の Port 2 は Port 1 と Port 2 を持つ。
- Bus 02 の Port 2 の Port 2 には Dev 5 が接続されている。
- Dev というのはデバイスを抜き差しするたびに変わってゆく Device 番号。Bus ごとに一意に定まる。
- Bus 02 の Dev 5 は If 1 と If 0 を持つ。
- Bus 02 の Dev 5 の If 1 の Class は Wireless である。
- 多分この Wireless はクラスデバイスの事。
また lsusb
コマンドでもう少し整理された情報が出る。
# lsusb
Bus 002 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Bus 001 Device 002: ID 0e0f:000b VMware, Inc.
Bus 002 Device 004: ID 0e0f:0008 VMware, Inc.
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
lsusb -t
では現れなかったがデバイスを特定する ID が vendor:product の形で表示される。
これらの Bus や Device を使って表示するデバイスを特定出来る。
# lsusb -s 002:006 # Bus とDevice が当てはまる情報だけを表示
# lsusb -D /dev/bus/usb/002/006 # デバイスファイル /dev/bus/usb/(Bus)/(Device) から詳しく表示
# lsusb -d 0a5c:21e8 # vendor:product が当てはまる情報だけを表示
sysfs による接続状態の表示
Linux では、sysfs というのでデバイスの状態をファイルシステム経由で見ることが出来る。
USB デバイスの表示
USB デバイスは /sys/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/ のような感じのややこしい所に現れるが、/sys/bus/usb/devices からリンクされている。
# ls -l /sys/bus/usb/devices
total 0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 1-0:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-0:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-0:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-0:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-1:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.1:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.1:1.1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.1
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.2 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.2
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.2:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.2/2-2.2:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2.2:1.1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.2/2-2.2:1.1
lrwxrwxrwx 1 root root 0 Oct 24 12:21 2-2:1.0 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2:1.0
lrwxrwxrwx 1 root root 0 Oct 24 12:21 usb1 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1
lrwxrwxrwx 1 root root 0 Oct 24 12:21 usb2 -> ../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2
このうち :
がついているリンクが実際のデバイスに対応している。書式は bus-port.port:config.interface
のようになっているらしい。これであるデバイスを制御しているドライバが分かる。以下の例では 2-1:1.0 (Bus 2, Port 1, Config 1, Interface 0) を制御しているドライバは usbhid という事が分かる。
# ls -l /sys/bus/usb/devices/2-1\:1.0/driver
lrwxrwxrwx 1 root root 0 Oct 24 12:21 /sys/bus/usb/devices/2-1:1.0/driver -> ../../../../../../../bus/usb/drivers/usbhid
USB ドライバの表示
USB ドライバは /sys/bus/usb/drivers に現れる。
# ls /sys/bus/usb/drivers
btusb cp210x ftdi_sio hdm_usb hub k2l-mocca pl2303 usb usb-storage usbfs usbhid usbserial usbserial_generic
このディレクトリを見ると、あるドライバがどのデバイスを制御しているか分かる。この例では usbhid が 2-1:1.0
すなわち Bus 2, Port 1, Config 1, Interface 0 を制御している。
# ls -l /sys/bus/usb/drivers/usbhid/
total 0
lrwxrwxrwx 1 root root 0 Oct 24 12:24 2-1:1.0 -> ../../../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0
--w------- 1 root root 4096 Oct 24 12:24 bind
lrwxrwxrwx 1 root root 0 Oct 24 12:24 module -> ../../../../module/usbhid
-rw-r--r-- 1 root root 4096 Oct 24 12:24 new_id
-rw-r--r-- 1 root root 4096 Oct 24 12:24 remove_id
--w------- 1 root root 4096 Oct 24 12:21 uevent
--w------- 1 root root 4096 Oct 24 12:24 unbind
udev によるドライバの自動登録
USB の接続を検知してドライバを割り当てるために udev という仕組みが使われている。udev はカーネルからデバイスイベントを受け取り以下を行う。
- ネットワークインタフェースの名前を変更する
- デバイスノードへの symlink を作成する
- あるプログラムを実行する
udev ルールは以下に従って作成する
- ルールの位置は優先順に以下のどこかに置く。同じ名前のルールがあると優先順序の高いルールだけが有効になる。
- /etc/udev/rules.d
- /run/udev/rules.d
- /lib/udev/rules.d
- ルールのファイル名は必ず .rules で終わる。
- ルールは置かれたディレクトリに関わらず名前でソートされてから読み込む。
-
はコメント
- 一行に , で区切って複数のマッチや代入を書く。
- マッチには
==
と!=
がある。一行の全てのマッチが成功すると代入が実行される。 - 代入には
=
,+=
,-=
,:=
がある。
udev にどんなイベントが来るのかを調べる
udevadm monitor
で実際にどんなデバイスイベントが来るのかを調べる事が出来る。以下の例では、BT ドングルを刺したイベントを観察している。
# udevadm monitor
KERNEL[1607.753652] add /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1 (usb)
KERNEL[1607.816654] add /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0 (usb)
KERNEL[1607.825478] add /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0 (bluetooth)
KERNEL[1607.825506] add /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0/rfkill6 (rfkill)
最初の行は以下のような意味
-
KERNEL
: KERNEL と UDEV の二種類がある -
[1607.753652]
: 多分タイムスタンプ -
add
: action を表す、add, remove, change がある -
/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1
: devpath を表す。sysfs 中でデバイスを表すパス -
(usb)
: subsystem を表す、usb, bluetooth など
ルールの中で使う変数は udevadm monitor -p
で観察出来る。全部見ると量が多くて大変なので、subsystem 等でフィルタすれば良い。
# udevadm monitor -p -s bluetooth
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
KERNEL[2265.779078] remove /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0 (bluetooth)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0
DEVTYPE=host
SEQNUM=4437
SUBSYSTEM=bluetooth
UDEV [2265.797035] remove /devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0 (bluetooth)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0
DEVTYPE=host
SEQNUM=4437
SUBSYSTEM=bluetooth
SYSTEMD_ALIAS=/sys/subsystem/bluetooth/devices/hci0
SYSTEMD_WANTS=bluetooth.target
TAGS=:systemd:
USEC_INITIALIZED=1607841223
Modalias
udev ではデバイスの情報をコンパクトに表現するために、Modalias という物を使う。Modalias は sysfs 経由 で取得出来る。例えば 2-2.2:1.0 (USB Bus 2, Port 2, Port2, Config 1, Interface 0) の Modalias は以下で取得する。
# cat /sys/bus/usb/devices/2-2.2\:1.0/modalias
usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00
まるで暗号のようだが、Modalias は英小文字を区切りになっている。drivers/usb/core/sysfs.c によると以下のように読める。
- usb: サブタイプを表す
- v0A12 (device vendor: Cambridge Silicon Radio, Ltd)
- p0001 (device product: Bluetooth Dongle (HCI mode)
- d8891 (bcc device:)
- dcE0 (device class: Wireless)
- dsc01 (device subclass: Radio Frequency)
- dp01 (device protocol: Bluetooth)
- icE0 (interface class: Wireless)
- isc01 (interface subclasss: Radio Frequency)
- ip01 (interface protocol: Bluetooth)
- in00 (interface)
この Modalias を人間でも読める情報に分解するために hwdb データベースが使われる。hwdb の設定ファイルは例えば /lib/udev/hwdb.d/20-usb-classes.hwdb あたりに入っている。文法は読めば分かるがこの例だと usb:v*p*d*dcE0dsc01dp01*
の行にマッチして
usb:v*p*d*dcE0dsc01dp01*
ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth
属性の一つとして ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth
が得られる。
systemd-hwdb を使うと hwdb を検索して Modalias から 属性を得られる。
# systemd-hwdb query usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00
ID_USB_CLASS_FROM_DATABASE=Wireless
ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency
ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth
ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio, Ltd
ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HCI mode)
udevadmin による udev のテスト
udevadmin
を使って udev の動作をテスト出来る。
# udevadm test /sys/bus/usb/devices/2-2.2:1.0
...
IMPORT builtin 'hwdb' /lib/udev/rules.d/50-udev-default.rules:15
RUN 'kmod load $env{MODALIAS}' /lib/udev/rules.d/80-drivers.rules:5
created db file '/run/udev/data/+usb:2-2.2:1.0' for '/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.2/2-2.2:1.0'
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.2/2-2.2:1.0
DEVTYPE=usb_interface
DRIVER=usbfs
ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HCI mode)
ID_USB_CLASS_FROM_DATABASE=Wireless
ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth
ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency
ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio, Ltd
INTERFACE=224/1/1
MODALIAS=usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00
PRODUCT=a12/1/8891
SUBSYSTEM=usb
TYPE=224/1/1
USEC_INITIALIZED=18792432
run: 'kmod load usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00'
Unload module index
Unloaded link configuration context.
udev builtin command のテストも出来る。
# udevadm test-builtin "kmod load usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00" /sys/bus/usb/devices/2-2.2:1.0
calling: test-builtin
=== trie on-disk ===
tool version: 230
file size: 6948766 bytes
header size 80 bytes
strings 1781678 bytes
nodes 5167008 bytes
Load module index
Found container virtualization none
timestamp of '/etc/systemd/network' changed
timestamp of '/lib/systemd/network' changed
Parsed configuration file /lib/systemd/network/99-default.link
Created link configuration context.
Execute 'load' 'usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00'
Inserted 'btusb'
Inserted 'btusb'
Unload module index
Unloaded link configuration context.
ドライバの割り当て
ドライバの機能は usb_driver 構造体で定義されている。例えば btusb.c の定義は以下のようになっている。
static struct usb_driver btusb_driver = {
.name = "btusb",
.probe = btusb_probe,
.disconnect = btusb_disconnect,
#ifdef CONFIG_PM
.suspend = btusb_suspend,
.resume = btusb_resume,
#endif
.id_table = btusb_table,
.supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(btusb_driver);
定義した usb_driver
は module_usb_driver
によってシステムに登録する。
重要なメンバ
- name: ドライバの名前
- prove: カーネルがデバイスにドライバを割り当てる時に呼ぶコールバック。成功すれば 0 を返す。
- disconnect: カーネルが切断される時に呼ばれる。