はじめに
デバイス認識時になんやかんやしたい時。通常はudevで色々やればいい話なんですが搭載されていない環境(Androidとか)での制御方法について
せっかくdriverのコードを読んだので覚書
driver動作の流れをざっくり
-
module_init
でdriverの処理を登録 -
module_exit
でdriverの処理を削除(デストラクタのようなイメージ) - その他driverの種類や用途によって登録処理の内容が異なる
- USBデバイスなら
module_usb_driver
とか、module_XXX_driver
が多そう - これらで関数ポインタを登録して、対応するドライバのアクセスがあったら関数が呼ばれる
- USBデバイスなら
特に登録する関数ポインタの中で.probeがデバイス認識時に呼ばれます。
オブジェクト指向の言葉で言い換えると、
module_init
⇒コンストラクタ
module_exit
⇒デストラクタ
module_XXX_driver
で使う構造体(関数ポインタの定義)⇒インターフェースクラス
module_XXX_driver
⇒インターフェースクラス実装
って感覚が近い気がします。例えばUSBデバイス系のdriverならusb_driver
インターフェース(struct usb_driver
)で、以下のように関数を代入することでメソッドを定義する感じ。
static struct usb_driver ax88179_178a_driver = {
.name = "ax88179_178a",
.id_table = products,
.probe = usbnet_probe,
.suspend = ax88179_suspend,
.resume = ax88179_resume,
.reset_resume = ax88179_resume,
.disconnect = usbnet_disconnect,
.supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
};
というわけで、デバイス認識時で何かしたい⇒デバイス認識時に呼ばれる.probe
でなんやかんやすればいいというわけです。
デバイス認識時にインターフェース制御したい
netdevice.hのint dev_change_flags(struct net_device *, unsigned int);
を使います。
第一引数はdriverの関数ポインタの引数で渡ってくるdev構造体を利用。USBデバイスドライバの場合の引数はstruct usb_interface *udev
なので、中身を参照する必要があります。
usbnet.cを参考に、こんな感じでnet_deviceを取り出します。
struct usbnet *dev = usb_get_intfdata(intf);
struct net_device * net = dev->net;
後はflagを弄るだけ。flagの定義はioctl、netdeviceのflagと同じです。
今のflagはdev->flags
に詰まっているので、そちらにflag追加するだけ。例えばIFF_UPをor演算で追加すればデバイスがupします。
unsigned int flags = dev->flags;
int ret;
ret = dev_change_flags(dev, flags | IFF_UP);
これで完了。と思いきや
何も考えずにこちらを実行するとこんなエラーログが出ます。RTNL: assertion failed at
。
ログの原因はASSERT_RTNL
。!rtnl_is_locked()の条件に引っかかっています。エラー要因としてはrtnl_lock
してないから。rtnl_lock
、rtnl_unlock
を実行してあげる必要があるようです。
unsigned int flags = dev->flags;
int ret;
rtnl_lock();
ret = dev_change_flags(dev, flags | IFF_UP);
rtnl_unlock();
ただ他のdriverコードを見ると、例えばlinux-4.9/drivers/net/vrf.c
なんかはrtnl_lock()
呼んでないケースもあったり。
どこでlockを読んでるのか、コードを読んだ上で追加が必要ですね。
参考
linux-4.9 driverコード
linux-4.9.tar.gz、
kernelのdriverコード:linux-4.9/drivers/net/usb/usbnet.c, linux-4.9/drivers/net/vrf.c等