物理地址转换为虚拟地址(动态)
说明:
参考文章http://blog.csdn.net/do2jiang/article/details/5450839
几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。
根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:
(1)I/O映射方式(I/O-mapped)
典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。
(2)内存映射方式(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在arch/arm/include/asm/io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中,原型如下:
1 #define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
iounmap函数用于取消ioremap()所做的映射,原型如下:
1 #define iounmap(cookie) __arch_iounmap(cookie)
这两个函数都是实现在arch/arm/mm/ioremap.c文件中。
1 void __iomem * __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) 2 { 3 unsigned long last_addr; 4 unsigned long offset = phys_addr & ~PAGE_MASK; 5 unsigned long pfn = __phys_to_pfn(phys_addr); 6 7 /* * Don't allow wraparound or zero size */ 8 last_addr = phys_addr + size - 1; 9 if (!size || last_addr < phys_addr) 10 return NULL; 11 return __arm_ioremap_pfn(pfn, offset, size, mtype); 12 }
在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。
为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。
独立编址 x86 端口和内存各自有独立的地址空间
统一编址 arm 设备端口和设备内存 共用同一个地址空间
设备端口(ioport)访问:
4 2 1
读 inl inw inb
写 outl outw outb
设备内存(iomem)访问:
读 ioread32 ioread16 ioread8
readl readw readb readsb
写 iowrite32 iowrite16 iowrite8
writel writew writeb writesb
1 #define readsb(p,d,l) __raw_readsb(__mem_pci(p),d,l) 2 #define readsw(p,d,l) __raw_readsw(__mem_pci(p),d,l) 3 #define readsl(p,d,l) __raw_readsl(__mem_pci(p),d,l) 4 5 #define writesb(p,d,l) __raw_writesb(__mem_pci(p),d,l) 6 #define writesw(p,d,l) __raw_writesw(__mem_pci(p),d,l) 7 #define writesl(p,d,l) __raw_writesl(__mem_pci(p),d,l) 8 9 #define readb(c) ({ __u8 __v = __raw_readb(__mem_pci(c)); __v; }) 10 #define readw(c) ({ __u16 __v = le16_to_cpu((__force __le16) \ 11 __raw_readw(__mem_pci(c))); __v; }) 12 #define readl(c) ({ __u32 __v = le32_to_cpu((__force __le32) \ 13 __raw_readl(__mem_pci(c))); __v; }) 14 15 #define writeb(v,c) __raw_writeb(v,__mem_pci(c)) 16 #define writew(v,c) __raw_writew((__force __u16) \ 17 cpu_to_le16(v),__mem_pci(c)) 18 #define writel(v,c) __raw_writel((__force __u32) \ 19 cpu_to_le32(v),__mem_pci(c)) 20 21 #define memset_io(c,v,l) _memset_io(__mem_pci(c),(v),(l)) 22 #define memcpy_fromio(a,c,l) _memcpy_fromio((a),__mem_pci(c),(l)) 23 #define memcpy_toio(c,a,l) _memcpy_toio(__mem_pci(c),(a),(l)) 24 25 #ifndef ioread8 26 #define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; }) 27 #define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; }) 28 #define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; }) 29 30 #define iowrite8(v,p) __raw_writeb(v, p) 31 #define iowrite16(v,p) __raw_writew((__force __u16)cpu_to_le16(v), p) 32 #define iowrite32(v,p) __raw_writel((__force __u32)cpu_to_le32(v), p) 33 34 #define ioread8_rep(p,d,c) __raw_readsb(p,d,c) 35 #define ioread16_rep(p,d,c) __raw_readsw(p,d,c) 36 #define ioread32_rep(p,d,c) __raw_readsl(p,d,c) 37 38 #define iowrite8_rep(p,s,c) __raw_writesb(p,s,c) 39 #define iowrite16_rep(p,s,c) __raw_writesw(p,s,c) 40 #define iowrite32_rep(p,s,c) __raw_writesl(p,s,c)
程序一:把LED灯对应的物理地址转换为虚拟地址
创建文件夹/nfsroot/kern/2012-04-24/01/。
创建文件/nfsroot/kern/2012-04-24/01/test.c,内容如下:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/io.h> 4 5 MODULE_LICENSE("GPL"); 6 7 // GPM0/1/2/3 --> LED0/1/2/3 8 #define GPMCON 0x7F008820 9 #define GPMDAT 0x7F008824 10 11 static unsigned long p_conf = GPMCON; 12 static unsigned long p_data = GPMDAT; 13 14 static unsigned int size = 4; 15 16 static void * v_conf; 17 static void * v_data; 18 19 static int __init test_init(void) 20 { 21 v_conf = ioremap(p_conf, size); 22 v_data = ioremap(p_data, size); 23 24 *(volatile unsigned long *)v_conf = 0x1111; 25 *(volatile unsigned long *)v_data = 0x0; 26 27 return 0; 28 } 29 30 static void __exit test_exit(void) 31 { 32 *(volatile unsigned long *)v_data = 0xf; 33 34 iounmap(v_data); 35 iounmap(v_conf); 36 } 37 38 module_init(test_init); 39 module_exit(test_exit);
创建文件/nfsroot/kern/2012-04-24/01/Makefile,内容如下:
1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:
1 [root@localhost 01]# pwd 2 /nfsroot/kern/2012-04-24/01 3 [root@localhost 01]# ls 4 Makefile test.c 5 [root@localhost 01]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-04-24/01/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-04-24/01/test.mod.o 12 LD [M] /nfsroot/kern/2012-04-24/01/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 01]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 01]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 01]#
在开发板端载入模块,观看LED情况,卸载模块,过程如下:
1 [root@timkyle 01]# pwd 2 /kern/2012-04-24/01 3 [root@timkyle 01]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 01]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 01]# lsmod 11 [root@timkyle 01]# insmod test.ko 12 [root@timkyle 01]# lsmod 13 test 1224 0 - Live 0xbf00c000 14 [root@timkyle 01]# rmmod test 15 [root@timkyle 01]# lsmod 16 [root@timkyle 01]#
程序二:对虚拟地址由直接指针操作,改为用内核函数操作
创建文件夹/nfsroot/kern/2012-04-24/02/。
创建文件/nfsroot/kern/2012-04-24/02/test.c,内容如下:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/io.h> 4 5 MODULE_LICENSE("GPL"); 6 7 // GPM0/1/2/3 --> LED0/1/2/3 8 #define GPMCON 0x7F008820 9 #define GPMDAT 0x7F008824 10 11 static unsigned long p_conf = GPMCON; 12 static unsigned long p_data = GPMDAT; 13 14 static unsigned int size = 4; 15 16 static void * v_conf; 17 static void * v_data; 18 19 static int __init test_init(void) 20 { 21 v_conf = ioremap(p_conf, size); 22 v_data = ioremap(p_data, size); 23 24 #if 0 25 *(volatile unsigned long *)v_conf = 0x1111; 26 *(volatile unsigned long *)v_data = 0x0; 27 #else 28 iowrite32(0x1111, v_conf); 29 iowrite32(0x0, v_data); 30 #endif 31 32 return 0; 33 } 34 35 static void __exit test_exit(void) 36 { 37 #if 0 38 *(volatile unsigned long *)v_data = 0xf; 39 #else 40 iowrite32(0xf, v_data); 41 #endif 42 43 iounmap(v_data); 44 iounmap(v_conf); 45 } 46 47 module_init(test_init); 48 module_exit(test_exit);
创建文件/nfsroot/kern/2012-04-24/02/Makefile,内容和程序一相同。
在主机端编译模块,过程和程序一相同。
在开发板端载入模块,观看LED情况,卸载模块,过程和程序一相同。
程序三:整页映射,然后通过偏移量对应需要地址
创建文件夹/kern/2012-04-24/08/。
创建文件/kern/2012-04-24/08/test.c,内容如下:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/io.h> 4 5 MODULE_LICENSE("GPL"); 6 7 // GPMCON 0x7F008820 8 // GPMDAT 0x7F008824 9 #define BASE 0x7F008000 10 11 static unsigned long base = BASE; 12 static void *v_base; 13 static void *v_conf; 14 static void *v_data; 15 16 static int __init test_init(void) 17 { 18 v_base = ioremap(base, SZ_4K); 19 v_conf = v_base + 0x820; 20 v_data = v_base + 0x824; 21 22 iowrite32(0x1111, v_conf); 23 iowrite32(0x0, v_data); 24 25 return 0; 26 } 27 28 static void __exit test_exit(void) 29 { 30 iowrite32(0xf, v_data); 31 iounmap(v_base); 32 } 33 34 module_init(test_init); 35 module_exit(test_exit);
创建文件/nfsroot/kern/2012-04-24/08/Makefile,内容和程序一相同。
在主机端编译模块,过程和程序一相同。
在开发板端载入模块,观看LED情况,卸载模块,过程和程序一相同。