一、I/O端口和I/O内存
ISA设备普遍使用I/O端口,而大多数PCI设备则把寄存器映射到某个内存地址区段
1. I/O寄存器和常规内存
I/O寄存器和RAM的最主要区别:I/O操作具有边际效应而内存操作没有
访问I/O寄存器时需避免由于CPU或编译器不恰当的优化(内存访问顺序)而改变预期的动作,因此驱动程序必须确保不使用高速缓存,并且在访问寄存器时不发生读或写指令的重新排序
硬件自身缓存引起的问题解决:只需把底层硬件配置成在访问I/O区域时禁止硬件缓存即可
#include <linux/kernel.h>
void barrier(void)
/* 设置内存屏障,避免屏障前后优化(会把当前CPU寄存器中修改过的数值保存到内存) */
#include <asm/system.h>
void rmb(void);//读内存屏障
void read_barrier_depends(void);//特殊、弱一些??
void wmb(void);//保证写不会乱序
void mb(void);//保证读写不会乱序
void smp_rmb(void);
void smp_read_barrier_depends(void);
void smp_wmb(void);
void smp_mb(void);
/* SMP系统的屏障接口 */
二、使用I/O端口
1. I/O端口分配
#include <linux/ioport.h>
struct resource *request_region(unsigned long first, unsigned long n,
const char *name);
/*
注册,使用起始于first的n个端口
所有端口分配可从/proc/ioports得到
*/
void release_region(unsigned long start, unsigned long n);
/* 返回端口 */
int check_region(unsigned long first, unsigned long n);
/*
检查端口是否可用,由于不是原子操作,故返回值不能确保分配能否成功
不赞成使用,建议使用request_region
*/
2. 操作I/O端口
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
/* 8位读写端口 */
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
/* 16位读写端口 */
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
/* 32位读写端口 */
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
/* 串操作,向内存地址addr连续读/写count数目的N位数据 */
当处理器速度快于总线传输速度时,应使用暂停式I/O(上述接口名字后面加上_p)
平台相关性:数据类型不兼容
ARM:端口映射到内存,支持所有函数;串操作用C语言实现;端口类型是unsigned int
三、使用I/O内存
与设备通信另一种机制:使用映射到内存的寄存器或者设备内存
I/O内存分配:
struct resource *request_mem_region(unsigned long start, unsigned long len,
char *name);
/* 分配I/O内存区域:从start开始分配len字节的内存区域 */
void release_mem_region(unsigned long start, unsigned long len);
/* 释放 */
int check_mem_region(unsigned long start, unsigned long len);
/* 检查给定的I/O内存区域是否可用,但不安全 */
I/O内存映射(返回的地址不能直接访问):
#include <asm/io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);
void *ioremap_nocache(unsigned long phys_addr, unsigned long size);//非缓存版本
void iounmap(void * addr);
访问I/O内存:
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
/* 读I/O内存 */
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
/* 写I/O内存 */
void ioread8_rep(void *addr, void *buf, unsigned long count);
void ioread16_rep(void *addr, void *buf, unsigned long count);
void ioread32_rep(void *addr, void *buf, unsigned long count);
void iowrite8_rep(void *addr, const void *buf, unsigned long count);
void iowrite16_rep(void *addr, const void *buf, unsigned long count);
void iowrite32_rep(void *addr, const void *buf, unsigned long count);
/* 重复版本:从buf向addr读或写count个值 */
void memset_io(void *addr, u8 value, unsigned int count);
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);
/* 在一块I/O内存上读写 */
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);
/* 老接口,不执行类型检查,安全性较差 */
若想像I/O内存一样使用I/O端口时,可以使用以下映射接口:
void *ioport_map(unsigned long port, unsigned int count);
/* 映射coun个I/O端口,返回的地址可以使用ioread8及其同类函数访问 */
void ioport_unmap(void *addr);
/* 撤销映射 */