要知道在Linux系统中,一切皆文件,一个底层设备的驱动程序固然也不例外,它以一种有异于普通文件的设备文件的身份幸存在于fs当中,设备文件与保存数据或读取存储数据的普通文件不同,它只提供可调用内核内部函数的信息:设备驱动程序类型(字符型/块型)、主设备号、次设备号。
应用程序为了使用设备,向内核申请控制设备,此时内核利用上述的3种信息中的设备驱动程序类型信息和主设备号,调用相应的设备驱动程序。至于次设备号,它只用作区分同一类设备驱动程序下的不同设备。
应用程序要操纵外部硬件设备,需要像对待普通文件一样,对此硬件对应的设备文件open(),read(),write(),···一系列的系统调用,从而实现了对外部设备的间接操纵。一个设备驱动往内核中注册后,要想在/dev目录下创建这样的一个相应设备文件,可通过如下两种方式实现:
1.手动创建
在insmod xxx.ko装载xxx驱动后,利用mknod命令手动创建相应设备节点:
mknod /dev/xxx c major minor /* c表示此为字符设备,major –主设备号,minor-次设备号 */
mknod /dev/xxx b major minor /* b表示此为块设备,major –主设备号,minor-次设备号 */
注意:可查看Documentation/device.txt文件预先确定可用的设备号,或,cat /proc/devices确定哪些设备号已经被用。
mknod 之后,可通过ls /dev查看,如有xxx,代表已经成功创建xxx设备节点。
2. 模块加载时候自动创建
内核中定义了struct class结构体,一个struct class结构体类型变量对应着一个类,内核提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。
这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
#define class_create(owner, name) \
({ \
static structlock_class_key __key; \
__class_create(owner, name,&__key); \
})
owner-此类是属于哪个设备的,此值通常为THIS_MODULE,表示调用此函数的模块;
name-此类的名字,随便起;
返回值为struct class *类型,即为要创建的类的指针地址。
紧接着,类下创建相应的节点,通过device_create()函数:
extern struct device *device_create(struct class *cls, struct device*parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));
cls-类的地址,即为此前device_create()返回的struct class *类型值;
parent-父类的地址,有就填,没有父类的,即为NULL值;
devt-设备的主次设备号,即为MKDEV(major,minor);
drvdata-设备值,只用作设备辨别用,有就填,没有就填NULL,有没有倒没多大的影响;
const char *fmt-设备节点的名字,即ls /dev/fmt
注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同而已,而且里面的参数设置也有一些微小的变化。
struct class和device_create(…)以及device_create(…)都定义在linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
下面给出一个使用实例:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/of_device.h> int device_driver_major_number; /* 设备的主设备号 */ static struct class *user_drv_class; /* 创建类,系统自动生成设备节点 */ static struct class_device *user_drv_class_dev; /* 类下创建设备 */ static int user_drv_open(struct inode *inode, struct file *file) { } static ssize_t user_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { } static ssize_t user_drv_read(struct file *file, char __user *user, size_t size,loff_t*o) { } /* 创建设备结构 file_operations*/ static struct file_operations user_drv_fops = { .owner = THIS_MODULE, .open = user_drv_open, .write = user_drv_write, .read = user_drv_read, }; /* 注册模块 */ static int user_drv_init(void) { /* 注册设备 */ device_driver_major_number = register_chrdev(0, "user_drv_name", &user_drv_fops); /* 注册类class_drv_name */ user_drv_class = class_create(THIS_MODULE, "class_drv_name"); /* 注册类下设备device_inode_name,/dev/device_inode_name */ user_drv_class_dev = device_create(user_drv_class, NULL, MKDEV(device_driver_major_number, 0), NULL, "device_inode_name"); return 0; } /* 卸载模块*/ static void user_drv_exit(void) { /* 注销设备 */ unregister_chrdev(major, "first_drv"); /* 注销类下的设备device_inode_name */ device_destroy(user_drv_class_dev, MKDEV(major, 0)); /* 注销类class_drv_name */ class_destroy(user_drv_class); } module_init(user_drv_init); module_exit(user_drv_exit); MODULE_LICENSE("GPL");
在模块注册module_init(user_drv_init)之时,调用user_drv_init()函数,register_chrdev()注册设备后即在/dev下自动创建相应的设备节点。
模块退出时,调用device_destroy(),class_destroy即可实现对类,设备文件的自动清除。