linux驱动静态分配内存,Linux驱动设计——内存与IO访问

名词解释

内存空间与IO空间

内存空间是计算机系统里面非系统内存区域的地址空间,现在的通用X86体系提供32位地址,寻址4G字节的内存空间,但一般的计算机只安装256M字节或者更少的内存,剩下的高位内存就被用于PCI或者AGP及系统桥设备的使用上面,主机可以像访问系统内存一样访问这些高端内存,这样对于扩展的设备有更大的空间。

IO空间是X86系统上面的专用空间,现在的IO空间大小是64K字节,从0x0000到0xffff,可以供设备使用,比如南桥很多的设备就是挂在IO空间上,很多的PCI设备也使用IO空间,IO空间寻址使用专门的IO命令来完成。

配置空间是即插即用设备的广义描述,一般的配置空间指的是PCI设备或者PCI桥设备的配置空间,在配置空间里,一般的PCI设备的配置空间是256字节,但很多桥设备都是用扩展的配置空间,比如系统桥空间可以达到1k字节。配置空间为设备提供其配置信息,比如设备的IO基地址,内存基地址和中断号等信息。这些信息由BIOS或者操作系统写入,一般只有驱动程序才会访问配置空间。

在X86 处理器中存在着I/O 空间的概念,I/O 空间是相对于内存空间而言的,它通过特定的指令in、out 来访问。端口号标识了外设的寄存器地址。

目前,大多数嵌入式微控制器如ARM、PowerPC 等中并不提供I/O 空间,而仅存在内存空间。内存空间可以直接通过地址、指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。内存地址可以直接由C 语言指针操作。

例如在186 处理器中执行如下代码:

unsigned char *p = (unsigned char *)0xF000FF00;

*p=11;  //程序的意义为在绝对地址0xF0000+0xFF00(186 处理器使用16 位段地址和16 位偏移地址)写入11。

ARM、PowerPC 等未采用段地址,p 指向的内存空间就是0xF000FF00,而*p = 11 就是在该地址写入11。

Question:对于ARM来说,内存空间0xF000FF00是物理地址还是虚拟地址? Answer:虚拟地址,物理地址必须映射为虚拟地址才可以访问

即便是在X86 处理器中,虽然提供了I/O 空间,如果由我们自己设计电路板,外设仍然可以只挂接在内存空间。此时,CPU 可以像访问一个内存单元那样访问外设I/O 端口,而不需要设立专门的I/O 指令。因此,内存空间是必须的,而I/O 空间是可选的。

20180921225126658648.png

内存管理单元(MMU)

功能:内存管理(虚拟地址和物理地址的映射、内存访问权限保护和Cache 缓存控制等硬件支持)

操作原理:

内存的存取

设备IO 端口和IO 内存的访问

设备通常会提供一组寄存器来用于控制设备、读写设备和获取设备状态,即控制寄存器、数据寄存器和状态寄存器。这些寄存器可能位于I/O 空间,也可能位于内存空间。当位于I/O 空间时,通常被称为I/O 端口,位于内存空间时,对应的内存空间被称为I/O 内存。

当然对于ARM来说,这些端口寄存器是位于内存空间(IO内存)。

Linux对IO端口的访问

把IO端口映射到内存空间

以后用到再来添加

Linux对IO内存的访问

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

//ioremap()的原型如下:

void *ioremap(unsigned long offset, unsigned long size);

//通过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);

beep_ioremap实例

...

//Port GPBx Register address declaration

#define GPBCON (unsigned long)ioremap(0x56000010,4)

#define GPBDAT (unsigned long)ioremap(0x56000014,4)

#define GPBUP (unsigned long)ioremap(0x56000018,4)

...

void beep_start( void )

{

/*config GPBCON, set GPB0 as output port*/

port_status = readl(GPBCON);

port_status &= ~0x03;

port_status |= 0x01;

writel(port_status,GPBCON);

port_status = readl(GPBDAT);

port_status |= 0x01; // set 1 to GPB0

writel(port_status,GPBDAT);

}

void beep_stop( void )

{

/*config GPBCON, set GPB0 as output port*/

port_status = readl(GPBCON);

port_status &= ~0x03;

port_status |= 0x01;

writel(port_status,GPBCON);

port_status = readl(GPBDAT);

port_status &= ~0x01;// set 0 to GPB0

writel(port_status,GPBDAT);

}

...

申请和释放IO端口和IO内存

(此处只看IO内存)

//一组函数用于申请和释放I/O 内存的范围,成对存在

//这个函数向内核申请n 个内存地址,这些地址从first 开始,name 参数为设备的名称。如果分配成功返回值是非 NULL,如果返回NULL,则意味着申请I/O 内存失败。

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

//

void release_mem_region(unsigned long start, unsigned long len);

上述request_region()和release_mem_region()都不是必须的,但建议使用。其任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源时就会失败。

有许多设备驱动程序在没有申请I/O 端口和I/O 内存之前就直接访问了,这不够安全。

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

I/O 内存的访问步骤:

1. 调用request_mem_region()申请资源

2.将寄存器地址通过ioremap()映射到内核空间虚拟地址

3. 通过Linux 设备访问编程接口访问这些设备的寄存器

4. 访问完成后,应对ioremap()申请的虚拟地址进行释放,并释放release_mem_region()申请的I/O 内存资源

20180921225127338362.png

将设备地址映射到用户空间

1.内存映射与VMA

一般情况下,用户空间是不可能也不应该直接访问设备的,但是,设备驱动程序中可实现mmap()函数,这个函数可使得用户空间直能接访问设备的物理地址。实际上,mmap()实现了这样的一个映射过程:它将用户空间的一段内存与设备内存关联,当用户访问用户空间的这段地址范围时,实际上会转化为对设备的访问。

(这种能力对于显示适配器一类的设备非常有意义,如果用户空间可直接通过内存映射访问显存的话,屏幕帧的各点的像素将不再需要一个从用户空间到内核空间的复制的过程。)

mmap()必须以PAGE_SIZE 为单位进行映射,实际上,内存只能以页为单位进行映射,若要映射非PAGE_SIZE 整数倍的地址范围,要先进行页对齐,强行以PAGE_SIZE 的倍数大小进行映射。

//驱动中mmap()函数的原型

int(*mmap)(struct file *, struct vm_area_struct*);

//应用层的系统调用

caddr_t mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset);

/*

参数fd 为文件描述符,一般由open()返回,fd 也可以指定为!1,此时需指定flags 参数MAP_ANON,表明进行的是匿名映射。

len 是映射到调用用户空间的字节数,它从被映射文件开头offset 个字节开始算起,offset 参数一般设为0,表示从文件头开始映射。

prot 参数指定访问权限,可取如下几个值的“或”:PROT_READ(可读)、PROT_WRITE(可写)、PROT_EXEC(可执行)和PROT_NONE(不可访问)。

参数addr 指定文件应被映射到用户空间的起始地址,一般被指定为NULL,这样,选择起始地址的任务将由内核完成,而函数的返回值就是映射到用户空间的地址。其类型caddr_t 实际上就是void *。

*/

当用户调用mmap()的时候,内核会进行如下处理。

① 在进程的虚拟空间查找一块VMA。

② 将这块VMA 进行映射。

③ 如果设备驱动程序或者文件系统的file_operations 定义了mmap()操作,则调用它。

④ 将这个VMA 插入进程的VMA 链表中。

IO内存静态映射

DMA

原文:http://www.cnblogs.com/kwseeker-bolgs/p/4471164.html

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

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

相关文章

Windows Socket 编程_ 简单的服务器/客户端程序

一。程序运行效果图二。程序源代码 三。程序设计相关基础知识 1.计算机网络 2.IP地址 3.协议 4.网络体系结构 5.TCP/IP体系结构与特点 6.客户机/服务器模式 7.TCP/IP特点 8.套接字的引入 9.面向 连接/无连接 的套接字的系统调用时序图/流程图 …

JDK / JRE zip

Server JRE与JRE的区别:Server JRE一般用于服务器上安装,只有64bit版本,不会安装浏览器插件、自动更新,有监视工具、没有Java Fx和其他开发工具;有安装程序,只是一压缩目录。Server JRE 8大约是完整JDK的40…

linux 磁盘挂载sde,linux lvm挂载新的硬盘并且扩容

PV(Physical Volume)- 物理卷物理卷在逻辑卷管理中处于最底层,它可以是实际物理硬盘上的分区,也可以是整个物理硬盘,也可以是raid设备。VG(Volumne Group)- 卷组卷组建立在物理卷之上,一个卷组中至少要包括一个物理卷,…

TDH-大数据基础

------------------------------------------------------------------------------------*******大数据概念和基础**********1.大数据的四个特点:数据规模大,生成、处理速度快,数据类型多样,价值巨大密度低;2.大数据历…

xshell十大技巧

xshell是我用过的最好用的ssh客户端工具,没有之一。这个软件完全免费,简单易用,可以满足通过ssh管理linux vps所有需要,唯一遗憾的是没有官方中文版。警告:不要下载所谓的汉化版,可能有木马。此前已有报道使…

深度linux 网络配置文件,solver及其配置 - Caffe 深度学习入门教程_Linux教程_Linux公社-Linux系统门户网站...

solver及其配置solver算是caffe的核心的核心,它协调着整个模型的运作。caffe程序运行必带的一个参数就是solver配置文件。运行代码一般为#caffe train --solver*_slover.prototxt在Deep Learning中,往往loss function是非凸的,没有解析解&…

restful服务端客户端_测试RESTful服务的客户端

restful服务端客户端开发使用RESTful Web API的应用程序可能意味着开发服务器和客户端。 为服务器端编写集成测试可以像使用Arquillian启动服务器一样容易,并且可以通过REST确保测试服务是否按预期工作。 问题是如何测试客户端。 在本文中,我们将了解如何…

笑郭网络验证3.8研究笔记(内有视频教程)

链接:http://pan.baidu.com/s/1kUVkY2N 密码:m6de 转载于:https://www.cnblogs.com/Sendige/p/9600782.html

Java开发人员必须看到的13个Decks保持更新

Java领域中有许多关键人物,每个人物对语言的未来都有自己的看法和看法。 尽管我们不能参加任何活动,聚会或谈话,但要感谢各种幻灯片共享站点,我们才能听到这些影响者的意见。 在下面的文章中,我们收集了关于Java的最好…

已经windows如何在安装linux,如何在已经安装linux情况下安装windows

1.找到磁盘分区工具,将一个主分区磁盘格式化为windows认可的文件系统,如: ntfs.2.安装windows,或ghost to this partition。(最好是ghost,这样不会抢linux的启动区域。那样的话就改的就是windows下的文件。3.更改linux或windows下的grub.以liunx(ubuntu)…

标准模板库STL学习总结

标准模板库就是类与函数模板的大集合。STL共有6种组件:容器,容器适配器,迭代器,算法,函数对象和函数适配器。1、容器: 容器是用来存储和组织其他对象的对象。STL容器类的模板在标准头文件中定义。主要如下所…

MicroRNA Ranking(Tehran2016)

题意&#xff1a;给出m个n的全排列&#xff0c;求一个n的全排列&#xff0c;满足对于i<j&#xff0c;至少存在一半的全排列中&#xff0c;ai排在aj的前面&#xff0c;求字典序最小方案&#xff0c;或者是无解。 (1)首先我们对 vis[ a[i] ][ a[j] ] ,求出a[i] 对 a[j] 的贡献…

linux gpt引导分区,linux 引导gpt分区windows及clover

原因&#xff1a;电脑采用gpt分区&#xff0c;安装了win10linux(debian)mac 10.11(El capitan),使用clover做引导器时&#xff0c;可以引导linux和mac,但通过bootmgfw.efi引导win10时无限转圈,不能正常启动win10。所以考虑用linux的grubx引导器。1、引导Clocer查看clover所在EF…

查看表状态及索引碎片语句

语句&#xff1a;DBCC SHOWCONTIG (SrMissedCalls) WITH ALL_INDEXES&#xff1b; 其中‘SrMissedCalls’是表名字。 执行之后展示内容&#xff1a; 转载于:https://www.cnblogs.com/shendaxian/p/9604826.html

visualvm远程jvm_VisualVM:通过SSH监视远程JVM(是否为JMX)

visualvm远程jvmVisualVM是用于监视JVM&#xff08;5.0&#xff09;的有关内存使用&#xff0c;线程&#xff0c;GC&#xff0c;MBeans等的出色工具。让我们看看如何通过SSH使用它来监视&#xff08;甚至使用JMX进行配置&#xff09;使用JMX或不使用JMX的远程JVM。它。 这篇文章…

如何切换pip的源

参考别人的帖子https://blog.csdn.net/chenghuikai/article/details/55258957 转载于:https://www.cnblogs.com/PoeticalJustice/p/9609659.html

shp文件的读取

转载自&#xff1a;http://blog.csdn.net/gisfarmer/article/details/3861554做GIS开发的朋友可能对shp并不陌生&#xff0c;但是看到CSDN网友不断提问关于shp文件的一些问题&#xff0c;利用闲暇我对shp文件的一些知识加以总结&#xff0c;共享CSDN网友。首先了解一下shp文件的…

Java 8:CompletableFuture与并行流

这篇文章展示了Java 8的CompletableFuture在执行异步计算时如何与并行流进行比较。 我们将使用以下类对长时间运行的任务进行建模&#xff1a; class MyTask {private final int duration;public MyTask(int duration) {this.duration duration;}public int calculate() {Sy…

linux使用gpio开一个线程,LINUX的gpio_request_one作用

一直习惯使用gpio_request来申请一个GPIO&#xff0c;然后用gpio_direction_input、gpio_direction_output等函数来配置对应的GPIO&#xff0c;用gpio_free来释放申请。后来看到别人也会用gpio_request_one来申请和配置一个GPIO&#xff0c;然后就去看看看这个接口的实现&#…

Python制作回合制手游外挂简单教程(下)

引入&#xff1a; 接着上篇的博文&#xff0c;今天我们讲如何实现助人为乐 前期准备&#xff1a; 如何获取图片中指定文字的坐标&#xff1f; 我的思路是截取一个小区域&#xff0c;再根据小区域左上角的坐标获取中央坐标 例如&#xff1a; 获取坐上角的x和y坐标&#xff0c;测…