Let's test our really simple skeleton misc character driver (in the ch1/miscdrv directory; we assume you have built and inserted it as shown in Figure 1.4). We test it by issuing open(2), read(2), write(2), and close(2) system calls upon it; how exactly can we do so? We can always write a small C program to do precisely this, but an easier way is to use the useful dd(1) "disk duplicator" utility. We use it like this:
dd if=/dev/llkd_miscdrv of=readtest bs=4k count=1
Internally dd opens the file we pass it as a parameter (/dev/llkd_miscdrv) via if= (here, it's the first parameter to dd; if= specifies the input file), it will read from it (via the read(2) system call, of course). The output is to be written to the file specified by the parameter of= (the second parameter to dd, and is a regular file named readtest); the bs specifies the block size to perform I/O in and count is the number of times to perform I/O). After performing the required I/O, the dd process will close(2) the files. This sequence is reflected in the kernel log (Figure 1.5):
After verifying that our driver (LKM) is inserted, we issue the dd(1) command, having it read 4,096 bytes from our device (as the block size (bs) is set to 4k and count to 1). We have it write the output (via the of= option switch) to a file named readtest. Looking up the kernel log, you can see (Figure 1.5) that the dd process has indeed opened our device (our PRINT_CTX() macro's output shows that it's the process context currently running the code of our driver!). Next, we can see (via the output from pr_fmt()) that control goes to our driver's read method, within which we emit a simple printk and return the value 4096 signifying success (though we really didn't read anything!). The device is then closed by dd. Furthermore, a quick check with the hexdump(1) utility reveals that we did indeed receive 0x1000 (4,096) nulls (as expected) from the driver (in the file readtest; do realize that this is the case because dd initialized it's read buffer to NULLs).
Figure 1.6 shows how we (minimally) test writing to our driver, again via dd(1). This time we read 4k of random data (by leveraging the kernel's built-in mem driver's /dev/urandom facility), and write the random data to our device node; in effect, to our 'device':
(By the way, I have also included a simple user space test app for the driver; it can be found here: ch1/miscdrv/rdwr_test.c. I will leave it to you to read its code and try out.)
You might be thinking: we did apparently succeed in reading and writing data to and from user space to our driver, but, hang on, we never actually saw any data transfer taking place within the driver code. Yes, this is the topic of the next section: how you will actually copy the data from the user space process buffer into your kernel driver's buffer, and vice versa. Read on!