背光设备驱动开发概述
要想了解驱动的开发,必须先了解Linux的sysfs文件系统。Linux 2.6内核的一个重要特色是提供了统一的内核设备模型,随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔等支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,2.6内核开发了全新的设备模型。
图一 LInux设备模型
Linux 2.6 内核引入了Sysfs文件系统,Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其顶层目录主要有:block、devices、bus、drivers、class、power和firmware等。其中:block目录包含所有的块设备;devices目录包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构;bus目录包含系统中所有的总线类型;drivers目录包括内核中所有已注册的设备驱动程序;class目录包含系统中的设备类型(如网卡设备,声卡设备等)。在/sys/bus的pci目录下又会再分出drivers和devices目录,而devices目录中的文件是对/sys/devices目录中文件的符号链接。同样的,/sys/class目录下包含许多对/sys/devices下文件的链接,如图一,是描述的2.6内核总线、类、设备和驱动的层级关系,也符合LINUX 2.6内核的设备模型。
当前多开发的背光设备文件对应于/sys/class/backlight/目录下的文件。前面已将说过,在/sys/class/目录下的文件对应的都是系统中对应的不同设备类型,/sys/class/backlight就是注册的背光设备类型,而在/sys/class/backlight/目录下的文件就是所注册的背光设备。对于背光设备类型的注册一般系统都完成了,代码如下:
static struct class *backlight_class;
static int __init backlight_class_init(void)
{
backlight_class = class_create(THIS_MODULE, "backlight");//注册背光设备类型;
if (IS_ERR(backlight_class)) {
……}
backlight_class->dev_attrs = bl_device_attributes; //指定背光设备类型的属性文件;
return 0;
}
其中class的结构体定义如下:
struct class {
const char * name; //每个类需要一个唯一的名字, 它将显示在/sys/class中
struct module * owner;struct kset subsys; //对应的subsystem
struct list_head children; //class_device链表
struct list_head devices;
struct list_head interfaces; //class_interface链表
struct kset class_dirs;
struct semaphore sem; //children和interfaces链表锁struct class_attribute * class_attrs; //指向类属性的指针
struct class_device_attribute * class_dev_attrs; //指向类中每个设备的一组默认属性的指针
struct device_attribute * dev_attrs; //指向该类中属性文件的指针……
void (*release)(struct class_device *dev); //把设备从类中删除的函数
void (*class_release)(struct class *class); //删除类本身的函数
……
};
通常用device_attribute结构体来定义属性文件,包含四个参数:设备属性名称、权限还有show和store方法。Linux内核提供了关于背光设备驱动的相关框架,包括背光设备的相关数据结构和相关注册函数等,可参考linux内核backlight.h和backlight.c,还有platform_device.h。下面先来几个重要的结构体和相关函数:
背光设备的结构体:
struct backlight_device {
struct backlight_properties props; //背光设备属性结构体
struct mutex update_lock;
struct mutex ops_lock;
struct backlight_ops *ops; //背光设备的相关操作函数
struct notifier_block fb_notif;
struct device dev;
};
其中backlight_properties和backlight_ops结构体定义如下:
struct backlight_properties {
int brightness; //当前亮度,最大值不能超过max_brightness
int max_brightness; //最大亮度,只读
int power; //当前的电源模式
int fb_blank;
};
struct backlight_ops {
int (*update_status)(struct backlight_device *); //更新背光设备亮度等属性
int (*get_brightness)(struct backlight_device *); //获取背光设备亮度
int (*check_fb)(struct fb_info *);
};
在/sys/class/backlight/目录下注册和移除具体的背光设备时调用下面两个函数:
struct backlight_device *backlight_device_register(const char *name,struct device *dev, void *devdata, struct backlight_ops *ops);
void backlight_device_unregister(struct backlight_device *bd);
背光设备驱动框架分析
下面我们来对IMX233LCD背光设备的驱动框架进行下分析,设计到的硬件的不做介绍,不同的开发平台具有硬件差异性,下篇文章将会对当前平台硬件相关的部分做介绍。
module_init(genericbl_init);
module_exit(genericbl_exit);static int __init genericbl_init(void)
{
return platform_driver_register(&genericbl_driver);
}static void __exit genericbl_exit(void)
{
platform_driver_unregister(&genericbl_driver);
}
module_init() –> genericbl_init() –> platform_driver_register(&genericbl_driver);
module_exit() –> genericbl_exit() –> platform_driver_unregister(&genericbl_driver);
genericbl_driver如下:
static int genericbl_probe(struct platform_device *pdev);
static int genericbl_remove(struct platform_device *pdev);
static struct platform_driver genericbl_driver = {
.probe = genericbl_probe,
.remove = genericbl_remove,
.driver = {
.name = "generic-bl",
},
};
platform_device和platform_driver结构体分别对应平台设备和驱动,具体看参见platform_device.h头文件。genericbl_probe回调函数很关键,可以看做一个探测函数,在注册设备驱动之后如果探测到与此驱动相对应的背光设备时才会运行genericbl_probe函数,同时会传递一个保存此设备信息的platform_device结构体,并且在会在/sys/class/backlight目录下注册并建立具体的背光设备文件(包括对应背光设备的属性文件),最后还要进行相关的初始化工作。genericbl_remove回调函数是移除/sys/class/backlight/目录下相应的设备文件(包括对应背光设备的属性文件)。genericbl_probe函数中在/sys/class/backlight目录下注册具体的背光设备时调用如下函数:
struct backlight_device *backlight_device_register(const char *name,struct device *parent, void *devdata, struct backlight_ops *ops)
想进一步了解backlight_device_register函数实现的代码可阅读backlight.c,该函数返回一个backlight_device背光设备结构体的指针,背光设备文件注册创建之后其目录会包含几个属性文件(见图二),主要属性文件是brightness和max_brightness,分别表示背光灯的亮度和最大亮度。需要注意的是注册完设备文件之后要进行相关的属性文件初始化。背光设备注册函数的第四个参数是个backlight_ops结构体指针,参考backlight.h,本驱动对应的结构体指针genericbl_ops初始化如下:
static struct backlight_ops genericbl_ops = {
.get_brightness = genericbl_get_intensity,
.update_status = genericbl_send_intensity,
};
static int genericbl_send_intensity(struct backlight_device *bd)会接触到硬件相关的部分,它主要通过修改寄存器来达到修改背光灯设备亮度等相关属性,而static int genericbl_get_intensity(struct backlight_device *bd)是获取背光设备的亮度属性,不需要接触硬件。
图二 背光设备属性文件
当更新背光灯亮度的时候我们,我们会通过修改/sys/class/backlight/背光设备文件名/下的brightness这个背光属性文件,它的值一般为0 – 10 ,最大值为同目录下max_brightness属性文件的值,修改brightness文件的值后会最终调用背光等驱动的genericbl_send_intensity函数来修改相对应的寄存器,从而形成对应的波形输入到RT9284B15PJ6芯片,最后来更改LCD背光灯的亮度,具体的原理看参考前一篇背光设备的原理。