キャラクタ型デバイス

メモ


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);

コンパイル、モジュールのロード

Makefileを書く
$ cat Makefile
obj-m := hoge_chrdev.o
コンパイルする

カーネルディレクトリは、適宜、読み替えること。

$ make -C /usr/src/kernels/2.6.16-0vl68-i686 M=`pwd` modules
モジュールをロードする

rootになって insmodする。

$ su
# insmod hoge_chrdev.ko
モジュールを確認する

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〜のメッセージが出力し、モジュールのロードに成功。
バイスのメジャー番号、マイナー番号ともに
指定した値になっていることがわかる。

モジュールをアンロードする

rootになって rmmodでアンロードする。

# rmmod hoge_chrdev
バイス番号が削除されているのを確認

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
カーネルのメッセージも確認

dmesgで確認。
アンロードされている。

$ dmesg
ByeBye, hoge_chrdev.

あ、そうか、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の確認をする日を用意したいかな。
まだまだつづく。

ちなみに

Linuxカーネルは 2.6.25がリリースされたりしてて
全然最新環境じゃないですが><
気にせずやります!