Mini2440通过platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))对平台设备进行注册,在mini2440_devices平台设备数组指针中通过&s3c_device_i2c0包括I2c0的平台设备,下面是i2c平台设备和其资源:
-
static struct resource s3c_i2c_resource[] = {
-
[0] = {
-
.start = S3C_PA_IIC,
-
.end = S3C_PA_IIC + SZ_4K - 1,
-
.flags = IORESOURCE_MEM,
-
},
-
[1] = {
-
.start = IRQ_IIC,
-
.end = IRQ_IIC,
-
.flags = IORESOURCE_IRQ,
-
},
-
};
-
-
struct platform_device s3c_device_i2c0 = {
-
.name = "s3c2410-i2c",
-
#ifdef CONFIG_S3C_DEV_I2C1
-
.id = 0,
-
#else
-
.id = -1,
-
#endif
-
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
-
.resource = s3c_i2c_resource,
-
};
但是在此name的成员是"s3c2410-i2c",而在mini2440下面我们可以查看/sys/deices/platform目录下面是“s3c2410-i2c ”目录,而不是"s3c2410-i2c"目录,这是为什么呢?这说明在注册平台设备以前系统改动了i2c平台设备的名称,最后经过分析是在mini2440_map_io()函数中修改了i2平台设备名称。
platform_add_devices()函数是platform_device_register()函数的复合版,platform_device_register()函数经过device_initialize()函数初始化,此函数的主要作用是把dev->kobject的父节点指向device,然后把通过kobject_init()函数把device_ktype赋给dev->kobject;接着调用platform_device_add()函数对平台设备注册,下面是device_ktype结构,因为以后分析很可能要要用到此结构:
-
static struct kobj_type device_ktype = {
-
.release = device_release,
-
.sysfs_ops = &dev_sysfs_ops,
-
};
在调用platform_device_add()函数时首先通过赋值语句把i2c设备的父节点指向平台设备,然后把设备的总线指向platform_bus_type,接着就是根据设备资源或i/o类型或内存类型分别插入不同链表进行管理,然后剩下将会调用device_add()函数对设备进行注册。
device_add()函数会通过device_private_init()函数初始化device的私有数据,然后通过setup_parent()函数设定dev->kobj.parent 指向平台设备的 kobj,接着通过kobject_add()函数像sysfs虚拟文件系统注册kobject,然后再/sys/devices/platform目录下会创建一个名为“s3c2440-i2c”的目录,接着会调用device_create_file()函数在/sys/devices/platform/ s3c2440-i2c目录下创建root用户可读写的ueven文件。
接着会通过bus_add_device()函数在/sys/devices/platform/ s3c2440-i2c目录下创建一个名为modalias只读文件和一个指向/sys/bus/platform的名字为“subsystem”的链接文件,通过klist_add_tail()函数会将该设备添加到bus->p->klist_devices(/sys/bus/platform)链表上。接着调用kobject_uevent(&dev->kobj, KOBJ_ADD);产生一个热插拔事件,然后调用bus_probe_device()函数探测驱动。
bus_probe_device()函数会调用device_attach()函数进行匹配,此函数又会调用bus_for_each_drv(dev->bus, NULL, dev, __device_attach)进行设备驱动绑定,其中回调函数__device_attach其主要主要,__device_attach,此函数首先调用driver_match_device()函数有此函数调用总线match()函数,platfrom bus的match函数如下:
-
static int platform_match(struct device *dev, struct device_driver *drv)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct platform_driver *pdrv = to_platform_driver(drv);
-
-
/* match against the id table first */
-
if (pdrv->id_table)
-
return platform_match_id(pdrv->id_table, pdev) != NULL;
-
-
/* fall-back to driver name match */
-
return (strcmp(pdev->name, drv->name) == 0);
-
}
其中platform_match_id()函数:
-
static const struct platform_device_id *platform_match_id(
-
struct platform_device_id *id,
-
struct platform_device *pdev)
-
{
-
while (id->name[0]) {
-
if (strcmp(pdev->name, id->name) == 0) {
-
pdev->id_entry = id;
-
return id;
-
}
-
id++;
-
}
-
return NULL;
-
}
而在关于I2C平台设备驱动中pdev->id_entry指向的是:
-
static struct platform_device_id s3c24xx_driver_ids[] = {
-
{
-
.name = "s3c2410-i2c",
-
.driver_data = TYPE_S3C2410,
-
}, {
-
.name = "s3c2440-i2c",
-
.driver_data = TYPE_S3C2440,
-
}, { },
-
};
pdev->id_entry = id,由此赋值语句可知,最终指向s3c24xx_driver_ids数组的第二个单元。太神奇啦!
当match匹配的时候会调用driver_probe_device()函数,此函数通过调用really_probe()函数,然后通过driver_sysfs_add()函数在/sys/bus/platform/drivers/目录下创建一个名为“s3c2440-i2c”的指向/sys/devices/platform/s3c2440-i2c目录的链接文件和在/sys/devices/platform/ s3c2440-i2c目录下名为“driver”的指向/sys/bus/platform/drivers/s3c-i2c的链接文件。
然后通过drv->probe(dev)此调用s3c24xx_i2c_probe()函数,此函数还回后通过driver_bound()函数绑定设备与驱动,driver_bound()函数主要通过klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices)语句把设备绑定到对应驱动链表的设备链表上(/sys/bus/platform/s3c-i2c/devices)。
至此,整个探测过程算是完成,然后继续回到device_add()函数,接着通过klist_add_tail()函数把设备挂到其父设备节点,也就是/sys/devices/platform.。到此处整个平台设备注册才算完成。
下面在简单的分析一下平台设备驱动的注册过程,在mini2440中通过platform_driver_register(&s3c24xx_i2c_driver)注册i2c平台设备驱动,其中s3c24xx_i2c_driver为:
-
static struct platform_driver s3c24xx_i2c_driver = {
-
.probe = s3c24xx_i2c_probe,
-
.remove = s3c24xx_i2c_remove,
-
.id_table = s3c24xx_driver_ids,
-
.driver = {
-
.owner = THIS_MODULE,
-
.name = "s3c-i2c",
-
.pm = S3C24XX_DEV_PM_OPS,
-
},
-
};
platform_driver_register()函数通过赋值语句把platform_bus_type、s3c24xx_i2c_probe和s3c24xx_i2c_remove两个函数赋给platform_driver里边的元素driver.bus、driver.probe和driver.remove,这也是刚才为什么能够通过调用realprobe()函数获得s3c24xx_i2c_probe()函数的的原因,最后此函数通过调用driver_register()函数对驱动进行注册。
driver_register()函数会通过driver_find()函数查看是否重复注册,然后会接着调用bus_add_driver()函数进行注册。
bus_add_driver()函数通过priv->kobj.kset = bus->p->drivers_kset;和error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name)向sys文件系统注册,此时会在/sys/bus/platform/drivers目下创建一个名为“s3c-i2c”的目录,接着就会调用driver_attach()函数,进行驱动找设备的过程,真是设备找驱动,驱动找设备,是不是像这个世界“男的找女的,女的找男的,然后结婚(绑定)”?哈哈!!!
接着会调用klist_add_tail()函数把驱动添加到平台设备的bus下的p->klist_drivers(我喜欢理解成/sys/bus/platform/drivers下,devices可是/sys/bus/platform/devices下,呵呵,呼应的很),然后会调用driver_create_file()函数在/sys/bus/platform/drivers目录下创建只有root用户可写的“uevent”文件,然后通过调用add_bind_files()函数在创建两个只有root用户可写的“bind”和“ubind”属性文件。
至此,整个平台设备于平台驱动探索和绑定过程分析完毕,其实所有的设备模型的探索路径和其具有相仿的路径,平台设备总线是linux内核引入的一种统一管理设备的虚拟总线,但是也是分析和理解其它总线的基础,呵呵。当然在此并没有对它进行详细分析,详细分析,属性文件。kobject、kset、bus、device、driver等设备模型机制LDD3、linux内核设计与实现和复旦abc以及http://blogold.chinaunix.net/u3/92745/article.html都讲解的很经典,值得反复阅读。
在BSP文件中,static void __init mini2440_init(void)函数中,首先调用s3c_i2c0_set_platdata()函数,此函数作用是初始化控制器平台相关数据:
void __init s3c_i2c0_set_platdata
(struct s3c2410_platform_i2c
*pd
)
-
{
-
struct s3c2410_platform_i2c *npd;
-
-
if (!pd)
-
pd = &default_i2c_data0;
-
-
npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
-
if (!npd)
-
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
-
else if (!npd->cfg_gpio)
-
npd->cfg_gpio = s3c_i2c0_cfg_gpio;
-
-
s3c_device_i2c0.dev.platform_data = npd;
-
}
下面是24XX系类关于i2c平台设备驱动的probe()函数:
阅读(2912) | 评论(0) | 转发(0) |