mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-11-22 04:31:09 +08:00
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:
parent
fb15882377
commit
d8216ff7b6
|
@ -2,8 +2,8 @@
|
||||||
* chardev.h - the header file with the ioctl definitions.
|
* chardev.h - the header file with the ioctl definitions.
|
||||||
*
|
*
|
||||||
* The declarations here have to be in a header file, because they need
|
* 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
|
* to be known both to the kernel module (in chardev2.c) and the process
|
||||||
* calling ioctl (ioctl.c).
|
* calling ioctl() (in userspace_ioctl.c).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CHARDEV_H
|
#ifndef CHARDEV_H
|
||||||
|
@ -45,5 +45,6 @@
|
||||||
|
|
||||||
/* The name of the device file */
|
/* The name of the device file */
|
||||||
#define DEVICE_FILE_NAME "char_dev"
|
#define DEVICE_FILE_NAME "char_dev"
|
||||||
|
#define DEVICE_PATH "/dev/char_dev"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
104
examples/other/userspace_ioctl.c
Normal file
104
examples/other/userspace_ioctl.c
Normal 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);
|
||||||
|
}
|
|
@ -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.
|
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.
|
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.
|
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 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).
|
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.
|
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}.
|
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/chardev.h}
|
||||||
|
|
||||||
\samplec{examples/ioctl.c}
|
\samplec{examples/other/userspace_ioctl.c}
|
||||||
|
|
||||||
\section{System Calls}
|
\section{System Calls}
|
||||||
\label{sec:syscall}
|
\label{sec:syscall}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user