20150313 驱动模块分离概念
2015-03-13 Lover雪儿
还记得以前刚开始学习编写程序的时候,无论再多的代码,再多的功能都是使劲的往同一个.C文件中塞,最后导致的直接结果就是,代码多,功能复杂,严重的妨碍了我们代码可移植性甚至良好的阅读性,接下来,我们开始来学习将一个驱动程序进行拆分,根据各种代码的性质或者功能来写入不同的.C文件中,此处,我们来尝试使用平台设备来实现IMX257蜂鸣器的驱动分离代码。
前面我们实现了beep驱动,博客地址
在本实验中,我们根据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 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 #define DRIVER_NAME "beep_dev"10 #define DEVICE_NAME "beep_dev"11 #define IORESOURCE_IOMUX_BASE 0x0000020012 #define IORESOURCE_GPIO1_BASE 0x0000040013 #define IORESOURCE_BEEP_BIT 0x0000080014 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_2630 .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_drv.c 程序:
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 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_test.c 程序:
1 #include2 #include 3 #include 4 #include 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 \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 }