Linux駆動学習ノート3--文字デバイス駆動例(driver+client)
文字デバイスドライバの例
前節の基礎があります.次に、文字デバイスドライバを作成する方法を学び、クライアントテストを通じて、文字デバイスドライバが正常に作成されたかどうかを検証します.
1、文字デバイスドライバ
以下は文字デバイス駆動ソースです
borytest.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <asm/atomic.h>
#include <linux/slab.h>
#define BORY_MAJOR 255 /* bory */
static int bory_major = BORY_MAJOR;
struct bory_dev /* */
{
struct cdev cdev;
atomic_t counter;
struct timer_list s_timer;
};
struct bory_dev *bory_devp; /* */
static void bory_timer_handle(unsigned long arg) /* */
{
printk(KERN_NOTICE "======== bory_timer_handle ");
mod_timer(&bory_devp->s_timer, jiffies + HZ);
atomic_inc(&bory_devp->counter);
printk(KERN_ERR "current jiffies is %ld
", jiffies);
}
int bory_open(struct inode *inode, struct file *filp) /* */
{
printk(KERN_NOTICE "======== bory_open ");
init_timer(&bory_devp->s_timer);
bory_devp->s_timer.function = &bory_timer_handle;
bory_devp->s_timer.expires = jiffies + HZ;
add_timer(&bory_devp->s_timer); /* */
atomic_set(&bory_devp->counter, 0);
return 0;
}
int bory_release(struct inode *inode, struct file *filp) /* */
{
printk(KERN_NOTICE "======== bory_release ");
del_timer(&bory_devp->s_timer);
return 0;
}
static ssize_t bory_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
printk(KERN_NOTICE "======== bory_read ");
int counter;
counter = atomic_read(&bory_devp->counter);
if(put_user(counter, (int*)buf))
{
return -EFAULT;
}else
{
return sizeof(unsigned int);
}
}
/* */
static const struct file_operations bory_fops =
{
.owner = THIS_MODULE,
.open = bory_open,
.release = bory_release,
.read = bory_read,
};
/* cdev*/
static void bory_setup_cdev(struct bory_dev *dev, int index)
{
printk(KERN_NOTICE "======== bory_setup_cdev 1");
int err, devno = MKDEV(bory_major, index);
printk(KERN_NOTICE "======== bory_setup_cdev 2");
cdev_init(&dev->cdev, &bory_fops);
printk(KERN_NOTICE "======== bory_setup_cdev 3");
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &bory_fops;
printk(KERN_NOTICE "======== bory_setup_cdev 4");
err = cdev_add(&dev->cdev, devno, 1);
printk(KERN_NOTICE "======== bory_setup_cdev 5");
if(err)
{
printk(KERN_NOTICE "Error %d add bory %d", err, index);
}
}
int bory_init(void)
{
printk(KERN_NOTICE "======== bory_init ");
int ret;
dev_t devno = MKDEV(bory_major, 0);
/* */
if(bory_major)
{
printk(KERN_NOTICE "======== bory_init 1");
ret = register_chrdev_region(devno, 1, "bory");
}else
{
printk(KERN_NOTICE "======== bory_init 2");
ret = alloc_chrdev_region(&devno,0,1,"bory");
bory_major = MAJOR(devno);
}
if(ret < 0)
{
printk(KERN_NOTICE "======== bory_init 3");
return ret;
}
/* */
bory_devp = kmalloc(sizeof(struct bory_dev), GFP_KERNEL);
if(!bory_devp) /* */
{
ret = -ENOMEM;
printk(KERN_NOTICE "Error add bory");
goto fail_malloc;
}
memset(bory_devp,0,sizeof(struct bory_dev));
printk(KERN_NOTICE "======== bory_init 3");
bory_setup_cdev(bory_devp, 0);
printk(KERN_NOTICE "======== bory_init 4");
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
}
void bory_exit(void) /* */
{
printk(KERN_NOTICE "End bory");
cdev_del(&bory_devp->cdev); /* cdev*/
kfree(bory_devp); /* */
unregister_chrdev_region(MKDEV(bory_major,0),1); //
}
MODULE_AUTHOR("BORY");
MODULE_LICENSE("Dual BSD/GPL");
module_param(bory_major, int, S_IRUGO);
module_init(bory_init);
module_exit(bory_exit);
次はMakefileファイルです
ifneq ($(KERNELRELEASE),)
obj-m := borytest.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
そしてディレクトリの下でコマンドmakeを叩いて、lsはファイルを見ることができます
bory@borya:~/driver/timertest2$ ls
borytest.c borytest.mod.c borytest.o Makefile~ Module.symvers
borytest.ko borytest.mod.o Makefile modules.order
マウントko
bory@borya:~/driver/timertest2$ sudo insmod ./borytest.ko
dmesgコマンドを使用して印刷されたlog情報を表示
bory@borya:~/driver/timertest2$ dmesg | tail -10
[26341.765552] End second
[26586.476212] ======== bory_init
[26586.476216] ======== bory_init 1
[26586.476219] ======== bory_init 3
[26586.476220] ======== bory_setup_cdev 1
[26586.476221] ======== bory_setup_cdev 2
[26586.476223] ======== bory_setup_cdev 3
[26586.476224] ======== bory_setup_cdev 4
[26586.476227] ======== bory_setup_cdev 5
[26586.476228] ======== bory_init 4
同様にlsmodコマンドを使用して、上の文字デバイスが正常にロードされたかどうかを確認できます.
bory@borya:~/driver/timertest2$ lsmod | head -5
Module Size Used by
borytest 12714 0
hello 12448 0
nls_utf8 12493 1
isofs 39549 1
2、クライアントテストプログラム
はい、文字デバイスドライバが完了しました.次にクライアントテストプログラムを書きます.
test.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd,i;
int data;
fd = open("/sys/module/borytest", O_RDONLY);/* /dev/second */
if (fd < 0)
{
printf("open /dev/borytest error
");
} else
{
printf("open /dev/borytest success
");
}
close(fd);
}
コマンドgcc-o test.o testcコンパイルbory@borya:~/driver/test$ gcc -o test.o test.c
bory@borya:~/driver/test$ ./test.o
open /dev/borytest success
印刷結果が表示されました.文字デバイスを開くのに成功しました.
3、エラー処理
文字デバイスドライバをロードすると、デバイス番号が使用されているため、変更が必要になる可能性があります.
#define BORY_MAJOR 255
の値です.文字デバイス番号が使用されているかどうかを確認するには、次のコマンドを使用します.
bory@borya:~/driver/test$ cat /proc/devices
Character devices:
255 bory
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
13 input
21 sg
29 fb
99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
251 hidraw
252 usbmon
253 bsg
254 rtc
Block devices:
1 ramdisk
259 blkext
7 loop
8 sd
9 md
11 sr
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
253 device-mapper
254 mdp
上には
255 bory
次回の書き込みデバイス駆動時に255デバイス番号を使用できなくなります.