Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2998712
  • 博文数量: 674
  • 博客积分: 17881
  • 博客等级: 上将
  • 技术积分: 4849
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 10:15
文章分类

全部博文(674)

文章存档

2013年(34)

2012年(146)

2011年(197)

2010年(297)

分类: LINUX

2010-06-15 18:35:10

内核中的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:
 *   "", where is a short description of the
 *   type of device, like "pci" or "floppy", and is the
 *   enumerated instance of the device, like '0' or '42'.
 *   Driver IDs are simply "".
 *   So, extract the 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这一中介注册,并通过名称进行相互间的匹配。很是有点婚姻中介的味道,还有点对暗号的神秘,呵呵!
 
阅读(1552) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~