裝置控制介面ioctl詳解

2022-08-01 00:27:15 字數 3497 閱讀 1858

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 函式)

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

ioctl裝置控制 筆記

使用者層 原型 int ioctl int fd,unsigned long cmd,說明 其中原點表示可選引數,存在與否依賴於控制命令 第二個引數 是否涉及到與裝置的資料互動 驅動層 原型 int 說明 cmd引數是從使用者層傳下來,可選引數arg以乙個unsigned long 的形式傳遞 為乙...

ioctl 字元裝置的控制技術

字元裝置的控制 1.字元裝置控制理論 1.1 作用 大部分驅動程式除了需要提供讀寫裝置的能力外,還需要具備控制裝置的能力。比如 改變波特率 1.2 應用程式介面 在使用者空間,使用ioctl系統呼叫來控制裝置,原型如下 int ioctl int fd,unsigned long cmd,fd 要控...