Develop an android driver that can count words (2)
8, Specify callback function
The content in this section is critical. Regardless of the Linux driverProgramHow complex or "cool" the features are, must allow applications in the user space to interact with the driver in the kernel space to make sense. The most common interaction method is to read and write device files. You can use the file_operations.read and file_operations.write member variables to specify the callback function pointers to be called for reading and writing device files.
In this section, we will add two functions for word_count.c: word_count_read and word_count_write. These two functions are used to read data from the device file and write data to the device file respectively. In this example, the word_count function is used to calculate the number of words. First, the word_count_read and word_count_write functions are used to read and write device file data, this helps you understand how to interact with device files. The word_count.c file written in this section is a branch. You can find the word_count.c file in the word_count/read_write directory. You can use this file to overwrite the same name file in the word_count directory to test the example in this section.
The function of this example is to read the data from the/dev/wordcount device file (only once) after writing data to the/dev/wordcount device file ). Next, let's take a look at the completeCode.
# Include <Linux/module. h> # include <Linux/init. h> # include <Linux/kernel. h> # include <Linux/Fs. h> # include <Linux/miscdevice. h> # include <ASM/uaccess. h> # define device_name "wordcount" // define the device file name static unsigned char mem [10000]; // Save the data written to the device file static char read_flag = 'y '; // y: the data has been read from the device file. N: The data has not been read from the device file. Static int written_count = 0; // number of bytes of data written to the device file // This function is called when reading data from the device file // file: pointing to the device file, Buf: Saving readable data count: number of readable bytes PPOs: the offset of the read data static ssize_t word_count_read (struct file * file, char _ User * Buf, size_t count, loff_t * PPOs) {// if the data in the device file has not been read, you can read if (read_flag = 'n') {// copy the data in the kernel space to the user space, data in the Buf is the data copy_to_user (BUF, (void *) MEm, written_count) read from the device file; // outputs the number of bytes read to the log printk ("read count: % d ", (INT) written_count); // set the read status of data read_flag = 'y'; return written_count;} // The data has been read from the device file, else {return 0 ;}/// this function is called when data is written to the device file. // file: pointing to the device file, Buf: Saving the written data count: number of bytes of data written PPOs: Offset of data written static ssize_t word_count_write (struct file * file, const char _ User * Buf, size_t count, loff_t * PPOs) {// copy the user space data to the kernel space. The data in MEM is the data copy_from_user (MEm, Buf, count) written to the device file ); // set the unread data status read_flag = 'n'; // number of bytes for saving written data written_count = count; // output the number of bytes written to the log printk ("written count: % d", (INT) count); Return count ;} // describes the callback function pointer corresponding to the event triggered by the device file. // you need to set the Read and Write member variables, the system can call the function static struct file_operations dev_fops = {. owner = this_module ,. read = word_count_read ,. write = word_count_write}; // description of the device file information static struct miscdevice MISC = {. minor = misc_dynamic_minor ,. name = device_name ,. foPs = & dev_fops}; // initialize the Linux driver static int word_count_init (void) {int ret; // create the device file ret = misc_register (& MISC ); // output the log information printk ("word_count_init_success \ n"); return ret;} // uninstall the Linux driver static void word_count_exit (void) {// Delete the device file misc_deregister (& MISC); // output the log information printk ("word_init_exit_success \ n");} // register the module_init (word_count_init) function for Linux driver initialization ); // register module_exit (word_count_exit), module_author ("lining"), and module_description ("Statistics of word count. "); module_alias (" Word Count module. "); module_license (" GPL ");
To write the above Code, you need to understand the following points.
1. The parameters of the word_count_read and word_count_write functions are basically the same, and only the Buf of the 2nd parameters is slightly different. The Buf parameter type of the word_count_read function is char *, while the Buf parameter type of the word_count_write function is const char *, which means that the Buf parameter value in the word_count_write function cannot be modified. The Buf parameter in the word_count_read function indicates the data read from the device file. That is to say, the data in the Buf can be read from the device file. The amount of data that can be read depends on the return value of the word_count_read function. If the word_count_read function returns N, you can read n characters from the Buf. Of course, if n is 0, no characters can be read. If n is less than 0, an error occurs (n is the error code ). The Buf in the word_count_write function indicates the data written by the user space application. There is a "_ User" macro before the Buf parameter, indicating that the Buf's memory zone is located in the user space.
2. because programs in the kernel space cannot directly access data in the user space, in the word_count_read and word_count_write functions, use the copy_to_user and copy_from_user functions to copy data from the kernel space to the user space or from the user space to the kernel space.
3. In this example, data can only be read from the device file once. That is to say, once you write data, once you read the data, you cannot read any data from the device file for the second time. Unless data is written again. This function is controlled by the read_flag variable. When the value of read_flag is N, it indicates that the device file has not been read, and data will be normally read in the word_count_read function. If the value of read_flag is Y, the data in the device file has been read. The word_count_read function returns 0 directly. The application cannot read any data.
4. In fact, the Count parameter of the word_count_read function represents the number of bytes read from the device file. However, when you use the cat command to test the word_count driver. Read 32768 bytes directly. Therefore, the Count parameter is useless (the value is always 32768 ). Therefore, you need to save the number of bytes written in the word_count_write function, and directly use the number of bytes written in the word_count_read function. That is to say, the number of bytes written will be read.
5. All written data is saved in the mem array. The array is defined as 10000 characters, so the number of data bytes written cannot exceed 10000, otherwise it will overflow.
To help readers test the examples in this section, I have written several shell script files that allow the word_count driver to be tested on the ubuntulinux, The cloud6410 Development Board, and the android simulator. There is a script file build. Sh that is responsible for scheduling. All the examples in this book will have a build. Sh script file. to execute this script file, you need to ask the user to selectSource codeCompile to that platform and select 6-11 from the menu. You can enter 1, 2, or 3 to select the compilation platform. If you press the Enter key directly, the default value is 1st compilation platforms (ubuntulinux ).
The code for the build. Sh script file is as follows:
Source/root/Drivers/Common. sh # select_target is a function. The syntax shows the selection menu shown in Figure 6-11 and receives user input. # modify the function in common. the sh file defines select_target if [$ selected_target = 1]; then source. /build_ubuntu.sh # compile the script file Elif [$ selected_target = 2]; then source. /build_s36410.sh # Run the script file Elif [$ selected_target = 3] and compile it into the script file of the platform driver of the cloud6410; then source. /build_emulator.sh # Run the script file fi compiled into the android simulator platform driver.
The build. Sh script file contains three script files (build_ubuntu.sh, build_s36410.sh, and build_emulator.sh). The Code of these three script files is similar, but the Linux kernel version selected is different. After compilation, the compiled Linux driver file (*. so file) upload to the/data/local directory of the corresponding platform, and install the Linux driver. For example, the Code of the build_initi6410.sh script file is as follows:
Source/root/Drivers/Common. sh # The initi6410_kernel_path variable is the path of the Linux kernel source code applicable to the cloud6410 platform. # This variable and other similar variables are stored in common. the sh script file defines make-C $ initi6410_kernel_path M =$ {PWD} find_devices # If you choose everything, directly exit if ["$ selected_device" = "]; then exit else # upload Driver (word_count.ko) ADB-S $ selected_device push $ {PWD}/word_count.ko/data/local # determine whether the word_count driver has testing =$ (ADB-S $ selected_device shell lsmod | grep "word_count ") If ["$ testing "! = ""]; Then # Delete the existing word_count driver ADB-S $ selected_device shell rmmod word_count fi # Install the word_count driver ADB-S $ selected_device shell "insmod/data/local/word_count.ko" on the cloud6410 Development Board" fi
To use the above script file, you need to create a MAKEFILE file in the read_write directory. The content is as follows:
OBJ-M: = word_count.o
Now run the build. Sh script file, select the platform to be compiled, and run the following command to write data to the/dev/word_count device file.
# Echo 'Hello lining'>/dev/wordcount
Run the following command to read data from the/dev/word_count device file.
# Cat/dev/wordcount
If "Hello lining" is output, the test is successful.
Note:If you want to test the word_count driver on the Development Board and Android simulator, run the shell. Sh script file or the ADB shell command to access the terminal of the corresponding platform. The shell. Sh script is in the/root/DRIVERS directory. The difference between the two methods is that if multiple Android devices are connected to a PC, Shell. the sh script displays a menu similar to the one shown in Figure 6-11. You can choose the Android device to enter, the ADB shell command must add the-s command line parameter to specify the Android device ID to enter the terminal of the corresponding Android device.
IX,Measure the number of words.Algorithm
This section describes the word_count-driven business logic: count the number of words. The algorithms implemented in this section calculate a word by a string separated by spaces, tabs (ASCII: 9), carriage returns (ASCII: 13), and line breaks (ASCII: 10, this algorithm takes into account multiple separators (space characters, tabs, carriage returns, and line breaks. The following is the complete word_count drive code. The Code contains the get_word_count function for counting the number of words.
# Include <Linux/module. h> # include <Linux/init. h> # include <Linux/kernel. h> # include <Linux/Fs. h> # include <Linux/miscdevice. h> # include <ASM/uaccess. h> # define device_name "wordcount" // define the device file name static unsigned char mem [10000]; // Save the data written to the device file static int word_count = 0; // Number of words # define true-1 # define false 0 // determines whether the specified character is a space (including space characters, tabs, carriage returns, and linefeeds) Static char is_spacewhite (char C) {If (C = ''| C = 9 | C = 13 | C = 10) return true; else return false;} // count the number of words. Static int get_word_count (const char * BUF) {int n = 1; int I = 0; char c = ''; char flag = 0; // process the case where multiple spaces are separated, 0: Normal, 1: if (* Buf = '\ 0') return 0; // the first character is a space. If (is_spacewhite (* BUF) is counted from 0) = true) n --; // scan every character in the string for (; (C = * (BUF + I ))! = '\ 0'; I ++) {// conditions where words are separated by only one space if (flag = 1 & is_spacewhite (c) = false) {flag = 0;} // when words are separated by multiple spaces, ignore extra spaces else if (flag = 1 & is_spacewhite (c) = true) {continue;} // when the current character is space, the number of words plus 1 If (is_spacewhite (c) = true) {n ++; flag = 1 ;}} // If the string ends with one or more spaces, It is not counted (the number of words minus 1) if (is_spacewhite (* (BUF + I-1) = true) n --; return N;} // The function static ssize_t word_count_read called when reading data from the device file (struct file * file, char _ User * Buf, size_t count, loff_t * PPOs) {unsigned char temp [4]; // splits the number of words (INT type) into four bytes and stores them in Buf temp [0] = word_count> 24; temp [1] = word_count> 16; temp [2] = word_count> 8; temp [3] = word_count; copy_to_user (BUF, (void *) temp, 4 ); printk ("read: Word Count: % d", (INT) count); Return count ;} // The function static ssize_t word_count_write (struct file * file, const char _ User * Buf, size_t count, loff_t * PPOs) called when writing data to the device file) {ssize_t written = count; copy_from_user (MEm, Buf, count); mem [count] = '\ 0'; // count the number of words word_count = get_word_count (MEM ); printk ("Write: Word Count: % d", (INT) word_count); Return written ;} // description the callback function pointer corresponding to the event triggered by the device file: static struct file_operations dev_fops = {. owner = this_module ,. read = word_count_read ,. write = word_count_write}; // description of the device file information static struct miscdevice MISC = {. minor = misc_dynamic_minor ,. name = device_name ,. foPs = & dev_fops}; // initialize the Linux driver static int word_count_init (void) {int ret; // create the device file ret = misc_register (& MISC ); // output the log information printk ("word_count_init_success \ n"); return ret;} // uninstall the Linux driver static void word_count_exit (void) {// Delete the device file misc_deregister (& MISC); // output the log information printk ("word_init_exit_success \ n");} // register the module_init (word_count_init) function for Linux driver initialization ); // register module_exit (word_count_exit), module_author ("lining"), and module_description ("Statistics of word count. "); module_alias (" Word Count module. "); module_license (" GPL ");
To compile the word_count driver, you need to understand the following points.
1. the get_word_count function uses the 1st "\ 0" characters in the mem array as the end character of the string. Therefore, in the word_count_write function, set the value of MEM [count] To "\ 0 ", otherwise, the get_word_count function cannot know where the string to count the number of words ends. The maximum length of the string to be counted is 10000 because the mem array length is 9999 and the last character of the string is "\ 0.
2. The number of words is stored using int type variables. The word_count_write function calculates the number of words (value of the word_count variable). In the word_count_read function, the word_count integer variable value is decomposed into four bytes and stored in the Buf. Therefore, the four bytes need to be combined into int type values in the application.
10,Compile, install, and uninstallLinuxDriver
In the previous section, all the word_count drivers have been compiled and tested multiple times. The word_count driver has been installed and uninstalled many times. The word_count driver is the same as the driver in the read_write directory. There is also a build. SH and three platform-related script files. These script files are similar to those in Section 6.3.5. Run the build. Sh script file and select the platform to compile. Run the following two lines to view the log output information and the word_count Driver Module (word_count.ko.
# Dmesg | tail-N 1
# Modinfo word_count.ko
If the information shown in 6-12 is displayed, the word_count driver works properly.
All the script files in this book use the insmod command to install the Linux driver. In addition to this command, you can also use the modprobe command to install the Linux driver. The difference between insmod and modprobe Is That The modprobe command can check the dependency of the driver module. For example, module A depends on Module B (B must be loaded before module A is loaded ). If you use the insmod command to load module A, an error occurs. The modprobe command is used to load module A, and Module B will load it now. Before using The modprobe command to load the driver module, use the depmod command to check the dependency between the Linux driver module.
# Depmod/root/Drivers/ch06/word_count/word_count.ko
The depmod Command actually adds the Linux driver module File (including its path) to the following file.
/Lib/modules/3.0.0-16-generic/modules. Dep
After the depmod command detects the dependency, you can call the modprobe command to load the Linux driver.
# Modprobe word_count
Note the following when using the depmod and modprobe commands:
1. The depmod command must use the absolute path of the Linux Driver Module (. Ko file.
2. The depmod command writes the dependency information of the kernel module to the module. Dep file of the currently used kernel. For example, I use linux3.0.0.16 in Ubuntu Linux, so I should go to the 3.0.0-16-Generic Directory to find the modules. Dep file. If you use other linux kernels, you need to find the modules. Dep file in the corresponding directory.
3. The modprobe command only needs to use the driver name and does not need to be followed by. Ko.
Test the Linux driver on the android simulator and ubuntu.
This article is excerptedAndroid Deep Exploration (Volume 1): Hal and driver development,
Next several articlesArticleWe will explain in detail how to develop the Linux driver of the ARM architecture, and test the Linux driver by using the android program, ndk, and executable files. In ubuntu
Linux, Android simulator and cloud6410 Development Board (you can buy OK6410-A Development Board, need to brush Android)