linux设备驱动开发学习--内存和IO访问

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rocky_zhm/article/details/47619849
一 I/O 端口

1. 读写字节端口(8 位宽)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);

2. 读写字端口(16 位宽)
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);

3. 读写长字端口(32 位宽)
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);

4. 读写一串字节。
void insb(unsigned port, void *addr, unsigned long count);  //insb()从端口 port 开始读 count 个字节端口,并将读取结果写入 addr 指向的内存
void outsb(unsigned port, void *addr, unsigned long count);  //outsb()将 addr 指向的内存的 count 个字节连续地写入 port 开始的端口。

5. 读写一串字。
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);

6. 读写一串长字。
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);

二 I/O 内存
在内核中访问 I/O 内存之前,需首先使用 ioremap()函数将设备所处的物理地址映射到虚拟地址
void *ioremap(unsigned long offset, unsigned long size);
ioremap()返回一个特殊的虚拟地址,该地址可用来存取特定的物理地址范围。
通过 ioremap()获得的虚拟地址应该被 iounmap()函数释放,
void iounmap(void * addr);
在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,
但是可以使用 Linux 内核的如下一组函数来完成设备内存映射的虚拟地址的读写.

1. 读 I/O 内存。
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
与上述函数对应的较早版本的函数为(这些函数在 Linux 2.6 中仍然被支持)
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);

2. 写 I/O 内存。
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
与上述函数对应的较早版本的函数为(这些函数在 Linux 2.6 中仍然被支持)
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);

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

4. 写一串 I/O 内存
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);

5. 复制 I/O 内存。
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);

6. 设置 I/O 内存。
void memset_io(void *addr, u8 value, unsigned int count);

三 把 I/O 端口映射到内存空间

void *ioport_map(unsigned long port, unsigned int count);
可以把 port 开始的 count 个连续的 I/O 端口重映射为一段“内存空间”
void ioport_unmap(void *addr);
当不再需要这种映射时,需要调用上面的函数来撤销

四 I/O 端口申请

Linux 内核提供了一组函数用于申请和释放 I/O 端口。
struct resource *request_region(unsigned long first, unsigned long n,const char *name);
这个函数向内核申请 n 个端口,这些端口从 first 开始,name 参数为设备的名称。如果分配成功返回值是非 NULL,如果返回 NULL,则意味着申请端口失败。
void release_region(unsigned long start, unsigned long n);
当用 request_region()申请的 I/O 端口使用完成后,应当使用 release_region()函数将它们归还给系统

五 I/O 内存申请
同样,Linux 内核也提供了一组函数用于申请和释放 I/O 内存的范围。
struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
这个函数向内核申请 n 个内存地址,这些地址从 first 开始,name 参数为设备的名称
void release_mem_region(unsigned long start, unsigned long len);
当用request_mem_region()申请的I/O内存使用完成后,应当使用release_mem_region()函数将它们归还给系统

基于 DMA 的硬件使用总线地址而非物理地址,总线地址是从设备角度上看到的内存地址,
物理地址则是从 CPU 角度上看到的未经转换的内存地址(经过转换的为虚拟地址)

字符设备与块设备 I/O 操作的不同如下。
(1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。
大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。
(2)块设备对于 I/O 请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,
字符设备无须缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,
因为在读写连续的扇区比分离的扇区更快。
(3)字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可随机访问,
但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能。

猜你喜欢

转载自blog.csdn.net/Rocky_zhm/article/details/47619849