#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include <linux/wait.h>
#include <linux/sched.h>
//#include<linux/poll.h>
#include<linux/irq.h>
#include<asm/irq.h>
#include<linux/interrupt.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include <linux/ioport.h>
#include<asm/signal.h>
#include<mach/regs-gpio.h>
#include<mach/hardware.h>
#include<linux/platform_device.h>
#include<linux/cdev.h>
#include<linux/timer.h>
#include<linux/delay.h>
#include<linux/types.h>
#include<linux/module.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#define DEVICE_NAME "buttons" //设备名
#define KEY_UP 1 //抬起标志
#define KEY_DOWN 0 //按下标志
#define KEY_MID 2 //不确定标?
#define KEY_DELAY_1 (HZ/40)//按键按下过滤延时20ms
static volatile int key_status;//状态标志数组(0,1,2三态)
static volatile char key_values;//最终输出状态结果('0','1'两态,确保read时不会读到KEY_MIDD状态)
static struct timer_list key_timers; //6按键去抖动定时器
/*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读>取到键值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户>态;当没有按键按下时,系统并不*/
/*会轮询按键状态,以节省时钟资源*/
static DECLARE_WAIT_QUEUE_HEAD(button_key_waitq);
/*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/
static volatile int ev_press = 0;
/*
设备
*/
struct key_irq_desc{ //dev_id
int irq;
int number;
char *name;
};
/*
设备结构体
*/
static struct key_irq_desc key_irq[]={
{IRQ_EINT(1),0,"KEY1"},
};
/*
标识按下按键的编号
*/
static volatile int key_press=0;//标识按下的按键的编号
/*
中断处理例程
*/
static irqreturn_t key_interrupt(int irq,void *dev_id)
{
if(key_status==KEY_UP || key_status==KEY_DOWN)
{
key_status=KEY_MID;
key_timers.expires=jiffies+KEY_DELAY_1;
add_timer(&key_timers);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
/*
function 函数定义
*/
static void timer_call(unsigned long number)
{
if(gpio_get_value(S3C64XX_GPN(1))==0)
{
key_status=KEY_DOWN;
key_values=0;
}
else
{
key_status=KEY_UP;
key_values=1;
}
ev_press=1;
wake_up_interruptible(&button_key_waitq);
}
/*
对应open函数
*/
static int s3c64xx_buttons_open(struct inode *inode,struct file *file)
{
int err=0;
key_values=1;
key_status=KEY_UP;
/*注册中断函数*/
err=request_irq(key_irq[0].irq,key_interrupt,IRQ_TYPE_EDGE_BOTH,
key_irq[0].name,(void *)&key_irq[0]);
if (err)
{
/*如果出错,返回*/
printk(KERN_ALERT "KEY REQUEST failed (%d)\n",err);
return -EBUSY;
}
setup_timer(&key_timers,timer_call,0);
/*注册成功,则中断队列标记为1,表示可以通过read 读取*/
ev_press = 0;
/*正常返回*/
return 0;
}
/*
对应read函数
无论count参数是多少,都输出完整key_buff数组
*/
static int s3c64xx_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
{
unsigned long err;
if (!(filp->f_flags & O_NONBLOCK))
{
if (!ev_press)
{
/*当中断标识为0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/
wait_event_interruptible(button_key_waitq, ev_press);
}
}
/*把中断标识清零*/
ev_press = 0;
/*一组键值被传递到用户空间*/
err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
/*
对应release函数
*/
static int s3c64xx_buttons_close(struct inode *inode,struct file *filp)
{
if (key_irq[0].irq > 0)
free_irq(key_irq[0].irq,(void *)&key_irq[0]);
del_timer(&key_timers);
return 0;
}
/*设备操作集*/
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c64xx_buttons_open,
.release = s3c64xx_buttons_close,
.read = s3c64xx_buttons_read,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/*设备初始化,主要是注册设备*/
static int __init dev_init(void)
{
int ret;
//set gpn0 to input
s3c_gpio_cfgpin(S3C64XX_GPN(0), S3C_GPIO_SFN(0));
//上拉有效
s3c_gpio_setpull(S3C64XX_GPN(0), S3C_GPIO_PULL_UP);
/*把按键设备注册为misc 设备,其设备号是自动分配的*/
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
/*注销设备*/
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("easylwl_wondfo");