写在前面的话:
对于框架,我觉得就是在一定规范的形式下去实现你要的功能。这里就涉及到一个变与不变的地方。你所要实现的功能会是千差万别的---这就是变的地方,而所谓既定的规范,包括建立目录和属性文件这是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文件和源代码。
希望大家多多提意见哦!