IOCTL的機制

IOCTL(I/O Control)是一種系統呼叫的介面。User process呼叫ioctl()即可對驅動程式送出系統呼叫,如此會呼叫驅動程式的IOCTL處理函式,與read()與wrsite()一樣可跟驅動程式交換資料。在這時交換的資料格式,可由驅動程式開發者自由決定,因此也可以說IOCTL是比read()與write()還靈活的介面。

user process 的ioctl()原型:

#include <sys/ioctl.h>
int ioctl(int d,int request,...);

第一引數是(d)是裝置檔的handle,第二引數(request)則是驅動程式定義的指令代碼(32bit),一般透過驅動程式提供的巨集來指定。第三引數之後是"...",代表可變引數。第三引數也由驅動程式自由決定,當然可以是沒有第三引數。ioctl的傳回值為0表示成功,傳回-1表示失敗。

想支援ioctl(),驅動程式必須提供IOCTL方法,並將file_operations結構的ioctl成員設為函式指標。IOCTL方法如下:

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

ioctl()是user process呼叫的,所以驅動程式的IOCTL方法是在"user context"底下運作,因此也可以sleep。IOCTL方法內有critical section的時候,可以使用"semaphore"來鎖定。
第一引數(inode) 是 user process 開啟的裝置檔相關資訊。
第二引數(filp) 通常在取出驅動程式私有資料時,會透過 filp 進行。
第三引數(cmd) 是 IOCTL 的指令,透過此引數,驅動程式可以判斷 user process 究竟想做什麼。
第四引數(arg) 相當於 ioctl() 可變引數(...) 的參數,內含 user process 指標。驅動程式不能直接讀寫這個指標,必須透過copy_from_user()以及copy_to_user()讀寫資料。

IOCTL指令

IOCTL 指令是 unsigned int 型別的變數,它的格式是固定的,以巨集定義。
| dir(2bit) | Size(14bit) | type(8bit) | nr(8bit) |
IOCTL 指令格式定義在 include/asm-generic/ioctl.h。

#define _IOC(dir,type,nr,size) \
         (((dir)  << _IOC_DIRSHIFT) | \
         ((type) << _IOC_TYPESHIFT) | \
         ((nr)   << _IOC_NRSHIFT) | \
         ((size) << _IOC_SIZESHIFT))

#define _IO(type,nr)            _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)      _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)     _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

這四個巨集用途如下表所示:

巨集 用途
_IO() 無引數的IOCTL
_IOR() 從驅動程式讀取資料
_IOW() 把資料寫給驅動程式
_IOWR() 與驅動程式讀寫資料

這些巨集可以用來在標頭檔定義下面這些巨集,讓驅動程式、user process可以include進來用

#include <linux/ioctl.h>

struct ioctl_cmd { 
unsigned int reg; 
unsigned int offset; 
unsigned int val; 
}; 

#define IOC_MAGIC 'd' 

#define IOCTL_VALSET _IOW(IOC_MAGIC, 1, struct ioctl_cmd) 
#define IOCTL_VALGET _IOR(IOC_MAGIC, 2, struct ioctl_cmd)

此例的 IOCTL_VALSET 巨集是用來傳資料給驅動程式, IOCTL_GET 則是從驅動程式讀回資料。
巨集的 type 引數長度為 1byte,所以通常用字元常數指定,驅動程式名稱的第一個字元是很常見的選擇。
引數 nr 是數值(number) 的意思,通常為 IOCTL 指令連續編號。
引數 size 是傳給 ioctl() 可變引數(...) 的介面型別。
驅動程式可以使用 _IOC_SIZE 巨集得知傳給 ioctl() 可變引數的結構大小。

#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

在讀寫 user process 指標時,可以先呼叫 access_ok() 巨集判斷指標可否讀寫。

#define access_ok(type, addr, size) (likely(__range_ok(addr, size) == 0))

第一個引數應該是VERIFY_READ或VERIFY_WRITE的其中之一,取決於想讀或寫,addr是要被檢察的user space位址,而size是檢查範圍(以byte為計算單位)。回傳值1代表成功,0代表失敗。

權限

IOCTL 指令如果要限制只有 root 可以使用,此時 capable() 就很方便:

int capable(int cap);

這個函式的引數可以指定的巨集,定義在 include/linux/capability.h 內。
要檢查是否擁有 root 權限,可以寫成:

if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM goto done; }

results matching ""

    No results matching ""