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デバイス番号を使用できなくなります.