从DM9000驱动看platform device与driver的关系(转)


原文:http://blog.csdn.net/lights_joy/archive/2009/06/08/4251003.aspx

 

内核中的 platform driver 机制需要将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过 platform device 提供的标准接口进行申请并使用。这样可以提高驱动和资源管理的独立性。本文的目的就是希望弄清楚 platform device driver 之间的关系。

1.1     相关数据结构

1.1.1    device

这个结构体定义为:

struct device {

     struct klist       klist_children;

     struct klist_node  knode_parent;      /* node in sibling list */

     struct klist_node  knode_driver;

     struct klist_node  knode_bus;

     struct device      *parent;

 

     struct kobject kobj;

     char bus_id[BUS_ID_SIZE];   /* position on parent bus */

     struct device_type *type;

     unsigned       is_registered:1;

     unsigned       uevent_suppress:1;

     struct device_attribute uevent_attr;

     struct device_attribute *devt_attr;

 

     struct semaphore   sem; /* semaphore 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      *driver_data; /* data private to the driver */

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

 

     spinlock_t         devres_lock;

     struct list_head   devres_head;

 

     /* class_device migration path */

     struct list_head   node;

     struct class        *class ;

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

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

 

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

};

这个结构体有点复杂,不过我们暂时用不了这么多。

 

 

 

1.1.2    resource

这个结构体定义为:

/*

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

};

在这个结构体中, start end 的意义将根据 flags 中指定的资源类型进行解释。内核对资源进行了分类,一共有四种类型:

#define IORESOURCE_IO       0x00000100    /* Resource type */

#define IORESOURCE_MEM      0x00000200

#define IORESOURCE_IRQ      0x00000400

#define IORESOURCE_DMA      0x00000800

对于 DM9000 来说,其定义的资源如下:

static struct resource dm9000_bfin_resources[] = {

     {

         .start = 0x2C000000,

         .end = 0x2C000000 + 0x7F,

         .flags = IORESOURCE_MEM,

     }, {

         .start = IRQ_PF10,

         .end = IRQ_PF10,

         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,

     },

};

也就是说,它定义了两种类型的资源。从这里也可以看出 resource 结构体里面的 name 成员没有太大的用处。

 

1.1.3    platform_device

这个结构体定义为:

struct platform_device {

     const char     * name;

     u32      id;

     struct device dev;

     u32      num_resources;

     struct resource    * resource;

};

它对 device 加了一层包装,添加了 resource 的内容。看看 DM9000 的定义:

static struct platform_device dm9000_bfin_device = {

     .name = "dm9000" ,

     .id = -1,

     .num_resources = ARRAY_SIZE(dm9000_bfin_resources),

     .resource = dm9000_bfin_resources,

};

注意这里的 name

1.1.4    device_driver

这个结构体定义为:

struct device_driver {

     const char          * name;

     struct bus_type        * bus;

 

     struct kobject         kobj;

     struct klist       klist_devices;

     struct klist_node  knode_bus;

 

     struct module      * owner;

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

     struct module_kobject  * mkobj;

 

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

};

 

 

1.1.5    platform_driver

这个结构体定义为:

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 (*suspend_late)(struct platform_device *, pm_message_t state);

     int (*resume_early)(struct platform_device *);

     int (*resume)(struct platform_device *);

     struct device_driver driver;

};

它在 device_driver 的基础上封装了几个操作函数。

1.1.6    bus_type

这个结构体定义为:

struct bus_type {

     const char          * name;

     struct module      * owner;

 

     struct kset        subsys;

     struct kset        drivers;

     struct kset        devices;

     struct klist       klist_devices;

     struct klist       klist_drivers;

 

     struct blocking_notifier_head bus_notifier;

 

     struct bus_attribute   * bus_attrs;

     struct device_attribute * dev_attrs;

     struct driver_attribute * drv_attrs;

     struct bus_attribute drivers_autoprobe_attr;

     struct bus_attribute drivers_probe_attr;

 

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

     int       (*uevent)(struct device *dev, char **envp,

                     int num_envp, char *buffer, int buffer_size);

     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 (*suspend_late)(struct device * dev, pm_message_t state);

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

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

 

     unsigned int drivers_autoprobe:1;

};

 

 

 

1.2     资源注册

arch/blackfin/mach-bf561/boards/ezkit.c 中有这样的代码:

static int __init ezkit_init(void )

{

     int ret;

 

     printk(KERN_INFO "%s(): registering device resources/n" , __func__);

 

     ret = platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices));

     if (ret < 0)

         return ret;

 

     return 0;

}

 

arch_initcall(ezkit_init);

这里使用了 arch_initcall 来对 ezkit_init 函数进行调用次序的限制,而驱动的加载通常是使用 module_init 进行限制的,因此 ezkit_init 函数将先于驱动加载。

在这里 ezkit_devices 的定义为:

static struct platform_device *ezkit_devices[] __initdata = {

     &dm9000_bfin_device,

…………

};

1.2.1    platform_add_devices

这个函数比较简单:

/**

  *   platform_add_devices - add a numbers of platform devices

  *   @devs: array of platform devices to add

  *   @num: number of platform devices in array

  */

int platform_add_devices(struct platform_device **devs, int num)

{

     int i, ret = 0;

 

     for (i = 0; i < num; i++) {

         ret = platform_device_register(devs[i]);

         if (ret) {

              while (--i >= 0)

                   platform_device_unregister(devs[i]);

              break ;

         }

     }

 

     return ret;

}

为这个数组中的每个元素调用 platform_device_register ,如果出错则注销此前注册的所有 platform device

1.2.2    platform_device_register

这个函数的实现为:

/**

  *   platform_device_register - add a platform-level device

  *   @pdev:   platform device we're adding

  *

  */

int platform_device_register(struct platform_device * pdev)

{

     device_initialize(&pdev->dev);

     return platform_device_add(pdev);

}

也比较简单,先调用 device_initialize 初始化 platform_device::dev ,这里仅仅是对 device 结构体的成员赋初值,略过它不做分析。接下来的关键是 platform_device_add

1.2.3    platform_device_add

这个函数定义为:

/**

  *   platform_device_add - add a platform device to device hierarchy

  *   @pdev:   platform device we're adding

  *

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

  *   separately _iff_ pdev was allocated by platform_device_alloc().

  */

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)

         snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u" , pdev->name, pdev->id);

     else

         strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

 

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

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

 

         if (r->name == NULL)

              r->name = pdev->dev.bus_id;

 

         p = r->parent;

         if (!p) {

              if (r->flags & IORESOURCE_MEM)

                   p = &iomem_resource;

              else if (r->flags & IORESOURCE_IO)

                   p = &ioport_resource;

         }

 

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

              printk(KERN_ERR

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

                     pdev->dev.bus_id, i);

              ret = -EBUSY;

              goto failed;

         }

     }

 

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

           pdev->dev.bus_id, pdev->dev.parent->bus_id);

 

     ret = device_add(&pdev->dev);

     if (ret == 0)

         return ret;

 

  failed:

     while (--i >= 0)

         if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))

              release_resource(&pdev->resource[i]);

     return ret;

}

在这个函数里做了两件关键的事情,一个是注册 device 设备,它将 device::bus 指定为 platform_bus_type 。另一个是注册 resource 。看下面的这几行代码:

         if (!p) {

              if (r->flags & IORESOURCE_MEM)

                   p = &iomem_resource;

              else if (r->flags & IORESOURCE_IO)

                   p = &ioport_resource;

         }

对照 DM9000 的资源定义:

static struct resource dm9000_bfin_resources[] = {

     {

         .start = 0x2C000000,

         .end = 0x2C000000 + 0x7F,

         .flags = IORESOURCE_MEM,

     }, {

         .start = IRQ_PF10,

         .end = IRQ_PF10,

         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,

     },

};

它的中断资源并没有进行注册。

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

{

…………………… ..

 

     if ((error = device_add_attrs(dev)))

         goto AttrsError;

     if ((error = device_pm_add(dev)))

         goto PMError;

     if ((error = bus_add_device(dev)))

         goto BusError;

………………… ..

}

这里有一个关键调用 bus_add_device ,它将把 dev 添加到 platform_bus_type 这一全局变量中的列表。

1.2.5    bus_add_device

这个函数定义为:

/**

  *   bus_add_device - add device to bus

  *   @dev:    device being added

  *

  *   - Add the device to its bus's list of devices.

  *   - Create link to device's bus.

  */

int bus_add_device(struct device * dev)

{

     struct bus_type * bus = get_bus(dev->bus);

     int error = 0;

 

     if (bus) {

         pr_debug("bus %s: add device %s/n" , bus->name, dev->bus_id);

         error = device_add_attrs(bus, dev);

         if (error)

              goto out_put;

         error = sysfs_create_link(&bus->devices.kobj,

                            &dev->kobj, dev->bus_id);

         if (error)

              goto out_id;

         error = sysfs_create_link(&dev->kobj,

                   &dev->bus->subsys.kobj, "subsystem" );

         if (error)

              goto out_subsys;

         error = make_deprecated_bus_links(dev);

         if (error)

              goto out_deprecated;

     }

     return 0;

 

out_deprecated:

     sysfs_remove_link(&dev->kobj, "subsystem" );

out_subsys:

     sysfs_remove_link(&bus->devices.kobj, dev->bus_id);

out_id:

     device_remove_attrs(bus, dev);

out_put:

     put_bus(dev->bus);

     return error;

}

注意当执行到此函数时 dev->bus 指向 platform_bus_type 这一全局变量,因而这一函数将把 dev 添加到 platform_bus_type 的链表中。

 

 

1.3     驱动注册

下面是 DM9000 网卡的驱动加载代码:

static int __init

dm9000_init(void )

{

     printk(KERN_INFO "%s Ethernet Driver/n" , CARDNAME);

 

     return platform_driver_register(&dm9000_driver);   /* search board and register */

}

module_init(dm9000_init);

很简单的代码,直接调用 platform_driver_register 注册驱动,这里 dm9000_driver 的定义为:

static struct platform_driver dm9000_driver = {

     .driver  = {

         .name    = "dm9000" ,

         .owner   = THIS_MODULE,

     },

     .probe   = dm9000_probe,

     .remove  = dm9000_drv_remove,

     .suspend = dm9000_drv_suspend,

     .resume  = dm9000_drv_resume,

};

 

1.3.1    platform_driver_register

这个函数定义为:

/**

  *   platform_driver_register

  *   @drv: platform driver structure

  */

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

}

注意由于 DM9000 platform_driver 中指定了 probe remove suspend resume 这四个函数,因此 device_driver 结构体中的这几个函数指针将进行初始化设置。最后再调用 driver_register 注册 driver 成员,有点奇怪,怎么就抛弃了 platform_driver 呢?

1.3.2    driver_register

这个函数定义为:

/**

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

{

     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->bus 指向的是 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,

};

 

1.3.3    bus_add_driver

这个函数定义为:

/**

  *   bus_add_driver - Add a driver to the bus.

  *   @drv:    driver.

  *

  */

int bus_add_driver(struct device_driver *drv)

{

     struct bus_type * bus = get_bus(drv->bus);

     int error = 0;

 

     if (!bus)

         return -EINVAL;

 

     pr_debug("bus %s: add driver %s/n" , bus->name, drv->name);

     error = kobject_set_name(&drv->kobj, "%s" , drv->name);

     if (error)

         goto out_put_bus;

     drv->kobj.kset = &bus->drivers;

     if ((error = kobject_register(&drv->kobj)))

         goto out_put_bus;

 

     if (drv->bus->drivers_autoprobe) {

         error = driver_attach(drv);

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

     if (error) {

         /* How the hell do we get out of this pickle? Give up */

         printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n" ,

              __FUNCTION__, drv->name);

     }

     error = add_bind_files(drv);

     if (error) {

         /* Ditto */

         printk(KERN_ERR "%s: add_bind_files(%s) failed/n" ,

              __FUNCTION__, drv->name);

     }

 

     return error;

out_unregister:

     kobject_unregister(&drv->kobj);

out_put_bus:

     put_bus(bus);

     return error;

}

当函数执行到此的时候, drv->bus 将指向 platform_bus_type 这一全局变量,而这一全局变量的 drivers_autoprobe 成员在 bus_register 这一全局初始化函数中设置为 1 。因此这里将调用 driver_attach 函数,注意此时传递进去的参数 drv 指向的是 dm9000_driver driver 成员。

1.3.4    driver_attach

这一函数定义为:

/**

  *   driver_attach - try to bind driver to devices.

  *   @drv:    driver.

  *

  *   Walk the list of devices that the bus has on it and try to

  *   match the driver with each one.  If driver_probe_device()

  *   returns 0 and the @dev->driver is set, we've found a

  *   compatible pair.

  */

int driver_attach(struct device_driver * drv)

{

     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

很简单,转向 bus_for_each_dev

1.3.5    bus_for_each_dev

这一函数定义为:

/**

  *   bus_for_each_dev - device iterator.

  *   @bus:    bus type.

  *   @start:  device to start iterating from.

  *   @data:   data for the callback.

  *   @fn: function to be called for each device.

  *

  *   Iterate over @bus's list of devices, and call @fn for each,

  *   passing it @data. If @start is not NULL, we use that device to

  *   begin iterating from.

  *

  *   We check the return of @fn each time. If it returns anything

  *   other than 0, we break out and return that value.

  *

  *   NOTE: The device that returns a non-zero value is not retained

  *   in any way, nor is its refcount incremented. If the caller needs

  *   to retain this data, it should do, and increment the reference

  *   count in the supplied callback.

  */

 

int bus_for_each_dev(struct bus_type * bus, struct device * start,

              void * data, int (*fn)(struct device *, void *))

{

     struct klist_iter i;

     struct device * dev;

     int error = 0;

 

     if (!bus)

         return -EINVAL;

 

     klist_iter_init_node(&bus->klist_devices, &i,

                   (start ? &start->knode_bus : NULL));

     while ((dev = next_device(&i)) && !error)

         error = fn(dev, data);

     klist_iter_exit(&i);

     return error;

}

简单枚举此总线上注册的 device ,然后为其调用 __driver_attach 函数,试图将一个 device 和传递进来的 driver 相匹配。

1.3.6    __driver_attach

这一函数定义为:

static int __driver_attach(struct device * dev, void * data)

{

     struct device_driver * drv = data;

 

     /*

       * Lock device and try to bind to it. We drop the error

       * here and always return 0, because we need to keep trying

       * to bind to devices and some drivers will return an error

       * simply if it didn't support the device.

       *

       * driver_probe_device() will spit a warning if there

       * is an error.

       */

 

     if (dev->parent)   /* Needed for USB */

         down(&dev->parent->sem);

     down(&dev->sem);

     if (!dev->driver)

         driver_probe_device(drv, dev);

     up(&dev->sem);

     if (dev->parent)

         up(&dev->parent->sem);

 

     return 0;

}

很简单,转而调用 driver_probe_device 进行驱动的匹配。

1.3.7    driver_probe_device

这个函数定义为:

/**

  * driver_probe_device - attempt to bind device & driver together

  * @drv: driver to bind a device to

  * @dev: device to try to bind to the driver

  *

  * First, we call the bus's match function, if one present, which should

  * compare the device IDs the driver supports with the device IDs of the

  * device. Note we don't do this ourselves because we don't know the

  * format of the ID structures, nor what is to be considered a match and

  * what is not.

  *

  * This function returns 1 if a match is found, -ENODEV if the device is

  * not registered, and 0 otherwise.

  *

  * This function must be called with @dev->sem held.  When called for a

  * USB interface, @dev->parent->sem must be held as well.

  */

int driver_probe_device(struct device_driver * drv, struct device * dev)

{

     int ret = 0;

 

     if (!device_is_registered(dev))

         return -ENODEV;

     if (drv->bus->match && !drv->bus->match(dev, drv))

         goto done;

 

     pr_debug("%s: Matched Device %s with Driver %s/n" ,

           drv->bus->name, dev->bus_id, drv->name);

 

     ret = really_probe(dev, drv);

 

done:

     return ret;

}

此时的 drv->bus 指向 platform_bus_type 这一全局变量,而它的 match 函数为 platform_match ,且让我们看看它是如何确定 device driver 是否匹配的。

/**

  *   platform_match - bind platform device to platform driver.

  *   @dev:    device.

  *   @drv:    driver.

  *

  *   Platform device IDs are assumed to be encoded like this:

  *   "<name><instance>", where <name> is a short description of the

  *   type of device, like "pci" or "floppy", and <instance> is the

  *   enumerated instance of the device, like '0' or '42'.

  *   Driver IDs are simply "<name>".

  *   So, extract the <name> from the platform_device structure,

  *   and compare it against the name of the driver. Return whether

  *   they match or not.

  */

 

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

}

也就是说,它通过比较 pdev->name drv->name 是否匹配来决定。

对于 DM9000 的驱动来说,这里的 pdev 指向 dm9000_bfin_device ,看看它的初始值:

static struct platform_device dm9000_bfin_device = {

     .name = "dm9000" ,

     .id = -1,

     .num_resources = ARRAY_SIZE(dm9000_bfin_resources),

     .resource = dm9000_bfin_resources,

};

再看 drv ,其指向 dm9000_driver 这一变量中的 driver 成员。

static struct platform_driver dm9000_driver = {

     .driver  = {

         .name    = "dm9000" ,

         .owner   = THIS_MODULE,

     },

     .probe   = dm9000_probe,

     .remove  = dm9000_drv_remove,

     .suspend = dm9000_drv_suspend,

     .resume  = dm9000_drv_resume,

};

在进行了正确的名称匹配之后,将调用 really_probe 进行硬件检测。

1.3.8    really_probe

这一函数定义为:

static int really_probe(struct device *dev, struct device_driver *drv)

{

     int ret = 0;

 

     atomic_inc(&probe_count);

     pr_debug("%s: Probing driver %s with device %s/n" ,

           drv->bus->name, drv->name, dev->bus_id);

     WARN_ON(!list_empty(&dev->devres_head));

 

     dev->driver = drv;

     if (driver_sysfs_add(dev)) {

         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n" ,

              __FUNCTION__, dev->bus_id);

         goto probe_failed;

     }

 

     if (dev->bus->probe) {

         ret = dev->bus->probe(dev);

         if (ret)

              goto probe_failed;

     } else if (drv->probe) {

         ret = drv->probe(dev);

         if (ret)

              goto probe_failed;

     }

 

     driver_bound(dev);

     ret = 1;

     pr_debug("%s: Bound Device %s to Driver %s/n" ,

           drv->bus->name, dev->bus_id, drv->name);

     goto done;

 

probe_failed:

     devres_release_all(dev);

     driver_sysfs_remove(dev);

     dev->driver = NULL;

 

     if (ret != -ENODEV && ret != -ENXIO) {

         /* driver matched but the probe failed */

         printk(KERN_WARNING

                "%s: probe of %s failed with error %d/n" ,

                drv->name, dev->bus_id, ret);

     }

     /*

       * Ignore errors returned by ->probe so that the next driver can try

       * its luck.

       */

     ret = 0;

done:

     atomic_dec(&probe_count);

     wake_up(&probe_waitqueue);

     return ret;

}

此时的 drv->bus 指向 platform_bus_type 这一全局变量,其 probe 回调函数没有指定,而 drv->probe 函数则指向 dm9000_probe 。因此转向 dm9000_probe 执行,并将 dm9000_bfin_device 做为参数传递进去。

1.4     结论

platform device driver 分别向 platform_bus_type 这一中介注册,并通过名称进行相互间的匹配。很是有点婚姻中介的味道,还有点对暗号的神秘,呵呵!

 

 

2        参考资料

DM9000 驱动看 platform device driver 的关系 2009-6-8

uclinux 内核驱动的初始化顺序 (2009-6-7)

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值