第六章 高階字元驅動程式操作

2021-08-02 10:40:44 字數 4538 閱讀 2587

1、ioctl

​除了讀取和寫入裝置之外,大部分驅動程式還需要另外一種能力,即通過裝置驅動程式執行各種型別的硬體控制。

在使用者空間,ioctl系統呼叫有如下原型:

int

ioctl

(intfd,

unsigned

long

cmd,

.../*char *argp*/);

驅動程式的ioctl方法原型與使用者空間的版本不一樣

int

(*ioctl

)(struct

inode

*inode

,struct

file

*filp

,unsigned

intcmd

,unsigned

long

arg);

inode 和 filp 兩個指標對應於應用程式傳遞的檔案描述符fd,這和傳遞給open方法的引數一樣。

引數cmd由使用者空間不經修改的傳遞給驅動程式。

可選的arg引數則無論使用者程式使用的是指標還是整數值,它都以unsigned long的形式傳遞給驅動程式。

大多數的ioctl的實現中都包含乙個switch語句來根據cmd引數選擇對應的操作。

2、選擇ioctl命令

在編寫ioctl**之前,需要選擇對應不同命令的編號。為了防止對錯誤的裝置使用正確的命令,命令號應該在系統範圍內惟一。

為了方便程式設計師建立位移的ioctl命令號,每乙個命令號被分為多個位欄位。

在2.6核心中,命令號被分為了4個字段,定義在中

type

幻數。選擇乙個號碼,並在整個驅動程式中使用這個號碼。這個欄位有8位寬(_ioc_typebits)

number

序數(順序編號)。也是8位寬(_ioc_nrbits).

direction

如果命令涉及到資料的傳輸,則該字元段定義資料的傳輸方向。 可用的值有_ioc_none、_ioc_read、_ioc_write以及_ioc_read | _ioc_write .注意這個字段乙個掩碼,可以用邏輯and操作從中分解出

_ioc_read 和 _ioc_write

size

所涉及的使用者資料大小。這個欄位的寬度與使用者體系結構有關,通常是13位或者14位。具體可以通過_ioc_sizebits找到針對特定體系結構的具體數值。 、

在中包含的標頭檔案定義了一些構造命令編號的巨集:

_io

(type,nr

)//用於構造無引數的命令編號

_ior

(type,nr

,datatype

)//用於構造從驅動程式中讀取資料的命令編號

_iow

(type,nr

,datatype

)//用於寫入資料的命令

_iowr

(type,nr

,datatype

)//用於雙向傳輸

ioctl命令長32bit。分為上述的四個段,具體的分布如下:

dir(2)   size(14)   type(8)   nr(8)

另外的一些巨集:

_ioc_dir(nr

)//判斷nr的方向

_ioc_type(nr

)//判斷nr的型別

_ioc_nr(nr

)//判斷nr的序數

_ioc_size(nr

)//判斷nr的大小

3、使用ioctl引數

​在前面的ioctl中有乙個附加的引數

argp。如果這個是個整數,那就直接使用就可以了。但是如果是乙個指標的話,就需要注意問題了。

如果這個指標是指向使用者空間的話,就必須確保指向的使用者空間是合法的。驅動程式應該負責對每個用到的使用者空間位址作適當的檢查,如果是非法位址則返回乙個錯誤。

在中宣告的access_ok函式是用來驗證位址的。

int

access_ok

(int

type

,const

void

*addr

,unsigned

long

size

);

/* 第乙個引數應該是verify_read或verify_write,取決於要執行的動作是

讀取還是寫入

使用者空間

的記憶體。

注意,verify_write 是 verify_read 的超集 -- 如果可以安全的寫記憶體塊,那麼自然也總能讀到記憶體塊

addr引數是乙個使用者空間位址,size是位元組數。*/

返回值:

此函式檢查使用者空間中的記憶體塊是否可用。如果可用,則返回真(非0值),否則返回假 (0) 。

除了copy_from_user和copy_to_user之外,還可以使用已經為最常用的資料大小1(1、2、4、8位元組)優化過的一組函式。

#include

put_user

(datum

.ptr

)

__put_user

(datum

,ptr

)

get_user

(local

,ptr

)

__get_user

(local

,ptr

)

put_user  和 __put_user兩個巨集的作用是把datum寫到使用者空間。

它們相對比較快,當要傳遞單個資料時,應該用這些巨集,而不是用copy_to_user。由於這些巨集不進行型別檢查,所以可以傳遞給put_user任意型別的指標,只要是乙個使用者空間位址指標就行。傳遞的資料大小依賴於ptr引數的型別,在編譯時由編譯器的內建指令sizeof和typeof確定。總之,如果ptr是乙個字元指標,就傳遞1個位元組。

put_user進行檢查以確保程序可以寫入指定的記憶體位址,並在成功時返回0,出錯時返回-efault。__put_user做的檢查少些(它不呼叫access_ok)。如果指向使用者不能寫入的記憶體時,就會出現操作失敗。因此__put_user應該在已經使用access_ok檢驗過的記憶體區後再使用。

get_user 和 __get_user 用於從使用者空間接收乙個資料。除了傳輸方向相反之外,它們與put_user和__put_user差不多。接收的數值被儲存在local中,返回值指明了操作是否成功。

4、權能和受限操作

全部權能操作都可以在中找到,其中包含了系統能夠理解的所有權能;不修改核心源**,驅動程式作者或系統管理員就無法定義新的權能。

cap_dac_override

/* 越過檔案或目錄的訪問限制(資料訪問控制或dac)的能力 */

cap_net_admin

/* 執行網路管理任務的能力, 包括那些能影響網路介面的任務 */

cap_sys_module

/* 載入和解除安裝核心模組的能力 */

cap_sys_rawid

/* 執行「裸」io操作的能力 */

cap_sys_admin

/* 截獲的能力,他提供了訪問許多系統管理操作的途徑 */

cap_sys_tty_config

/* 執行tty配置任務的能力 */

在執行一項特權操作之前,裝置驅動程式應該檢查呼叫程序是否有合適的權能。權能的檢查通過capable函式實現(定義在中)

int

capable

(int

capability

);

/* 有相應的權能時返回真,沒有相應權能時返回假 */。

第六章 高階字元驅動程式操作

ioctl 大多數ioctl 的實現中都包括乙個 switch 語句來根據 cmd引數選擇對應的操作。使用者空間,ioctl 原型如下 int ioctl int fd,unsigned long cmd,最後省略號一般表示可變引數,但在實際系統中,系統呼叫不會真正的使用可變數目的引數。它只是為了在...

第六章 字元裝置

記錄一下 建立乙個簡單的字元裝置的編碼過程 前提準備 已經編譯好的linux核心 進入.drivers char 目錄 這裡存放著這字元裝置驅動 mkdir globalmem 建立乙個我們新建驅動的目錄並進入 新建globalmem.c檔案,清單如下 include include include...

第六章 LCD驅動移植

6.1 認識lcd相關硬體原理 lcd顯示屏相關引數,如何設定引數,如何根據型號編寫驅動 6.1.1 概述 0 顯示漢字,字元,圖形 低壓,低功耗,體積小,重量輕,超薄 1 根據物理結構 扭曲向列型 tn lcd 超扭曲向列型 stn lcd 雙層超扭曲向列型 dstn lcd 薄膜電晶體型 tft...