linux ioctl函數及其驅動和應用程式注意事項

來源:互聯網
上載者:User
 在mtd-util的flash_eraseall中發現這樣的用法:
----------------------------------------------------------------
#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
......
ioctl(fd, MEMGETINFO, &meminfo)
----------------------------------------------------------------
找了一下解釋如下:
對於系統支援裝置的ioctl號,你可以在/usr/include下面的標頭檔中找到,對於你自己的裝置,如果需要使用ioctl介面,則需要定義自己的ioctl號。以前的2.4中有個問題是,大家都隨便定義自己的ioctl號,造成很大可能性的重複性。一個壞處是難以管理,另外一個是容易造成錯誤,例如如果使用者本來希望開啟一個串口裝置,結果通過open開啟了網口,如果串口的某個ioctl號正好是網口的關閉操作,這樣就會造成錯誤。在2.6裡面,你定義自己的ioctl號最好使用_IO,   _IOR,   _IOW和_IORW來定義,這些宏考慮了第三個參數的長度,裝置的magic number,以及操作的方向等,避免了2.4中的問題

在驅動程式裡, ioctl() 函數上傳送的變數 cmd 是應用程式用於區別裝置驅動程式請求處理內容的值。cmd除了可區別數字外,還包含有助於處理的幾種相應資訊。 cmd的大小為 32位,共分 4 個域:

bit31~bit30 2位為 “區別讀寫” 區,作用是區分是讀取命令還是寫入命令。

bit29~bit15 14位為 "資料大小" 區,表示 ioctl() 中的 arg 變數傳送的記憶體大小。

bit20~bit08  8位為 “魔數"(也稱為"幻數")區,這個值用以與其它裝置驅動程式的 ioctl 命令進行區別。

bit07~bit00   8位為 "區別序號" 區,是區分命令的命令順序序號。

像命令碼中的 “區分讀寫區” 裡的值可能是 _IOC_NONE (0值)表示無資料轉送,_IOC_READ (讀), _IOC_WRITE (寫) , _IOC_READ|_IOC_WRITE (雙向)。

核心定義了 _IO() , _IOR() , IOW() 和 _IOWR() 這 4 個宏來輔助產生上面的 cmd 。下面分析 _IO() 的實現,其它的類似。

在 asm-generic/ioctl.h 裡可以看到 _IO() 的定義
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)

再看 _IOC() 的定義

#define _IOC(dir,type,nr,size) \
    (((dir)  << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr)   << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

可見,_IO() 的最後結果由 _IOC() 中的 4 個參數移位組合而成。

再看 _IOC_DIRSHIT 的定義

#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)

_IOC_SIZESHIFT 的定義

#define _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)

_IOC_TYPESHIF 的定義

#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)

_IOC_NRSHIFT 的定義

#define _IOC_NRSHIFT    0

_IOC_NRBITS 的定義

#define _IOC_NRBITS    8

_IOC_TYPEBITS 的定義

#define _IOC_TYPEBITS    8

由上面的定義,往上推得到

引用 _IOC_TYPESHIFT = 8

_IOC_SIZESHIFT = 16

_IOC_DIRSHIFT = 30

所以,(dir)  << _IOC_DIRSHIFT) 表是 dir 往左移 30 位,即移到 bit31~bit30 兩位上,得到方向(讀寫)的屬性;
            (size) << _IOC_SIZESHIFT) 位左移 16 位得到“資料大小”區;
            (type) << _IOC_TYPESHIFT) 左移 8位得到"魔數區" ;
           (nr)   << _IOC_NRSHIFT)      左移 0 位( bit7~bit0) 。

這樣,就得到了 _IO() 的宏值。

關於IOCTL驅動的編寫方法LDD這本書確實寫的比較明白了,在這呢我就簡單的做一個介紹。這裡我主要描述自己編寫IOCTL驅動時所遇到的問題及其原因。
    驅動裡的ioctl函數主要實現不用read,write函數的與使用者空間的簡單資料互動及無參數的命令控制。那麼我們如何?這幾種功能的IOCTL函數呢?ioctl驅動中以SWITCH{case A,case B}結構以實現對不同命令的響應,首先我們要對我們要使用的“A”,“B”命令定義一個整個作業系統內唯一的標識,同時這個標識又能夠表明我們的操作類型及傳遞參數的類型(如果需要的話)。linux系統的每一個命令號被分為多個位欄位,這些位欄位包括type,number,direction,size,分別表示幻數(與裝置相關的一個字母,以避免與核心衝突),序數(命令編號),方向位(以使用者空間為參照的讀,寫和無資料轉送),size(傳遞參數類型)。核心中/include/asm/ioctl.h,/Documentation/ioctl-number.txt兩個檔案表明了我們應該如何定義ioctl命令編號及自訂的幻數,。
    看起來構造IOTCL命令這麼複雜,慶幸的是核心已經為我們編寫了構造命令編號的宏命令。_IO(type,nr),_IOR(type,nr,datatype),_IOW(type,nr,datatype)。它們在<linux/ioctl.h>包含的<asm/ioctl.h>中有定義,所以在我們的驅動中應包含這個標頭檔。其中_IO()用做無參數的命令編號,_IOR()用做從驅動中讀取資料的命令編號,_IOW()用做寫入資料命令。 LDD中用:define SCULL_IOC_MAGIC 'k' ,define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,1,int)表示SCULL_IOCSQUANTUM命令編號為向驅動中寫資料,命令編號為1,傳送參數類型為int。

這裡把我所寫的部分代碼貼出來:
 驅動程式部分代碼:
 #define MOTOR_MAGIC  'k'
 #define SET_PULSE _IOW(MOTOR_MAGIC,  1,int)
  .
  .
  static int motor_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{      int data=0;
      int retval;
      switch (cmd) {
      case SET_PULSE:
              if (copy_from_user(&data, (int *)arg, sizeof(int)))
               return -EFAULT;
             .
             .
             .
}
   
應用程式部分代碼: 
    #define MOTOR_MAGIC  'k'
    #define SET_PULSE _IOW(MOTOR_MAGIC,  1,int)
    int pulse,fileno;
    fileno = open("/tmp/usb",O_RDWR);
    if (fileno == -1) {
        printf("open device key error!\n");
        return 0;
    }
       .
       .
       printf("Please input the pulse:\n"
       scanf("%d",pulse); 
       if(ioctl(fileno,SET_PULSE,pulse)<0){
          perror("ioctl error");
          exit(1);
       }      
        .
        .
  這裡我們一定要注意的是data 和pulse的資料類型一定要與SET_PULSE _IOW(MOTOR_MAGIC,  1,int)中定義的int類型嚴格一致,即使是unsigned int 和int類型之間也是有很大差別的,我就是因為設定了unsigned int類型的data和pulse卻使用SET_PULSE _IOW(MOTOR_MAGIC,  1,int)搞了我兩三天才搞明白。 再有就是ioctl函數的聲明一定要與 static int motor_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);相一致,我剛開始就是定義錯了,也是搞了我好長時間才明白。因為核心非常的相信你,它不會檢查你的IOTCL定義有沒有問題,它只負責把你的IOCTL函數映射到驅動的ioctl操作上。其中inode和filp對應應用程式傳遞的檔案描述符,這和傳遞給open方法的參數一樣,參數cmd 由使用者空間不經修改的傳遞給驅動程式,可選的arg參數無論使用者空間傳遞的是指標還是值,它都以unsigned long的形式傳遞給驅動程式。

  還有就是我在驅動中明明是用的是使用地址的copy_from_usr函數,但使用者程式卻只能傳遞值.搞了半天最後才知道,原來我的使用者程式中的scanf(x),'x'沒有加'&'符號,所以這時從終端把讀到的資料寫到了'x'所代表的地址處,也就是說現在'x'所代表的地址正是所讀入的資料.呵呵,總結出的經驗就是----->對庫函數的參數類型一定要搞清楚.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.