Fix incoherent ioctl examples (#139)

Previously ioctl.c is a userspace program for chardev2.c and chardev.h [1].
But now, this file is an independent kernel module, and the original code
disappear.
This patch adds back the original userspace code and renames it to
userspace_ioctl.c.

[1] https://tldp.org/LDP/lkmpg/2.4/html/x856.html
This commit is contained in:
linD026 2022-02-21 00:53:29 +08:00 committed by GitHub
parent fb15882377
commit d8216ff7b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 4 deletions

View File

@ -2,8 +2,8 @@
* chardev.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because they need
* to be known both to the kernel module (in chardev.c) and the process
* calling ioctl (ioctl.c).
* to be known both to the kernel module (in chardev2.c) and the process
* calling ioctl() (in userspace_ioctl.c).
*/
#ifndef CHARDEV_H
@ -45,5 +45,6 @@
/* The name of the device file */
#define DEVICE_FILE_NAME "char_dev"
#define DEVICE_PATH "/dev/char_dev"
#endif

View File

@ -0,0 +1,104 @@
/* userspace_ioctl.c - the process to use ioctl's to control the kernel module
*
* Until now we could have used cat for input and output. But now
* we need to do ioctl's, which require writing our own process.
*/
/* device specifics, such as ioctl numbers and the
* major device file. */
#include "../chardev.h"
#include <stdio.h> /* standard I/O */
#include <fcntl.h> /* open */
#include <unistd.h> /* close */
#include <stdlib.h> /* exit */
#include <sys/ioctl.h> /* ioctl */
/* Functions for the ioctl calls */
int ioctl_set_msg(int file_desc, char *message)
{
int ret_val;
ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);
if (ret_val < 0) {
printf("ioctl_set_msg failed:%d\n", ret_val);
}
return ret_val;
}
int ioctl_get_msg(int file_desc)
{
int ret_val;
char message[100] = { 0 };
/* Warning - this is dangerous because we don't tell
* the kernel how far it's allowed to write, so it
* might overflow the buffer. In a real production
* program, we would have used two ioctls - one to tell
* the kernel the buffer length and another to give
* it the buffer to fill
*/
ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);
if (ret_val < 0) {
printf("ioctl_get_msg failed:%d\n", ret_val);
}
printf("get_msg message:%s", message);
return ret_val;
}
int ioctl_get_nth_byte(int file_desc)
{
int i, c;
printf("get_nth_byte message:");
i = 0;
do {
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);
if (c < 0) {
printf("\nioctl_get_nth_byte failed at the %d'th byte:\n", i);
return c;
}
putchar(c);
} while (c != 0);
return 0;
}
/* Main - Call the ioctl functions */
int main(void)
{
int file_desc, ret_val;
char *msg = "Message passed by ioctl\n";
file_desc = open(DEVICE_PATH, O_RDWR);
if (file_desc < 0) {
printf("Can't open device file: %s, error:%d\n", DEVICE_PATH,
file_desc);
exit(EXIT_FAILURE);
}
ret_val = ioctl_set_msg(file_desc, msg);
if (ret_val)
goto error;
ret_val = ioctl_get_nth_byte(file_desc);
if (ret_val)
goto error;
ret_val = ioctl_get_msg(file_desc);
if (ret_val)
goto error;
close(file_desc);
return 0;
error:
close(file_desc);
exit(EXIT_FAILURE);
}

View File

@ -1314,11 +1314,16 @@ Notice here the roles of read and write are reversed again, so in ioctl's read i
The ioctl function is called with three parameters: the file descriptor of the appropriate device file, the ioctl number, and a parameter, which is of type long so you can use a cast to use it to pass anything.
You will not be able to pass a structure this way, but you will be able to pass a pointer to the structure.
Here is an example:
\samplec{examples/ioctl.c}
You can see there is an argument called \cpp|cmd| in \cpp|test_ioctl_ioctl()| function.
It is the ioctl number.
The ioctl number encodes the major device number, the type of the ioctl, the command, and the type of the parameter.
This ioctl number is usually created by a macro call (\cpp|_IO|, \cpp|_IOR|, \cpp|_IOW| or \cpp|_IOWR| --- depending on the type) in a header file.
This header file should then be included both by the programs which will use ioctl (so they can generate the appropriate ioctl's) and by the kernel module (so it can understand it).
In the example below, the header file is chardev.h and the program which uses it is ioctl.c.
In the example below, the header file is \verb|chardev.h| and the program which uses it is \verb|userspace_ioctl.c|.
If you want to use ioctls in your own kernel modules, it is best to receive an official ioctl assignment, so if you accidentally get somebody else's ioctls, or if they get yours, you'll know something is wrong.
For more information, consult the kernel source tree at \src{Documentation/userspace-api/ioctl/ioctl-number.rst}.
@ -1330,7 +1335,7 @@ The solution is using atomic Compare-And-Swap (CAS), which we mentioned at \ref{
\samplec{examples/chardev.h}
\samplec{examples/ioctl.c}
\samplec{examples/other/userspace_ioctl.c}
\section{System Calls}
\label{sec:syscall}