`
helloyesyes
  • 浏览: 1271865 次
  • 性别: Icon_minigender_2
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Linux内核访问外设I O资源的方式

阅读更多

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/15/6421954.aspx

首先介绍一下I/O端口和I/O内存。

1.I/O端口:当一个寄存器或内存位于I/O空间时,称其为I/O端口。

2.I/O内存:当一个寄存器或内存位于内存空间时,称其为I/O内存。

再来看一下I/O寄存器和常规内存的区别:I/O寄存器具有边际效应(side effect),而内存操作则没有,内存写操作的唯一结果就是在指定位置存贮一个数值;内存读操作则仅仅是返回指定位置最后一次写入的数值。何为边际效应呢?就是读取某个地址时可能导致该地址内容发生变化。比如很多设备的中断状态寄存器只要一读取,便自动清零。

现在来看一看如何在Linux驱动程序中使用I/O端口和I/O内存。

使用I/O端口的步骤:

1.申请

2.访问

3.释放

申请I/O端口:

在尚未对这些端口进行申请之前我们是不应该对这些端口进行操作的。内核为我们提供了一个注册用的接口:

这个函数告诉内核,我们要使用起始于firstn个端口,参数name应该是设备的名称。如果分配成功,则返回非NULL。如果request_region返回NULL,那么我们就不能使用这些期望的端口。

访问I/O端口:

访问I/O端口时,多数硬件都会把8位,16位和32位的端口区分开。因此,C语言程序中必须调用不同的函数来访问大小不同的端口。

字节(8位宽度)读写端口。

读写16位端口。

读写32位端口。

释放I/O端口:

如果不在使用某组I/O端口(可能在卸载模块时),则应该使用下面的函数将这些端口返回给系统:

使用I/O内存的步骤:

1.申请

2.映射

3.访问

4.释放

根据计算机平台和所使用总线的不同,I/O内存可能是,也可能不是通过页表访问的。如果访问是经由页表进行的,内核必须首先安排物理地址使其对设备驱动程序可见(这通常意味着在进行任何I/O之前必须先调用ioremap)。如果访问无需页表,那么I/O内存区域就非常类似于I/O端口,可以使用适当形式的函数读写它们。

I/O内存申请:

该函数从start开始分配len字节长的内存区域。如果成功,返回非NULL指针;否则返回NULL值。

I/O内存映射:

把物理地址转化成虚拟地址,返回值是虚拟地址。

解除映射。

I/O内存访问:

从内存中读取:

其中addr应该是从ioremap获得的地址。

还有一组写入I/O内存类似函数:

I/O内存释放:

I/O内存一样使用I/O端口:

该函数重新映射countI/O端口,使其看起来像I/O内存。此后,驱动程序可在该函数返回的地址上使用ioread8及其同类的函数。

当不再需要这种映射时,需要调用下面的函数来撤销:

上边的是基础知识。

在我们的开发板上内存映射分3个层次(下边的所有内核代码使用的是linux3.6.30.4):

1.开发板的层次

如:声卡,网卡和开发板相关的部分

2.最小系统的层次

系统必须的几个,如GPIO,IRQ,MEMCTRL,UART

3.其他系统的层次

不影响开机的部分,如USB,LCD,ADC

开发板mapio的初始化:

smdk2440_map_io函数中会调用:

最小系统IO初始化:

s3c24xx_init_io函数会调用:

这个部分是系统启动必须的映射。后续会调用(cpu->map_io(mach_desc,size);来完成其他映射。这个函数会调用:

所以,如果新添加一个驱动,首先要看是否完成了IO映射,如果没有的话,就在开发板部分加入。Linux内核访问外设I/O资源的方式有两种:动态映射和静态映射(map_desc)

动态映射方式上边已经讲述,这里着重讲述静态映射——通过map_desc结构静态创建I/O资源映射表。内核提供了在系统启动时通过map_desc结构体静态创建I/O资源到内核地址空间的线性映射表(即page table)的方式。这种映射表是一种一一映射的关系。程序员可以自己定义该I/O内存资源映射后的虚拟地址。创建好静态映射表,在内核或驱动中访问该I/O资源时则无需再进行ioremap动态映射,可以直接通过映射后的I/O虚拟地址去访问。下面详细分析这种机制的原理并举例说明如何通过这种静态映射的方式访问外设I/O内存资源。

内核提供了一个重要的结构体struct machine_desc,这个结构体在内核移植中起到相当重要的作用,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体包含了体系结构相关部分的几个重要成员的初始化函数,包括map_io,init_irq,init_machine以及phys_iotimer成员等。

machine_desc结构体定义如下:

这里的map_io成员即内核提供给用户的创建外设I/O资源到内核虚拟地址静态映射表的接口函数。map_io成员函数会在系统初始化过程中被调用,流程如下:

start_kernel->setup_arch()->paging_init()->devicemaps_init()中被调用。

machine_desc结构体通过MACHINE_START宏来初始化。

用户可以在定义machine_desc结构时指定map_io的接口函数。s3c2410 machine_desc结构体如下定义:

展开后的结果是:

下边是smdk2440_map_io函数:

它调用了s3c24xx_init_io函数:

所以,smdk2410_map_io最终调用iotable_init建立映射表。

iotable_init函数的参数有两个:一个是map_desc结构体,另一个是该结构体的数量nr。这里最关键的就是struct map_descmap_desc结构的定义如下:

map_desc定义在arch/arm/include/asm/mach/map.h中,

create_mapping函数就是通过map_desc提供的信息创建线性映射表的。

这样的话我们就可以知道了创建I/O映射表的大致流程为:只要定义相应的I/O资源的map_desc结构体,并将该结构体传给iotable_init函数执行,就可以创建相应的I/O资源到内核虚拟地址空间的映射表了。我们来看看s3c2410是怎么定义map_desc结构体的。

IODESC_ENT定义在arch/arm/plat-s3c/include/plat/cpu.h中,展开后等价于:

这里S3C24XX_PA_TIMERS3C24XX_VA_TIMER为定义在arch/arm/plat-s3c24xx/include/plat/map.hTIMER寄存器的物理地址和虚拟地址。在这里map_desc结构体的virtual成员被初始化为S3C24XX_VA_TIMERpfn成员值通过__phys_to_pfn内核函数计算,只要传递给它该I/O的物理地址就可行了。Length为映射资源的大小。MT_DEVICEI/O类型,通常定义为MT_DEVICE。这里最重要的即virtual成员的值S3C24XX_VA_ TIMER,这个值即该I/O资源映射后的内核虚拟地址,创建映射表成功后,便可以在内核或驱动中直接通过该虚拟地址访问这个I/O资源。

在ARM9中,如果从nand启动,sram被映射到bank0,在启动后这个sram可以用作其他用途。下边是测试程序sram.c:

在内核中修改:

测试结果:

分享到:
评论

相关推荐

    Linux内核访问外设I O资源的方式(转)

    linux Linux内核访问外设I O资源的方式.

    Linux常见驱动源码分析(kernel hacker修炼之道全集)--李万鹏

    Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼之道之驱动-流水灯.pdf kernel hacker修炼之道之驱动-混杂设备.pdf kernel hacker修炼之道之驱动-按键....

    LINUX编程白皮书 (全集)

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    嵌入式Linux之我行系列

    ·嵌入式Linux之我行——内核访问外设I/O资源的方式 ·嵌入式Linux之我行——深入理解DM9000在mini2440上的驱动 ·嵌入式Linux之我行——LCD背光驱动在2440上的实例开发 ·嵌入式Linux之我行——LED驱动在2440上的...

    Linux编程从入门到精通

    linux内核编程 目 录 雷蒙序 简介 Linux文档工程小组“公告” 译者序 第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 ...

    linux编程白皮书

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    LINUX编程白皮书

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    Linux编程白皮书

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    Linux编程资料

    第一部分 Linux内核 前言 第1章 硬件基础与软件基础 6 1.1 硬件基础 6 1.1.1 CPU 7 1.1.2 存储器 8 1.1.3 总线 8 1.1.4 控制器和外设 8 1.1.5 地址空间 9 1.1.6 时钟 9 1.2 软件基础 9 1.2.1 计算机语言 9 1.2.2 ...

    UNIX操作系统教程 张红光

    第1章绪论.1 1.1操作系统概述1 1.1.1建立操作系统的目标1 1.1.2操作系统是用户与计算机的接口1 1.1.3操作系统是资源管理器2 1.2UNIX系统的主要特性3 1.3UNIX系统的发展史4 1.4开源软件与UNIX的推广发展6 1.4.1开源...

    《计算机操作系统》期末复习指导

    作业平均周转时间=∑(作业完成时刻i-作业提交时刻i)/n个作业 (2)最短作业优先:在作业内容参差很不均衡时有合理性 (3)“响应比”最高的优先 “响应(系数)比”:作业响应时间(等待和运行)/...

Global site tag (gtag.js) - Google Analytics