キャラクタ型デバイス
メモ
register_chrdev_region()
alloc_chrdev_region()
unregister_chrdev_region()
MKDEV()
MAJOR()
MINOR()
cdev_init()
cdev_add()
cdev_del()
cdev_alloc()
デバイス番号の割り当てと解放
ソース (hoge_chrdev.c)
とりあえずこんな感じ。
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> // printk() #include <linux/kdev_t.h> // dev_t, MAJOR(), MINOR() #include <linux/fs.h> // *_chrdev_region() #include <linux/types.h> int hoge_major_no = 30; int hoge_minor_no = 0; int hoge_num = 1; static dev_t hoge_dev; static int __init hoge_init(void) { int result; #if 0 result = alloc_chrdev_region(&hoge_dev, hoge_minor_no, hoge_num, "hoge"); if (result < 0) { printk(KERN_ALERT "hoge_init() : alloc_chrdev_region() failed\n"); return result; } hoge_major_no = MAJOR(hoge_dev); hoge_minor_no = MINOR(hoge_dev); #else hoge_dev = MKDEV(hoge_major_no, hoge_minor_no); #endif result = register_chrdev_region(hoge_dev, hoge_num, "hoge"); if (result < 0) { printk(KERN_ALERT "hoge_init() : register_chrdev_region() failed\n"); return result; } // ---- // init success printk(KERN_ALERT "Hello, hoge_chrdev!!\n"); printk(KERN_ALERT "major=%d minor=%d\n", hoge_major_no, hoge_minor_no); return 0; } static void __exit hoge_exit(void) { unregister_chrdev_region(hoge_dev, hoge_num); printk(KERN_ALERT "ByeBye, hoge_chrdev.\n"); } module_init(hoge_init); module_exit(hoge_exit);
コンパイル、モジュールのロード
モジュールを確認する
hoge_chrdevモジュール、ロードされてます。
$ lsmod Module Size Used by hoge_chrdev 6024 0 nfsd 212868 13 exportfs 9600 1 nfsd lockd 61320 2 nfsd nfs_acl 7680 1 nfsd sunrpc 145340 9 nfsd,lockd,nfs_acl ipv6 252768 26 autofs4 22660 2 ipt_REJECT 9344 1 xt_state 6144 10 ip_conntrack 52568 1 xt_state nfnetlink 10520 1 ip_conntrack xt_tcpudp 7552 12 iptable_filter 7040 1 ip_tables 17108 1 iptable_filter x_tables 16644 4 ipt_REJECT,xt_state,xt_tcpudp,ip_tables speedstep_lib 9988 0 encode_eucjp 247424 0 dm_mod 56984 0 video 19972 0 button 10640 0 battery 14084 0 ac 8964 0 lp 15784 0 parport_pc 30276 1 parport 38472 2 lp,parport_pc floppy 61668 0 nvram 12680 0 shpchp 46688 0 i2c_i810 9220 0 i2c_algo_bit 12808 1 i2c_i810 8139cp 25600 0 i2c_i801 12428 0 i2c_core 24576 2 i2c_algo_bit,i2c_i801 8139too 30208 0 mii 9472 2 8139cp,8139too uhci_hcd 34576 0
デバイス番号を確認する
/proc/devicesに、hogeがちゃんと追加されている。
30番と 254番にいるけど
30番は MKDEV( )で作ったほう
254番は alloc_chrdev_region( )で確保したほう。
(でも、alloc〜の戻り値 resultが負だったから
正しくregister〜されなかったと思ったが。あとで整理。)
$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 6 lp 7 vcs 10 misc 13 input 29 fb 30 hoge 128 ptm 136 pts 180 usb 189 usb_device 254 hoge Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 22 ide1 253 device-mapper 254 mdp
カーネルメッセージをみる
printk( )を仕込んでいたのを確認する。
dmesgで得られるメッセージのうち
必要なものだけ、貼りつけときます。
$ dmesg hoge_init() : register_chrdev_region() failed Hello, hoge_chrdev!! major=30 minor=0
hoge_chrdev.cの #if 0 のロジックのほうで実行したときは
最初の行の、failed になっているログが出た。
#else のロジックの MKDEV( )するほうで実行したときは
Hello〜のメッセージが出力し、モジュールのロードに成功。
デバイスのメジャー番号、マイナー番号ともに
指定した値になっていることがわかる。
デバイス番号が削除されているのを確認
unregister_chrdev_region( )でデバイス番号削除される。
キャラクタ型デバイスの 30番の hogeが削除されている。
(254番が残ったままだな...)
$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 6 lp 7 vcs 10 misc 13 input 29 fb 128 ptm 136 pts 180 usb 189 usb_device 254 hoge Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 22 ide1 253 device-mapper 254 mdp
あ、そうか、alloc〜と register〜がぶつかってるか
alloc_chrdev_region() と register_chrdev_region() は
どちらかでよいのだ。
alloc_chrdev_region() を getterみたいな使い方と勘違い。
書き換えなきゃ。
struct cdev構造体
/usr/src/linux-2.6.16/include/linux/cdev.h
短いから全部貼りつけとこう。
#ifndef _LINUX_CDEV_H #define _LINUX_CDEV_H #ifdef __KERNEL__ struct cdev { struct kobject kobj; struct module *owner; struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; }; void cdev_init(struct cdev *, struct file_operations *); struct cdev *cdev_alloc(void); void cdev_put(struct cdev *p); int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *); void cd_forget(struct inode *); #endif #endif
キャラクタ型デバイスを登録してみる
hoge_chrdev.c に、キャラクタ型デバイスの
登録処理を追加してみよう。
cdev_init( ), cdev_add( ), cdev_del( )を使ってみる。
そして、alloc_chrdev_region( )を使う処理も修正。
file_operations構造体も用意した。
まだ処理は空っぽ。
使わないメソッドは、NULLを指定しておく。
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> // printk() #include <linux/kdev_t.h> // dev_t, MAJOR(), MINOR() #include <linux/fs.h> // (register|alloc|unregister)_chrdev_region(), #include <linux/types.h> #include <linux/cdev.h> // cdev_init(), cdev_add(), cdev_del(), cdev_alloc() int hoge_major_no = 30; int hoge_minor_no = 0; int hoge_num = 1; static dev_t hoge_dev; static struct cdev hoge_cdev; static int hoge_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { // XXX write ioctl process here return 0; } static int hoge_open(struct inode *inode, struct file *filp) { // XXX write open process here return 0; } static int hoge_close(struct inode *inode, struct file *filp) { // XXX write close(release) process here return 0; } static struct file_operations hoge_ops = { .owner = THIS_MODULE, .llseek = NULL, .read = NULL, .write = NULL, .ioctl = hoge_ioctl, .open = hoge_open, .release = hoge_close, }; static int __init hoge_init(void) { int result; #if 1 result = alloc_chrdev_region(&hoge_dev, hoge_minor_no, hoge_num, "hoge"); if (result < 0) { printk(KERN_ALERT "hoge_init() : alloc_chrdev_region() failed\n"); return result; } hoge_major_no = MAJOR(hoge_dev); hoge_minor_no = MINOR(hoge_dev); #else hoge_dev = MKDEV(hoge_major_no, hoge_minor_no); result = register_chrdev_region(hoge_dev, hoge_num, "hoge"); if (result < 0) { printk(KERN_ALERT "hoge_init() : register_chrdev_region() failed\n"); return result; // XXX omit error recovery } #endif cdev_init(&hoge_cdev, &hoge_ops); hoge_cdev.owner = THIS_MODULE; hoge_cdev.ops = &hoge_ops; result = cdev_add(&hoge_cdev, hoge_dev, hoge_num); if (result < 0) { printk(KERN_ALERT "hoge_init() : cdev_add() failed\n"); return result; // XXX omit error recovery } // ---- // init success printk(KERN_ALERT "Hello, hoge_chrdev!!\n"); printk(KERN_ALERT "major=%d minor=%d\n", hoge_major_no, hoge_minor_no); return 0; } static void __exit hoge_exit(void) { cdev_del(&hoge_cdev); unregister_chrdev_region(hoge_dev, hoge_num); printk(KERN_ALERT "ByeBye, hoge_chrdev.\n"); } module_init(hoge_init); module_exit(hoge_exit);
よしよし。
なんだかデバドラっぽくなってきたかも。
あとは割り込みが入ったり、スレッドの処理がはいったりで
ある程度イメージできるかな、と。
メモリ管理のところは、ちょっと時間かけないと、かも。
じゃ、コンパイル、実行して確認
カーネルメッセージを確認
さっきと同じ手順で makeして insmodした。
カーネルのメッセージを確認。
$ dmesg Hello, hoge_chrdev!! major=253 minor=0
こんどは、alloc_chrdev_region( )で取得した
メジャー番号になっている。
register_chrdev_region( )と衝突していたのもなくなった。
/proc/devicesを確認
253番が追加されてるのを確認してみる。
$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 6 lp 7 vcs 10 misc 13 input 29 fb 128 ptm 136 pts 180 usb 189 usb_device 253 hoge 254 hoge Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 22 ide1 253 device-mapper
前につくった 254番は残ってる><
けど、ちゃんと 253番に追加されてる。
よいね。
じゃ、モジュールをアンロードしよう。
アンロード
# rmmod hoge_chrdev
カーネルメッセージを確認。
$ dmesg ByeBye, hoge_chrdev.
もいっちょ、/proc/devicesを確認。
$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 6 lp 7 vcs 10 misc 13 input 29 fb 128 ptm 136 pts 180 usb 189 usb_device 254 hoge Block devices: 1 ramdisk 2 fd 3 ide0 7 loop 9 md 22 ide1 253 device-mapper 254 mdp
254番の hogeをなんとかしたいけど、、
ひとまずオッケー。
次は
メソッドの実装をして、実際にデータの読み書きをします。
ioctlの確認をする日を用意したいかな。
まだまだつづく。