一、内核中为什么引入自动创建设备节点
1.因为应用层通过设备节点调用内核层,每次都需要输入mknod命令手动创建设备节点
2.mknod命令手动创建设备节点,需要注意主设备号和次设备号
二、自动创建设备节点机制
udev机制:在2.6内核版本引入,udev自动创建设备几点机制在用户空间实现
mdev是轻量级的udev,在嵌入式开发中,常用udev自动创建设备节点机制
三、自动创建设备节点实现
四、向上层提交目录信息
#include <linux/device.h>
struct class* class_create(owner, name)
函数功能:向应用层/sys/class目录下,提交目录信息
参数:
owner:THIS_MODULE
name:向上层提交目录名字
返回值: 成功返回struct class* 结构体指针 ====> 目录句柄
失败借助于IS_ERR,判断是否在4K空间
例子: struct class* cls;
cls = class_create(THIS_MODULE,"myled");
if (IS_ERR(cls)) {
return PTR_ERR(cls);
}
void class_destroy(struct class *cls);//取消向上层提交目录信息
五、向上层提交设备节点信息
struct device * device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
函数功能:向/dev目录下,提交设备节点信息
参数:
cls:目录句柄指针
parent:NULL
devt:设备号
MAJOR(dev):根据设备号,获取主设备号的值
MINOR(dev):根据设备号,获取次设备号的值
MKDEV(ma,mi):根据主设备号和次设备号,合成设备号
drvdata:NULL
fmt:格式控制符
....:可变参数
返回值:
成功返回struct device* 结构体指针
失败借助于IS_ERR,判断是否在4K空间
void device_destroy(struct class *cls, dev_t devt); //取消向上层提交设备节点信息
六、如何判断内核中错误码
1.在内核中顶端预留出4k空间用来存放错误码
2.通过IS_ERR将错误码转换为地址,判断地址是否在顶层4k空间
3.通过PTR_ERR将地址转换为错误码,返回错误码
七、练习代码
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/device.h> #define CNAME "mycdev" unsigned int major = 0; char kbuf[128] = {}; struct class* cls; struct device* device; int myled_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff) { int ret; printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); //如果用户空间想读的大小256个字节,大于内核空间的大小128个字节,需要更正用户空间读的大小 if(size > sizeof(kbuf)) size = sizeof(kbuf); ret = copy_to_user(ubuf,kbuf,size); //将内核空间的数据,写入到用户空间 if(ret){ printk("copy to user is error\n"); return -EIO; } return size; } ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff) { int ret; printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); //如果用户空间想写的大小256个字节,大于内核空间的大小128个字节,需要更正用户空间写的大小 if(size > sizeof(kbuf)) size = sizeof(kbuf); ret = copy_from_user(kbuf,ubuf,size); //将用户空间的数据,写入到内核空间 if(ret){ printk("copy from user is error\n"); return -EIO; } printk("kernel kbuf=%s\n",kbuf); return size; } int myled_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } //操作方法结构体 const struct file_operations fops = { .open = myled_open, .read = myled_read, .write = myled_write, .release = myled_close, }; //入口函数 static int __init demo_init(void) { //注册字符设备驱动 major = register_chrdev(0,CNAME,&fops); if(major < 0){ printk("register chrdev is error\n"); return -EIO; } printk("major = %d\n",major); //向上层提交目录信息 cls = class_create(THIS_MODULE,CNAME); if(IS_ERR(cls)) //判断是否在4K空间 { printk("class create is error\n"); return PTR_ERR(cls); //将地址转换为错误码 } //向上层提交设备节点信息 device = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME); if(IS_ERR(device)) //判断是否在4K空间 { printk("device create is error\n"); return PTR_ERR(device); //将地址转换为错误码 } return 0; //函数的返回值 } //出口函数 static void __exit demo_exit(void) { device_destroy(cls,MKDEV(major,0)); //取消向上层提交设备节点信息 class_destroy(cls); //取消向上层提交目录信息 unregister_chrdev(major,CNAME);//注销字符设备驱动 } module_init(demo_init); //指定入口地址 module_exit(demo_exit); //指定出口地址 MODULE_LICENSE("GPL"); //许可证,遵循GPL协议