1

#导入Word文档图片# Linux平台设备驱动模型​

 1 year ago
source link: https://blog.51cto.com/u_11822586/5488155
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

#导入Word文档图片# Linux平台设备驱动模型​

推荐 原创

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 平台总线驱动模型特点

  1. 平台模型采用了分层结构,把一个设备驱动程序分成了两个部分:​

平台设备(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函数。​

  1. 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);导出给其他模块使用)​

  1. 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);导出给其他模块使用)​

在设备层编程中,这两个函数所做的工作是相反的,一个用于添加设备到内核,另一个用于从内核中把设备删除。​

  1. 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函数。​

  1. 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);导出给其他模块使用)​

  1. 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);导出给其他模块使用)​

在设备层编程中,这两个函数所做的工作是相反的,一个用于添加驱动到内核,另一个用于从内核中把驱动删除。​

  1. 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);导出给其他模块使用)​

  1. 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"); /*指定驱动许可证*/

#导入Word文档图片# Linux平台设备驱动模型​_linux

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");​

  • 1
  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK