LDD3学习7--硬件接口I/O端口(以short为例)
1 理论
1.1 基本概念
目前对外设的操作,都是通过寄存器。寄存器的概念,其实就是接口,访问硬件接口,有I/O端口通信和内存映射I/O (Memory-Mapped I/O),I/O端口通信是比较老的那种,都是老的串口并口设备,PS/2鼠标在用,感觉现在应该用不到了。以我浅显的比喻,就是一个是API通信,一个是内存映射。
另外说说这个IO操作模型和总线的关系,两者其实没有关系,比如说I2C总线可以使用IO端口也可以使用内存映射,实际上用的应该是内存映射,但是这块现在是被封装在open,read这几个接口之后,所以一般也感觉不到。
1.2 CPU缓冲
书里面讲的有点绕,也可能是翻译的问题,其实本质就是多核的情况下,可能后面的变量先于前面的变量生效。
// CPU1: Producer
void update_data() {data = 42; // 更新数据mb(); // 确保data的更新在flag设置之前完成flag = 1; // 设置标志
}
// CPU2: Consumer
void read_data() {while (flag == 0); // 等待标志被设置int value = data; // 读取数据// 使用value进行后续操作
}
在CPU1中:
data = 42;:更新数据。
mb();:插入全内存屏障,确保在此之前的所有内存操作(即data的更新)在此之后的操作(即flag的设置)之前完成。
flag = 1;:设置标志,通知CPU2数据已准备好。
在CPU2中:
while (flag == 0);:等待flag被设置。
int value = data;:读取数据,确保读取的是更新后的值。
1.3 申请IO的API
这里会使用request_region,release_region这几个接口。
申请成功后,会在/proc/ioports看到。
soft@7080:~/memo$ cat /proc/ioports
0000-0000 : PCI Bus 0000:000000-0000 : dma10000-0000 : pic10000-0000 : timer00000-0000 : timer10000-0000 : keyboard0000-0000 : keyboard0000-0000 : rtc00000-0000 : dma page reg0000-0000 : pic20000-0000 : dma20000-0000 : fpu0000-0000 : PNP0C04:000000-0000 : serial0000-0000 : iTCO_wdt0000-0000 : pnp 00:030000-0000 : pnp 00:010000-0000 : pnp 00:010000-0000 : pnp 00:010000-0000 : pnp 00:010000-0000 : pnp 00:010000-0000 : pnp 00:010000-0000 : pnp 00:01
0000-0000 : PCI conf1
0000-0000 : PCI Bus 0000:000000-0000 : pnp 00:030000-0000 : ACPI PM1a_EVT_BLK0000-0000 : ACPI PM1a_CNT_BLK0000-0000 : ACPI PM_TMR0000-0000 : ACPI PM2_CNT_BLK0000-0000 : pnp 00:050000-0000 : ACPI GPE0_BLK0000-0000 : pnp 00:070000-0000 : 0000:00:02.00000-0000 : 0000:00:17.00000-0000 : ahci0000-0000 : 0000:00:17.00000-0000 : ahci0000-0000 : 0000:00:17.00000-0000 : ahci0000-0000 : 0000:00:1f.40000-0000 : i801_smbus
1.4 操作端口
在<asm/io.h>中,使用unsigned inb(unsigned port);void outb(unsigned char byte, unsigned port);unsigned inl(unsigned port);。这里主要的差别是数据的宽度。在底层,8位,16位,32位都必须要做出区别。
用户空间中也可以通过<sys/io.h>的接口操作,但是需要root权限,以及使用ioperm 和 iopl申请权限。
还有接口可以实现直接读取或者写入一串字符:
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
1.5 平台差异
最后是这些IO接口不是所有平台可用,书中列出了这些区别。对我来说,x86,ARM,MIPS这几个平台能用就够了。
2 short代码

书中是和并口设备交互,串口设备如上图。现在实在是找不到这样的设备了。我的重点是后面的USB,所以这次就代码走读为主。代码是short.c,就一个c文件。还是很简单。
module_init(short_init);
module_exit(short_cleanup);
重点就是两个函数,short_init和short_cleanup。
1 short_init
int short_init(void)
{int result;/** first, sort out the base/short_base ambiguity: we'd better* use short_base in the code, for clarity, but allow setting* just "base" at load time. Same for "irq".*/short_base = base;short_irq = irq;/* Get our needed resources. */if (!use_mem) {if (! request_region(short_base, SHORT_NR_PORTS, "short")) {printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",short_base);return -ENODEV;}} else {if (! request_mem_region(short_base, SHORT_NR_PORTS, "short")) {printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n",short_base);return -ENODEV;}/* also, ioremap it */short_base = (unsigned long) ioremap(short_base, SHORT_NR_PORTS);/* Hmm... we should check the return value */}/* Here we register our device - should not fail thereafter */result = register_chrdev(major, "short", &short_fops);if (result < 0) {printk(KERN_INFO "short: can't get major number\n");if (!use_mem) {release_region(short_base, SHORT_NR_PORTS);} else {release_mem_region(short_base, SHORT_NR_PORTS);}return result;}if (major == 0) major = result; /* dynamic */short_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */ /* FIXME */short_head = short_tail = short_buffer;/** Fill the workqueue structure, used for the bottom half handler.* The cast is there to prevent warnings about the type of the* (unused) argument.*//* this line is in short_init() */INIT_WORK(&short_wq, (void (*)(struct work_struct *)) short_do_tasklet);/** Now we deal with the interrupt: either kernel-based* autodetection, DIY detection or default number*/if (short_irq < 0 && probe == 1)short_kernelprobe();if (short_irq < 0 && probe == 2)short_selfprobe();if (short_irq < 0) /* not yet specified: force the default on */switch(short_base) {case 0x378: short_irq = 7; break;case 0x278: short_irq = 2; break;case 0x3bc: short_irq = 5; break;}/** If shared has been specified, installed the shared handler* instead of the normal one. Do it first, before a -EBUSY will* force short_irq to -1.*/if (short_irq >= 0 && share > 0) {result = request_irq(short_irq, short_sh_interrupt,IRQF_SHARED,"short",short_sh_interrupt);if (result) {printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq);short_irq = -1;}else { /* actually enable it -- assume this *is* a parallel port */outb(0x10, short_base+2);}return 0; /* the rest of the function only installs handlers */}if (short_irq >= 0) {result = request_irq(short_irq, short_interrupt,0, "short", NULL);if (result) {printk(KERN_INFO "short: can't get assigned irq %i\n",short_irq);short_irq = -1;}else { /* actually enable it -- assume this *is* a parallel port */outb(0x10,short_base+2);}}/** Ok, now change the interrupt handler if using top/bottom halves* has been requested*/if (short_irq >= 0 && (wq + tasklet) > 0) {free_irq(short_irq,NULL);result = request_irq(short_irq,tasklet ? short_tl_interrupt :short_wq_interrupt,0, "short-bh", NULL);if (result) {printk(KERN_INFO "short-bh: can't get assigned irq %i\n",short_irq);short_irq = -1;}}return 0;
}
首先是request_region,如果配置了内存映射,就是request_mem_region和ioremap。
之后是注册字符设备,register_chrdev。
内存是用的__get_free_pages。
之后的INIT_WORK看起来是处理中断用的。
之后根据probe状态处理probe,有两种,short_kernelprobe和short_selfprobe。这两个的区别还要再看看。
后面是request_irq,之后outb(0x10,short_base+2);向寄存器写入0x10。
在较早的硬件中(例如并口设备),基地址和中断号通常是预定义的,形成了硬件设计上的约定。例如,地址0x378通常对应 IRQ 7,地址0x278通常对应 IRQ 2。
2 short_cleanup
倒是没啥特别的,就是清理。
void short_cleanup(void)
{if (short_irq >= 0) {outb(0x0, short_base + 2); /* disable the interrupt */if (!share) free_irq(short_irq, NULL);else free_irq(short_irq, short_sh_interrupt);}/* Make sure we don't leave work queue/tasklet functions running */if (tasklet)tasklet_disable(&short_tasklet);elseflush_scheduled_work();unregister_chrdev(major, "short");if (use_mem) {iounmap((void __iomem *)short_base);//release_mem_region(short_base, SHORT_NR_PORTS);release_mem_region(base, SHORT_NR_PORTS);} else {release_region(short_base,SHORT_NR_PORTS);}if (short_buffer) free_page(short_buffer);
}
free_irq,unregister_chrdev,release_mem_region,release_region,free_page。
相关文章:
LDD3学习7--硬件接口I/O端口(以short为例)
1 理论 1.1 基本概念 目前对外设的操作,都是通过寄存器。寄存器的概念,其实就是接口,访问硬件接口,有I/O端口通信和内存映射I/O (Memory-Mapped I/O),I/O端口通信是比较老的那种,都是老的串口并口设备&am…...
openharmony电源管理子系统
电源管理子系统 简介目录使用说明相关仓 简介 电源管理子系统提供如下功能: 重启服务:系统重启和下电。系统电源管理服务:系统电源状态管理和休眠运行锁管理。显示相关的能耗调节:包括根据环境光调节背光亮度,和根…...
【Rust自学】13.4. 闭包 Pt.4:使用闭包捕获环境
13.4.0. 写在正文之前 Rust语言在设计过程中收到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。 在本章中,我们会讨论 Rust 的一…...
在 macOS 上,用命令行连接 MySQL(/usr/local/mysql/bin/mysql -u root -p)
根据你提供的文件内容,MySQL 的安装路径是 /usr/local/mysql。要直接使用 mysql 命令,你需要找到 mysql 可执行文件的路径。 在 macOS 上,mysql 客户端通常位于 MySQL 安装目录的 bin 子目录中。因此,完整的路径应该是࿱…...
mono3d汇总
lidar坐标系 lidar坐标系可以简单归纳为标准lidar坐标系和nucense lidar坐标系,参考链接。这个坐标系和车辆的ego坐标系是一致的。 标准lidar坐标系 opendet3d,mmdetection3d和kitt都i使用了该坐标系 up z^ x front| /| /left y <------ 0kitti采…...
K8S 节点选择器
今天我们来实验 pod 调度的 nodeName 与 nodeSelector。官网描述如下: 假设有如下三个节点的 K8S 集群: k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、镜像准备 1.1、镜像拉取 docker pull tomcat:8.5-jre8…...
【2024年华为OD机试】 (C卷,200分)- 反射计数(Java JS PythonC/C++)
一、问题描述 题目解析 题目描述 给定一个包含 0 和 1 的二维矩阵,一个物体从给定的初始位置出发,在给定的速度下进行移动。遇到矩阵的边缘时会发生镜面反射。无论物体经过 0 还是 1,都不影响其速度。请计算并给出经过 t 时间单位后&#…...
AI编程工具使用技巧——通义灵码
活动介绍通义灵码1. 理解通义灵码的基本概念示例代码生成 2. 使用明确的描述示例代码生成 3. 巧妙使用注释示例代码生成 4. 注意迭代与反馈原始代码反馈后生成优化代码 5. 结合生成的代码进行调试示例测试代码 其他功能定期优化生成的代码合作与分享结合其他工具 总结 活动介绍…...
挖掘机检测数据集,准确识别率91.0%,4327张原始图片,支持YOLO,COCO JSON,PASICAL VOC XML等多种格式标注
挖掘机检测数据集,准确识别率91.0%,4327张图片,支持YOLO,COCO JSON,PASICAL VOC XML等多种格式标注 数据集详情 数据集分割 训练组70% 3022图片 有效集20% 870图片 测试集10&…...
使用Docker部署postgresql
使用Docker部署postgresql postgresql数据库在Docker中的镜像的名称为postgres,可以从DockerHub中pull下来,如果pull不下来那么很大概率是网络问题导致的,这时候你可能需要在网上找一些能用的镜像源,以成功拉取postgres镜像。 有…...
LabVIEW时域近场天线测试
随着通信技术的飞速发展,特别是在5G及未来通信技术中,天线性能的测试需求日益增加。对于短脉冲天线和宽带天线的时域特性测试,传统的频域测试方法已无法满足其需求。时域测试方法在这些应用中具有明显优势,可以提供更快速和精准的…...
LabVIEW桥接传感器数据采集与校准程序
该程序设计用于采集来自桥接传感器的数据,执行必要的设置(如桥接配置、信号采集参数、时间与触发设置),并进行适当的标定和偏移校正,最终通过图表呈现采集到的数据信息。程序包括多个模块,用于配置通道、触…...
菜品管理(day03)
公共字段自动填充 问题分析 业务表中的公共字段: 而针对于这些字段,我们的赋值方式为: 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。 在更新数据时, 将updateTime 设置为当前时间…...
深入理解 Android 混淆规则
在 Android 开发中,混淆(Obfuscation)是一种保护代码安全的重要手段,通常通过 ProGuard 或 R8 工具来实现。本文将详细介绍 Android 混淆规则的基本原理、配置方法以及最佳实践,帮助开发者更好地保护应用代码。 博主博…...
《Keras 3 在 TPU 上的肺炎分类》
Keras 3 在 TPU 上的肺炎分类 作者:Amy MiHyun Jang创建日期:2020/07/28最后修改时间:2024/02/12描述:TPU 上的医学图像分类。 (i) 此示例使用 Keras 3 在 Colab 中查看 GitHub 源 简介 设置 本教程将介…...
从 Android 进行永久删除照片恢复的 5 种方法
从 Android 设备中丢失珍贵的照片可能是一种毁灭性的经历。无论是由于意外删除、软件故障还是系统更新,如何从 Android 永久恢复已删除的照片是一个普遍的问题。 幸运的是,有一些解决方案可以帮助找回丢失的记忆。本指南将涵盖您需要了解的有关如何检索…...
SDL2:Android APP编译使用
SDL2:Android APP编译使用 3. SDL2:Android APP编译使用3.1 Android Studio环境准备:3.2 构建Android APP(1)方式一:快速构建APK工程(2)方式二:自定义APK工程(…...
linux systemd 服务连续启动失败,不会再重启分析
1. 问题现象 在Linux 系统中,将自已写的可执行文件放到 systemd 服务中做成service 服务,以支持开机自启和失败重启。但是发现服务在重启多次失败后再也起不来,服务状态是 failed,并且报 start request repeated too quickly. 2.…...
【云岚到家】-day03-门户缓存方案选择
【云岚到家】-day03-门户缓存方案选择 1.门户常用的技术方案 什么是门户 说到门户马上会想到门户网站,中国比较早的门户网站有新浪、网易、搜狐、腾讯等,门户网站为用户提供一个集中的、易于访问的平台,使他们能够方便地获取各种信息和服务…...
在IDEA中使用通义灵码插件:全面提升开发效率的智能助手
在IDEA中使用通义灵码插件:全面提升开发效率的智能助手 随着软件开发行业对效率和质量要求的不断提高,开发者们一直在寻找能够简化工作流程、提升代码质量的工具。阿里云推出的通义灵码插件正是这样一个旨在帮助开发者更高效地编写高质量代码的强大工具…...
从多路复用到三维光阵:Arduino驱动8x8x8 LED立方体全解析
1. 项目概述:用Arduino点亮一个三维世界几年前,我第一次在创客展上看到一个8x8x8的LED立方体,那种由数百个光点构成的、在三维空间中流动的动画效果,瞬间就把我吸引住了。它不像普通的平面LED屏,而是真正有“深度”的光…...
如何在macOS上免费解锁QQ音乐加密文件:完整指南
如何在macOS上免费解锁QQ音乐加密文件:完整指南 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默认转换结果…...
淘宝淘金币自动化脚本终极指南:如何每天节省25分钟实现智能任务管理
淘宝淘金币自动化脚本终极指南:如何每天节省25分钟实现智能任务管理 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/taoji…...
【C语言】C 语言为什么叫 C 语言呢?
【C语言】C 语言为什么叫 C 语言呢?笔记改自于王道训练营资料 其实是因为先有高级语言ALGOL 60,简称 A 语言,后来经过简化,变为 BCPL 语言,简称 B 语言,而 C 语言是在 B 语言的基础之上发展而来的ÿ…...
终极免费音乐解锁工具:5步轻松解密你的加密音乐文件
终极免费音乐解锁工具:5步轻松解密你的加密音乐文件 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https:/…...
XZ6128A工作电压5-100V 输出电流5A 升压型大功率LED灯恒流驱动控制芯片
概述 XZ6128A是一款高效率、高精度的升压型大功率LED灯恒流驱动控制芯片。 XZ6128A内置高精度误差放大器,固定关断时间控制电路,恒流驱动电路等,特别适合大功率、多个高亮度LED灯串的恒流驱动。 XZ6128A采用固定关断时间的控制方式࿰…...
Java网络编程基础分享
在学习 Java 的过程中,网络编程是非常重要的一环。无论是后端开发、分布式系统、即时通讯、文件传输,还是游戏服务、物联网设备,都离不开网络通信一、计算机网络基础1.1 什么是计算机网络把不同地理位置、具有独立功能的计算机,通…...
AI算法工程师如何进行数据预处理?这5个步骤让你的数据更优质
在AI模型开发与测试的全流程中,数据质量直接决定了最终模型的效果上限——哪怕是最先进的大语言模型,用劣质数据训练出来也只能输出劣质结果。对于软件测试从业者来说,不管是参与AI模型的功能测试、性能测试,还是负责测试数据集的…...
基于窗口比较器与晶体管逻辑的可编程非线性电压指示器设计
1. 项目概述:打造一个可编程的“移动光点”电压指示器在电子制作和仪器仪表领域,我们经常需要一个直观的电压指示器。经典的LM3914点/条图显示驱动芯片大家都很熟悉,它能把一个模拟电压信号转换成10个LED的点亮状态,形成移动的光点…...
Mysql?基础语法!!!
作为程序员、数据分析从业者,甚至是产品运营,SQL都是必须掌握的核心技能。不管是后端开发对数据库增删改查,还是数据分析提取业务数据,本质都是在写SQL语句。很多新手觉得SQL难,其实是没有理清逻辑。SQL的核心逻辑非常…...
