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");
具体思路:
直接将大片物理地址映射到内存中,然后根据地址偏移量寻找地址并且对其进行操作。