Chinaunix首页 | 论坛 | 博客
  • 博客访问: 584589
  • 博文数量: 92
  • 博客积分: 5026
  • 博客等级: 大校
  • 技术积分: 1321
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-28 11:04
文章分类

全部博文(92)

文章存档

2011年(9)

2010年(17)

2009年(12)

2008年(54)

我的朋友

分类: LINUX

2011-03-25 23:30:53

文件: 设备驱动模型platform总线.rar
大小: 10KB
下载: 下载
简介

内核:2.6.29

作者:  hjlin

现实的linux设备和驱动都是挂接在某一个具体的总线上。比如,pciusbi2cspi等。Linux将一些不依附此类总线的设备挂接到platform总线上。如lcd,看门狗等控制器作为platform_device

platform总线

数据结构

Platformbus_type实例。主要提供了matchuevent和电源管理相关的方法。

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,

};

注意这句话.dev_attrs    = platform_dev_attrs

static struct device_attribute platform_dev_attrs[] = {

       __ATTR_RO(modalias),

       __ATTR_NULL,

};

#define __ATTR_RO(_name) { \

       .attr = { .name = __stringify(_name), .mode = 0444 },      \

       .show      = _name##_show,                               \

}

可知platform下的所有设备都有一个属性(就是sysfs中的一个文件),名字叫modalias,读取方法为 modalias_show 的方法。在 读方法中返回的就是platform:device_name这样的字符串。

match方法

platform总线驱动和设备匹配的规则就是他们的名字字符串是否相同。

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

}

uevent方法

kobject_uevent时候添加一个key=MODALIASvalue=platform:device_name的值.(但是在kobject_uevent的实现里,暂时没有找到如何调用到这个方法的?)

static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)

{

       struct platform_device   *pdev = to_platform_device(dev);

 

       add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,

              (pdev->id_entry) ? pdev->id_entry->name : pdev->name);

       return 0;

}

内核如何使用

内核在初始化platform框架的时候,通过bus_register注册了定义的platform_bus_type

int __init platform_bus_init(void)

{

       int error;

       early_platform_cleanup();

       error = device_register(&platform_bus);

       if (error)

              return error;

       error =  bus_register(&platform_bus_type);

       if (error)

              device_unregister(&platform_bus);

       return error;

}

Bus_register已经分析过,可知经过bus_register后的sys文件系统结构如下:

/sys/bus/platform/

|-- devices

|   |-- …

|-- drivers

|   |-- …

|-- drivers_autoprobe

|-- drivers_probe

`-- uevent

 

Platform设备

数据结构

Platform设备简单的封装了struct device,主要是增加了struct resource来表示设备在系统中所占用的内存或者中断资源等。

struct platform_device {

       const char       * name;   //设备名称,和设备id一起设置内嵌kobjname

       int          id;           //设备id,和设备名称一起设置内嵌kobjname

       struct device   dev;  //内部封装的device

       u32         num_resources;

       struct resource * resource; //一些io或者中断资源

};

设备注册

内核提供了注册platform_device的方法。相对于默认的device_registerplatform_device_register方法主要多做了以下的工作:

1.    设置父kobjplatform_bus的内嵌kobjplatform_bus是一个struct device

struct device platform_bus = {

       .init_name      = "platform",

};

并且在__init platform_bus_init(void)方法中,通过device_register(&platform_bus);注册到系统中,并在sys中创建/sys/platform表示它。因此platform_device_register注册后的devicesys目录应该类似/sys/platform/xxx_device的结构。

?为什么要这样做,我觉得主要是让sys层次更加清晰易懂。

2.    设置devicebusplatform_bus_type,理所当然的了。不知道具体哪个总线根本没法匹配驱动。每个真实的驱动必须都有一个具体的总线类型如果你使用驱动模型的话。

3.    通过insert_resource将设备定义的所要分配的资源注册到内核。

经过platform_device_register注册的设备,在sys中看起来的结构可能是:

/sys/devices/platform/serial8250.0

|-- uevent

|-- modalias

|-- subsystem -> ../../../bus/platform

|-- power

|-- tty

|-- driver -> ../../../bus/platform/drivers/serial8250

 

/sys/bus/platform/devices

|-- serial8250.0 -> ../../../devices/platform/serial8250.0

 

int platform_device_register(struct platform_device * pdev)

{

       device_initialize(&pdev->dev);

       return platform_device_add(pdev);

}

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.%d", 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;

}

Platform驱动

数据结构

相对于标准的struct device_drvierplatform_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

};

驱动注册

类似platform_driver,就是对driver_register的一层很简单的包装。在sys文件系统中的结构可能是:

/sys/bus/platform/drivers/serial8250

|-- serial8250 -> ../../../../devices/platform/serial8250

|-- serial8250.0 -> ../../../../devices/platform/serial8250.0

|-- uevent

|-- unbind

|-- bind

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

}

platform总线的使用

首先在bsp中,需要注册所有板子上的platform设备,比如lcdrtc等(就是cpu集成的这些设备芯片),否则将没有机会识别该设备。pci或者usb上面的设备内核可以检测识别。集成的一定要手动配置的。这应该就是bsp的意思了吧。

举个例子, arch/arm/mach-s3c2410/Mach-smdk2410.c

static struct platform_device *smdk2410_devices[] __initdata = {

       &s3c_device_usb,

       &s3c_device_lcd,

       &s3c_device_wdt,

       &s3c_device_i2c,

       &s3c_device_iis,

};

大概说这个板子的内建芯片设备有这么多种。

static void __init smdk2410_init(void)

{

       platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));

       smdk_machine_init();

}

然后在init中会将这些platform设备添加到设备模型中。这样后来加载驱动的时候就会匹配到这些设备了。从而调用到驱动的probe函数。Probe函数呢,就是驱动的核心了,一般主要有两种事情:

A.  申请中断,注册中断服务程序,io端口等一些和硬件相关的事情。

B.   注册字符驱动等可以和用户态进行交互的事情。

C.   3个是我们看不着的事情,因为内核的设备驱动模型已经帮我们默默的做了很多事情了。比如在/sys文件系统层次展示,引用计数实现资源管理,uevent的热插拔。甚至是更具体的事情,比如说input子系统,她把上面同用户态交互的B的事情都替你干好了。实在是很贤惠啊。我们唯一需要做的就是按照内核的驱动模型来编写代码就可以了。

 

阅读(1426) | 评论(0) | 转发(0) |
0

上一篇:linux内核驱动模型

下一篇:android 系统调用

给主人留下些什么吧!~~