主题 : 请问大侠们按键中断驱动问题 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 11172
精华: 0
发帖: 10
金钱: 50 两
威望: 10 点
综合积分: 20 分
注册时间: 2009-11-29
最后登录: 2010-06-05
楼主  发表于: 2010-01-27 20:49

 请问大侠们按键中断驱动问题

linux宿主机:F9
arm:mini2440

按键中断驱动:用的是GPIO中的GPF6(EINT6)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#define DEVICE_NAME     "keys"
#define KEY_DOWN    0
#define KEY_UP    1

static volatile int ev_press = 0;
static volatile int key7_status;
static DECLARE_WAIT_QUEUE_HEAD(key_waitq);

struct key_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;    
};

static struct key_irq_desc key_irqs = {
    IRQ_EINT6, S3C2410_GPF6, S3C2410_GPF6_EINT6, 6, "KEY7"
};

static irqreturn_t key_interrupt(int irq, void *dev_id)
{
    
    if(!s3c2410_gpio_getpin(key_irqs.pin))//按下,低电平则为真
        {
            if(key7_status == KEY_UP)
                {
                    key7_status = KEY_DOWN;
                    ev_press = 1;
                    printk(DEVICE_NAME "enter in irq!\n");
                    wake_up_interruptible(&key_waitq);
                }
        }
    return IRQ_RETVAL(IRQ_HANDLED);
}

static int keys_open(struct inode *inode, struct file *file)
{
    int ret;
    ret = request_irq(key_irqs.irq, key_interrupt, IRQF_TRIGGER_FALLING, key_irqs.name, NULL);
    if(ret)
        {
            disable_irq(key_irqs.irq);
            free_irq(key_irqs.irq, NULL);
            return -EBUSY;
        }
        
                     key7_status = KEY_UP;
    printk(DEVICE_NAME "enter in open!\n");
    return 0;
}

static int keys_close(struct inode *inode, struct file *file)
{
    disable_irq(key_irqs.irq);
    free_irq(key_irqs.irq, NULL);
    return 0;
}

static int keys_read(struct file *file, char __user *buf, size_t count, loff_t *offp)
{
    unsigned long ret;
    if(!ev_press)
        {
            if(file->f_flags & O_NONBLOCK)
                return -EAGAIN;
            else
                wait_event_interruptible(key_waitq,ev_press);
        }
        
    ev_press = 0;
    
    ret = copy_to_user(buf, (void *)key7_status, min(sizeof(key7_status), count));
    memset((void *)key7_status, KEY_UP, sizeof(key7_status));
    printk(DEVICE_NAME  "enter in read!n");
    return ret? -EFAULT : min(sizeof(key7_status), count);
}

static int keys_poll(struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &key_waitq, wait);
    if(ev_press)
        {
            mask |= POLLIN | POLLRDNORM;
        }
    return mask;
}

static struct file_operations keys_fops = {
    .owner = THIS_MODULE,
    .open = keys_open,
    .release = keys_close,
    .read = keys_read,
    .poll = keys_poll,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &keys_fops,
};

static int __init keys_init(void)
{
    int ret;
    ret = misc_register(&misc);
    
    printk(DEVICE_NAME "\tinitialized!\n");
    return ret;
}

static void __exit keys_exit(void)
{
    misc_deregister(&misc);
}

module_init(keys_init);
module_exit(keys_exit);
MODULE_LICENSE("GPL");

测试程序为:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv)
{
    int fd;
    int key_status;
    
    fd = open("/dev/keys", 0);
    if(fd < 0)
        {
            printf("Open Keys Device Faild!\n");
            exit(1);
        }
        
    while(1)
    {
        int i;
        int ret;
        fd_set rds;
        
        ret = select(fd + 1, &rds, NULL, NULL, NULL);
        if(ret < 0)
            {
                printf("Read Keys Device Failed!\n");
                exit(1);
            }
            
        if(ret == 0)
            {
                printf("Read Keys Device Timeout!\n");
            }
        else if(FD_ISSET(fd, &rds))
            {
                ret = read(fd, key_status, sizeof(key_status));
                if(ret != sizeof(key_status))
                    {
                        if(errno != EAGAIN)
                            {
                                printf("Read Key Device Failed!\n");
                            }
                        continue;
                    }
                else
                    {
                        printf("Key%d is DOWN\n", key_status+1);
                    }
            }
    }
    close(fd);
    return 0;
}

驱动能够insmod成功,当运行测试程序后,超级终端会显示报错信息:
[root@FriendlyARM plg]# ./key7_2_test
keysenter in open!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
keysenter in irq!
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c3028000
[00000000] *pgd=339c6031, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#5]
Modules linked in: key7
CPU: 0    Tainted: G      D     (2.6.29.4-FriendlyARM #6)
PC is at __copy_to_user+0x84/0x314
LR is at keys_read+0x134/0x14c [key7]
pc : [<c0129424>]    lr : [<bf00028c>]    psr: 00000013
sp : c306bef4  ip : 0000001c  fp : c306bf54
r10: 00000000  r9 : c306a000  r8 : 00000000
r7 : 00000004  r6 : c306bf78  r5 : bf000aac  r4 : 00000004
r3 : 00000000  r2 : ffffffe4  r1 : 00000000  r0 : 00000000
Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: c000717f  Table: 33028000  DAC: 00000015
Process key7_2_test (pid: 579, stack limit = 0xc306a268)
Stack: (0xc306bef4 to 0xc306c000)
bee0:                                              bf000aac c306bf78 00000004
bf00: 00000000 00000000 00000004 00000000 00000004 bf00028c c3b8f378 bef63ca0
bf20: 00000000 00000000 00000000 00000000 c306a000 c3055600 00000000 c306bf78
bf40: 00000003 c0028008 c306bf74 c306bf58 c0088468 bf000168 00000000 00000000
bf60: c3055600 00000003 c306bfa4 c306bf78 c0088580 c00883c4 00000000 00000000
bf80: 00000000 00000000 0000008e 00008600 00000000 000083f4 00000000 c306bfa8
bfa0: c0027e60 c0088544 00008600 00000000 00000003 00000000 00000004 00000001
bfc0: 00008600 00000000 000083f4 00000003 00000000 00000000 40025000 bef63d34
bfe0: 00000000 bef63c90 00008598 400dda3c 60000010 00000003 00000000 00000000
Backtrace:
[<bf000158>] (keys_read+0x0/0x14c [key7]) from [<c0088468>] (vfs_read+0xb4/0xe8)
r8:c0028008 r7:00000003 r6:c306bf78 r5:00000000 r4:c3055600
[<c00883b4>] (vfs_read+0x0/0xe8) from [<c0088580>] (sys_read+0x4c/0x84)
r7:00000003 r6:c3055600 r5:00000000 r4:00000000
[<c0088534>] (sys_read+0x0/0x84) from [<c0027e60>] (ret_fast_syscall+0x0/0x2c)
r6:000083f4 r5:00000000 r4:00008600
Code: e4915004 e4916004 e4917004 e4918004 (e491e004)
keysenter in irq!
---[ end trace 1d63f8cfc03fdeb0 ]---
Segmentation fault
[root@FriendlyARM plg]#

我想请问大侠们出了什么问题,还有请问大家有没有比较好的办法进行内核调试,或者驱动调试,因为我不知道该怎么改,谢谢!
*無鈳取玳
级别: 论坛版主
UID: 27
精华: 12
发帖: 5398
金钱: 40120 两
威望: 17929 点
综合积分: 11036 分
注册时间: 2008-01-16
最后登录: 2014-11-22
1楼  发表于: 2010-01-28 10:30
我想内核已经很明确的告诉你问题所在了
Unable to handle kernel NULL pointer dereference at virtual address 00000000

这个内核crash(准确的名字叫kernel OOPS)是由于dereference了一个NULL指针引起的

PC is at __copy_to_user+0x84/0x314

内核挂在__copy_to_user()这个函数里,通常这是由于传入的参数不正确造成的

Backtrace:
[<bf000158>] (keys_read+0x0/0x14c [key7]) from [<c0088468>] (vfs_read+0xb4/0xe8)
r8:c0028008 r7:00000003 r6:c306bf78 r5:00000000 r4:c3055600
[<c00883b4>] (vfs_read+0x0/0xe8) from [<c0088580>] (sys_read+0x4c/0x84)
r7:00000003 r6:c3055600 r5:00000000 r4:00000000
[<c0088534>] (sys_read+0x0/0x84) from [<c0027e60>] (ret_fast_syscall+0x0/0x2c)

这是内核死掉之前的call stack。可以看到,上面所说的__copy_to_user()调用应该是由最后一步的keys_read()引起的。对应你的驱动源代码,可以发现是keys_read()里面的
复制代码
  1. ret = copy_to_user(buf, (void *)key7_status, min(sizeof(key7_status), count));

这句话引起的,你把key7_status的值而非它的地址作为源地址传入了copy_to_user,当它的值为0(KEY_DOWN)时,就等于dereference了一个NULL指针。

关于内核驱动的调试,Linux Device Driver 第三版的第四章里有很详细的介绍。论坛里就有这本书的电子书版下载。
"If you have an apple and I have an apple and we exchange apples, then you and I will
still each have one apple. But if you have an idea and I have an idea and we exchange
these ideas, then each of us will have two ideas."
级别: 新手上路
UID: 11172
精华: 0
发帖: 10
金钱: 50 两
威望: 10 点
综合积分: 20 分
注册时间: 2009-11-29
最后登录: 2010-06-05
2楼  发表于: 2010-01-28 15:19
引用第1楼kasim于2010-01-28 10:30发表的  :
我想内核已经很明确的告诉你问题所在了

这是内核死掉之前的call stack。可以看到,上面所说的__copy_to_user()调用应该是由最后一步的keys_read()引起的。对应你的驱动源代码,可以发现是keys_read()里面的
[code]
ret = copy_to_user(buf, (void *)key7_status, min(sizeof(key7_status), count));
.......

首先谢谢您的解答,我有点明白了,但是我现在把KEY_DOWN的值改为了2,不是0了,但还是报错了,报错的代码是
[root@FriendlyARM plg]# insmod key7.ko
keys    initialized!
[root@FriendlyARM plg]# ./key7_2_test
keysenter in open!
enter in irq!       //我打印了些消息,看看是否进入中断
key_status is 2<1>Unable to handle kernel NULL pointer dereference at virtual address 00000002
pgd = c39a4000
[00000002] *pgd=33056031, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1]
Modules linked in: key7
CPU: 0    Not tainted  (2.6.29.4-FriendlyARM #6)
PC is at __copy_to_user+0xb8/0x314
LR is at keys_read+0x140/0x15c [key7]
pc : [<c0129458>]    lr : [<bf0002a8>]    psr: 80000013
sp : c306bf04  ip : c306be30  fp : c306bf54
r10: 00000000  r9 : c306a000  r8 : 00000000
r7 : 00000004  r6 : c306bf78  r5 : bf000ad8  r4 : 00000001
r3 : 00000000  r2 : 80000000  r1 : 00000002  r0 : 00000000
Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: c000717f  Table: 339a4000  DAC: 00000015
Process key7_2_test (pid: 571, stack limit = 0xc306a268)
Stack: (0xc306bf04 to 0xc306c000)
bf00:          00000000 00000001 00000000 00000001 bf0002a8 40024000 bece3ca0
bf20: 00000000 00000000 00000000 00000000 c306a000 c3055100 00000000 c306bf78
bf40: 00000003 c0028008 c306bf74 c306bf58 c0088468 bf000178 00000000 00000000
bf60: c3055100 00000003 c306bfa4 c306bf78 c0088580 c00883c4 00000000 00000000
bf80: 00000000 00000000 0000008e 000085f8 00000000 000083f4 00000000 c306bfa8
bfa0: c0027e60 c0088544 000085f8 00000000 00000003 00000000 00000004 00000001
bfc0: 000085f8 00000000 000083f4 00000003 00000000 00000000 40025000 bece3d34
bfe0: 00000000 bece3c90 00008598 400dda3c 60000010 00000003 00000000 00000000
Backtrace:
[<bf000168>] (keys_read+0x0/0x15c [key7]) from [<c0088468>] (vfs_read+0xb4/0xe8)
r8:c0028008 r7:00000003 r6:c306bf78 r5:00000000 r4:c3055100
[<c00883b4>] (vfs_read+0x0/0xe8) from [<c0088580>] (sys_read+0x4c/0x84)
r7:00000003 r6:c3055100 r5:00000000 r4:00000000
[<c0088534>] (sys_read+0x0/0x84) from [<c0027e60>] (ret_fast_syscall+0x0/0x2c)
r6:000083f4 r5:00000000 r4:000085f8
Code: e4a08004 e4a0e004 e8bd01e0 e1b02f82 (14d13001)
---[ end trace 9707b15fc03fdeb0 ]---
Segmentation fault
[root@FriendlyARM plg]#
为什么现在还是报这个错呢?麻烦解答下,谢谢!
*無鈳取玳
级别: 论坛版主
UID: 27
精华: 12
发帖: 5398
金钱: 40120 两
威望: 17929 点
综合积分: 11036 分
注册时间: 2008-01-16
最后登录: 2014-11-22
3楼  发表于: 2010-01-28 17:31

 回 2楼(shawn.wong) 的帖子

你认为dereference一个值为“2”的指针程序就不会挂了吗?你还是先去搞清楚C语言里变量的地址和值的区别再编程吧。
"If you have an apple and I have an apple and we exchange apples, then you and I will
still each have one apple. But if you have an idea and I have an idea and we exchange
these ideas, then each of us will have two ideas."
级别: 新手上路
UID: 11172
精华: 0
发帖: 10
金钱: 50 两
威望: 10 点
综合积分: 20 分
注册时间: 2009-11-29
最后登录: 2010-06-05
4楼  发表于: 2010-01-28 19:54
指针是有点糊涂,不过我发完帖子后想到了问题在哪了,(void *)key7_status只是把key7_status转换为了指针,而且是空指针,而不是取地址了。所以现在上来说下,谢谢斑竹了,学到东西了