linux裝置驅動學習(七)

來源:互聯網
上載者:User

進階字元裝置驅動

一、ioctl大部分裝置除了讀寫能力,還可進行超出簡單的資料轉送之外的操作,所以裝置驅動也必須具備進行各種硬體控制操作的能力. 這些操作常常通過 ioctl 方法來支援,它有和使用者空間版本不同的原型:

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

需要注意的是:不管可選的參數arg是否由使用者給定為一個整數或一個指標,它都以一個unsigned long的形式傳遞。如果調用程式不傳遞arg參數, 被驅動收到的 arg 值是未定義的。因為在arg參數上的類型檢查被關閉了,所以若一個非法參數傳遞給 ioctl,編譯器是無法警示的,且任何關聯的錯誤難以尋找.

選擇ioctl命令

為了防止向錯誤的裝置使用正確的命令,命令號應該在系統範圍內唯一。為方便程式員建立唯一的 ioctl 命令代號, 每個命令號被劃分為多個位欄位。要按 Linux 核心的約定方法為驅動選擇 ioctl 的命令號, 應該首先看看 include/asm/ioctl.h 和 Documentation/ioctl-number.txt。 要使用的位欄位符號定義在 <linux/ioctl.h> :

type(幻數):8 位寬(_IOC_TYPEBITS),參考ioctl-number.txt選擇一個數,並在整個驅動中使用它。

number(序數):順序編號,8 位寬(_IOC_NRBITS)。

direction(資料傳送的方向):可能的值是 _IOC_NONE(沒有資料轉送)、_IOC_READ、 _IOC_WRITE和 _IOC_READ|_IOC_WRITE (雙向傳輸資料)。該欄位是一個位元遮罩(兩位), 因此可使用 AND 操作來抽取_IOC_READ 和 _IOC_WRITE。

size(資料的大小):寬度與體繫結構有關,ARM為14位.可在宏 _IOC_SIZEBITS 中找到特定體系的值. 

 <linux/ioctl.h> 中包含的 <asm/ioctl.h>定義了一些構造命令編號的宏:

_IO(type,nr)/*沒有參數的命令*/
_IOR(type, nr, datatype)/*從驅動中讀資料*/
_IOW(type,nr,datatype)/*寫資料*/
_IOWR(type,nr,datatype)/*雙向傳送*/
/*type 和 number 成員作為參數被傳遞, 並且 size 成員通過應用 sizeof 到 datatype 參數而得到*/

這個標頭檔還定義了用來解開這個欄位的宏:

_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)

具體的使用方法在實驗中展示。轉自:http://blog.chinaunix.net/u1/34474/showart.php?id=411534

為了建立唯一的ioctl操作符,每一個命令號被分為多個位欄位。使用16位整數:高8位為與裝置相關的幻數,低8位是一個序號碼,在裝置內事唯一的。

ioctl傳回值:

POSIX 標準規定:如果使用了不合適的 ioctl 命令號,應當返回-ENOTTY 。這個錯誤碼被 C 庫解釋為"不合適的裝置 ioctl。然而,它返回-EINVAL仍是相當普遍的。

預定義命令

有一些ioctl命令是由核心識別的,當這些命令用於自己的裝置時,他們會在我們自己的檔案操作被調用之前被解碼. 因此, 如果你選擇一個ioctl命令編號和系統預定義的相同時,你永遠不會看到該命令的請求,而且因為ioctl 號之間的衝突,應用程式的行為將無法預測。預定義命令分為 3 類:

(1)用於任何檔案(常規, 裝置, FIFO和socket) 的命令

(2)只用於常規檔案的命令

(3)特定於檔案系統類型的命令 

下列 ioctl 命令是預定義給任何檔案,包括裝置特定檔案:

FIOCLEX :設定 close-on-exec 標誌(File IOctl Close on EXec)。
FIONCLEX :清除 close-no-exec 標誌(File IOctl Not CLose on EXec)。
FIOQSIZE :這個命令返回一個檔案或者目錄的大小; 當用作一個裝置檔案, 但是, 它返回一個 ENOTTY 錯誤。
FIONBIO:"File IOctl Non-Blocking I/O"(在"阻塞和非阻塞操作"一節中描述)。 
在應用設定自己的IOCTL命令時,要盡量避免與上述命令衝突。
ioctl參數:當使用ioctl命令中的arg參數時,如果它是個整數則可以直接使用,如果是個指標,則需要一些問題。當用一個指標指向使用者空間時,必須確保所指向的使用者空間時合法的。對未驗證的使用者空間訪問將導致核心oops、系統崩潰等問題。驅動程式應該負責對每個用到的使用者空間地址做檢查。
int access_ok(int type,const void *addr,unsigned long size);
可以通過上面的函數acces_ok來驗證地址(而不傳輸資料)。聲明在<asm/uaccess.h>
type參數應該是VERIFY_READ或VERIFY_WRITE,取決於要執行的是讀取還是寫入使用者空間記憶體區。如果既要讀又要寫則用VERIFY_WRITE。addr是一個使用者空間的地址,size是位元組數。例如,如果ioctl要從使用者空間讀一個整數,size為sizeof(int).
access_ok返回一個布爾值,1表示成功,0表示失敗,返回-EFAULT給調用者。
access_ok注意兩點:1:它並沒有完成驗證記憶體的全部工作,而只檢查了所引用的記憶體是不是位於進程有對應存取權限的地區內,特比是確保地址沒有指向核心空間的記憶體區。2.大多數驅動程式並不調用access_ok,因為記憶體管理函數會處理它。
int err;
//測試類型和編號為欄位,並拒絕錯誤的命令號
//在調用access_ok之前,返回ENOTTY(不恰當的ioctl),早期ioctl主要用來實現對終端操作,所以失敗返回標準為:-ENOTTY
if(_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)  return  -ENOTTY;
if(_IOC_NR(cmd)  >  SCULL_IOC_MAXNR)   return -ENOTTY;
這兩部說明了前邊定義的關於幻術,方向,編號等的作用,這裡通過對傳入的cmd進行測試,對cmd進行唯一的標示。
//方向位是一個掩碼,所以需要做位操作,ioctl中的direction欄位是針對使用者層來看的,而access_ok是針對核心來看的,所以在方向上正好相反。
if(_IOC_DIR(cmd) & _IOC_READ)
       err  = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
if(_IOC_DIR(cmd) & _IOC_WRITE)
       err = !access_ok(VERIFY_READ, (void __user *)arg,_IOC_SIZE(cmd));
if(err)   return -EFAULT;

put_user(datum,ptr);

__put_user(datum,ptr);//把datum寫到使用者空間

get_user(local,ptr)

__get_user(local,ptr);//從使用者空間接受一個資料

上面的宏用於當要傳遞單個資料時,應該用這些函數而不是copy系列。

 

__put_user(datum,ptr)和__get_user(local,ptr) 進行更少的檢查(不調用 access_ok), 但是仍然能夠失敗如果被指向的記憶體對使用者是不可寫的,所以他們應只用在記憶體區已經用 access_ok 檢查過的時候。作為通用的規則:當實現一個 read 方法時,調用 __put_user 來節省幾個周期, 或者當你拷貝幾個項時,在第一次資料傳送之前調用 access_ok 一次。

在調用上述的函數傳遞大小不符合任意一個特定值的數值時,會出現:convesiob to non_scale type requesed,這是必須使用copy_from_user或copy_to_user

權能與受限操作

Linux 核心提供了一個更加靈活的系統, 稱為權能(capability)。核心專為許可管理上使用權能並匯出了兩個系統調用 capget 和 capset,這樣可以從使用者空間管理權能,其定義在 <linux/capability.h> 中。對裝置驅動編寫者有意義的權能如下:

CAP_DAC_OVERRIDE /*越過在檔案和目錄上的訪問限制(資料存取控制或 DAC)的能力。*/

CAP_NET_ADMIN /*進行網路管理工作的能力, 包括那些能夠影響網路介面的任務*/

CAP_SYS_MODULE /*載入或去除核心模組的能力*/

CAP_SYS_RAWIO /*進行 "raw"(裸)I/O 操作的能力. 例子包括存取裝置連接埠或者直接和 USB 裝置通訊*/

CAP_SYS_ADMIN /*截獲的能力, 提供對許多系統管理操作的途徑*/

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

在進行一個特權操作之前, 一個裝置驅動應當檢查調用進程有合適的能力,檢查是通過 capable 函數來進行的(定義在 <linux/sched.h> )scull的ioctl實現了在必要時檢查使用者的特權層級:

if (! capable (CAP_SYS_ADMIN))
 return -EPERM;

 

ioctl命令的實現:

switch(cmd){

           case  SCULL_IOCRTSET:            

                   scull_qset = SCULL_QSET;

                   scull_quantum = SCULL_AUANTUM;

                   break;

            case SCULL_IOCSQUANTUM:      通過指標進行設定

                   if(!capible(CAP_SYS_ADMIN)) //對於使用者程式來說,使用者可以任意讀取quantum和qset的值,但如果使用者層要修改這些值則要進行許可權檢查

                           return -EPERM;

                   retval = __get_user(scull_quantum,(int __user *)arg); //與使用者空間進行互動,通過指標設定。

                   break;

            case SCULL_IOCTQUANTUM:        通過參數直接設定

                   if(!capilble(CAP_SYS_ADMIN))

                            return -EPERM;

                   reval = arg;

                   break;

            case SCULL_IOCGQUANTUM:      通過指標擷取

                   retval = __put_user(scull_quantum,(int __user *)arg); // 向使用者空間寫不需要檢查

                   break;

            case SCULL_IOCQQUANTUM:       通過傳回值擷取

                   return scull_quantum;    //傳回值 這裡沒有break

            case  SCULL_IOCXQUANTUM:        通過指標交換

                   if(!capible(CAP_SYS_ADMIN)

                             return -EPERM;

                   tmp = scull_quantum;

                   retval = __get_user(scull_quantum,(int __user *)arg);

                   i f(retval == 0)

                         retval = __put_user(tmp,(iint __user *)arg);

                   break;

             default:

                   return -ENOTTY;

}

在使用者空間的調用過程如下:

int quantum;

ioctl(fd,SCULL_IOCSQUANTUM,&quantum);

ioctl(fd,SCULL_IOCTQUANTUM,quantum);

ioctl(fd,SCULL_IOCGQUANTM,&quantum);

quantum = ioctl(fd,SCULL_IOCQQUANTUM);

ioctl(fd,SCULL_IOCXQUANTUM,&quantum);

通過傳回值設定的時候,應用程式中都應該提供相應介面。

 

 

            

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.