字符驱动

设备三种类型

1、字符设备

2、块设备

3、网络设备



字符设备:在IO传输过程中以字符为单位,传输速率较慢

内核代码:demo_chr_dev.c

 1 #include <linux/module.h>//define THIS_MODULE
 2 #include <linux/kernel.h>
 3 #include <linux/fs.h>  //define file_operations
 4 #include <linux/cdev.h>//define cdev 
 5 
 6 static struct cdev chr_dev;
 7 static dev_t ndev;//char device main id and sub id
 8 
 9 static int chr_open(struct inode * nd, struct file *filp)
10 {
11     int major = MAJOR(nd->i_rdev);
12     int minor = MINOR(nd->i_rdev);
13     printk("char_open, major=%d,minor=%d\n", major, minor);
14     return 0;
15 };
16 
17 static ssize_t chr_read(struct file *f, char __user *u, size_t sz, loff_t *off)
18 {
19     printk("In the char_read() function!\n");
20     return 0;
21 }
22 
23 struct file_operations chr_ops =
24 {
25     .owner = THIS_MODULE,
26     .open = chr_open,
27     .read = chr_read,
28 };
29 
30 
31 static int demo_init(void)
32 {
33     int ret;
34     cdev_init(&chr_dev, &chr_ops);
35     ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");
36     if(ret < 0)
37         return ret;
38     printk("demo_init(): major=%d,minor=%d\n", MAJOR(ndev), MINOR(ndev));
39     ret = cdev_add(&chr_dev, ndev, 1);
40     if(ret < 0)
41         return ret;
42     return 0;
43 }
44 
45 static void demo_exit(void)
46 {
47     printk("Removing char_dev module...\n");
48     cdev_del(&chr_dev);
49     unregister_chrdev_region(ndev, 1);
50 }
51 
52 module_init(demo_init);
53 module_exit(demo_exit);
54  
55 
56 MODULE_LICENSE("GPL");
57 MODULE_AUTHOR("Sain");
58 MODULE_DESCRIPTION("a char device driver as an example");

//Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
obj-m := demo_chr_dev.o
endif

整个代码相较于hello world 主要增加了下面两个响应函数

.open = chr_open,
.read = chr_read,

 

动态加载内核模块

sudo insmod demo_chr_dev.ko

dmesg能看到打出来相应日志了:

[ 4833.212757] demo_init(): major=245,minor=0

 

驱动测试程序

 1 //main.c
 2 
 3 
 4 #include <stdio.h>
 5 #include <fcntl.h>   //define O_RDONLY  
 6 #include <unistd.h>  //use posix function open read
 7 #define CHR_DEV "/dev/chr_dev"
 8 int main(void){
 9 
10     int ret;
11     char buff[32]={0};
12     int fd = open(CHR_DEV, O_RDONLY | O_NDELAY);
13     if(fd < 0){
14         printf("open file %s failed\n", CHR_DEV);
15         return -1;
16     }
17 
18     read(fd,buff,32);
19     printf("read:[%s]", buff);
20     close(fd);
21     return 0;
22 
23 }

gcc main.c -o main( 注意:不要gcc -c main.c -o main,加上-c完全时另外一个意思了)

比较坑的是,直接执行./main 函数会提示打开失败

还要根据设备号信息用mknod 命令在系统的/dev/目录创建新的设备节点

ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");

根据前面的log创建设备节点sudo mknod chr_dev c 245 0

 

ninjame@ubuntu1604:/dev$ ls -l chr_dev
crw-r--r-- 1 root root 245, 2 9月   1 00:13 chr_dev

 

重新执行./main

 可以看到应用程序成功调用到了设备驱动程序实现的函数

[ 4833.212757] demo_init(): major=245,minor=0
[ 5069.412260] char_open, major=245,minor=0
[ 5069.412269] In the char_read() function!

 

ps:

 

1、实际中发现创建节点的名字可以随意 ,只要主设备号、次设备号对上就可以

sudo mknod chr_devmode c 245 0  也是可以的

2、测试代码open 函数在打开失败时是完全不会调到内核函数的

chr_open 函数不存在出错分支,可见内核的调用和我们普通调用函数还是有很大区别的

static int chr_open(struct inode * nd, struct file *filp)
{
    int major = MAJOR(nd->i_rdev);
    int minor = MINOR(nd->i_rdev);
    printk("char_open, major=%d,minor=%d\n", major, minor);
    return 0;
};

 

posted @ 2017-09-01 00:46  疾风剑  阅读(288)  评论(0编辑  收藏  举报