20150313 驱动模块分离概念

20150313 驱动模块分离概念

2015-03-13 Lover雪儿

还记得以前刚开始学习编写程序的时候,无论再多的代码,再多的功能都是使劲的往同一个.C文件中塞,最后导致的直接结果就是,代码多,功能复杂,严重的妨碍了我们代码可移植性甚至良好的阅读性,接下来,我们开始来学习将一个驱动程序进行拆分,根据各种代码的性质或者功能来写入不同的.C文件中,此处,我们来尝试使用平台设备来实现IMX257蜂鸣器的驱动分离代码。

前面我们实现了beep驱动,博客地址http://www.cnblogs.com/lihaiyan/p/4298105.html

在本实验中,我们根据beep的性质,分为设备,驱动两个模块分别编写驱动程序,在设备的.C文件中,负责定义驱动的设备IO地址等可变的信息,而在驱动的.C文件中负责统一的内存地址IOREMAP映射,注册设备等不变的代码。这样,当我们程序要修改时,比如说如果我们要将beep修改为led的话,那么就只需要将设备的.C文件中的地址修改过来就可以了,大大的增强了代码的可移植性。

接下来,我么开始步入正题:

一、设备beep_dev.c的实现

1.定义平台设备结构体

如图所示,在平台设备结构体,需要注意两个地方,

第一个是.name,它是用于保存在设备链中,利用它在驱动链中进行匹配,找到相应的驱动程序。

第二个是.resources,它是一个资源结构体,用于保存我们IMX257板子的内存IO地址,当我们要更改硬件是,只需要修改资源结构体中的数据即可。简单的说,资源结构体主要的功能就是传递参数,将板子的内存地址传入beep_drv.c中,beep_drv.c只要通过platform_get_resource函数即可得到资源中保存的数据。

2.定义资源结构体

如图所示:

在资源结构体数组中,

数组0中存储了IMX257的IOMUX的基地址

数组1中存储了IMX257的GPIO1的基地址,此处我们的beep为GPIO1_26

数组2中存储了IMX257的beep在GPIO1地址的偏移

在beep_drv.c将这些数据获取,然后分别ioremap进行地址映射

3.接下来就是在分别在入口函数中申请平台设备,在出口函数中注销平台设备

总结一下,可以发现我们的设备beep_dev.C文件中只做了一件事,那就是保存IMX257的地址,所以,如果我们以后要移植 驱动程序的话,只需要修改地址就够了。当然,蜂鸣器有点简单,在以后的程序可能就没这么简单了,不过总体的框架都是这样,实现了驱动模块分离。

二、驱动beep_drv.c的实现

1.平台驱动支持

和上面差不多,此处我们先实现一个平台驱动的支持,也就是

①定义平台驱动结构体

②在入口函数中注册平台驱动

③在出口函数中卸载平台驱动

此处就不再详细将这个,见下图:

2.平台驱动探测函数probe实现

接下来,重点来了,我们此处驱动程序的核心就是在probe这个函数。

当我们的驱动和设备匹配成功之后,就会调用probe函数,在此函数中可以干任何我们想干的事。

所以,我们呢,就利用此函数完成我们的硬件设备初始化的工作。

①得到前面设备beep_dev.c文件中的资源结构体的资源

②将前面的资源中的地址,通过ioremap函数分别映射地址

③注册字符设备驱动,创建类,然后再类下面创建设备节点

如下图所示:

3.平台驱动释放函数remove实现

在remove函数中,自然就是将前面我们映射的地址取消映射,卸载字符设备驱动程序

如图所示:

4.实现file_opration结构体

前面注册字符设备时,需要一个file_operation结构体,用于制定读写函数,如下图所示:

5.实现打开函数beep_open

前面,我们的probe函数中已经实现了IO地址的映射,以及设备号的自动申请,自动创建设备节点。但是,单独只有这些是还不够的,由于我们IMX257芯片的IO引脚为复用IO,总共有七中模式,要想实现特定的功能,必须还得对IO引脚进行配置,

所以,我们此处,在打开函数中实现引脚的配置。

①配置GPIO引脚为模式5

②配置GPIO引脚为上拉模式,电压1.8v,CMOS输出

③配置蜂鸣器的GPIO1_26为输出模式

④将蜂鸣器引脚电平清零。

经过前面几步,我们就实现了IMX257引脚的正确配置。

如下图所示:

6.实现写函数beep_write

通过前面的额正确配置GPIO,我们就已经可以正确的使用beep了,接下来,我们通过写函数来将我们的数据传入内核,从而相应的控制蜂鸣器的正确鸣叫与停止。

三 、应用程序beep_test.c的实现

在测试程序中,我们只需要实现以下几步

①打开设备

②判断用户输入的命令

③根据用户的命令来相应的控制

当我们用户输入

./beep_test on         蜂鸣器响

./beep_test off        蜂鸣器地址响

./beep_test on_off     鸣器响5声

四、驱动测试:

imx257板子上加载,如图所示:

移除驱动:

附 设备 beep_dev.c 程序:

 1 #include<linux/module.h>
 2 #include<linux/init.h>
 3 #include<linux/types.h>
 4 #include<linux/fs.h>
 5 #include<asm/system.h>
 6 #include<linux/device.h>
 7 #include<linux/platform_device.h>
 8
 9 #define DRIVER_NAME "beep_dev"
10 #define DEVICE_NAME "beep_dev"
11 #define IORESOURCE_IOMUX_BASE       0x00000200
12 #define IORESOURCE_GPIO1_BASE       0x00000400
13 #define IORESOURCE_BEEP_BIT         0x00000800
14
15
16 /* 定义资源结构体 */
17 static struct resource beep_resource[] = {
18     [0] =     {
19         .start     = 0x43FAC000,//IOMUX基地址
20         .end     = 0x43FAC000 + 0xFFF,
21         .flags = IORESOURCE_IOMUX_BASE,
22     },
23     [1] =     {
24         .start     = 0x53FCC000,//GPIO1基地址
25         .end     = 0x53FCC000 + 0xFFF,
26         .flags = IORESOURCE_GPIO1_BASE,
27     },
28     [2] = {
29         .start = 26,        //beep为GPIO1_26
30         .end = 26,
31         .flags = IORESOURCE_BEEP_BIT,
32     },
33 };
34
35 static void beep_release(struct device *dev){
36
37 }
38
39 /*分配/设置/注册一个platform_device 结构体*/
40 static struct platform_device beep_dev = {
41     .name             = DEVICE_NAME,
42     .id                  = -1,
43     .num_resources     = ARRAY_SIZE(beep_resource),
44     .resource         = beep_resource,
45     .dev             = {
46         .release = beep_release,
47     },
48 };
49
50 //入口函数
51 static int beep_dev_init(void){
52         //注册一个平台设备
53     platform_device_register(&beep_dev);
54     return 0;
55 }
56
57 //出口函数
58 static void beep_dev_exit(void){
59     platform_device_unregister(&beep_dev);
60 }
61
62 module_init(beep_dev_init);
63 module_exit(beep_dev_exit);
64
65 MODULE_AUTHOR("Lover雪儿");
66 MODULE_VERSION("0.1.0");
67 MODULE_LICENSE("GPL");

beep_dev.c

附 驱动 beep_drv.c 程序:

  1 #include<linux/module.h>
  2 #include<linux/init.h>
  3 #include<linux/types.h>
  4 #include<linux/fs.h>
  5 #include<asm/system.h>
  6 #include<linux/platform_device.h>
  7 #include<linux/io.h>
  8 #include<linux/device.h>
  9 #include<linux/uaccess.h>
 10
 11 #define DRIVER_NAME "beep_dev"
 12 #define DEVICE_NAME "beep_dev"
 13
 14 #define IORESOURCE_IOMUX_BASE        0x00000200
 15 #define IORESOURCE_GPIO1_BASE        0x00000400
 16 #define IORESOURCE_BEEP_BIT            0x00000800
 17
 18 static int major;        //主设备号
 19 static struct class *beep_class;//创建类,在类下面创建设备
 20 //定义寄存器
 21 static unsigned long base_iomux;      //iomux基址 0X 43FA C000 -  0X 43FA FFFF
 22 static unsigned long base_gpio1;    //gpio3      0X 53FC C000 -  0X 53FC FFFF
 23 #define MUX_CTL      (*(volatile unsigned long *)(base_iomux + 0x011c))// MUX_CTL模式选择  配置寄存器
 24 #define PAD_CTL      (*(volatile unsigned long *)(base_iomux + 0x0314))// PAD_CTL GPIO常用功能设置
 25 #define DR_GPIO1     (*(volatile unsigned long *)(base_gpio1 + 0x0000))// GPIO DR   数据寄存器  DR
 26 #define GDIR_GPIO1     (*(volatile unsigned long *)(base_gpio1 + 0x0004))// GPIO GDIR 方向控制寄存器  GDIR
 27 static int beep_pin;//引脚的偏移
 28
 29
 30 static int beep_open(struct inode *inode, struct file *file)
 31 {
 32     printk("<0>function open!\n\n");
 33     //引脚配置
 34     //MUX_CTL
 35     MUX_CTL &= ~(0x07 << 0);
 36     MUX_CTL |= (0X05 << 0);    //设置为ALT5  GPIO1_26 BEEP
 37     //PAD_CTL
 38     PAD_CTL &= ~(0x01<<13 | 0x01<<3 | 0x03<<1 | 0x01<<0);   //1.8v 不需要上拉下拉  CMOS输出 slew rate
 39     //GDIR_GPIO1    配置为输出模式
 40     GDIR_GPIO1 &= ~(0x01 << beep_pin);
 41     GDIR_GPIO1 |= (0x01 << beep_pin);    //配置为输出模式
 42
 43     //DR_GPIO1        配置为输出0 点亮ERR_LED
 44     DR_GPIO1 &= ~(0x01 << beep_pin);        //将GPIO1_26清零
 45
 46     return 0;
 47 }
 48 static ssize_t beep_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
 49 {
 50     int val;
 51     printk("<0>function write!\n\n");
 52     if(copy_from_user(&val,buf,count))
 53         printk("<0>copy_from_user error!\n");
 54     if(val == 1){    //响蜂鸣器
 55         DR_GPIO1 |= (0x01 << beep_pin);        //将GPIO1_26置1
 56     }else{            //停蜂鸣器
 57         DR_GPIO1 &= ~(0x01 << beep_pin);        //将GPIO1_26清零
 58     }
 59     return count;
 60 }
 61
 62 //构造file_operation结构体
 63 static struct file_operations beep_fops = {
 64     .owner    = THIS_MODULE,    /*宏*/
 65     .open    = beep_open,
 66     .write    = beep_write,
 67 };
 68
 69
 70
 71 //探测函数
 72 static int beep_probe(struct platform_device *pdev){
 73     struct resource *res;
 74     printk("<0> beep_probe,found beep\n\n");
 75     /* 根据platform的资源进行ioremap */
 76     //获取资源 IOMUX 的地址
 77     res = platform_get_resource(pdev,IORESOURCE_IOMUX_BASE,0);
 78     //映射IOMUX的内存地址
 79     base_iomux = (unsigned long)ioremap(res->start,res->end - res->start);
 80
 81     //获取资源 GPIO1 的地址
 82     res = platform_get_resource(pdev,IORESOURCE_GPIO1_BASE,0);
 83     //映射GP1IO1的内存地址
 84     base_gpio1 = (unsigned long)ioremap(res->start,res->end - res->start);
 85
 86     //获取资源 LED灯 的偏移地址
 87     res = platform_get_resource(pdev,IORESOURCE_BEEP_BIT,0);
 88     beep_pin = res->start;
 89
 90
 91     /* 注册字符设备驱动程序 */
 92     major = register_chrdev(0,DEVICE_NAME,&beep_fops);
 93     //创建类,然后再类下面创建设备节点
 94     beep_class = class_create(THIS_MODULE,DEVICE_NAME);
 95     device_create(beep_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);//dev/beep_dev
 96
 97     return 0;
 98 }
 99
100 static int beep_remove(struct platform_device *pdev){
101     printk("<0> beep_remove, remove beep\n\n");
102     //删除类
103     device_destroy(beep_class,MKDEV(major,0));
104     class_destroy(beep_class);
105     /* 卸载字符设备驱动程序 */
106     unregister_chrdev(major,DEVICE_NAME);    /* iounmap */
107     //解除地址映射
108     iounmap((void __iomem *)base_iomux);
109     iounmap((void __iomem *)base_gpio1);
110     return 0;
111 }
112
113 //定义一个平台驱动结构体
114 struct platform_driver beep_drv = {
115     .probe = beep_probe,
116     .remove = beep_remove,
117     .driver = {
118         .name = DRIVER_NAME,
119     }
120 };
121
122
123 static int beep_drv_init(void){
124     platform_driver_register(&beep_drv);
125     return 0;
126 }
127
128 static void beep_drv_exit(void){
129     platform_driver_unregister(&beep_drv);
130 }
131
132 module_init(beep_drv_init);
133 module_exit(beep_drv_exit);
134
135 MODULE_AUTHOR("Lover雪儿");
136 MODULE_VERSION("0.1.0");
137 MODULE_LICENSE("GPL");

beep_drv.c

附 应用程序 beep_test.c 程序:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5
 6 /* beep_test on
 7  * beep_test off
 8  */
 9 int main(int argc, char **argv)
10 {
11     int fd;
12     int i = 0;
13     int val = 1;
14     fd = open("/dev/beep_dev", O_RDWR);
15     if (fd < 0){
16         printf("can‘t open!\n");
17     }
18     if (argc != 2){
19         printf("Usage :\n");
20         printf("%s <on|off|on_off>\n", argv[0]);
21         return 0;
22     }
23     if (strcmp(argv[1], "on") == 0){
24         val  = 1;
25         write(fd, &val, 4);
26     }else if (strcmp(argv[1], "off") == 0){
27         val = 0;
28         write(fd, &val, 4);
29     }else{
30         i=5;
31         while(i--){
32             val = 0;
33             write(fd, &val, 4);
34             val  = 1;
35             write(fd, &val, 4);
36         }
37         val = 0;
38         write(fd, &val, 4);
39     }
40     return 0;
41 }

beep_test.c

时间: 2024-10-18 04:41:25

20150313 驱动模块分离概念的相关文章

驱动程序分层分离概念_总线驱动设备模型_P

分层概念: 驱动程序向上注册的原理: 比如:输入子程序一个input.c作为一层,下层为Dev.c和Dir.c,分别编写Dev.c和Dir.c向上Input.c注册:如图所示 分离概念: 分离概念主要是讲,设备驱动程序分成两个部分,也将引进另一个新概念bus_dri_dev模型 总线-驱动-设备模式,是讲吧一个驱动分成两个部分,分别挂载到一条总线上的链表中,总线上有.match函数还对两个链表相同名字相匹配,匹配成功跳到driver驱动程序的probe函数来实现驱动的操作. 一下例子主要编写总线

软件测试中桩模块与驱动模块的概念与区别(转载)

桩模块和驱动模块(以C语言为例): 很多人对桩模块和驱动模块的概念会搞不清楚,那么下面来介绍这两个概念: 模块结构实例图: 假设现在项目组把任务分给了7个人,每个人负责实现一个模块.你负责的是B模块,你很优秀,第一个完成了编码工作,现在需要开展单元测试工作,先分析结构图: 1.由于B模块不是最顶层模块,所以它一定不包含main函数(A模块包含main函数),也就不能独立运行. 2.B模块调用了D模块和E模块,而目前D模块和E模块都还没有开发好,那么想让B模块通过编译器的编译也是不可能的. 那么怎

MySQL 主从复制与读写分离概念及架构分析

1.MySQL主从复制入门 首先,我们看一个图: 影响MySQL-A数据库的操作,在数据库执行后,都会写入本地的日志系统A中 假设,实时的将变化了的日志系统中的数据库事件操作,在MYSQL-A的3306端口,通过网络发给MYSQL-B. MYSQL-B收到后,写入本地日志系统B,然后一条条的将数据库事件在数据库中完成. 那么,MYSQL-A的变化,MYSQL-B也会变化,这样就是所谓的MYSQL的复制,即MYSQL replication. 在上面的模型中,MYSQL-A就是主服务器,即mast

Mysql之主从模式、读写分离概念

MySQL主从架构复制: 在主server上每次进行可能引起数据变化的操作都要先记录到二进制文件中,并将数据同步到主server的磁盘上,与此同时还要通过端口(3306)将二进制日志发送到从server上,在从server上,从server会先将接受的二进制日志保存为中继日志(relay log),在从中继日志中进行读取,重新执行一遍操作,进行数据的复制 但一般来说,如果主server有多个cpu,当数据量变化很频繁时,可能会在每个cpu上都运行一个事务,而注server只能一个个的记录入二进制

20150503 imx257下实现I2C驱动的四种方法

20150503 imx257下实现I2C驱动的四种方法 2015-05-3 Lover雪儿 时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码... 好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!! 今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法. 一.一个简单的i2c驱动 和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313

驱动模块和桩模块的概念和区别

转:http://blog.csdn.net/xhf55555/article/details/6819010 桩模块和驱动模块(以C语言为例): 很多人对桩模块和驱动模块的概念会搞不清楚,那么下面来介绍这两个概念: 模块结构实例图: 假设现在项目组把任务分给了7个人,每个人负责实现一个模块.你负责的是B模块,你很优秀,第一个完成了编码工作,现在需要开展单元测试工作,先分析结构图: 1.由于B模块不是最顶层模块,所以它一定不包含main函数(A模块包含main函数),也就不能独立运行. 2.B模

14.linux-platform机制实现驱动层分离(详解)

版权声明:本文为博主原创文章,未经博主允许不得转载. 本节目标:        学习platform机制,如何实现驱动层分离 1.先来看看我们之前分析输入子系统的分层概念,如下图所示: 如上图所示,分层就是将一个复杂的工作分成了4层, 分而做之,降低难度,每一层专注于自己的事情, 系统只将其中的核心层和事件处理层写好了,所以我们只需要来写驱动层即可,接下来我们来分析platform机制以及分离概念 2.分离概念 优点: 将所有设备挂接到一个虚拟的总线上,方便sysfs节点和设备电源的管理 使得驱

《驱动学习 - platform机制实现驱动层分离》

1.先来看看我们之前分析输入子系统的分层概念,如下图所示: 如上图所示,分层就是将一个复杂的工作分成了4层, 分而做之,降低难度,每一层专注于自己的事情, 系统只将其中的核心层和事件处理层写好了,所以我们只需要来写驱动层即可,接下来我们来分析platform机制以及分离概念. 2.分离概念 优点: 将所有设备挂接到一个虚拟的总线上,方便sysfs节点和设备电源的管理 使得驱动代码,具有更好的扩展性和跨平台性,就不会因为新的平台而再次编写驱动 介绍: 分离就是在驱动层中使用platform机制把硬

S3C2440 驱动分层概念

为自己的坚持加油! 切入正题,今天要学习的是驱动的分层/分离概念. 分离分层的目的是将硬件相关的代码和系统中比较稳定的代码分离开,并且按照一定的框架联系到一起.这样我们在写一个驱动的时候能够更加灵活,顶层的应用程序也能更加稳定的调用底层的接口.对驱动开发者而言,这样写驱动程序将更有逻辑性.纯粹时个人理解哈. 以input.c框架为例,从图中可以看到系统把硬件相关的代码放在一起,把纯软件的相对稳定的部分放在一起,如evdev.c 最后他们通过input.c相连接. 除了输入子系统之外,设备总线也遵