如何撰寫Driver支援sysfs檔案系統
前言
在現在Linux的系統中,有一個新的虛擬檔案系統,叫做sysfs,它只要是來管理Linux系統中的週邊元件,及配合使用公用程式來自動產生device node。其實它是一個虛擬的記憶體檔案系統,它是由proc所變化而來的,它是由各個driver所產生,換另一句話就是要Driver有撰寫才會有這樣的功能,它是繼承kobject的資料結構,另外其檔名及內容都是driver自行解決,目前主要都是來設定driver自己本身的一些功能,這樣可以讓驅動程式更多元化,或者可以讀取驅動程式現在的情形為何,這可以來維護系統的運作,現在有很多驅動程式已經支援這個功能,所以有許多的系統管理程式,也是透過這個介面來管理系統核心。現在我們來介紹如何在driver中撰這些程式碼。
如何啓動這個目錄
因為它是一個虛擬的記憶體檔案系統,所以將使用Linux系統中管理檔案系統一樣的方法,就是使用mount的指令將其掛載在作業系統的檔案系統中,以下為單獨載入的指令:
> mount –t sysfs sysfs /sys
另外在kernel的選項中必須要啓動以下選項:
CONFIG_SYSFS=y
系統驅動程式結構
我們先來了解一下linux kernel其中驅動程式的結構,在linux kernel的驅動程式中會將各種元件給予分門別類,並且在各種類別中會給予建構一個管理層的驅動程式,其主要目的是要建立和應用程式之間的標準介面,並且管理相同類別中的各家不同的硬體廠商,如此一來即可規範硬體廠商如何撰寫廠商,避免因不同的廠商而產生不同的驅動程式的撰寫格式不同,或者造成應用程式的介面,進而造成使用者因不同的廠商而要修改軟體。其架構圖如下:
圖一:驅動程式系統架構
以圖一中來看將會有一個RTC class產生,它將會統籌管理系統中所有的RTC元件,而系統中所有的RTC將會和它來做一個註冊,如此會產生一個RTC的class及其底下的RTC0, RTC1 ….. RTCn,其會在/sys/class之下會建立一個RTC的目錄,而每一個註冊的RTC元件將會在RTC class目錄之下產生RTC0至RTCn的子目錄,而每一個子目錄將會各別產生或者說是繼承RTC class的各個檔案。
Kernel API
在介紹如何撰寫前,先來了解有那些kernel API可供使用,其所用的include file是,而API如下:
struct class *class_create(struct module *owner, const char *name)
這是建立一個模組的類別,其input/output說明如下:
Input :
struct module *owner – 模組的擁有者,通常是設定為THIS_MODULE。
const char *name –要建立之類別名稱,之後可以建立在這個類別之下的檔案。
Output :
其返回值為一個class資料結構的位址point,這個資料結構會在後面介紹,這個point位址將會給予保留,因為我們需要在其中幾個call back欄位填入我們要實現的功能,也就是提供給AP如何溝通的介面。
extern struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) __attribute__((format(printf, 5, 6)));
#define device_create_drvdata device_create
這是在該類別之下建立一個週邊元件,也就是建立一個device。
Input :
struct class *cls – 是前一個API所return回來的class pointer。
struct devuce *parent – 為該元件的上一層driver為何若没有可以設定為NULL。
dev_t devt – 這是該元件的major number & minor number的結合,可以使用巨集指令MKDEV()來產生。
const char *fmt – 為該元件的device node名稱。
Output :
其返回值為一個device的資料結構,可讓driver其它地方使用。
資料結構
如前面所提,其中最重要的資料結構宣告如下:
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
struct pm_ops *pm;
struct class_private *p;
};
其中的成員struct device_attribute *dev_attrs; 是比較重要,這是設定在這類別之下所建立的元件都會繼承這些基設定,而每一個設都可以設定為唯讀或可讀寫,可以參考以下範例得到較清楚的解答。
範例程式
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/tty.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#define VICTOR_DEBUG
#include "../include/mydebug.h"
#define MY_MAJOR 40
struct class *sysfs_class;
static ssize_t
sysfs_show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "show name\n");
}
static ssize_t
sysfs_show_date(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t retval;
retval = sprintf(buf, "show date\n");
return retval;
}
/*
這是一個讀取的call back function,必需返回共有多少的bytes放在buffer中。
*/
static ssize_t
sysfs_show_time(struct device *dev, struct device_attribute *attr, char *buf)
{
ssize_t retval;
retval = sprintf(buf, "show time\n");
return retval;
}
static ssize_t
sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "show max user freq\n");
}
/*
這是一個寫入的call back functio,一樣必需返回共有多少bytes被處理。
*/
static ssize_t
sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t n)
{
printk("get string = %s\n", buf);
return n;
}
/*
以下設定中,S_IRUGO表示唯讀而己,S_IWUR表示可以寫入,而相對應的call back function必需要實現,其定義在linux/stat.h之。
*/
static struct device_attribute sysfs_attrs[] = {
__ATTR(name, S_IRUGO, sysfs_show_name, NULL),
__ATTR(date, S_IRUGO, sysfs_show_date, NULL),
__ATTR(time, S_IRUGO, sysfs_show_time, NULL),
__ATTR(max_user_freq, S_IRUGO | S_IWUSR, sysfs_show_max_user_freq, sysfs_set_max_user_freq),
{ },
};
static int sysfs_suspend(struct device *dev, pm_message_t mesg)
{
dbg_printk("\n");
return 0;
}
static int sysfs_resume(struct device *dev)
{
dbg_printk("\n");
return 0;
}
static int __init mysysfs_init(void)
{
dbg_printk("\n");
sysfs_class = class_create(THIS_MODULE, "mysysfs");
if (IS_ERR(sysfs_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(sysfs_class);
}
dbg_printk("class create OK.\n");
sysfs_class->suspend = sysfs_suspend;
sysfs_class->resume = sysfs_resume;
sysfs_class->dev_attrs = sysfs_attrs;
device_create_drvdata(sysfs_class, NULL, MKDEV(200, 0), NULL, "mysysfs0");
return 0;
}
static void __exit mysysfs_exit(void)
{
dbg_printk("\n");
device_destroy(sysfs_class, MKDEV(200, 0));
class_destroy(sysfs_class);
}
module_init(mysysfs_init);
module_exit(mysysfs_exit);
沒有留言:
張貼留言