乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      UIO 子系統(tǒng)結(jié)構(gòu)介紹

       lifei_szdz 2012-04-05

      UIO 子系統(tǒng)結(jié)構(gòu)介紹

      分類: Linux 內(nèi)核 Linux 基礎(chǔ)學(xué)習(xí) Linux 驅(qū)動 移植 實(shí)時(shí)操作系統(tǒng) 雜項(xiàng) 259人閱讀 評論(0) 收藏 舉報(bào)
      AUTHOR: Joseph Yang (楊紅剛) <ganggexiongqi@gmail.com>
      CONTENT: Introduction of UIO subsystem ( UIO子系統(tǒng)介紹 )
      NOTE: linux-3.0
      LAST MODIFIED:09-15-2011
      -----------------------------------------------------------------------------------------------------------
      Distributed and Embedded System Lab (分布式嵌入式系統(tǒng)實(shí)驗(yàn)室,蘭州大學(xué))
      ===============================================================


      ------1------為什么出現(xiàn)了UIO?


      硬件設(shè)備可以根據(jù)功能分為網(wǎng)絡(luò)設(shè)備,塊設(shè)備,字符設(shè)備,或者根據(jù)與CPU相連的方式
      分為PCI設(shè)備,USB設(shè)備等。它們被不同的內(nèi)核子系統(tǒng)支持。這些標(biāo)準(zhǔn)的設(shè)備的驅(qū)動編寫
      較為容易而且容易維護(hù)。很容易加入主內(nèi)核源碼樹。
      但是,又有很多設(shè)備難以劃分到這些子系統(tǒng)中,比如I/O卡,現(xiàn)場總線接口或者定制的FPGA。

      通常這些非標(biāo)準(zhǔn)設(shè)備的驅(qū)動被實(shí)現(xiàn)為字符驅(qū)動。這些驅(qū)動使用了很多內(nèi)核內(nèi)部函數(shù)和宏。
      而這些內(nèi)部函數(shù)和宏是變化的。這樣驅(qū)動的編寫者必須編寫一個完全的內(nèi)核驅(qū)動,而且一直維護(hù)
      這些代碼。而且這些驅(qū)動進(jìn)不了主內(nèi)核源碼。于是就出現(xiàn)了用戶空間I/O框架(Userspace I/O framework)。

      ---------2----------UIO 是怎么工作的?


      一個設(shè)備驅(qū)動的主要任務(wù)有兩個:
       1. 存取設(shè)備的內(nèi)存
       2. 處理設(shè)備產(chǎn)生的中斷
       
       對于第一個任務(wù),UIO 核心實(shí)現(xiàn)了mmap()可以處理物理內(nèi)存(physical memory),邏輯內(nèi)存(logical memory),
       虛擬內(nèi)存(virtual memory)。UIO驅(qū)動的編寫是就不需要再考慮這些繁瑣的細(xì)節(jié)。
       
       第二個任務(wù),對于設(shè)備中斷的應(yīng)答必須在內(nèi)核空間進(jìn)行。所以在內(nèi)核空間有一小部分代碼
       用來應(yīng)答中斷和禁止中斷,但是其余的工作全部留給用戶空間處理。
       
       如果用戶空間要等待一個設(shè)備中斷,它只需要簡單的阻塞在對 /dev/uioX的read()操作上。
       當(dāng)設(shè)備產(chǎn)生中斷時(shí),read()操作立即返回。UIO 也實(shí)現(xiàn)了poll()系統(tǒng)調(diào)用,你可以使用
       select()來等待中斷的發(fā)生。select()有一個超時(shí)參數(shù)可以用來實(shí)現(xiàn)有限時(shí)間內(nèi)等待中斷。
       
       對設(shè)備的控制還可以通過/sys/class/uio下的各個文件的讀寫來完成。你注冊的uio設(shè)備將會出現(xiàn)在該目錄下。
       假如你的uio設(shè)備是uio0那么映射的設(shè)備內(nèi)存文件出現(xiàn)在 /sys/class/uio/uio0/maps/mapX,對該文件的讀寫就是
       對設(shè)備內(nèi)存的讀寫。

       如下的圖描述了uio驅(qū)動的內(nèi)核部分,用戶空間部分,和uio 框架以及內(nèi)核內(nèi)部函數(shù)的關(guān)系。


       Figure 1: uio_architecture
       
       詳細(xì)的UIO驅(qū)動的編寫可以參考 drivers/uio/下的例子,以及Documentation/DocBook/uio-howto.tmpl
       //tmpl格式的文件可以借助 docbook-utils (debian下)工具轉(zhuǎn)化為pdf或者h(yuǎn)tml合格等。
       
       ---------3----------- “uio 核心的實(shí)現(xiàn) 和 uio驅(qū)動的內(nèi)核部分的關(guān)系“詳談
       重要的結(jié)構(gòu):
             struct uio_device {
          struct module       *owner;
          struct device       *dev; //在__uio_register_device中初始化
          int         minor; // 次設(shè)備id號,uio_get_minor
          atomic_t        event; //中斷事件計(jì)數(shù)
          struct fasync_struct    *async_queue;//該設(shè)備上的異步等待隊(duì)列//
                                                                     // 關(guān)于 “異步通知“ //參見LDD3第六章
          wait_queue_head_t   wait; //該設(shè)備上的等待隊(duì)列,在注冊設(shè)備時(shí)(__uio_register_device)初始化
          int         vma_count;
          struct uio_info     *info;// 指向用戶注冊的uio_info,在__uio_register_device中被賦值的
          struct kobject      *map_dir;
          struct kobject      *portio_dir;
      };  

            /*
       * struct uio_info - UIO device capabilities
       * @uio_dev:        the UIO device this info belongs to
       * @name:       device name
       * @version:        device driver version
       * @mem:        list of mappable memory regions, size==0 for end of list
       * @port:       list of port regions, size==0 for end of list
       * @irq:        interrupt number or UIO_IRQ_CUSTOM
       * @irq_flags:      flags for request_irq()
       * @priv:       optional private data
       * @handler:        the device's irq handler
       * @mmap:       mmap operation for this uio device
       * @open:       open operation for this uio device
       * @release:        release operation for this uio device
       * @irqcontrol:     disable/enable irqs when 0/1 is written to /dev/uioX
       */     
      struct uio_info {
          struct uio_device   *uio_dev; // 在__uio_register_device中初始化
          const char      *name; // 調(diào)用__uio_register_device之前必須初始化
          const char      *version; //調(diào)用__uio_register_device之前必須初始化
          struct uio_mem      mem[MAX_UIO_MAPS];
          struct uio_port     port[MAX_UIO_PORT_REGIONS];
          long            irq; //分配給uio設(shè)備的中斷號,調(diào)用__uio_register_device之前必須初始化
          unsigned long       irq_flags;// 調(diào)用__uio_register_device之前必須初始化
          void            *priv; //
          irqreturn_t (*handler)(int irq, struct uio_info *dev_info); //uio_interrupt中調(diào)用,用于中斷處理
                                                                                                         // 調(diào)用__uio_register_device之前必須初始化
          int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); //在uio_mmap中被調(diào)用,
                                                                                                                      // 執(zhí)行設(shè)備打開特定操作
          int (*open)(struct uio_info *info, struct inode *inode);//在uio_open中被調(diào)用,執(zhí)行設(shè)備打開特定操作
          int (*release)(struct uio_info *info, struct inode *inode);//在uio_device中被調(diào)用,執(zhí)行設(shè)備打開特定操作
          int (*irqcontrol)(struct uio_info *info, s32 irq_on);//在uio_write方法中被調(diào)用,執(zhí)行用戶驅(qū)動的
                                                                                             //特定操作。
      };

       先看一個uio 核心和 uio 設(shè)備之間關(guān)系的圖,有個整體印象:


                        Figure 2: uio_core_device


       
       uio核心部分是一個名為"uio"的字符設(shè)備(下文稱為“uio核心字符設(shè)備“)。用戶驅(qū)動的內(nèi)核部分
       使用uio_register_device向uio核心部分 注冊uio設(shè)備。uio 核心的任務(wù)就是管理好這些注冊的uio
       設(shè)備。這些uio設(shè)備使用的數(shù)據(jù)結(jié)構(gòu)是  uio_device。而這些設(shè)備屬性,比如name, open(),
       release()等操作都放在了uio_info結(jié)構(gòu)中,用戶使用 uio_register_device注冊這些驅(qū)動之前
       要設(shè)置好uio_info。
       uio核心字符設(shè)備注冊的
       uio_open
       uio_fasync
       uio_release
       uio_poll
       uio_read
       uio_write
       中除了完成相關(guān)的維護(hù)工作外,還調(diào)用了注冊在uio_info中的相關(guān)方法。比如,在
       uio_open中調(diào)用了uio_info中注冊的open方法。
       那么這里有一個問題,uio核心字符設(shè)備怎么找到相關(guān)設(shè)備的uio_device結(jié)構(gòu)的呢?
       這就涉及到了內(nèi)核的idr機(jī)制,關(guān)于該機(jī)制可以參考:
       http://blog.csdn.net/ganggexiongqi/article/details/6737389
       在uio.c中,有如下的定義:
       static DEFINE_IDR(uio_idr);
       /* Protect idr accesses */
      static DEFINE_MUTEX(minor_lock);
      在你調(diào)用uio_register_device(內(nèi)部調(diào)用了__uio_register_device)注冊你的uio 設(shè)備時(shí),
      在__uio_register_device中調(diào)用了uio_get_minor函數(shù),在uio_get_minor函數(shù)中,利用
      idr機(jī)制(idr_get_new)建立了次設(shè)備號和uio_device類型指針之間的聯(lián)系。而uio_device指針
      指向了代表你注冊的uio設(shè)備的內(nèi)核結(jié)構(gòu)。在uio核心字符設(shè)備的打開方法,uio_open中
      先取得了設(shè)備的次設(shè)備號(iminor(inode)),再次利用idr機(jī)制提供的方法(idr_find)取得了
      對應(yīng)的uio_device類型的指針。并且把該指針保存在了uio_listener結(jié)構(gòu)中,以方便以后
      使用。

      ----4---關(guān)于設(shè)備中斷的處理

      在__uio_register_device中,為uio設(shè)備注冊了統(tǒng)一的中斷處理函數(shù)uio_interrupt,
      在該函數(shù)中,調(diào)用了uio設(shè)備自己提供的中斷處理函數(shù)handler(uio_info結(jié)構(gòu)中)。
      并調(diào)用了uio_event_notify函數(shù)對uio設(shè)備的中斷事件計(jì)數(shù)器增一, 通知各個讀進(jìn)程
      “有數(shù)據(jù)可讀”。每個uio設(shè)備的中斷處理函數(shù)都是單獨(dú)注冊的。

        關(guān)于中斷計(jì)數(shù): uio_listener
                  struct uio_listener {
                      struct uio_device *dev; //  保存uio設(shè)備的指針,便于訪問
                      s32 event_count; //跟蹤uio設(shè)備的中斷事件計(jì)數(shù)器
                  };
               對于每一個注冊的uio 設(shè)備(uio_device), 都關(guān)聯(lián)一個這樣的結(jié)構(gòu)。
               它的作用就是跟蹤每個uio設(shè)備(uio_device)的中斷事件計(jì)數(shù)器值。
               
               在用戶空間進(jìn)行文件打開操作(open)時(shí),與uio設(shè)備關(guān)聯(lián)的uio_listener結(jié)構(gòu)就被分配,
               指向它的指針被保存在filep指針的private_data字段以供其他操作使用。
               在用戶空間執(zhí)行文件關(guān)閉操作時(shí),和uio設(shè)備關(guān)聯(lián)的uio_listener結(jié)構(gòu)就被銷毀。
               
               在uio設(shè)備注冊時(shí),uio core會為設(shè)備注冊一個通用的中斷處理函數(shù)(uio_interrupt),
               在該函數(shù)中,會調(diào)用uio設(shè)備自身的中斷處理函數(shù)(handler). 中斷發(fā)生時(shí),
               uio_event_notify將被調(diào)用,用來對設(shè)備的中斷事件計(jì)數(shù)器()增一,并通知各讀進(jìn)程,
               有數(shù)據(jù)可讀。
               
               uio_poll 操作判斷是否有數(shù)據(jù)可讀的依據(jù)就是 listener中的中斷事件計(jì)數(shù)值
               (event_count)和uio設(shè)備中的中斷事件計(jì)數(shù)器值不一致(前者小于后者)。因?yàn)?br>          listener的值除了在執(zhí)行文件打開操作時(shí)被置為被賦值外,只在uio_read操作中
               被更新為uio設(shè)備的中斷事件計(jì)數(shù)器值。    
               
       疑問1:
       對于中斷事件計(jì)數(shù)器,uio_device中定義為 atomic_t 類型,又有
                   typedef struct {
                      int counter;
                  } atomic_t;
         需不需要考慮溢出問題?
         同樣的問題存在在uio_listener的event_count字段。
         
         關(guān)于uio_device的event字段 uio_howto中:
         event: The total number of interrupts handled by the driver since the last time the device node
                     was read.
         【如果中斷事件產(chǎn)生的頻率是100MHZ的話,(2^32)/(10^8) = 42 秒 】counter計(jì)數(shù)器就會
         溢出。所以,依賴于counter的操作可能會出現(xiàn)問題。//補(bǔ)充:中斷發(fā)生的頻率最多為kHz不會是 Mhz,所以[]中的假設(shè)是不合理的,但是溢出會發(fā)生,而且,依賴counter值的應(yīng)用可能會出現(xiàn)問題?。?br>    
         我們可以添加一個timer,在timer 處理函數(shù)中,調(diào)用uio_event_notify增加counter的值,
         很快會觀察到溢出。<<<<<<< 例子,還沒有寫 (^_^)
         //其實(shí),可以在我們注冊的函數(shù)中,得到uio_device的指針,可以直接修改event的值。
         

         ===========關(guān)于 sysfs文件創(chuàng)建
         sysfs下uio相關(guān)的文件結(jié)構(gòu)如下
      1.  sys  
      2.  ├───uio  
      3.        ├───uio0  
      4.        │     ├───maps  
      5.        │          ├───mapX  
      6.        ├───uio1  
      7.              ├───maps                           
      8.              │    ├───mapX        
      9.              ├───portio  
      10.                    ├───portX  


           其中的uio是uio模塊加載時(shí),uio_init調(diào)用init_uio_class調(diào)用class_register注冊到內(nèi)核空間的。
           關(guān)于這個類的方法有個疑問,就是比如在show_event方法中,
           struct uio_device *idev = dev_get_drvdata(dev);//具體的uio設(shè)備相關(guān)的信息
          這個uio_device相關(guān)的信息是怎么跟 uio class聯(lián)系上的?
          在調(diào)用__uio_register_device注冊uio設(shè)備時(shí),通過
          
          idev->dev = device_create(&uio_class, parent,
                        MKDEV(uio_major, idev->minor), idev,
                        "uio%d", idev->minor);
           其中,idev就是 uio_device類型的指針,它作為drvdata被傳入,
           device_create調(diào)用了device_create調(diào)用了device_create_vargs調(diào)用了dev_set_drvdata。
           這樣在uio class的 show_event方法中,就可以使用
           struct uio_device *idev = dev_get_drvdata(dev);
           得到了uio設(shè)備的結(jié)構(gòu)體的指針。
           
           device_create調(diào)用完畢后在 /sys/class/uio/下就會出現(xiàn) 代表uio設(shè)備的uioX文件夾,
           其中X為uio設(shè)備的次設(shè)備號。
           
           往下,就不再啰嗦了。希望有所幫助。
      =======================================
      參考:  
        1,2 參考了Userspace I/O drivers in a realtime context  Hans J. Koch, Linutronix GmbH
        3,4 參考了 uio.c 分析 http://blog.csdn.net/ganggexiongqi/article/details/6737647                       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多