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 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);