Linux下的platform总线驱动Word文件下载.docx
《Linux下的platform总线驱动Word文件下载.docx》由会员分享,可在线阅读,更多相关《Linux下的platform总线驱动Word文件下载.docx(23页珍藏版)》请在冰点文库上搜索。
=NULL;
return(strcmp(pdev->
name,drv->
name)==0);
//没有支持项,则老实匹配名字
}
通过上面这个match函数我们知道,如果驱动中定义了驱动支持项,那么在总线执行match函数中,就会将驱动支持项中每一个名字和设备名字匹配,看看是否匹配成功。
如果驱动没有设置支持项,就会把驱动的名字和设备的名字匹配,如果一样,则匹配成功。
2.Platform设备
structplatform_device{
constchar*name;
//名
intid;
structdevicedev;
//内嵌设备
u32num_resources;
//资源个数
structresource*resource;
//资源结构体
structplatform_device_id*id_entry;
structpdev_archdataarchdata;
我们重点来看看platform_device中资源结构体的定义
structresource{
resource_size_tstart;
//起始地址
resource_size_tend;
//结束地址
constchar*name;
unsignedlongflags;
//标号
structresource*parent,*sibling,*child;
对于这个资源结构体中的flags标号可以有IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA四种选择,重点是申请内存(IORESOURCE_MEM)和申请中断号(IORESOURCE_IRQ)用的比较多。
2.1Platform设备的静态加载
所谓的静态加载,就是把platform设备编译进内核,对于platform_device的定义常常在BSP中实现,我们这里拿Mini2440举例,看看对于的BSP文件mach-smdk2440.c
structplatform_devices3c_device_lcd={
s3c2410-lcd"
.id=-1,
.num_resources=ARRAY_SIZE(s3c_lcd_resource),
.resource=s3c_lcd_resource,
.dev={
.dma_mask=&
s3c_device_lcd_dmamask,
.coherent_dma_mask=0xffffffffUL
这是基于Mini2440的LCD平台设备在BSP文件中的定义,那么我们怎么把它加入内核呢?
staticstructplatform_device*smdk2440_devices[]__initdata={
&
s3c_device_usb,
s3c_device_lcd,//添加LCD平台设备
s3c_device_wdt,
s3c_device_i2c0,
s3c_device_iis,
嗯,原来我们建立了一个platform_device数组,然后把LCD的platform_device添加到这个数组中,那么这个platform_device数组怎么注册到内核的呢?
staticvoid__initsmdk2440_machine_init(void)
s3c24xx_fb_set_platdata(&
smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));
//加到内核
smdk_machine_init();
看到了吧,在smdk2440_machine_init中,我们调用了platform_add_devices函数来把platform_device注册到内核,再继续跟踪下platform_add_devices
intplatform_add_devices(structplatform_device**devs,intnum)
inti,ret=0;
for(i=0;
i<
num;
i++){
ret=platform_device_register(devs[i]);
if(ret){
while(--i>
=0)
platform_device_unregister(devs[i]);
//注册设备
break;
returnret;
好了,到此为止,我们已经看到了如果添加platform_device,以及这个platform_device又是如何被注册到内核的全过程。
背景:
阅读新闻
[日期:
2012-12-16]
来源:
CSDN
作者:
weiqing1981127
[字体:
大
中
小]
除了BSP中定义的资源外,有的设备可能还会有一些配置信息,而这些配置信息依赖于板子,不适合放到驱动中,为此,我们的platform提供了平台数据platform_data的支持。
在内核中添加平台数据有两种方式,仍然以LCD为例
staticstructs3c2410fb_mach_infosmdk2440_fb_info__initdata={//平台数据
.displays=&
smdk2440_lcd_cfg,
.num_displays=1,
.default_display=0,
.lpcsel=((0xCE6)&
~7)|1<
<
4,
上面的smdk2440_fb_info就是LCD的平台数据,我们怎么把这个LCD的平台数据告诉LCD的platform_device呢?
//添加平台数据
看到没?
上面的s3c24xx_fb_set_platdata函数就完成了平台数据的添加,继续跟踪这个s3c24xx_fb_set_platdata函数
void__inits3c24xx_fb_set_platdata(structs3c2410fb_mach_info*pd)
structs3c2410fb_mach_info*npd;
npd=kmalloc(sizeof(*npd),GFP_KERNEL);
if(npd){
memcpy(npd,pd,sizeof(*npd));
s3c_device_lcd.dev.platform_data=npd;
//平台数据添加的实现
}else{
printk(KERN_ERR"
nomemoryforLCDplatformdata\n"
);
好了,我们可以看到其实把这个平台数据保存在了平台设备中内嵌的设备结构体的platform_data中。
刚才说了添加平台数据有两种方式,根据上面的原理,其实我们可以直接把平台数据保存在了平台设备中内嵌的设备结构体的platform_data中,具体代码如下
.platform_data=&
smdk2440_fb_info//添加平台数据
到此为止,我们已经明白了platform_device的静态添加全过程。
2.2Platform设备的动态加载
由于静态添加platform_device需要最后编译内核,这个不利于修改,所以在开发阶段,我们可以采用platform设备的动态加载方法。
具体操作是:
先分配platform_device,然后向platform_device中添加资源结构体,最后把platform_device注册到内核,对应三个函数如下
structplatform_devicemy_device=platform_device_alloc("
s3c2410-buttons"
-1);
platform_device_add_resources(my_device,s3c_buttons_resource,3);
ret=platform_device_add(my_device);
当然,上面三个函数还是封装在模块加载函数中,也就是把平台设备的加载写成一个模块的形式。
3.Platform驱动
structplatform_driver{
int(*probe)(structplatform_device*);
//探测函数
int(*remove)(structplatform_device*);
//移除函数
void(*shutdown)(structplatform_device*);
int(*suspend)(structplatform_device*,pm_message_tstate);
//挂起
int(*resume)(structplatform_device*);
//恢复
structdevice_driverdriver;
//内嵌设备驱动
structplatform_device_id*id_table;
//驱动支持项
根据上面的platform_driver结构体的定义,我们需要思考下platform驱动名字在哪里呢?
实际上在内嵌的设备驱动中定义的。
3.1Platform驱动的静态加载
写一个驱动,测试驱动阶段我们一般采用动态加载的方式,当驱动已经成型,我们就会采用静态加载的方式,把驱动编译入内核。
把驱动静态编译入内核的方式主要是根据Makefile和Kconfig两张地图,在Makefile中添加驱动文件名,在Kconfig中添加对应的驱动菜单选项,当我面makezImage时就会自动编译我们的驱动文件。
3.2Platform驱动的动态加载
拿一个基于平台设备的按键驱动例子看看
staticstructplatform_drivermy_driver={
.probe=my_probe,//探测函数
.remove=my_remove,
.driver={
.owner=THIS_MODULE,
},
上面是按键驱动中定义了的一个platform_driver,然后我们只需要在驱动模块加载函数中执行platform_driver_register(&
my_driver)就可以把platform驱动加入内核了。
在我们进行insmod加载时就会调用这个模块加载函数,从而注册platform驱动。
二.平台设备的资源
1.平台数据和私有数据的区别
前面在讲平台设备的静态加载的时候,我们提到平台数据的概念,在内核驱动代码中还会出现私有数据这一名词。
那么平台数据和私有数据有什么区别呢?
首先平台数据是由于引入平台设备而产生的,平台数据主要保存的是一些依赖的板子的配置信息,平台数据的定义是定义在平台设备所在的BSP中的,我们在平台驱动中可以进行读取到在BSP中定义的平台数据。
而私有数据是作为一个驱动中保存设备信息的一个结构体,它定义在平台驱动中,而不是BSP中,我们在平台驱动中可以把一个设备结构体设置为这个平台驱动的私有数据,也可以根据这个平台设备,读取这个平台设备的私有数据。
好了,下面我们先看看怎么在平台驱动中读取在BSP中定义的平台数据,仍然以LCD为例,只需要在设备驱动需要获取平台数据的地方执行如下代码
structs3c2410fb_mach_info*pdata=pdev->
dev.platform_data;
接下来,我们研究下私有数据。
私有数据的定义各种各样,总之是一个结构体。
那么怎么将一个设备结构体设置为平台设备的私有数据呢?
structbuttons*key;
platform_set_drvdata(pdev,key);
同样怎么根据这个平台设备,读取这个平台设备的私有数据呢?
Structbuttons*keyt=platform_get_drvdata(pdev);
最后补充两个点:
第一,根据经验发现平台数据是为私有数据服务的,也就是平台数据可能成为私有数据的一部分。
第二,对于由设备获得平台设备的情况,我们可以通过*pdev=to_platform_device(dev)代码获得。
2.Platform设备资源的读取
我们在BSP中定义了平台设备的资源,那么怎么获取这些资源呢?
首先我们要明白,设备和驱动的第一次匹配是发生在总线上的match函数中,这次匹配成功后所做的操作只是把设备和驱动相连。
当我们执行平台驱动中的probe时,会发生第二次设备和驱动的匹配,也就是所谓的探测。
所以,我们对在BSP中定义的平台设备的资源会在平台驱动的probe函数中读取到,下面我们就看看如何读取这些资源了。
对于资源中的存储空间的资源读取,首先读取资源,然后申请空间,最后完成由虚拟地址到物理地址的映射。
具体函数如下
structresource*res=platform_get_resource(pdev,IORESOURCE_MEM,0);
structresource*buttons_mem=request_mem_region(res->
start,
res->
end-res->
start+1,pdev->
name);
void__iomem*buttons_base=ioremap(res->
start,res->
end-res->
start+1);
对于中断资源的读取,只要一步如下操作即可。
structresource*buttons_irq1=platform_get_resource(pdev,IORESOURCE_IRQ,0);
三.平台设备驱动测试
这里我们采用Mini2440开发板,编写基于平台设备的按键驱动,要求按键驱动触发方式为单边沿触发,同时要求添加设备属性项。
因为这个驱动比较简单,我就不去细致分析了,如果对硬件不理解可以参考mini2440开发板数据手册,如果对软件不理解,可以参考上文平台设备的讲解。
在此,我提供platform设备模块代码,platform驱动模块代码,应用层测试代码,需要注意的是在动态加载测试时需要先加载设备模块,再加载驱动模块。
1.platform设备模块代码
#include<
linux/device.h>
linux/module.h>
linux/kernel.h>
linux/init.h>
linux/string.h>
linux/platform_device.h>
linux/interrupt.h>
linux/sysfs.h>
linux/stat.h>
#defineGPGCON0x56000060//控制端口地址
#defineGPGDAT0x56000064//数据端口地址
ssize_ttest_show(structdevice*dev,structattribute*attr,char*buf);
ssize_ttest_store(structdevice*dev,structattribute*attr,char*buf,size_tcount);
staticDEVICE_ATTR(buttons,S_IRWXUGO,test_show,test_store);
//设备属性
ssize_ttest_show(structdevice*dev,structattribute*attr,char*buf)//读设备属性
printk("
call:
test_show.\n"
attrname:
%s.\n"
attr->
sprintf(buf,"
%s\n"
returnstrlen(attr->
name)+2;
ssize_ttest_store(structdevice*dev,structattribute*attr,char*buf,size_tcount)//写设备属性
test_store.\n"
write:
%s.\n"
buf);
strcpy(attr->
name,buf);
returncount;
staticstructresources3c_buttons_resource[]=
[0]={//内存资源
.start=GPGCON,
.end=GPGDAT,
.flags=IORESOURCE_MEM,
[1]={//中断号
//KEY1
.start=IRQ_EINT8,
.end=IRQ_EINT8,
.flags=IORESOURCE_IRQ,
[2]={
//KEY2
.start=IRQ_EINT11,
.end=IRQ_EINT11,
MODULE_AUTHOR("
WJB"
MODULE_LICENSE("
DualBSD/GPL"
staticstructplatform_device*my_device=NULL;
staticint__initmy_device_init(void)
intret=0;
my_device=platform_device_alloc("
//申请平台设备
//添加资源
//注册平台设备
device_create_file(&
my_device->
dev,&
dev_attr_buttons);
//添加设备属性
if(ret)
platform_device_put(my_device);
staticvoidmy_device_exit(void)
platform_device_unregister(my_device);
device_remove_file(&
module_init(my_device_init);
module_exit(my_device_exit);
2.platform驱动模块