第一種是使用mknod手工創建:# mknod <devfilename> <devtype> <major> <minor>
第二種是自動創建設備節點:利用udev(mdev)來實現設備文件的自動創建,首先應保證支持udev(mdev),由busybox配置。
具體udev相關知識這里不詳細闡述,可以移步Linux 文件系統與設備文件系統 —— udev 設備文件系統,這里主要講使用方法。
在驅動用加入對udev 的支持主要做的就是:在驅動初始化的代碼里調用class_create(...)為該設備創建一個class,再為每個設備調用device_create(...)創建對應的設備。
內核中定義的struct class結構體,顧名思義,一個struct class結構體類型變量對應一個類,內核同時提供了class_create(…)函數,可以用它來創建一個類,這個類存放於sysfs下面,一旦創建好了這個類,再調用 device_create(…)函數來在/dev目錄下創建相應的設備節點。
這樣,加載模塊的時候,用戶空間中的udev會自動響應 device_create()函數,去/sysfs下尋找對應的類從而創建設備節點。
下面是兩個函數的解析:
支持字符設備文件自動生成
linux/device.h
struct class *class_create(struct module *owner, const char *name); /* 功能:在/sys/class目錄下創建一個目錄,目錄名是name指定的 參數: struct module *owner - THIS_MODULE const char *name - 設備名 返回值: 成功:class指針 失敗: - bool IS_ERR(const void *ptr) 判斷是否出錯 long PTR_ERR(const void *ptr) 轉換錯誤碼 */
void class_destroy(struct class *cls); /* 功能:刪除class指針指向的目錄 參數: struct class *cls - class指針 */
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...); /* 功能: 在class指針指向的目錄下再創建一個目錄,目錄名由const char *fmt, ...指出、並導出設備信息(dev_t) 參數: struct class *cls - class指針 struct device *parent - 父對象,NULL dev_t devt - 設備號 void *drvdata - 驅動私有數據 const char *fmt, ... - fmt是目錄名字符串格式,...就是不定參數 返回值: 成功 - device指針 失敗 - bool IS_ERR(const void *ptr) 判斷是否出錯 long PTR_ERR(const void *ptr) 轉換錯誤碼 */
void device_destroy(struct class *cls, dev_t devt); /* 功能:刪除device_create創建的目錄 參數: struct class *cls - class指針 dev_t devt - 設備號 */
創建次序
struct class *class_create(struct module *owner, const char *name);
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
刪除次序
void device_destroy(struct class *cls, dev_t devt);
void class_destroy(struct class *cls);
實例:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/errno.h> #include <asm/current.h> #include <linux/sched.h> #include <linux/device.h> MODULE_LICENSE("GPL"); static struct class *cls = NULL; static int major = 0; static int minor = 0; const int count = 6; #define DEVNAME "demo" static struct cdev *demop = NULL; //打開設備 static int demo_open(struct inode *inode, struct file *filp) { //get command and pid printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__);return 0; } //關閉設備 static int demo_release(struct inode *inode, struct file *filp) { //get major and minor from inode printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__); return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .open = demo_open, .release= demo_release, }; static int __init demo_init(void) { dev_t devnum; int ret, i; struct device *devp = NULL; //1. alloc cdev obj demop = cdev_alloc(); if(NULL == demop){ return -ENOMEM; } //2. init cdev obj cdev_init(demop, &fops); ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME); if(ret){ goto ERR_STEP; } major = MAJOR(devnum); //3. register cdev obj ret = cdev_add(demop, devnum, count); if(ret){ goto ERR_STEP1; } cls = class_create(THIS_MODULE, DEVNAME); if(IS_ERR(cls)){ ret = PTR_ERR(cls); goto ERR_STEP1; } for(i = minor; i < (count+minor); i++){ devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i); if(IS_ERR(devp)){ ret = PTR_ERR(devp); goto ERR_STEP2; } } return 0; ERR_STEP2: for(--i; i >= minor; i--){ device_destroy(cls, MKDEV(major, i)); } class_destroy(cls); ERR_STEP1: unregister_chrdev_region(devnum, count); ERR_STEP: cdev_del(demop); //get command and pid printk(KERN_INFO "%s : %s : %d - fail.\n", __FILE__, __func__, __LINE__); return ret; } static void __exit demo_exit(void) { int i; //get command and pid printk(KERN_INFO "%s : %s : %d - leave.\n", __FILE__, __func__, __LINE__); for(i=minor; i < (count+minor); i++){ device_destroy(cls, MKDEV(major, i)); } class_destroy(cls); unregister_chrdev_region(MKDEV(major, minor), count); cdev_del(demop); } module_init(demo_init); module_exit(demo_exit);
下面可以看幾個class幾個名字的對應關系: