Linux核心開發之記憶體與I/O訪問(二)

來源:互聯網
上載者:User

“小濤哥,咱們說Linux裝置驅動程式說了那麼久,怎麼從來不說實際裝置呢,頂多就說了下記憶體,總感覺驅動程式是和裝置分離的,怎麼關聯起來..”小王思索著。

“不錯,這也正是這次講課的內容,裝置I/O連接埠與I/O記憶體的訪問”我啊,禁不住拍拍她的頭。

  對於一塊實際的裝置而言,通常會提供一組寄存器來用於控制裝置,讀寫裝置和擷取裝置狀態,也就是我們常說的控制寄存器,資料寄存器和狀態寄存器。這些寄存器可能位於I/O空間(這時叫做I/O連接埠),也可能位於記憶體空間(對應的記憶體空間被成為I/O記憶體)。在Linux中提供了一系列的I/O連接埠和I/O記憶體操作的介面如下:

  1)I/O連接埠操作:在Linux裝置驅動中,應使用Linux核心提供的函數來訪問定位於I/O空間的連接埠,包括一下幾種:

   *讀寫位元組連接埠(8位寬)

     unsigned inb(unsigned port);【讀】          voi outb(unsigned char byte, unsigned port);【寫】

   *讀寫字連接埠(16位寬)

     unsigned inw(unsigned port);【讀】          voi outw(unsigned short word, unsigned port);【寫】

   *讀寫長字連接埠(32位寬)

     unsigned inl(unsigned port);【讀】          voi outl(unsigned longword, unsigned port);【寫】

   *讀寫一串位元組

     unsigned insb(unsigned port, void *addr, unsigned long count);【讀】      voi outsb(unsigned port, void *addr, unsigned long count);【寫】

   *讀寫一串字

     unsigned insw(unsigned port, void *addr,unsigned long count);【讀】      voi outsb(unsigned port, void *addr, unsigned long count);【寫】

   *讀寫一串長字

     unsigned insl(unsigned port, void * addr, unsigned long count);【讀】      voi outsb(unsigned port, void *addr, unsigned long count);【寫】

    說明:上述各函數中I/O連接埠port的類型長度依賴與具體的硬體平台,所以只是寫出了unsigned

  2)I/O記憶體:在核心中訪問I/O記憶體之前,需首先使用ioremap()函數將裝置所處的物理地址映射到虛擬位址。

   *ioremap()原型:void *ioremap(unsigned long offset, unsigned long size);

     它返回一個特殊的虛擬位址,該地址可用來存取特定的物理位址範圍。用它返回的虛擬位址應該使用iounmap()函數釋放。

   *iounmap()原型:void iounmap(void *addr);

   現在,有了物理地址鎖映射出來的虛擬位址後,我們就可以通過c指標來直接存取這些地址,但Linux核心也提供了一組函數來完成這中虛擬位址的讀寫。如下

   *讀IO記憶體

     unsigned int ioread8(void *addr);      unsigned int ioread16(void *addr);     unsigned int ioread32(void *addr);  與之對應的較早版本是:

     unsigned readb(address);                unsigned readw(address);                 unsigned readl(address);  這些在2.6的核心中依然可以使用。

   *寫IO記憶體

     void iowrite8(u8 value,void *addr);  void iowrite16(u16 value, void *addr);   void iowrite32(u32 value, void *addr);  與之對應的較早版本是:

     void writeb(unsigned value, address); void writew(unsigned value,address);  void writel(unsigned value,address);  2.6的核心中依然可以使用。

   *讀一串IO記憶體                                                                          *寫一串IO記憶體

     void ioread8_rep(void *addr, void *buf, unsigned long count);       void iowrite8_rep(void *addr, void *buf, unsigned long count);

     void ioread16_rep(void *addr, void *buf, unsigned long count);     void iowrite8_rep(void *addr, void *buf, unsigned long count);

     void ioread32_rep(void *addr, void *buf, unsigned long count);     void iowrite8_rep(void *addr, void *buf, unsigned long count);

   *複製IO記憶體

     void memcpy_fromio(void *dest, void *source, unsigned int count);

     void memcpy_toio(void *dest, void *source, unsigned int count);

   *設定IO記憶體

     void *ioport_map(unsigned long port, unsigned int count);

  3)把IO連接埠映射到記憶體空間

     void *ioport_map(unsigned long port, unsigned int count);   通過這個函數,可以把port開始的count個連續的IO連接埠重新對應為一段“記憶體空間”。然後

                                                                                     就可以在其返回的地址上向訪問IO記憶體一樣訪問這些連接埠,當不再需要這種映射時,調用:

     void ioport_unmap(void *addr);                                      來撤銷這種映射

  4)IO連接埠申請

     struct resource *request_region(unsigned long first, unsigned long n, const char *name);

     這個函數向核心申請n個連接埠,這些連接埠從first開始,name參數為裝置的名稱,成功返回非NULL.一旦申請連接埠使用完成後,應當使用:

     void release_region(unsigned long start, unsigned long n);

  5)IO記憶體申請

    struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

    這個函數向核心申請n個記憶體,這些地址從first開始,name為裝置的名稱,成功返回非NULL,一旦申請的記憶體使用量完成後,應當使用:

    void release_mem_region() ;          來釋放歸回給系統。需要說明的是這兩個函數也不是必須的,但建議使用。  

6)通過以上的基礎,我們就可以歸納出裝置驅動訪問IO連接埠和IO記憶體的步驟。

   一種方法是:直接使用IO連接埠操作函數:在裝置開啟或驅動模組被載入時申請IO連接埠地區,之後使用inb(),outb()等進行連接埠訪問,最後在裝置關閉或驅動被卸載時釋放IO連接埠範圍。流程如下:

  

   另外一種途徑是:將IO連接埠映射為記憶體進行訪問,在裝置開啟或驅動模組被載入時,申請IO連接埠地區並使用ioport_map()映射到記憶體,之後使用IO記憶體的函數進行連接埠訪問,最後,在裝置關閉或驅動模組被卸載時釋放IO連接埠並釋放映射,流程如下:

  

  上邊是IO連接埠的存取方法,至於IO記憶體的存取方法是:首先調用request_mem_region()申請資源,接著將寄存器地址通過ioremap()映射到核心空間的虛擬位址,之後就可以Linux裝置訪問編程介面訪問這些寄存器了,訪問完成後,使用ioremap()對申請的虛擬位址進行釋放,並釋放release_mem_region()申請的IO記憶體資源。

流程如下:

  

 

“小王,感覺怎麼樣呢, 我是盡最大努力了哈”我說。

“可以,可以,感覺挺好,我覺得哈,你要是多畫圖,我就沒問題,就像上邊三個圖一樣”小王調皮的說。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.