Android学习之Platform总线 1

借鉴http://wenku.baidu.com/view/107d915377232f60ddcca113.html学习i.mx53

1.platform

Platform 总线是 2.6kernel引入的一种虚拟总线,用来管理CPU的片上资源,具有更好的移植性,很多驱动都用platform改写了。

platform_bus_typeKernel_imx/drivers/base/platform.c中定义如下:

struct bus_type platform_bus_type = {

       .name             = "platform",

       .dev_attrs       = platform_dev_attrs,

       .match            = platform_match,

       .uevent           = platform_uevent,

       .pm         = &platform_dev_pm_ops,

};

EXPORT_SYMBOL_GPL(platform_bus_type);

bus_typekernel_imx/include/linux/device.h 中定义如下

 

struct bus_type {

       const char              *name;

       struct bus_attribute *bus_attrs;

       struct device_attribute    *dev_attrs;

       struct driver_attribute    *drv_attrs;

 

       int (*match)(struct device *dev, struct device_driver *drv);

       int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

       int (*probe)(struct device *dev);

       int (*remove)(struct device *dev);

       void (*shutdown)(struct device *dev);

 

       int (*suspend)(struct device *dev, pm_message_t state);

       int (*resume)(struct device *dev);

 

       const struct dev_pm_ops *pm;

 

       struct bus_type_private *p;

};

 

可见,platform其只是bus_type的一种。

总线bus是联系drivedevice的中间枢纽,Device通过所属的bus找到driver,由match操作方法进行匹配。

 

2 deviceplatform_device

Platform device会有一个名字用于driver binding,另外IRQ以及地址空间等资源也要给出。

Platform_device 结构体用来描述设备的名称、资源信息等。定义在

kernel_imx/include/linux/platform_device.h 中定义如下

struct platform_device {

       const char       * name;

       int          id;

       struct device   dev;

       u32         num_resources;

       struct resource * resource;

 

       const struct platform_device_id     *id_entry;

 

       /* arch specific additions */

       struct pdev_archdata      archdata;

};

其封装了struct devicestruct resource,可知platform_devicedevice派生而来。

 

struct resource {

       resource_size_t start;

       resource_size_t end;

       const char *name;

       unsigned long flags;

       struct resource *parent, *sibling, *child;

};

 

 

struct device {

       struct device          *parent;

 

       struct device_private      *p;

 

       struct kobject kobj;

       const char              *init_name; /* initial name of the device */

       struct device_type   *type;

 

       struct mutex           mutex;    /* mutex to synchronize calls to

                                    * its driver.

                                    */

 

       struct bus_type       *bus;              /* type of bus device is on */

       struct device_driver *driver;  /* which driver has allocated this

                                      device */

       void        *platform_data;      /* Platform specific data, device

                                      core doesn't touch it */

       struct dev_pm_info power;

 

#ifdef CONFIG_NUMA

       int          numa_node;    /* NUMA node this device is close to */

#endif

       u64         *dma_mask;   /* dma mask (if dma'able device) */

       u64         coherent_dma_mask;/* Like dma_mask, but for

                                        alloc_coherent mappings as

                                        not all hardware supports

                                        64 bit addresses for consistent

                                        allocations such descriptors. */

 

       struct device_dma_parameters *dma_parms;

 

       struct list_head       dma_pools;     /* dma pools (if dma'ble) */

 

       struct dma_coherent_mem     *dma_mem; /* internal for coherent mem

                                        override */

       /* arch specific additions */

       struct dev_archdata archdata;

#ifdef CONFIG_OF

       struct device_node  *of_node;

#endif

 

       dev_t                    devt;       /* dev_t, creates the sysfs "dev" */

 

       spinlock_t              devres_lock;

       struct list_head       devres_head;

 

       struct klist_node     knode_class;

       struct class             *class;

       const struct attribute_group **groups;  /* optional groups */

 

       void (*release)(struct device *dev);

}

 

3 device_registerplatform_device_register

3.1

Kernel_imx/drivers/base/core.c

int device_register(struct device *dev)

{

       device_initialize(dev);

       return device_add(dev);

}

/**

 * device_add - add device to device hierarchy.

 * @dev: device.

 *

 * This is part 2 of device_register(), though may be called

 * separately _iff_ device_initialize() has been called separately.

 *

 * This adds @dev to the kobject hierarchy via kobject_add(), adds it

 * to the global and sibling lists for the device, then

 * adds it to the other relevant subsystems of the driver model.

 *

 * NOTE: _Never_ directly free @dev after calling this function, even

 * if it returned an error! Always use put_device() to give up your

 * reference instead.

 */

int device_add(struct device *dev)

{

       struct device *parent = NULL;

       struct class_interface *class_intf;

       int error = -EINVAL;

 

       dev = get_device(dev);

       if (!dev)

              goto done;

 

       if (!dev->p) {

              error = device_private_init(dev);

              if (error)

                     goto done;

       }

 

       /*

        * for statically allocated devices, which should all be converted

        * some day, we need to initialize the name. We prevent reading back

        * the name, and force the use of dev_name()

        */

       if (dev->init_name) {

              dev_set_name(dev, "%s", dev->init_name);

              dev->init_name = NULL;

       }

 

       if (!dev_name(dev)) {

              error = -EINVAL;

              goto name_error;

       }

 

       pr_debug("device: '%s': %s/n", dev_name(dev), __func__);

 

       parent = get_device(dev->parent);

       setup_parent(dev, parent);

 

       /* use parent numa_node */

       if (parent)

              set_dev_node(dev, dev_to_node(parent));

 

       /* first, register with generic layer. */

       /* we require the name to be set before, and pass NULL */

       error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

       if (error)

              goto Error;

 

       /* notify platform of device entry */

       if (platform_notify)

              platform_notify(dev);

 

       error = device_create_file(dev, &uevent_attr);

       if (error)

              goto attrError;

 

       if (MAJOR(dev->devt)) {

              error = device_create_file(dev, &devt_attr);

              if (error)

                     goto ueventattrError;

 

              error = device_create_sys_dev_entry(dev);

              if (error)

                     goto devtattrError;

 

              devtmpfs_create_node(dev);

       }

 

       error = device_add_class_symlinks(dev);

       if (error)

              goto SymlinkError;

       error = device_add_attrs(dev);

       if (error)

              goto AttrsError;

       error = bus_add_device(dev);

       if (error)

              goto BusError;

       error = dpm_sysfs_add(dev);

       if (error)

              goto DPMError;

       device_pm_add(dev);

 

       /* Notify clients of device addition.  This call must come

        * after dpm_sysf_add() and before kobject_uevent().

        */

       if (dev->bus)

              blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

                                        BUS_NOTIFY_ADD_DEVICE, dev);

 

       kobject_uevent(&dev->kobj, KOBJ_ADD);

       bus_probe_device(dev);

       if (parent)

              klist_add_tail(&dev->p->knode_parent,

                            &parent->p->klist_children);

 

       if (dev->class) {

              mutex_lock(&dev->class->p->class_mutex);

              /* tie the class to the device */

              klist_add_tail(&dev->knode_class,

                            &dev->class->p->class_devices);

 

              /* notify any interfaces that the device is here */

              list_for_each_entry(class_intf,

                                &dev->class->p->class_interfaces, node)

                     if (class_intf->add_dev)

                            class_intf->add_dev(dev, class_intf);

              mutex_unlock(&dev->class->p->class_mutex);

       }

done:

       put_device(dev);

       return error;

 DPMError:

       bus_remove_device(dev);

 BusError:

       device_remove_attrs(dev);

 AttrsError:

       device_remove_class_symlinks(dev);

 SymlinkError:

       if (MAJOR(dev->devt))

              devtmpfs_delete_node(dev);

       if (MAJOR(dev->devt))

              device_remove_sys_dev_entry(dev);

 devtattrError:

       if (MAJOR(dev->devt))

              device_remove_file(dev, &devt_attr);

 ueventattrError:

       device_remove_file(dev, &uevent_attr);

 attrError:

       kobject_uevent(&dev->kobj, KOBJ_REMOVE);

       kobject_del(&dev->kobj);

 Error:

       cleanup_device_parent(dev);

       if (parent)

              put_device(parent);

name_error:

       kfree(dev->p);

       dev->p = NULL;

       goto done;

}

3.2

Kernel_imx/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)

{

       int i, ret = 0;

 

       if (!pdev)

              return -EINVAL;

 

       if (!pdev->dev.parent)

              pdev->dev.parent = &platform_bus;

 

       pdev->dev.bus = &platform_bus_type;

 

       if (pdev->id != -1)

              dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);

       else

              dev_set_name(&pdev->dev, "%s", pdev->name);

 

       for (i = 0; i < pdev->num_resources; i++) {

              struct resource *p, *r = &pdev->resource[i];

 

              if (r->name == NULL)

                     r->name = dev_name(&pdev->dev);

 

              p = r->parent;

              if (!p) {

                     if (resource_type(r) == IORESOURCE_MEM)

                            p = &iomem_resource;

                     else if (resource_type(r) == IORESOURCE_IO)

                            p = &ioport_resource;

              }

 

              if (p && insert_resource(p, r)) {

                     printk(KERN_ERR

                            "%s: failed to claim resource %d/n",

                            dev_name(&pdev->dev), i);

                     ret = -EBUSY;

                     goto failed;

              }

       }

 

       pr_debug("Registering platform device '%s'. Parent at %s/n",

               dev_name(&pdev->dev), dev_name(pdev->dev.parent));

 

       ret = device_add(&pdev->dev);

       if (ret == 0)

              return ret;

 

 failed:

       while (--i >= 0) {

              struct resource *r = &pdev->resource[i];

              unsigned long type = resource_type(r);

 

              if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

                     release_resource(r);

       }

 

       return ret;

}

EXPORT_SYMBOL_GPL(platform_device_add);

 

Device_register()platform_device_regist()都会先初始化设备,区别在于platform_device_add()包括了device_add(),不过要先注册resources,然后将设备挂接到特定的platform总线。

 

 

4 device_driverplatform_driver

 platform_device 是一种device,自己本身不会做事情,要有人为它做,那就是platform driverPlatform driver遵循linux系统的driver model。对于devicediscovery/enumerate都不是driver自己完成的而是由系统的driver注册机制完成。Driver编写人员只要将注册必须的数据结构初始化并调用注册driverkernel API就可以了

platform_driver结构体的原型定义在

Kernel_imx/include/linux/platform_device.h

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;

       const struct platform_device_id *id_table;

};

它包含了设备操作的几个功能函数,同时包含了一个device_driver结构。Device_driverplatform_driver的基类。

 

struct device_driver {

       const char              *name;

       struct bus_type              *bus;

 

       struct module         *owner;

       const char              *mod_name;   /* used for built-in modules */

 

       bool suppress_bind_attrs;      /* disables bind/unbind via sysfs */

 

#if defined(CONFIG_OF)

       const struct of_device_id       *of_match_table;

#endif

 

       int (*probe) (struct device *dev);

       int (*remove) (struct device *dev);

       void (*shutdown) (struct device *dev);

       int (*suspend) (struct device *dev, pm_message_t state);

       int (*resume) (struct device *dev);

       const struct attribute_group **groups;

 

       const struct dev_pm_ops *pm;

 

       struct driver_private *p;

};

device_driver提供了一些接口,但是并没有实现,相当于虚函数,由派生类platform_devier进行重载。

nameowner主要用来和相关的platform_device关联起来,owner的作用是指模块的所有者,驱动程序中一般初始化为THIS_MODULE

Device_driver结构中也有一个name变量,内核正是通过这个一致性来为驱动程序找到资源,即platform_device中的resource

 

5 driver_registerplatform_driver_register

Kernel/drivers/base/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;

 

       return driver_register(&drv->driver);

}

EXPORT_SYMBOL_GPL(platform_driver_register);

 

struct device转化为struce platform_driver,然后调用platform_driver中的相应接口函数,这是linux内核的面向对象的设计思想。

/**

 * driver_register - register driver with bus

 * @drv: driver to register

 *

 * We pass off most of the work to the bus_add_driver() call,

 * since most of the things we have to do deal with the bus

 * structures.

 */

int driver_register(struct device_driver *drv)

{

       int ret;

       struct device_driver *other;

 

       BUG_ON(!drv->bus->p);

 

       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);

 

       other = driver_find(drv->name, drv->bus);

       if (other) {

              put_driver(other);

              printk(KERN_ERR "Error: Driver '%s' is already registered, "

                     "aborting.../n", drv->name);

              return -EBUSY;

       }

 

       ret = bus_add_driver(drv);

       if (ret)

              return ret;

       ret = driver_add_groups(drv, drv->groups);

       if (ret)

              bus_remove_driver(drv);

       return ret;

}

 

 

如果总线上的设备需要匹配,则验证是否匹配,调用platform_match,匹配成功,则将设备和driver使用driver_attach绑定起来。所以,我们要使platform_deviceplatform_driver中的name属性一致。

成功以后,调用probe接口,如果busdevice同时具备probe方法,则优先调用总线的probe。对于platform_driver来说,调用platform_drv_probe 来完成操作,至此,platform_driver成功挂接到platform bus上,并与特定的设备实现绑定,并对设备进行了probe处理。

 

6 busdevicedriver关系

Platform device包含device,根据device可以获得相应的busdriver

设备添加到总线上后形成一个双向循环链表,根据总线可以获得其上挂接的所有device,进而获得了platform device。根据device也可以获得驱动总线上所有设备的相关driver

Platform driver包含driver,根据driver可以获得相应的bus,进而获得bus上所有的device,进一步获得platform device,根据namedriverplatform device进行匹配,匹配成功后将device与相应的driver关联起来,即实现了platform deviceplatform driver的关联。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值