在編寫ioctl代碼之前,需要選擇對應不同命令的編號。爲了防止對錯誤的設備使用正確的命令,命令號應該在系統範圍內唯一,這種錯誤匹配並不是不會發生,程序可能發現自己正在試圖對FIFO和audio等這類非串行設備輸入流修改波特率,如果每一個ioctl命令都是唯一的,應用程序進行這種操作時就會得到一個EINVAL錯誤,而不是無意間成功地完成了意想不到的操作。
要按Linux內核的約定方法爲驅動程序選擇ioctl編號,應該首先看看include/asm/ioctl.h和Doucumention/ioctl-number.txt這兩個文件。頭文件定義了要使用的位字段:類型(幻數)、序數、傳送方向以及參數大小等。ioctl-number.txt文件中羅列了內核所使用的幻數,選擇自己的幻數要避免和內核衝突。以下是對include/asm/ioctl.h中定義的宏的註釋:
#define _IOC_NRBITS 8 //序數(number)字段的字位寬度,8bits
#define _IOC_TYPEBITS 8 //幻數(type)字段的字位寬度,8bits
#define _IOC_SIZEBITS 14 //大小(size)字段的字位寬度,14bits
#define _IOC_DIRBITS 2 //方向(direction)字段的字位寬度,2bits
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) //序數字段的掩碼,0x000000FF
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) //幻數字段的掩碼,0x000000FF
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) //大小字段的掩碼,0x00003FFF
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) //方向字段的掩碼,0x00000003
#define _IOC_NRSHIFT 0 //序數字段在整個字段中的位移,0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //幻數字段的位移,8
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) //大小字段的位移,16
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) //方向字段的位移,30
/*
* Direction bits.
*/
#define _IOC_NONE 0U //沒有數據傳輸
#define _IOC_WRITE 1U //向設備寫入數據,驅動程序必須從用戶空間讀入數據
#define _IOC_READ 2U //從設備中讀取數據,驅動程序必須向用戶空間寫入數據
/*
*_IOC 宏將dir,type,nr,size四個參數組合成一個cmd參數,如下圖:
*
*/
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
/*
* used to create numbers
*/
//構造無參數的命令編號
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
//構造從驅動程序中讀取數據的命令編號
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
//用於向驅動程序寫入數據命令
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
//用於雙向傳輸
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/*
*used to decode ioctl numbers..
*/
//從命令參數中解析出數據方向,即寫進還是讀出
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
//從命令參數中解析出幻數type
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
//從命令參數中解析出序數number
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
//從命令參數中解析出用戶數據大小
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
/* ...and for the drivers/sound files... */
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)