Chinaunix首页 | 论坛 | 博客
  • 博客访问: 170292
  • 博文数量: 27
  • 博客积分: 566
  • 博客等级: 中士
  • 技术积分: 487
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-07 19:07
文章分类

全部博文(27)

文章存档

2013年(23)

2012年(1)

2007年(3)

我的朋友

分类: 嵌入式

2013-06-17 16:14:37

Mini2440通过platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))对平台设备进行注册,在mini2440_devices平台设备数组指针中通过&s3c_device_i2c0包括I2c0的平台设备,下面是i2c平台设备和其资源:

  1. static struct resource s3c_i2c_resource[] = {
  2.     [0] = {
  3.         .start = S3C_PA_IIC,
  4.         .end = S3C_PA_IIC + SZ_4K - 1,
  5.         .flags = IORESOURCE_MEM,
  6.     },
  7.     [1] = {
  8.         .start = IRQ_IIC,
  9.         .end = IRQ_IIC,
  10.         .flags = IORESOURCE_IRQ,
  11.     },
  12. };

  13. struct platform_device s3c_device_i2c0 = {
  14.     .name = "s3c2410-i2c",
  15. #ifdef CONFIG_S3C_DEV_I2C1
  16.     .id = 0,
  17. #else
  18.     .id = -1,
  19. #endif
  20.     .num_resources = ARRAY_SIZE(s3c_i2c_resource),
  21.     .resource = s3c_i2c_resource,
  22. };
但是在此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结构,因为以后分析很可能要要用到此结构:

  1. static struct kobj_type device_ktype = {
  2.     .release = device_release,
  3.     .sysfs_ops = &dev_sysfs_ops,
  4. };

在调用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函数如下:

  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct platform_driver *pdrv = to_platform_driver(drv);

  5.     /* match against the id table first */
  6.     if (pdrv->id_table)
  7.         return platform_match_id(pdrv->id_table, pdev) != NULL;

  8.     /* fall-back to driver name match */
  9.     return (strcmp(pdev->name, drv->name) == 0);
  10. }
其中platform_match_id()函数:

  1. static const struct platform_device_id *platform_match_id(
  2.             struct platform_device_id *id,
  3.             struct platform_device *pdev)
  4. {
  5.     while (id->name[0]) {
  6.         if (strcmp(pdev->name, id->name) == 0) {
  7.             pdev->id_entry = id;
  8.             return id;
  9.         }
  10.         id++;
  11.     }
  12.     return NULL;
  13. }
而在关于I2C平台设备驱动中pdev->id_entry指向的是:

  1. static struct platform_device_id s3c24xx_driver_ids[] = {
  2.     {
  3.         .name = "s3c2410-i2c",
  4.         .driver_data = TYPE_S3C2410,
  5.     }, {
  6.         .name = "s3c2440-i2c",
  7.         .driver_data = TYPE_S3C2440,
  8.     }, { },
  9. };
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为:

  1. static struct platform_driver s3c24xx_i2c_driver = {
  2.     .probe = s3c24xx_i2c_probe,
  3.     .remove = s3c24xx_i2c_remove,
  4.     .id_table = s3c24xx_driver_ids,
  5.     .driver = {
  6.         .owner = THIS_MODULE,
  7.         .name = "s3c-i2c",
  8.         .pm = S3C24XX_DEV_PM_OPS,
  9.     },
  10. };
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)
  1. {
  2.     struct s3c2410_platform_i2c *npd;

  3.     if (!pd)
  4.         pd = &default_i2c_data0;

  5.     npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
  6.     if (!npd)
  7.         printk(KERN_ERR "%s: no memory for platform data\n", __func__);
  8.     else if (!npd->cfg_gpio)
  9.         npd->cfg_gpio = s3c_i2c0_cfg_gpio;

  10.     s3c_device_i2c0.dev.platform_data = npd;
  11. }
下面是24XX系类关于i2c平台设备驱动的probe()函数:



阅读(2898) | 评论(0) | 转发(0) |
0

上一篇:Rime笔记

下一篇:MACHINE_START

给主人留下些什么吧!~~