linux_davinci按键驱动 http://blog./uid-27717694-id-3658419.html
简单的按键驱动,貌似是只有一个键的操作,定义了3种键状态:按下、未按键、按键释放;
整体思路:驱动设计按键事件本身定义为中断,按键事件用队列处理,貌似没有用到消息机制; 中断方面:使用到标准中断模式:顶半部 + 底半部处理; 临界保护:貌似因为一个键操作,没有过分的保护,只有开关中断的操作;
------------------------------
-
一、概述
-
该按键驱动原理虽简单,但是在处理中却运用到了Linux驱动中中断的一些关键技术,比如“顶半部”和“底半部”使用,等待队列的设置。
-
这里“顶半部”即中断处理函数运行时间很短,基本就做了两件事:1、关中断;2、调用定时器。具体代码如下:
-
-
二、需要的结构定义
-
#define STATUS_NOKEY 0 //无按键状态,按键抬起
-
#define STATUS_DOWNX 1 //有按键状态,但不确定
-
#define STATUS_DOWN 2 //等释放状态,确定按下
-
-
#define BUF_HEAD (keydev.buf[keydev.head])
-
#define BUF_TAIL (keydev.buf[keydev.tail])
-
#define INCBUF(x,mod) ((++(x)) & ((mod)-1))
-
#define MAX_BUTTON_BUF 16 //按键缓冲区大小
-
#define BUTTON_MAJOR 232 // major device NO./* 主设备号 */
-
#define DEVICE_NAME "Key-IN1" /*定义设备驱动的名字,或设备节点名称*/
-
#define RTU_KEY_IN1 GPIO_TO_PIN(6, 10)
-
-
struct button_irq_desc {
-
int irq;
-
unsigned long flags;
-
char *name;
-
};
-
-
/* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
-
static struct button_irq_desc button_irqs[] = {
-
{gpio_to_irq(RTU_KEY_IN1),IRQF_TRIGGER_FALLING, "KEY_IN1"}, /* K1 */
-
};
-
-
//按键结构体
-
typedef struct key_dev{
-
unsigned int keyStatus; //按键状态
-
unsigned char buf[MAX_BUTTON_BUF]; //按键缓冲区
-
unsigned int head,tail; //按键缓冲区头和尾
-
wait_queue_head_t wq; //等待队列
-
struct timer_list key_timer; //按键去抖定时器
-
struct cdev cdev; //cdev结构体
-
} KEY_DEV;
-
-
static KEY_DEV keydev;
-
static struct class *buttons_class;
-
-
三、具体实现
-
static unsigned char get_key(int irqno)
-
{
-
return (gpio_get_value(RTU_KEY_IN1) >> 10);//返回的数值是寄存器IN_DATA67的数值,取
- 第10位的值,即GPIO6_10的数值
-
}
-
-
static void (*keyEvent)(unsigned long key);
-
-
static void keyEvent_raw(unsigned long key)
-
{
-
BUF_HEAD=key; //添加到buffer头
-
keydev.head=INCBUF(keydev.head,MAX_BUTTON_BUF);
-
wake_up_interruptible(&keydev.wq); //唤醒等待队列
-
}
-
-
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
-
{
-
// disable_irqs(); //关闭中断,转入查询状态
-
keydev.keyStatus=STATUS_DOWNX; //转为不确定状态
-
keydev.key_timer.data=get_key(irq)|(irq<<8); //低8位存放键值,高位存放中断号
-
keydev.key_timer.expires=jiffies+HZ/50; //延迟20ms
-
add_timer(&keydev.key_timer); //启动定时器
-
return IRQ_HANDLED;
-
}
-
-
static int buttons_open(struct inode *inode,struct file *file)
-
{
-
int i;
-
int ret=0;
-
-
for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
-
{ // 注册中断处理函数,设置GP6[10]IO口为中断下降沿有效的触发方式
-
ret = request_irq(gpio_to_irq(RTU_KEY_IN1), buttons_interrupt, button_irqs[i].flags, button_irqs[i].name, (void *)&button_irqs[i]);
-
if(ret)
-
{
-
break;
-
}
-
}
-
-
if(ret)
-
{//中断申请失败处理,释放已经注册的中断
-
i--;
-
for(; i>= 0; i--)
-
free_irq(gpio_to_irq(RTU_KEY_IN1), (void *)&button_irqs[i]);
-
return -EBUSY;
-
}
-
-
keydev.head=keydev.tail=0;//清空按键动作缓冲区
-
keyEvent=keyEvent_raw; //函数指针指向按键处理函数
-
return 0;
-
}
-
-
-
static void key_timer_handler(unsigned long key)
-
{
-
if( get_key(key) == (key & 0xff) )
-
{//仍处于按下状态
-
if(keydev.keyStatus==STATUS_DOWNX){//从中断进入
-
keydev.keyStatus=STATUS_DOWN;
-
keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
-
keyEvent(key & 0xff);//记录键值,唤醒等待队列
-
add_timer(&keydev.key_timer);
-
}
-
else{//keyStatus=STATUS_DOWN
-
keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
-
add_timer(&keydev.key_timer);
-
}
-
}
-
else{//键已抬起
-
keydev.keyStatus=STATUS_NOKEY;
-
// enable_irqs();//使能irq
-
}
-
}
-
-
static void keyEvent_dummy(unsigned long key) {}
-
-
static int buttons_close(struct inode *inode,struct file *filp)
-
{
-
keyEvent=keyEvent_dummy;//函数指针指向空函数
-
return 0;
-
}
-
-
static unsigned char keyRead(void)
-
{
-
unsigned char key_ret;
-
key_ret=BUF_TAIL;
-
keydev.tail=INCBUF(keydev.tail,MAX_BUTTON_BUF);
-
return key_ret;
-
}
-
-
static ssize_t buttons_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
-
{
-
static unsigned char key_ret;
-
unsigned long flag;
-
retry:
-
if(keydev.head!=keydev.tail){//当前循环队列中有数据
-
local_irq_save(flag); //进入临界区,关闭中断
-
key_ret=keyRead();//读取按键
-
local_irq_restore(flag); //退出临界区
-
copy_to_user(buffer,(char *)&key_ret,1);
-
return 1;
-
}
-
else {
-
if(filp->f_flags & O_NONBLOCK) //若用户采用非阻塞方式读取
-
return -EAGAIN;
-
interruptible_sleep_on(&keydev.wq);//采用阻塞方式读取
-
goto retry;
-
}
-
return 0;
-
}
-
-
-
static struct file_operations buttons_fops =
-
{
-
.owner = THIS_MODULE,
-
.open = buttons_open, /*open()*/
-
.release = buttons_close, /*release()*/
-
.read = buttons_read, /*read()*/
-
};
-
-
static void buttons_setup_cdev(void)/* 初始化并注册cdev */
-
{
-
int err,devno = MKDEV(BUTTON_MAJOR,0);
-
cdev_init(&keydev.cdev,&buttons_fops);
-
keydev.cdev.owner = THIS_MODULE;
-
keydev.cdev.ops = &buttons_fops;
-
err = cdev_add(&keydev.cdev, devno, 1);
-
if (err)
-
printk(KERN_NOTICE "Error %d adding utukey", err);
-
}
-
static struct gpio gpios_6_array[] =
-
{
-
{ GPIO_TO_PIN(6, 10), GPIOF_IN,"RTU_KEY_IN1"},
-
};
-
-
static int __init buttons_init(void)
-
{
-
int result;
-
dev_t devno = MKDEV(BUTTON_MAJOR,0);//用主次设备号生成设备号
-
-
//申请GPIO,并设置为输入
-
int ret = gpio_request_array(gpios_6_array, ARRAY_SIZE(gpios_6_array));
-
if (ret < 0)
-
{
-
printk(KERN_ALERT "Cannot open GPIO 6_array\n");
-
gpio_free_array(gpios_6_array, ARRAY_SIZE(gpios_6_array));
-
}
-
-
/* 申请设备号 */
-
if (BUTTON_MAJOR)
-
result = register_chrdev_region(devno, 1, DEVICE_NAME);
-
else//动态申请设备号
-
{
-
result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
-
int button_major = MAJOR(devno);
-
printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major);
-
}
-
if (result < 0)
-
return result;
-
-
buttons_setup_cdev();
-
keydev.head=keydev.tail=0;//初始化按键缓冲区
-
keydev.keyStatus=STATUS_NOKEY;//初始化按键状态
-
init_waitqueue_head(&keydev.wq);//初始化等待队列
-
init_timer(&keydev.key_timer);//初始化定时器,实现软件去抖
-
keydev.key_timer.function=key_timer_handler;
-
-
//生成sysfs文件系统所需的class和属性文件
-
buttons_class = class_create(THIS_MODULE,DEVICE_NAME);
-
if(IS_ERR(buttons_class))
-
{
-
printk(KERN_ALERT"err:fail in buttons_class!\n");
-
return -1;
-
}
-
device_create(buttons_class,NULL,MKDEV(BUTTON_MAJOR,0),NULL,DEVICE_NAME);
-
printk(KERN_WARNING"buttons Module initialed!\n");
-
}
-
-
static void __exit buttons_exit(void)
-
{
-
cdev_del(&keydev.cdev); // 注销cdev
-
unregister_chrdev_region(MKDEV(BUTTON_MAJOR, 0), 1); // 释放设备号
-
device_destroy(buttons_class,MKDEV(BUTTON_MAJOR,0));
-
class_destroy(buttons_class);
-
}
-
-
module_init(buttons_init);
-
module_exit(buttons_exit);
-
-
MODULE_AUTHOR("WBL");
-
MODULE_DESCRIPTION("Davinci BUTTON Driver");
-
MODULE_LICENSE("GPL");
|