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

 

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

  

本文适用于

ADSP-BF561

uclinux-2008r1.5-rc3 (smp patch)

Visual DSP++ 5.0(update 5)

 

欢迎转载,但请保留作者信息

 

 

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

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;

};

在这个结构体中,startend的意义将根据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);

}

注意由于DM9000platform_driver中指定了proberemovesuspendresume这四个函数,因此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_driverdriver成员。

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,且让我们看看它是如何确定devicedriver是否匹配的。

/**

 *   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->namedrv->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 devicedriver分别向platform_bus_type这一中介注册,并通过名称进行相互间的匹配。很是有点婚姻中介的味道,还有点对暗号的神秘,呵呵!

 

 

 

2       参考资料

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

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

 

 

 

 

 

 

你可能感兴趣的:(struct,Module,Semaphore,hierarchy,resources,deprecated)