linux按键驱动

按键驱动,写成标准字符驱动。下面这个是这个是杨创提供的代码。

/*
 *
 * A button  driver for UTU2410 a board based on s3c2440
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.No warranty
 * is attached;we cannot take responsibility for errors or
 * fitness for use.
 *
 *
 */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/input.h>


#include <linux/kernel.h>       /* printk() */
#include <linux/fs.h>           /* everything... */
#include <linux/cdev.h>
#include <linux/interrupt.h>    /* request_irq() */
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>        /* copy_to_user() */
#include <linux/delay.h>	/* mdelay() */
#include <linux/irq.h>


#define BUTTONSSTATUS_DOWNX 2
#define BUTTONSSTATUS_DOWN 0
#define BUTTONSSTATUS_UP 1
#define BUF_CLEAR _IO(0xFF, 0)

#define DEVICE_NAME "utu2440-buttons"
#define MAX_BUTTONS_BUF 16
#define BUTTONS_NUM  6
#define BUF_HEAD (utubuttons_dev.buf[utubuttons_dev.head])
#define BUF_TAIL (utubuttons_dev.buf[utubuttons_dev.tail])
#define ISBUTTONS_DOWN(buttons) (s3c2410_gpio_getpin(buttons_info_tab[buttons].gpio_port) == BUTTONSSTATUS_DOWN)
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define BUTTONS_TIME_DELAY (HZ/10)
#define BUTTONS_TIME_DELAY1 (HZ/100)

#define UTUBUTTONS_MAJOR 0
unsigned int utubuttons_major = UTUBUTTONS_MAJOR;


struct utubuttons_dev
{
    struct cdev cdev;
    unsigned int buttons_status[BUTTONS_NUM];
    unsigned int buf[MAX_BUTTONS_BUF];
    unsigned int head,  tail;
    wait_queue_head_t wq;
};

struct utubuttons_dev utubuttons_dev;
struct utubuttons_dev *utubuttons_devp;
struct timer_list buttons_timer[BUTTONS_NUM];

static struct buttons_info
{
    int irq_no;
    int irq_type;
    unsigned int gpio_port;
    unsigned int gpio_setting;
    int buttons_code;
    char *name;
}buttons_info_tab[] = 
{
    {
        IRQ_EINT0, IRQT_FALLING, S3C2410_GPF0, S3C2410_GPF0_INP, 1, "Key Up"
    },
    {
        IRQ_EINT1, IRQT_FALLING, S3C2410_GPF1, S3C2410_GPF1_INP, 2, "Key Down"
    },
    {
        IRQ_EINT2, IRQT_FALLING, S3C2410_GPF2,S3C2410_GPF2_INP, 3, "Key Left"
    },
    {
        IRQ_EINT3, IRQT_FALLING, S3C2410_GPF3,S3C2410_GPF3_INP, 4, "Key Right"
    },
    {
        IRQ_EINT11, IRQT_FALLING, S3C2410_GPG3,S3C2410_GPG3_INP, 5, "Key Enter"
    },
    {
        IRQ_EINT19, IRQT_FALLING, S3C2410_GPG11,S3C2410_GPG11_INP, 6, "Key Exit"
    },
};


static irqreturn_t utubuttons_irq(int irq, void *dev_id)
{
    int buttons = (int)dev_id;
    int i;
    int found = 0;
    
    for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++)
    {
        if (buttons_info_tab[i].irq_no == irq) {
            found = 1;
            break;
        }
    }
    if (!found)
    {
        printk(KERN_NOTICE"bad irq %d in button\n", irq);
        return IRQ_NONE;
    }
    disable_irq(buttons_info_tab[buttons].irq_no);
    utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_DOWNX;
    buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY1;
    add_timer(&buttons_timer[buttons]);
    return IRQ_HANDLED;
}


static int request_irqs(void)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++)
    {
        s3c2410_gpio_cfgpin(buttons_info_tab[i].gpio_port, buttons_info_tab[i].gpio_setting);
        set_irq_type(buttons_info_tab[i].irq_no, buttons_info_tab[i].irq_type);
        if (request_irq(buttons_info_tab[i].irq_no, utubuttons_irq, SA_INTERRUPT, DEVICE_NAME, (void *)i))
        {
            printk(KERN_WARNING "buttons:can't get irq no.%d\n", buttons_info_tab[i].irq_no);            
            return -1;
        }
    }
    return 0;
}


static void free_irqs(void)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++)
    {
        free_irq(buttons_info_tab[i].irq_no, (void *)i);
    }
}


static void buttonsEvent(unsigned buttons)
{
    BUF_HEAD = buttons_info_tab[buttons].buttons_code;
    utubuttons_dev.head = INCBUF(utubuttons_dev.head, MAX_BUTTONS_BUF);
    wake_up_interruptible(&(utubuttons_dev.wq));
}


static ssize_t utubuttons_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
    unsigned int buttons_ret;
    unsigned long flags;
retry:
    if (utubuttons_dev.head != utubuttons_dev.tail)
    {
        local_irq_save(flags);    
        buttons_ret = BUF_TAIL;
        utubuttons_dev.tail = INCBUF(utubuttons_dev.tail, MAX_BUTTONS_BUF);
        local_irq_restore(flags);      
        copy_to_user(buffer, &buttons_ret, sizeof(unsigned int));
        return sizeof(unsigned int);
    }else
    {
        if (filp->f_flags & O_NONBLOCK)
        {
            return -EAGAIN;
        }
        interruptible_sleep_on(&(utubuttons_dev.wq));
        if (signal_pending(current))                          
        {
            return -ERESTARTSYS;
        }
        goto retry;
    }
    
    return sizeof(unsigned int);
}


static int utubuttons_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)
{
    unsigned long flags;
    
    switch (cmd)
    {
        case BUF_CLEAR:
    	local_irq_save(flags);    	
        utubuttons_dev.head = utubuttons_dev.tail = 0;
        local_irq_restore(flags);
         
        printk(KERN_INFO "buttons buffer is cleared\n");      
        break;

        default:
        return  -EINVAL;
    }
    return 0;
}


static void utubuttons_timer_callback(unsigned long data)
{
    int buttons = data;
    if (ISBUTTONS_DOWN(buttons))
    {
        if (utubuttons_dev.buttons_status[buttons] == BUTTONSSTATUS_DOWNX)
        {
            utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_DOWN;
            buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY;
            buttonsEvent(buttons);
            add_timer(&buttons_timer[buttons]);
        }else
        {
            buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY;
            add_timer(&buttons_timer[buttons]);
        }
    }else
    {
        utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_UP;
        enable_irq(buttons_info_tab[buttons].irq_no);
    }
}

static int utubuttons_open(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "utubuttons opened\n");
    return 0;
}

static int utubuttons_release(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "utubuttons released\n");
    return 0;
}

static const struct file_operations utubuttons_fops =
{
    .owner = THIS_MODULE,
    .read  = utubuttons_read,
    .ioctl = utubuttons_ioctl,
    .open  = utubuttons_open,
    .release = utubuttons_release,
};


static void utubuttons_setup_cdev(void)
{
    int err,devno = MKDEV(utubuttons_major,0);
    cdev_init(&utubuttons_dev.cdev,&utubuttons_fops);
    utubuttons_dev.cdev.owner = THIS_MODULE;
    utubuttons_dev.cdev.ops = &utubuttons_fops;
    err = cdev_add(&utubuttons_dev.cdev, devno, 1);
    if (err)
        printk(KERN_NOTICE "Error %d adding utubuttons", err);
}


static int __init utubuttons_init(void)
{
    int result, i;
    dev_t devno = MKDEV(utubuttons_major,0);
		printk(KERN_INFO "Initial utulinux 2440 Buttons driver!\n");

    result = request_irqs();
    if (result) {
        unregister_chrdev_region(devno,1);
        return result;
    }


    if (utubuttons_major)
        result = register_chrdev_region(devno, 1, DEVICE_NAME);
    else
    {
        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        utubuttons_major = MAJOR(devno);
        printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, utubuttons_major);
    }

    if (result < 0)
        return result;


    utubuttons_devp = kmalloc(sizeof(struct utubuttons_dev), GFP_KERNEL);
    if (!utubuttons_devp)
    {
        result = -ENOMEM;
        goto fail_malloc;
    }

    memset(utubuttons_devp, 0, sizeof(struct utubuttons_dev));
    utubuttons_setup_cdev();
    init_waitqueue_head(&(utubuttons_dev.wq));
    utubuttons_dev.head = utubuttons_dev.tail = 0;

   
    for(i = 0; i < BUTTONS_NUM; i++)
    {
        utubuttons_dev.buttons_status[i] = BUTTONSSTATUS_UP;
    }


    for(i = 0; i < BUTTONS_NUM; i++)
    {        
        buttons_timer[i].function = utubuttons_timer_callback;
        buttons_timer[i].data = i;
        init_timer(&buttons_timer[i]);
    }
    return 0;

    fail_malloc: unregister_chrdev_region(devno, 1);
    return result;
}


static void __exit utubuttons_exit(void)
{
    int i;
    cdev_del(&utubuttons_dev.cdev);
    kfree(utubuttons_devp);
    unregister_chrdev_region(MKDEV(utubuttons_major, 0), 1);
    free_irqs();
    for(i = 0; i < BUTTONS_NUM; i++)
    {
        del_timer(&buttons_timer[i]);
    }
}

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

module_init(utubuttons_init);
module_exit(utubuttons_exit); 

分析一下这个驱动。

1.数据结构分析。

a.按键的状态,个人感觉最好用一个union。按键有三种情况,按下,按起,长按

b.按键设备。1,继承字符设备;2,按键状态;3,按键的缓冲区;4,等待队列及其头尾

c.定时器。

d.按键信息,个人理解是按键在特定的开发板上的资源信息,个人感觉最好放在与特定的平台相关的代码下,有利于做到移植时的与平台无关。

                   1.中断号;2,中断类型;3,哪个GPIO;4,GPIO设置;5,按键编码;6,按键名称

e.其他。1.设备名称;2.主设备号。

2.驱动分析

a.入口,出口。module_init(utubuttons_init);module_exit(utubuttons_exit);

     utubuttons_init主要做了以下的事情:

             1.申请设备号,申请中断

              2.注册设备号,分配设备内存并初始化

             3.安装字符设备

             4.初始化按键队列及其头尾

             5.初始化按键状态和定时器

    utubuttons_exit主要做了一下事情:

             1.卸载字符设备

              2.释放设备内存

               3.注销设备号

                4.释放中断

                5. 释放定时器

    其中,安装字符设备为其中心枢纽,联系了设备和驱动的关系

             主要做了如下事情:

             1.初始化字符设备,将驱动和设备关联

             2.填充按键设备

             3.将按键设备添加到内核中

b.标准文件驱动编写

   1.文件操作指针结构赋值

    2.完成相应操作

c.中断回调和时间回调,以及按键事件相应

   

   

 


 

你可能感兴趣的:(linux按键驱动)