赵 波,高 真 香 子,项 伯 阳,于 忠 得
(1.大连工业大学 信息科学与工程学院,辽宁 大连 116034;2.大连交通大学 电气信息学院,辽宁 大连 116028)
为统一管理Linux中的所有设备,Linux开发了设备模型。Linux设备模型可描述为:总线—设备—驱动程序编程接口,内核设备支持被清晰地结构化为总线、设备和驱动程序[1]。platform驱动架构是Linux设备模型中的重要组成部分,用于片上系统的外围设备控制器驱动的实现。随着片上系统(SOC)的集成度越来越高,越来越多的外围设备控制器被集成进SOC,现有的驱动程序已无法满足使用需求,研究platform驱动架构有利于这些设备驱动的改写、维护与扩展,本文实现了基于platform驱动架构的LCD驱动。
Linux设备模型,实质上就是系统地管理Linux中所有设备,将设备间的层次关系抽象出来。内核设备驱动模型按层次可划分为总线、设备、驱动三层架构,其基本关系可简要概括为[2]:
(1)驱动核心可以注册多种类型的总线;
(2)每种总线下面可以挂载许多设备(通过kset devices);
(3)每种总线下可以使用多种设备驱动(通过包含一个kset drivers);
(4)每个驱动可以处理一组设备。
平台设备(platform)驱动架构的实现,是在Linux设备模型的基础上,通过对总线、设备、驱动3个概念的再定义而实现的[3],在Linux内核中,这3个概念通过platform总线、platform_device(platform设备)、platform_driver(platform驱动)3个部分实现。
Linux操作系统中,platform总线负责将platform驱动架构注册进系统内核,在内核识别platform总线之后,才能进行platform设备、platform驱动的识别与自动匹配,所以,platform总线应该在系统上电后自动启动。platform总线的生成与注册过程如图1所示。
图1 platform总线的生成与注册过程Fig.1 Platform bus formation and registration process
在系统上电后,Linux内核会自动运行/init/main.c程序,完成一些基本启动配置,然后调用/drivers/base/init.c文件中的driver_init()函数,进行内核驱动机制初始化,其中就会通过调用/drivers/base/platform.c文件的 device_register()函数,将platform总线与platform驱动架构注册进Linux内核。其中,platform总线作为设备的一种,通过platform_bus结构体表示;platform驱动架构通过platform_bus_type结构体来实现,这两个结构体均在platform.c中被定义为全局对象。
platform设备主要包括LCD、串口等可以被CPU总线直接寻址的集成于片上系统(SOC)的外围设备控制器等[4]。Linux内核中,platform设备通过platform_device结构体实现,代码路径:/include/linux/platform_device.h,具体定义如下:
其中,设备名称name是platform设备与platform驱动自动匹配的关键,只有name值相同时,platform设备与platform驱动才能匹配上。
Linux系统中,通过结构体的互相包含方式,实现了面向对象思想的继承。在platform设备结构体中,就包含了struct device结构体,实现了对Linux中设备概念(struct device)的继承。在struct device dev中dev->platform_data与dev->driver_data是两个void型变量,可用于存储具体设备的硬件信息、驱动信息等,如LCD的分辨率、刷新率等,可定义一个FS2410_fb_mach_info结构,记录LCD的硬件信息(屏幕尺寸、屏幕信息、LCD配置寄存器)等,并将该结构体赋值给platform_device中的dev->platform_data。
*resource是具体设备的资源,如中断号IRQ、地址资源等。通过platform_device_register()可以自动将platform_device结构体(硬件配置信息)注册进内核空间。
platform设备必须在platform驱动之前注册入内核空间,否则无法自动匹配。
platform驱动完全遵照设备驱动模型的约定[3],通过platform_driver_register()函数完成platform_driver的注册,platform驱动的封装结构体为 platform_driver,代码路径为:/include/linux/platform_device.h,具体定义如下:struct device_driver driver,实现了对 device_driver的继承,为具体的platform驱动提供了统一内核接口;同时,提供了(*suspend)、(*resume)等函数指针,可用于休眠、唤醒等智能电源管理功能。在具体应用中,只需驱动开发人员实现设备底层功能函数,同时将功能函数填充platform_driver结构体的接口中,就可以由Linux内核自动进行platform驱动的管理,大大减轻了工作难度与强度。
对于platform_driver的注册,可以通过调用platform_driver_register(&platform_driver)来实现,其主要涉及的数据流程参见图2。
图2 注册platform驱动的内核数据流程Fig.2 Kernel data flow of Platform driver register
在platform驱动的注册过程中,会在_driver_attach()中通过调用driver_match_device(drv,dev)来匹配platform设备中name与platform驱动中的name值,如果相同,platform设备与platform驱动就绑定成功,底层的设备就可以正常地运行。
本研究以优龙FS2410开发板为硬件平台,Linux 2.6.31为内核版本,外接8寸夏普LCD显示屏,采用Platform驱动架构的LCD驱动实现。在FS2410中,LCDC(LCD控制器)是集成于SOC的外围设备控制器,被CPU总线直接寻址,按照platform驱动架构的一般步骤,有四个环节:定义platform 设备(platform_device)、注册platform设备、定义platform驱动(platform_driver)、注册platform驱动,同时必须保证platform设备在platform驱动之前注册进内核空间。
代码路径:/arch/arm/plat-s3c24xx/Devs.c。
在Devs.c统一定义了S3C24xx架构的platform_device,在这个文件里,可定义
在同文件的如下程序中定义了Lcdc所持有的资源(CPU寻址地址、IRQ中断号)。
同时,定义一个FS2410fb_mach_info结构体,记录LCD的屏幕信息、分辨率、LCD配置寄存器等信息,并填充到platform_device的dev->platform_data中,供内核空间调用。
代码路径:/arch/arm/mach-s3c2410/mach_smdk2410.c。
在 mach-smdk2410.c中,通过platform_add_devices(smdk2410_devices,…)将 smdk2410_devices注册进内核空间,其中smdk2410_devices[]={&s3c_device_usb,&s3c_device_lcd,…},即所有2410平台的platform设备,都被装填入smdk2410_devics[]这个数组,在platform_add_devices()中统一注册进内核空间。
代码路径:/drivers/video/s3c2410fb.c。
在s3c2410fb.c对platform_driver进行了定义:
在此结构体中,具体填充了probe驱动探测函数,suspend、resume电源管理函数,对于驱动开发人员,不需关心内核对它的调用,只需关心具体功能函数的实现,并填充到标准接口即可。在.driver中.name=”FS2410-lcd”,这个值需要与platform_device中的name完全一致。在suspend、resume部分,实行了Linux电源管理,在具体驱动开发中,可以在此处实现智能节电措施,可以采用如安卓操作系统(Android)中的唤醒锁(wakelock)等机制。
S3c2410fb_probe()的调用实现,是在将platform驱动注册进内核空间,并和内核已维护的platform_device链表中的name相匹配后。其具体实现流程:
在/drivers/video/s3c2410fb.c中通过调用platform_driver_register()实现了platform_driver的注册,具体实现如下:platform_driver_register(&s3c2410fb_driver)。
采用platform驱动架构,移植LCD驱动,修改相应 Makefile、Kconfig[5],编译内核,并通过USB下载到FS2410开发板后,串口打印显示LCD驱动加载成功。图3为在移植后的8寸夏普LCD屏上运行QT/E应用程序,显示结果清晰、稳定,无撕裂现象。
图3 移植实验结果Fig.3 Porting experiment results
采用platform驱动架构,具有框架代码复用、设备资源与驱动独立性强、代码精简、具有统一内核接口、易于维护与扩展等特点。在开发具体驱动时,只需专注完成底层设备操作函数集,并与platform_driver结构体提供的内核接口一一对应,保证device.name与driver.name相一致,platform设备在platform驱动之前注册进内核空间,就可以使驱动良好、稳定地运行,大大减轻了工作强度,压缩新产品的研发时间。移植试验证明,采用该架构的驱动具有很好的移植性、可维护性、扩展性。
[1]VENKATESWARAN S.Essential Linux Device Drivers[M].Boston:Prentice Hall,2008:71-77.
[2]CORBET J,RUBINI A,HARTMAN G K.Linux设备驱动程序[M].3版.魏永明,译.北京:中国电力出版社,2006:359-388.
[3]韦东山.嵌入式Linux应用开发完全手册[M].北京:人民邮电出版社,2008:476-490.
[4]宫莉莉,赵勇.基于嵌入式Linux系统的LCD驱动实现[J].微计算机信息,2008,24(35):1-3.
[5]苏哲欣,刘鸿飞,薛晓.基于嵌入式Linux的LCD驱动分析与实现[J].工业控制计算机,2009,22(2):29-30.