組み込みLINUXドライバーが11個の物理アドレス-ユーザー空間仮想アドレスマッピングmmap()を学習

組み込みLINUXドライバーが11個の物理アドレス-ユーザー空間仮想アドレスマッピングmmap()を学習

1.ヘッダーファイル、関数、説明

/* 物理地址到用户空间虚拟地址的映射函数: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.コード例(カーネルスペース)

特徴:
ハードウェアの物理アドレスとユーザー空間の仮想アドレス間のマッピングを実現し、ハードウェアデバイスをユーザー空間で操作できるようにします。

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");

3、コード例(ユーザー空間)

#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;
}

おすすめ

転載: blog.csdn.net/weixin_47273317/article/details/108153297