日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

《Linux设备驱动程序》学习2—高级字符设备驱动ioctl

發布時間:2024/9/21 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Linux设备驱动程序》学习2—高级字符设备驱动ioctl 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
今天進入《Linux設備驅動程序》第六章高級字符設備驅動程序操作的學習,學習的過程和簡單字符設備驅動程序的學習是一樣的,看書,看程序,然后就是看Tek的博客筆記。依然tek的博客中對于這一部分的知識點概括的很詳細了,所以我依舊談談對這一部分自己的理解體會。 ?? ?總的來說這一塊雖然叫做高級字符設備驅動程序操作,涉及的知識點特別是函數比簡單字符設備驅動少多了,但是僅僅是考慮這一點的話那就錯了。前面的簡單字符設備驅動函數的種類雖然比較多,但是它是有限的,可以說基本上就是那幾個系統調用在驅動上的實現。相對于簡單字符設備驅動,高級字符設備驅動操作的定義簡單,也就意味著留給我們操作的空間也就很大了。就如書中講的?除了讀取和寫入設備以外,我們還希望通過設備驅動程序執行各種類型的硬件控制,這些操作就通過ioctl來實現。可以這么理解,把簡單驅動程序看成是C語言中的基本類型,如int, char, double,這些基本類型各不同,但是是有限的,而ioctl就相當于結構體類型,雖然基本的結構差不多,但是每個結構體卻是各不相同的,而且需要我們自己去定義。
在用戶空間下,ioctl系統條用具有如下原型:
  • int?ioctl(int?fd,?unsigned long cmd,?...);
  • 而驅動程序的ioctl方法原型為:
  • int?(*ioctl)?(struct innode?*innode,struct file?*filp,?unsigned?int?cmd,?unsigned long arg);
  • ?? ?雖然兩者版本不同,但是其中的參數還是有很大關系的,用戶空間下的fd指的是文件描述符,和驅動程序下的innode,filp相對應,而cmd參數則一致。不管可選的參數arg是否由用戶給定為一個整數或一個指針,它都以一個unsigned long的形式傳遞。如果調用程序不傳遞arg參數, 被驅動收到的 arg 值是未定義的。因為在arg參數上的類型檢查被關閉了,所以若一個非法參數傳遞給 ioctl,編譯器是無法報警的,且任何關聯的錯誤難以查找。參數可以是整數參數,也可以是指針,如果用指針的話,就可以向ioctl調用傳遞任意的數據了,這樣設備就可以跟用戶空間交換任意的數據了。當然這個指針所指向的用戶空間數據必須是有效的才行,如何判斷它的有效性后面會講到。
    一、ioctl步驟 ?? ? 實現ioctl方法的步驟一般包括兩步,定義命令和實現命令
    1、定義命令
    ?? ?前面說過,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 中找到特定體系的值.?

    ?? ?一般來說一個驅動程序肯定是針對某一種設備的,而命令中的幻數位段就是為了區分這個驅動程序是否是針對這個設備的,也就是說幻數和設備驅動是在一個層次上的,每個設備驅動只有一個幻數,兩個不同設備驅動之間的幻數不同。一個設備驅動上可能會有好幾個命令,我們就用number(序數)來區分,也就是同一設備驅動下的不同命令的序數是不一樣的。

    ?? ?在編程過程中,?<asm/ioctl.h>定義了一些構造命令編號的宏:

  • _IO(type,nr)/*沒有參數的命令*/
  • _IOR(type,?nr,?datatype)/*從驅動中讀數據*/
  • _IOW(type,nr,datatype)/*寫數據*/
  • _IOWR(type,nr,datatype)/*雙向傳送*/
  • 這個頭文件還定義了用來解開這個字段的宏:

  • _IOC_DIR(nr)
  • _IOC_TYPE(nr)
  • _IOC_NR(nr)
  • _IOC_SIZE(nr)
  • 2、實現命令

    ?? ?實現命令從用戶空間來看就是實現ioctl這個系統調用,實現ioctl系統調用可分為實現三個技術環節:返回值、參數使用、命令操作

    返回值

    ?? ?ioctl的實現通常就是一個機遇命令號的switch語句,就是前面命令定義的在這個設備驅動中幾個命令。但是當命令號不能匹配合法操作時,比如你用戶空間下傳入的命令號在設備驅動中根本就沒有定義。此時,有些內核會返回 -ENVAL(Invalid argument 非法參數),這是合理的。而POSIX 標準規定:如果使用了不合適的 ioctl 命令號,應當返回-ENOTTY 。這個錯誤碼被 C 庫解釋為"不合適的設備 ioctl。然而,它返回-EINVAL仍是相當普遍的。


    參數使用

    ?? ?前面講用戶空間ioctl系統調用中的“...”代表著可選的arg參數,不管用戶程序使用的是指針還是整數值,驅動程序的ioctl方法都識別成unsigned long的形式。但是驅動在使用這個傳過來的參數時,就會有區別了,如果傳遞的是一個整數,它可以直接使用,這是沒有問題的。如果是一個指針,驅動程序也就要以指針來對待它,就要小心了。比如當一個指針指向用戶空間時,就必須確保指向的用戶空間時合法的,此時驅動程序就要負責對用到的用戶空間地址做適當的檢查了。

    ?? ?用戶空間地址的檢測有課分為兩種,一種是調用的內核函數是帶有檢測的,這樣就不需要檢測了,如

  • copy_from_user
  • copy_to_user
  • get_user
  • put_user
  • 另外一種是調用的內核函數是不帶有檢測的,如

  • __get_user
  • __put_user
  • 我們在調用這兩個函數傳輸數據前,首先要通過函數access_ok驗證地址。

  • int?access_ok(int?type,?const?void?*addr,?unsigned long size);?
  • //從addr指的地址下進行size個字節的type(read或者write)檢測
  • ?? ?關于access_ok,有兩點有趣之處需要注意。第一,它并沒有完成驗證內存的全部工作,而只檢查了所引用的內存是否位于進程有對應訪問權限的區域內,特別是要確保訪問地址沒有指向內核空間的內存區。第二,大多數驅動常年供需代碼中都不需要真正調用access_ok,因為會用到更好的內存管理程序。

    命令操作

    ?? ?命令操作就是switch語句選中的命令下要完成的事,這是用戶程序要求內核對設備做的真正的事,相當于這個函數被調用了。


    二、ioctl在scull中的實現分析

    1、定義命令

    ?? ?下面是scull中的一些ioctl命令定義(scull.h)。這些命令是用來設置和獲取驅動程序的配置參數。

  • /*?使用“k”作為幻數?*/
  • #define SCULL_IOC_MAGIC?'k'
  • /*?在你自己的代碼中,請使用不同的8位數字*/

  • #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC,?0)

  • /*
  • ?*?S means?"Set"?through a ptr,
  • ?*?T means?"Tell"?directly with the argument value
  • ?*?G means?"Get":?reply by setting through a pointer
  • ?*?Q means?"Query":?response?is?on?the return value
  • ?*?X means?"eXchange":?switch G?and?S atomically
  • ?*?H means?"sHift":?switch T?and?Q atomically
  • ?*/
  • #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,?1,?int)
  • #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC,?2,?int)
  • #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,?3)
  • #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC,?4)
  • #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,?5,?int)
  • #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC,?6,?int)
  • #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,?7)
  • #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC,?8)
  • #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC,?9,?int)
  • #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10,?int)
  • #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,?11)
  • #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC,?12)

  • /*
  • ?*?The other entities only have?"Tell"?and?"Query",?because they're
  • ?*?not?printed?in?the book,?and?there's no need?to?have all six.
  • ?*?(The previous stuff was only there?to?show different ways?to?do?it.
  • ?*/
  • #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,?13)
  • #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,?14)
  • /*?...?more?to?come?*/
  • ?#define SCULL_IOC_MAXNR 14

    ?? ?程序先定義了一個幻數,用字符“k”表示,這個是隨機的,只要所有的幻數一致就行。接著定義SCULL_IOC_MAX個不同的cmd。


    2、實現中的ioctl參數使用

  • int?scull_ioctl(struct inode?*inode,?struct file?*filp,
  • ?????????????????unsigned?int?cmd,?unsigned long arg)
  • {

  • ????int?err?=?0,?tmp;
  • ????int?retval?=?0;
  • ????
  • ????/*
  • ?????*?抽取類型和編號位字段,并拒絕錯誤的命令號:
  • ?????*?在調用access_ok()之前返回ENOTTY(不恰當的ioctl)
  • ?????*/
  • ????if?(_IOC_TYPE(cmd)?!=?SCULL_IOC_MAGIC)?return?-ENOTTY;
  • ????if?(_IOC_NR(cmd)?>?SCULL_IOC_MAXNR)?return?-ENOTTY;

  • ????/*
  • ?????*?方向是一個位掩碼,而VERIFY_WRITE用于R/W傳輸
  • ?????*/
  • ????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_READ,?(void __user?*)arg,?_IOC_SIZE(cmd));
  • ????if?(err)?return?-EFAULT;

  • ????switch(cmd)?{
  • case ...
  • ...
  • }
  • ...
  • }

    ?? ?首先用解開為字段的宏來判斷類型和編號是不是這個驅動程序的,也就是前面說的一個驅動中只有一個固定的幻數,這是區別去其他驅動的關鍵。

    ?? ?特別要注意的是,定義命令的時候direction位段是從應用程序的角度看的,也就是說,IOC_READ意味著從設備中讀取數據,IOC_WRITE意味著向設備中寫數據。而access_ok中的第一個參數type(VERITY_READ或VERIFY_WRITE),取決于要執行的動作是要讀取還是寫入用戶空間的內存區。所以他們之間的概念剛好相反。

    3、scull中ioctl命令的實現

    ?? ?正如前面說過的,scull中ioctl命令實現也就是用了一個switch語句,在每個cmd下都有不用的操作函數入口。


    三、測試

    ?? ?測試程序我還是運用了簡單字符設備測試程序里思想,其中運用了ioctl給設備傳送了參數quantum,觀察默認quantum和改變quantum下的寫讀操作。

    模塊程序:?ioctl.zip??

    測試程序:?ioctl-test.zip???


    實驗板測試輸出:

  • #?./ioctl_test
  • write code=20?
  • ever out?
  • read code=20?
  • quantum has been changed ? ? ?
  • [0]=0?[1]=1?[2]=2?[3]=3?[4]=4
  • [5]=5?[6]=6?[7]=7?[8]=8?[9]=9
  • [10]=10?[11]=11?[12]=12?[13]=13?[14]=14
  • [15]=15?[16]=16?[17]=17?[18]=18?[19]=19
  • Now?the quantum?is?changed?
  • write code=6?
  • write code=6?
  • write code=6?
  • writever out?
  • ever out ? ? ??
  • ever out?
  • ever out?
  • e code=2?
  • read code=6?
  • read code=6?
  • read code=6?
  • read code=2?
  • [0]=0?[1]=1?[2]=2?[3]=3?[4]=4
  • [5]=5?[6]=6?[7]=7?[8]=8?[9]=9
  • [10]=10?[11]=11?[12]=12?[13]=13?[14]=14
  • [15]=15?[16]=16?[17]=17?[18]=18?[19]=19
  • 總結

    以上是生活随笔為你收集整理的《Linux设备驱动程序》学习2—高级字符设备驱动ioctl的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。