i.MX6ULL|字符设备驱动流程深究

上一篇介绍了虚拟字符设备的驱动,这篇就深入学习字符驱动的流程,看看字符驱动和应用层是怎么配合使用的!
1、备份原来的驱动
2、修改原来的驱动
在打印输出时,[bsp]开头表示驱动,[app]开头表示应用,makefile不用修改;
chrdevbase.c
#include #include #include #include #include #include #define chrdevbase_major 200 /* 主设备号 */#define chrdevbase_name chrdevbase /* 设备名 */static char readbuf[100]; /* 读缓冲区 */static char writebuf[100]; /* 写缓冲区 */static char kerneldata[] = {kernel data!};/* * @description : 打开设备 * @param - inode : 传递给驱动的inode * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * @return : 0 成功;其他 失败 */static int chrdevbase_open(struct inode *inode, struct file *filp){ //printk(chrdevbase open!); return 0;}/* * @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符) * @param - buf : 返回给用户空间的数据缓冲区 * @param - cnt : 要读取的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 读取的字节数,如果为负值,表示读取失败 */static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){ int retvalue = 0; /* 向用户空间发送数据 */ memcpy(readbuf, kerneldata, sizeof(kerneldata)); retvalue = copy_to_user(buf, readbuf, cnt); if(retvalue == 0){ printk([bsp]kernel senddata ok!); }else{ printk([bsp]kernel senddata failed!); } //printk(chrdevbase read!); return 0;}/* * @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符 * @param - buf : 要写给设备写入的数据 * @param - cnt : 要写入的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 写入的字节数,如果为负值,表示写入失败 */static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){ int retvalue = 0; /* 接收用户空间传递给内核的数据并且打印出来 */ retvalue = copy_from_user(writebuf, buf, cnt); if(retvalue == 0){ printk([bsp]kernel recevdata:%s, writebuf); }else{ printk([bsp]kernel recevdata failed!); } //printk(chrdevbase write!); return 0;}/* * @description : 关闭/释放设备 * @param - filp : 要关闭的设备文件(文件描述符) * @return : 0 成功;其他 失败 */static int chrdevbase_release(struct inode *inode, struct file *filp){ //printk(chrdevbase release!); return 0;}/* * 设备操作函数结构体 */static struct file_operations chrdevbase_fops = { .owner = this_module, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release,};/* * @description : 驱动入口函数 * @param : 无 * @return : 0 成功;其他 失败 */static int __init chrdevbase_init(void){ int retvalue = 0; /* 注册字符设备驱动 */ retvalue = register_chrdev(chrdevbase_major, chrdevbase_name, &chrdevbase_fops); if(retvalue < 0){ printk([bsp]chrdevbase driver register failed); } printk([bsp]chrdevbase init!); return 0;}/* * @description : 驱动出口函数 * @param : 无 * @return : 无 */static void __exit chrdevbase_exit(void){ /* 注销字符设备驱动 */ unregister_chrdev(chrdevbase_major, chrdevbase_name); printk([bsp]chrdevbase exit!);}/* * 将上面两个函数指定为驱动的入口和出口函数 */module_init(chrdevbase_init);module_exit(chrdevbase_exit);/* * license和作者信息 */module_license(gpl);module_author(zuozhongkai);  
chrdevbaseapp.c
#include stdio.h#include unistd.h#include sys/types.h#include sys/stat.h#include fcntl.h#include stdlib.h#include string.hstatic char usrdata[] = {usr data!};/* * @description : main主程序 * @param - argc : argv数组元素个数 * @param - argv : 具体参数 * @return : 0 成功;其他 失败 */int main(int argc, char *argv[]){ int fd, retvalue; char *filename; char readbuf[100], writebuf[100]; if(argc != 3){ printf([app]error usage!); return -1; } filename = argv[1]; /* 打开驱动文件 */ fd = open(filename, o_rdwr); if(fd < 0){ printf([app]can't open file %s, filename); return -1; } if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */ retvalue = read(fd, readbuf, 50); if(retvalue < 0){ printf([app]read file %s failed!, filename); }else{ /* 读取成功,打印出读取成功的数据 */ printf([app]read data:%s,readbuf); } } if(atoi(argv[2]) == 2){ /* 向设备驱动写数据 */ memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 50); if(retvalue < 0){ printf([app]write file %s failed!, filename); } } /* 关闭设备 */ retvalue = close(fd); if(retvalue < 0){ printf([app]can't close file %s, filename); return -1; } return 0;}  
3、编译驱动和应用
4、复制需要的文件到根文件系统中
将 chrdevbase.ko 和 chrdevbaseapp 复制到 rootfs/lib/modules/4.1.15 目录中:
5、启动内核
在uboot界面输入下面指令启动系统,
tftp 80800000 zimagetftp 83000000 imx6ull-14x14-evk.dtbbootz 80800000 - 83000000  
6、加载设备驱动
需要进入驱动文件目录才能加载设备驱动;
// 加载驱动insmod chrdevbase.ko// 查看驱动lsmod// 指令查看devices 信息cat /proc/devices  
效果如图:
7、创建设备节点文件
输入如下命令创建/dev/chrdevbase 这个设备节点文件:
mknod /dev/chrdevbase c 200 0  
8、验证读写
// 读./chrdevbaseapp /dev/chrdevbase 1// 写./chrdevbaseapp /dev/chrdevbase 2// 可以使用下面这行输出文件名称,输出/dev/chrdevbaseprintf(filename:%s,argv[1]);// 可以使用下面这行输出参数,输出1 或者 2printf(dat:%d,atoi(argv[2]);  
读的流程:
写的流程:
注意事项
下面这个函数的打印输出会印象到应用层的输出,看到应用层输出异常就把这个函数的输出给屏蔽就好;


加密货币正在为替代支付系统,未来五年将有10亿人使用加密货币
荣耀V9评测:华为荣耀V9怎么样?配置、价格、外观、系统一览,你中了它的毒了吗?
物业小区高校水电抄表充值管理系统简介
很可能这就是诺基亚8将搭载骁龙835,但依然延迟
5G 不仅对消费者有利,物联网也将受益
i.MX6ULL|字符设备驱动流程深究
韩国正在接收40架F-35A战斗机
微雪电子M24LR-DISCOVERY开发板简介
具有快速瞬态响应的极低压差稳压电路
储能逆变器关键技术参数解读
基于ICR技术的FPGA配置电路硬件设计及其工作原理
不要让选择网线型号带给你焦虑
电池技术——蓄电池的类型及术语2
创基Type-C连接器定制生产厂家让你得到不仅是喜欢
灰尘去无踪,家用手持吸尘器助力健康生活
机器人再次对战人类最强大脑 这次换成人脸识别
GE推出新款金属3D打印机、材料以及应用软件
测试测量企业7月齐聚成都,共同探讨行业发展方向
python迭代调用内置函数计时比较(下)
最新版电信资费营销行为规范详细解读