上一篇我們介紹到創建設備文件的方法,利用cat /proc/devices查看申請到的設備名,設備號。
第一種是使用mknod手工創建:mknod filename type 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下尋找對應的類從而創建設備節點。
下面是兩個函數的解析:
1、class_create(...) 函數
功能:創建一個類;
下面是具體定義:
[cpp]?view plaincopy
#define?class_create(owner,?name)???????\?? ({??????????????????????\?? ????static?struct?lock_class_key?__key;?\?? ????__class_create(owner,?name,?&__key);????\?? })??
owner:THIS_MODULE
name ?: 名字
__class_create(owner, name, &__key)源代碼如下:
[cpp]?view plaincopy
struct?class?*__class_create(struct?module?*owner,?const?char?*name,?? ?????????????????struct?lock_class_key?*key)?? {?? ????struct?class?*cls;?? ????int?retval;?? ?? ????cls?=?kzalloc(sizeof(*cls),?GFP_KERNEL);?? ????if?(!cls)?{?? ????????retval?=?-ENOMEM;?? ????????goto?error;?? ????}?? ?? ????cls->name?=?name;?? ????cls->owner?=?owner;?? ????cls->class_release?=?class_create_release;?? ?? ????retval?=?__class_register(cls,?key);?? ????if?(retval)?? ????????goto?error;?? ?? ????return?cls;?? ?? error:?? ????kfree(cls);?? ????return?ERR_PTR(retval);?? }?? EXPORT_SYMBOL_GPL(__class_create);??
銷毀函數:void class_destroy(struct class *cls)
[cpp]?view plaincopy
void?class_destroy(struct?class?*cls)?? {?? ????if?((cls?==?NULL)?||?(IS_ERR(cls)))?? ????????return;?? ?? ????class_unregister(cls);?? }??
2、device_create(...) 函數
struct device *device_create(struct class *class, struct device *parent,
? ? ? ? ? ? ? ? ?dev_t devt, void *drvdata, const char *fmt, ...)
功能:創建一個字符設備文件
參數:
? ? ? struct class *class ?:類
? ? ? struct device *parent:NULL
? ? ?dev_t devt ?:設備號
? ? ?void *drvdata ?:null、
? ? ?const char *fmt ?:名字
返回:
? ??struct device *
下面是源碼解析:
[cpp]?view plaincopy
struct?device?*device_create(struct?class?*class,?struct?device?*parent,?? ?????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?...)?? {?? ????va_list?vargs;?? ????struct?device?*dev;?? ?? ????va_start(vargs,?fmt);?? ????dev?=?device_create_vargs(class,?parent,?devt,?drvdata,?fmt,?vargs);?? ????va_end(vargs);?? ????return?dev;?? }??
device_create_vargs(class, parent, devt, drvdata, fmt, vargs)解析如下:
[cpp]?view plaincopy
struct?device?*device_create_vargs(struct?class?*class,?struct?device?*parent,?? ???????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?? ???????????????????va_list?args)?? {?? ????return?device_create_groups_vargs(class,?parent,?devt,?drvdata,?NULL,?? ??????????????????????fmt,?args);?? }??
現在就不繼續往下跟了,大家可以繼續往下跟;
下面是一個實例:
hello.c
[cpp]?view plaincopy
#include?<linux/module.h>?? #include?<linux/fs.h>?? #include?<linux/cdev.h>?? #include?<linux/device.h>?? ?? static?int?major?=?250;?? static?int?minor=0;?? static?dev_t?devno;?? static?struct?class?*cls;?? static?struct?device?*test_device;?? ?? static?int?hello_open?(struct?inode?*inode,?struct?file?*filep)?? {?? ????printk("hello_open?\n");?? ????return?0;?? }?? static?struct?file_operations?hello_ops=?? {?? ????.open?=?hello_open,?? };?? ?? static?int?hello_init(void)?? {?? ????int?ret;?????? ????printk("hello_init?\n");?? ?? ?? ????devno?=?MKDEV(major,minor);?? ????ret?=?register_chrdev(major,"hello",&hello_ops);?? ?? ????cls?=?class_create(THIS_MODULE,?"myclass");?? ????if(IS_ERR(cls))?? ????{?? ????????unregister_chrdev(major,"hello");?? ????????return?-EBUSY;?? ????}?? ????test_device?=?device_create(cls,NULL,devno,NULL,"hello");?? ????if(IS_ERR(test_device))?? ????{?? ????????class_destroy(cls);?? ????????unregister_chrdev(major,"hello");?? ????????return?-EBUSY;?? ????}????? ????return?0;?? }?? static?void?hello_exit(void)?? {?? ????device_destroy(cls,devno);?? ????class_destroy(cls);??? ????unregister_chrdev(major,"hello");?? ????printk("hello_exit?\n");?? }?? MODULE_LICENSE("GPL");?? module_init(hello_init);?? module_exit(hello_exit);??
test.c
[cpp]?view plaincopy
#include?<sys/types.h>?? #include?<sys/stat.h>?? #include?<fcntl.h>?? #include?<stdio.h>?? ?? ?? main()?? {?? ????int?fd;?? ?? ?? ????fd?=?open("/dev/hello",O_RDWR);?? ????if(fd<0)?? ????{?? ????????perror("open?fail?\n");?? ????????return?;?? ????}?? ?? ?? ????close(fd);?? }??
makefile
[cpp]?view plaincopy
ifneq??($(KERNELRELEASE),)?? obj-m:=hello.o?? $(info?"2nd")?? else?? KDIR?:=?/lib/modules/$(shell?uname?-r)/build?? PWD:=$(shell?pwd)?? all:?? ????$(info?"1st")?? ????make?-C?$(KDIR)?M=$(PWD)?modules?? clean:?? ????rm?-f?*.ko?*.o?*.symvers?*.mod.c?*.mod.o?*.order?? endif??
下面可以看幾個class幾個名字的對應關系:
上一篇我們介紹到創建設備文件的方法,利用cat /proc/devices查看申請到的設備名,設備號。
第一種是使用mknod手工創建:mknod filename type 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下尋找對應的類從而創建設備節點。
下面是兩個函數的解析:
1、class_create(...) 函數
功能:創建一個類;
下面是具體定義:
[cpp]?view plaincopy
#define?class_create(owner,?name)???????\?? ({??????????????????????\?? ????static?struct?lock_class_key?__key;?\?? ????__class_create(owner,?name,?&__key);????\?? })??
owner:THIS_MODULE
name ?: 名字
__class_create(owner, name, &__key)源代碼如下:
[cpp]?view plaincopy
struct?class?*__class_create(struct?module?*owner,?const?char?*name,?? ?????????????????struct?lock_class_key?*key)?? {?? ????struct?class?*cls;?? ????int?retval;?? ?? ????cls?=?kzalloc(sizeof(*cls),?GFP_KERNEL);?? ????if?(!cls)?{?? ????????retval?=?-ENOMEM;?? ????????goto?error;?? ????}?? ?? ????cls->name?=?name;?? ????cls->owner?=?owner;?? ????cls->class_release?=?class_create_release;?? ?? ????retval?=?__class_register(cls,?key);?? ????if?(retval)?? ????????goto?error;?? ?? ????return?cls;?? ?? error:?? ????kfree(cls);?? ????return?ERR_PTR(retval);?? }?? EXPORT_SYMBOL_GPL(__class_create);??
銷毀函數:void class_destroy(struct class *cls)
[cpp]?view plaincopy
void?class_destroy(struct?class?*cls)?? {?? ????if?((cls?==?NULL)?||?(IS_ERR(cls)))?? ????????return;?? ?? ????class_unregister(cls);?? }??
2、device_create(...) 函數
struct device *device_create(struct class *class, struct device *parent,
? ? ? ? ? ? ? ? ?dev_t devt, void *drvdata, const char *fmt, ...)
功能:創建一個字符設備文件
參數:
? ? ? struct class *class ?:類
? ? ? struct device *parent:NULL
? ? ?dev_t devt ?:設備號
? ? ?void *drvdata ?:null、
? ? ?const char *fmt ?:名字
返回:
? ??struct device *
下面是源碼解析:
[cpp]?view plaincopy
struct?device?*device_create(struct?class?*class,?struct?device?*parent,?? ?????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?...)?? {?? ????va_list?vargs;?? ????struct?device?*dev;?? ?? ????va_start(vargs,?fmt);?? ????dev?=?device_create_vargs(class,?parent,?devt,?drvdata,?fmt,?vargs);?? ????va_end(vargs);?? ????return?dev;?? }??
device_create_vargs(class, parent, devt, drvdata, fmt, vargs)解析如下:
[cpp]?view plaincopy
struct?device?*device_create_vargs(struct?class?*class,?struct?device?*parent,?? ???????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?? ???????????????????va_list?args)?? {?? ????return?device_create_groups_vargs(class,?parent,?devt,?drvdata,?NULL,?? ??????????????????????fmt,?args);?? }??
現在就不繼續往下跟了,大家可以繼續往下跟;
下面是一個實例:
hello.c
[cpp]?view plaincopy
#include?<linux/module.h>?? #include?<linux/fs.h>?? #include?<linux/cdev.h>?? #include?<linux/device.h>?? ?? static?int?major?=?250;?? static?int?minor=0;?? static?dev_t?devno;?? static?struct?class?*cls;?? static?struct?device?*test_device;?? ?? static?int?hello_open?(struct?inode?*inode,?struct?file?*filep)?? {?? ????printk("hello_open?\n");?? ????return?0;?? }?? static?struct?file_operations?hello_ops=?? {?? ????.open?=?hello_open,?? };?? ?? static?int?hello_init(void)?? {?? ????int?ret;?????? ????printk("hello_init?\n");?? ?? ?? ????devno?=?MKDEV(major,minor);?? ????ret?=?register_chrdev(major,"hello",&hello_ops);?? ?? ????cls?=?class_create(THIS_MODULE,?"myclass");?? ????if(IS_ERR(cls))?? ????{?? ????????unregister_chrdev(major,"hello");?? ????????return?-EBUSY;?? ????}?? ????test_device?=?device_create(cls,NULL,devno,NULL,"hello");?? ????if(IS_ERR(test_device))?? ????{?? ????????class_destroy(cls);?? ????????unregister_chrdev(major,"hello");?? ????????return?-EBUSY;?? ????}????? ????return?0;?? }?? static?void?hello_exit(void)?? {?? ????device_destroy(cls,devno);?? ????class_destroy(cls);??? ????unregister_chrdev(major,"hello");?? ????printk("hello_exit?\n");?? }?? MODULE_LICENSE("GPL");?? module_init(hello_init);?? module_exit(hello_exit);??
test.c
[cpp]?view plaincopy
#include?<sys/types.h>?? #include?<sys/stat.h>?? #include?<fcntl.h>?? #include?<stdio.h>?? ?? ?? main()?? {?? ????int?fd;?? ?? ?? ????fd?=?open("/dev/hello",O_RDWR);?? ????if(fd<0)?? ????{?? ????????perror("open?fail?\n");?? ????????return?;?? ????}?? ?? ?? ????close(fd);?? }??
makefile
[cpp]?view plaincopy
ifneq??($(KERNELRELEASE),)?? obj-m:=hello.o?? $(info?"2nd")?? else?? KDIR?:=?/lib/modules/$(shell?uname?-r)/build?? PWD:=$(shell?pwd)?? all:?? ????$(info?"1st")?? ????make?-C?$(KDIR)?M=$(PWD)?modules?? clean:?? ????rm?-f?*.ko?*.o?*.symvers?*.mod.c?*.mod.o?*.order?? endif??
下面可以看幾個class幾個名字的對應關系:
總結
以上是生活随笔為你收集整理的Linux 字符设备驱动结构(二)—— 自动创建设备节点的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。