7.1 platform设备驱动概念
Linux内核中常见的总线有I2C总线、PCI总线、串口总线、SPI总线、CAN总线、单总线等,所以有些设备和驱动可以直接就可以挂在这些总线上,然后通过总线上的match进行设备和驱动的匹配。但是有些设备并不属于这些常见总线,所以我们引入了一种虚拟总线,也就是platform驱动。
Platform总线
Struct bus_type platform_bus_type = {
.name = “platform”, //名
.dev_attrs = platform_dev_attrs, //属性
.match = platform_match, //设备和驱动的匹配函数
.uevent = platform_uevent, //卸载函数
.pm = &platform_dev_pm_ops,//电源管理
};
设备和驱动的匹配函数match
Static int platform_match(struct device *dev,struct device_driver *drv)
{
Struct platform_device *pdev = to_platform_device(dev);//获得平台设备
Struct platfoem_driver *pdev = to_platform_driver(dev);//获得平台驱动
If(pdrv -> id_table)//如果平台驱动有支持项,进入platform_match_id
{
Return platform_match_id(pdrv->id_table,pdev) != NULL;
}
Return (strcmp(pdev->name,drv->name) == 0);//没有支持项,则老师匹配名字
}
通过上面的match函数可知,如果驱动中定义了驱动支持项,那么在总线执行match函数时,就会将驱动支持项中每一个名字和设备名字匹配,看看是否匹配成功。如果驱动没有设置支持项,就会把驱动的名字和设备的名字匹配,如果一样,则匹配成功。
7.1.2platform设备
Struct platform_device
{
Const char *name; //名
Int id;
Struct device dev;//内嵌设备
U32 num_resources;//资源个数
Struct platform_device_id *id_entry;//资源结构体
Struct pdev_archdata archdata;
}
Platform_device中资源结构体的定义
Struct resource
{
Resource_size_t start;
Resource_size_t end;
Const char *name;
Unsigned long flags;
Struct resource *parent, *sibling, *child;
}
对于flags标号可以有IORESOURCE_IO、IORESOURC_MEM、IORESOURC_IRQ、IORESOURC_DMA四种选择,申请内存IORESOURC_MEM、申请中断号IORESOURC_IRQ用的比较多。
1、platform设备的静态加载
所谓静态加载就是把platform设备编译进内核,对于platform_device的定义常常在BSP中实现。以mini2240举例,看看对应的BSP文件match-smdk2240.c
Struct platform_device s3c_device_lcd = {
.name = “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文件中的定义
Static struct platform_device *smdk2440_devices[] _initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_devide_i2c0,
&s3c_device_iis,
};
原来建立了platform_device数组,然后把LCD的platform_device添加到这个数组中
Static void_init smad2440_machine_init(void)
{
S3c24xx_fb_set_platform(&smdk2440_fb_info);
S3c_i2c0_set_platdata(NULL);
Platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));
Smdk_machine_init();
}
调用platform_add_devices函数把platform_device注册到内核,再继续跟踪platform_add_devices.
Int platform_add_devices(struct platform_device **devs, int num)
{
Int i;
Int 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;
}
}
Return ret;
}
除了BSP中定义的资源外,有的设备可能还会有一些配置信息,这些配置信息依赖于板子,不适合放到驱动中。为此,platform提供了平台数据platform_data的支持,在内核中添加平台数据有两种方式
2、platform设备的动态加载
由于静态添加platform_device,则需要最后编译内核,这个不利于修改,所以在开发阶段,我们可以采用platform设备的动态加载方法。具体方法是:先分配platform_device,然后向platform_device中添加资源结构体,最后把platform_device注册到内核。
7.1.3platform驱动
定义Platform_driver结构体:
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;
Struct platform_device_id *id_table;
};
1、platform驱动的静态加载
写一个驱动,在测试阶段我们一般采用动态加载的方式,当驱动已经成型,就会采用静态加载的方式,把驱动编译入内核。把驱动静态编译入内核的方式主要是根据MAKEFILE和KCONFIG两张地图,在MAKEFILE中添加驱动文件名,在kconfig中添加对应的驱动菜单选项。
2、platform驱动的动态加载
只需要在驱动模块加载函数中执行“platform_driver_register(&my_driver)”就可以把platform驱动加入内核。在进行insmod加载时就会调用这个模块加载函数,从而注册platform驱动。
7.2 平台设备的资源
7.2.1平台数据和私有数据的区别
在讲平台设备的静态加载时,提到了平台数据的概念,内核驱动代码中还会出现私有数据这一名词。
那平台数据和私有数据的区别:
平台数据是由于引入平台设备而产生的,青苔数据主要保存的是一些依赖于板子的配置信息,平台数据是定义在平台设备所在的BSP中的,在平台驱动中可以进行读取到在BSP中定义的平台数据。
私有数据是作为一个驱动中保存设备信息的一个结构体,它定义在平台驱动中,而不是BSP中,我们在平台驱动中可以把一个设备结构设置为这个平台驱动的私有数据,也可以根据这个平台设备,读取这个平台设备的私有数据。
在平台驱动中读取在BSP中定义的平台数据:
Struct s3c2410fb_match_info *pdata = pdev->dev.platform_data;
设置平台设备的私有数据
Struct buttons *key;
Platform_set_drvdata(pdev,key);
读取私有数据
Struct buttons *key = platform_get_drvdata(pdev);
补充:第一,根据经验发现平台数据是为私有数据服务,平台数据可能成为私有数据的一部分;第二,对于由设备获得平台设备的情况,可通过*pdev = to_platform_device(dev)获得。
7.2.2platform设备资源的读取
在BSP中定义平台设备的资源,可以在probe函数中读取到。对资源中存储空间的资源读取,首先要都区资源,然后申请空间,最后完成由虚拟地址到物理地址的映射。