linux--物理地址到虚拟地址映射,ioremap()函数

CPU对I/O端口的编制方式

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

  1. I/O映射方式:X86处理器为外设实现了单独的地址空间,称IO地址,通过IO指令来访问;
  2. 内存映射方式:ARM、PowerPC等通常只实现一个物理地址空间,外设I/O端口为内存的一部分,CPU可以访问内存单元那样访问IO端口。

物理地址到虚拟地址的映射

内存管理单元(MMU)通常以页为单位进行处理,而不是字节,ioremap函数也同样属于页映射。

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

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

参数:

  1. 物理地址
  2. 要映射的空间的大小

返回值:页映射,返回虚拟地址

※同一物理地址可以多次ioremap映射,分配的虚拟空间地址各部相同,iounmap互补影响。

ioremap()与 vmalloc()类似,也需要建立新的页表,但是它并不进行 vmalloc()中所执行的内存分配行为。ioremap()返回一个特殊的虚拟地址,该地址可用来存取特定的物理地址范围。通过 ioremap()获得的虚拟地址应该被 iounmap()函数释放,其原型如下:
 

void iounmap(void * addr);

参数:

  1. addr是ioremap生成的虚拟地址

简单实现的驱动代码:

#include 
#include 
#include 

//用于存放虚拟地址和物理地址
unsigned int virt_addr,phys_addr;
//用户存放三个寄存器的地址
unsigned int *GPL2CON,*GPL2DAT,*GPL2PUD;

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SZA");

static void gpl2_device_init(void)
{
	phys_addr = 0x11000100;
	GPL2CON = (unsigned int*)ioremap(phys_addr,0x01);
	GPL2DAT = GPL2CON + 1;
	GPL2PUD = GPL2CON + 2;
	printk("virt_addr is %x \n",virt_addr);
	printk("GPL2CON is %x \n",(unsigned int)GPL2CON);
	printk("GPL2DAT is %x \n",(unsigned int)GPL2DAT);
	printk("GPL2PUD is %x \n",(unsigned int)GPL2PUD);
}

static void gpl2_configure(void)
{
	*GPL2CON &= 0xFFFFFFF1;
	*GPL2CON |= 0x00000001;
	*GPL2PUD |=0x0003;
}

static void gpl2_on(void)
{
	*GPL2DAT |= 0x01;
}

static void gpl2_off(void)
{
	*GPL2DAT &= 0xfe;
}

static int led_gpl2_init(void)
{
	printk("init!\n");
	gpl2_device_init();//实现IO内存的映射
	gpl2_configure();//配置GPL2为输出模式
	gpl2_on();
	printk("led gpl2 open\n");
	return 0;
}


static void led_gpl2_exit(void)
{
	printk("exit!\n");
	gpl2_off();
	
	printk("led gpl2 close\n");
}

module_init(led_gpl2_init);
module_exit(led_gpl2_exit);

 

你可能感兴趣的:(linux)