码迷,mamicode.com
首页 > 其他好文 > 详细

register_chrdev_region/alloc_chrdev_region和cdev注册字符设备驱动

时间:2016-12-27 23:19:56      阅读:383      评论:0      收藏:0      [点我收藏+]

标签:style   ast   应该   Owner   tail   简便   region   string   val   

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。


(1)register_chrdev  比较老的内核注册的形式   早期的驱动
(2)register_chrdev_region/alloc_chrdev_region + cdev  新的驱动形式

(3)register_chrdev()函数是老版本里面的设备号注册函数,可以实现静态和动态注册两种方法,主要是通过给定的主设备号是否为0来进行区别,为0的时候为动态注册。register_chrdev_region以及alloc_chrdev_region就是将上述函数的静态和动态注册设备号进行了拆分的强化。

 

register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值, 这组连续设备号的起始设备号, 相当于register_chrdev()中主设备号
Count:连续编号范围.   是这组设备号的大小(也是次设备号的个数)
Name:编号相关联的设备名称. (/proc/devices); 本组设备的驱动名称

alloc_chrdev_region自动分配设备号
(1)register_chrdev_region是在事先知道要使用的主、次设备号时使用的;要先查看cat /proc/devices去查看没有使用的。
(2)更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。
(3)自动分配的设备号,我们必须去知道他的主次设备号,否则后面没法去mknod创建他对应的设备文件。

alloc_chrdev_region函数,来让内核自动给我们分配设备号

(1)使用alloc_chrdev_region函数,让内核为我们自动分配一个设备号

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

1:这个函数的第一个参数,是输出型参数,获得一个分配到的设备号。可以用MAJOR宏和MINOR宏,将主设备号和次设备号,提取打印出来,看是自动分配的是多少,方便我们在mknod创建设备文件时用到主设备号和次设备号。 mknod /dev/xxx c 主设备号 次设备号

2:第二个参数:次设备号的基准,从第几个次设备号开始分配。

3:第三个参数:次设备号的个数。

4:第四个参数:驱动的名字。

5:返回值:小于0,则错误,自动分配设备号错误。否则分配得到的设备号就被第一个参数带出来。

 

cdev介绍

cdev是一个结构体,里面的成员来共同帮助我们注册驱动到内核中,表达字符设备的,将这个struct cdev结构体进行填充,主要填充的内容就是

struct cdev {

struct kobject kobj;

struct module *owner;//填充时,值要为 THIS_MODULE,表示模块

const struct file_operations *ops;//这个file_operations结构体,注册驱动的关键,要填充成这个结构体变量

struct list_head list;

dev_t dev;//设备号,主设备号+次设备号

unsigned int count;//次设备号个数

};

 

file_operations这个结构体变量,让cdev中的ops成员的值为file_operations结构体变量的值。这个结构体会被cdev_add函数想内核注册

cdev结构体,可以用很多函数来操作他。

如:

cdev_alloc:让内核为这个结构体分配内存的

cdev_init:将struct cdev类型的结构体变量和file_operations结构体进行绑定的

cdev_add:向内核里面添加一个驱动,注册驱动

cdev_del:从内核中注销掉一个驱动。注销驱动

设备号

(1)dev_t类型(包括了主设备号和次设备号  不同的内核中定义不一样有的是16位次设备号和16位主设备号构成  有的是20为次设备号12位主设备号 )

(2)MKDEV、MAJOR、MINOR三个宏

MKDEV:  是用来将主设备号和次设备号,转换成一个主次设备号的。(设备号)

MAJOR:   从设备号里面提取出来主设备号的。

MINOR宏:从设备号中提取出来次设备号的。

register_chrdev_region的使用对比register_chrdev:
 1 // 模块安装函数
 2 static int __init chrdev_init(void)
 3 {    
 4     int retval;
 5     
 6     printk(KERN_INFO "chrdev_init helloworld init\n");
 7 
 8 /*
 9     // 在module_init宏调用的函数中去注册字符设备驱动
10     // major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号
11     // 内核如果成功分配就会返回分配的主设备好;如果分配失败会返回负数
12     mymajor = register_chrdev(0, MYNAME, &test_fops);
13     if (mymajor < 0)
14     {
15         printk(KERN_ERR "register_chrdev fail\n");
16         return -EINVAL;
17     }
18     printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
19 */    
20 
21     // 使用新的cdev接口来注册字符设备驱动
22     // 新的接口注册字符设备驱动需要2步
23     
24     // 第1步:注册/分配主次设备号
25     mydev = MKDEV(MYMAJOR, 0);
26     retval = register_chrdev_region(mydev, MYCNT, MYNAME);//
//动态时如下直接改 同时将2526行去掉 其他都一样
//int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
27 if (retval) { 28 printk(KERN_ERR "Unable to register minors for %s\n", MYNAME); 29 return -EINVAL; 30 } 31 printk(KERN_INFO "register_chrdev_region success\n"); 32 // 第2步:注册字符设备驱动 33 cdev_init(&test_cdev, &test_fops); 34 retval = cdev_add(&test_cdev, mydev, MYCNT); 35 if (retval) { 36 printk(KERN_ERR "Unable to cdev_add\n"); 37 return -EINVAL; 38 } 39 printk(KERN_INFO "cdev_add success\n"); 40 41 42 // 使用动态映射的方式来操作寄存器 43 if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON")) 44 return -EINVAL; 45 if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON")) 46 return -EINVAL; 47 48 pGPJ0CON = ioremap(GPJ0CON_PA, 4); 49 pGPJ0DAT = ioremap(GPJ0DAT_PA, 4); 50 51 *pGPJ0CON = 0x11111111; 52 *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 53 54 55 return 0; 56 } 57 58 // 模块下载函数 59 static void __exit chrdev_exit(void) 60 { 61 printk(KERN_INFO "chrdev_exit helloworld exit\n"); 62 63 *pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); 64 65 // 解除映射 66 iounmap(pGPJ0CON); 67 iounmap(pGPJ0DAT); 68 release_mem_region(GPJ0CON_PA, 4); 69 release_mem_region(GPJ0DAT_PA, 4); 70 71 /* 72 // 在module_exit宏调用的函数中去注销字符设备驱动 73 unregister_chrdev(mymajor, MYNAME); 74 */ 75 76 // 使用新的接口来注销字符设备驱动 77 // 注销分2步: 78 // 第一步真正注销字符设备驱动用cdev_del 79 cdev_del(&test_cdev); 80 // 第二步去注销申请的主次设备号 81 unregister_chrdev_region(mydev, MYCNT); 82 }

完整代码:

技术分享
  1 #include <linux/module.h>        // module_init  module_exit
  2 #include <linux/init.h>            // __init   __exit
  3 #include <linux/fs.h>
  4 #include <asm/uaccess.h>
  5 #include <mach/regs-gpio.h>
  6 #include <mach/gpio-bank.h>        // arch/arm/mach-s5pv210/include/mach/gpio-bank.h
  7 #include <linux/string.h>
  8 #include <linux/io.h>
  9 #include <linux/ioport.h>
 10 #include <linux/cdev.h>
 11 
 12 
 13 
 14 #define MYMAJOR        200
 15 #define MYCNT        1
 16 #define MYNAME        "testchar"
 17 
 18 #define GPJ0CON        S5PV210_GPJ0CON
 19 #define GPJ0DAT        S5PV210_GPJ0DAT
 20 
 21 #define rGPJ0CON    *((volatile unsigned int *)GPJ0CON)
 22 #define rGPJ0DAT    *((volatile unsigned int *)GPJ0DAT)
 23 
 24 #define GPJ0CON_PA    0xe0200240
 25 #define GPJ0DAT_PA     0xe0200244
 26 
 27 unsigned int *pGPJ0CON;
 28 unsigned int *pGPJ0DAT;
 29 
 30 
 31 int mymajor;
 32 static dev_t mydev;
 33 static struct cdev test_cdev;
 34 
 35 char kbuf[100];            // 内核空间的buf
 36 
 37 
 38 static int test_chrdev_open(struct inode *inode, struct file *file)
 39 {
 40     // 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
 41     // 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
 42     printk(KERN_INFO "test_chrdev_open\n");
 43     
 44     rGPJ0CON = 0x11111111;
 45     rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));        //
 46     
 47     return 0;
 48 }
 49 
 50 static int test_chrdev_release(struct inode *inode, struct file *file)
 51 {
 52     printk(KERN_INFO "test_chrdev_release\n");
 53     
 54     rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
 55     
 56     return 0;
 57 }
 58 
 59 ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
 60 {
 61     int ret = -1;
 62     
 63     printk(KERN_INFO "test_chrdev_read\n");
 64     
 65     ret = copy_to_user(ubuf, kbuf, count);
 66     if (ret)
 67     {
 68         printk(KERN_ERR "copy_to_user fail\n");
 69         return -EINVAL;
 70     }
 71     printk(KERN_INFO "copy_to_user success..\n");
 72     
 73     
 74     return 0;
 75 }
 76 
 77 // 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
 78 static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,
 79     size_t count, loff_t *ppos)
 80 {
 81     int ret = -1;
 82     
 83     printk(KERN_INFO "test_chrdev_write\n");
 84 
 85     // 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中
 86     //memcpy(kbuf, ubuf);        // 不行,因为2个不在一个地址空间中
 87     memset(kbuf, 0, sizeof(kbuf));
 88     ret = copy_from_user(kbuf, ubuf, count);
 89     if (ret)
 90     {
 91         printk(KERN_ERR "copy_from_user fail\n");
 92         return -EINVAL;
 93     }
 94     printk(KERN_INFO "copy_from_user success..\n");
 95     
 96     if (kbuf[0] == 1)
 97     {
 98         rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
 99     }
100     else if (kbuf[0] == 0)
101     {
102         rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
103     }
104     
105     
106 /*
107     // 真正的驱动中,数据从应用层复制到驱动中后,我们就要根据这个数据
108     // 去写硬件完成硬件的操作。所以这下面就应该是操作硬件的代码
109     if (!strcmp(kbuf, "on"))
110     {
111         rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
112     }
113     else if (!strcmp(kbuf, "off"))
114     {
115         rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
116     }
117 */
118 
119     
120     
121     return 0;
122 }
123 
124 
125 // 自定义一个file_operations结构体变量,并且去填充
126 static const struct file_operations test_fops = {
127     .owner        = THIS_MODULE,                // 惯例,直接写即可
128     
129     .open        = test_chrdev_open,            // 将来应用open打开这个设备时实际调用的
130     .release    = test_chrdev_release,        // 就是这个.open对应的函数
131     .write         = test_chrdev_write,
132     .read        = test_chrdev_read,
133 };
134 
135 
136 // 模块安装函数
137 static int __init chrdev_init(void)
138 {    
139     int retval;
140     
141     printk(KERN_INFO "chrdev_init helloworld init\n");
142 
143 /*
144     // 在module_init宏调用的函数中去注册字符设备驱动
145     // major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号
146     // 内核如果成功分配就会返回分配的主设备好;如果分配失败会返回负数
147     mymajor = register_chrdev(0, MYNAME, &test_fops);
148     if (mymajor < 0)
149     {
150         printk(KERN_ERR "register_chrdev fail\n");
151         return -EINVAL;
152     }
153     printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
154 */    
155 
156     // 使用新的cdev接口来注册字符设备驱动
157     // 新的接口注册字符设备驱动需要2步
158     
159     // 第1步:注册/分配主次设备号
160     mydev = MKDEV(MYMAJOR, 0);
161     retval = register_chrdev_region(mydev, MYCNT, MYNAME);
162     if (retval) {
163         printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
164         return -EINVAL;
165     }
166     printk(KERN_INFO "register_chrdev_region success\n");
167     // 第2步:注册字符设备驱动
168     cdev_init(&test_cdev, &test_fops);
169     retval = cdev_add(&test_cdev, mydev, MYCNT);
170     if (retval) {
171         printk(KERN_ERR "Unable to cdev_add\n");
172         return -EINVAL;
173     }
174     printk(KERN_INFO "cdev_add success\n");
175 
176     
177     // 使用动态映射的方式来操作寄存器
178     if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
179         return -EINVAL;
180     if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
181         return -EINVAL;
182     
183     pGPJ0CON = ioremap(GPJ0CON_PA, 4);
184     pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
185     
186     *pGPJ0CON = 0x11111111;
187     *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));        //
188     
189 
190     return 0;
191 }
192 
193 // 模块下载函数
194 static void __exit chrdev_exit(void)
195 {
196     printk(KERN_INFO "chrdev_exit helloworld exit\n");
197 
198     *pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));    
199     
200     // 解除映射
201     iounmap(pGPJ0CON);
202     iounmap(pGPJ0DAT);
203     release_mem_region(GPJ0CON_PA, 4);
204     release_mem_region(GPJ0DAT_PA, 4);
205 
206 /*    
207     // 在module_exit宏调用的函数中去注销字符设备驱动
208     unregister_chrdev(mymajor, MYNAME);
209 */    
210 
211     // 使用新的接口来注销字符设备驱动
212     // 注销分2步:
213     // 第一步真正注销字符设备驱动用cdev_del
214     cdev_del(&test_cdev);
215     // 第二步去注销申请的主次设备号
216     unregister_chrdev_region(mydev, MYCNT);
217 }
218 
219 
220 module_init(chrdev_init);
221 module_exit(chrdev_exit);
222 
223 // MODULE_xxx这种宏作用是用来添加模块描述信息
224 MODULE_LICENSE("GPL");                // 描述模块的许可证
225 MODULE_AUTHOR("aston");                // 描述模块的作者
226 MODULE_DESCRIPTION("module test");    // 描述模块的介绍信息
227 MODULE_ALIAS("alias xxx");            // 描述模块的别名信息
View Code
技术分享
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <string.h>
 6 
 7 
 8 #define FILE    "/dev/test"            // 刚才mknod创建的设备文件名
 9 
10 char buf[100];
11 
12 int main(void)
13 {
14     int fd = -1;
15     int i = 0;
16     
17     fd = open(FILE, O_RDWR);
18     if (fd < 0)
19     {
20         printf("open %s error.\n", FILE);
21         return -1;
22     }
23     printf("open %s success..\n", FILE);
24 
25 /*    
26     // 读写文件
27     write(fd, "on", 2);
28     sleep(2);
29     write(fd, "off", 3);
30     sleep(2);
31     write(fd, "on", 2);
32     sleep(2);
33 */
34 /*
35     write(fd, "1", 1);
36     sleep(2);
37     write(fd, "0", 1);
38     sleep(2);
39     write(fd, "1", 1);
40     sleep(2);
41 */
42     while (1)
43     {
44         memset(buf, 0 , sizeof(buf));
45         printf("请输入 on | off \n");
46         scanf("%s", buf);
47         if (!strcmp(buf, "on"))
48         {
49             write(fd, "1", 1);
50         }
51         else if (!strcmp(buf, "off"))
52         {
53             write(fd, "0", 1);
54         }
55         else if (!strcmp(buf, "flash"))
56         {
57             for (i=0; i<3; i++)
58             {
59                 write(fd, "1", 1);
60                 sleep(1);
61                 write(fd, "0", 1);
62                 sleep(1);
63             }
64         }    
65         else if (!strcmp(buf, "quit"))
66         {
67             break;
68         }
69     }
70 
71     
72     // 关闭文件
73     close(fd);
74     
75     return 0;
76 }
View Code

 

整理自

http://blog.csdn.net/tommy_wxie/article/details/7195471

http://blog.sina.com.cn/s/blog_14f1867f70102wlrj.html

http://edu.51cto.com/pack/view/id-529.html

 

register_chrdev_region/alloc_chrdev_region和cdev注册字符设备驱动

标签:style   ast   应该   Owner   tail   简便   region   string   val   

原文地址:http://www.cnblogs.com/zhaobinyouth/p/6227644.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!