按键驱动,写成标准字符驱动。下面这个是这个是杨创提供的代码。
/*
*
* 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.中断回调和时间回调,以及按键事件相应