16.1 平台设备和驱动初识
16.1.1 总线驱动模型简介
在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中集成的独立的外设控制器、挂接在SOC内存空间的外设等却不依附于此类总线。
基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。
16.1.2 平台总线驱动模型特点
- 平台模型采用了分层结构,把一个设备驱动程序分成了两个部分:
平台设备(platform_device)和平台驱动(platform_driver)。
(2)平台设备将设备本身的资源注册进内核,可以由内核统一管理。
(3)统一了设备驱动模型,使智能电源管理更容易实现(设计设计之初就是为了管理电源的)。
(4)从代码的维护角度看,平台模型的可移植性,通用性更好。
16.2 平台设备驱动模型分层
下面分别来介绍设备和驱动层的结构及内核提供编写的API函数。
16.2.1 平台设备层核心数据结构
在Linux 2.6内核中将每个设备的资源用结构struct platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,struct platform_device结构如下:
struct platform_device { const char * name; //设备名,要求和驱动层中的.name相同。 int id; //设备ID,一般为-1 struct devicedev;//内嵌标准device,一般可以用来传递平台数据 u32 num_resources; //设备占用资源个数 struct resource* resource;//设备占用资源的首地址。 struct platform_device_id*id_entry; //设备id入口,一般驱动不用 /* arch specific additions */ struct pdev_archdataarchdata; }; |
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,结构如下:
struct resource { resource_size_t start;//资源起始物理地址 resource_size_t end; //资源结束物理地址 const char *name; //资源名称,可以随便写,一般要有意义。 unsigned long flags;//资源类型,IO,内存,中断,DMA。 struct resource *parent, *sibling, *child; }; |
struct resource 结构中flags成员是指资源的类型,目前可用资源类型定义在include\linux\ioport.h文件 中。如下:
#define IORESOURCE_IO0x00000100 空间,一般在X86框架中存在,ARM一般没有 #define IORESOURCE_MEM0x00000200 内存空间,占用的是CPU 4G统一编址空间 #define IORESOURCE_IRQ0x00000400 中断资源,实际上就是中断号 #define IORESOURCE_DMA0x00000800 内存空间,占用的是CPU 4G统一编址空间, 但是这个空间是用来做dma模型使用的缓冲空间。 |
另外一个很重要的成员是struct device,这个成员是用来实现设备模型的,其中的void 成员用途很大,一般称为平台数据指针,可以给平台驱动层传递任何信息需要的信息,因这个成员指针类型是void类型的,在平台设备驱动模型中,随处可以看到这个成员的踪迹,在例子中会看到如何使用。
以下struct device的结构信息:
struct device { struct device*parent; /* 父设备指针 */ struct device_private*p; struct kobject kobj; const char*init_name; /*逻辑设备的名字*/ struct device_type*type; /* 设备类型 */ struct semaphoresem;/* semaphore to synchronize calls toits driver. */ struct bus_type*bus; /* 设备所属的总线类型 */ struct device_driver *driver;/* 指向开辟struct device结构driver指针*/ void*platform_data;/* 平台设备的私有数据指针,要以传递任何结构*/ struct dev_pm_infopower; #ifdef CONFIG_NUMA intnuma_node;/* NUMA node this device is close to */ #endif u64*dma_mask;/* dma mask (if dma'able device) */ /*Like dma_mask, but foralloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ u64coherent_dma_mask; struct device_dma_parameters *dma_parms; /* dma pools (if dma'ble) */ struct list_headdma_pools; /* internal for coherent mem override */ struct dma_coherent_mem*dma_mem; /* arch specific additions */ struct dev_archdataarchdata; dev_tdevt;/* 存放设备号dev_t,creates the sysfs"dev" */ spinlock_tdevres_lock; struct list_headdevres_head; struct klist_nodeknode_class; struct class*class; /* 设备所属类*/ const struct attribute_group **groups;/* optional groups */ void(*release)(struct device *dev); }; |
以上结构中对驱动开发者比较重要的成员已经使用粗体标注出来。
16.2.2 platform设备层API
在学习平台驱动设备层代码如何编写前,先介绍一下和设备层代码相关的内核API函数。
- int platform_device_register(struct platform_device *pdev)
函数原型 | int platform_device_register(struct platform_device *pdev) |
函数功能 | 向内核注册一平台设备 |
函数参数 | pdev: 要注册的struct platform_device结构指针 |
函数返回值 | 0:注册成功; 负数:注册失败 |
函数头文件 | include\linux\platform_device.h |
函数定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_device_register);导出给其他模块使用) |
- void platform_device_unregister(struct platform_device *pdev)
函数原型 | void platform_device_unregister(struct platform_device *pdev) |
函数功能 | 把指定的平台设备struct platform_device从内核中删除 |
函数参数 | pdev: 要删除的struct platform_device结构指针 |
函数返回值 | 无 |
函数头文件 | include\linux\platform_device.h |
函数定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_device_unregister);导出给其他模块使用) |
在设备层编程中,这两个函数所做的工作是相反的,一个用于添加设备到内核,另一个用于从内核中把设备删除。
- int platform_add_devices(struct platform_device **devs, int num)
函数原型 | int platform_add_devices(struct platform_device **devs, int num) |
函数功能 | 把devs数组中的num个平台设备struct platform_device结构注册到内核中。 |
函数参数 | pdev: struct platform_device结构指针数组 num:pdev数组元素中的个数。 |
函数返回值 | 0:注册成功; 负数:注册失败 |
函数头文件 | include\linux\platform_device.h |
函数定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_add_devices);导出给其他模块使用) |
这个函数内部调用的还是platform_device_register(struct platform_device *pdev),不同在于platform_device_register(struct platform_device *pdev)只注册单个平台设备,platform_add_devices(struct platform_device **devs, int num)注册多个平台设备结构。
16.2.3 platform 设备层编程
需要实现的结构体是:struct platform_device。 1)初始化 struct resource 结构变量。 2)初始化 struct platform_device 结构变量。 3)向系统注册设备,使用platform_device_register()函数。 |
16.2.4 平台驱动层核心数据结构
在内核中平台驱动模型的驱动层使用struct platform_driver结构来描述一个设备的驱动信息,定义在include\linux\platform_device.h文件中,结构如下:
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;//里边的name很重要,用来匹配 struct platform_device_id *id_table; }; |
这个结构中probe,remove是必须的要实现的函数成员,其他函数成员根据需要自行决定是否要实现。
struct device_driver driver成员中的name成员很重要,它的内容必须要和设备层核心结构struct platform_device的name成员相同,才能实现驱动层和设备层的绑定。
struct device_driver结构如下:
struct device_driver { const char*name; 驱动层的名字,用来和设备层匹配的*/ struct bus_type*bus; struct module*owner; const char*mod_name;/* used for built-in modules */ bool suppress_bind_attrs;/* disables bind/unbind via sysfs */ int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; }; |
16.2.5 platform 驱动层核心API
在学习平台驱动层代码如何编写前,也先来学习下和平台驱动层相关的内核API函数。
- int platform_driver_register(struct platform_driver *drv)
函数原型 | int platform_driver_register(struct platform_driver *drv) |
函数功能 | 向内核注册一个平台驱动结构struct platform_driver |
函数参数 | drv: 要注册的struct platform_driver结构指针 |
函数返回值 | 0:注册成功; 负数:注册失败 |
函数头文件 | include\linux\platform_device.h |
函数定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_driver_register);导出给其他模块使用) |
- void platform_driver_unregister(struct platform_driver *drv)
函数原型 | void platform_driver_unregister(struct platform_driver *drv) |
函数功能 | 把指针的平台设备struct platform_device从内核中删除 |
函数参数 | drv: 要删除的struct platform_driver结构指针 |
函数返回值 | 无 |
函数头文件 | include\linux\platform_device.h |
函数定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_driver_unregister);导出给其他模块使用) |
在设备层编程中,这两个函数所做的工作是相反的,一个用于添加驱动到内核,另一个用于从内核中把驱动删除。
- struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,
unsigned int num)
原型 | struct resource*platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) |
功能 | 从平台设备struct platform_device指针的资源指针成员串获取物理资源结构地址。 |
参数 | dev: 平台设备结构指针 type: 资源类型 num: 用户空间传递下来的资源资源数组中的同类索引号,注意:不同类型资源是分开编索引号。 资源编号是从0开始计算。 |
返回值 | 有效指针:成功,resource 资源指针; :调用失败 |
头文件 | include\linux\platform_device.h |
定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_driver_unregirxster);导出给其他模块使用) |
- int platform_get_irq(struct platform_device *dev, unsigned int num)
原型 | int platform_get_irq(struct platform_device *dev, unsigned int num) |
功能 | 从平台设备struct platform_device指针的资源指针成员获中断编号。 |
参数 | dev: 平台设备结构指针 num: 用户空间传递下来的资源数组中中断资源索引号,注意:不同类型资源是分开编索引号 |
返回值 | 正数:成功,中断号; -ENXIO:调用失败 (实际上是-6) |
头文件 | include\linux\platform_device.h |
定义文件 | drivers\base\platform.c (用EXPORT_SYMBOL_GPL(platform_driver_unregister);导出给其他模块使用) |
5. 驱动端的注册还提供了更加方便的宏,可代替原来驱动入口和出口宏
- 原来的注册代码如下:
/* 平台驱动端注册结构体 */ static struct platform_driver tiny4412_drv= { .driver= { .name="led_src", }, .probe=drv_probe, .remove=drv_remove, }; static int __init tiny4412_drv_init(void) { /*1. 平台驱动端注册*/ platform_driver_register(&tiny4412_drv); 提示: 驱动安装成功!\n"); return 0; } static void __exit tiny4412_drv_exit(void) { /*2. 平台驱动端注销*/ platform_driver_unregister(&tiny4412_drv); 提示: 驱动卸载成功!\n"); } module_init(tiny4412_drv_init); /*指定驱动的入口函数*/ module_exit(tiny4412_drv_exit); /*指定驱动的出口函数*/ MODULE_LICENSE("GPL"); /*指定驱动许可证*/ |
- 修改之后的注册代码:
/* 平台驱动端注册结构体 */ static struct platform_driver tiny4412_drv= { .driver= { .name="led_src", }, .probe=drv_probe, .remove=drv_remove, }; /* 注册平台设备的驱动端 */ module_platform_driver(tiny4412_drv); MODULE_LICENSE("GPL"); /*指定驱动许可证*/ |
16.2.6 platform驱动层编程
驱动层需要实现的结构体是struct platform_driver,以下是对platform 驱动层编程简要说明:
1)编写探测函数probe。
2)编写探测函数remove。
3)填充struct platform_driver下struct device_driver driver成员的子成员name,name值要和设备层struct platform_device的name相同。
4)调用int platform_driver_register(struct platform_driver *drv)函数进行注册前面填充好的struct platform_driver结构即可。
说明:struct platform_driver结构中除probe,remove要实现外,其他成员函数可以根据需要决定是否要实现。probe,remove函数所做的工作几乎是相反的,有特定的函数框架。
probe函数的框架:
1)获取平台设备私有数据。
2)获取平台设备占用的物理资源。
3)如果是内存资源,则向内核申请物理内存资源,申请成功后对物理内存空间进行重映射,转换为虚拟地址空间,再使用虚拟地址空间进行硬件的配置。
如果是中断资源,则进行中断函数注册,注册方法前面章节已经讲述过。
4)硬件初始化。
5)注册用户空间的接口:可以是/dev/目录下的接口,也可以是/sys/或/proc/目录的接口。
目前我们已经学习的是/dev/目录下生成用户空间的访问接口,也就是注册字符设备。
remov函数的框架:
这个函数是和probe函数相反的,简单说,probe函数中只要申请了资源,在这个函数中就要释放相应的资源。
16.3 基于平台模型的led设备驱动
现在我们已学习了平台设备驱动模型的一些基础知识了,但是至于如何编写平台设备驱动,还是不太清楚。
接下来先看一个简单的例子程序,把前面的leds字符设备驱动修改成平台驱动模型。
16.3.1 平台模型led驱动软件框架分析
平台驱动模型是分层结构,分成了平台设备和平台驱动两层,并且各自都有自己的核心结构,以及对应的注册函数,所以led平台驱动模型设计成员两个独立的模块文件,分别按设备层和驱动层的编写方法进行编程。
实现设备层:
1.分析led设备占用的物理资源,定义资源结构体struct resource,并填充所需要的资源。
2.设计led的平台数据结构,并定义平台数据变量并填充。
3.定义struct platform_device结构,填充name,,如果定义了平台数据,则还要把led平台数据指针填充dev成员下地platform_data成员中。
4.在模块的初始化函数中调用platform_device_register(struct platform_device *pdev)函数进行注册已经填充好的struct platform_device结构。
5.在模块卸载函数中调用platform_device_unregister(struct platform_device *pdev)函数进行注销struct platform_device结构。
实现设备层:
1. 编写平台驱动probe函数,请参考前面所说的框架进行编程(平台数据,平台资源要结合设备层内容)
2. 编写平台驱动remove函数。
3. 定义struct platform_driver结构,并填充这个结构。
4. 在模块的初始化函数中调用platform_driver_register(struct platform_driver *drv)函数进行注册已经填充好的struct platform_driver结构。
5. 在模块卸载函数中调用platform_driver_unregister(struct platform_driver *drv);函数进行注销struct platform_driver e结构。
16.3.2 平台模型led 设备层编程
设备层代码清单:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> //平台设备相关结构和函数 #include <linux/ioport.h>资源结构,falgs的宏 //设备名字 #define DEVNAME"platform_led" //定义设备资源 static struct resourcedev_res[]={ [0] = { .start = 0x110002E0, .end = 0x110002E0+8-1, .name = "led_res", .flags = IORESOURCE_MEM, }, [1] = { .start = 4, .end = 4, .name = "button_res", .flags = IORESOURCE_IRQ, }, }; //这个函数必须有,要不然卸载报错,里边可以什么都不做 static voiddev_release(struct device *dev) { ; } //设备层核心结构 static struct platform_device led_dev = { .name = DEVNAME, .id = -1, .num_resources = ARRAY_SIZE(dev_res), .resource = dev_res, .dev ={ .release = dev_release, }, }; //insmod---初始化函数 static int __init led_dev_init(void) { int ret=0; printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); ret = platform_device_register(&led_dev); if(ret < 0){ printk("platform_device_register is fail!!\n"); goto platform_device_register_err; } return 0; platform_device_register_err: platform_device_unregister(&led_dev); return ret; } //rmmod---卸载函数 static void __exit led_dev_exit(void) { printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); platform_device_unregister(&led_dev); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("GPL"); |
16.3.3 平台模型LED驱动层编程
驱动层代码清单,如下:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> //驱动层平台设备相关结构和函数 #include <linux/fs.h> #include <linux/miscdevice.h> #include <asm/io.h> #include <asm/uaccess.h> #define DRVNAME "platform_led" #define MISCNAME "platform_misc_led" unsigned int *base_addr = NULL; #define RGPM4CON (*(volatile unsigned *)(base_addr + 0)) #define RGPM4DAT (*(volatile unsigned *)(base_addr + 1)) //驱动的open接口 static int led_open(struct inode *i_node, struct file *p_file) { printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); RGPM4CON &= ~((0xF<<4*0)|(0xF<<4*1)|(0xF<<4*2)|(0xF<<4*3)); RGPM4CON |= (0x1<<4*0)|(0x1<<4*1)|(0x1<<4*2)|(0x1<<4*3); RGPM4DAT |= (0x1<<0)|(0x1<<1)|(0x1<<2)|(0x1<<3); return 0; } //驱动程序的close函数 static int led_release0(struct inode *i_node, struct file *p_file) { printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); return 0; } //驱动程序的write接口 static ssize_t led_write(struct file *i_node, const char __user *buf, size_t count, loff_t *off) { int i = 0,ret = 0; char buff[4] = {0}; printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); ret = copy_from_user(buff,buf,count); if(ret < 0){ printk("copy_from_user is fail!!\n"); return -1; } for(i=0;i<sizeof(buff);i++) { if(buff[i] == 0){ RGPM4DAT |= (1<<i); printk("led[%d] is off\n",i); } else if(buff[i] == 1){ RGPM4DAT &= ~(1<<i); printk("led[%d] is on\n",i); } else{ printk("on/off led err!!\n"); return -1; } } return 0; } //文件操作集合 static struct file_operations f_ops ={ .open = led_open, .release = led_release0, .write = led_write, }; // 杂项设备核心结构 static struct miscdevice misc_led = { .minor = 255, .name = MISCNAME, .fops = &f_ops, }; //探测函数 static int led_probe(struct platform_device *led_dev) { struct resource *led_res = NULL; int ret = 0; printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); //从设备层获得资源 led_res = platform_get_resource(led_dev,IORESOURCE_MEM,0); if( led_res == NULL ){ printk("platform_get_resource is fail\n"); return -1; } printk("platform get resource[0] = %x\n",led_res->start); //io资源映射 base_addr = ioremap(led_res->start,(led_res->end)-(led_res->start)+1); if( base_addr==NULL ){ printk("ioremap is fail\n"); return -1; } // 注册杂项设备 ret = misc_register(&misc_led); if(ret<0){ printk("misc_register is fail!!\n"); return ret; } return 0; } //反探测函数 static int led_remove(struct platform_device *led_dev) { printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); iounmap(base_addr); misc_deregister(&misc_led); return 0; } //驱动层核心结构 static struct platform_driver led_drv = { .probe = led_probe, .remove = led_remove, .driver ={ .name = DRVNAME, }, }; //insmod---初始化函数 static int __init led_drv_init(void) { int ret = 0; printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); ret = platform_driver_register(&led_drv); if(ret < 0){ printk("platform_driver_register is fail!!\n"); goto platform_driver_register_err; } return 0; platform_driver_register_err: platform_driver_unregister(&led_drv); return ret; } //rmmod---卸载函数 static void __exit led_drv_exit(void) { printk("line = %d, %s is called!!!\n",__LINE__,__FUNCTION__); platform_driver_unregister(&led_drv); } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL"); |
16.3.4 makefile文件
obj-m := platform_led_drv.o platform_led_dev.o KDIR := /works/linux_kernel/linux-3.5 all: make -C $(KDIR) M=$(PWD) modules arm-linux-gcc -o app_platform app.c rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.unsigned *order *~ *.cmd cp *.ko app_platform /works/rootfs/home clean: rm -f *ko app *.o *.mod.o *.mod.c *.symvers *.markers *.unsigned *order *~ *.cmd |
16.3.5 测试用的应用程序
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char *argv[]) { int fd; char buf[4]={0,1,0,1}; if(argc <2){ printf("usage : %s <file name >\n",argv[0]); return -1; } fd = open(argv[1],O_RDWR); if(fd<0){ printf("fd = %d,open %d file is fail!!\n",fd,argv[1]); return -1; } write(fd,buf,4); sleep(2); while(1) { memset(buf, 0, 4); buf[0]=1; write(fd,buf,4); sleep(2); memset(buf, 0, 4); buf[1]=1; write(fd,buf,4); sleep(2); memset(buf, 0, 4); buf[2]=1; write(fd,buf,4); sleep(2); memset(buf, 0, 4); buf[3]=1; write(fd,buf,4); sleep(2); } return 0; } |
16.3.4 测试结果
[root@WBYQ /]#cd home/ [root@WBYQ /home]#ls app_btn k_timer.ko platform_led_drv.ko app_platform key_timer.ko work_queue.ko btn.ko mplayer_installdir btn_app platform_led_dev.ko [root@WBYQ /home]# [root@WBYQ /home]#insmod platform_led_dev.ko [ 42.020000] line = 50, led_dev_init is called!!! [root@WBYQ /home]#insmod platform_led_drv.ko [ 60.350000] line = 138, led_drv_init is called!!! [ 60.355000] line = 89, led_probe is called!!! [ 60.355000] platform get resource[0] = 110002e0 [root@WBYQ /home]# [root@WBYQ /home]#./app_platform /dev/platform_misc_led [ 127.660000] line = 19, led_open is called!!! [ 127.660000] line = 42, led_write is called!!! [ 127.660000] led[0] is off //看到led隔开,1亮1灭 [ 127.660000] led[1] is on [ 127.660000] led[2] is off [ 127.660000] led[3] is on [ 129.660000] line = 42, led_write is called!!! [ 129.660000] led[0] is on //这个灯亮,其他灭 [ 129.660000] led[1] is off [ 129.660000] led[2] is off [ 129.660000] led[3] is off [ 131.660000] line = 42, led_write is called!!! [ 131.660000] led[0] is off [ 131.660000] led[1] is on //这个灯亮,其他灭 [ 131.660000] led[2] is off [ 131.665000] led[3] is off [ 133.665000] line = 42, led_write is called!!! [ 133.665000] led[0] is off [ 133.665000] led[1] is off [ 133.665000] led[2] is on//这个灯亮,其他灭 [ 133.665000] led[3] is off ^C[ 135.085000] line = 30, led_release0 is called!!! [root@WBYQ /home]#//卸载 [root@WBYQ /home]#rmmod platform_led_dev [ 231.095000] line = 67, led_dev_exit is called!!! [ 231.095000] line = 117, led_remove is called!!! rmmod: module 'platform_led_dev' not found [root@WBYQ /home]#rmmod platform_led_drv [ 238.855000] line = 155, led_drv_exit is called!!! rmmod: module 'platform_led_drv' not found [root@WBYQ /home]# |
16.4 id_table实现多设备匹配
16.4.1 设备端代码
#include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> /*平台设备资源结构体*/ static struct resourceresource1[]= { { .start=0x110002E0, .end=0x110002E4, .name="led", .flags=IORESOURCE_MEM }, { .start=0x11000C60, .end=0x11000C64, .name="key", .flags=IORESOURCE_IRQ }, { .start=0x114000A0, .end=0x114000A4, .name="beep", .flags=IORESOURCE_MEM } }; /*平台设备资源结构体*/ static struct resourceresource2[]= { { .start=0x110002E0, .end=0x110002E4, .name="led", .flags=IORESOURCE_MEM }, { .start=0x11000C60, .end=0x11000C64, .name="key", .flags=IORESOURCE_IRQ }, { .start=0x114000A0, .end=0x114000A4, .name="beep", .flags=IORESOURCE_MEM } }; /*平台设备资源结构体*/ static struct resourceresource3[]= { { .start=0x110002E0, .end=0x110002E4, .name="led", .flags=IORESOURCE_MEM }, { .start=0x11000C60, .end=0x11000C64, .name="key", .flags=IORESOURCE_IRQ }, { .start=0x114000A0, .end=0x114000A4, .name="beep", .flags=IORESOURCE_MEM } }; /*设备端的资源释放函数*/ static voidled_release1(struct device *dev) { printk("设备端资源释放OK\r\n"); } /*设备端的资源释放函数*/ static voidled_release2(struct device *dev) { printk("设备端资源释放OK\r\n"); } /*设备端的资源释放函数*/ static voidled_release3(struct device *dev) { printk("设备端资源释放OK\r\n"); } /*平台设备核心结构体*/ static struct platform_device pdev1= { .name="led_device1", /*平台设备的名称*/ .num_resources=3, /*资源数量*/ .resource=resource1, /*资源结构体*/ .dev= { .release=led_release1, //.platform_data=, }, }; /*平台设备核心结构体*/ static struct platform_device pdev2= { .name="led_device2", /*平台设备的名称*/ .num_resources=3, /*资源数量*/ .resource=resource2, /*资源结构体*/ .dev= { .release=led_release2, //.platform_data=, }, }; /*平台设备核心结构体*/ static struct platform_device pdev3= { .name="led_device3", /*平台设备的名称*/ .num_resources=3, /*资源数量*/ .resource=resource3, /*资源结构体*/ .dev= { .release=led_release3, //.platform_data=, }, }; static int __init key_dev_init(void) { /*1. 平台设备的注册*/ platform_device_register(&pdev1); platform_device_register(&pdev2); platform_device_register(&pdev3); //platform_add_devices return 0; } static void __exit key_dev_exit(void) { /*平台设备的注销*/ platform_device_unregister(&pdev1); platform_device_unregister(&pdev2); platform_device_unregister(&pdev3); } module_init(key_dev_init); module_exit(key_dev_exit); MODULE_LICENSE("GPL"); |
16.4.2 驱动端代码
#include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> static int led_probe(struct platform_device *device) { printk("资源匹配成功!\r\n"); struct resource* resource=device->resource; /*得到资源指针*/ int cnt=device->num_resources; /*得到资源结构体的数量*/ while(cnt--) { /*1. 提取资源*/ printk("资源名称: %s\r\n",resource->name); printk("起始地址: 0x%X\r\n",resource->start); printk("结束地址: 0x%X\r\n",resource->end); printk("\r\n"); resource++; /*移动到下一个结构体*/ } /*2. 映射IO口*/ /*3. 注册字符设备*/ return 0; } static int led_remove(struct platform_device *device) { printk("资源卸载成功!\r\n"); return 0; } /*名称列表*/ static struct platform_device_id table[]= { {"led_device1",0}, {"led_device2",0}, {"led_device3",0}, }; /*平台设备驱动层的核心结构体*/ static struct platform_driver drv= { .probe=led_probe, .remove=led_remove, .driver= { .name="led_device", }, .id_table=table, }; static int __init key_dev_init(void) { /*平台设备驱动层注册函数*/ platform_driver_register(&drv); return 0; } static void __exit key_dev_exit(void) { /*平台设备驱动层的注销函数*/ platform_driver_unregister(&drv); } module_init(key_dev_init); module_exit(key_dev_exit); MODULE_LICENSE("GPL"); |