Linux内核开发之内存与I/O访问(二)

内核驱动之I/O内存访问 内核驱动之I/O内存访问 I/O 内存访问流程: 1. request_mem_region()  申请IO内存 2.ioremap() 将物理地址映射到虚拟地址 3.ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写 4.iounmap() 释放虚拟内存 5.release_mem_region 阅读详情

“小涛哥,咱们说Linux设备驱动程序说了那么久,怎么从来不说实际设备呢,顶多就说了下内存,总感觉驱动程序是和设备分离的,怎么关联起来..”小王思索着。

“不错,这也正是这次讲课的内容,设备I/O端口与I/O内存的访问”我啊,禁不住拍拍她的头。

  对于一块实际的设备而言,通常会提供一组寄存器来用于控制设备,读写设备和获取设备状态,也就是我们常说的控制寄存器,数据寄存器和状态寄存器。这些寄存器可能位于I/O空间(这时叫做I/O端口),也可能位于内存空间(对应的内存空间被成为I/O内存)。在Linux中提供了一系列的I/O端口和I/O内存操作的接口如下:

  1)I/O端口操作:在Linux设备驱动中,应使用Linux内核提供的函数来访问定位于I/O空间的端口,包括一下几种:

   *读写字节端口(8位宽)

     unsigned inb(unsigned port);【读】          voi outb(unsigned char byte, unsigned port);【写】

   *读写字端口(16位宽)

     unsigned inw(unsigned port);【读】          voi outw(unsigned short word, unsigned port);【写】

   *读写长字端口(32位宽)

     unsigned inl(unsigned port);【读】          voi outl(unsigned longword, unsigned port);【写】

   *读写一串字节

     unsigned insb(unsigned port, void *addr, unsigned long count);【读】      voi outsb(unsigned port, void *addr, unsigned long count);【写】

   *读写一串字

     unsigned insw(unsigned port, void *addr,unsigned long count);【读】      voi outsb(unsigned port, void *addr, unsigned long count);【写】

   *读写一串长字

     unsigned insl(unsigned port, void * addr, unsigned long count);【读】      voi outsb(unsigned port, void *addr, unsigned long count);【写】

    说明:上述各函数中I/O端口port的类型长度依赖与具体的硬件平台,所以只是写出了unsigned

  2)I/O内存:在内核中访问I/O内存之前,需首先使用ioremap()函数将设备所处的物理地址映射到虚拟地址。

   *ioremap()原型:void *ioremap(unsigned long offset, unsigned long size);

     它返回一个特殊的虚拟地址,该地址可用来存取特定的物理地址范围。用它返回的虚拟地址应该使用iounmap()函数释放。

   *iounmap()原型:void iounmap(void *addr);

   现在,有了物理地址锁映射出来的虚拟地址后,我们就可以通过c指针来直接访问这些地址,但Linux内核也提供了一组函数来完成这中虚拟地址的读写。如下

   *读IO内存

     unsigned int ioread8(void *addr);      unsigned int ioread16(void *addr);     unsigned int ioread32(void *addr);  与之对应的较早版本是:

     unsigned readb(address);                unsigned readw(address);                 unsigned readl(address);  这些在2.6的内核中依然可以使用。

   *写IO内存

     void iowrite8(u8 value,void *addr);  void iowrite16(u16 value, void *addr);   void iowrite32(u32 value, void *addr);  与之对应的较早版本是:

     void writeb(unsigned value, address); void writew(unsigned value,address);  void writel(unsigned value,address);  2.6的内核中依然可以使用。

   *读一串IO内存                                                                          *写一串IO内存

     void ioread8_rep(void *addr, void *buf, unsigned long count);       void iowrite8_rep(void *addr, void *buf, unsigned long count);

     void ioread16_rep(void *addr, void *buf, unsigned long count);     void iowrite8_rep(void *addr, void *buf, unsigned long count);

     void ioread32_rep(void *addr, void *buf, unsigned long count);     void iowrite8_rep(void *addr, void *buf, unsigned long count);

   *复制IO内存

     void memcpy_fromio(void *dest, void *source, unsigned int count);

     void memcpy_toio(void *dest, void *source, unsigned int count);

   *设置IO内存

     void *ioport_map(unsigned long port, unsigned int count);

  3)把IO端口映射到内存空间

     void *ioport_map(unsigned long port, unsigned int count);   通过这个函数,可以把port开始的count个连续的IO端口重映射为一段“内存空间”。然后

                                                                                     就可以在其返回的地址上向访问IO内存一样访问这些端口,当不再需要这种映射时,调用:

     void ioport_unmap(void *addr);                                      来撤销这种映射

  4)IO端口申请

     struct resource *request_region(unsigned long first, unsigned long n, const char *name);

     这个函数向内核申请n个端口,这些端口从first开始,name参数为设备的名称,成功返回非NULL.一旦申请端口使用完成后,应当使用:

     void release_region(unsigned long start, unsigned long n);

  5)IO内存申请

    struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

    这个函数向内核申请n个内存,这些地址从first开始,name为设备的名称,成功返回非NULL,一旦申请的内存使用完成后,应当使用:

    void release_mem_region() ;          来释放归回给系统。需要说明的是这两个函数也不是必须的,但建议使用。  

6)通过以上的基础,我们就可以归纳出设备驱动访问IO端口和IO内存的步骤。

   一种方法是:直接使用IO端口操作函数:在设备打开或驱动模块被加载时申请IO端口区域,之后使用inb(),outb()等进行端口访问,最后在设备关闭或驱动被卸载时释放IO端口范围。流程如下:

   123

   另外一种途径是:将IO端口映射为内存进行访问,在设备打开或驱动模块被加载时,申请IO端口区域并使用ioport_map()映射到内存,之后使用IO内存的函数进行端口访问,最后,在设备关闭或驱动模块被卸载时释放IO端口并释放映射,流程如下:

   456

  上边是IO端口的访问方法,至于IO内存的访问方法是:首先调用request_mem_region()申请资源,接着将寄存器地址通过ioremap()映射到内核空间的虚拟地址,之后就可以Linux设备访问编程接口访问这些寄存器了,访问完成后,使用ioremap()对申请的虚拟地址进行释放,并释放release_mem_region()申请的IO内存资源。

流程如下:

   789

 

“小王,感觉怎么样呢, 我是尽最大努力了哈”我说。

“可以,可以,感觉挺好,我觉得哈,你要是多画图,我就没问题,就像上边三个图一样”小王调皮的说。

访问I/O内存和I/O端口设备 前面为了写pwm驱动,仔细研究了下I/O内存和I/O端口设备的区别,以及访问方式。不过,其实也没必要纠结这个了,因为现在绝大部分设备都使用I/O内存映射的。 I/O独立编址和I/O统一编址         首先有两个概念:I/O独立编址和I/O统一编址;记住这两种编址方式都是由CPU架构决定的。         I/O独立编址:应该只有X86处理器才是I/O独立编址,其他的处理器 阅读详情

相关推荐

第11章 内存与IO访问之设备IO端口和I/O内存的访问

11.4 设备I/O端口和I/O内存的访问    设备通常会提供一组寄存器来控制设备、读写设备和获取设备状态,即控制寄存器、数据寄存器和状态寄存器。这些寄存器可能位于I/O空间中,也可能位于内存空间中。当寄存器位于I/O空间时,被称为I/O端口;当寄存器位于内存空间时,对应的内存空间被称为I/O内存。11.4.1 Linux I/O端口和I/O内存访问接口1.I/O端口    在Linux设备驱动...

xiezhi123456的博客 3205

设备I/O端口与I/O内存的访问

<br />对于一块实际的设备而言,通常会提供一组寄存器来用于控制设备,读写设备和获取设备状态,也就是我们常说的控制寄存器,数据寄存器和状态寄存器。这些寄存器可能位于I/O空间(这时叫做I/O端口),也可能位于内存空间(对应的内存空间被成为I/O内存)。在Linux中提供了一系列的I/O端口和I/O内存操作的接口如下:<br />  1)I/O端口操作:在Linux设备驱动中,应使用Linux内核提供的函数来访问定位于I/O空间的端口,包括一下几种:<br />   *读写字节端口(8位宽)<br />  

longjiacheng2006的专栏 1082

Linux设备驱动之I/O端口与I/O内存

从CPU连出来一把线:数据总线、地址总线、控制总线,这把线上挂着N个接口,有相同的,有不同的,名字叫做存储器接口、中断控制接口、DMA接口、并行接口、串行接口、AD接口……一个设备要想接入,就用自己的接口和总线上的某个匹配接口对接……于是总线上出现了各种设备:内存、硬盘,鼠标、键盘,显示器…… 对于CPU而言,如果它要发数据到某个设备,其实是发到对应的接口,接口电路里有多个寄存器(也称为端口

u014379540的博客 1992

设备I/O端口和I/O内存的访问

设备通常会提供一组寄存器来控制设备、读写设备和获取设备状态,即控制寄存器、数据寄存器和状态寄存器。 这些寄器可能位于I/O空间中,也可能位于内存空间中。当位于I/O空间时,通常被称为I/O端口;当位于内存空间时,对应的内存空间被称为I/O内存。 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口,通常包括:控制寄存器、状态寄存器和数据寄存器三大类。 根据访问外设寄存器的不同方式...

QtHalcon 4100

CANopen I/O口设备规范DS401(英文)

CANopen协议中的通用I/O口设备规范,对于数字量,模拟量输入输出均有定义。

基于航位推算与多传感器融合的高精度避障小车设计

航位推算(Dead Reckoning)是一种在无外部定位信号下通过里程计与惯性测量单元(IMU)持续积分估计位姿的基础导航方法,其核心在于运动学建模、误差累积抑制与多源数据融合。该技术具备低依赖、高实时、强确定性等工程优势,广泛应用于服务机器人、AGV、竞赛无人平台等受限环境下的自主导航系统。结合编码器高分辨率测距与九轴IMU姿态解算,可显著抑制纯里程计漂移,提升短时定位精度至厘米级。本文以全国大学生电子设计竞赛K题为背景,深入实现了一种面向确定性场地的离线建图+航位推算双模协同架构,重点解决嵌入式资源约

weixin_36328210的博客 81

二、(正点原子)Linux LED驱动开发实验

对Linux下的驱动编写框架的了解。

Tofu_Cabbage的博客 1247

CentOS7 编译升级GCC至9.3.0

1)下载GCC9.2.0源码并解压 从GCC官方FTP下载地址可以看到GCC最新版本为9.3.0。由于官网下载速度慢,实际上用清华大学的镜像站进行下载,代码如下: wget -c /opt/tmp/ https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz #考虑到安装失败的可能性,创建一个临时文件夹来安装GCC...

haha123486的博客 1万+

浅析Linux设备驱动:IO端口和IO内存

在计算机系统中,外部设备通常会提供一组寄存器或内存用于处理器配置和访问设备功能。这些寄存器或内存可能位于IO空间,或者内存空间。当寄存器或内存位于IO空间时,称为IO端口;当寄存器或内存位于内存空间时,称为IO内存。

Aspiresky的专栏 1635

Oracle 11g R2 RAC 到单实例 Data Guard 搭建(RMAN备份方式)

Oracle RAC集群到单机的dataguard搭建过程

驰兔的博客 1722

保姆级教程:用YOLOv8n模型在RKNN和Horizon平台完成目标检测(附C++部署代码与性能实测)

本文提供了一份详尽的YOLOv8n模型在RKNN和Horizon平台上的部署指南,涵盖环境配置、模型优化、跨平台适配及性能调优。通过实战案例和C++部署代码,帮助开发者在嵌入式设备上高效实现目标检测,显著提升推理性能。

weixin_42659194的博客 468

I/O端口与I/O内存 对外设访问方式

从CPU连出来一把线:数据总线、地址总线、控制总线,这把线上挂着N个接口,有相同的,有不同的,名字叫做存储器接口、中断控制接口、DMA接口、并行接口、串行接口、AD接口……一个设备要想接入,就用自己的接口和总线上的某个匹配接口对接……于是总线上出现了各种设备:内存、硬盘,鼠标、键盘,显示器……         对于CPU而言,如果它要发数据到某个设备,其实是发到对应的接口,接口电路里有多个寄存器

YuZhiHui_No1的专栏 6615

数字图像处理:直方图匹配或规定化Histogram Matching (Specification)原理及感悟

本文介绍了连续图像和数字图像直方图匹配(直方图规定化)的原理、处理过程,并提供了案例进行了讲解。可以看到,直方图均衡处理是直方图匹配的一个重要桥梁。 最后,对于直方图规定化这个翻译个人觉得很low,个人感觉叫指定直方图更好理解,但Histogram Specification怎么翻译确实不太好把握,因此还是叫Histogram Matching直方图匹配最好。

老猿Python 2880

【Linux设备驱动】中的内存管理与I/O访问

通过对Linux设备驱动中的内存管理和I/O访问的深入探讨,我们了解了这些技术的重要性及其背后的实现原理。合理的内存管理可以提升驱动程序的性能和系统的稳定性;高效的I/O访问则能够加速设备与内核之间的数据交换。通过使用内核提供的API和工具,我们可以构建出既高效又可靠的Linux设备驱动程序。希望本文能够帮助读者更好地理解和掌握Linux设备驱动开发的关键技术。

极客代码 1581

MySQL 高可用与集群技术深度解析

高可用性(High Availability,HA)是指系统在较长时间内持续提供服务的能力,通常以系统正常运行时间占总时间的百分比(如 “99.99% 可用性”)来衡量。对于数据库系统而言,高可用性目标是通过冗余设计、故障自动检测与恢复、负载均衡等技术手段,最大限度地减少因硬件故障、软件错误、网络问题等导致的服务中断,确保数据的高可靠性和业务的连续性。

加入“Super Entity”,与全能开发团队共探AI智能体与数字人项目,开启前沿技术之旅。 1502

Electron - Electron failed to install correctly, please delete node_modules/electron and try install

Electron安装失败:Electron failed to install correctly, please delete node_modules/electron and try installing again,electron安装报错:Electron failed to install correctly...解决方案,Electron 未能正确安装,请删除 node_modules/electron,然后再次尝试安装,如何解决这个问题,手把手教你解决,在yarn dev时报错,electr

🏆 前端领域优质创作者 4368

Linux 内存与I/O访问

内存空间与I/O空间

Dylan的博客 855

7.1华为OD机试真题 新系统 - 奇偶三数之和 (Java/Py/C/C++/Js/Go)

7.1华为OD机试真题 新系统 - 奇偶三数之和 (Java/Py/C/C++/Js/Go)

算法大师! 445

【Linux驱动开发】Linux设备驱动中内存与I/O访问的底层机制及技术实现深度解析

本文深入探讨了Linux设备驱动开发中内存与I/O访问的核心技术。主要内容包括:1) 内存访问原理,详细解析MMU的页表结构、地址转换过程和内存映射类型;2) 内核地址空间划分机制,重点介绍DMA内存区域管理;3) 内存屏障与缓存一致性实现;4) 直接内存访问(DMA)操作技术,包括Scatter-Gather机制实现。通过剖析Linux 5.x内核源码,揭示了底层硬件访问的关键机制和优化技术,为设备驱动开发人员提供了深入理解内核内存与I/O管理的重要参考。

love131452098的博客 1117

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【Linux驱动设备开发详解】11.内存与I/O访问

外设可处于CPU的I/O空间和内存空间,除x86外,嵌入式处理器一般只存在内存空间。在Linux系统中,为I/O内存和I/O端口的访问提供了一套同一的方法,访问流程一般为"申请资源->映射->访问->去映射->释放资源"对于有MMU的处理器而言, Linux系统的内部布局比较复杂, 可直接映射的物理内存称为常规内存,超出部分为高端内存。kmalloc() 和__get_free_pages() 申请的内存在物理上连续, 而vmalloc() 申请的内存在物理上不连续。

qq_38089448的博客 985

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Linux设备驱动程序学习(八)——内存与I/O访问

内存空间和I/O空间   在X86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言的,它通过特定的指令in、out来访问。端口号标识了外设的寄存器地址。Intel语法中的in、out指令格式如下: IN 累加器, {端口号│DX} OUT {端口号│DX},累加器   目前,大多数嵌入式微控制器(如ARM、PowerPC等)中并不提供I/O空间,而仅存在内存空间。内存空间可以直接通...

Alex_wu的博客 3058

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Linux 内核源码分析---I/O 体系结构与访问设备

学习记录

学习记录 1845

深入理解 Linux 内核中的 __iomem:I/O 内存访问的 “安全标签”

_iomem// 兼容模式下的定义(如部分编译器不支持扩展属性)但这只是表象。// 主流架构下的实际定义无论形式如何,__iomem的本质是一个用于标记 “I/O 内存指针” 的类型限定符。它不分配内存,不修改指针值,只为编译器和开发者提供 “类型元信息”。__iomem。

2301_80317247的博客 1253

Linux学习一 I/O内存分配与I/O访问

C++中有些用的二联符和三联符 ??=  表示 #    ??)   表示]      ??(   表示[ ??!  表示 |     ??/   表示\      ??-   表示~ ??>  表示 }     ?? %>  表示 }      bitor  表示|     or_eq  表示|= :>  表示 ]      xor    表示^     not    

ShyBoyes的专栏 1302
上一篇: Linux内核开发之内存与I/O访问(一)
下一篇: Linux内核开发之内存与I/O访问(三)
wustdatamining
博客等级 码龄15年 12粉丝 81原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值