Summary of the usefulness of ioctl functions (for beginners)

1 Why does ioctl appear

        (1) Although there are many corresponding device operation functions in the file operation structure "struct file_operations", some commands cannot find the corresponding operation functions. For example, if a CD-ROM driver wants an operation to eject the CD-ROM , this operation is not required by all character devices, so the file operation structure will not have corresponding function operations.

        (2) In addition to the ability to read and write devices, most drivers also need to have the ability to control the hardware . What the user program does is just tell the driver what it wants to do through the command code. As for how to interpret these commands and how to implement them These commands, this is what the driver does .

        (3) The programmers who write the application only need to pass the command code provided by the driver developer in the driver-related .H file. The command code is explained in text, because some drivers are encrypted and contain ioctl The operation will not show you the source code, but will provide the H file that contains the explanation of all command codes of the driver and the description of the operation results, so that you only need to understand the command codes, and application programmers can quickly develop and get started .

        Summary: For this reason, ioctl has its usefulness--some functions that cannot be classified are placed in the function operation of ioctl, and the corresponding operation is realized through the specified command. Therefore, the ioctl function implements multiple operation steps on the hardware, and integrates them to complete a task, instead of a single write/read operation, which calls the corresponding operation through the command passed in by the application layer.


2 ioctl function prototype

    2.1   In the application, use the ioctl system call to control the device , the prototype is as follows

  1. int ioctl(int fd,unsigned long cmd,...);  
  2. /* 
  3. fd: file descriptor 
  4. cmd: control command 
  5. ...: optional parameters: insert *argp, the specific content depends on cmd 
  6. */ 

    2.2   In the driver, use ioctl to operate the hardware , the prototype is as follows

  1. int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);  
  2. /* 
  3. The two pointers inode and filp correspond to the file descriptor fd passed by the application, which is the same as passing the parameters of the open method. 
  4. cmd is passed directly to the driver by user space unmodified 
  5. arg is optional. 
  6. */          

        Summary: When we load the driver into the kernel, the driver will call the kernel's registration function to register the driver's file_operations. At this time, we open the driver's device file fd through the application layer, and when we call ioctl, it will be automatically hooked to Specify the ioctl function in the driver to complete the specified hardware operation.

3 Several important parameters of the ioctl function

    3.1 Why does cmd become so complicated

(1) The so-called complex command is nothing more than a 32-bit integer. For example, it is possible to define a command in the simplest way.  
                                      #define TEST_CLEAR 155
         As above but the kernel developers found something wrong with this. If there are two different devices, but their ioctl's cmd is the same
                                       #define TEST_OPEN 155 

        When the amount of program code, functions, and modules are too large, some day the application developer did not pay attention. When operating a certain function, they originally wanted to open the A device and operate the A hardware, but accidentally wrote the wrong code to open the B device, and called ioctl, that's the end of it. Because there is also a corresponding implementation of cmd in this file, you will affect the normal use of the B driver for no reason. In order to prevent such a thing from happening, the kernel has a new definition of cmd, which stipulates that cmd should be different.

Code after using complex cmd:

                        #define TEST_MAGIC 'x' //define the magic number
                        #define TEST_MAX_NR 3 //define the maximum ordinal number of the command

                        #define TEST_CLEAR _IO(TEST_MAGIC, 1)
                        #define TEST_OFFSET _IO(TEST_MAGIC, 2)
                        #define TEST_KBUF _IO(TEST_MAGIC, 3) 

   

   3.2 Composition of cmd

         Preface:   In the body of the ioctl function implemented in the driver, there is actually a switch {case} structure, each case corresponds to a command code, and some corresponding operations are performed. How to implement these operations, this is each programmer's own business, because the device is specific. The key is how to organize the command code, because the command code in ioctl is the only way to contact the user program command and driver support.

A command code             is defined in the Linux kernel like this :

            | Device Type | Serial Number | Orientation | Data Size |

            |  8    bit   |  8 bit     | 2 bit  |  8~14 bit |

    1) Magic number: No matter how nice the name is, it is just a number from 0 to 0xff , accounting for 8 bits (_IOC_TYPEBITS) . This number is used to distinguish different drivers. Like when the device number is applied, the kernel has a document that gives some recommended or already used magic numbers.
    2) Ordinal number: Use this number to number your own commands, accounting for 8 bits (_IOC_NRBITS) , and my program starts from 1 .

    3) Data transmission direction: 2bit (_IOC_DIRBITS) . If it is involved to pass parameters, the kernel requires to describe the direction of transmission, and the direction of transmission is described from the perspective of the application layer.

    4) Data size: related to the architecture , 14bit (_IOC_SIZEBITS) under ARM , if the data is int , the value assigned by the kernel to this is sizeof(int)

        Summary: In this way, a command becomes a command code in the form of an integer. However, the command code is very unintuitive, so some macros are provided in the Kernel. These macros can generate the command code according to the easy-to-understand strings, or obtain some user-understandable strings from the command code to indicate the device type corresponding to the command. , device serial number, data transfer direction and data transfer size.

        as follows:

  1. //nr is the serial number, the maximum number of commands, datatype is the data type, such as int  
  2. _IO(type, nr )  //command without parameters  
  3. _IOR(type, nr, datatype)  //Read data from the driver  
  4. _IOW(type, nr, datatype)  //Write data to the driver  
  5. _IOWR(type,nr, datatype)  //Two-way transmission  
  6.   
  7.   Example of define command:  
  8. #define MEM_IOC_MAGIC 'm' //Define the type  
  9. #define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)  
  10. #define MEM_IOCGQSET _IOR(MEM_IOC_MAGIC, 1, int)  

   The final goal of cmd: Since the command is so laboriously defined, of course, it is necessary to check whether the command is valid. All we can add verification code to the driver to determine whether the command is the command of the driver.

                 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;
                 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

3.3 ioctl中的arg之传参

    前言:  应用层的ioctl的第三个参数是"...",这个跟printf的"..."可不一样,printf中是意味这你可以传任意个数的参数,而ioctl最多也只能传一个,"..."的意思是让内核不要检查这个参数的类型。也就是说,从用户层可以传入任何参数,只要你传入的个数是1.

一般会有两种的传参方法:

    3.3.1  整数,那可是省力又省心,直接使用就可以了。

        如下代码:作为运算参数 
                应用层   ioctl(fd, TEST_OFFSET, 10);
                驱动层    switch(cmd){
                                        case TEST_OFFSET: 
                                        filp->f_pos += (int)arg;        
                                }

    3.3.2  指针,通过指针的就传什么类型都可以了,当然用起来就比较烦。

        (1)考虑到参数不可能永远只是一个正数这么简单,如果要传多一点的东西,譬如是结构体,那就得用上指针了。

        (2) When it comes to pointers passed from the application, I have to think of my evil example of passing in an illegal pointer. Therefore, any pointers in the driver that deal with the application layer must first check the safety of the pointers.

      The code is as follows: Because the pointer is passed from the user program, the safety must be checked

       驱动代码              if(copy_from_user(&val, (struct ioctl_data *)arg, sizeof(struct ioctl_data))){
                                    ret = - EFAULT;
                                    goto RET;

                                    }


       Application code struct ioctl_data my_data= {

                                   .size = 10,
                                   .buf = "123456789"
                                    };

                                    ioctl(fd, TEST_KBUF, &my_data);






Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325805739&siteId=291194637