[LDD3速记]_与硬件通信(I/O端口和I/O内存)

一、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);
/* 撤销映射 */


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值