实现按键驱动,板上加载驱动出错,Oops信息如下:
可知,是因为使用了空指针,导致内核访问了非法地址,在源码中去看一下:
1 ... 2 //按键中断的处理函数 3 irqreturn_t key_irq_handler(int irqno, void *devid) 4 { 5 printk("----------%s---------",__FUNCTION__); 6 7 int value; 8 //读取按键状态 9 value = readl(key_dev->reg_base + 4) & (0x01<<2); 10 11 if(value){ 12 printk("key3 up "); 13 key_dev->event.code = KEY_ENTER; 14 key_dev->event.value = 0; 15 }else{ 16 printk("key3 down "); 17 key_dev->event.code = KEY_ENTER; 18 key_dev->event.value = 1; 19 } 20 return IRQ_HANDLED; 21 } 22 23 ... 24 25 //驱动初始化函数 26 static int __init key_drv_init(void) 27 { 28 //获取到中断号 29 int ret; 30 31 //1、设定全局设备对象并分配空间 32 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //GFP_KERNEL表正常分配内存 33 //kzalloc相比于kmalloc,不仅分配连续空间,还会将内存初始化清零 34 35 //2、动态申请设备号 36 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); 37 38 //3、创建设备节点文件 39 key_dev->cls = class_create(THIS_MODULE, "key_cls"); 40 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major, 0), NULL, "key0"); 41 42 //4、硬件初始化 -- 地址映射或中断申请 43 key_dev->irqno = get_irqno_from_node(); 44 45 ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 46 "key3_eint10", NULL); 47 if(ret != 0) 48 { 49 printk("request_irq error "); 50 return ret; 51 } 52 53 //a. 硬件如何获取数据 54 key_dev->reg_base = ioremap(GPXCON_REG,8); 55 56 57 return 0; 58 } 59 ...
跟踪一下代码,会发现是在初始化函数中出错,在request_irq函数中,看参数设置是否正确,参数1:中断号,参数2:发生中断时,调用的回调函数,参数3:中断触发类型,参数4/5,分别是const char *name, 和void *dev,name通常为设备驱动名字,dev为void*指针,作为参数传递给回调函数,为dev_id中断名称,作为共享中断时的中断区别参数,这里我们设置为NULL。
参数设置基本上没问题,再看一下信息,报错信息说无法对虚拟地址0x00000004进行解引用即读值,进入key_irq_handler, 在读寄存器的函数readl中,对虚拟地址基地址进行偏移后在读出值进行与&操作,为什么报错信息说这个虚拟地址基地址为0,那就是说并没有地址映射成功,基地址是空的。返回初始化函数,看看ioremap函数,发现ioremap在request_irq函数之后,所以基地址没有与物理地址进行映射,将ioremap放到request_irq之前即可。
对注册中断服务函数的详细解析见:https://blog.csdn.net/wealoong/article/details/7566546
对Oops信息调试详解的文章见:https://blog.csdn.net/kangear/article/details/8217329