用户使用open函数,open函数第一个参数是指定路径下的文件名,通过虚拟文件系统层可以根据文件名找到inode号,进而找到文件对应的inode结构体。在indoe结构体中找到文件对应的cdev设备驱动结构体,存在操作方法file_operations指针,会回调操作方法的open函数。
字符设备注册流程
1.为字符设备驱动对象申请空间
2.字符设备驱动对象的初始化
2.1实现字符设备驱动对象的部分初始化
2.2申请设备号
3.将字符设备驱动对象注册进内核
注销流程
1.注销字符设备驱动
2.释放目录空间
3.注销字符设备驱动对象
4.释放申请的设备号
5.释放对象空间
实现代码
#include <linux/init.h> #include <linux/module.h> #include<linux/fs.h> #include<linux/io.h> #include<linux/device.h> #include<linux/cdev.h> #include<linux/slab.h> #include"led.h" struct cdev *cdev; unsigned int major=500; unsigned int minor=0; dev_t devno; struct class *cls; struct device * dev; int mycdev_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return 0; } int mycdev_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } //定义操作方法结构体变量并赋值 struct file_operations fops={ .open=mycdev_open, .unlocked_ioctl=mycdev_ioctl, .release=mycdev_close, }; static int __init mycdev_init(void) { int ret,i; //1.分配对象空间 cdev=cdev_alloc(); if(cdev==NULL) { printk("分配字符设备驱动对象失败\n"); ret=-EFAULT; goto OUT1; } printk("分配对象空间成功\n"); //2.初始化对象 cdev_init(cdev,&fops); //3.申请设备号 if(major>0)//静态指定设备号 { ret= register_chrdev_region(MKDEV(major,minor),3,"myled"); if(ret) { printk("静态指定设备号失败\n"); goto OUT2; } } else if(major==0)//动态申请设备号 { ret=alloc_chrdev_region(&devno,minor,3,"myled"); if(ret) { printk("动态申请设备号失败\n"); goto OUT2; } //获取主设备号和次设备号 major=MAJOR(devno); minor=MINOR(devno); } printk("申请设备号成功\n"); //4.注册字符设备驱动对象 ret=cdev_add(cdev,MKDEV(major,minor),3); if(ret) { printk("注册字符设备驱动对象失败\n"); goto OUT3; } printk("注册字符设备驱动对象成功\n"); //向上提交目录 cls=class_create(THIS_MODULE,"myled"); if(IS_ERR(cls)) { printk("向上提交目录失败\n"); ret=-PTR_ERR(cls); goto OUT4; } printk("向上提交目录成功\n"); //向上提交设备节点信息 for(i=0;i<3;i++) { dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i); if(IS_ERR(dev)) { ret=-PTR_ERR(dev); goto OUT5; } } printk("向上提交设备节点成功\n"); //完成硬件寄存器地址的映射以及初始化 return 0; OUT5: //释放已经申请的设备节点信息 for(--i;i>=0;i--) { device_destroy(cls,MKDEV(major,i)); } //释放目录空间 class_destroy(cls); OUT4: //注销字符设备驱动对象 cdev_del(cdev); OUT3: //释放设备号 unregister_chrdev_region(MKDEV(major,minor),3); OUT2: //释放对象空间 kfree(cdev); OUT1: return ret; } static void __exit mycdev_exit(void) { //销毁设备节点 int i; for(i=0;i<3;i++) { device_destroy(cls,MKDEV(major,i)); } //释放目录空间 class_destroy(cls); //1.注销字符设备驱动对象 cdev_del(cdev); //2.释放设备号 unregister_chrdev_region(MKDEV(major,minor),3); //3.释放对象空间 kfree(cdev); } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL");