本文連結自:
http://qikee.blogbus.com/logs/37072405.html
I/O連接埠是驅動程式與許多裝置之間的通訊方式,Linux的核心為我們提供了I/O連接埠分配的操作介面,但對PCI裝置來講,它的配置地址空間已經為其指定了I/O連接埠範圍,不需要額外的分配操作。Linux核心
提供了如下一些訪問I/O連接埠的內嵌函式:
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
下面我們重點來看一下2.6核心引入的ioport_map函數:
void *ioport_map( unsigned long port, unsigned int count );
通過這個函數,可以把port開始的count個連續連接埠重新對應為一段“記憶體空間”。然後就可以在其返回的地址上象訪問I/O記憶體一樣訪問這幾個I/O連接埠。當不需要這種映射時,需要調用下面的函數來撤消:
void iport_unmap(void *addr);
除了I/O連接埠之外,和裝置通訊的另一種主要機制是通過使用映射到記憶體的寄存器或裝置記憶體。這兩種都稱為I/O記憶體,因為寄存器和記憶體的差別對軟體是透明的。
對於分配好的I/O記憶體,一般不鼓勵直接使用指向I/O記憶體的指標進行訪問,最好通過頁表,用封裝函數訪問。要通過頁表訪問,那麼需要對分配好的I/O記憶體進行映射,確保該I/O記憶體對核心而言是可訪問的。完成I/O記憶體映射的函數是ioremap.
#include <asm/io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);
註冊驅動程式成功後,rtl8139_init_one會被調用,在這個函數中,我們可以通過插入一些列印輸出語句看到PCI的配置地址空間和I/O地址地區的一些情況。
首先,插入以下語句:
u16 vendor, device;
pci_read_config_word(pdev, 0, &vendor);
pci_read_config_word(pdev, 2, &device);
printk(KERN_INFO "%x, %x/n", vendor, device);
下面通過在 rtl8139_init_one在插入代碼,以四種不同方式訪問裝置記憶體。第一種是通過訪問I/O記憶體實現,後三種則是通過訪問I/O連接埠的形式實現。
第一種:
unsigned long mmio_start, addr1, addr2;
void __iomem *ioaddr;
mmio_start = pci_resource_start( pdev, 1);
ioaddr = pci_iomap(pdev, 1, 0);
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "mmio start: %lX/n", mmio_start);
printk(KERN_INFO "ioaddr: %p/n", ioaddr);
第二種:
unsigned long pio_start, pio_len, addr1, addr2;
void __iomem *ioaddr;
pio_start = pci_resource_start( pdev, 0);
pio_len = pci_resource_len (pdev, 0);
ioaddr = ioport_map(pio_start, pio_len);
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "pio start: %lX/n", pio_start);
printk(KERN_INFO "ioaddr: %p/n", ioaddr);
第三種:
unsigned long pio_start, addr1, addr2;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inl( pio_start );
addr2 = inl( pio_start + 4 );
printk(KERN_INFO "port io start: %lX/n", pio_start);
第四種:
unsigned long pio_start;
u8 addr1, addr2, addr3, addr4, addr5, addr6;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inb( pio_start );
addr2 = inb( pio_start + 1 );
addr3 = inb( pio_start + 2 );
addr4 = inb( pio_start + 3 );
addr5 = inb( pio_start + 4 );
addr6 = inb( pio_start + 5 );
printk(KERN_INFO "port io start: %lX/n", pio_start);