一。平台设备模型(platform)
2.6内核开始引入了一套新的设备驱动模型。平台设备(platform_device)和平台驱动(platform_driver)。平台设备与一般的device,driver模型区别是,平台设备将设备本身的资源注册进内核,可以由内核统一管理。
在文件include\linux\platform_device.h中,定义platform_device结构体。
点击(此处)折叠或打开
- struct platform_device {
- const char * name; //平台设备名称
- u32 id; //驱动绑定,一般为-1
- struct device dev; //device设备
- u32 num_resources; //引用的资源数
- struct resource * resource; //引用的资源
- };
platform_drvier也在同样的文件中定义。
点击(此处)折叠或打开
- 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 (*resume)(struct platform_device *);
- struct device_driver driver; //device_driver
- };
系统初始化时,会生成好平台设备总线等,建立平台设备模型 .
点击(此处)折叠或打开
- int __init platform_bus_init(void)
- {
- int error;
- early_platform_cleanup(); //clean up early platform code
- error = device_register(&platform_bus); //注册platform_bus 设备 (1)
- if (error)
- return error;
- error = bus_register(&platform_bus_type); //注册平台设备总线 (2)
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
(1)platform_bus 的定义,实际的总线也是一个设备
点击(此处)折叠或打开
- struct device platform_bus = {
- .init_name = "platform",
- };
这个名为platform的设备添加后,/sys/device/plaform将出现
(2)platform_bus_type定义 这是platform总线类型
点击(此处)折叠或打开
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
平台设备的资源
平台设备的一个特点就是将此平台设备所用到的资源,独立出来,注册进内核。这样可方便的管理资源,使资源可以和驱动主体分离,便于修改移植等。资源文件一般都定义在板级的文件中。驱动可以通过函数来获取。
资源结构resource的定义:
点击(此处)折叠或打开
- struct resource {
- resource_size_t start; //资源的开始地址
- resource_size_t end; //资源的结束地址
- const char *name; //资源的名称
- unsigned long flags; //资源的标志 eg,IORESOURCE_IO(IO端口),IORESOURCE_MEM(内存资源)IORESOURCE_IRQ(中断)
- struct resource *parent, *sibling, *child; //资源关系结构
- };
我们这里看一个资源的例子,s3c的RTC时钟模块是一个平台设备,其资源定义如下(arch/arm/plat-s3c24xx/devs.c).
s3c_rtc_resource是一个资源数组。
点击(此处)折叠或打开
- static struct resource s3c_rtc_resource[] = {
- //RTC IO内存资源
- [0] = {
- .start = S3C24XX_PA_RTC, //IO内存的开始地址。(RTC寄存器地址)
- .end = S3C24XX_PA_RTC + 0xff, //IO内存的结束地址
- .flags = IORESOURCE_MEM, //设置这个资源的类型是IO内存资源
- },
- //RTC 使用的中断资源
- [1] = {
- .start = IRQ_RTC, //RTC滴答所使用的中断源号为46
- .end = IRQ_RTC,
- .flags = IORESOURCE_IRQ, //资源类型为中断
- },
- //
- [2] = {
- .start = IRQ_TICK, //RTC报警所使用的中断号
- .end = IRQ_TICK,
- .flags = IORESOURCE_IRQ //资源类型为中断
- }
- };
这些资源的设备的关联会在定义设备时关联,如
点击(此处)折叠或打开
- struct platform_device s3c_device_rtc = {
- .name = "s3c2410-rtc",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_rtc_resource),
- .resource = s3c_rtc_resource, //设备的资源为s3c_rtc_resource
- };
二、注册平台设备
平台设备的注册函数会在相应的板级文件中初始化部分被调用到,例如mini2440的板级文件arch/arm/math-s3c2440/math-mini2440.c中mini2440_init()函数中一句platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
mini2440_devices变量就是保存mimi2440所有平台设备的数组,
点击(此处)折叠或打开
- static struct platform_device *mini2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_wdt,
- ...
- ...
平台设备注册函数platform_add_devices,此函数会在板级文件初始化函数(_init函数)中调用如mini2440_init。
点击(此处)折叠或打开
- /**
- * 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 - add a platform-level device 添加一个平台设备
- * @pdev: platform device we're adding
- */
- int platform_device_register(struct platform_device *pdev)
- {
- device_initialize(&pdev->dev); //初始化平台设备(平台设备结构中的device结构)
- return platform_device_add(pdev); //添加这个平台设备
- }
- /**
- * 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) //设备还没有设置父设备,则设置为platform_bus设备
- pdev->dev.parent = &platform_bus;
- pdev->dev.bus = &platform_bus_type; //设置设备总线类型为平台总线类型
- if (pdev->id != -1)
- dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
- else //一般均为-1
- dev_set_name(&pdev->dev, "%s", pdev->name);
- //设置设备名称(实现语句:kobject_set_name_vargs(&dev->kobj, fmt, vargs);)
- for (i = 0; i < pdev->num_resources; i++) {
- struct resource *p, *r = &pdev->resource[i];
- if (r->name == NULL) //如果资源没有名称,则设置为设备的名称
- r->name = dev_name(&pdev->dev);
- p = r->parent; //父资源
- if (!p) { //没有父资源则
- if (resource_type(r) == IORESOURCE_MEM)
- p = &iomem_resource; //如果资源的类型是内存资源,则将其父资源指向iomem_resource资源
- else if (resource_type(r) == IORESOURCE_IO)
- p = &ioport_resource; //如果资源的类型是IO资源,则将其父资源指向ioport_resource资源
- }
- if (p && insert_resource(p, r)) {
- printk(KERN_ERR
- "%s: failed to claim resource %d\n",
- dev_name(&pdev->dev), i);
- ret = -EBUSY;
- goto failed;
- }
- }
- pr_debug("Registering platform device '%s'. Parent at %s\n",
- dev_name(&pdev->dev), dev_name(pdev->dev.parent));
- ret = device_add(&pdev->dev); //将设备添加入系统
- if (ret == 0)
- return ret;
- failed:
- while (--i >= 0) {
- struct resource *r = &pdev->resource[i];
- unsigned long type = resource_type(r);
- if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
- release_resource(r);
- }
- return ret;
- }
我们看一下在platform核心层的这个函数主要做了什么
设置了驱动的父设备(指向platform_bus总线设备)
设置了总线类型(platform_bus_type总线)
设置设备名称
设置了资源
最后注册进设备模型核心(device_add)
三、注册平台设备驱动
使用函数platform_driver_register(),函数在drivers/base/platfrom.c中,此函数一般在platform的驱动程序文件中的入口函数被调用,在驱动程序中一般会定义一个platform_driver的一个实体变量,来表示这个驱动。例如这个RTC驱动,
点击(此处)折叠或打开
- static struct platform_driver s3c2410_rtc_driver = {
- .probe = s3c_rtc_probe,
- .remove = __devexit_p(s3c_rtc_remove),
- .suspend = s3c_rtc_suspend,
- .resume = s3c_rtc_resume,
- .driver = {
- .name = "s3c2410-rtc",
- .owner = THIS_MODULE,
- },
- };
点击(此处)折叠或打开
- int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type; //(1)
- if (drv->probe)
- drv->driver.probe = platform_drv_probe; //(2)
- if (drv->remove)
- drv->driver.remove = platform_drv_remove; //(3)
- if (drv->shutdown)
- drv->driver.shutdown = platform_drv_shutdown; //(4)
- if (drv->suspend)
- drv->driver.suspend = platform_drv_suspend; //(5)
- if (drv->resume)
- drv->driver.resume = platform_drv_resume; //(6)
- return driver_register(&drv->driver); //(7)
- }
可以看出此函数主要是设置了platform_driver结构体,最后注册结构体中的drvier型变量。
(1)设置device_driver 的总线类型bus,为platform_bus_type,
点击(此处)折叠或打开
- struct bus_type platform_bus_type = {
- .name = "platform", //总线类型的名称
- .match = platform_match, //<1>
- .suspend = platform_suspend, //<2>
- .resume = platform_resume, //<3>
- };
<1>platform_match函数定义了platform类型总线,device和driver之间的匹配方法。在文件drivers/base/Platform.c
点击(此处)折叠或打开
- static int platform_match(struct device * dev, struct device_driver * drv)
- {
- struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- //通过container_of函数数获得包含dev的platform_device变量。
- return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
- //platform模型是通过比较设备名称和驱动名称还进行匹配的。
- }
<2>platform总线上的挂起函数platform_suspend是调用相应device设备的driver的suspend函数
<3>platform总线上的恢复函数同上。
(2)--(6)都是在注册platform_driver变量时,检查是否定义了相应的函数,如果定义则使用默认函数,默认的函数会将platform_driver的父结构device_driver的操作函数设置成platform_driver中定义的函数。probe函数如下
点击(此处)折叠或打开
- static int platform_drv_probe(struct device *_dev)
- {
- struct platform_driver *drv = to_platform_driver(_dev->driver); //获得子结构platform_driver
- struct platform_device *dev = to_platform_device(_dev); //获得子结构platform_device
- return drv->probe(dev); //调用的是platform_driver的probe函数。
- }
(7)调用driver_register将platform_driver的父结构device_driver注册进设备模型核心。
平台设备和平台驱动的关联
注册平台设备驱动时即platform_driver_register函数,会调用到driver_register函数,此函数会调用driver_attach(),这个函数将在对应总线上寻找匹配的设备。