本文共 9853 字,大约阅读时间需要 32 分钟。
八、 指定回调函数
本节讲的内容十分关键。不管Linux驱动程序的功能多么复杂还是多么“酷”,都必须允许用户空间的应用程序与内核空间的驱动程序进行交互才有意义。而最 常用的交互方式就是读写设备文件。通过file_operations.read和file_operations.write成员变量可以分别指定读写 设备文件要调用的回调函数指针。
在本节将为word_count.c添加两个函数:word_count_read和word_count_write。这两个函数分别处理从设备文件读 数据和向设备文件写数据的动作。本节的例子先不考虑word_count要实现的统计单词数的功能,先用word_count_read和 word_count_write函数做一个读写设备文件数据的实验,以便让读者了解如何与设备文件交互数据。本节编写的word_count.c文件是 一个分支,读者可在word_count/read_write目录找到word_count.c文件。可以用该文件覆盖word_count目录下的同 名文件测试本节的例子。
本例的功能是向设备文件/dev/wordcount写入数据后,都可以从/dev/wordcount设备文件中读出这些数据(只能读取一次)。下面先看看本例的完整的代码。
编写上面代码需要了解如下几点。
1. word_count_read和word_count_write函数的参数基本相同,只有第2个参数buf稍微一点差异。 word_count_read函数的buf参数类型是char*,而word_count_write函数的buf参数类型是const char*,这就意味着word_count_write函数中的buf参数值无法修改。word_count_read函数中的buf参数表示从设备文 件读出的数据,也就是说,buf中的数据都可能由设备文件读出,至于可以读出多少数据,取决于word_count_read函数的返回值。如果 word_count_read函数返回n,则可以从buf读出n个字符。当然,如果n为0,表示无法读出任何的字符。如果n小于0,表示发生了某种错误 (n为错误代码)。word_count_write函数中的buf表示由用户空间的应用程序写入的数据。buf参数前有一个“__user”宏,表示 buf的内存区域位于用户空间。
2. 由于内核空间的程序不能直接访问用户空间中的数据,因此,需要在word_count_read和word_count_write函数中分别使用 copy_to_user和copy_from_user函数将数据从内核空间复制到用户空间或从用户空间复制到内核空间。
3. 本例只能从设备文件读一次数据。也就是说,写一次数据,读一次数据后,第二次无法再从设备文件读出任何数据。除非再次写入数据。这个功能是通过 read_flag变量控制的。当read_flag变量值为n,表示还没有读过设备文件,在word_count_read函数中会正常读取数据。如果 read_flag变量值为y,表示已经读过设备文件中的数据,word_count_read函数会直接返回0。应用程序将无法读取任何数据。
4. 实际上word_count_read函数的count参数表示的就是从设备文件读取的字节数。但因为使用cat命令测试word_count驱动时。直 接读取了32768个字节。因此count参数就没什么用了(值总是32768)。所以要在word_count_write函数中将写入的字节数保存, 在word_count_read函数中直接使用写入的字节数。也就是说,写入多少个字节,就读出多少个字节。
5. 所有写入的数据都保存在mem数组中。该数组定义为10000个字符,因此写入的数据字节数不能超过10000,否则将会溢出。
为了方便读者测试本节的例子,笔者编写了几个Shell脚本文件,允许在UbuntuLinux、S3C6410开发板和Android模拟器上测试 word_count驱动。其中有一个负责调度的脚本文件build.sh。本书所有的例子都会有一个build.sh脚本文件,执行这个脚本文件就会要 求用户选择将源代码编译到那个平台,选择菜单如图6-11所示。用户可以输入1、2或3选择编译平台。如果直接按回车键,默认值会选择第1个编译平台 (UbuntuLinux)。
build.sh脚本文件的代码如下:
在build.sh脚本文件中涉及到了3个脚本文件(build_ubuntu.sh、build_s3c6410.sh和 build_emulator.sh),这3个脚本文件的代码类似,只是选择的Linux内核版本不同。对于S3C6410和Android模拟器平台, 编译完后Linux驱动,会自动将编译好的Linux驱动文件(*.so文件)上传到相应平台的/data/local目录,并安装Linux驱动。例 如,build_s3c6410.sh脚本文件的代码如下:
使用上面的脚本文件,需要在read_write目录建立一个Makefile文件,内容如下:
obj-m := word_count.o
现在执行build.sh脚本文件,选择要编译的平台,并执行下面的命令向/dev/word_count设备文件写入数据。
# echo ‘hello lining’ > /dev/wordcount
然后执行如下的命令从/dev/word_count设备文件读取数据。
# cat /dev/wordcount
如果输出“hello lining”,说明测试成功。
注意:如 果在S3C6410开发板和Android模拟器上测试word_count驱动,需要执行shell.sh脚本文件或adb shell命令进入相应平台的终端。其中shell.sh脚本在/root/drivers目录中。这两种方式的区别是如果有多个Android设备和 PC相连时,shell.sh脚本会出现一个类似图6-11所示的选择菜单,用户可以选择进入哪个Android设备的终端,而adb shell命令必须要加-s命令行参数指定Android设备的ID才可以进入相应Android设备的终端。
九、实现统计单词数的算法
本节开始编写word_count驱动的业务逻辑:统计单词数。本节实现的算法将由空格、制表符(ASCII:9)、回车符(ASCII:13)和换行符 (ASCII:10)分隔的字符串算做一个单词,该算法同时考虑了有多个分隔符(空格符、制表符、回车符和换行符)的情况。下面是word_count驱 动完整的代码。在代码中包含了统计单词数的函数get_word_count。
编写word_count驱动程序需要了解如下几点。
1. get_word_count函数将mem数组中第1个为“\0”的字符作为字符串的结尾符,因此在word_count_write函数中将 mem[count]的值设为“\0”,否则get_word_count函数无法知道要统计单词数的字符串到哪里结束。由于mem数组的长度为 10000,而字符串最后一个字符为“\0”,因此待统计的字符串最大长度为9999。
2. 单词数使用int类型变量存储。在word_count_write函数中统计出了单词数(word_count变量的值),在 word_count_read函数中将word_count整型变量值分解成4个字节存储在buf中。因此,在应用程序中需要再将这4个字节组合成 int类型的值。
十、编译、安装、卸载Linux驱动程序
在上一节word_count驱动程序已经全部编写完成了,而且多次编译测试该驱动程序。安装和卸载word_count驱动也做过多次。 word_count驱动与read_write目录中的驱动一样,也有一个build.sh和3个与平台相关的脚本文件。这些脚本文件与6.3.5节的 实现类似,这里不再详细介绍。现在执行build.sh脚本文件,并选择要编译的平台。然后执行下面两行命令查看日志输出信息和word_count驱动 模块(word_count.ko)的信息。
# dmesg |tail -n 1
# modinfo word_count.ko
如果显示如图6-12所示的信息,表明word_count驱动工作完全正常。
本书的脚本文件都是使用insmod命令安装Linux驱动的,除了该命令外,使用modprobe命令也可以安装Linux驱动。insmod和 modprobe的区别是modprobe命令可以检查驱动模块的依赖性。如A模块依赖于B模块(装载A之前必须先装载B)。如果使用insmod命令装 载A模块,会出现错误。而使用modprobe命令装载A模块,B模块会现在装载。在使用modprobe命令装载驱动模块之前,需要先使用depmod 命令检测Linux驱动模块的依赖关系。
# depmod /root/drivers/ch06/word_count/word_count.ko
depmod命令实际上将Linux驱动模块文件(包括其路径)添加到如下的文件中。
/lib/modules/3.0.0-16-generic/modules.dep
使用depmod命令检测完依赖关系后,就可以调用modprobe命令装载Linux驱动。
# modprobe word_count
使用depmod和modprobe命令需要注意如下几点:
1. depmod命令必须使用Linux驱动模块(.ko文件)的绝对路径。
2. depmod命令会将内核模块的依赖信息写入当前正在使用的内核的modules.dep文件。例如,笔者的Ubuntu Linux使用的是Linux3.0.0.16,所以应到3.0.0-16-generic目录去寻找modules.dep文件。如果读者使用了其他 Linux内核,需要到相应的目录去寻找modules.dep文件。
3. modprobe命令只需使用驱动名称即可,不需要跟.ko。
本文节选至, 接下来几篇文章将详细阐述如何开发ARM架构的Linux驱动,并分别利用android程序、NDK、可执行文件测试Linux驱动。可在ubuntu Linux、Android模拟器和S3C6410开发板(可以选购OK6410-A开发板,需要刷Android)
转载地址:http://xcego.baihongyu.com/