免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3995 | 回复: 4
打印 上一主题 下一主题

AT91SAM9260按键驱动bug(一加载就死机,搞了2天没搞定,请各位给看看),附源码! [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-06-03 08:58 |只看该作者 |倒序浏览
//**************按键驱动,读取方式*******************
//*********************2010-6-1******************************
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/atmel_pdc.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <mach/at91_pio.h>
#include <linux/timer.h>

#define DEVICE_NAME "key"

#define KEY_TIMER_DELAY1  (HZ/50)   //按键按下去抖延时20ms        
#define KEY_TIMER_DELAY2  (HZ/10)   //按键抬起去抖延时100ms

#define KEYSTATUS_DOWN              0     //按键按下                    
#define KEYSTATUS_UP                1     //按键抬起               
#define KEYSTATUS_DOWNX             2     //按键不确定  
#define KEY_NUM                     4     // 4个按键

#define DP_MAJOR 0                                //主设备号
#define DP_MINOR 0                          //次设备号
static  int key_major = DP_MAJOR;

//***********设备结构体********************************
#define MAX_KEY_BUF     16   // 按键缓存区大小
typedef unsigned char KEY_RET;

typedef struct
{
    unsigned int keyStatus[KEY_NUM];    // 4个按键的按键状态
    KEY_RET buf[MAX_KEY_BUF];
    unsigned int head,tail;             // 按键缓存区头和尾
    wait_queue_head_t wq;               // 等待队列
    struct cdev cdev;                   // cdev 结构体
}KEY_DEV;
KEY_DEV key_dev,*key_devp;

// **************去抖定时器*****************************
static struct timer_list key_timer;


//***************管理按键缓冲区**********************
#define BUF_HEAD (key_dev.buf[key_dev.head]) //缓冲头
#define BUF_TAIL (key_dev.buf[key_dev.tail]) //缓冲尾
#define INCBUF(x,mod) ((++(x))&((mod)-1))

// **************按键硬件资源**************************
static struct key_info
{
    int irq_no;                         // 中断号
    int irq_type;                       // 中断类型
    unsigned int gpio_port;             // GPIO端口
    int key_no;                         // 键值
}key_info_tab[4]={
    {AT91_PIN_PB0,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB0,1},
    {AT91_PIN_PB1,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB1,2},
    {AT91_PIN_PB2,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB2,3},
    {AT91_PIN_PB3,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB3,4},
};


//***************硬件初始化*****************************
static void key_io_init(void)
{
    at91_set_gpio_input(AT91_PIN_PB0, 1);        
    at91_set_deglitch(AT91_PIN_PB0, 1);
        
    at91_set_gpio_input(AT91_PIN_PB1, 1);        
    at91_set_deglitch(AT91_PIN_PB1, 1);

    at91_set_gpio_input(AT91_PIN_PB2, 1);        
    at91_set_deglitch(AT91_PIN_PB2, 1);
        
    at91_set_gpio_input(AT91_PIN_PB3, 1);        
    at91_set_deglitch(AT91_PIN_PB3, 1);
}


//****************按键处理********************************
static void keyEvent(unsigned int key)
{
    BUF_HEAD = key_info_tab[key].key_no;                  // 记录键值
    key_dev.head = INCBUF(key_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针
    wake_up_interruptible(&(key_dev.wq));                // 唤醒等待队列
}


//****************中断处理函数**************************
static irqreturn_t key_irq_handler(int irq,void *dev_id/*,struct pt_regs *reg*/)
{
    int key = (int)dev_id;                //从0开始
    int found = 0;
    int i;
  
    for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)   // 查找产生中断的按键
    {
        if (key_info_tab[i].irq_no == irq)
        {
            found = 1;
            break;
        }
    }
    if (!found)                                     // 没找到
    {
        printk(KERN_ALERT "bad irq %d in button\n", irq);
        return IRQ_NONE;   //错误的中断
    }

        //printk(KERN_ALERT "int happens!\n");

        if(key_dev.keyStatus[key] == KEYSTATUS_UP)                //前一个状态
        {
            disable_irq(key_info_tab[key].irq_no);            
                if(!at91_get_gpio_value(key_info_tab[key].gpio_port))
                {               
                    key_dev.keyStatus[key] = KEYSTATUS_DOWNX;  // 不确定是否为按下
                    key_timer.expires = jiffies + KEY_TIMER_DELAY1;// 延迟
                        key_timer.data = key;
                        add_timer(&key_timer);  // 启动定时器
                }
                else
                        enable_irq(key_info_tab[key].irq_no);
        }

    return IRQ_HANDLED;   //正确的中断
}


//****************定时器处理函数***********************
static void key_timer_handler(unsigned long data)
{
    int key = data;
    int up = at91_get_gpio_value(key_info_tab[key].gpio_port);
    if (!up)
    {
        if (key_dev.keyStatus[key] == KEYSTATUS_DOWNX)
        {
            key_dev.keyStatus[key] = KEYSTATUS_DOWN;
            key_timer.expires = jiffies + KEY_TIMER_DELAY2; //延迟           
            keyEvent(key);  //记录键值,唤醒等待队列
        }
        else
            key_timer.expires = jiffies + KEY_TIMER_DELAY2; //延迟

                add_timer(&key_timer);
    }
    else       //滤波成功
    {
        key_dev.keyStatus[key] = KEYSTATUS_UP;
                del_timer(&key_timer);
        enable_irq(key_info_tab[key].irq_no);
    }
}


// ***************申请系统中断**************************
static int request_irqs(void)
{
    struct key_info *k;
    int i;
    int ret;
    for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)
        {
        k = key_info_tab + i;
        set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type);      
            
        //申请中断,将按键序列号作为参数传入中断服务程序        
        ret = request_irq(k->irq_no,key_irq_handler,0,DEVICE_NAME,(void *)i);        
        if(ret)
        {
            printk(KERN_NOTICE "buttons:ret is %d\r\n",ret);
            return -1;
        }
    }
    return 0;
}


// ***************释放系统中断*************************
static void free0_irqs(void)
{
    struct key_info *k;
    int i;
    for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)
    {
        k = key_info_tab + i;
        free_irq(k->irq_no,(void *)i);
    }
}

//***************文件操作******************************
static int key_open(struct inode *inode, struct file *filp)
{
    key_dev.head = key_dev.tail = 0; //清空按键动作缓冲区
    return 0;
}
static int key_release(struct inode *inode, struct file *filp)
{
    return 0;
}
static ssize_t key_read(struct file *filp, char __user *buf, size_t count,
    loff_t*ppos)
{
    unsigned int key_ret;
    unsigned long flags;
retry:
    if (key_dev.head != key_dev.tail)                         // 缓冲区有数据?
    {
        local_irq_save(flags);                                      // 进入临界区 ,关中断      
        key_ret = BUF_TAIL;                                         // 读出键值
        key_dev.tail = INCBUF(key_dev.tail, MAX_KEY_BUF);     // 调整缓冲区尾指针
        local_irq_restore(flags);                                   // 退出临界区,开中断        
        if(copy_to_user(buf, (void *)&key_ret, sizeof(unsigned int)))       // 拷贝到用户空间   
            return -EFAULT;
        else
            return sizeof(unsigned int);
    }
    else   // 缓冲区没数据
    {
        if (filp->f_flags & O_NONBLOCK)                             // 若采用非阻塞方式读取则返回错误
           return -EAGAIN;
        interruptible_sleep_on(&(key_dev.wq));                   // 使进程睡眠
        if (signal_pending(current))                               //在这里等中断
            return -ERESTARTSYS;
        goto retry;
    }
     return sizeof(unsigned int);
}

//****************文件操作结构体**********************
static struct file_operations key_fops =
{
    .owner = THIS_MODULE,
    .open = key_open,            // 启动设备
    .release = key_release,      // 关闭设备
    .read = key_read,            // 读取按键的键值
};

// ********************************************************
static int __init key0_init(void)
{
    int i;
    int result;  
  
    dev_t devnum = MKDEV(key_major, DP_MINOR);  
    if (key_major)
        result = register_chrdev_region(devnum, 1, DEVICE_NAME);
    else                   //动态申请
    {
        result = alloc_chrdev_region(&devnum, 0, 1, DEVICE_NAME);
        key_major = MAJOR(devnum);
        printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, key_major);
    }

        key_devp = &key_dev;
/*
        key_devp = (KEY_DEV*)kmalloc(sizeof(key_dev),GFP_KERNEL);
      if(!key_devp)
     {
        result = - ENOMEM;
        goto fail_malloc;
        }
*/
    //memset(key_devp,0,sizeof(key_dev));

        //cdev_init(&key_devp->cdev, &key_fops);        //设备注册
        //key_devp->cdev.owner=THIS_MODULE;
        //key_devp->cdev.ops=&key_fops;
    //if(cdev_add(&key_devp->cdev, devnum, 1))                        //
    //    printk(KERN_ALERT"Add char dev error!\n");  

        cdev_init(&key_dev.cdev, &key_fops);        //设备注册
        key_dev.cdev.owner=THIS_MODULE;
        key_dev.cdev.ops=&key_fops;
    if(cdev_add(&key_dev.cdev, devnum, 1))                        //
        printk(KERN_ALERT"Add char dev error!\n");  


       
    for(i = 0; i < KEY_NUM; i++)                        // 初始化结构体
       key_dev.keyStatus[i] = KEYSTATUS_UP;
        key_dev.head = key_dev.tail = 0;     
    init_waitqueue_head(&(key_dev.wq));  // 等待队列

        init_timer(&key_timer);                        //去抖定时器初始化
        key_timer.function=key_timer_handler;

    key_io_init();                                   //硬件 初始化   
   
    if(-1 == request_irqs())
        printk(KERN_NOTICE "request_irqs failed!\r\n");
        return 0;

        printk(KERN_ALERT "INIT ERROR !!!\r\n");
/*       
fail_malloc:
        unregister_chrdev_region(devnum,1);
    return result;
*/

}


// ********************************************************
static void __exit key0_exit(void)
{
    free0_irqs();   
        cdev_del(&key_dev.cdev);   
    kfree(key_devp);
    unregister_chrdev_region(MKDEV(key_major,DP_MINOR),1);  
}

module_init(key0_init);
module_exit(key0_exit);

MODULE_AUTHOR("caohaiming");
MODULE_LICENSE("Dual BSD/GPL");

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [报告]
发表于 2010-06-04 17:02 |只看该作者
能把死机的信息提供一下,好分析的

论坛徽章:
0
3 [报告]
发表于 2010-06-04 20:57 |只看该作者
你最好给个信息截图,这样好分析。  有可能模块初始化函数有问题

论坛徽章:
0
4 [报告]
发表于 2013-07-08 13:53 |只看该作者
什么问题?

论坛徽章:
3
卯兔
日期:2013-08-26 22:14:57未羊
日期:2013-09-08 19:16:05未羊
日期:2014-10-23 10:34:12
5 [报告]
发表于 2013-07-21 23:39 |只看该作者
代码好长,超过20行的,从来不分析
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP