嵌入式LINUX驱动学习之10 物理地址-内核虚拟地址映射 ioremap()/iounmap()

@[TOC](嵌入式LINUX驱动学习之10物理地址-内核虚拟地址映射 ioremap()/iounmap())

一、头文件、函数及说明

//源码位置:arch/arm/include/asm/io.h 
#define MT_DEVICE               0
#define MT_DEVICE_NONSHARED     1
#define MT_DEVICE_CACHED        2
#define MT_DEVICE_WC  
#define ioremap(cookie,size)            __arm_ioremap((cookie), (size), MT_DEVICE)//实现源码见附A.1
/*
    ioremap(unsigned long pyh,size_t size);
    参数说明:
            cookie : 类型:unsigned long ,用于保存保存物理地址
            size   : 类型:size_t  , 用于保存数据大小,单位Byte;
    功    能:
            将物理地址映射成内核区的虚拟地址,将来操作内核空间的虚拟地址,就相当于操作物理地址;
    返回值 :
            成功:void * 类型的内核空间虚拟址
            失败:NULL;
 */
 
#define iounmap                         __arm_iounmap
extern void __arm_iounmap(volatile void __iomem *addr);
 /*
    iounmap(void * addr);
    参数说明: 
            addr: ioremap()的返回值;
    功    能:
            释放形参内核虚拟地址和对应的物理地址的映射关系。
*/

二、代码举例

功能:
通过按键k1控制蜂鸣器的开和关

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <cfg_type.h>
#include <linux/io.h>
/*定义按键信息,通过数组形式目的是为后续可以扩充其它功能键*/
struct btn_src {
    
    
    char * name ;
    int    gpio;
};
struct btn_src btn_info[] = {
    
    
    {
    
    
        .name = "BTN_BEEP",
        .gpio = PAD_GPIO_A + 28
    }
};
static spinlock_t lock;//定义一个锁结构体对象
void * v_beep_base;//保存ioremap分配的基础地址
unsigned long *beep_base;//转换为整形的基础地址
unsigned long *beep_outenb;//GPIO模式:0:输入,1:输出
unsigned long *beep_altfn;//GPIO复用功能选择器
int beep_state;//用于记录蜂鸣器状态
/*控制蜂鸣器开和ud */
static void beep_func(void){
    
    
    /*使用衍生自旋锁保护寄存器和全局变量操作过程,使得执行过程不被中断*/
    unsigned long flags;
    spin_lock_irqsave(&lock,flags);
    if(beep_state){
    
    
        *beep_base &= ~(0x1 << 14);//低电平关闭蜂鸣器
    }
    else {
    
    
        *beep_base |= (1 << 14);//高电平时打开蜂鸣器
    }
    beep_state = 1-beep_state;//将蜂鸣器状态反转;
    spin_unlock_irqrestore(&lock,flags);//释放锁

}
/*中断处理函数:
   当发生中断时调用beep_func()函数 ,并且打印中断号信息
*/
static irqreturn_t btn_irq(int irq,void * args){
    
    
    struct btn_src *btn_ = (struct btn_src *) args;
    beep_func();
    printk("%s,%s , irq: %d\n",__func__,btn_ -> name,irq);
    return IRQ_HANDLED;
}

static int btn_beep_init(void){
    
    
    int i = 0;
    for(; i<ARRAY_SIZE(btn_info); i++){
    
    
        gpio_request(btn_info[i].gpio,btn_info[i].name);
        gpio_direction_input(btn_info[i].gpio);
        request_irq(gpio_to_irq(btn_info[i].gpio),btn_irq,\
                    IRQF_TRIGGER_RISING,btn_info[i].name,\
                    &btn_info[i]);
    }
    spin_lock_init(&lock);//初始化一个自旋锁对象
    v_beep_base = ioremap((unsigned long) 0xC001C000,0x24);//基础地址
    beep_base = (unsigned long *) v_beep_base;//转换为整形的基础地址
    beep_outenb = (unsigned long *) (v_beep_base + 0x04);//GPIO模式:0:输入,1:输出
    beep_altfn =  (unsigned long *) (v_beep_base + 0x20);//GPIO复用功能选择器
    beep_state = 0;//用于记录蜂鸣器状态
    *beep_outenb |= (0x1 << 14);//设置GPIOC14为输出口
    *beep_base &= ~(0X1 << 14);//设置GPIOC14为低电平,即蜂鸣器为关闭状态
    *beep_altfn &= ~(0x11 << 28);/*功能复用选择寄存器,设置为GPIO功能*/
    *beep_altfn |= (0x1 << 28);
    printk("加载模块完成\n");
    return 0;
}
static void btn_beep_exit(void){
    
    
    int i = 0;
    for(; i < ARRAY_SIZE(btn_info);i ++){
    
    
        free_irq(gpio_to_irq(btn_info[i].gpio),&btn_info[i]);
        gpio_free(btn_info[i].gpio);
    }
    *beep_base &= ~(0x1 << 14);//模块卸载前,将GPIOC14电平设置为低电平
    iounmap(v_beep_base);//释放映射的虚拟地址空间
    printk("卸载模块完成\n");
}
module_init(btn_beep_init);
module_exit(btn_beep_exit);
MODULE_LICENSE("GPL");
                                      

附A.1

//头文件位置:arch/arm/include/asm/io.h 

//实现源码位置:arch/arm/mm/ioremap.c
void __iomem *
__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
    
    
        return arch_ioremap_caller(phys_addr, size, mtype,
                __builtin_return_address(0));
}

猜你喜欢

转载自blog.csdn.net/weixin_47273317/article/details/108144690