歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux高級字符設備驅動

1. Ioctl 用來做什麼?

大部分驅動除了需要具備讀寫設備的能力外,還需要具備對硬件控制的能力。例如,要求設備報告錯誤信息,改變波特率,這些操作常常通過ioctl方法來實現。

1.1 用戶使用方法
在用戶空間,使用ioctl 系統調用來控制設備,原型如下:
         int ioctl(int fd,unsigned long cmd,...)
原型中的點表示這是一個可選的參數,存在與否依賴於控制命令(第2 個參數)是否涉及到與設備的數據交互。

1.2 驅動ioctl方法
        ioctl 驅動方法有和用戶空間版本不同的原型:
        int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
        cmd參數從用戶空間傳下來,可選的參數arg 以一個unsigned long 的形式傳遞,不管它是一個整數或一個指針。如果cmd命令不涉及數據傳輸,則第3 個參數arg的值無任何意義。

2. Ioctl實現
2.1 實現Ioctl方法的步驟:
      1)  定義命令
      2.)  實現命令

2.2 定義命令
        在編寫ioctl代碼之前,首先需要定義命令。為了防止對錯誤的設備使用正確的命令,命令號應該在系統范圍內是唯一的。ioctl 命令編碼被劃分為幾個位段,include/asm/ioctl.h中定義了這些位字段:類型(幻數),序號,傳送方向,參數的大小。Documentation/ioctl-number.txt文件中羅列了在內核中已經使用了的幻數。
        定義ioctl 命令的正確方法是使用4 個位段, 這個列表中介紹的符號定義在<linux/ioctl.h>中:
1) Type
     幻數(類型): 表明哪個設備的命令,在參考了ioctlnumber.txt之後選出,8 位寬。
2)  Number
      序號,表明設備命令中的第幾個,8 位寬
3) Direction
     數據傳送的方向,可能的值是_IOC_NONE(沒有數據傳輸),_IOC_READ, _IOC_WRITE。數據傳送是從應用程序的觀點來看待的,_IOC_READ 意思是從設備讀。
4) Size
用戶數據的大小。(13/14位寬,視處理器而定)

內核提供了下列宏來幫助定義命令:
1)  _IO(type,nr)
    沒有參數的命令
2)  _IOR(type,nr,datatype)
    從驅動中讀數據
3)  _IOW(type,nr,datatype)
   寫數據到驅動
4) _IOWR(type,nr,datatype)
   雙向傳送,type 和number 成員作為參數被傳遞。

定義命令(范例)
#define MEM_IOC_MAGIC ‘m’ //定義幻數
#define MEM_IOCSET
_IOW(MEM_IOC_MAGIC, 0, int)
#define MEM_IOCGQSET
_IOR(MEM_IOC_MAGIC, 1, int)

2.3 Ioctl函數實現
        定義好了命令,下一步就是要實現Ioctl函數了,Ioctl函數的實現包括如下3個技術環節:
       1)  返回值
       2) 參數使用
       3) 命令操作

 

2.3.1 Ioctl函數實現(返回值)
        Ioctl函數的實現通常是根據命令執行的一個switch語句。但是,當命令號不能匹配任何一個設備所支持的命令時,通常返回-EINVAL(“非法參數”)。

2.3..2 Ioctl函數實現(參數)
         如何使用Ioctl中的參數?
         如果是一個整數,可以直接使用。如果是指針,我們必須確保這個用戶地址是有效的,因此使用前需進行正確的檢查。

2.3.3 Ioctl函數實現(參數檢查)
        不需要檢測:
        1) copy_from_user

        2) copy_to_user
        3) get_user
        4) put_user
        需要檢測:
       1) __get_user
       2) __put_user

         int access_ok(int type, const void *addr, unsigned long size)

        第一個參數是VERIFY_READ 或者VERIFY_WRITE,用來表明是讀用戶內存還是寫用戶內存。addr 參數是要操作的用戶內存地址,size 是操作的長度。如果ioctl 需要從用戶空間讀一個整數,那麼size參數等於sizeof(int)。access_ok 返回一個布爾值: 1 是成功(存取沒問題)和0 是失敗(存取有問題),如果該函數返回失敗, 則Ioctl應當返回–EFAULT 。

3. Ioctl函數實現范例

if (_IOC_DIR(cmd) & _IOC_READ)
         err = !access_ok(VERIFY_WRITE, (void __user *)arg,_IOC_SIZE(cmd));   //why _IOC_READ 對應VERIFY_WRITE ???
else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void __user *)arg,_IOC_SIZE(cmd));
if (err)
      return -EFAULT;


switch(cmd)
{
           case MEM_IOCSQUANTUM: /* Set: arg points to the value */
           retval = __get_user(scull_quantum, (int *)arg);
           break;


           case MEM_IOCGQUANTUM: /* Get: arg is pointer to result */
           retval = __put_user(scull_quantum, (int *)arg);
           break;


          default:
          return –EINVAL;
}

Copyright © Linux教程網 All Rights Reserved