通常在Linux中,把SOC系统中集成的独立外设单元(如:I2C、IIS、RTC、看门狗等)都被当作平台设备来处理。在Linux中用platform_device结构体来描述一个平台设备
平台设备驱动分析:
使用内核linux-2.6.22
分析工具:source insight
我们来分析s3c2410-lcd是如何进行平台设备驱动注册的。
drivers/video/s3c2410fb.c
首先我们找到s3c2410fb.c的入口函数:
module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup);
然后查找 s3c2410fb:
int __devinit s3c2410fb_init(void)
{
return platform_driver_register(&s3c2410fb_driver); //在入口函数中注册了&s3c2410fb_driver结构体
}
static void __exit s3c2410fb_cleanup(void)
{
platform_driver_unregister(&s3c2410fb_driver);
}
我们就跳到s3c2410fb_driver结构体:
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe, /*这个函数很重要,它的作用就相当于我们普通字符设备驱动中的init函数*/
.remove = s3c2410fb_remove, /*这个函数的作用就相当于我们普通字符设备驱动中的exit函数里所作的工作*/
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2410-lcd", //名字很重要,设备驱动和设备只有在相同的名字情况下,才能够进行匹配。
.owner = THIS_MODULE,
},
};
接下来分析.probe = s3c2410fb_probe, 中的s3c2410fb_probe结构体
static int __init s3c2410fb_probe(struct platform_device *pdev)
{
struct s3c2410fb_info *info;
struct fb_info *fbinfo;
struct s3c2410fb_hw *mregs;
int ret;
int irq;
int i;
u32 lcdcon1;
mach_info = pdev->dev.platform_data; //mach_info函数非常重要,他会和dev.platform_data中的dev进行匹配。
}
对于这个结构体里面的函数我就不一一分析了
我们构造的每一个平台设备结构体都会添加到platform.c(总线驱动中):
drivers/base/platform.c
找到它的入口函数:
int __init platform_bus_init(void)
{
int error;
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
找到platform_bus_type结构体:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
那么我们的设备驱动&s3c2410fb_driver又是如何注册到总线驱动platform.c中的呢?
platform_driver_register(&s3c2410fb_driver);
在platform.c中:
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //将设备驱动加入到平台总线的驱动链表上面
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
drivers/base/driver.c
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL); //
return bus_add_driver(drv); //将本drv驱动注册登记到drv->bus所在的总线上
}
我们找到bus_add_driver结构体:
drivers/base/bus.c
int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = get_bus(drv->bus);
int error = 0;
drv->kobj.kset = &bus->drivers;
if (drv->bus->drivers_autoprobe) {
error = driver_attach(drv); //在此去总线的设备链表上去搜索每一个设备,每搜索一个设备都会去调用__driver_attach;
if (error)
goto out_unregister;
}
klist_add_tail(&drv->knode_bus, &bus->klist_drivers); //将自己挂载到驱动链表上面去
module_add_driver(drv->owner, drv);
error = driver_add_attrs(bus, drv);
}
driver_attach起的作用是什么?
drivers/base/dd.c
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); //查找bus驱动中的每一个drv
}
来看看 __driver_attach函数的作用:
static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
return 0;
}
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
分析driver_probe_device结构体:
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
if (drv->bus->match && !drv->bus->match(dev, drv)) //platform_match
goto done;
ret = really_probe(dev, drv); //名字相同,代表匹配成功,成功则调用次函数
done:
return ret;
}
再来看看really_probe:
static int really_probe(struct device *dev, struct device_driver *drv)
{
dev->driver = drv;
if (driver_sysfs_add(dev)) {
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
} else if (drv->probe) {
ret = drv->probe(dev); //platform_drv_probe
if (ret)
}
}
drivers/base/platform.c
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev); //s3c2410fb_probe
}
platform_device注册过程:arch/arm/mach-s3c2410/mach-smdk2410.c
static void __init smdk2410_init(void)
{
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices)); //添加dev
smdk_machine_init();
}
static struct platform_device *smdk2410_devices[] __initdata = { //各种设备
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
drivers/base/platform.c
int platform_device_register(struct platform_device * pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
int platform_device_add(struct platform_device *pdev)
{
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) {
}
ret = device_add(&pdev->dev);
}
drivers/base/core.c
int device_add(struct device *dev)
{
dev = get_device(dev);
if (dev->bus)
bus_attach_device(dev); //开始匹配平台驱动,开始去找控制驱动(lcd控制器驱动)
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
down(&dev->class->sem);
list_add_tail(&dev->node, &dev->class->devices);
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
void bus_attach_device(struct device * dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
dev->is_registered = 1;
if (bus->drivers_autoprobe)
ret = device_attach(dev);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
}
}
查看device_attach函数
int device_attach(struct device * dev)
{
int ret = 0;
down(&dev->sem);
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
up(&dev->sem);
return ret;
}
再看看__device_attach函数:
drivers/base/dd.c
static int __device_attach(struct device_driver * drv, void * data)
{
struct device * dev = data;
return driver_probe_device(drv, dev); //也会调用总线的match函数来实现匹配过程,根据名字进行匹配
}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev); //platform_drv_probe最终会调用到我们的s3c2410fb_probe if (ret)
}
}
就分析到这了,后面有一些因为时间匆忙以后再做详细分析。