1、I/O端口和I/O内存
I/O寄存器和常规内存
Linux提供了4个宏来解决由编译器和硬件重新排序引起的问题:
#include <linux/kernel.h>
void barrier(void);
#include <asm/system.h>
void rmb(void);
void read_barrier_depends(void);
void wmb(void);
void mb(void);
针对smp系统的,仅在内核针对smp系统时有效,在单处理器系统上,会被扩展为简单的调用:
void smp_rmb(void);
void smp_read_depends(void);
void smp_wmb(void);
void smp_mb(void);
某些体系架构上,允许把赋值语句和内存屏障进行结合以提高效率。内核提供了执行这种合并的宏,默认情况下,这些宏的定义如下:
#define set_mb(var, value) do {var = value; mb()} while 0
#define set_wmb(var, value) do {var = value; wmb()} while 0
#define set_rmb(var, value) do {var = value; rmb()} while 0
2、使用I/O端口:
(1)I/O端口分配:
#include <linux/ioport.h>
struct resource *request_region(unsigned long first, unsigned long n, const char *name);
将端口返还给系统:
void release_region(unsigned long start, unsigned long n);
检查给定的I/O端口是否可用:
int check_region(unsigned long first, unsigned long n);
(2)操作I/O端口:
定义在<asm/io.h>中的一些访问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 long word, unsigned port);
(3)串操作
串I/O函数的原型如下:
1)从内存地址addr开始连续读/写count数目的字节,只对单一端口port读取或写入数据。
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
2)对一个16位的端口读写数据:
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
3)对一个32位的端口读写数据:0
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
3、I/O内存分配和映射
(1)分配内存区域函数接口:
struct resource *request_mem_region(unsigned long start, unsigned long len, char * name);
(2)释放已分配的内存区域:
void release_mem_region(unsigned long start, unsigned long len);
(3)检查给定的区域是否可用:
int check_mem_region(unsigned long start, unsigned long len);//
老函数,不安全,不建议使用
分配完内存后我们必须首先建立映射,映射的建立由ioremap函数来完成。
#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);
4、访问I/O内存
(1)要从I/O内存中读取数据,使用下面的函数之一:
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
注:addr是从ioremap获得的地址。
(2)用于写入I/O内存的函数:
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
(3)如果在给定的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);
(4)上面给出的函数均在给定的addr处执行所有的I/O操作,如果我们在一块I/O内存上执行则使用下面的函数之一:
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);
5、像I/O内存一样使用端口
将端口映射到内存上:
void *ioport_map(unsigned long port, unsigned int count);
当不需要要这种映射时,可调用下面的函数来撤销:
void ioport_unmap(void *addr);