count web page visits
Panasonic Viera 3D

Linux驱动框架----hwmon

写在前面的话:

对于框架,我觉得就是在一定规范的形式下去实现你要的功能。这里就涉及到一个变与不变的地方。你所要实现的功能会是千差万别的---这就是变的地方,而所谓既定的规范,包括建立目录和属性文件这是Linux系统已经为我们做好了的,我们只需要直接拿来引用就OK了。

那么今天,我们就来看看hwmon框架是怎么样的。


对hwmon而言,它是sysfs框架下的一个类,但是所有有关该类与sys的接口都已经在drivers/hwmon/hwmon.c实现,因而我们也不必过多的关心。

那么我们到底要做些什么呢?

对于你要实现的功能部分,你就自己去想象吧,下面我们来说说在我们的程序中对于不变的那部分要如何去实现。

我就以最基本的读CPU温度的程序为例,来说明整个框架的编写。

一.作为驱动,首先我们要做就是驱动的注册和撤销。

因此,在XXX_hwmon_init()函数中对hwmon驱动和该驱动的属性文件等进行注册。

my_hwmon_dev = hwmon_device_register(NULL);

ret = sysfs_create_group(&my_hwmon_dev->kobj,&my_hwmon_attribute_group);

ret = create_sysfs_temp_files(&my_hwmon_dev->kobj);

在有这么多的注册函数时,对于函数的异常处理,我个人觉得用goto来实现,比较直观和易用,让人看起来一目了然。

 1 static int __init my_hwmon_init(void)
 2 {
 3         int ret;
 4 
 5         printk(KERN_INFO "my cpu temperature hwmon enter!\n");
 6 
 7         my_hwmon_dev = hwmon_device_register(NULL);
 8         if (IS_ERR(my_hwmon_dev)) {
 9                 ret = -ENOMEM;
10                 printk(KERN_ERR "my_hwmon_device_register fail!\n");
11                 goto fail_hwmon_device_register;
12         }
13 
14         ret = sysfs_create_group(&my_hwmon_dev->kobj,
15                                 &my_hwmon_attribute_group);
16         if (ret) {
17                 printk(KERN_ERR "fail to create my hwmon!\n");
18                 goto fail_create_group_hwmon;
19         }
20 
21         ret = create_sysfs_temp_files(&my_hwmon_dev->kobj);
22         if (ret) {
23                 printk(KERN_ERR "fail to create temperature files!\n");
24                 goto fail_create_sysfs_temp_files;
25         }
26         return ret;
27 
28 fail_create_sysfs_temp_files:
29         sysfs_remove_group(&my_hwmon_dev->kobj, &my_hwmon_attribute_group);
30 fail_create_group_hwmon:
31         hwmon_device_unregister(my_hwmon_dev);
32 fail_hwmon_device_register:
33         return ret;
34 }

而在XXX_hwmon_exit()函数中,和XXX_hwmon_init()函数相对应的,以相反的顺序对已注册的设备进行撤销。

remove_sysfs_temp_files(&my_hwmon_dev->kobj);

sysfs_remove_group(&my_hwmon_dev->kobj,&my_hwmon_attribute_group);

hwmon_device_unregister(my_hwmon_dev);

好了,至此,hwmon架构的初始化和撤销的工作已经完成。

二.接下去就是对hwmon下的各种属性文件的创建和对温度的读写。

1.hwmon整体属性框架

总的来讲分为三部曲:

首先,每个hwmon设备都会有自己独有的属性,这些独有属性就被SENSOR_DEVICE_ATTR声明为struct attribute结构体。

SENSOR_DEVICE_ATTR具体定义在include/linux/hwmon-sysfs.h中:

1 #define SENSOR_DEVICE_ATTR(_name, _mode, _show, _store, _index) \
2 
3 struct sensor_device_attribute sensor_dev_attr_##_name          \
4 
5         = SENSOR_ATTR(_name, _mode, _show, _store, _index)

在程序中,则被写作:

1 static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_my_hwmon_name, NULL, 0);

 然后,加入到sysfs框架的struct attribute属性结构体数组中,注意该结构体数组最后一项必须以NULL结尾。

struct attribute定义在include/linux/sysfs.h中。

1 static struct attribute *my_hwmon_attributes[] = {
2         &sensor_dev_attr_name.dev_attr.attr,
3         NULL,
4 };

最后,将这些众多的属性汇总到结构struct attribute_group中。这个struct attribute_group被sysfs_create_group()函数调用建立整个属性框架。

struct attribute_group定义在include/linux/sysfs.h中。

1 static struct attribute_group my_hwmon_attribute_group = {
2         .attrs = my_hwmon_attributes,
3 };

在我的程序中,这里只有一个属性—name,表示该hwmon驱动的名称。

2.温度相关属性文件的建立

在上面,整个hwmon的框架已经建立,接下来就是温度的读取,显示。这个过程其实是sysfs框架的内容。

首先,确定会有多少属性,每个属性文件都会被SENSOR_DEVICE_ATTR声明为struct attribute结构体。其中的_show和_store是以函数形式实现,因此对应的实现相应的函数,若没有这个功能的实现就以NULL代替;

_show表示的是从属性文件中读取出数据。

_store表示的是向属性文件中写入数据。

因此,如果该属性文件是不可写是,_store实现为NULL。

然后,将同一seneor的属性加入到sysfs框架的struct attribute属性结构体数组中,注意该结构体数组最后一项必须以NULL结尾,被sysfs_create_files()函数调用。

以上两步是建立属性文件最基本的两步,我们的具体功能可以在_show和_store两个函数中去实现。当然对于一些复杂的设备来讲仅仅这两步是不够的,如果该设备属于平台驱动或者是某一大类设备,如I2C,我们此时就需要根据具体情况去声明相应的结构,并按照相应结构的规范去操作。

在我的程序中仅限于前两步,因为程序中并未涉及到任何其他设备,只是简单的读取了温度寄存器。程序中定义了两个属性my_cpu_temp_label和get_my_cpu_temp。

下面是我模块在加载后生成目录的tree型图:

       

这些就是我对hwmon框架在目前基础上的了解。

最后向大家做个推荐啊:我的所有程序都会在github上,所以,大家要是有兴趣的话,可以去github上去clone。

我的github地址为:https://github.com/Antonio-Zhou

这篇文章中完整代码的下载方式为:git clone git://github.com/Antonio-Zhou/LoongsonDriver.git

下载之后,其中会有一个cpu_temp的目录,里面存放着makefile文件和源代码。

希望大家多多提意见哦!

posted @ 2012-11-12 15:31  夜半私时  阅读(18540)  评论(4编辑  收藏  举报