|
驱动已验证没有问题
驱动代码:
/* 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周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|