platform总线--从设备找到驱动的过程

来源:互联网 发布:erp管理系统源码 编辑:程序博客网 时间:2024/06/06 15:48


  platform总线驱动的注册过程,和input设备 驱动注册过程很像,都是逐个遍历设备,检查是否和驱动匹配。

  由此联想,platform总线加载设备的过程,应该也是遍历驱动,看是否和设备匹配。


2.1 注册设备和总线类型

注册设备使用的是 platform_device_add 函数---/driver/base/Platform.c

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);elsedev_set_name(&pdev->dev, "%s", pdev->name);

platform_device_add 函数第一部分是设置设备的 父设备 和 总线类型





6.2.2 注册设备的资源

  platform_device_add函数第二部分注册设备的资源:

 -------把设备IO端口和IO内存资源注册到系统

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

  注意 I/O端口设备控制寄存器) 和 I/O内存 注册 到系统 的代码.
  --设备基本概念一章,PCI总线通过扫描设备的配置文件,可以配置设备的I/O端口 和 I/O内存 ,platform总线在物理上并不存在,不能自动扫描,如何配置如下:
  --从内核驱动中,platform设备 很多使用探测的方式:--一般先往一个I/O端口写数据,看是否回应判断设备的 I/O端口是否存在。至于本章的q40kbd设备,他甚至没有注册自己的I/O端口和内存,而是直接使用缺省值,这是一个很古老的设备。

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

啊啊

  pdev->resource[i]各个部分的分析:

/* * Resources are tree-like, allowing * nesting etc.. */struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;};

  platform_device 结构体介绍:

struct platform_device {const char* name;intid;struct devicedev;u32num_resources;struct resource* resource;const struct platform_device_id*id_entry;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdataarchdata;};




2.3 增加一个设备对象

  platform_device_add 函数第三部分调用 device_add 增加一个设备对象 ,代码:

/** *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 it 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. */int device_add(struct device *dev){struct device *parent = NULL;char *class_name = NULL;int error = -EINVAL;dev = get_device(dev);if (!dev || !strlen(dev->bus_id))goto Error;parent = get_device(dev->parent);//--获取父设备-----------------------------------------pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);/* first, register with generic layer. */kobject_set_name(&dev->kobj, "%s", dev->bus_id);if (parent)dev->kobj.parent = &parent->kobj;//--在sys目录生成设备目录-----------------------if ((error = kobject_add(&dev->kobj)))goto Error;//--设置uevent属性--------------------------------dev->uevent_attr.attr.name = "uevent";dev->uevent_attr.attr.mode = S_IWUSR;if (dev->driver)dev->uevent_attr.attr.owner = dev->driver->owner;dev->uevent_attr.store = store_uevent;device_create_file(dev, &dev->uevent_attr);

    函数device_add第一部分要为设备在sys目录创建目录和uevent属性文件。这些内容出现多次,不分析

//设备的属性文件if (MAJOR(dev->devt)) {struct device_attribute *attr;attr = kzalloc(sizeof(*attr), GFP_KERNEL);if (!attr) {error = -ENOMEM;goto PMError;}attr->attr.name = "dev";attr->attr.mode = S_IRUGO;if (dev->driver)attr->attr.owner = dev->driver->owner;attr->show = show_dev;error = device_create_file(dev, attr);if (error) {kfree(attr);goto attrError;}dev->devt_attr = attr;}//--创建设备的符号链接if (dev->class) {sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,  "subsystem");sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,  dev->bus_id);sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");class_name = make_class_name(dev->class->name, &dev->kobj);sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);}//--设备的能源管理if ((error = device_pm_add(dev)))goto PMError;if ((error = bus_add_device(dev)))goto BusError;kobject_uevent(&dev->kobj, KOBJ_ADD);

    第二部分为设备创建一堆符号链接和设备属性文件

bus_attach_device(dev);if (parent)klist_add_tail(&dev->knode_parent, &parent->klist_children);

    第三部分调用bus_attach_device 把设备注册到总线,代码如下:

/** *bus_attach_device - add device to bus *@dev:device tried to attach to a driver * *- Try to attach to driver. */void bus_attach_device(struct device * dev){struct bus_type * bus = dev->bus;if (bus) {device_attach(dev);klist_add_tail(&dev->knode_bus, &bus->klist_devices);}}/** *device_attach - try to attach device to a driver. *@dev:device. * *Walk the list of drivers that the bus has and call *driver_probe_device() for each pair. If a compatible *pair is found, break out and return. * *Returns 1 if the device was bound to a driver; *0 if no matching device was found; error code otherwise. * *When called for a USB interface, @dev->parent->sem must be held. */int device_attach(struct device * dev){int ret = 0;down(&dev->sem);if (dev->driver) {//--设备已经有了驱动,执行sysfs的连接和链表操作---------------------device_bind_driver(dev);ret = 1;} elseret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);up(&dev->sem);return ret;}

  上一节添加驱动到总线的处理函数函数是bus_for_each_dev,添加设备到总线的处理函数bus_for_each_drv;
  --bus_for_each_dev:遍历设置,为驱动寻找合适设备
  --bus_for_each_drv:遍历驱动,为设备寻找合适的驱动


  platform总线驱动的注册过程,和input设备 驱动注册过程很像,都是逐个遍历设备,检查是否和驱动匹配。

  由此联想,platform总线加载设备的过程,应该也是遍历驱动,看是否和设备匹配。


2.1 注册设备和总线类型

注册设备使用的是 platform_device_add 函数---/driver/base/Platform.c

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);elsedev_set_name(&pdev->dev, "%s", pdev->name);

platform_device_add 函数第一部分是设置设备的 父设备 和 总线类型





6.2.2 注册设备的资源

  platform_device_add函数第二部分注册设备的资源:

 -------把设备IO端口和IO内存资源注册到系统

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

  注意 I/O端口设备控制寄存器) 和 I/O内存 注册 到系统 的代码.
  --设备基本概念一章,PCI总线通过扫描设备的配置文件,可以配置设备的I/O端口 和 I/O内存 ,platform总线在物理上并不存在,不能自动扫描,如何配置如下:
  --从内核驱动中,platform设备 很多使用探测的方式:--一般先往一个I/O端口写数据,看是否回应判断设备的 I/O端口是否存在。至于本章的q40kbd设备,他甚至没有注册自己的I/O端口和内存,而是直接使用缺省值,这是一个很古老的设备。

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

啊啊

  pdev->resource[i]各个部分的分析:

/* * Resources are tree-like, allowing * nesting etc.. */struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;};

  platform_device 结构体介绍:

struct platform_device {const char* name;intid;struct devicedev;u32num_resources;struct resource* resource;const struct platform_device_id*id_entry;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdataarchdata;};




2.3 增加一个设备对象

  platform_device_add 函数第三部分调用 device_add 增加一个设备对象 ,代码:

/** *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 it 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. */int device_add(struct device *dev){struct device *parent = NULL;char *class_name = NULL;int error = -EINVAL;dev = get_device(dev);if (!dev || !strlen(dev->bus_id))goto Error;parent = get_device(dev->parent);//--获取父设备-----------------------------------------pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);/* first, register with generic layer. */kobject_set_name(&dev->kobj, "%s", dev->bus_id);if (parent)dev->kobj.parent = &parent->kobj;//--在sys目录生成设备目录-----------------------if ((error = kobject_add(&dev->kobj)))goto Error;//--设置uevent属性--------------------------------dev->uevent_attr.attr.name = "uevent";dev->uevent_attr.attr.mode = S_IWUSR;if (dev->driver)dev->uevent_attr.attr.owner = dev->driver->owner;dev->uevent_attr.store = store_uevent;device_create_file(dev, &dev->uevent_attr);

    函数device_add第一部分要为设备在sys目录创建目录和uevent属性文件。这些内容出现多次,不分析

//设备的属性文件if (MAJOR(dev->devt)) {struct device_attribute *attr;attr = kzalloc(sizeof(*attr), GFP_KERNEL);if (!attr) {error = -ENOMEM;goto PMError;}attr->attr.name = "dev";attr->attr.mode = S_IRUGO;if (dev->driver)attr->attr.owner = dev->driver->owner;attr->show = show_dev;error = device_create_file(dev, attr);if (error) {kfree(attr);goto attrError;}dev->devt_attr = attr;}//--创建设备的符号链接if (dev->class) {sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,  "subsystem");sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,  dev->bus_id);sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");class_name = make_class_name(dev->class->name, &dev->kobj);sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);}//--设备的能源管理if ((error = device_pm_add(dev)))goto PMError;if ((error = bus_add_device(dev)))goto BusError;kobject_uevent(&dev->kobj, KOBJ_ADD);

    第二部分为设备创建一堆符号链接和设备属性文件

bus_attach_device(dev);if (parent)klist_add_tail(&dev->knode_parent, &parent->klist_children);

    第三部分调用bus_attach_device 把设备注册到总线,代码如下:

/** *bus_attach_device - add device to bus *@dev:device tried to attach to a driver * *- Try to attach to driver. */void bus_attach_device(struct device * dev){struct bus_type * bus = dev->bus;if (bus) {device_attach(dev);klist_add_tail(&dev->knode_bus, &bus->klist_devices);}}/** *device_attach - try to attach device to a driver. *@dev:device. * *Walk the list of drivers that the bus has and call *driver_probe_device() for each pair. If a compatible *pair is found, break out and return. * *Returns 1 if the device was bound to a driver; *0 if no matching device was found; error code otherwise. * *When called for a USB interface, @dev->parent->sem must be held. */int device_attach(struct device * dev){int ret = 0;down(&dev->sem);if (dev->driver) {//--设备已经有了驱动,执行sysfs的连接和链表操作---------------------device_bind_driver(dev);ret = 1;} elseret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);up(&dev->sem);return ret;}

  上一节添加驱动到总线的处理函数函数是bus_for_each_dev,添加设备到总线的处理函数bus_for_each_drv;
  --bus_for_each_dev:遍历设置,为驱动寻找合适设备
  --bus_for_each_drv:遍历驱动,为设备寻找合适的驱动

原创粉丝点击