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...