linux驱动开发 --miscdevice
来源:互联网 发布:apache服务器下载64位 编辑:程序博客网 时间:2024/06/09 13:47
前言
在linux驱动中,需要提供主设备号和次设备号号,通常使用的主设备号是从
0到 255之间的数,仅仅使用主设备号,还是不叫紧张的,因此需要利用次设备号。
linux中,提供了miscdevice这种杂项设备,指定主设备号为10,次设备号可以设置为
系统动态分配。
在具体分析miscdevice之前,先给出miscdevice的核心设计思想。
应用层,打开/dev/xxx 节点,会得到 文件描述符 fd,通过fd,可以执行 read()、
wriete()、ioctl()、mmap()等对这个文件的操作。
应用层调用 open()函数,经过系统调用后,会调用驱动程序的 open()函数。 在前边分析
调用过程时,分析过这个open()的调用过程。
驱动程序中,调用file_opeartions的open()的时候,此时,调用到另外一个 file_operations的
open(),此时应用层会得到这个open()时的 文件描述符fd,此时应用层 read()、write()等操作,
就调用到了这个file_operations。故在cdev_init时的 fops 充当一个中转的作用,转到具体的
fops 来操作。
给出在linux驱动开发之字符设备–自动创建设备节点
代码上的一个 diff
@@ -134,6 +135,19 @@ static struct file_operations fops ={ .unlocked_ioctl = cdev_demo_ioctl, };+static int misc_open(struct inode * inode, struct file * file)+{+ printk("%s,%d\n",__func__,__LINE__);+ + file->f_op = &fops;+ return file->f_op->open(inode,file) ;+}++static struct file_operations misc_fops = {+ .owner = THIS_MODULE;+ .open = misc_open,+};+ char cdev_buf[2] = "a"; static ssize_t cdev_demo_show(struct device *dev,struct device_attribute *attr, char *buf) @@ -171,7 +185,7 @@ static int __init cdev_demo_init(void) return -ENOMEM; }- cdev_init(pdev,&fops);+ cdev_init(pdev,&misc_fops); ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME); if(ret){
关键的代码是
file->f_op = &fops; return file->f_op->open(inode,file) ;
将变量 fops 赋值给了 file 的 f_op (const struct file_operations *f_op;)
在返回的时候,调用这个file->f_op-open,其实质是调用到 fops的open()。
这样的一种方式,实现了 中转的作用。 利用这种中转,可以进行代码的分层,在通用层中
实现抽象的open(),在具体的设备中,实现具体的open()、read()、write()。
miscdevice分析
miscdevice 结构体
struct miscdevice { int minor; //次设备号 通常为MISC_DYNAMIC_MINOR 动态分配 const char *name; //设备为的名字 const struct file_operations *fops;//函数操作集 struct list_head list; struct device *parent; struct device *this_device; const char *nodename; umode_t mode;};
通常 miscdevice的 minor 、name 和 fops是需要实现的。
int misc_register(struct miscdevice * misc)
misc 注册函数,成功返回0 ,失败返回负数
int misc_deregister(struct miscdevice *misc)
misc 注销函数,成功返回0 ,失败返回负数
/* * Head entry for the doubly linked miscdevice list */static LIST_HEAD(misc_list);static DEFINE_MUTEX(misc_mtx);/* * Assigned numbers, used for dynamic minors */#define DYNAMIC_MINORS 64 /* like dynamic majors */static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);static int misc_open(struct inode * inode, struct file * file){ //取出次设备号 int minor = iminor(inode); struct miscdevice *c; int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; mutex_lock(&misc_mtx); //misc_list中,取出具体设备的misc_device来,得到fops list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) { mutex_unlock(&misc_mtx); request_module("char-major-%d-%d", MISC_MAJOR, minor); mutex_lock(&misc_mtx); list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) goto fail; } err = 0; //保存原有的fops old_fops = file->f_op; //赋值新的fops file->f_op = new_fops; //如果设备实现了open函数 if (file->f_op->open) { /* * FIXME: this is a workaround for pmem * PMEM need private_data to be NULL for an unmapped file. * The private_data is used to store PMEM internal data strucutre. * Leakage may happen if we just override this field in pmem_open function */ if (strcmp(c->name, "pmem_multimedia") != 0 && strcmp(c->name, "vmem_multimedia") != 0) file->private_data = c; //调用open函数 err=file->f_op->open(inode,file); if (err) { //调用失败,恢复fops为保存old_fops fops_put(file->f_op); file->f_op = fops_get(old_fops); } } fops_put(old_fops);fail: mutex_unlock(&misc_mtx); return err;}static struct class *misc_class;static const struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, .llseek = noop_llseek,};/** * misc_register - register a miscellaneous device * @misc: device structure * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. */int misc_register(struct miscdevice * misc){ dev_t dev; int err = 0; INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); //动态分配次设备号 if (misc->minor == MISC_DYNAMIC_MINOR) { //从位图中,从 misc_minors 开始,查找最小的 zero位,做多查找64次 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { mutex_unlock(&misc_mtx); //没有次设备号,可以分配 return -EBUSY; } //赋值次设备号 misc->minor = DYNAMIC_MINORS - i - 1; //设置位图 set_bit(i, misc_minors); } else { //指定次设备号 struct miscdevice *c; //从 misc_list中,查看是否制定的这个 minor是否已经注册 list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { mutex_unlock(&misc_mtx); return -EBUSY; } } } //使用MKDEV创建设备号 dev = MKDEV(MISC_MAJOR, misc->minor); //创建设备 misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); //判断设备是否创建成功,不成功,则销毁 次设备号 if (IS_ERR(misc->this_device)) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); err = PTR_ERR(misc->this_device); goto out; } /* * Add it to the front, so that later devices can "override" * earlier defaults */ //将设备添加到misc_list的头部 list_add(&misc->list, &misc_list); out: mutex_unlock(&misc_mtx); return err;}/** * misc_deregister - unregister a miscellaneous device * @misc: device to unregister * * Unregister a miscellaneous device that was previously * successfully registered with misc_register(). Success * is indicated by a zero return, a negative errno code * indicates an error. */int misc_deregister(struct miscdevice *misc){ int i = DYNAMIC_MINORS - misc->minor - 1; if (WARN_ON(list_empty(&misc->list))) return -EINVAL; mutex_lock(&misc_mtx); //删除链表 misc->list,相当于同时在misc_list 中删除了这个 misc list_del(&misc->list); //销毁设备 device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); //清理次设备号 if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); return 0;}EXPORT_SYMBOL(misc_register);EXPORT_SYMBOL(misc_deregister);static char *misc_devnode(struct device *dev, umode_t *mode){ struct miscdevice *c = dev_get_drvdata(dev); if (mode && c->mode) *mode = c->mode; if (c->nodename) return kstrdup(c->nodename, GFP_KERNEL); return NULL;}static int __init misc_init(void){ int err;//创建proc 文件系统#ifdef CONFIG_PROC_FS proc_create("misc", 0, NULL, &misc_proc_fops);#endif misc_class = class_create(THIS_MODULE, "misc"); err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) goto fail_remove; err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) goto fail_printk; misc_class->devnode = misc_devnode; return 0;fail_printk: printk("unable to get major %d for misc devices\n", MISC_MAJOR); class_destroy(misc_class);fail_remove: remove_proc_entry("misc", NULL); return err;}subsys_initcall(misc_init);
1.利用链表,进行存储具体的设备
2.使用open时的中转功能
3.通用层: miscdevice,提供结构体、创建类,提供创建设备的核心函数
具体设备层: 填充结构题,实现具体功能; 利用提供的核心函数实现注册设备 。
参考文献
linux驱动开发之字符设备框架 -调用过程分析
- linux驱动开发 --miscdevice
- Linux设备驱动编程---miscdevice杂类设备的使用方法
- miscdevice混杂设备驱动
- miscdevice混杂设备驱动
- miscdevice混杂设备驱动
- linux miscdevice 驱动程序
- linux 混杂设备miscdevice
- Linux miscdevice 混杂设备
- 【Linux内核驱动】基于platform总线的miscdevice驱动(LED)
- miscdevice混杂设备驱动 1
- miscdevice混杂设备驱动 2
- Linux中miscdevice的分析
- android miscdevice(混杂设备)驱动编写注意
- 一步一步写miscdevice的驱动模块
- 一步一步写miscdevice的驱动模块
- 内核驱动中常见的miscdevice、platform_device、platform_driver
- 内核驱动中常见的miscdevice、platform_device、platform_driver
- 内核驱动中常见的miscdevice、platform_device、platform_driver
- android网络编程之HttpUrlConnection--POST请求
- Latex:基本用法、表格、公式、算法(持续更新)
- 2017年第0届浙江工业大学之江学院程序设计竞赛决赛—K qwb与小数
- 常用工具类
- 【实训】第一周总结下
- linux驱动开发 --miscdevice
- 用nc做网络压力测试
- 对于RSA加密算法的理解
- 动作识别文献阅读笔记--光流法系列
- Linux 进程控制
- Android中快速生成dimens文件
- 索引与视图
- 名企笔试:腾讯2016招聘笔试(微信红包)
- JSTL标准标签库之SQL标签