linux驅動中如何自動生成設備文件節點?

linux驅動中如何自動生成設備文件節點?


一、自動生成設備文件的必要性

    在我們初學驅動開發的時候,我們的設備文件需要我們在知道設備號之後,使用命令 “mknod c|b dev_name major minor” 來生成,這樣的設備文件生成方法在實際項目中顯然是不行的,當驅動程序過多的時候,我們很難有精力來管理這麼多的設備文件。因此,在驅動中,使得設備文件能夠自動生成非常有必要。下面,將介紹在兩種不同目錄下生成設備文件的方法。

二、如何自動生成設備文件節點

1. mdev機制

    udev 是一個用戶程序,在 Linux 下通過 udev 來實現設備文件的創建與刪除,udev 可以檢測系統中硬件設備狀態,可以根據系統中硬件設備狀態來創建或者刪除設備文件。比如使用insmod或modprobe 命令成功加載驅動模塊以後就自動在/dev 目錄下創建對應的設備節點文件,使用rmmod 命令卸載驅動模塊以後就刪除掉/dev 目錄下的設備節點文件。使用 busybox 構建根文件系統的時候,busybox 會創建一個 udev的簡化版本—mdev,所以在嵌入式 Linux 中我們使用mdev 來實現設備節點文件的自動創建與刪除,Linux 系統的熱插拔事件也由 mdev 管理,在/etc/init.d/rcS 文件中如下語句:

echo /sbin/mdev > /proc/sys/kernel/hotplug

    上述命令設置熱插拔事件由 mdev 來管理,關於 udev 或 mdev 更加詳細的工作原理這裏就不詳細探討了,我們重點來學習一下如何通過 mdev 來實現設備文件節點的自動創建與刪除。

2. 在/dev下生成設備文件節點
2.1 創建和刪除類

    自動創建設備節點的工作是在驅動程序的入口函數中完成的,一般在 cdev_add 函數後面添加自動創建設備節點相關代碼。首先要創建一個 class 類,class 是個結構體,定義在文件include/linux/device.h 裏面。class_create 是類創建函數,class_create 是個宏定義,內容如下:

#define class_create(owner, name)	\
({	\
	static struct lock_class_key __key; 	\
	__class_create(owner, name, &__key);	\
})

struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)

    根據上述代碼,將宏 class_create 展開以後內容如下:

struct class *class_create (struct module *owner, const char *name)

    class_create 一共有兩個參數,參數 owner 一般爲 THIS_MODULE,參數 name 是類名字。返回值是個指向結構體 class 的指針,也就是創建的類。
    卸載驅動程序的時候需要刪除掉類,類刪除函數爲 class_destroy,函數原型如下:

void class_destroy(struct class *cls);

    參數 cls 就是要刪除的類。

2.2 創建和刪除設備

    一小節創建好類以後還不能實現自動創建設備節點,我們還需要在這個類下創建一個設備。使用 device_create 函數在類下面創建設備,device_create 函數原型如下:

struct device *device_create(struct class*class,
								struct device *parent,
								dev_t devt,
								void *drvdata,
								const char *fmt, ...)

    device_create 是個可變參數函數,參數 class 就是設備要創建哪個類下面;參數 parent 是父設備,一般爲 NULL,也就是沒有父設備;參數 devt 是設備號;參數 drvdata 是設備可能會使用的一些數據,一般爲 NULL;參數 fmt 是設備名字,如果設置 fmt=xxx 的話,就會生成/dev/xxx這個設備文件。返回值就是創建好的設備。
    同樣的,卸載驅動的時候需要刪除掉創建的設備,設備刪除函數爲 device_destroy,函數原型如下:

void device_destroy(struct class *class, dev_t devt)

    參數 class 是要刪除的設備所處的類,參數 devt 是要刪除的設備號。

2.3 參考示例
struct user_dev_t {
	struct class *class; /* 類 */
	struct device *device; /* 設備 */
    dev_t devid;
};
struct user_dev_t user_dev;

#define DEV_CLASS_NAME "yyy"
#define DEV_NAME	"xxx"

/* 驅動入口函數 */
static int __init xxx_init(void)
{
	/* 創建類 */
	user_dev.class = class_create(THIS_MODULE, DEV_CLASS_NAME);
	/* 創建設備 */
	user_dev.device = device_create(user_dev.class, NULL, user_dev.devid, NULL, DEV_NAME);
	return 0;
}

/* 驅動出口函數 */
static void __exit xxx_exit(void)
{
	/* 刪除設備 */
	device_destroy(user_dev.class, user_dev.devid);
	/* 刪除類 */
	class_destroy(user_dev.class);
}

module_init(xxx_init);
module_exit(xxx_exit);
3. 在/sys/class中生成設備文件節點

    有時候,我們會採取在 /sys/class 中生成設備文件節點,而不是 /dev 中。

3.1 創建、刪除類和設備

    首先,我們需要使用結構struct class_attribute創建一個屬性數組,來組織設備文件的讀寫函數,struct class_attribute定義如下:

struct class_attribute {
	struct attribute attr;
	ssize_t (*show)(struct class *class, struct class_attribute *attr, 
    		char *buf);
	ssize_t (*store)(struct class *class, struct class_attribute *attr,
			const char *buf, size_t count);
};

    attr爲屬性,show函數爲讀文件操作函數,store函數爲寫文件操作函數。
其中,結構struct attribute結構定義如下:

struct attribute {
	const char		*name;
	umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};

    name爲設備文件名稱,mode爲模式。
    然後,需要定義一個struct class結構,爲其成員class_attrs指定爲上述結構。結構全部定義完畢之後,便可以使用函數class_register將class註冊到系統。class_register定義如下:

#define class_register(class)			\
({						\
	static struct lock_class_key __key;	\
	__class_register(class, &__key);	\
})

int __class_register(struct class *cls, struct lock_class_key *key)

其爲一個宏,其中, 參數class爲上述定義的struct class。我們將其展開後得到如下接口:

int class_register(struct class *cls);

在驅動卸載的時候,需要刪除這個class,使用函數class_unregister,其定義如下:

void class_unregister(struct class *cls);
3.2 參考示例

    在下面的參考示例中,會生成設備文件 /sys/class/eurphan/xxx

/*
 * @description		: sysfs文件節點讀函數
 * @param - class	: 傳遞給驅動的class
 * @param - attr	: sysfs文件節點屬性
 * @param - buff	: 接收數據的緩存
 * @return			: 讀取到的數據個數
 */
static ssize_t xxx_show(struct class *class, struct class_attribute *attr, char *buff)
{
	return 0;
}

/*
 * @description		: sysfs文件節點寫函數
 * @param - class	: 傳遞給驅動的class
 * @param - attr	: sysfs文件節點屬性
 * @param - buff	: 寫入的數據緩存
 * @param - count	: 寫入的數據個數
 * @return			: 實際寫入的數據個數
 */
static ssize_t xxx_store(struct class *class, struct class_attribute *attr, const char *buff, size_t count)
{
	return count;
}
/* class屬性結構 */
static struct class_attribute xxx_class_attr[] = {
	__ATTR(xxx, 0644, xxx_show, xxx_store),
	__ATTR_NULL,
};

/* class結構 */
static struct class xxx_class = {
	.name = "eurphan",
	.owner = THIS_MODULE,
	.class_attrs = xxx_class_attr,
};

/*
 * @description		: 驅動卸載函數
 * @param			: 無
 * @return			: 加載是否成功
 */
static __init int xxx_init(void)
{
	class_register(&xxx_class);
	return 0;
}

/*
 * @description		: 驅動卸載函數
 * @param			: 無
 * @return			: 無
 */
static __exit void xxx_exit(void)
{
	class_unregister(&xxx_class);
}

module_init(xxx_init);
module_exit(xxx_exit);

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