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文件和源代碼。

希望大家多多提意見哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章