linux设备驱动之字符设备驱动模型(1)

 一:字符设备驱动

  在linux下面,应用层看到的一切皆为文件(名字)所有的设备都是文件,都可以调用open,read,write来操作,而在内核中每个中每个设备有唯一的对应一个设备号;

    APP   (名字)            
    OS      (设备号)
    HW

 

 

 

 

  下面我们写一个简单的字符设备驱动,再应用层我们打开一个设备,看看它是怎么来调用内核中的函数的;

  首先我们使用命令mknod 创建一个名为wangcai的设备名,在应用曾打开它;

  mknod /dev/设备文件名  c   主设备号   次设备号

  命令 mknod wangcai c 9  0

  在注册字符设备是我们需要用到这几个struct cdev,原型如下:

1 struct cdev
2 {
3     struct kobject kobj;          // 内嵌的kobject对象,描述设备引用计数
4     struct module *owner;         // 所属模块,一般赋值为THIS_MODULE
5     struct file_operations *ops;  // 文件操作结构体
6     struct list_head list;
7     dev_t dev;                    // 设备号
8     unsigned int count;
9 };

   cdev结构体的dev_t定义了设备号,32位。高12位为主设备号,低20位为次设备号。

  (1)应用层打开设备

 1 #include <stdio.h>
 2 #include <fcntl.h>
 3 
 4 int main()
 5 {
 6     int fd = 0;
 7     fd = open("wangcai", O_RDWR);
 8     if(fd < 0) {
 9         perror("open error");
10         return 1;
11     }
12 
13     return 0;
14 }

  (2)在内核中册了设备文件wangcai和方法ops

  

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/cdev.h>
 4 #include <linux/fs.h>
 5 
 6 MODULE_LICENSE("GPL");
 7 MODULE_AUTHOR("bunfly");
 8 int file_open(struct inode *no, struct file *fp);
 9 
10 struct cdev wangcai;//设备
11 struct file_operations fops;//方法
12 dev_t devno;
13 
14 int bunfly_init()
15 {
16     fops.open = file_open;//调用open        
17     //wangcai.ops = &fops;
18     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
19     //wangcai.dev = (9, 0);
20     devno = MKDEV(9, 0);//字符设备号注册
21     //insert_list(&wangcai);
22     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
23 
24 }
25 
26 int bunfly_exit()
27 {
28     printk("this is bunfly_exit\n");
29 }
30 
31 module_init(bunfly_init);
32 module_exit(bunfly_exit);
33 
34 int file_open(struct inode *no, struct file *fp)
35 {
36     printk("this is file_open\n");
37 
38     return 0;
39 }

  下面代码是实现字符设备的读写操作:

  (1)应用层

  write:

#include <stdio.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if(argc < 3) {
        printf("using %s <dev> <msg>\n", argv[0]);
        return 1;
    }

    int fd = 0;
    int ret = 0;

    fd = open(argv[1], O_RDWR);
    if(fd < 0) {
        perror("open error");

        return 1;
    }

    ret = write(fd, argv[2], strlen(argv[2]));
    if(ret < 0) {
        perror("write error");
        return 1;
    }
    close(fd);

    return 0;
}

  read:

  

 1 #include <stdio.h>
 2 #include <fcntl.h>
 3 #include <string.h>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     if(argc != 2) {
 8         printf("using %s <dev>\n", argv[0]);
 9         return 1;
10     }
11 
12     int fd = 0;
13     int ret = 0;
14     unsigned char data[1024] = {0};
15 
16     fd = open(argv[1], O_RDWR);
17     if(fd < 0) {
18         perror("open error");
19 
20         return 1;
21     }
22 
23     ret = read(fd, data, 1024);
24     if(ret < 0) {
25         perror("write error");
26         return 1;
27     }
28 
29     printf("data is: %s\n", data);
30 
31     close(fd);
32 
33     return 0;
34 }

 

  

  (2)内核  

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/cdev.h>
 4 #include <linux/fs.h>
 5 
 6 MODULE_LICENSE("GPL");
 7 MODULE_AUTHOR("bunfly");
 8 
 9 int file_open(struct inode *no, struct file *fp);
10 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t * loff);
11 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); 
12 
13 struct cdev wangcai;//设备
14 struct file_operations fops;//方法
15 dev_t devno;
16 unsigned char data[1024] = {0};
17 
18 int bunfly_init()
19 {
20     fops.open = file_open;        
21     fops.read = file_read;
22     fops.write = file_write;
23     
24     //wangcai.ops = &fops;
25     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
26     //wangcai.dev = (9, 0);
27     devno = MKDEV(9, 0);//字符设备号注册
28     //insert_list(&wangcai);
29     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
30 
31 }
32 
33 int bunfly_exit()
34 {
35     cdev_del(&wangcai);   /*注销设备*/
36       unregister_chrdev_region(MKDEV(9, 0), 1);
37     printk("this is bunfly_exit\n");
38 }
39 
40 module_init(bunfly_init);
41 module_exit(bunfly_exit);
42 
43 int file_open(struct inode *no, struct file *fp)
44 {    
45     return 0;
46 }
47 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
48 {
49     strcpy(buff, data);    
50     return size;
51 }
52 
53 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
54 {
55     memset(data, 0, 1024);
56     strcpy(data, buff);
57     return size;
58 }
59

  下面代码是通过ioctl()函数来控制灯亮灯灭:

  (1)应用层

  

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    if(argc != 3) {
        printf("using %s <devname> 1:0\n", argv[0]);
        return 1;
    }

    int fd = 0;
    fd = open(argv[2], O_RDWR);
    if(fd < 0) {
        perror("open");
        return 1;
    }    

    ioctl(fd, atoi(argv[2]));
    close(fd);
    return 0;
}

 

 

 

  (2)内核

  

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/cdev.h>
 4 #include <linux/fs.h>
 5 #include <linux/io.h>
 6 #include <linux/gpio.h>
 7 
 8 MODULE_LICENSE("GPL");
 9 MODULE_AUTHOR("bunfly");
10 
11 long my_ioctl(struct file *fp, unsigned int id, unsigned long fd);
12 int file_open(struct inode *no, struct file *fp);
13 
14 struct cdev wangcai;
15 struct file_operations fops;
16 dev_t devno;
17 unsigned long gpio_virt;
18 unsigned long *gpm4con, *gpm4dat;
19 
20 int bunfly_init()
21 {
22     fops.open = file_open;        
23     fops.unlocked_ioctl = my_ioctl;
24 
25     //wangcai.ops = &fops;
26     cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
27     //wangcai.dev = (9, 0);
28     devno = MKDEV(9, 0);//字符设备号注册
29     //insert_list(&wangcai);
30     cdev_add(&wangcai, devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
31 
32 
33     gpio_virt = ioremap(0x11000000, SZ_4K);//led物理地址到虚拟地址映射
34       gpm4con = gpio_virt + 0x02e0;
35      gpm4dat = gpio_virt + 0x02e4;
36 
37     return 0;
38 }
39 
40 int bunfly_exit()
41 {
42     cdev_del(&wangcai);   /*注销设备*/
43       unregister_chrdev_region(MKDEV(9, 0), 1);
44     printk("this is bunfly_exit\n");
45 
46     return 0;
47 }
48 
49 module_init(bunfly_init);
50 module_exit(bunfly_exit);
51 
52 int file_open(struct inode *no, struct file *fp)
53 {    
54     return 0;
55 }
56  
57 long my_ioctl(struct file *fp, unsigned int id, unsigned long fd)
58 {
59     if(id == 1) {
60         *gpm4con = 0x1111;
61         *gpm4dat = 0x0;
62     }
63     if(id == 0) {
64         *gpm4con = 0x1111;
65         *gpm4dat = 0xf;
66     }
67 }

     通过上面的代码我们已经了解了字符设备驱动的原理,在linux下应用层看到的设备都只 是一个名字,应用层打开一个设备最终会调到内核中的file_operations方法来进行读写操作,如果我们只创建一个的设备的时候,我们可以对他正 常的读写,那如果当我们有两个设备时,我们是否还能正常的进行读写操作,明显是存在问题的,就是第二次的数据会将第一次的数据覆盖,因为我们只有一个数据存储的data;那么解决这个问题的方法就是封装;

  下面是具体代码:

  

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/cdev.h>
 4 #include <linux/fs.h>
 5 
 6 MODULE_LICENSE("GPL");
 7 MODULE_AUTHOR("bunfly");
 8 
 9 int file_open(struct inode *no, struct file *fp);
10 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff);
11 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); 
12 
13 struct file_operations fops;//方法
14 
15 /*封装*/
16 struct bunfly_cdev {
17     dev_t devno;//设备号
18     struct cdev cdev;
19     unsigned char data[1024];    
20 };
21 
22 int bunfly_init()
23 {
24     fops.open = file_open;        
25     fops.read = file_read;
26     fops.write = file_write;
27     
28     struct bunfly_cdev wangcai;
29     cdev_init(&wangcai.cdev, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
30     wangcai.devno = MKDEV(9, 0);//注册设备号
31     cdev_add(&wangcai.cdev, wangcai.devno, 1);// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中
32 
33     struct bunfly_cdev tugou;
34     cdev_init(&tugou.cdev, &fops);
35     tugou.devno = MKDEV(9, 1);
36     cdev_add(&tugou.cdev, tugou.devno, 1);
37 
38     return 0;
39 }
40 
41 int bunfly_exit()
42 {
43     printk("this is bunfly_exit\n");
44 
45     return 0;
46 }
47 
48 module_init(bunfly_init);
49 module_exit(bunfly_exit);
50 
51 int file_open(struct inode *no, struct file *fp)
52 {
53     struct cdev *addr = no->i_cdev;//找到struct cdev dev 在struct bunfly_cdev中的地址
54     struct bunfly_cdev *this = container_of(addr, struct bunfly_cdev, cdev);
55     fp->private_data = this;       //父类在子类中的地址  //子类类型       
56 
57     return 0;
58 }
59 ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
60 {
61     struct bunfly_cdev *this = fp->private_data;
62     strcpy(buff,this-> data);    
63 
64     return size;
65 }
66 
67 ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
68 {
69     struct bunfly_cdev *this = fp->private_data;
70     memset(this->data, 0, 1024);
71     strcpy(this->data, buff);
72 
73     return size;
74 }
75  

 

 

  代码中有看到了container_of,再次强调掌握,还需要注意的是:

  (1)每一个设备文件仅有inode结构体 ;

  (2)每打开一次文件就创建一个file 结构体;

 

  

posted @ 2015-09-12 16:15  zhangwju  阅读(601)  评论(0编辑  收藏  举报