一文讲解字符设备驱动基础——读写接口的操作实践
一、细节提要
1、与用户与内核数据交换有关的函数 (1)copy_from_user()函数
该将数据从用户空间复制到内核空间。
如果成功复制则返回0,如果不成功复制则返回尚未成功复制的剩下的字节数。
(2)copy_to_user()函数
将数据从内核空间复制到用户空间。
(3)复制机制与使用mmap的对比
复制时,内核空间和用户空间的地址不一样,效率低。
2、代码逻辑图

二、代码示例
1、代码编写
在ubuntu的/home/xjh/iot/embedded_basic/rootfs/tmp中编写代码:app.c与module_test.c。
2、代码编译
(1)编译驱动源代码
利用同目录下的Makefile文件编译module_test.c,得到module_test.ko驱动文件。
(2)编译应用层程序
怎样编译app.c?使用ubuntu的gcc还是交叉编译工具链?或者和驱动源代码一样的操作(这个如何完成编译的)?
因为此应用层程序要在开发板运行,因此需要使用交叉编译工具链中的gcc来编译,而不是 ubuntu 中的gcc。
在已经正确安装了交叉编译工具链的ubuntu系统中,使用如下命令编译app.c得到app.exe。
因为之前实验中已经将ubuntu的/home/xjh/iot/embedded_basic/rootfs/tmp挂载到开发板/mnt目录,所以开发板完全启动后,可以在/mnt目录中看到刚才编译的文件。
【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)


3、代码测试
可以直接在开发板的/mnt目录下进行测试。
(1)装载测试
装载后查看/proc/devices,是否有驱动源码中所写的驱动名字、自动分配的主设备号。
(2)创建设备文件
(3)操作设备文件
运行应用层程序app.exe,观察运行效果。
说明
1)Linux系统字符编码默认是UTF-8格式的,如果SecureCRT没有设置成UTF-8格式,中文显示会出现乱码。设置SCRT格式的方法见博客:SecureCRT显示乱码的解决办法
2)为何应用层判断是否open成功的代码很迟才输出?(待解决)
(4)卸载模块测试
4、总结说明
(1)应用层的代码编译,要使用交叉编译工具链。
(2)驱动源代码的编译,要使用与开发板系统内核版本一致的内核源码进行编译。其实应该还是借用ubuntu的 gcc工具来进行编译的,因为内核源码中没有gcc编译器。Makefile 文件指明要进入这个与开发板系统内核版本一致的内核源码中,然后make modules。这说明是根据内核源码中的Makefile文件的指示,执行Makefile文件中的一个目标modules。
(3)mymajor = register_chrdev(0, MYNAME, &test_fops);
参数 0 表示让系统自动分配主设备号。
参数MYNAME表示设备(或者说驱动)的名字。
参数&test_fops是指向struct file_operations变量的指针,这个变量代表着驱动的实质内容。
返回值mymajor代表系统为设备分配的主设备号。
/proc/devices文件记录着系统中已经注册的块设备和字符设备。可以通过cat或者vim(但只能读不能修改)来查看此文件内容。注册成功后,可以通过/proc/devices文件查看到该设备的名字MYNAME及其设备号mymajor。
此函数成功后,主设备号为mymajor的设备与它的驱动内容&test_fops就关联起来了,也就是说一个设备对应了一个驱动内容。那在应用层如何表示这个设备呢?见(4)。
(4)/dev/test是设备文件,是命令行中使用mknod命令创建的。安装好驱动模块后,得到系统分配的主设备号mymajor(这里是250)。然后利用这个主设备号来创建设备文件,即执行“mknod /dev/test c 250 126”。这样一来,主次设备号为250、126的设备就和设备文件/dev/test关联了,应用层通过API操作/dev/test文件,也就是操作主次设备号为250、126的设备,而应用层操作里的API就对应着这个设备对应的驱动&test_fops。
设备文件不能使用vim打开,可以使用ls -l 命令查看。
综合(3)(4),主次设备号、设备名字、驱动内容、设备文件,这几个概念要清楚。
主次设备号:主设备号是register_chrdev()的返回值mymajor,次设备号在mknod时设定。
设备名字:MYNAME (主设备号、设备名字在/proc/devices文件中)
驱动内容:&test_fops (程序猿在驱动源代码中编写)
设备文件:/dev/test,利用mknod命令手动创建
(5)设备文件是手动创建的,能不能让它自动创建呢?字符设备驱动高级篇4——自动创建设备文件的函数代码分析_天糊土的博客-CSDN博客
(6)应用层的open、read等API,与驱动源码的test_chrdev_open、test_chrdev_read等具体操作函数,通过struct file_operations的填充而关联起来。不过这里的驱动源码中的test_chrdev_open、test_chrdev_read等具体操作函数,并没有做什么事情,按理应该操作一些硬件的。这里只是为了演示应用层读写操作与驱动层的读写如何关联起来的,因而没有硬件操作细节(比如硬件寄存器操作这些行为)。
关于具体的硬件操作见:字符设备驱动基础5——驱动如何操控硬件(动静态映射操作LED)
1、应用层代码:app.c
2、驱动文件:module_test.c
