分类: LINUX
2014-06-26 07:56:02
在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。
1.为设备编写platform_device设备结构
struct platform_device {
const char * name; //设备名
int id; //设备编号
struct device dev;
u32 num_resources; //设备使用资源的数目
struct resource * resource; //设备使用资源
};
struct resource结构体:
struct resource {
resource_size_t start; //资源起始地址
resource_size_t end; //资源结束地址
const char *name;
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
注:struct resource结构中我们通常关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。start、end的含义会随着flags而变更,如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资源。
注:设备名与id
The platform_device.dev.bus_id is the canonical name for the devices. It's built from two components:
* platform_device.name ... which is also used to for driver matching.
* platform_device.id ... the device instance number, or else "-1" to indicate there's only one.
These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and "serial/3" indicates bus_id "serial.3"; both would use the platform_driver named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id) and use the platform_driver called "my_rtc".
2.注册platform_device设备结构体
int platform_device_register(struct platform_device *pdev); //注册一个设备
int platform_add_devices(struct platform_device **pdevs, int ndev); //注册多个设备
platform设备注册例子:
linux2.6.26.8内核中NAND FLASH设备注册实例:
nand控制器资源:
linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
}
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
注册nand flash作为platform device:
linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
…
};
void __init smdk_machine_init(void)
{
…
s3c_device_nand.dev.platform_data = &smdk_nand_info; //注意这里的赋值,在nand flash驱动程序的probe函数里面利用了这里赋值的数据
platform_add_devices(smdk_devs,ARRAY_SIZE(smdk_devs)); //注册设备
s3c2410_pm_init();
}
注:
In many cases, the memory and IRQ resources associated with the platform device are not enough to let the device's driver work. Board setup code will often provide additional information using the device's platform_data field to hold additional information.
platform_data的形式是自定义的,如我们这里就利用了platform_data: s3c_device_nand.dev.platform_data = &smdk_nand_info;
1.为驱动程序编写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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *); //设备恢复
struct device_driver driver;
};
2.注册platform_driver结构体
int platform_driver_register(struct platform_driver *);
platform设备注册例子:
linux2.6.26.8内核中NAND FLASH设备驱动注册实例:
linux/drivers/mtd/nand/s3c2410.c:
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand",
.owner = THIS_MODULE,
},
};
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver); //注册设备驱动
}
module_init(s3c2410_nand_init);
注:驱动程序绑定由内核自动执行,当内核发现一个驱动程序与一个设备匹配时,将调用驱动程序的probe函数,完成对设备的探测及初始化等工作。
Device Binding ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Driver binding is performed automatically by the driver core, invoking driver probe() after finding a match between device and driver. If the probe() succeeds, the driver and device are bound as usual. There are three different ways to find such a match:
- Whenever a device is registered, the drivers for that bus are checked for matches. Platform devices should be registered very
early during system boot.
- When a driver is registered using platform_driver_register(),
all unbound devices on that bus are checked for matches.
Drivers usually register later during booting, or by module
loading.
- Registering a driver using platform_driver_probe() works just
like using platform_driver_register(), except that the driver
won't be probed later if another device registers. (Which is
OK, since this interface is only for use with non-hotpluggable
devices.)
附:内核如何判定platform总线上的设备与驱动匹配
系统中为platform总线定义了一个bus_type的实例
platform_bus_type,
其定义如下:
1 struct bus_type platform_bus_type = {
2 .name = "platform",
3 .dev_attrs = platform_dev_attrs,
4 .match = platform_match,
5 .uevent = platform_uevent,
6 .pm = PLATFORM_PM_OPS_PTR,
7 };
8 EXPORT_SYMBOL_GPL(platform_bus_type);
这里要重点关注其match()成员函数,正是此成员表明了platform_device和platform_driver之间如何匹配,如代码清单4所示。
代码清单4 platform_bus_type的match()成员函数
1 static int platform_match(struct device *dev, struct
device_driver *drv)
2 {
3 struct platform_device *pdev;
4
5 pdev = container_of(dev, struct platform_device, dev);
6 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) ==
0);
7 }
从代码清单4的第6行可以看出,匹配platform_device和platform_driver主要看二者的name字段是否相同
1.注销驱动程序
void platform_driver_unregister(struct platform_driver *);
例子:
linux/drivers/mtd/nand/s3c2410.c:
static void __exit s3c2410_nand_exit(void)
{
platform_driver_unregister(&s3c2412_nand_driver);
platform_driver_unregister(&s3c2440_nand_driver);
platform_driver_unregister(&s3c2410_nand_driver);
}
module_exit(s3c2410_nand_exit);
2.注销设备
void platform_device_unregister(struct platform_device *pdev);