搜索
bottom↓
回复: 6

mini2440 linux按键驱动

[复制链接]

出0入0汤圆

发表于 2012-3-31 21:18:10 | 显示全部楼层 |阅读模式
驱动已验证没有问题
驱动代码:

/* the driver for mini2440 key */
#include <linux/errno.h>                 // perror()
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>                 // kmalloc()
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>                //POLL_IN TASK_INTERRUPTIBLE...
#include <linux/irq.h>                        //IRQ_TYPE_LEVEL_LOW
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/device.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>

#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>

#define DEVICE_NAME "2440button"
#define KEY_NUM 6                                                        //按键数
#define KEYBUF_LEN 8                                                //队列长度
#define KEYPRESS_X 0                                                //按键状态
#define KEYPRESS_DOWN 1
#define KEYPRESS_UP 2
#define BUTTON_DELAY_20MS         (HZ/50)                        //防抖延时20ms

static struct key_irq
{
        int irq;                                 //中断号               
        int pin;
        int pin_setting;                //按键IO设置
        char key_number;                //按键值
};

static struct key_irq key_irqs[] = {
        {IRQ_EINT8,  S3C2410_GPG(0),  S3C2410_GPG0_EINT8,   1},
        {IRQ_EINT11, S3C2410_GPG(3),  S3C2410_GPG3_EINT11,  2},
        {IRQ_EINT13, S3C2410_GPG(5),  S3C2410_GPG5_EINT13,  3},
        {IRQ_EINT15, S3C2410_GPG(7),  S3C2410_GPG7_EINT15,  4},
        {IRQ_EINT14, S3C2410_GPG(6),  S3C2410_GPG6_EINT14,  5},
        {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 6},
};

static struct key_dev
{
        char key_status[KEY_NUM];                        //记录按键状态
        char key_buf[KEYBUF_LEN];                        //按键缓冲队列
        char head;
        char tail;
        struct cdev cdev;
        struct class *key_class;                        //creat device node
        wait_queue_head_t wait;
};

static struct key_dev *key_devp;
static int key_major = 0;
static struct timer_list key_timers[KEY_NUM];        //防抖定时器
static

static void key_timer_handler(unsigned long data)
{
        int index = data;
        int up = s3c2410_gpio_getpin(key_irqs[index].pin);
       
        if( !up ) {
                if( key_devp->key_status[index] == KEYPRESS_X) {
                        key_devp->key_status[index] = KEYPRESS_DOWN;
                        if((key_devp->head + 1)%KEYBUF_LEN != key_devp->tail) {                //QUEUE not full                 
                                key_devp->key_buf[key_devp->head++] = key_irqs[index].key_number;
                                if(key_devp->head >= KEYBUF_LEN)
                                        key_devp->head = 0;
                        }
                        wake_up_interruptible(&key_devp->wait);
                }
                mod_timer(&key_timers[index], jiffies + 2*BUTTON_DELAY_20MS);                //key up shake
        } else {
                key_devp->key_status[index] = KEYPRESS_UP;
        }                       
}

static irqreturn_t key_isr_handler(int irq, void *dev_id)
{
        int key;

        //只响应未按下的按键中断
        for(key = 0; key < KEY_NUM; key++)
                if(irq == key_irqs[key].irq)
                        break;

        if(key_devp->key_status[key] == KEYPRESS_UP) {
//                printk("irq %d\n", key);
                key_devp->key_status[key] = KEYPRESS_X;                                               
                key_timers[key].expires = jiffies + BUTTON_DELAY_20MS;
                add_timer(&key_timers[key]);
        }
       
        return IRQ_HANDLED;
}

static int request_key_irqs(void)
{
        int i;
       
        for(i = 0; i < KEY_NUM; i++) {
                set_irq_type(key_irqs[i].irq, IRQ_TYPE_EDGE_FALLING);                        //下降沿触发,低电平触发不可靠
                if(request_irq(key_irqs[i].irq, key_isr_handler, IRQF_DISABLED, DEVICE_NAME, NULL))
                {
                        printk(KERN_ERR "request irq %d err\n", i);
                        return -1;
                }
        }
        return 0;
}

static ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *fops)
{
        struct key_dev *devp = filp->private_data;
        char key;
        unsigned long flag;

        if(devp->head == devp->tail) {
                if(filp->f_flags & O_NONBLOCK)
                        return -EAGAIN;

                interruptible_sleep_on(&devp->wait);
        }

        if(devp->head != devp->tail) {               
                local_irq_save(flag);
                key = devp->key_buf[devp->tail++];
                if(devp->tail >= KEYBUF_LEN )
                        devp->tail = 0;
                local_irq_restore(flag);
        }
       
        if(put_user(key, buf)) {
                return -EFAULT;
        } else {
                return sizeof(char);
        }
}

static unsigned int key_poll(struct file *filp, poll_table *wait)
{
        unsigned int mask = 0;
        struct key_dev *devp = filp->private_data;

        poll_wait(filp, &devp->wait, wait);

        if(devp->head != devp->tail)
                mask |= POLL_IN | POLLRDNORM;

        return mask;
}

static int key_open(struct inode *inode, struct file *filp)
{
        struct key_dev *devp;
        int i;
       
        devp = container_of(inode->i_cdev, struct key_dev, cdev);
        filp->private_data = devp;
        devp->head = devp->tail = 0;
        for(i = 0; i < KEY_NUM; i++)
                devp->key_status[i] = KEYPRESS_UP;
       
        return 0;
}

static int key_release(struct inode *inode, struct file *filp)
{
        return 0;
}

static struct file_operations key_fops = {
        .owner = THIS_MODULE,
        .read = key_read,
        .poll = key_poll,
        .open = key_open,
        .release = key_release,
};

static void key_setup_cdev(int index)
{
        int err;

        cdev_init(&key_devp->cdev, &key_fops);
        key_devp->cdev.owner = THIS_MODULE;
        err = cdev_add(&key_devp->cdev, MKDEV(key_major, index), 1);
        if(err)
                printk(KERN_NOTICE "err %d add cdev 2440button", err);
        init_waitqueue_head(&key_devp->wait);
}

static int __init key_2440_init(void)
{
        int ret, i;
        dev_t dev;

        ret = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
        if(ret)
                return ret;
        key_major = MAJOR(dev);

        key_devp = kmalloc(sizeof(struct key_dev), GFP_KERNEL);
        if(!key_devp) {
                ret = -ENOMEM;
                goto fail;
        }
        memset(key_devp, 0, sizeof(struct key_dev));
        key_setup_cdev(0);

        key_devp->key_class = class_create(THIS_MODULE, DEVICE_NAME);
        if(IS_ERR(key_devp->key_class)) {
                ret = -EFAULT;
                goto fail;
        }
        device_create(key_devp->key_class, NULL, dev, NULL, DEVICE_NAME);

        request_key_irqs();
        for(i = 0; i < KEY_NUM; i++)
        {
                s3c2410_gpio_cfgpin(key_irqs[i].pin, key_irqs[i].pin_setting);        //设置io为中断
                setup_timer(&key_timers[i], key_timer_handler, i);
        }

        printk("2440key initialized\n");

        return 0;
       
fail:
        unregister_chrdev_region(dev, 1);
        return ret;
}

static void __exit key_2440_exit(void)
{
        int i;

        for(i = 0; i < KEY_NUM; i++)                // release irq
                free_irq(key_irqs[i].irq, NULL);
        cdev_del(&key_devp->cdev);
        device_destroy(key_devp->key_class, MKDEV(key_major, 0));
        class_destroy(key_devp->key_class);
        kfree(key_devp);
        unregister_chrdev_region(MKDEV(key_major, 0), 1);
}

module_init(key_2440_init);
module_exit(key_2440_exit);

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

测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>

int main(void)
{
        int fd, len, ret;
        char key;
        fd_set rfds;
        struct timeval time;

        fd = open("/dev/2440button", O_RDWR);
        if(fd < 0)
        {
                fprintf(stderr, "can't open /dev/2440button\n");
                exit(1);
        }
        fprintf(stderr, "key test\n");

        while(1)
        {
                FD_ZERO(&rfds);
                FD_SET(fd, &rfds);
       
                time.tv_sec = 10;
                time.tv_usec = 0;
               
                ret = select(fd+1, &rfds, NULL, NULL, &time);
                if(ret < 0)
                        printf("select err\n");
                else if(ret == 0)
                {
                        printf("time out\n");
                        exit(0);
                }
                else
                {
                        len = read(fd, &key, 1);
                        if(len == 1)
                                printf("press key %d\n", key);
                        else
                                printf("read error\n");
                }
        }
        close(fd);
        return 0;
}

测试结果:
[@RIVER: 2.6.32.2_river]# ./key
key test
press last key 3
press last key 1
press last key 3
press last key 6
press last key 1
press last key 2
press last key 5
press last key 4
press last key 2       
time out
[@RIVER: 2.6.32.2_river]

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2012-4-5 22:27:23 | 显示全部楼层
很好,又有收获

出0入0汤圆

发表于 2012-4-11 13:48:47 | 显示全部楼层
学习linux驱动开发中,不错的帖子。

出0入0汤圆

发表于 2012-4-11 14:02:39 来自手机 | 显示全部楼层
必须顶,mark

出0入0汤圆

发表于 2012-8-28 10:09:32 | 显示全部楼层
有点乱,lz加点注释啊

出0入0汤圆

发表于 2012-9-4 22:44:46 | 显示全部楼层
就是啊,不是很懂哦!

出0入20汤圆

发表于 2012-9-5 09:43:14 | 显示全部楼层
支持一下
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 16:45

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表