http://blog.163.com/he_junwei/blog/static/19793764620152510533753/
http://blog.csdn.net/styyzxjq2009/article/details/8023501
. The implementation of the IOCTL
I. Introduction to THE IOCTL:
Although there are many corresponding device operation functions in the file manipulation structure "struct file_operations", some commands do not find the corresponding operation function. such as CD-ROM drive, want a eject CD-ROM operation, this operation is not all the character device needs, so the file operation structure does not have corresponding function operation.
For this reason, the IOCTL has its usefulness ———— some functions that cannot be categorized are uniformly placed in the IOCTL function operation, and the corresponding operation is implemented by the specified command. Therefore, the IOCTL function implements multiple operations on the hardware, invoking the corresponding operation by applying the command passed in the layer.
Let's take a look at the connection between the application layer and the IOCTL of the driver function:
As can be seen above, FD through the kernel to find the corresponding inode and file structure pointer and passed to the drive function, while the other two parameters are not modified (the type changed nothing).
Briefly describe the function:
Int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);
Parameters:
1) Inode and FILE:IOCTL operations may be to modify the properties of a file, or to access hardware. To modify
File attributes, you need to use these two structures, so here are their pointers.
2) cmd: command, and then say it in a lengthy way.
3) arg: parameter, then also a lengthy speech.
return value:
1) If an illegal command is passed in, the IOCTL returns the error number-einval.
2) The driver function return value in the kernel has a default method, as long as a positive number, the kernel will be silly to think that this is the correct return, and pass it to the application layer, if it is negative, the kernel will consider it to be an error.
There are several different commands in the IOCTL, and that depends on the implementation of the function to determine the return value. For example, if there is a function like read in the IOCTL, the return value can be returned as read.
Of course, it is also possible not to return.
Second, the IOCTL cmd
Plainly, the CMD is a number, if the application layer from the value of the driver has a corresponding operation, so it can be.
One of the simplest IOCTL implementations: 3rd_char_4/1st
1) to define a command first, use a simple 0, a command header file, the driver and application functions to include this header file:
/*test_cmd.h*/
1 #ifndef _test_cmd_h
2 #define _test_cmd_h
3
4 #define TEST_CLEAR 0
5
6 #endif/*_test_cmd_h*/
2) drive to achieve IOCTL:
The command test_clear operation is to empty the kbuf in the drive.
122 int Test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long Arg)
123 {
124 int ret = 0;
_test_t *dev = filp->private_data;
126
127 switch (CMD) {
Case Test_clear:
129 memset (dev->kbuf, 0, dev_size);
dev->cur_size = 0;
131 Filp->f_pos = 0;
ret = 0;
133 break;
134 Default:/* processing when the command is wrong */
135 P_debug ("Error cmd!\n");
136 ret =-EINVAL;
137 break;
138}
139
return ret;
141}
3) One more application:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include "Test_cmd.h"
7
8 int main (void)
9 {
Ten char buf[20];
int FD;
int ret;
13
FD = open ("/dev/test", O_RDWR);
if (FD < 0)
16 {
Perror ("open");
return-1;
19}
20
Write (FD, "Xiao Bai", 10); 1 Write first
22
The IOCTL (FD, test_clear); 2 Empty again
24
ret = Read (FD, buf, 10); 3 Re-verification
if (Ret < 0)
27 {
Perror ("read");
29}
30
-Close (FD);
return 0;
33}
Note: In order for read to return an error, I modified the start of the read, write function of the driver when the first
Judging, you will know at a glance.
4) Verify:
[root:1st]# insmod Test.ko
MAJOR[253] minor[0]
Hello kernel
[root:1st]# mknod/dev/test C 253 0
[root:1st]#./app
<kernel>[test_write]write bytes, cur_size:[10]
<kernel>[test_write]kbuf is [Xiao Bai]
Read:no such device or address//haha! It's a mistake! Because there is no data to read.
It is perfectly possible to define a command as described above, but the kernel developer finds something wrong with it.
If there are two different devices, but their IOCTL cmd is the same, which day who accidentally opened the wrong, and called the IOCTL, so it is finished. Because this file also has a CMD corresponding to the implementation.
To prevent such a thing from happening, the kernel has a new definition of CMD, which specifies that the CMD should be different.
Iii. cmd in the IOCTL
A cmd is divided into 4 segments, each of which has its own meaning, and the cmd is defined in <linux/ioctl.h>. Note: But in fact <linux/ioctl.h> only contains <asm/ioctl.h>, which shows that this is platform-related, arm is defined in <ARCH/ARM/INCLUDE/ASM/IOCTL.H> But this document also contains other documents <ASM-GENERIC/IOCTL.H>, thousand find million find, finally found.
In <asm-generic/ioctl.h>, CMD is split as follows:
Explain four parts, all in <asm-generic/ioctl.h> and ioctl-number.txt these two documents are described.
1) Magic number: said the more beautiful name is only a 0~0xff number, accounting for 8bit (_ioc_typebits). This number is used to differentiate between different drivers, like when the device number is applied, and the kernel has a document that gives some of the magic numbers that are recommended or used.
/*documentation/ioctl/ioctl-number.txt*/
164 ' W ' all CERN SCI driver
165 ' Y ' 00-1f packet based user level communications
166 <mailto:[email Protected]>
167 ' z ' 00-3f CAN bus card
168 <mailto:[email protected]>
169 ' z ' 40-7f CAN bus card
<mailto:[email protected]>
You can see that ' X ' is not used by anyone, I'll take this as magic number!
2) Ordinal: Use this number to give your own command number, accounting for 8bit (_ioc_nrbits), my program starting from 1 to sort.
3) data transmission direction: accounted for 2bit (_ioc_dirbits). If a reference is involved, the kernel requires a description of the direction of the transmission, and the direction of transmission is described in terms of the application layer.
1) _ioc_none: value is 0, no data transfer.
2) _ioc_read: The value is 1 and the data is read from the device driver.
3) _ioc_write: A value of 2 to write data to the device driver.
4) _ioc_read|_ioc_write: bidirectional data transmission.
4) Data size: Related to architecture, Arm occupies 14bit (_ioc_sizebits), if the data is int, the value assigned by the kernel is sizeof (int).
Emphasize that the kernel is required to classify the cmd in this way, of course, you can not do this, just to meet the requirements of the kernel, so that their program looks very authentic. My program does not run as required above.
Since the kernel defines CMD as such, there must be a way for the user to define it easily:
_io (TYPE,NR)//command with no parameters
_ior (type,nr,size)//The command reads data from the drive
_iow (type,nr,size)//The command is written from the driver to the data
_IOWR (type,nr,size)//bidirectional data transmission
The above command has a direction defined, we want to pass the magic number (type), the ordinal (NR), and the size. Here Szie parameters only need to fill in the parameters of the type, such as int, the above command will help you to detect the correct type and then assign sizeof (int).
Commands that generate CMD must have a command to split cmd:
_ioc_dir (CMD)//extract direction from command
_ioc_type (CMD)//Extract magic number from command
_IOC_NR (CMD)//extract ordinal from command
_ioc_size (CMD)//Extract data size from command
The more you talk, the more complicated you are, and the more you talk about it, the pre-defined commands.
The predefined commands are identified by the kernel and implemented accordingly, in other words, once you have used these commands, you should not expect your driver to be able to receive it, because the kernel takes it away and handles it.
Divided into three categories:
1) commands available for any file
2) commands for ordinary files only
3) commands for specific file system types
In fact, I do not understand the above three categories, anyway, I made up a few number of their own when the command is not wrong, if it is really afraid of error, then do not use others have already used the magic number on the line.
Talk so much, finally to the program, modify the previous program, so that it looks more content.
/3rd_char/3rd_char_4/2nd
1) Change the command first:
/*test_cmd.h*/
1 #ifndef _test_cmd_h
2 #define _test_cmd_h
3
4 #define Test_magic ' x '//define magic number
5 #define TEST_MAX_NR 1//define the maximum ordinal of a command, with only one command of course 1
6
7 #define Test_clear _io (test_magic, 0)
8
9 #endif/*_test_cmd_h*/
2) since so hard to change the cmd, in the drive function of course to do some parameter test:
/*test.c*/
122 int Test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
123 {
124 int ret = 0;
_test_t *dev = filp->private_data;
126
127/* Since it's so hard to define the command, of course, verify that the command is valid */
if (_ioc_type (cmd) = test_magic) Return-einval;
129 if (_IOC_NR (cmd) > Test_max_nr) return-einval;
130
131 switch (CMD) {
Test_clear Case:
133 memset (dev->kbuf, 0, dev_size);
134 Dev->cur_size = 0;
135 Filp->f_pos = 0;
136 ret = 0;
137 break;
138 Default:/* processing when the command is wrong */
139 P_debug ("Error cmd!\n");
ret =-EINVAL;
141 break;
142}
143
144 return RET;
145}
Passing in each parameter checks to see if the magic number is the correct number of sequential numbers.
3) Validation of the application:
The result is exactly the same as the previous one because the operation of the command has not been modified
[root:2nd]# insmod Test.ko
MAJOR[253] minor[0]
Hello kernel
[root:2nd]# mknod/dev/test C 253 0
[root:2nd]#./app
<kernel>[test_write]write bytes, cur_size:[10]
<kernel>[test_write]kbuf is [Xiao Bai]
Read:no such device or address
The integer parameter of ARG in the IOCTL.
None of the examples above use the IOCTL's arguments. First, let's talk about the way the IOCTL passes the arguments.
The third parameter of the IOCTL of the application layer is "...", which is different from the "..." of printf, which means that you can pass any number of arguments, and the IOCTL can only pass one, "..." means that the kernel does not check the type of this parameter. In other words, you can pass in any parameter from the user layer, as long as you pass in the number 1.
There are generally two ways to pass a parameter:
1) integer, that is labor-saving and worry, direct use can be.
2) The pointer, through the pointer on what type can be, of course, it is more annoying to use.
First, simple, use integers as parameters:
example, implement a command, change the offset by passing in parameters, although Llseek has been implemented, here just want to verify the method of positive parameter.
1) Add a command first:
1 #ifndef _test_cmd_h
2 #define _test_cmd_h
3
4 #define Test_magic ' x '//define magic number
5 #define TEST_MAX_NR 2//define the maximum ordinal of a command
6
7 #define Test_clear _io (test_magic, 1)
8 #define Test_offset _io (test_magic, 2)
9
Ten #endif/*_test_cmd_h*/
Here someone will ask, clearly you are to pass parameters, why not _iow and use _io to define the command?
There are two reasons:
1) because the direction of the data is defined so that the driver's function verifies the security of the data, the general pointer needs to verify the security, because someone will be malicious (recall Copy_to_user).
2) personal preferences, convenient I write a program to introduce another method of communication, the command is just a number, as long as not to conflict with the predefined commands can be.
2) Update Test_ioctl
122 int Test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long Arg)
123 {
124 int ret = 0;
_test_t *dev = filp->private_data;
126
127/* Since it's so hard to define the command, of course, verify that the command is valid */
if (_ioc_type (cmd) = test_magic) Return-einval;
129 if (_IOC_NR (cmd) > Test_max_nr) return-einval;
130
131 switch (CMD) {
Test_clear Case:
133 memset (dev->kbuf, 0, dev_size);
134 Dev->cur_size = 0;
135 Filp->f_pos = 0;
136 ret = 0;
137 break;
138 Case Test_offset://Change the offset according to the parameters passed in
139 Filp->f_pos + = (int) arg;
P_debug ("Change offset!\n");
141 ret = 0;
142 break;
143 Default:/* processing when the command is wrong */
144 P_debug ("Error cmd!\n");
145 ret =-EINVAL;
146 break;
147}
148
149 return ret;
150}
The Tset_offset command changes the offset according to the parameters, but one thing to note here is that the type of the parameter, the drive function must know what type of argument is coming from the application, otherwise it cannot be used. In this function, the arguments from the application layer are int, so you have to use int in the drive.
3) Change the application again:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6
7 #include "Test_cmd.h"
8
9 int main (void)
10 {
One char buf[20];
int FD;
int ret;
14
FD = open ("/dev/test", O_RDWR);
if (FD < 0)
17 {
Perror ("open");
return-1;
20}
21st
Write (FD, "Xiao Bai", 10); Write first
23
The IOCTL (FD, Test_offset,-10); and change the offset.
25
+ ret = read (FD, buf, 10); Re-read data
printf ("<app> buf is [%s]\n", buf);
if (Ret < 0)
29 {
Perror ("read");
31}
32
Close (FD);
return 0;
35}
4) Verify
[root:3rd]# insmod Test.ko
MAJOR[253] minor[0]
Hello kernel
[root:3rd]# mknod/dev/test C 253 0
[root:3rd]#./app
<kernel>[test_write]write bytes, cur_size:[10]
<kernel>[test_write]kbuf is [Xiao Bai]
<kernel>[test_ioctl]change offset! Change the offset
<kernel>[test_read]read bytes, cur_size:[0]//No error, read successfully!
<app> buf is [Xiao Bai]
The above-mentioned arguments are very simple, and the next step is to use pointers to pass the arguments.
Given that a parameter cannot always be just a positive number, if you want to pass a little more stuff, such as a struct, you have to use a pointer.
Vi. the pointer of ARG in the IOCTL is passed.
When I talk about pointers from the application, I have to think of my evil example of passing illegal pointers. Therefore, any pointers that are used in the driver to deal with the application layer must first verify the security of the pointers.
There are two ways to test this:
1) test only when used.
2) One comes in and the IOCTL is tested.
First say the use of time test, White is to use Copy_xx_user series functions, the following implementation:
1) define a command first
1 #ifndef _test_cmd_h
2 #define _test_cmd_h
3
4 struct ioctl_data{
5 unsigned int size;
6 Char buf[100];
7};
8
9 #define DEV_SIZE 100
10
One #define Test_magic ' x '//define magic number
#define TEST_MAX_NR 3//define the maximum ordinal of a command
13
#define Test_clear _io (test_magic, 1)
#define Test_offset _io (test_magic, 2)
#define TEST_KBUF _io (test_magic, 3)
17
#endif/*_test_cmd_h*/
There is a more defined function here, although this command is related to pointers, but I still _iow, or that sentence, now do not need to use.
The operation of this command is to pass in a struct pointer that drives the KBUF and Cur_size and offsets based on the contents of the structure.
2) to implement a function:
122 int Test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long Arg)
123 {
124 int ret = 0;
_test_t *dev = filp->private_data;
126 struct Ioctl_data Val;
127
128/* Since it's so hard to define the command, of course, verify that the command is valid */
129 if (_ioc_type (cmd)! = test_magic) Return-einval;
if (_ioc_nr (cmd) > Test_max_nr) return-einval;
131
Switch (CMD) {
133 Case Test_clear:
134 memset (dev->kbuf, 0, dev_size);
135 dev->cur_size = 0;
136 Filp->f_pos = 0;
137 ret = 0;
138 break;
139 Case Test_offset://Change the offset according to the parameters passed in
Filp->f_pos + = (int) arg;
141 P_debug ("Change offset!\n");
142 ret = 0;
143 break;
144 Case TEST_KBUF://Modify Kbuf
145 if (Copy_from_user (&val, (struct ioctl_data *) arg, sizeof (struct ioctl_data))) {
146 ret =-efault;
147 goto RET;
148}
149 memset (dev->kbuf, 0, dev_size);
memcpy (Dev->kbuf, Val.buf, val.size);
151 dev->cur_size = val.size;
Filp->f_pos = 0;
153 ret = 0;
154 break;
155 Default:/* processing when the command is wrong */
156 P_debug ("Error cmd!\n");
157 ret =-EINVAL;
158 break;
159}
160
161 RET:
162 return ret;
163}
The 145th line, because the pointer is from the user program, so must check security.
3) to an application
9 int main (void)
10 {
One char buf[20];
int FD;
int ret;
14
Ioctl_data My_data= {
Size = 10,
BUF = "123456789"
18};
19
FD = open ("/dev/test", O_RDWR);
if (FD < 0)
22 {
Perror ("open");
return-1;
25}
26
Write (FD, "Xiao Bai", 10);
28
IOCTL (FD, TEST_KBUF, &my_data);
30
ret = Read (FD, buf, 10);
printf ("<app> buf is [%s]\n", buf);
if (Ret < 0)
34 {
Perror ("read");
36}
37
Close (FD);
0;
40}
4) Check again to verify:
[root:4th]#./app
<kernel>[test_write]write bytes, cur_size:[10]
<kernel>[test_write]kbuf is [Xiao Bai]
<kernel>[test_read]read bytes, cur_size:[0]
<app> BUF is [123456789]//success!
Note: Functions like Copy_xx_user contain put_user, get_user, etc., I will not elaborate.
Here's the second method: use ACCESS_OK to detect after entering the IOCTL.
To declare: The following verification method is incorrect. If you don't want to see it, today's content is finished.
Let's talk about the use of ACCESS_OK.
ACCESS_OK (type, addr, size)
Use: Detect the security of an address
Parameters:
Type: Used to specify the direction of the data transfer, verify_read means to read the application layer data, Verift_write represents the data to be written to the application layer. Note: This is in the opposite direction of the IOR IOW. If both read and write, use Verify_write.
Addr: Address of user space
Size: Amount of data
return value:
Successful return 1, failure returns 0.
Now that you know how to use it, you come directly to the program:
1) define the command
1 #ifndef _test_cmd_h
2 #define _test_cmd_h
3
4 struct ioctl_data{
5 unsigned int size;
6 Char buf[100];
7};
8
9 #define DEV_SIZE 100
10
One #define Test_magic ' x '//define magic number
#define TEST_MAX_NR 3//define the maximum ordinal of a command
13
#define Test_clear _io (test_magic, 1)
#define Test_offset _io (test_magic, 2)
#define TEST_KBUF _iow (test_magic, 3, struct ioctl_data)
17
#endif/*_test_cmd_h*/
It's finally going to use _IOW!
2) Implement the IOCTL
122 int Test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long Arg)
123 {
124 int ret = 0;
_test_t *dev = filp->private_data;
126
127/* Since it's so hard to define the command, of course, verify that the command is valid */
if (_ioc_type (cmd) = test_magic) Return-einval;
129 if (_IOC_NR (cmd) > Test_max_nr) return-einval;
130/* Determine the security of the pointer based on the direction specified by the extract command */
131 if (_ioc_dir (cmd) & _ioc_read)
ACCESS_OK ret = (verify_write, (void __user *) arg, _ioc_size (cmd));
133 Else if (_ioc_dir (cmd) & _ioc_write)
134 ret = ACCESS_OK (Verify_read, (void __user *) arg, _ioc_size (cmd));
135 if (!ret) Return-efault;
136
137 switch (CMD) {
138 Case Test_clear:
139 memset (dev->kbuf, 0, dev_size);
dev->cur_size = 0;
141 Filp->f_pos = 0;
142 ret = 0;
143 break;
144 Case Test_offset://Change the offset according to the parameters passed in
145 Filp->f_pos + = (int) arg;
146 P_debug ("Change offset!\n");
147 ret = 0;
148 break;
149 Case TEST_KBUF://Modify Kbuf
memset (dev->kbuf, 0, dev_size);
151 memcpy (Dev->kbuf, (struct ioctl_data *) arg)->BUF,
(struct ioctl_data *) arg) (->size);
153 dev->cur_size = ((struct ioctl_data *) arg)->size;
154 Filp->f_pos = 0;
155 ret = 0;
156 break;
157 Default:/* processing when the command is wrong */
158 P_debug ("Error cmd!\n");
159 ret =-EINVAL;
break;
161}
162
163 return ret;
164}
The above is not used Copy_to_user, but by ACCESS_OK to detect.
3) One more application:
9 int main (void)
10 {
One char buf[20];
int FD;
int ret;
14
Ioctl_data My_data= {
Size = 10,
BUF = "123456789"
18};
19
FD = open ("/dev/test", O_RDWR);
if (FD < 0)
22 {
Perror ("open");
return-1;
25}
26
Write (FD, "Xiao Bai", 10);
28
RET = IOCTL (FD, TEST_KBUF, &my_data);
if (Ret < 0)
31 {
Perror ("IOCTL");
33}
34
ret = Read (FD, buf, 10);
printf ("<app> buf is [%s]\n", buf);
PNS if (Ret < 0)
38 {
Perror ("read");
40}
41
Close (FD);
return 0;
44}
4) Verify that the effect is the same as the previous
[root:5th]#./app
<kernel>[test_write]write bytes, cur_size:[10]
<kernel>[test_write]kbuf is [Xiao Bai]
<kernel>[test_read]read bytes, cur_size:[0]
<app> BUF is [123456789]
The following is going to be the case, this driver is problematic, that is, verify that security does not work at all! When I pass the illegal pointer, the driver will also output, do not believe you can send an evil address (void *) 0 go in and try it.
Modify the same code as the application:
RET = IOCTL (FD, TEST_KBUF, &my_data);
Above is I do the wrong implementation, I would like to verify, as long as the ACCESS_OK test, the data will be safe, did not expect after the ACCESS_OK test will still be wrong.
However, Copy_to_user also calls ACCESS_OK again to call memcpy, and it does not go wrong. I'm not sure what I'm doing now, if anyone knows the trouble.
I checked the third version of the device driver, and on page 144 there was this saying:
1.access_ok did not finish all the memory checks,
2. Most of the driver code is not used ACCESS_OK, the memory management will be described later.
Here in the book there is such a convention: (All my own understanding)
1. Incoming pointers need to be checked for security. The memcpy function is not used in the kernel as much as possible.
The 2.copy_to_user.copy_from_user.get_user.put_user function detects the security of pointers before copying the data. No need for ACCESS_OK.
3. If the ACCSEE_OK test data is used at the beginning of the IOCTL function, the next code can use the __put_user or __get_user functions that do not need to be detected (examples in the book)
Although there are still writing things do not understand, but personally think, if the use of a access_ok to be so troublesome, then I will not be good, I will use the Copy_xx_user function, labor-saving and worry.
Vii. Summary:
This time the realization of the IOCTL:
1) How the command is defined.
2) How the parameters are passed.
IOCTL function usage