[LDD3阅读笔记] 与硬件通信
来源:互联网 发布:制作课件的软件 编辑:程序博客网 时间:2024/05/02 04:56
1. IO端口 (Port IO)
就是我们平时用的Port 80这种
2. IO内存 (Memmap IO)
如PCI,可以把一片寄存器映射到内存区域, 这片内存区域就叫作Memmap IO.
3. 内存屏障
程序会被编译器优化,但这对了访问IO可能会造成致命的错误。我们可以在对硬件以特定执行顺序的操作之间设置内存屏障(memory barrier)。
void barrier(void)
linux/kernel.h
通知编译器插入一个内存屏障,代码会把当前CPU寄存器中的所有修改过的数值保存到内存中,需要时再读出来。这个函数可以避免在屏障前后的编译器优化,但硬件能完成自己的排序。
__asm__ __volatile__("": : :"memory")
void rmb(void)
asm/system.h
保证屏障之前的读操作一定会在后来的读操作之前完成。
都是已编译的指令中流中插入硬件内存屏障
asm volatile("lfence":::"memory")
void read_barrier_depends(void)
----------------------------------------这个与rmb很微妙----------------不管
do { } while (0)
void wmb(void)
保证写操作不会乱序
asm volatile("sfence" ::: "memory")
void mb(void)
保证读写操作都不会乱序
asm volatile("mfence":::"memory")
void smp_rmb(void)
asm/system.h
同上面那一组,仅针对SMP系统编译时有效.
PS:目前好像都是SMP系统了
__asm__ __volatile__("": : :"memory")
void smp_read_barrier_depends(void)
do { } while (0)
void smp_wmb(void)
__asm__ __volatile__("": : :"memory")
void smp_mb(void)
__asm__ __volatile__("": : :"memory")
4. IO端口分配
#include <linux/port.h>
struct resource * request_region(unsigned long first, unsigned long n, const char *name);//失败返回 NULL
void release_region(unsigned long start, unsigned long n);
int check_region(unsigned long first, unsigned long n);//这函数返回成功并不代表request_region就能成功
5. 访问IO端口
#include <asm/io.h>
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);//不同的平台port的位宽可能不一样
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
PS:读返回的都是unsigned, 写则区别数据宽度
串操作
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
从内存地址addr开始连续读/写count数目的字节。只对单一端口port读取或写入数据
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
16位端口
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
32位端口
暂停式I/O
在处理器试图从总线快速传输数据时,某些平台(特别是i386)会遇到问题。解决办法是在每条I/O指令之后,如果还有其它类似指令,则插入一个小的延迟。(PS:我感觉一般不会这么用,如果有这种情况,应该明显的调用delay)
inb_p
outb_p
6. IO内存的分配和映射
分配与释放
struct resource *request_mem_region(unsigned long start, unsigned long len, char * name);
void release_mem_region(unsigned long start, unsigned long len);
分配到的IO内存不一定可以直接访问, 一定要先建立映射
映射
#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);
映射得到的地址空间也不应直接引用(虽然有些平台是可以的),而要通过accessor函数(见下一节)
7. 访问IO内存
#include <asm/io.h>s
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
PS:这一组写函数与读函数好像有点不太一样。。这里用的是u8, u16, u32, 而读是统一用的unsigned int.
重复版本
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);
PS:数据都是连续放的, count是指函数名指定数据大小的个数。
IO内存块
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);
一些老的函数
void readb(address);
unsigned readw(address);
unsigned readl(address);
readq//64位
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);
writeq//64位
8. 像IO内存一样使用IO端口
为了统一IO端口和IO内存,出现了一组软件上的统一函数
void *ioport_map(unsigned long port, unsigned int count);//port 一定要不大于PIO_MASK(即是真正的port IO(64K内)), 否则一定返回NULL
#define PIO_OFFSET0x10000UL
#define PIO_MASK0x0ffffUL
void __iomem *ioport_map(unsigned long port, unsigned int nr)
{
if (port > PIO_MASK)
return NULL;
return (void __iomem *) (unsigned long) (port + PIO_OFFSET);
}
void ioport_unmap(void *addr);
访问只限使用ioread8 之类的函数
PS:这只是软件上的一种封装,对于没有Port IO(IO端口)的系统没作用。 原理(只限x86)是Memmap IO的地址都是高地址,Port IO都在64K以内, 这样就能区分了。 注意不要试图用ioread8()之类的函数去统一所有IO操作(包括64K以下IO), 因为ioread8()访问64K以下IO会报错。
9. ISA
无视, 现在都不用了
- [LDD3阅读笔记] 与硬件通信
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3读书笔记(第9章 与硬件通信)
- LDD3源码分析之与硬件通信&中断处理
- LDD3源码分析之与硬件通信&中断处理
- LDD3学习笔记(12):与硬件通讯
- [LDD3阅读笔记]中断处理
- [LDD3速记]_与硬件通信(I/O端口和I/O内存)
- 与硬件通信
- ldd3笔记
- LDD3笔记
- LDD3 阅读随记
- 第九章:与硬件通信
- 第九章--与硬件通信
- Linux源代码阅读笔记-硬件中断
- 硬件复位芯片SGM4073阅读笔记
- 03-修整素材
- Apache log 日志分析
- linux进程内存布局(转)
- ARMv8(aarch64)页表建立过程详细分析
- 面向对象基础(六)[内部类]
- [LDD3阅读笔记] 与硬件通信
- 直接删除undo及temp表空间文件后的数据库恢复一例
- linux 时钟
- ubuntu搭建git服务器和心得体会
- CKeditor的引用
- CCNA学习笔记1
- 编码操练招式——网球计分
- 使用netstat和awk命令来统计网络连接数
- 表达式计算引擎