寫在前面的話:
對於框架,我覺得就是在一定規範的形式下去實現你要的功能。這裏就涉及到一個變與不變的地方。你所要實現的功能會是千差萬別的---這就是變的地方,而所謂既定的規範,包括建立目錄和屬性文件這是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文件和源代碼。
希望大家多多提意見哦!