前言
- 前提文章
platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动
Linux 设备树子系统【2】-设备树在platform设备驱动中的使用
Linux I2C子系统【1】-I2C驱动分析-设备树创建platform_device
下面将通过一个实例来深入我们的学习
一、platform 驱动的工作过程
platform
模型驱动编程,需要实现platform_device
(设备)与platform_driver
(驱动)在platform
(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations
(他包含的操作函数接口)、dev_t
(设备号)、设备文件(/dev)
等,因为用platform
机制编写的字符驱动,它的本质是字符驱动
我们要记住,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳。
在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:
a – 实现platform驱动
b – 实现platform设备
1.1那具体platform驱动的工作过程是什么呢:
设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;
如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;
如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。
二、实现platform 驱动与设备的详细过程
- 在分析platform 之前,可以先思考一下下面的问题:
a – 为什么要用 platform 驱动?不用platform驱动可以吗?
b – 设备驱动中引入platform 概念有什么好处?
现在先不回答,看完下面的分析就明白了,后面会附上总结。
platform_device
结构体 VSplatform_driver
结构体
设备(硬件部分):中断号,寄存器,DMA等 platform_device 结构体 | 驱动(软件部分)platform_driver 结构体 |
---|---|
struct platform_device { const char *name; 名字 int id; bool id_auto; struct device dev; 硬件模块必须包含该结构体 u32 num_resources; 资源个数 struct resource resource; 资源 人脉 const struct platform_device_id * id_entry; / arch specific additions */ struct pdev_archdata archdata; }; | struct platform_driver { int (* probe )(struct platform_device ); 硬件和软件匹配成功之后调用该函数 int ( remove)(struct platform_device *); 硬件卸载了调用该函数 void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver;内核里所有的驱动程序必须包含该结构体 const struct platform_device_id * id_table; 八字 }; |
设备实例: static struct platform_device hello_device= { .name = “bigbang”, .id = -1, .dev.release = hello_release, }; | 驱动实例: static struct platform_driver hello_driver= { .driver.name = “bigbang”, .probe = hello_probe, .remove = hello_remove, }; |
3、设备资源结构体
在struct platform_device
结构体中有一重要成员 struct resource *resource
struct resource {
resource_size_t start; /*资源起始地址*/
resource_size_t end; /*资源结束地址*/
const char *name;
unsigned long flags; /*区分是资源的什么类型*/
struct resource *parent, *sibling, *child;
};
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ 0x00000400
flags
指资源类型,我们常用的是 IORESOURCE_MEM
,IORESOURCE_IRQ
这两种。start
和end
的含义会随着flags
而变化,如
a–flags
为IORESOURCE_MEM
,start
,end
分别表示该platform_device
占据的内存的开始地址和结束地址。
b–flags
为 IORESOURCE_IRQ
时,start
、end
分别表示该platform_device
使用的中断号的开始地址和结束值;
static struct resource beep_resource[] =
{
[0] = {
.start = 0x114000a0,
.end = 0x114000a0+0x4,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0x139D0000,
.end = 0x139D0000+0x14,
.flags = IORESOURCE_MEM,
},
};
-
解决问题
现在可以回答这两个问题了
a – 为什么要用platform
驱动?不用platform
驱动可以吗?
b – 设备驱动中引入platform
概念有什么好处?引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;
设备驱动中引入platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体匹配信息,而在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
二、实例
这是一个蜂鸣器的驱动,其实前面已经有解析 Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:
-
device.c
#include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/ioport.h> static struct resource beep_resource[] = { [0] = { .start = 0x114000a0, .end = 0x114000a0 + 0x4, .flag = IORESOURCE_MEM, }, [1] = { .start = 0x139D0000, .end = 0x139D0000 + 0x14, .flag = IORESOURCE_MEM, }, }; static void hello_release(struct device *dev) { printk("hello_release"); return; } static struct platform_device hello_device = { .name = "bigbang", .id = -1, .dev.release = hello_release, .num_resources = ARRAY_SIZE(beep_resource), .resource = beep_resource, }; static int hello_init(void) { printk("hello_init"); return platform_device_register(&hello_device); } static void hello_exit(void) { printk("hello_exit"); platform_device_unregister(&hello_device); return; } MODULE_LICENSE("GPL"); module_init(hello_init); module_exit(hello_exit);
- driver.c
#include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/platform_device.h> #include <asm/io.h> static int major = 250; static int monor = 0; static dev_t devno; static struct class *cls; static struct device *test_device; #define TCFG0 0x0000 #define TCFG1 0x0004 #define TCON 0x0008 #define TCNTB0 0x000C #define TCMPB0 0x0010 static unsigned int *gpd0con; static void *timer_base; #define MAGIC_NUMBER 'k' #define BEEP_ON _IO(MAGIC_NUMBER ,0) #define BEEP_OFF _IO(MAGIC_NUMBER ,1) #define BEEP_FREQ _IO(MAGIC_NUMBER ,2) static void fs4412_beep_init(void) { writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con); writel ((readl(timer_base +TCFG0 )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0); writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 ); writel (500, timer_base +TCNTB0 ); writel (250, timer_base +TCMPB0 ); writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON ); } void fs4412_beep_on(void) { writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON ); } void fs4412_beep_off(void) { writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON ); } static void beep_unmap(void) { iounmap(gpd0con); iounmap(timer_base); } static int beep_open (struct inode *inode, struct file *filep) { fs4412_beep_on(); return 0; } static int beep_release(struct inode *inode, struct file *filep) { fs4412_beep_off(); return 0; } #define BEPP_IN_FREQ 100000 static void beep_freq(unsigned long arg) { writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0 ); writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 ); } static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { switch(cmd) { case BEEP_ON: fs4412_beep_on(); break; case BEEP_OFF: fs4412_beep_off(); break; case BEEP_FREQ: beep_freq( arg ); break; default : return -EINVAL; } return 0; } static struct file_operations beep_ops = { .open = beep_open, .release = beep_release, .unlocked_ioctl = beep_ioctl, }; static int beep_probe(struct platform_device *pdev) { int ret; printk("match ok!"); gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start); timer_base = ioremap(pdev->resource[1].start,pdev->resource[1].end - pdev->resource[1].start); devno = MKDEV(major,minor); ret = register_chrdev(major,"beep",&beep_ops); class = class_create(THIS_MODULE,"myclass"); if(IS_ERR(cls)){ unregister_chrdev(major,"beep"); return -EBUSY; } test_device = device_create(cls,NULL,devno,NULL,"beep"); /*mknod /dev/hello*/ if(IS_ERR(test_device)){ class_destroy(cls); unregister_chrdev(major,"beep"); return -EBUSY; } fs4412_beep_init(); return 0; } static int beep_remove(struct platform_device *pdev) { beep_unmap(); device_destroy(cls,devno); class_destroy(cls); unregister_chrdev(major,"beep"); return 0; } static struct platform_driver beep_driver = { .driver.name = "bigbang", .probe = beep_probe, .remove = beep_remove, }; static int beep_init(void) { printk("beep_init"); return platform_driver_register(&beep_driver); } static void beep_exit(void) { printk("beep_exit"); platform_driver_unregister(&beep_driver); return; } MODULE_LICENSE("GPL"); module_init(beep_init); moduel_exit(beep_exit);
- test.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> main() { int fd,i,lednum; fd = open("/dev/beep",O_RDWR); if(fd<0) { perror("open fail \n"); return ; } sleep(10); close(fd); }