0
点赞
收藏
分享

微信扫一扫

#导入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");




举报

相关推荐

0 条评论