内核模块 ioremap Segmentation fault 问题

1.问题发现

我学习内核驱动编程,练习最简单的led驱动。看教程的写法是,参考至https://blog.csdn.net/zqixiao_09/article/details/50850708

以下为伪代码:

#define  GPX2CON    0x11000c40  
#define  GPX2DAT    0x11000c44  
#define  GPX1CON    0x11000c20  
#define  GPX1DAT    0x11000c24  
#define  GPF3CON    0x114001e0  
#define  GPF3DAT    0x114001e4  
  
static int *pgpx2con  ;  
static int *pgpx2dat;  
  
static int *pgpx1con  ;  
static int *pgpx1dat;  
  
static int *pgpf3con  ;  
static int *pgpf3dat;  

/*****************************************/
    pgpx2con = ioremap(GPX2CON,4);  
    pgpx2dat = ioremap(GPX2DAT,4);  
  
    pgpx1con = ioremap(GPX1CON,4);  
    pgpx1dat =ioremap(GPX1DAT,4);  
  
    pgpf3con  = ioremap(GPF3CON,4);  
    pgpf3dat =ioremap(GPF3DAT,4);  
  
    writel((readl(pgpx2con)& ~(0xf<<28)) |(0x1<<28),pgpx2con) ;  
    writel((readl(pgpx1con)& ~(0xf<<0)) |(0x1<<0),pgpx1con) ;     
    writel((readl(pgpf3con)& ~(0xff<<16)) |(0x11<<16),pgpf3con) ;    

以下是我模仿他写的内核模块:

led.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>


#define PB_CFG 0x01c20824 //PB口控制寄存器
#define PB_DAT 0x01c20834 //PB口数据寄存器

 uint8_t *PBcfg;
 uint8_t *PBdat;
static int __init myled_init(void)
{

    PBcfg = ioremap(PB_CFG, 4); //映射到物理地址
    PBdat = ioremap(PB_DAT, 4);

    //配置io口为输出模式   //先左移12位取反得 FFFF0FFF 相与 再或上 0x1<<12  ioread32就是调用readl
    iowrite32((ioread32(PBcfg) & ~(7 << 16)) | (1 << 16), PBcfg); //PB4 设定out
    iowrite32((ioread32(PBcfg) & ~(7 << 20)) | (1 << 20), PBcfg); //PB5 设定out
    
    iowrite32(ioread32(PB_DAT) &(~(1<<4)),PBdat);//置低开灯
    iowrite32(ioread32(PB_DAT) &(~(1<<5)),PBdat);//置低开灯

    printk("myled_init ...
");
    return 0;
}

static void __exit myled_exit(void)
{
    iowrite32(ioread32(PB_DAT) | (1<<4),PBdat);//置高关灯
    iowrite32(ioread32(PB_DAT) | (1<<5),PBdat);//置高关灯
    iounmap(PBdat);
    iounmap(PBcfg);
    printk("myled_exit ...
");
}

module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");

具体思路是:

将两个外设地址,分别映射到不同的内存,并将内存地址赋值给Pbdat,Pbcfg。 然后再对两个地址进行读写操作。

但是这个模块只要运行就会出现:

#insmod led.ko
Segmentation fault

2.问题解决

我换了一种写法,如下:

ledq.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>

// status-led 接在PG15

#define BADDR 0x01c20800
#define PB_CFG1_OFF 0x24 //PG组IO口的功能配置寄存器1对基地址偏移为0XDC
#define PB_DATA_OFF 0x34 //PG组IO口的数据寄存器对基地址偏移为0xE8

//static volatile unsigned long *vaddr; //不可用
//int *vaddr; // 不可用
//unsigned int *vaddr;//不可用

unsigned char *vaddr;//可用

static int __init myled_init(void)
{
    vaddr = ioremap(BADDR, SZ_4K);//映射到物理地址
    //printk(KERN_INFO "vaddr=%ld",*vaddr);
    //配置io口为输出模式   //先左移16位取反得 FFFF0FFF 相与 再或上 0x1<<16  ioread32就是调用readl
    iowrite32((ioread32(vaddr + PB_CFG1_OFF) & ~(7 << 16)) | (1 << 16), vaddr + PB_CFG1_OFF); //PB4 设定out
    iowrite32((ioread32(vaddr + PB_CFG1_OFF) & ~(7 << 20)) | (1 << 20), vaddr + PB_CFG1_OFF); //PB5 设定out

    iowrite32(ioread32(vaddr + PB_DATA_OFF) & (~(1 << 4)), vaddr + PB_DATA_OFF); //置低开灯
    iowrite32(ioread32(vaddr + PB_DATA_OFF) & (~(1 << 5)), vaddr + PB_DATA_OFF); //置低开灯

    printk(KERN_INFO "myled_init ...
");
    return 0;
}

static void __exit myled_exit(void)
{
    printk(KERN_INFO "myled_exit ...
");
    iowrite32(ioread32(vaddr + PB_DATA_OFF) | (1 << 4), vaddr + PB_DATA_OFF); //置高关灯
    iowrite32(ioread32(vaddr + PB_DATA_OFF) | (1 << 5), vaddr + PB_DATA_OFF); //置高关灯
    // iowrite32(ioread32(PB_DAT) | (0x1<<5),PBdat);
    iounmap(vaddr);
}

module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");

具体思路:

直接将大片物理地址映射到内存中,然后根据地址偏移量寻找地址并且对其进行操作。

原文地址:https://www.cnblogs.com/ZQQH/p/8631749.html