新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux驅動總結3

        Linux驅動總結3

        作者: 時間:2016-12-01 來源:網絡 收藏
        學習了驅動程序的設計,感覺在學習驅動的同時學習linux內核,也是很不錯的過程哦,做了幾個實驗,該做一些總結,只有不停的作總結才能印象深刻。
        我的平臺是虛擬機,fedora14,內核版本為2.6.38.1.其中較之前的版本存在較大的差別,具體的實現已經在上一次總結中給出了。今天主要總結的是ioctl和堵塞讀寫函數的實現。
        一、ioctl函數的實現
        首先說明在2.6.36以后ioctl函數已經不再存在了,而是用unlocked_ioctl和compat_ioctl兩個函數實現以前版本的ioctl函數。同時在參數方面也發生了一定程度的改變,去除了原來ioctl中的struct inode參數,同時改變了返回值。
        但是驅動設計過程中存在的問題變化并不是很大,同樣在應用程序設計中我們還是采用ioctl實現訪問,而并不是unlocked_ioctl函數,因此我們還可以稱之為ioctl函數的實現。
        ioctl函數的實現主要是用來實現具體的硬件控制,采用相應的命令控制硬件的具體操作,這樣就能使得硬件的操作不再是單調的讀寫操作。使得硬件的使用更加的方便。
        ioctl函數實現主要包括兩個部分,首先是命令的定義,然后才是ioctl函數的實現,命令的定義是采用一定的規則。
        ioctl的命令主要用于應用程序通過該命令操作具體的硬件設備,實現具體的操作,在驅動中主要是對命令進行解析,通過switch-case語句實現不同命令的控制,進而實現不同的硬件操作。
        ioctl函數的命令定義方法:
        int (*unlocked_ioctl)(struct file*filp,unsigned int cmd,unsigned long arg)
        雖然其中沒有指針的參數,但是通常采用arg傳遞指針參數。cmd是一個命令。每一個命令由一個整形數據構成(32bits),將一個命令分成四部分,每一部分實現具體的配置,設備類型(幻數)8bits,方向2bits,序號8bits,數據大小13/14bits。命令的實現實質上就是通過簡單的移位操作,將各個部分組合起來而已。
        一個命令的分布的大概情況如下:
        |---方向位(31-30)|----數據長度(29-16)----------------|---------設備類型(15-8)------|----------序號(7-0)----------|
        |----------------------------------------------------------------------------------------------------------------------------------------|
        其中方向位主要是表示對設備的操作,比如讀設備,寫設備等操作以及讀寫設備等都具有一定的方向,2個bits只有4種方向。
        數據長度表示每一次操作(讀、寫)數據的大小,一般而已每一個命令對應的數據大小都是一個固定的值,不會經常改變,14bits說明可以選擇的數據長度最大為16k。
        設備類型類似于主設備號(由于8bits,剛好組成一個字節,因此經常采用字符作為幻數,表示某一類設備的命令),用來區別不同的命令類型,也就是特定的設備類型對應特定的設備。序號主要是這一類命令中的具體某一個,類似于次設備號(256個命令),也就是一個設備支持的命令多達256個。
        同時在內核中也存在具體的宏用來定義命令以及解析命令。
        但是大部分的宏都只是定義具體的方向,其他的都需要設計者定義。
        主要的宏如下:
        #include
        _IO(type,nr) 表示定義一個沒有方向的命令,
        _IOR(type,nr,size) 表示定義一個類型為type,序號為nr,數據大小為size的讀命令
        _IOW(type,nr,size) 表示定義一個類型為type,序號為nr,數據大小為size的寫命令
        _IOWR(type,nr,size) 表示定義一個類型為type,序號為nr,數據大小為size的寫讀命令
        通常的type可采用某一個字母或者數字作為設備命令類型。
        是實際運用中通常采用如下的方法定義一個具體的命令:
        //頭文件
        #include
        /*定義一系列的命令*/
        /*幻數,主要用于表示類型*/
        #define MAGIC_NUM k
        /*打印命令*/
        #define MEMDEV_PRINTF _IO(MAGIC_NUM,1)
        /*從設備讀一個int數據*/
        #define MEMDEV_READ _IOR(MAGIC_NUM,2,int)
        /*往設備寫一個int數據*/
        #define MEMDEV_WRITE _IOW(MAGIC_NUM,3,int)
        /*最大的序列號*/
        #define MEM_MAX_CMD 3
        還有對命令進行解析的宏,用來確定具體命令的四個部分(方向,大小,類型,序號)具體如下所示:
        /*確定命令的方向*/
        _IOC_DIR(nr)
        /*確定命令的類型*/
        _IOC_TYPE(nr)
        /*確定命令的序號*/
        _IOC_NR(nr)
        /*確定命令的大小*/
        _IOC_SIZE(nr)
        上面的幾個宏可以用來命令,實現命令正確性的檢查。
        ioctl的實現過程主要包括如下的過程:
        1、命令的檢測
        2、指針參數的檢測
        3、命令的控制switch-case語句
        1、命令的檢測主要包括類型的檢查,數據大小,序號的檢測,通過結合上面的命令解析宏可以快速的確定。
        /*檢查類型,幻數是否正確*/
        if(_IOC_TYPE(cmd)!=MAGIC_NUM)
        return -EINVAL;
        /*檢測命令序號是否大于允許的最大序號*/
        if(_IOC_NR(cmd)> MEM_MAX_CMD)
        return -EINVAL;
        2、主要是指針參數的檢測。指針參數主要是因為內核空間和用戶空間的差異性導致的,因此需要來自用戶空間指針的有效性。使用copy_from_user,copy_to_user,get_user,put_user之類的函數時,由于函數會實現指針參量的檢測,因此可以省略,但是采用__get_user(),__put_user()之類的函數時一定要進行檢測。具體的檢測方法如下所示:
        if(_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
        else if(_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
        if(err)/*返回錯誤*/
        return -EFAULT;
        當方向是讀時,說明是從設備讀數據到用戶空間,因此要檢測用戶空間的指針是否可寫,采用VERIFY_WRITE,而當方向是寫時,說明是往設備中寫數據,因此需要檢測用戶空間中的指針的可讀性VERIFY_READ。檢查通常采用access_ok()實現檢測,第一個參數為讀寫,第二個為檢測的指針,第三個為數據的大小。
        3、命名的控制:
        命令的控制主要是采用switch和case相結合實現的,這于window編程中的檢測各種消息的實現方式是相同的。
        /*根據命令執行相應的操作*/
        switch(cmd)
        {
        case MEMDEV_PRINTF:
        printk("<--------CMD MEMDEV_PRINTF Done------------>");
        ...
        break;
        case MEMDEV_READ:
        ioarg = &mem_devp->data;
        ...
        ret = __put_user(ioarg,(int *)args);
        ioarg = 0;
        ...
        break;
        case MEMDEV_WRITE:
        ...
        ret = __get_user(ioarg,(int *)args);
        printk("<--------CMD MEMDEV_WRITE Done ioarg = %d--------->",ioarg);
        ioarg = 0;
        ...
        break;
        default:
        ret = -EINVAL;
        printk("<-------INVAL CMD--------->");
        break;
        }
        這只是基本的框架結構,實際中根據具體的情況進行修改。這樣就實現了基本的命令控制。
        上一頁 1 2 3 下一頁

        關鍵詞: Linux驅動總

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 九龙县| 伊宁县| 大洼县| 万安县| 都江堰市| 宜丰县| 富蕴县| 黑山县| 清流县| 冀州市| 达拉特旗| 定陶县| 黄冈市| 云阳县| 灌阳县| 那坡县| 江门市| 峡江县| 潜山县| 平安县| 卓尼县| 溆浦县| 兴隆县| 纳雍县| 依兰县| 吴堡县| 崇左市| 利辛县| 曲靖市| 彭泽县| 洪江市| 和林格尔县| 喀什市| 兴城市| 象州县| 沙坪坝区| 甘德县| 晋州市| 依安县| 东乌珠穆沁旗| 新龙县|