<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Linux驅(qū)動總結(jié)3

          Linux驅(qū)動總結(jié)3

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

          關(guān)鍵詞: Linux驅(qū)動總

          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();