Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析
一、 ioremap() 函数基础概念
几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:
a -- I/O 映射方式(I/O-mapped)
典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。
b -- 内存映射方式(Memory-mapped)
RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。
但是,这两者在硬件实现上的差异对于软件来说是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是"I/O内存"资源。
一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。
Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中(这里是内核空间),原型如下:
1、ioremap函数
ioremap宏定义在asm/io.h内:
#define ioremap(cookie,size) __ioremap(cookie,size,0)
__ioremap函数原型为(arm/mm/ioremap.c):
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
参数:
phys_addr:要映射的起始的IO地址
size:要映射的空间的大小
flags:要映射的IO空间和权限有关的标志
该函数返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源。
2、iounmap函数
iounmap函数用于取消ioremap()所做的映射,原型如下:
void iounmap(void * addr);
二、 ioremap() 相关函数解析
在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。
读写I/O的函数如下所示:
a -- writel()
writel()往内存映射的 I/O 空间上写数据,wirtel() I/O 上写入 32 位数据 (4字节)。
原型:void writel (unsigned char data , unsigned short addr )
b -- readl()
readl() 从内存映射的 I/O 空间上读数据,readl 从 I/O 读取 32 位数据 ( 4 字节 )。
原型:unsigned char readl (unsigned int addr )
变量 addr 是 I/O 地址。
返回值 : 从 I/O 空间读取的数值。
具体定义如下:
- #define readb __raw_readb
- #define readw(addr) __le16_to_cpu(__raw_readw(addr))
- #define readl(addr) __le32_to_cpu(__raw_readl(addr))
- #ifndef __raw_readb
- static inline u8 __raw_readb(const volatile void __iomem *addr)
- {
- return *(const volatile u8 __force *) addr;
- }
- #endif
- #ifndef __raw_readw
- static inline u16 __raw_readw(const volatile void __iomem *addr)
- {
- return *(const volatile u16 __force *) addr;
- }
- #endif
- #ifndef __raw_readl
- static inline u32 __raw_readl(const volatile void __iomem *addr)
- {
- return *(const volatile u32 __force *) addr;
- }
- #endif
- #define writeb __raw_writeb
- #define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr)
- #define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)
三、使用实例
还是拿我们写PWM驱动的实例来解析
1、这里我们先定义了一些寄存器,这里使用的地址均是物理地址:
- #define GPD0CON 0x114000a0
- #define TIMER_BASE 0x139D0000
- #define TCFG0 0x0000
- #define TCFG1 0x0004
- #define TCON 0x0008
- #define TCNTB0 0x000C
- #define TCMPB0 0x0010
2、为了使用内存映射,我们需先定义指针用来保存内存映射后的地址:
- static unsigned int *gpd0con;
- static void *timer_base;
注意:这里timer_base 指针指向的类型设为 void, 主要因为上面使用了地址偏移,使用void 更有利于我们使用;
3、使用ioremap() 函数进行内存映射,并将映射的地址赋给我们刚才定义的指针
- gpd0con = ioremap(GPD0CON,4);
- timer_base = ioremap(TIMER_BASE,0x14);
4、得到地址后,可以调用 writel() 、readl() 函数进行相应的操作
- writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
- writel ((readl(timer_base +TCFG0 )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
- writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
- writel (500, timer_base +TCNTB0 );
- writel (250, timer_base +TCMPB0 );
- writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
可以看到,这里先从相应的地址中读取数据,修改完毕后,再利用writel函数进行数据写入。
Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析相关推荐
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
现在,我们来编写自己第一个字符设备驱动 -- 点亮LED.(不完善,后面再完善) 硬件平台:Exynos4412(FS4412) 编写驱动分下面几步: a -- 查看原理图.数据手册,了解设备的操作方 ...
- Linux 字符设备驱动开发基础(六)—— VFS 虚拟文件系统解析
一.VFS 虚拟文件系统基础概念 Linux 允许众多不同的文件系统共存,并支持跨文件系统的文件操作,这是因为有虚拟文件系统的存在.虚拟文件系统,即VFS(Virtual File System)是 ...
- Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动
编写驱动的第一步仍是看原理图: 可以看到,该蜂鸣器由 GPD0_0 来控制 ,查手册可知该I/O口由Time0 来控制,找到相应的寄存器: a -- I/O口寄存器及地址 GPD0CON 0x114 ...
- Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析
我们在前面讲到了file_operations,其是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL.其中有最重要的几个函数,分别是open().r ...
- Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析
解析完 open.close.read.write 四个函数后,终于到我们的 ioctl() 函数了 一. 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通 ...
- Linux 字符设备驱动开发--内存读写操作
学习Linux的累计时间已经有两年多了,工作关系,学习的过程总是断断续续的,现在整理一下,下面要分享的是一个简单的linux驱动程序,将内存当作一个虚拟的设备去读写,没有什么实际的用处,像hello ...
- Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析
前面在 Linux 字符设备驱动开发基础 (三)-- 字符设备驱动结构(中) ,我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} ,下面来介绍另 ...
- Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)
文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...
- Linux字符设备驱动详解四(使用自属的xbus驱动总线)
文章目录 系列文章目录 前言 驱动目录 正文 驱动总线 总线管理 总线注册 设备注册 驱动注册 代码示例 总结 系列文章目录 Linux字符设备驱动详解 Linux字符设备驱动详解二(使用设备驱动模型 ...
最新文章
- Vim 相关插件整理
- Qt学习五 - 对话框
- iOS----JSON解析
- 1.2开发文档简读,了解全貌.mp4
- 利用类定义一个指针会调用默认构造函数吗_C++的拷贝构造函数
- SpringCloud 教程 | 第一篇: 服务的注册与发现Eureka
- 纳德拉:微软正计划“终极移动设备”
- 【MyBatis笔记】01-MyBatis入门程序
- 网络爬虫随记:2018-03-12启(refreshing)
- Oracle 统计信息收集
- WaitForMultipleObjects、WaitForSingleObject、GetExitCodeThread
- Python中关于with open file as 的用法
- 【期末大作业】基于HTML+CSS+JavaScript南京大学网页校园教育网站html模板(3页)
- 数据库设计-多级栏目(标题)分类设计
- 如何查询中文期刊影响因子
- 第八届蓝桥杯(国赛)——瓷砖样式
- 理科爱好者杂志理科爱好者杂志社理科爱好者编辑部2022年第3期目录
- export命令在Mac Pycharm上如何设置环境变量!_ CodingPark编程公园
- php 项目总结,项目总结
- 奶茶介绍-网页实验报告