Linux對I/O連接埠資源的管理(4)

來源:互聯網
上載者:User

3.4 管理I/O連接埠資源

  我們都知道,採用I/O映射方式的X86處理器為外設實現了一個單獨的地址空間,也即“I/O空間”(I/O
Space)或稱為“I/O連接埠空間”,其大小是64KB(0x0000-0xffff)。linux在其所支援的所有平台上都實現了“I/O連接埠空間”這一概念。

  由於I/O空間非常小,因此即使外設匯流排有一個單獨的I/O連接埠空間,卻也不是所有的外設都將其I/O連接埠(指寄存器)映射到“I/O連接埠空間”中。
比如,大多數PCI卡都通過記憶體映射方式來將其I/O連接埠或外設記憶體映射到CPU的RAM物理地址空間中。而老式的ISA卡通常將其I/O連接埠映射到I
/O連接埠空間中。

  linux是基於“I/O
Region”這一概念來實現對I/O連接埠資源(I/O-mapped 或
Memory-mapped)的管理的。

  3.4.1 資源根節點的定義

  linux在kernel/Resource.c檔案中定義了全域變數ioport_resource和iomem_resource,來分別描述基
於I/O映射方式的整個I/O連接埠空間和基於記憶體映射方式的I/O記憶體資源空間(包括I/O連接埠和外設記憶體)。其定義如下:

struct resource ioport_resource =
    { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO };
struct resource iomem_resource =
    { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM
};

這裡在2.6.32核心中有變化, iomem_resource的end由0xffffffff改為了-1;更加合理了。可以參照這個網址http://www.spinics.net/lists/linux-pci/msg09857.html (The iomem_resource map reflects the available physical address space.We statically initialize the end to -1, i.e., 0xffffffff_ffffffff, but of course we can only use as much as the CPU can address.)

 


  其中,宏IO_SPACE_LIMIT表示整個I/O空間的大小,對於X86平台而言,它是0xffff(定義在include/asm-i386/io.h標頭檔中)。這裡我多寫一句,對於arm平台而言,以s3c2410為例,IO_SPACE_LIMIT是0xffffffff, 因為對於arm平台來說,它採用的是記憶體映射方式。
顯然,I/O記憶體空間的大小是4GB。

  3.4.2 對I/O連接埠空間的操作

  基於I/O
Region的操作函數__XXX_region(),linux在標頭檔include/linux/ioport.h中定義了三個對I/O連接埠空間進
行操作的宏:①request_region()宏,請求在I/O連接埠空間中分配指定範圍的I/O連接埠資源。②check_region()宏,檢查I
/O連接埠空間中的指定I/O連接埠資源是否已被佔用。③release_region()宏,釋放I/O連接埠空間中的指定I/O連接埠資源。這三個宏的定義如
下:

#define request_region(start,n,name)
__request_region(&ioport_resource, (start), (n), (name))
#define check_region(start,n)
__check_region(&ioport_resource, (start), (n))
#define release_region(start,n)
__release_region(&ioport_resource, (start), (n))

  其中,宏參數start指定I/O連接埠資源的起始物理地址(是I/O連接埠空間中的物理地址),宏參數n指定I/O連接埠資源的大小。

  3.4.3 對I/O記憶體資源的操作

  基於I/O
Region的操作函數__XXX_region(),linux在標頭檔include/linux/ioport.h中定義了三個對I/O記憶體資源進行操作的宏:①request_mem_region()宏,請求分配指定的I/O記憶體資源。②check_
mem_region()宏,檢查指定的I/O記憶體資源是否已被佔用。③release_
mem_region()宏,釋放指定的I/O記憶體資源。這三個宏的定義如下:

#define request_mem_region(start,n,name)
  __request_region(&iomem_resource, (start), (n),
(name))
#define check_mem_region(start,n)
__check_region(&iomem_resource, (start), (n))
#define release_mem_region(start,n)
__release_region(&iomem_resource, (start), (n))

  其中,參數start是I/O記憶體資源的起始物理地址(是CPU的RAM物理地址空間中的物理地址),參數n指定I/O記憶體資源的大小。

  3.4.4 對/proc/ioports和/proc/iomem的支援
       

  linux在ioport.h標頭檔中定義了兩個宏:

  get_ioport_list()和get_iomem_list(),分別用來實現/proc/ioports檔案和/proc/iomem檔案。其定義如下:

#define get_ioport_list(buf)
get_resource_list(&ioport_resource, buf, PAGE_SIZE)
#define get_mem_list(buf) get_resource_list(&iomem_resource,
buf, PAGE_SIZE)

2.6.32移除了這兩個函數,具體怎麼實現對/proc/ioports和/proc/iomem的支援我還不知道,請高手指教阿

3.5 訪問I/O連接埠空間

  在驅動程式請求了I/O連接埠空間中的連接埠資源後,它就可以通過CPU的IO指定來讀寫這些I/O連接埠了。在讀寫I/O連接埠時要注意的一點就是,大多數平台都區分8位、16位和32位的連接埠,也即要注意I/O連接埠的寬度。

  linux在include/asm/io.h標頭檔(對於i386平台就是include/asm-i386/io.h)中定義了一系列讀寫不同寬度I/O連接埠的宏函數。如下所示:

  ⑴讀寫8位寬的I/O連接埠

  unsigned char inb(unsigned port);
  void outb(unsigned char value,unsigned port);

  其中,port參數指定I/O連接埠空間中的連接埠地址。在大多數平台上(如x86)它都是unsigned
short類型的,其它的一些平台上則是unsigned
int類型的。顯然,連接埠地址的類型是由I/O連接埠空間的大小來決定的。

  ⑵讀寫16位寬的I/O連接埠

  unsigned short inw(unsigned port);
  void outw(unsigned short value,unsigned port);

  ⑶讀寫32位寬的I/O連接埠

  unsigned int inl(unsigned port);
  void outl(unsigned int value,unsigned port);

  3.5.1 對I/O連接埠的字串操作

  除了上述這些“單發”(single-shot)的I/O操作外,某些CPU也支援對某個I/O連接埠進行連續的讀寫操作,也即對單個I/O連接埠讀或寫一系列位元組、字或32位整數,這就是所謂的“字串I/O指令”(String
Instruction)。這種指令在速度上顯然要比用迴圈來實現同樣的功能要快得多。

  linux同樣在io.h檔案中定義了字串I/O讀寫函數:

  ⑴8位寬的字串I/O操作

  void insb(unsigned port,void * addr,unsigned long
count);
  void outsb(unsigned port ,void * addr,unsigned long
count);

  ⑵16位寬的字串I/O操作

  void insw(unsigned port,void * addr,unsigned long
count);
  void outsw(unsigned port ,void * addr,unsigned long
count);

  ⑶32位寬的字串I/O操作

  void insl(unsigned port,void * addr,unsigned long
count);
  void outsl(unsigned port ,void * addr,unsigned long
count);

  3.5.2 Pausing I/O

  在一些平台上(典型地如X86),對於老式匯流排(如ISA)上的慢速外設來說,如果CPU讀寫其I/O連接埠的速度太快,那就可能會發生遺失資料的現
象。對於這個問題的解決方案就是在兩次連續的I/O操作之間插入一段微小的時延,以便等待慢速外設。這就是所謂的“Pausing
I/O”。

  對於Pausing
I/O,linux也在io.h標頭檔中定義了它的I/O讀寫函數,而且都以XXX_p命名,比如:inb_p()、outb_p()等等。下面我們就以out_p()為例進行分析。

  將io.h中的宏定義__OUT(b,”b”char)展開後可得如下定義:

extern inline void outb(unsigned char value, unsigned short port)
{
__asm__ __volatile__ ("outb %" "b " "0,%" "w" "1"
: : "a" (value), "Nd" (port));
}

extern inline void outb_p(unsigned char value, unsigned short port)
{
__asm__ __volatile__ ("outb %" "b " "0,%" "w" "1"
__FULL_SLOW_DOWN_IO
: : "a" (value), "Nd" (port));
}

  可以看出,outb_p()函數的實現中被插入了宏__FULL_SLOWN_DOWN_IO,以實現微小的延時。宏__FULL_SLOWN_DOWN_IO在標頭檔io.h中一開始就被定義:

#ifdef SLOW_IO_BY_JUMPING
#define __SLOW_DOWN_IO "
jmp 1f
1: jmp 1f
1:"
#else
#define __SLOW_DOWN_IO "
outb %%al,$0x80"
#endif

#ifdef REALLY_SLOW_IO
#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
  __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO
#else
#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
#endif

  顯然,__FULL_SLOW_DOWN_IO就是一個或四個__SLOW_DOWN_IO(根據是否定義了宏REALLY_SLOW_IO來決
定),而宏__SLOW_DOWN_IO則被定義成毫無意義的跳躍陳述式或寫連接埠0x80的操作(根據是否定義了宏SLOW_IO_BY_JUMPING來
決定)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.