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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/3474.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端【3】--CSS布局,CSS实现横向布局,盒子模型

盒子分类 1、块级盒子 2、内联级盒子 3、内联块级盒子 4、弹性盒子 5、盒子内部分区 方法一&#xff1a;使用 float 普通盒子实现横向布局 方法二&#xff1a;使用 display: inline-block 内联块级元素实现横向布局 方法三&#xff1a;使用弹性盒子 flexbox&#xff0…

初学stm32 --- flash模仿eeprom

目录 STM32内部FLASH简介 内部FLASH构成&#xff08;F1&#xff09; FLASH读写过程&#xff08;F1&#xff09; 闪存的读取 闪存的写入 内部FLASH构成&#xff08;F4 / F7 / H7&#xff09; FLASH读写过程&#xff08;F4 / F7 / H7&#xff09; 闪存的读取 闪存的写入 …

LLM - 大模型 ScallingLaws 的 CLM 和 MLM 中不同系数(PLM) 教程(2)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145188660 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

【数据库】MySQL数据库SQL语句汇总

目录 1.SQL 通用语法 2.SQL 分类 2.1.DDL 2.2.DML 2.3.DQL 2.4.DCL 3.DDL 3.1.数据库操作 3.1.1.查询 3.1.2.创建 3.1.3.删除 3.1.4.使用 3.2.表操作 3.2.1.查询 3.2.2.创建 3.2.3.数据类型 3.2.3.1.数值类型 3.2.3.2.字符串类型 3.2.3.3.日期时间类型 3.2…

JavaEE之CAS

上文我们认识了许许多多的锁&#xff0c;此篇我们的CAS就是从上文的锁策略开展的新概念&#xff0c;我们来一探究竟吧 1. 什么是CAS&#xff1f; CAS: 全称Compare and swap&#xff0c;字⾯意思:“比较并交换”&#xff0c;⼀个CAS涉及到以下操作&#xff1a; 我们假设内存中…

【Go】Go数据类型详解—指针

1. 前言 在我看来&#xff0c;一门编程语言语法的核心就在于数据类型。而各类编程语言的基本数据类型大致相同&#xff1a;int整型、float浮点型、string字符串类型、bool布尔类型&#xff0c;但是在一些进阶数据类型上就有所不同了。本文将会介绍Go语言当中核心的数据类型——…

前端性能-HTTP缓存

前言 开启 HTTP 缓存是提升前端性能的常见手段之一。通过缓存&#xff0c;浏览器可以临时存储资源&#xff0c;在后续请求中直接使用本地副本&#xff0c;从而有效减少 HTTP 请求次数&#xff0c;显著缩短网页加载时间。以下是 HTTP 缓存的几个关键点&#xff1a; 1、减少重复…

2024CVPR《HomoFormer》

这篇论文提出了一种名为HomoFormer的新型Transformer模型,用于图像阴影去除。论文的主要贡献和创新点如下: 1. 研究背景与动机 阴影去除的挑战:阴影在自然场景图像中普遍存在,影响图像质量并限制后续计算机视觉任务的性能。阴影的空间分布不均匀且模式多样,导致传统的卷积…

arcgis提取不规则栅格数据的矢量边界

效果 1、准备数据 栅格数据:dem或者dsm 2、栅格重分类 分成两类即可 3、新建线面图层 在目录下选择预先准备好的文件夹,点击右键,选择“新建”→“Shapefile”,新建一个Shapefile文件。 在弹出的“新建Shapefile”对话框内“名称”命名为“折线”,“要素类型”选…

函数(函数的概念、库函数、自定义函数、形参和实参、return语句、数组做函数参数、嵌套调用和链式访问、函数的声明和定义、static和extern)

一、函数的概念 •C语⾔中的函数&#xff1a;⼀个完成某项特定的任务的⼀⼩段代码 •函数又被翻译为子函数&#xff08;更准确&#xff09; •在C语⾔中我们⼀般会⻅到两类函数&#xff1a;库函数 ⾃定义函数 二、库函数 1 .标准库和头文件 •C语⾔的国际标准ANSIC规定了⼀…

Docker私有仓库管理工具Registry

Docker私有仓库管理工具Registry 1 介绍 Registry是私有Docker仓库管理工具&#xff0c;Registry没有可视化管理页面和完备的管理策略。可借助Harbor、docker-registry-browser完成可视化和管理。Harbor是由VMware开发的企业级Docker registry服务。docker-registry-browser是…

Adobe与MIT推出自回归实时视频生成技术CausVid。AI可以边生成视频边实时播放!

传统的双向扩散模型&#xff08;顶部&#xff09;可提供高质量的输出&#xff0c;但存在显著的延迟&#xff0c;需要 219 秒才能生成 128 帧的视频。用户必须等待整个序列完成才能查看任何结果。相比之下CausVid将双向扩散模型提炼为几步自回归生成器&#xff08;底部&#xff…

MySQL(高级特性篇) 06 章——索引的数据结构

一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据查找时&#xff0c;首先查看查询条件…

turtle教学课程课堂学习考试在线网站

完整源码项目包获取→点击文章末尾名片&#xff01;

python中的RPA->playwright自动化录制脚本实战案例笔记

playwright录制功能使用绕过登录操作 1、首先安装playwright pip install playwright2、 安装支持的浏览器 playwright install # 安装支持的浏览器&#xff1a;cr, chromium, ff, firefox, wk 和 webkit3、接着在自己的项目下运行录制命令&#xff1a; playwright codegen…

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一&#xff0c;它的作用是为电脑的各个部件提供冷却&#xff0c;防止电脑过热。然而&#xff0c;有时候我们会发现电脑风扇的声音特别大&#xff0c;不仅影响我们的使用体验&#xff0c;也可能是电脑出现了一些问题。那么&#xff0c;电脑风扇声音大…

python如何解析word文件格式(.docx)

python如何解析word文件格式&#xff08;.docx&#xff09; .docx文件遵从开源的“Office Open XML标准”&#xff0c;这意味着我们能用python的文本操作对它进行操作&#xff08;实际上PPT和Excel也是&#xff09;。而且这并不是重复造轮子&#xff0c;因为市面上操作.docx的…

PHP智慧小区物业管理小程序

&#x1f31f;智慧小区物业管理小程序&#xff1a;重塑社区生活&#xff0c;开启便捷高效新篇章 &#x1f31f; 智慧小区物业管理小程序是一款基于PHPUniApp精心雕琢的智慧小区物业管理小程序&#xff0c;它犹如一股清新的科技之风&#xff0c;吹进了现代智慧小区的每一个角落…

26个开源Agent开发框架调研总结(一)

根据Markets & Markets的预测&#xff0c;到2030年&#xff0c;AI Agent的市场规模将从2024年的50亿美元激增至470亿美元&#xff0c;年均复合增长率为44.8%。 Gartner预计到2028年&#xff0c;至少15%的日常工作决策将由AI Agent自主完成&#xff0c;AI Agent在企业应用中…

云消息队列 Kafka 版 V3 系列荣获信通院“云原生技术创新标杆案例”

2024 年 12 月 24 日&#xff0c;由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;主办的“2025 中国信通院深度观察报告会&#xff1a;算力互联网分论坛”&#xff0c;在北京隆重召开。本次论坛以“算力互联网 新质生产力”为主题&#xff0c;全面展示中国…