Detailed explanation of IOCTL function usage

ioctl is a function in the device driver to manage the I/O channels of the device . The so-called management of the I/O channel is to control some characteristics of the device, such as the transmission baud rate of the serial port, the speed of the motor, and so on. The number of its calls is as follows: 
int ioctl(int fd, ind cmd, …); 
    where fd is the file identifier returned by the open function when the user program opens the device, cmd is the control command of the user program to the device, as for the following ellipsis , which are some supplementary parameters, generally at most one. The presence or absence of this parameter is related to the meaning of cmd. 

    The ioctl function is an attribute component in the file structure, that is, if your driver provides support for ioctl, the user can use the ioctl function in the user program to control the I/O channel of the device.


Briefly describe the function:

int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);

parameter:

1) Inode and file : The operation of ioctl may be to modify the attributes of the file, or to access the hardware. to modify

For file attributes, these two structures are used, so here are their pointers.

2) cmd : command, and then I will talk at length.

3) arg : parameter, and then there will be a long discussion.

return value:

1) If an illegal command is passed in, ioctl returns the error number -EINVAL .

2) The return value of the driver function in the kernel has a default method. As long as it is a positive number, the kernel will foolishly think that this is the correct return and pass it to the application layer. If it is a negative value, the kernel will think it is It's the wrong number.

There are many different commands in Ioctl , it depends on the implementation of its function to determine the return value. For example, if there is a function similar to read in ioctl , the return value can be returned like read .

Of course, it is also possible not to return.

How to implement
    ioctl The body of the ioctl function implemented in the driver actually has a switch{case} structure, each case corresponds to a command code, and some corresponding operations are performed. How to implement these operations is the application's own business.
Command codes in ioctl are the only way to contact user program commands and driver support . If there are two different devices, but the cmd (command code) of their ioctl is the same, someone accidentally opens the wrong device one day and calls the ioctl , and it's over. Because this file also has a corresponding implementation of cmd , we can generate unused command codes by ourselves. So a command code is defined like this in the Linux kernel .

A cmd is divided into 4 segments , each segment has its own meaning, cmd is defined in <linux/ioctl.h> . Note: But in fact <linux/ioctl.h> only includes <asm/ioctl.h> , which shows that this is platform-dependent, ARM is defined in <arch/arm/include/asm/ioctl.h > , but this file also contains other files <asm-generic/ioctl.h> , I searched for it, and finally found it.


In <asm-generic/ioctl.h> , cmd is split as follows:

All are described in the two documents <asm-generic/ioctl.h> and ioctl-number.txt http:/..../linux/include/asm-generic/ioctl.h

  #define _IOC(dir,type,nr,size) \
           (((dir)  << _IOC_DIRSHIFT) | \
          ((type) << _IOC_TYPESHIFT) | \
           ((nr)   << _IOC_NRSHIFT) | \
           ((size) << _IOC_SIZESHIFT))
____________________________________
| 设备类型 | 序列号 | 方向 |数据尺寸|
|----------|--------|------|--------|
| 8 bit | 8 bit |2 bit |8~14 bit|
|----------|--------|------|--------|

这样一来,一个命令就变成了一个整数形式的命令码;但是命令码非常的不直观,所以Linux Kernel中提供了一些这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。

    幻数:说得再好听的名字也只不过是个0~0xff的数,占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,像设备号申请的时候一样,内核有一个文档给出一些推荐的或者已经被使用的幻数。在内核文件中定义如下
Ioctl-number.txt (f:\sourceproject\linux-kernel\linux-3.14.26-g2489c02\documentation\ioctl)

点击(此处)折叠或打开

  1. Code Seq#(hex)    Include File        Comments
  2. ========================================================
  3. 0x00    00-1F    linux/fs.h        
  4. 0x00    00-1F    scsi/scsi_ioctl.h    
  5. 0x00    00-1F    linux/fb.h        
  6. 0x00    00-1F    linux/wavefront.h    
  7. 0x02    all    linux/fd.h
  8. 0x03    all    linux/hdreg.h
  9. 0x04    D2-DC    linux/umsdos_fs.h    Dead since 2.6.11, but don't reuse these.
  10. 0x06    all    linux/lp.h
  11. 0x09    all    linux/raid/md_u.h
  12. 0x10    00-0F    drivers/char/s390/vmcp.h
  13. 0x10    10-1F    arch/s390/include/uapi/sclp_ctl.h
  14. 0x10    20-2F    arch/s390/include/uapi/asm/hypfs.h
  15. 0x12    all    linux/fs.h
  16.         linux/blkpg.h
  17. 0x1b    all    InfiniBand Subsystem    <http://infiniband.sourceforge.net/>
  18. 0x20    all    drivers/cdrom/cm206.h
  19. 0x22    all    scsi/sg.h
  20. '#'    00-3F    IEEE 1394 Subsystem    Block for the entire subsystem
  21. '$'    00-0F    linux/perf_counter.h, linux/perf_event.h
  22. .....................
  23. ....................

四、CMD参数如何得出

    cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switch{case}结构进行相应的操作。
    Linux内核已经提供了相应的宏来自动生成ioctl命令码

_IO(type,nr)   //无数据传输
_IOR(type,nr,size)  //从设备读数据 
_IOW(type,nr,size)  //向设备写数据
_IOWR(type,nr,size)  //同时有读写数据

     上面的命令已经定义了方向,我们要传的是幻数 (type) 序号 (nr) 大小 (size) 。在这里 szie 的参数只需要填参数的类型,如 int ,上面的命令就会帮你检测类型的正确然后赋值 sizeof(int)      
     相对的,Linux内核也提供了相应的宏来从ioctl命令号种 解码 相应的域值:

_IOC_DIR(nr)  //从命令中提取方向
_IOC_TYPE(nr) //从命令中提取幻数
_IOC_NR(nr)  //从命令中提取序数
_IOC_SIZE(nr)  //从命令中提取数据大小

例:
/*include_cmd.hpp*/
#define LED_IOC_MAGIC 0x13  //定义幻数
#define LED_MAX_NR    3          //定义命令的最大序数
#define  LED_GPRS_MAGIC _IO(LED_IOC_MAGIC,0x00)  //0x00   用” 宏+幻数来自动生成ioctl命令码
#define LED_WIFI _MAGIC _IO(LED_IOC_MAGIC,0x01)  //0x00
#define LED_BT _MAGIC _IO(LED_IOC_MAGIC,0x02)  //0x00

/*test.cpp*/
fd = open();
ioctl(fd,LED_GPRS_MAGIC,0);
ioctl(fd,LED_GPRS_MAGIC,1);
ioctl(fd,LED_WIFI_MAGIC ,0);
ioctl(fd,LED_WIFI_MAGIC ,1);

/*test_ioctl.c*/
int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
{
    if(_IOC_TYPE(cmd) !=LED_IOC_MAGIC ) return -EINVAL;   //提取出幻数做检验
    if(_IOC_NR(cmd
) > LED_MAX_NR ) return -EINVAL;          //提取命令序数

    switch(cmd){
    case LED_GPRS_MAGIC:
     if(arg==0){
    //..........
    }else if(arg ==1){
    //..........
    }
    break;
    case LED_WIFI_MAGIC:
    //..........
    break;

    }

}
arg参数:

如果arg是一个整数,可以直接使用;
  如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
  内部有检查的,

不需要检测的:

[cpp]  view plain  copy
 
  1. copy_from_user  
  2. copy_to_user  
  3. get_user  
  4. put_user  

需要检测 的:

[cpp]  view plain  copy
 
  1. __get_user  
  2. __put_user 

Guess you like

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