ioctl方法詳解

2021-07-23 06:27:25 字數 3474 閱讀 3723

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

這是驅動程式裝置控制介面函式(ioctl函式)的核心原型定義,

struct inode * 和

struct file* 描述了操作的檔案,unsigned int 描述了ioctl命令號,這是乙個重要的引數,我們稍後會對它做詳細介紹。最後乙個引數是unsigned long資料型別,描述了ioctl命令可能帶有的引數,它可能是乙個整數或指標資料。

ioctl命令號是這個函式中最重要的引數,它描述的ioctl要處理的命令。linux中使用乙個32位的資料來編碼ioctl命令,它包含四個部分:dir:type:nr:size。

代表資料傳輸的方向,佔2位,可以是_ioc_none(無資料傳輸,0u),_ioc_write(向裝置寫資料,1u)或_ioc_read(從裝置讀資料,2u)或他們的邏輯或組合,當然只有_ioc_write和_ioc_read的邏輯或才有意義。

描述了ioctl命令的型別,8位。每種裝置或系統都可以指定自己的乙個型別號,ioctl用這個型別來表示ioctl命令所屬的裝置或驅動。一般用ascii碼字元來表示,如 'a'。

ioctl命令序號,一般8位。對於乙個指定的裝置驅動,可以對它的ioctl命令做乙個順序編碼,一般從零開始,這個編碼就是ioctl命令的序號。

ioctl命令的引數大小,一般14位。ioctl命令號的這個資料成員不是強制使用的,你可以不使用它,但是我們建議你指定這個資料成員,通過它我們可以檢查使用者空間資料的大小以避免錯誤的資料操作,也可以實現相容舊版本的ioctl命令。

我們可以自己來直接指定乙個ioctl命令號,它可能僅僅是乙個整數集,但linux中的ioctl命令號都是有特定含義的,因此通常我們不推薦這麼做。其實linux核心已經提供了相應的巨集來自動生成ioctl命令號:

_io(type,nr)

_ior(type,nr,size)

_iow(type,nr,size)

_iowr(type,nr,size)

巨集_io用於無資料傳輸,巨集_ior用於從裝置讀資料,巨集 _iow用於向裝置寫資料,巨集_iowr用於同時有讀寫資料的ioctl命令。相對的,linux核心也提供了相應的巨集來從ioctl命令號種解碼相應的域值:

_ioc_dir(nr)

_ioc_type(nr)

_ioc_nr(nr)

_ioc_size(nr)

這些巨集都定義在標頭檔案中(一般在標頭檔案中)。一般在使用中,先指定各個ioctl命令的順序編號(一般從0開始),然後根據使用的環境用這些巨集來自動生成ioctl命令號,在後面的例子中你可以了解實際的使用場景。

ioctl函式的返回值是乙個整數型別的值,如果命令執行成功,ioctl返回零,如果出現錯誤,ioctl函式應該返回乙個負值。這個負值會作為errno值反饋給呼叫此ioctl的使用者空間程式。關於返回值的具體含義,請參考和標頭檔案。

這裡有必要說明一下ioctl命令的引數,因為它很容易犯錯誤。如果ioctl命令引數僅僅是乙個整數,那麼事情很簡單了,我們可以在ioctl函式中直接使用它。但如果它是乙個指標資料,那麼使用上就要小心了。首先要說明這個引數是有使用者空間的程式傳遞過來的,因此這個指標指向的位址是使用者空間位址,在linux中,使用者空間位址是乙個虛擬位址,在核心空間是無法直接使用它的。為了解決在核心空間使用使用者空間位址的資料,linux核心提供了以下函式,它們用於在核心空間訪問使用者空間的資料,定義在標頭檔案中:

unsigned long __must_check copy_to_user(void __user *to,

const void *from, unsigned long n);

unsigned long __must_check copy_from_user(void *to,

const void __user *from, unsigned long n);

copy_from_user和copy_to_user一般用於複雜的或大資料交換,對於簡單的資料型別,如int或char,核心提供了簡單的巨集來實現這個功能:

#define get_user(x,ptr)

#define put_user(x,ptr)

其中,x是核心空間的簡單資料型別位址,ptr是使用者空間位址指標。

這裡有必要再一次強調的是,在核心模組或驅動程式的編寫中,我們強烈建議你使用核心提供的介面來生成並操作ioctl命令號,這樣可以對命令號賦予特定的含義,使我們的程式更加的健壯;另一方面也可以提高程式的可移植性。 舉例

好了,是時候舉個例子了。我們將擴充套件我們的helloworld驅動新增ioctl函式。

首先,我們新增乙個標頭檔案來定義ioctl介面需要用到的資料(hello.h):

#ifndef _hello_h

#define _hello_h

#include

#define maxbuf 20

typedef struct _buf_databuf_data;

#define hello_ioctl_nr_base            0

#define hello_ioctl_nr_set_data     (hello_ioctl_nr_base + 1)

#define hello_ioctl_nr_max             (hello_ioctl_nr_get_buff + 1)

#define hello_ioctl_set_data           _ior('h', hello_ioctl_nr_set_data, buf_data*)

#endif

然後為我們的驅動程式新增ioctl介面hello_ioctl,並實現這個函式:

static int hello_ioctl (struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg)

memset(hello_buf, 0, sizeof(hello_buf));

memcpy(hello_buf, buff.data, buff.size);

break;

default:

printk("hello_ioctl: unknown ioctl command (%d)\n", cmd);

break;

}error:

return err;

}static struct file_operations hello_fops = ;

後記到這裡我們已經向您展示了linux核心驅動程式的裝置控制介面(ioctl介面),詳細的介紹了它的使用,並給出了乙個實際的例子,儘管它很簡單,但已經足夠了。到這裡你可以寫出乙個標準的linux驅動程式了。不過這裡還有個問題,那就是我們不得不從/proc/devices檔案裡讀取裝置號然後手動建立裝置節點。我們是否可以讓系統自動的建立這個裝置節點檔案呢?當然可以。不過在那之前,我們必須深入了解linux的裝置驅動模型。後面的章節我們就詳細的介紹linux的裝置驅動模型及hotplug機制。

ioctl引數詳解

本函式影響由fd引數引用的乙個開啟的檔案。include int ioctl int fd,int request,返回0 成功 1 出錯 第三個引數總是乙個指標,但指標的型別依賴於request引數。套介面操作 檔案操作 介面操作 arp快取記憶體操作 路由表操作 流系統類別request 說明資...

ioctl 函式詳解

int ioctl struct inode struct file unsigned int,unsigned long 這是驅動程式裝置控制介面函式 ioctl函式 的核心原型定義,struct inode 和 struct file 描述了操作的檔案,unsigned int 描述了ioctl...

裝置控制介面ioctl詳解

int ioctl struct inode struct file unsigned int,unsigned long 這是驅動程式裝置控制介面函式 ioctl函式 的核心原型定義,struct inode 和struct file 描述了操作的檔案,unsigned int 描述了ioctl命...