udev (3)

キャラクタデバイスのメジャー番号の登録処理について。

登録手順

以下な手順で登録との事。

  1. alloc_chrdev_region() でメジャー番号の動的確保
  2. cdev_init() でシステムコールハンドラを登録
  3. cdev_add() でカーネルへのドライバの登録

との事。コードも見つつ順にチェックしてみます。

	alloc_ret = alloc_chrdev_region(&dev, 0, devone_devs, "devone");
	if(alloc_ret)
		goto error;
	devone_major = major = MAJOR(dev);

alloc_chrdev_region に渡す引数については

  1. 上記 dev に確保されたデバイスのメジャー番号とマイナー番号が格納される
  2. 第二引数はベースとなるマイナー番号を指定
  3. 第三引数は必要なマイナー番号の個数を指定
  4. 第四引数はドライバの名前を渡す

との事。戻り値としては 0 が正常でそれ以外は異常。次は cdev_init 手続きで呼び出し例は以下。

	cdev_init(&devone_cdev, &devone_fops);
	devone_cdev.owner = THIS_MODULE;
	devone_cdev.ops = &devone_fops;

cdev_init 手続きは第一引数の cdev 構造体の初期化とハンドラの登録との事。上の例で言えば devone_cdev という変数はドライバのアンロード時に必要となるため、グローバル変数として定義しておく必要があるとの事。引用している udev な例でもグローバル変数として定義されています。
THIS_MODULE の定義なんですが、include/linux/module.h の中で以下かな。

#define MODULE_GENERIC_TABLE(gtype,name)			\
extern const struct gtype##_id __mod_##gtype##_table		\
  __attribute__ ((unused, alias(__stringify(name))))

extern struct module __this_module;
#define THIS_MODULE (&__this_module)

ちょっとこの THIS_MODULE の意味不明です。あと、devone_cdev 構造体の ops 属性にハンドラな構造体のアドレスを設定しておりますな。ひらたさん本lによれば、cdev_init の呼び出し後に owner 属性には THIS_MODULE を代入せよ、との記述あり。
次は cdev_add ですが呼び出し例としては以下。

	cdev_err = cdev_add(&devone_cdev, MKDEV(devone_major, devone_minor), 1);
	if(cdev_err)
		goto error;

カーネルへの登録については

  • 第二引数は確保したメジャー番号とベースになるマイナー番号を MKDEV したものを渡す必要がある模様
  • 第三引数はドライバが取り扱うデバイスの個数を渡すとの事

これも成功すると 0 を戻す模様。

登録解除の手順

アンロードする時はロードの逆の手順との事。

  1. cdev_del() でカーネルからドライバの登録を解除
  2. unregister_chrdev_region() でメジャー番号の削除

という事で登録解除の例を以下に。

	cdev_del(&devone_cdev);
	unregister_chrdev_region(dev, devone_devs);

これ、第一引数には MKDEV(devone_major, devone_minor) の方が良さげ。ちなみに unregister_chrdev_region の第二引数は確保したマイナー番号の個数との事。

udev 対応

こちらも確認。登録時にやる事は二点な模様。

  • ドライバのクラス登録
  • /sys/class 配下にドライバ情報を作成

クラス登録の例は以下かな。

	devone_class = class_create(THIS_MODULE, "devone");
	if(IS_ERR(devone_class)) {
		goto error;
	}

ここでも第一引数は THIS_MODULE ですな。このあたりはちょっと調べる必要があるかもしれませんが、当座は固定って考えてても良いのだろうか。
ええと第二引数は「クラス名」との事。この関数呼び出しが成功したら /sys/class/devone なディレクトリが作られるのか。
あと戻りは例示してある通り、IS_ERR なマクロで判断してね、との事。
次は /sys/class/devone/デバイス名を作るために class_device_create() 手続きを呼び出すとの事。サンプルが以下。

	devone_dev = MKDEV(devone_major, devone_minor);
	class_dev = class_device_create(
		devone_class,
		NULL,
		devone_dev,
		NULL,
		"devone%d",
		devone_minor);
  • 第一引数は直前で呼び出してるはずの class_create 手続きの戻りを指定
  • 第二引数は親クラスを指定したい場合に使う (NULL でも構わない)
  • 第三引数は /sys/class/クラス名/デバイス名/dev に記述するメジャー番号、マイナー番号を指定との事

上記、若干意味不明だったので実機で確認してみました。

# insmod devone.ko
# cd /sys/class/devone/devone0
# cat dev
252:0
#

なるほど。続きを以下に。

  • 第四引数はクラスドライバに関連付けしたい struct device があれば指定との事 (NULL でも構わない)
  • 第五引数以降は「デバイス名」との事。上記の例で言えば devone0 がそれにあたる訳か

次は登録解除。以下が例です。

	/* unregister class */
	class_device_destroy(devone_class, devone_dev);
	class_destroy(devone_class);

上記、devone_class、devone_dev についてはグローバル変数として定義されているのかな。

static struct class *devone_class = NULL;
static dev_t devone_dev;
  • class_device_destroy は登録済みのデバイス名の削除
  • class_destroy は登録済みのクラスの削除

という事になる模様。

明日以降で

ひらたさん本の 6 章以降とか ldd3 をリファレンスにしつつ pl2303 なソレを確認してみようと思ってます。