Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145051
  • 博文数量: 19
  • 博客积分: 1746
  • 博客等级: 上尉
  • 技术积分: 443
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-22 22:54
文章分类
文章存档

2011年(3)

2010年(16)

分类: LINUX

2010-03-08 15:08:27

------------------------------------------------
#个人浅见,如有问题敬请谅解!
#kernel version: 2.6.26
#Author: andy wang
-------------------------------------------------
   以前的文章已经分析过了linux总线的注册过程,今天就拿一个具体总线例子来分析一下,platform总线是一个虚拟总线(在硬件上并非存在这个总线),在linux 2.6的设备驱动中platform总线用的比较多,所以分析platform总线是非常有必要的。
先看看在我的机子上platform总线上都注册了哪些设备和驱动:
[root@FLYING platform]# pwd
/sys/bus/platform
[root@FLYING platform]# tree
.
|-- devices
|   |-- bluetooth -> ../../../devices/platform/bluetooth
|   |-- floppy.0 -> ../../../devices/platform/floppy.0
|   |-- i8042 -> ../../../devices/platform/i8042
|   |-- microcode -> ../../../devices/platform/microcode
|   |-- pcspkr -> ../../../devices/platform/pcspkr
|   |-- serial8250 -> ../../../devices/platform/serial8250
|   `-- vesafb.0 -> ../../../devices/platform/vesafb.0
|-- drivers
|   |-- i8042
|   |   |-- bind
|   |   |-- i8042 -> ../../../../devices/platform/i8042
|   |   |-- uevent
|   |   `-- unbind
|   |-- parport_pc
|   |   |-- bind
|   |   |-- module -> ../../../../module/parport_pc
|   |   |-- uevent
|   |   `-- unbind
|   |-- pcspkr
|   |   |-- bind
|   |   |-- module -> ../../../../module/pcspkr
|   |   |-- pcspkr -> ../../../../devices/platform/pcspkr
|   |   |-- uevent
|   |   `-- unbind
|   |-- serial8250
|   |   |-- bind
|   |   |-- module -> ../../../../module/8250
|   |   |-- serial8250 -> ../../../../devices/platform/serial8250
|   |   |-- uevent
|   |   `-- unbind
|   `-- vesafb
|       |-- bind
|       |-- uevent
|       |-- unbind
|       `-- vesafb.0 -> ../../../../devices/platform/vesafb.0
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent
在这个目录树结构图中清晰的展示了注册在platform总线上的设备和驱动。
 
platform初始化
在系统初始化的时候会调用函数platform_bus_init();始化platform总线:
609 struct bus_type platform_bus_type = {
610         .name           = "platform",
611         .dev_attrs      = platform_dev_attrs,
612         .match          = platform_match,
613         .uevent         = platform_uevent,
614         .suspend        = platform_suspend,
615         .suspend_late   = platform_suspend_late,
616         .resume_early   = platform_resume_early,
617         .resume         = platform_resume,
618 };
 
621 int __init platform_bus_init(void)
622 {   
623         int error;
624    
625         error = device_register(&platform_bus);
626         if (error)
627                 return error;
628         error =  bus_register(&platform_bus_type);
629         if (error)
630                 device_unregister(&platform_bus);
631         return error;
632 }
625行,首先注册一个设备platform_bus ,它是platform总线下所有设备的父设备.
628行,注册platform总线,在以前文章已经详细分析过总线注册过程。
 
注册platform设备
接下来就分析如何在platform总线上注册设备。在platform.c文件中很容易找到这个函数platform_device_register(),就是用来注册一个设备到platform总线上的。
跟踪一下这个函数的流程:
platform_device_register()->device_initialize()->platform_device_add()-insert_resource ()->device_add();
最终还是调用一个基础函数device_add()注册设备。
详细分析一下platform_device_add()这个函数吧!
236 int platform_device_add(struct platform_device *pdev)
237 {
238         int i, ret = 0;
239
240         if (!pdev)
241                 return -EINVAL;
242
243         if (!pdev->dev.parent)
244                 pdev->dev.parent = &platform_bus;
245
246         pdev->dev.bus = &platform_bus_type;
247
248         if (pdev->id != -1)
249                 snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
250                          pdev->id);
251         else
252                 strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
253
254         for (i = 0; i < pdev->num_resources; i++) {
255                 struct resource *p, *r = &pdev->resource[i];
256
257                 if (r->name == NULL)
258                         r->name = pdev->dev.bus_id;
259
260                 p = r->parent;
261                 if (!p) {
262                         if (r->flags & IORESOURCE_MEM)
263                                 p = &iomem_resource;
264                         else if (r->flags & IORESOURCE_IO)
265                                 p = &ioport_resource;
266                 }
267
268                 if (p && insert_resource(p, r)) {
269                         printk(KERN_ERR
270                                "%s: failed to claim resource %d\n",
271                                pdev->dev.bus_id, i);
272                         ret = -EBUSY;
273                         goto failed;
274                 }
275         }
276
277         pr_debug("Registering platform device '%s'. Parent at %s\n",
278                  pdev->dev.bus_id, pdev->dev.parent->bus_id);
279
280         ret = device_add(&pdev->dev);
281         if (ret == 0)
282                 return ret;
                            ……………….
}
243-246行,指定父设备对象和总线类型,也就是在初始化platform总线时注册的设备和总线。
254-266行,将pdev设备使用的内存或IO资源注册到以iomem_resourceioport_resource为根的资源树上,这样做的目的是为了防止资源使用冲突。使用insert_resource() 把使用的资源信息注册到资源树上,而在后面要使用这些资源时必须要调用函数request_region()申请使用资源。这也是platform总线上设备的一个特点。
280行,最后调用函数device_add()注册设备,在以前文章中已经分析过了,这里就不多说了。
 
注册platform驱动
注册一个platform 设备驱动也是比较简单的.
443 int platform_driver_register(struct platform_driver *drv)
444 {
445         drv->driver.bus = &platform_bus_type;
446         if (drv->probe)
447                 drv->driver.probe = platform_drv_probe;
448         if (drv->remove)
449                 drv->driver.remove = platform_drv_remove;
450         if (drv->shutdown)
451                 drv->driver.shutdown = platform_drv_shutdown;
452         if (drv->suspend)
453                 drv->driver.suspend = platform_drv_suspend;
454         if (drv->resume)
455                 drv->driver.resume = platform_drv_resume;
456         return driver_register(&drv->driver);
457 }
445行,初始化驱动关联的总线,既然我们注册的是platform驱动,那么总线自然是platform_bus_type
446-455,初始化驱动的probe, remove, shutdown, suspend, resume回调函数.
456行,注册一个驱动。不多说,以前就分析过了。
 
Platform总线上设备与驱动匹配
在以前文中已经分析过设备和驱动的匹配过程,在匹配的时候会调用三个重要的回调函数一是总线的match方法,二是总线的prob方法,最后一个是驱动的prob方法.
在上面platform总线的定义中我们可以看到platform总线定义的match回调函数是platform_match();我们来跟踪一下代码:
555 static int platform_match(struct device *dev, struct device_driver *drv)
556 {
557         struct platform_device *pdev;
558
559         pdev = container_of(dev, struct platform_device, dev);
560         return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
561 }
可见platform总线上设备和驱动的匹配规则只是比较名字,只要是名字一样就匹配成功.
platform总线并未定义prob方法, 所有我们直接看驱动的prob方法
在上面分析的注册platform驱动有这么一段代码:
446         if (drv->probe)
447                 drv->driver.probe = platform_drv_probe;
所以最后调用的是函数platform_drv_probe(),看看它的实现:
394 static int platform_drv_probe(struct device *_dev)
395 {
396         struct platform_driver *drv = to_platform_driver(_dev->driver);
397         struct platform_device *dev = to_platform_device(_dev);
398
399         return drv->probe(dev);
400 }
这个函数很简单, 先是找到platform_driver,然后调用drv->probe() ,这个回调函数一般都是在我们定义platform_driver时定义的.
驱动的其它回调方法remove, shutdown, suspend, resume的实现都是和prob一样的,这里就不具体看了。
 
好了,到这里这个总线模型就分析完了, 这个总线还是比较简单的,但是它是非常有用的,在内核驱动中大量使用, 有了这个知识后就可以自己跟踪内核中的大部分驱动代码了.
 
阅读(2632) | 评论(2) | 转发(3) |
给主人留下些什么吧!~~

hidiy2010-05-18 14:26:32

拜读了,受益匪浅!