ioctl設備控制(筆記)

用戶層:
原型:int ioctl(int fd, unsigned long cmd, ...)
說明:其中原點表示可選參數,存在與否依賴於控制命令(第二個參數)是否涉及到與設備的數據交互;



驅動層:
原型:int
說明:cmd參數是從用戶層傳下來,可選參數arg以一個unsigned long 的形式傳遞(爲一個整數或爲一

個指針),如果cmd命令不涉及數據傳輸,則arg無任何意義;

用法步驟:
1、定義命令
ioctl命令編碼劃分爲幾個階段,在include/asm/ioctl.h中定義:類型(幻數)、序號、傳遞方向、參數

的大小。在documention/ioctl-number.txt中羅列了在內核中已經使用的幻數。
其中:
type:幻數(類型):表明哪個設備的命令,8位寬;
number:序號:表面設備命令中的第幾個,8位寬;
direction:數據傳遞的方向:常見的宏定義有:_IOC_NONE(沒有數據傳送),_IOC_READ(從設備

讀),_IOC_WRITE(從設備寫),數據傳送是從應用層的觀點來看待;
size:用戶數據的大小。(13/14位寬,視處理器而定)

_IO(type, nr)    //沒有參數的命令
_IOR(type, nr, datatype)     //從驅動中讀數據
_IOW(type, nr, datatype)     //寫數據到驅動
_IOWR(type, nr, datatype)     //雙向傳輸,type和number成員作爲參數被傳遞

定義命令範例:
#define MEM_IOC_MAGIC     'm'

#define MEM_IOCSET    _IOW(MEM_IOC_MAGIC, 0, int)

#define MEM_IOCGQSET    _IOW(MEM_IOC_MAGIC, 1, int)

2、實現ioctl
包括三個環節:1、返回值  2,參數使用   3命令操作
注:當命令號不能匹配任何一個設備所支持的命令時,通常返回-EINVAL (非法參數)

參數檢查:
如果是一個整數,可以直接使用,如果是指針,首先得確保用戶地址的有效性;
不需要檢測的有:
copy_form_user, copy_to_user, get_user, put_user;
需要檢測的有:__get_user, __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 是失敗(存取有問題), 如果該函數返回失

敗,則返回-EFAULT;

參數檢查範例:
if(_IOC_DIR(cmd) & _IOC_READ)
   err = !access_ok(VERIFY_WRITE, (void __user*)arg, _IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
   err = !access_ok(VERIFY_WREAD, (void __user*)arg, _IOC_SIZE(cmd));
if(err)

    return -EFAULT;


===================================

範例:

用戶層:

cmd = MEMDEV_IOCPRINT;
	if (ioctl(fd, cmd, &arg) < 0)
        {
        	printf("Call cmd MEMDEV_IOCPRINT fail\n");
        	return -1;
	}
	
	
	/* 調用命令MEMDEV_IOCSETDATA */
	printf("<--- Call MEMDEV_IOCSETDATA --->\n");
	cmd = MEMDEV_IOCSETDATA;
	arg = 2007;
	if (ioctl(fd, cmd, &arg) < 0)
        {
        	printf("Call cmd MEMDEV_IOCSETDATA fail\n");
        	return -1;
	}

	
	/* 調用命令MEMDEV_IOCGETDATA */
	printf("<--- Call MEMDEV_IOCGETDATA --->\n");
	cmd = MEMDEV_IOCGETDATA;
	if (ioctl(fd, cmd, &arg) < 0)
        {
        	printf("Call cmd MEMDEV_IOCGETDATA fail\n");
        	return -1;
	}

驅動層:

int memdev_ioctl(struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{

    int err = 0;
    int ret = 0;
    int ioarg = 0;
    
    /* 檢測命令的有效性 */
    if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) 
        return -EINVAL;
    if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) 
        return -EINVAL;

    /* 根據命令類型,檢測參數空間是否可以訪問 */
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (err) 
        return -EFAULT;

    /* 根據命令,執行相應的操作 */
    switch(cmd) {

      /* 打印當前設備信息 */
      case MEMDEV_IOCPRINT:
      	printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
        break;
      
      /* 獲取參數 */
      case MEMDEV_IOCGETDATA: 
        ioarg = 1101;
        ret = __put_user(ioarg, (int *)arg);
        break;
      
      /* 設置參數 */
      case MEMDEV_IOCSETDATA: 
        ret = __get_user(ioarg, (int *)arg);
        printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
        break;

      default:  
        return -EINVAL;
    }
    return ret;

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章