Linux之platform总线机制与wtd驱动开发

1、概述:

通常在Linux中,把SoC系统中集成的独立外设单元(如:I2C、IIS、RTC、看门狗等)都被当作平台设备来处理。

从Linux2.6起,引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver,来管理相应设备。

Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。

Linux platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。

这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。

platform是一个虚拟的地址总线,相比pci,usb,它主要用于描述SOC上的片上资源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platform所描述的资源有一个共同点,就是可以在cpu的总线上直接取址。

2、platform机制分为三个步骤

1)总线注册阶段:

内核启动初始化时的main.c文件中的

kernel_init() ->do_basic_setup() -> driver_init() ->platform_bus_init() ->bus_register(&platform_bus_type),注册了一条platform总线(虚拟总线)

platform总线用于连接各类采用platform机制的设备,此阶段无需我们修改,由linux内核维护。

2)添加设备阶段:

设备注册的时候

Platform_device_register() -> platform_device_add() -> pdev->dev.bus = &platform_bus_type -> device_add(),就这样把设备给挂到虚拟的总线上。

本阶段是增加设备到plartform总线上,我们增加硬件设备,需要修改此处信息。

此部分操作一般arm/arm/mach-s3c2440/mach-smdk2440.c类似的文件中,需要我们根据硬件的实际需要修改相应代码

3)驱动注册阶段:

Platform_driver_register() ->driver_register() -> bus_add_driver() -> driver_attach() ->bus_for_each_dev(),
对在每个挂在虚拟的platform bus的设备作__driver_attach() ->driver_probe_device()

判断drv->bus->match()是否执行成功,此时通过指针执行platform_match-> strncmp(pdev->name , drv->name , BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver->probe(platform_device)。)
开始真正的探测,如果probe成功,则绑定设备到该驱动。

本阶段是在编写具体的驱动程序时完成,在注册驱动时获取步骤2中已经申请的资料,一般由程序开发者完成。

3、platform设备开发过程

platform机制开发的并不复杂,由两部分组成:platform_device和platfrom_driver

通过Platform机制开发发底层驱动的大致流程为: 
  定义 platform_device

注册 platform_device

定义 platform_driver

注册 platform_driver

以linux2.6.34平台下S3C2440为例:前两项工作主要在arch/arm/mach-s3c2440/match-smdk2440.c与arch/arm/platform-s3c24xx/devs.c中完成,后两项工作主要在编写具体的驱动程序时完成

在2.6内核中platform设备用结构体platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,

struct platform_device{const char * name;u32 id;struct device dev;u32 num_resources;struct resource * resource;
};

每个具体的驱动都对应一个这样的结构体。

Platform_device结构体描述了一个platform结构的设备,在其中包含了

一般设备的结构体:struct device dev;

设备的资源结构体:struct resource * resource;

还有设备的名字:const char * name。
(注意,这个名字一定要和后面platform_driver.driver->name相同,因为在注册具体的设备驱动时会遍历这个结构体查找相应的数据结构,后面会详细讲解)

该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\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; //资源链表指针
};

主要用于定义具体设备占用的硬件资源(如:地址空间、中断号等;

其中 flags位表示该资源的类型

start和end分别表示该资源的起始地址和结束地址;

要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用;

即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。

我们以内核中对SMDK2440的支持为例观察一下整个过程:

内核启动过程中会调用用 arch/arm/mach-s3c2440/smdk2440_machine_init()函数进行板级硬件初始化

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; //资源链表指针
};

此函数中调用了platform_add_devices() -> platform_device_register()注册platform设备
注册顺序根据同文件夹下的

static struct platform_device *smdk2440_devices[] __initdata =
{
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_dm9k,
&s3c24xx_uda134x,
&s3c_device_sdi,
};

结构体进行
这些设备的初始化一般都在arch/arm/plat-s3c24xx/devs.c下
我们以s3c_device_wdt为例进行观察:

/* Watchdog */
//看门狗资源结构体
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG,
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM, //看门狗所使用的IO口范围
},
[1] = {
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ, //看门狗所使用的中断资源
}
};//定义了一个看门狗结构体
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt", //驱动名称
.id = -1, //id号,-1代表自动分配
.num_resources = ARRAY_SIZE(s3c_wdt_resource),//指定资源数量
.resource = s3c_wdt_resource, //指定资源结构体
};

platform_driver在具体的硬件设备驱动编写中完成:
同plartform_device相似,需要定义并实现以下结构体

struct platform_driver (/include/linux/Platform_device.h)
{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结构的驱动。
其中除了一些函数指针外,还有一个一般驱动的device_driver结构。

/*Watchdog平台驱动结构体,平台驱动结构体定义在platform_device.h中,该结构体内的接口函数需要单独实现*/
static struct platform_driver watchdog_driver =
{.probe = watchdog_probe, /*Watchdog探测函数*/.remove = __devexit_p(watchdog_remove),/*Watchdog移除函数*/.shutdown = watchdog_shutdown, /*Watchdog关闭函数*/.suspend = watchdog_suspend, /*Watchdog挂起函数*/.resume = watchdog_resume, /*Watchdog恢复函数*/.driver = {/*注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来*/.name = "s3c2410-wdt",.owner = THIS_MODULE,},
};static int __init watchdog_init(void)
{/*将Watchdog注册成平台设备驱动*/return platform_driver_register(&watchdog_driver);
}static void __exit watchdog_exit(void)
{/*注销Watchdog平台设备驱动*/platform_driver_unregister(&watchdog_driver);
}module_init(watchdog_init);
module_exit(watchdog_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");
MODULE_DESCRIPTION("S3C2440 Watchdog Driver");static int __devinit watchdog_probe(struct platform_device *pdev)
{int ret;int started = 0;struct resource *res;/*定义一个资源,用来保存获取的watchdog的IO资源*//*在系统定义的watchdog平台设备中获取watchdog中断号platform_get_irq定义在platform_device.h中*/wdt_irqno = platform_get_irq(pdev, 0);/*申请Watchdog中断服务,这里使用的是快速中断:IRQF_DISABLED。中断服务程序为:wdt_irq,将Watchdog平台设备pdev做参数传递过去了*/ret = request_irq(wdt_irqno, wdt_irq, IRQF_DISABLED, pdev->name, pdev);/*获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致*/res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*申请watchdog的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),request_mem_region定义在ioport.h中*/wdt_mem = request_mem_region(res->start, res->end - res->start + 1, pdev->name);/*将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/wdt_base = ioremap(res->start, res->end - res->start + 1);return ret;
}/*Watchdog平台驱动的设备移除接口函数的实现*/
static int __devexit wdt_remove(struct platform_device *dev)
{/*释放获取的Watchdog平台设备的IO资源*/release_resource(wdt_mem);kfree(wdt_mem);wdt_mem = NULL;/*同watchdog_probe中中断的申请相对应,在那里申请中断,这里就释放中断*/free_irq(wdt_irqno, dev);wdt_irq = NULL;/*释放获取的Watchdog平台设备的时钟*/clk_disable(wdt_clock);clk_put(wdt_clock);wdt_clock = NULL;/*释放Watchdog设备虚拟地址映射空间*/iounmap(wdt_base);return 0;
}#ifdef CONFIG_PM/*定义两个变量来分别保存挂起时的WTCON和WTDAT值,到恢复的时候使用*/
static unsigned long wtcon_save;
static unsigned long wtdat_save;/*Watchdog平台驱动的设备挂起接口函数的实现*/
static int wdt_suspend(struct platform_device *dev, pm_message_t state)
{/*保存挂起时的WTCON和WTDAT值*/wtcon_save = readl(wdt_base + S3C2410_WTCON);wtdat_save = readl(wdt_base + S3C2410_WTDAT);/*停止看门狗定时器*/wdt_start_or_stop(0);return 0;
}/*Watchdog平台驱动的设备恢复接口函数的实现*/
static int wdt_resume(struct platform_device *dev)
{/*恢复挂起时的WTCON和WTDAT值,注意这个顺序*/writel(wtdat_save, wdt_base + S3C2410_WTDAT);writel(wtdat_save, wdt_base + S3C2410_WTCNT);writel(wtcon_save, wdt_base + S3C2410_WTCON);return 0;
}#else /*配置内核时没选上电源管理,Watchdog平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了*/#define wdt_suspend NULL#define wdt_resume NULL
#endif

名字要一致的原因:

上面说的驱动在注册的时候会调用函数bus_for_each_dev(), 对在每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device(),在此函数中会对dev和drv做初步的匹配,调用的是drv->bus->match所指向的函数。platform_driver_register函数中drv->driver.bus = &platform_bus_type,所以drv->bus->match就为platform_bus_type->match,为platform_match函数,该函数如下:

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

是比较dev和drv的name,相同则会进入really_probe()函数,从而进入自己写的probe函数做进一步的匹配。所以dev->name和driver->drv->name在初始化时一定要填一样的。不同类型的驱动,其match函数是不一样的,这个platform的驱动,比较的是dev和drv的名字,还记得usb类驱动里的match吗?它比较的是Product ID和Vendor ID。

个人总结Platform机制的好处:

1 提供platform_bus_type类型的总线,把那些不是总线型的soc设备都添加到这条虚拟总线上。使得,总线——设备——驱动 的模式可以得到普及。

2 提供platform_device和platform_driver类型的数据结构,将传统的device和driver数据结构嵌入其中,并且加入resource成员,以便于和Open Firmware这种动态传递设备资源的新型bootloader和kernel 接轨。

[linux]platform总线机制与wtd驱动开发相关推荐

  1. linux驱动模型开发——linux platform总线机制讲解与实例开发

    1.概述: 通常在Linux中,把SoC系统中集成的独立外设单元(如:I2C.IIS.RTC.看门狗等)都被当作平台设备来处理. 从Linux2.6起,引入了一套新的驱动管理和注册机制:Platfor ...

  2. [linux驱动开发] 基于gpiod API的platform总线多个led驱动开发

    gpiod API对platform-led进行驱动开发 修改设备树源码 如何在驱动中获取设备树节点信息 计算设备子节点数量 给私有属性分配内存 对子节点进行遍历 gpiod的获取 根据设备树字节给的 ...

  3. 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  4. Linux platform总线(1):总体框架

    PlatForm设备驱动: 一.platform总线.设备与驱动 1.一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI.USB.I2 C.SPI等的设备而言,这自然不是问 ...

  5. linux platform匹配机制,Linux驱动中的platform总线详解

    platform总线是学习linux驱动必须要掌握的一个知识点. 一.概念 嵌入式系统中有很多的物理总线:I2c.SPI.USB.uart.PCIE.APB.AHB linux从2.6起就加入了一套新 ...

  6. linux 驱动没有设备id,linux不同总线的设备和驱动的匹配过程分析

    摘自: 前几日读书会,谈到linux中driver和device的匹配问题,我认为是通过设备名来匹配的,因为我之前看过platform的驱动,它就是通过设备name和驱动name来进行匹配,所以我确信 ...

  7. NUC972在linux下的KSZ8851-16 mll网卡驱动开发

    KSZ8851-16MLL是一款单端口以太网MAC与非PCI接口控制器,集成式MAC和PHY以太网控制器,可以替代DM9000.但是这个芯片的资料非常少,NUC972也是,百度全网也找不到有用的资料, ...

  8. stm32官方例程在哪找_正点原子Linux第十一章模仿STM32驱动开发格式实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第十一章模仿STM32驱动开发格式实验 在上一章使用C语 ...

  9. Linux 通过uinput机制实现蜂鸣器驱动

    通过uinput机制模拟了input设备,实现了蜂鸣器的控制. 系统内核:2.6.21 发布版本fedora7 1. 主要流程如下: 2. 详细代码如下 #include <stdio.h> ...

最新文章

  1. python键_在Python中创建键命令
  2. Gartner:人工智能将改变个人设备领域的游戏规则
  3. CodeBlocks+Qt(MinGW)配置 QT4.8.x MinGW 问题解决
  4. MySQL带关系运算符的查询
  5. 成员函数和友元函数实现一元运算符重载
  6. 今日上午,清华大学发布中国首个高校自研深度学习训练框架—计图Jittor
  7. 初识React Native虚拟DOM节点及API
  8. python web框架 多线程_Django基础知识 web框架的本质详解
  9. Matlab里evalin和assignin的用法
  10. Matlab各种最值问题
  11. python 内存分析_python内存管理分析
  12. Socket TCP和UDP的区别
  13. Javascript中的几种继承方式比较
  14. 【披着递推皮的动态规划】 山区建小学 题解
  15. jQuery中的编程范式
  16. Asp.Net中的正则表达式问题可以在此提问,今后大家一起研究!贴出基本语法参考...
  17. python socket recvfrom 超时捕获_python-udp客户端超时机制
  18. Fiddler抓包工具之详细使用步骤(超详细)
  19. python读入图片,可视化展示图片
  20. 网卡加到linux网桥,[Linux] ubuntu 14 通过网桥将无线网络桥接给有线网卡

热门文章

  1. 多渔:阿ken的故事
  2. 青年论坛:谈判的情感力量
  3. Hadoop入门教程 详细的入门实战教程
  4. ASP.NET 中关GridView里加入CheckBox 在后台获取不到选中状态的问题
  5. python查看电脑配置_怎么看电脑配置_怎么查看电脑配置好坏|信息【图文】-太平洋IT百科...
  6. 小程序生成网址链接,网址链接跳转小程序(附详细流程)
  7. Nginx无法启动 遇见unknown directive if(!-f in E:\xiangmu\nginx-1.14.0/conf/nginx.conf:28
  8. 多机房UPS及环境集中监控方案丨UPS环境综合监控主机
  9. 前端之vue监测数据改变的原理
  10. PPI网络比对文章汇总