linux设备驱动归纳总结(五):3.操作硬件——IO静态映射

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

有时候会觉得,每次访问硬件都要先通过ioremap来获取虚拟地址,其实有没有一种一劳永逸的方法,只要一次的操作,以后就能通过这个地址来访问硬件。答案是“有”,这就是接下来要介绍的IO内存静态映射。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、静态IO是怎么建立的

Io静态映射发生在内核启动的时候,接下来通过内核源代码来分析,如果你的开发板是mini2440或者时候mini2440的内核配置文件,可以跟着我同样修改。注意:我的开发板只是使用mini2440的配置文件,外围电路跟mini2440不一样。

注:以下代码在内核目录linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c。

静态映射的建立方法,是在内核启动的时候,读取struct map_desc结构体里面的成员:

/*arch/arm/include/asm/mach/map.h*/

14 struct map_desc {

15 unsigned long virtual; //存放以后需要操作的虚拟地址,由自己定义

16 unsigned long pfn; //需要操作的硬件的物理地址对应的页帧号,即物理地址右移12

17 unsigned long length; //需要映射的大小

18 unsigned int type; //类型

19 };

这里要说明两个成员:

1)物理地址的页帧号pfn:如果你了解linux的页式管理,那你应该知道,一个页的大小是4096B(2
<< 12),所以一个地址的31-12位是用来表示一个地址对应的页帧号。对应的,一个物理地址,只要右移12位就能得到对应的页帧号,也可以使用函数:

#define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) //其实也是右移12位

2)类型type:有下面这些类型定义:

/*include/asm/mach/map.h*/

21 /* types 0-3 are defined in asm/io.h */

22 #define MT_UNCACHED
4

23 #define MT_CACHECLEAN 5

24 #define MT_MINICLEAN 6

25 #define MT_LOW_VECTORS 7

26 #define MT_HIGH_VECTORS 8

27 #define MT_MEMORY 9

28 #define MT_ROM 10

其中,MT_UNCACHED是我们常用的,表示该地址不放在缓冲区cached中。要知道,为了方便内存的访问,内核会将一些经常使用的内存数据放在cached中,但是这样在访问寄存器时就不行了,如果寄存器改变了,内核读取数据是从cached中读取数据,而不在寄存器读取,这样的做法是不合理的。

首先,我们需要往这个结构体中填充我们需要访问的地址。

本来这个结构体是空的。

/*arch/arm/mach-s3c2440/mach-mini2440.c*/

45 static struct map_desc mini2440_iodesc[] __initdata = {

46 };

修改成:

45 static struct map_desc mini2440_iodesc[] __initdata = {

46 {

47 .virtual = 0xeeee0000,

48 .pfn = __phys_to_pfn(0x56000000), //0x56000

49 .length = SZ_4K, //这里我直接映射一页

50 .type = MT_UNCACHED

51 },

52 };

填充结构体后,我们再看看启动时通过调用什么函数:

/*linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c*/

264 static void __init mini2440_map_io(void)

265 { //就是这个函数,将我刚才修改的结构体的成员进行静态映射

266 s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));

267 s3c24xx_init_clocks(12000000);

268 s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));

269 }

这个函数里面有一个重要的函数——iotable_init(),其实大部分的工作都由这个函数来完成,实现静态映射。

既然修改了内核,就需要重新编译内核:

make bzImage

通过上面这几步,我们就实现了这样的一个操作,可以通过虚拟地址0xeeee0000来访问一页的物理地址。既然知道了虚拟地址和物理地址之间的关系,就不需要再用ioremap了。

为了更好的规范,我们使用一个头文件来定义寄存器的访问地址,方便编程时使用:

/*5th_mm_3/1st/test_map_io.h*/

1 #ifndef _TEST_H

2 #define _TEST_H

3

4 typedef volatile unsigned long * s3c_reg_t;

5

6 #define S3C2440_VA 0xeeee0000 //我们已经知道静态映射的虚拟地址

7

8 #define S3C2440_BASE(x) (S3C2440_VA + (x))

9 #define S3C2440_GPEBASE S3C2440_BASE(0x40)

10 #define S3C2440_GPECON S3C2440_BASE(0x40) //这就是我们要操作的寄存器

11 #define S3C2440_GPEDAT S3C2440_BASE(0x44)

12 #define S3C2440_GPEUP S3C2440_BASE(0x48)

13

14

15 #endif /* _TEST_H */

然后再修改一下前一节的2nd函数,去掉ioremap部分:

1 #include

2 #include

3

4 #include

5 #include "test_map_io.h"

6

7 s3c_reg_t *GPECON,
*GPEDAT, *GPEUP;

8 unsigned long reg;

9

10 void led_device_init(void)

11 {

12 GPECON = (s3c_reg_t
*)S3C2440_GPECON;

13 GPEDAT = (s3c_reg_t
*)S3C2440_GPEDAT;

14 GPEUP = (s3c_reg_t
*)S3C2440_GPEUP;

15 }

16

17 void led_configure(void)

18 {

19 reg = ioread32(GPECON);

20 reg &= ~(3 << 24);

21 reg |= (1 << 24);

22 iowrite32(reg, GPECON);

23

24 reg = ioread32(GPEUP);

25 reg &= ~(3 << 12);

26 iowrite32(reg, GPEUP);

27 }

28

29 void led_on(void)

30 {

31 reg = ioread32(GPEDAT);

32 reg &= ~(1 << 12);

33 iowrite32(reg, GPEDAT);

34 }

35

36 void led_off(void)

37 {

38 reg = ioread32(GPEDAT);

39 reg |= (1 << 12);

40 iowrite32(reg, GPEDAT);

41 }

42

43 static int __init test_init(void) //模块初始化函数

44 {

45 led_device_init();

46 led_configure();

47 led_on();

48 printk("hello led!\n");

49 return 0;

50 }

51

52 static void __exit test_exit(void) //模块卸载函数

53 {

54 led_off();

55 printk("bye\n");

56 }

57

58 module_init(test_init);

59 module_exit(test_exit);

60

61 MODULE_LICENSE("GPL");

62 MODULE_AUTHOR("xoao bai");

63 MODULE_VERSION("v0.1");

除了红笔部分,和删除的ioremap相关函数,其他部分都没有改动,效果还是一样,加载灯亮,卸载灯灭。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、总结

这节介绍的内容确实是少:

1)内核中使用什么数据结构来管理静态映射IO。

2)内核时候什么函数在启动过程实现静态IO映射。

3)如何编写驱动函数来使用静态映射IO。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代码: 5th_mm_3.rar

【Linux开发】linux设备驱动归纳总结(五):3.操作硬件——IO静态映射的更多相关文章

  1. linux设备驱动归纳总结(五):3.操作硬件——IO静态映射【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-83299.html linux设备驱动归纳总结(五):3.操作硬件——IO静态映射 xxxxxxxxx ...

  2. linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  3. 【Linux】linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  4. 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存

    linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  5. 【Linux开发】linux设备驱动归纳总结(五):2.操作硬件——IO内存

    linux设备驱动归纳总结(五):2.操作硬件--IO内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  6. 【Linux开发】linux设备驱动归纳总结(五):4.写个简单的LED驱动

    linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  7. linux设备驱动归纳总结(五):1.在内核空间分配内存【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-79134.html linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxx ...

  8. 【Linux开发】linux设备驱动归纳总结(六):1.中断的实现

    linux设备驱动归纳总结(六):1.中断的实现 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 【Linux开发】linux设备驱动归纳总结(六):2.分享中断号

    linux设备驱动归纳总结(六):2.分享中断号 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. Mysql入门-对表数据的增删改查

    这一部分是最简单的,也是最麻烦的.简单是因为其实只包括增删该插四个部分.大体上看,增加数据.删除数据.修改数据.查询数据都不麻烦啊,我们日常都是常用的.这个谁不会呢?以前在培训机构学mysql的时候, ...

  2. 安装交叉工具链arm-linux-gcc

    基本步骤如下: (1)建立目录 $ sudo mkdir /usr/local/arm (2)复制文件 文件arm-linux-gcc-4.4.3.tar.gz,放在/var/ftp中,进入ftp,$ ...

  3. BZOJ 4034 [HAOI2015]树上操作 线段树+树剖或dfs

    题意 直接照搬原题面 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所 ...

  4. clone的fork与pthread_create创建线程有何不同&pthread多线程编程的学习小结(转)

    进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽 象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的 过程就是这 ...

  5. 微信小程序_(组件)view视图容器

    微信小程序view组件官方文档 传送门 Learn 一.hover-class属性 二.hover-start-time与hover-stay-time属性 三.hover-stop-propagat ...

  6. Django 详解

    Django是一个开源的Web应用框架,由Python写成.采用MVC的软件设计模式,主要目标是使得开发复杂的.数据库驱动的网站变得简单.Django注重组件的重用性和“可插拔性”,敏捷开发和DRY法 ...

  7. Walkthrough: My first WPF desktop application

    Walkthrough: My first WPF desktop application This article shows you how to develop a Windows Presen ...

  8. java使用解压zip文件,文件名乱码解决方案

    File outFileDir = new File(outDir);if (!outFileDir.exists()) { boolean isMakDir = outFileDir.mkdirs( ...

  9. 显示Pl/Sql Developer window list窗口

    默认情况下Window List窗口是不显示的,这十分不方便 (一)在菜单项的Tools下的Preference选项中的UserInterface中选择Option,在右边对于的Autosave de ...

  10. Coding Rules

    c语言按行读取的时候,注意用fgets可以读一行,但默认会把换行符也读进去,使用scanf("%s")却不会.