简单的linux杂项设备驱动的写法
来源:互联网 发布:中国巡航导弹 知乎 编辑:程序博客网 时间:2024/06/05 04:42
linux将设备驱动分成几大类:字符设备、杂项设备、块设备、网络设备······
本篇文章介绍杂项设备驱动的编写,杂项设备与字符设备本质上没什么区别,但是写法和相关函数的使用上有区别。
除此之外杂项设备主设备号都为10,设备间通过次设备号来进行区分,与字符设备相比节约了主设备号。
杂项设备驱动编写模式一般如下:
在linux系统下一切皆文件,设备驱动同样秉承此“”大法“”。
对文件操作就少不了打开、读写、关闭等操作。
所以杂项设备驱动第一步就是进行文件操作函数的编写。
驱动模块最终需要加载到内核上运行,内核对文件的操作在内部有标准的系统调用,
那么怎样将自己写的文件操作函数挂接到系统标准的函数调用上呢?
这就是文件操作集合这个结构体所要做的工作了。
首先看一下该结构体的结构
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);};其实我们仔细观察,该结构体里除了第一个成员外,其余的成员都是函数指针,当我们在进一步观察。原来他们当中很多都很熟悉。
有许多跟原来经常调用的文件操作函数很类似,其实他们作用就是将自己书写的文件操作函数与系统标准文件操作函数调用衔接起来。
也可以把他们说成是文件操作的函数的声明,只有在这个结构体里声明过的函数,在对驱动操作时才能使用该函数。
什么意思呢?
如果我们没有在这个结构体中声明read,也就是读取文件的函数,我们在调用该驱动模块时就不能对该模块进行读操作。
该结构体成员很多,我们不必全部都要声明,我们在实际使用时,用到那个写哪个就可以了。
例如我们只写一个简单的LED灯的驱动,只涉及简单的IO控制,因此其余的函数对我们没有意义,我们就可以不写。
对于第一个成员owner,它的定义有很多,在早期简单的驱动编写中,我们统一将它赋值“”THIS_MODULE“”。
可以防止驱动在运行中被意外卸载。至于其他用法自己可以在以后深入学习中慢慢探索。
写完文件操作集合,还有另外一个重要结构体miscdevice,它记录了该驱动设备的相关信息,为设备注册提供必要信息。
struct miscdevice {int 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;};对于我们比较关注的就是前三项
minor 次设备号,若果让系统自动分配设备号 应赋值为MISC_DUNAMIC_MINOR
name 设备名称
fops 操作集合的声明 将文件操作集合结构体地址赋值给它即可
将以上内容完成后,就是驱动的初始化和卸载函数的书写。
模块初始化函数中主要完成的内容,主要是进行杂项设备的注册。
模块卸载函数,主要是对杂项设备的卸载。
最后声明模块的初始化、卸载函数的入口。声明遵循的开源协议。
最后可编写一个控制函数验证驱动的正确性
下面附上一段示例代码,此段并没有实质性操作,只简单演示杂项设备驱动编写流程
#include<linux/kernel.h>#include<linux/module.h>#include<linux/miscdevice.h>#include<linux/fs.h>#include<linux/export.h>//打开函数static int misc_open(struct inode *node, struct file *fp){ printk("this dev is open\r\n");return 0;}//关闭函数static int misc_close(struct inode *node, struct file *fp){ printk("this dev is close\r\n");return 0;}//读函数ssize_t misc_read(struct file *fp, char __user *buf, size_t size, loff_t *loff){ printk("this dev is read\r\n"); return 0;}//写函数ssize_t misc_write(struct file *fp, const char __user *buf, size_t size, loff_t *loff){ printk("this dev is write\r\n"); return 0;}
//文件操作集合struct file_operations fops={ .owner=THIS_MODULE, .open=misc_open, .read=misc_read, .write=misc_write, .release=misc_close,}; //设备相关信息struct miscdevice mymisc={ .minor=MISC_DYNAMIC_MINOR, .name="mymisc", .fops=fops,};//驱动初始化static int __init misc_init(void){ if((misc_register(&mymisc)) { printk("this module is insmod fail\r\n");return -1; } printk("this module is success\r\n"); return 0;}//驱动卸载static void __exit misc_exit(void){ printk("this module is exit\r\n");}module_init(misc_init);module_exit(misc_exit);MODULE_LICENSE("GPL");驱动控制函数代码
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>//主函数 参数为:传入设备名称(含有路径)int main(int argc,char *argv[]) {int fd = open(argv[1],O_RDWR);if(fd == -1){printf("打开失败\n");return 0;}read(fd,buf,0);write(fd,NULL,0);close(fd);return 0;}驱动控制代码的作用主要是将,设备节点打开对其进行读写关闭等操作(这里没有实质操作,只打印一句话),只是来验证驱动框架的正确性
makefile代码如下:
KERN_DIR = /zhangchao/linux3.5/linux-3.5all:make -C $(KERN_DIR) M=`pwd` modulesclean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m += misc.o
编译模块
[root@CentOS zhangchao]# makemake -C /zhangchao/linux3.5/linux-3.5 M=`pwd` modulesmake[1]: 进入目录“/zhangchao/linux3.5/linux-3.5” Building modules, stage 2. MODPOST 1 modulesmake[1]: 离开目录“/zhangchao/linux3.5/linux-3.5”[root@CentOS zhangchao]# lsMakefile misc.c misc.mod.c misc.o Module.symversmisc_app.c misc.ko misc.mod.o modules.order[root@CentOS zhangchao]#可以看到.ko模块文件已经生成
安装模块
[root@ZC/zhangchao]#insmod misc.ko [ 857.560000] this module is success
打印相关信息模块安装成功
查看模块
[root@ZC/zhangchao]#lsmodModule Size Used by Tainted: G misc 1440 0模块信息存在,模块已经成功安装
编译驱动控制代码
[root@CentOS zhangchao]# arm-linux-gcc misc_app.c -o run[root@CentOS zhangchao]# lsmain.c man misc.c misc.mod.c misc.o Module.symversMakefile misc_app.c misc.ko misc.mod.o modules.order run
编译没有报错,生成了可执行文件 run
查看设备
[root@ZC/zhangchao]#ls -al /dev/mymisc crw-rw---- 1 root root 10, 47 Mar 16 17:00 /dev/mymisc
设备已经存在,证明杂项设备注册成功,在设备列表内部能够查看到设备。主设备号10 次设备号47
运行控制 程序,传入参数为注册的设备节点,控制程序可以通过设备节点对设备进行读写等操作。(在开发板上执行)
[root@ZC/zhangchao]#./run /dev/mymisc [ 1487.635000] this dev is open[ 1487.635000] this dev is read[ 1487.635000] this dev is write[ 1487.635000] this dev is close[root@ZC/zhangchao]#
驱动卸载
[root@ZC/zhangchao]#rmmod misc.ko [ 1577.995000] this module is exit
模块编译,控制程序的编译在宿主机上进行,开发板上只有内核,没有编译工具,模块的装载、查看、卸载在开发板上运行。
宿主机: VMware12版本下的CentOS7
开发板: 友善之臂Tiny4412开发板
交叉编译器: arm-linux-gcc 4.5.1
内核版本:linux-3.5
- 简单的linux杂项设备驱动的写法
- Linux杂项设备驱动
- 以杂项设备驱动的方法注册字符设备驱动
- linux杂项设备驱动框架
- Linux 杂项设备驱动开发
- 块设备驱动的写法
- Linux 驱动的写法
- linux简单的字符设备驱动
- linux下简单的设备驱动开发
- Linux字符设备驱动的简单总结
- 杂项设备的学习
- 杂项设备的实现
- linux内核杂项设备驱动源码分析
- linux驱动杂项设备类misc
- linux内核杂项设备驱动源码分析
- 学习笔记:Linux杂项设备驱动框架
- linux led驱动例程(杂项设备)
- Linux字符设备驱动和杂项设备驱动对比
- 统计学习方法——第二章学习笔记
- 导出pdf之--html2canvas
- shiro--jar包导入
- 字符串编码解码工具类
- java基本操作
- 简单的linux杂项设备驱动的写法
- 利用inode删除指定文件
- 待整理
- puppeteer学习(一)——puppeteer与Chrome Headless
- Axios通过mock模拟数据进行通信的几种方式
- HTTP消息头网页缓存控制以及header常用指令
- python.scrapy爬虫-xpath查询语法
- nodeJS ejs模板语法(基础)
- springboot--页面访问拦截器