Embedded LINUX driver learning 11 physical address-user space virtual address mapping mmap ()

Embedded LINUX driver learning 11 physical address-user space virtual address mapping mmap ()

1. Header files, functions and descriptions

/* 物理地址到用户空间虚拟地址的映射函数:remap_pfn_range()
       头文件位置:include/linux/mm.h
*/
int remap_pfn_range(struct vm_area_struct *u_vma, unsigned long addr,\
                        unsigned long pfn, unsigned long size, pgprot_t);
/*
    参数说明: 
             u_vma : 用户空间虚拟内存空间,
             addr  : 要映射的目的虚拟内存空间, 即:u_vma -> vm_start
             pfn   : 物理地址,单位为页,即:物理地址>>12
             size  : 要映射的大小
             prot  : 权限   即:u_vma -> vm_page_prot
 */

//用户空间mmap()函数调用的底层函数:
     int (*mmap) (struct file *, struct vm_area_struct *);//include/linux/fs.h

//  结构体:struct vm_area_struct{}
struct vm_area_struct {
    
    
        struct mm_struct * vm_mm;        //用户虚拟内存空间
        unsigned long vm_start;         //用户虚拟内存空间,进程操作文件的开始地址
        unsigned long vm_end;           //用户虚拟内存空间,进程操作文件的结束地址
        struct vm_area_struct *vm_next, *vm_prev;//由内核维护的双向链表
        pgprot_t vm_page_prot; //权限  
        //............省略更多内容,可参见:include/linux/mm_types.h..............
}            

2. Code example (kernel space)

Features:
Realize the mapping between the physical address of the hardware and the virtual address of the user space, so that the hardware device can be operated in the user space

include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
unsigned long led_phy = (0xC001C000 >> 12); //定义全局无符号长整形数据保存物理地址
struct vm_area_struct *g_led_vma;//用于指向用户虚拟内存空间
static int mmap_led (struct file * file , struct vm_area_struct * u_led_vma){
    
    
    g_led_vma = u_led_vma;
    g_led_vma -> vm_page_prot = pgprot_noncached(g_led_vma -> vm_page_prot);//关闭缓存功能
    remap_pfn_range(g_led_vma,g_led_vma -> vm_start,\
                    led_phy,4096,g_led_vma ->vm_page_prot);
    return 0;
}

struct file_operations f_ops = {
    
    
    .owner = THIS_MODULE,
    .mmap = mmap_led
};
struct miscdevice misc_fops = {
    
    
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mmap_led",
    .fops = &f_ops
};

static int led_init(void){
    
    
    misc_register(&misc_fops);
    return 0;
}
static void led_exit(void){
    
    
    misc_deregister(&misc_fops);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

Three, code example (user space)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#define LED_ON  0X1000
#define LED_OFF 0X1001
static void * mmf_addr;//记录分配的虚拟内存空间
static unsigned long * led_base;//保存gpio_output地址,即首地址,选择高低电平
static unsigned long * led_outenb;//保存gpio_outenb地址,选择输入输出功能
static unsigned long * led_altfn;//保存gpio_altfn地址,功能复用器
static void led_run(unsigned int arg){
    
    
    if(arg == LED_ON)
        *led_base &= ~(0x1 << 7);//开灯
    else
        *led_base |= (0x1 << 7);//关灯
}

int main(int argc , char * argv[]){
    
    
    int fp;
    int ucmd;
    void * mmf_addr;
    if(argc != 3)
        goto comm_err;
    fp = open(argv[1],O_RDWR);
    if(fp < 0)
        goto file_err;
    mmf_addr = mmap(0,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fp,0);
    if(mmf_addr == MAP_FAILED)//判断分配地址空间是否成功
        goto mm_err;
    /*寄存器相关操作*/
    led_base = (unsigned long *) mmf_addr;
    led_outenb = (unsigned long *) (mmf_addr +0x04);
    led_altfn =  (unsigned long *) (mmf_addr + 0x20);
    *led_outenb |= (0x1 << 7);
    *led_base  |= (0x1 << 7);
    *led_altfn &= ~(0x3 << 14);
    *led_altfn |= (0x1 << 14);
    /*根据命令控制开关灯*/
    if(!strcmp(argv[2],"on"))
        ucmd = LED_ON;
    else if(!strcmp(argv[2],"off"))
        ucmd = LED_OFF;
    else {
    
    
        munmap(mmf_addr,4096);
        goto comm_err;
    }
    led_run(ucmd);
    munmap(mmf_addr,4096);//取消映射,注:取消映射后,寄存器的状态不会发生变量
    return 0;
comm_err :
    printf("命令错误!\n");
    printf("        comm <cdev_file> <on| off> <led_num> \n");
    return -1;
file_err :
    printf("文件打开失败\n");
    return -1;
mm_err :
    printf("mmap映射失败\n");
    return -1;
}

Guess you like

Origin blog.csdn.net/weixin_47273317/article/details/108153297