Chinaunix首页 | 论坛 | 博客
  • 博客访问: 984891
  • 博文数量: 153
  • 博客积分: 4195
  • 博客等级: 上校
  • 技术积分: 2631
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-22 11:32
文章存档

2012年(7)

2010年(35)

2009年(111)

分类:

2009-09-02 10:24:01

从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制设备用platform_device表示,驱动用platform_driver进行注册。

Linux platform driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备驱动的大致流程如图所示。


图 p
latform机制开发驱动流程

platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:

struct platform_device {

const char * name;    //定义平台设备的名称

int id;

struct device dev;

u32 num_resources;

struct resource * resource; //定义平台设备的资源。

};

下面来看一下platform_device结构体中最重要的一个成员struct resource * resourcestruct resource被定义在include/linux/ioport.h中,定义原型如下:

struct resource {

resource_size_t start;  //定义资源的起始地址

resource_size_t end;  //定义资源的结束地址

const char *name;    //定义资源的名称

unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型

struct resource *parent, *sibling, *child;  //资源链表指针

};

 
以我们的RTC驱动为例(为什么用RTC,RTC是一个标准的plartform device,机制是相同的,但是相对比较简单)
在我们的arch/arm/mach-sep4020/devices.c中写了rtc的plartform_device结构体和resources结构体:
/* RTC controller */
static struct resource sep4020_rtc_resource[] = {
 [0] = {
  .start = RTC_BASE_V,
  .end   = RTC_BASE_V+ 0x2f,
  .flags = IORESOURCE_MEM,
 },
 [1] = {
  .start = INTSRC_RTC,
  .end   = INTSRC_RTC,
  .flags = IORESOURCE_IRQ,
 }
};
struct platform_device sep4020_device_rtc = {
 .name    = "sep4020_rtc",
 .id    = -1,
 .num_resources   = ARRAY_SIZE(sep4020_rtc_resource),
 .resource   = sep4020_rtc_resource,
};
 
然后通过4020.c文件中将这个plartform_device结构体注册进去了:
static struct platform_device *devices[] __initdata = {
 &serial_device,
 &sep4020_device_rtc,
 &epson_ohci_device,
      &sep4020_device_usbgadget
};
 
platform_add_devices(devices, ARRAY_SIZE(devices));
通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用platform_device_register( )进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register()之前原因是驱动注册时需要匹配内核中所有已注册的设备名。(后面会详细介绍device和driver之间是如何通过注册的名字进行连接的

接下来来看platform_driver结构体的原型定义,在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 (*suspend_late)(struct platform_device *, pm_message_t state);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *);

struct device_driver driver;

};

内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/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;

if (drv->suspend)

drv->driver.suspend = platform_drv_suspend;

if (drv->resume)

drv->driver.resume = platform_drv_resume;

return driver_register(&drv->driver);

}

总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。

 

/*******************************************************************************/

device和driver之间是如何通过注册的名字进行连接的:

 

下面是转载的一份网友用dm9000为例写的联系:

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

 

 

 

2       参考资料
从DM9000驱动看platform device与driver的关系(2009-6-8)

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

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lights_joy/archive/2009/06/08/4251003.aspx

 

阅读(2227) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~