OK6410之input_dev按键驱动

之前学习过输入子系统的相关概念,这里我们就学习一下如何编写按键驱动。

本次要实现的是让开发板的5个按键代表键盘中的L、S、回车键等。

 

首先看看input_dev结构体:

struct input_dev {      

       void *private;
       const char *name;  //设备名字
       const char *phys;  //文件路径,比如 input/buttons
       const char *uniq;   
       struct input_id id;
       unsigned long evbit[NBITS(EV_MAX)];  //表示支持哪类事件,常用有以下几种事件(可以多选)
       //EV_SYN      同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
       //EV_KEY       键盘事件
       //EV_REL       (relative)相对坐标事件,比如鼠标
       //EV_ABS       (absolute)绝对坐标事件,比如摇杆、触摸屏感应
       //EV_MSC      其他事件,功能
       //EV_LED       LED灯事件
       //EV_SND      (sound)声音事件
       //EV_REP       重复键盘按键事件
  //(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件)   
       //EV_FF         受力事件
       //EV_PWR      电源事件
       //EV_FF_STATUS  受力状态事件
       unsigned long keybit[NBITS(KEY_MAX)];   //存放支持的键盘按键值
                                    //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)
       unsigned long relbit[NBITS(REL_MAX)];    //存放支持的相对坐标值
       unsigned long absbit[NBITS(ABS_MAX)];   //存放支持的绝对坐标值
       unsigned long mscbit[NBITS(MSC_MAX)];   //存放支持的其它事件,也就是功能
       unsigned long ledbit[NBITS(LED_MAX)];    //存放支持的各种状态LED
       unsigned long sndbit[NBITS(SND_MAX)];    //存放支持的各种声音
       unsigned long ffbit[NBITS(FF_MAX)];       //存放支持的受力设备
       unsigned long swbit[NBITS(SW_MAX)];     //存放支持的开关功能

 ... ...

这里需要较为注意的是evbitkeybit数组。

其中evbit表示支持哪类事件,比如按键需要支持键盘事件EV_KEY以及重复键盘事件EV_REP。

而keybit指的是支持的按键值,这里我们要支持的是KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT、KEY_ESC

为了让设置这两个数组,需要用到setbit()函数

set_bit(nr,p);                  //设置某个结构体成员p里面的某位等于nr,支持这个功能

set_bit(EV_KEY, button_dev->evbit);
set_bit(KEY_L, button_dev->keybit);

 

接下来开始注册设备

(1)注册设备

        /*1. 分配一个input_dev结构体 */
	button_dev = input_allocate_device();
	/* 2.设置 能产生按键类事件*/
		/* 哪类事件 */
	set_bit(EV_KEY, button_dev->evbit);
	/* 重复类事件 */
	set_bit(EV_REP, button_dev->evbit);
		/* 能产生哪些事件L,S,ENTER,SHIFT */
 
	set_bit(KEY_L, button_dev->keybit);
 
	/* 3.注册 */
	input_register_device(button_dev);
	

首先通过函数input_allocate_device()分配input_dev结构体,然后设置相关的事件,最后通过input_register_device()函数注册即可。

通过之前学习可知,调用input_register_device()函数后会自动寻找是否有与之匹配的驱动,若是有则connect。

(2)上报事件

当检测到有按键按下则通过input_event()函数上报事件。

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);  //上报事件
 // input_dev *dev :要上报哪个input_dev驱动设备的事件
 // type : 要上报哪类事件, 比如按键事件,则填入: EV_KEY
 // code: 对应的事件里支持的哪个变量,比如按下按键L则填入: KEY_L
 //value:对应的变量里的数值,比如松开按键则填入1,松开按键则填入0

同时要调用input_sync()函数通知系统,这样系统才能知道。



static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0); //就是上报同步事件,告诉内核:input_event()事件执行完毕
}

 

(3)注销设备

input_unregister_device(struct input_dev *dev);   //卸载/sys/class/input目录下的input_dev这个类设备, 一般在驱动出口函数写
 
input_free_device(struct input_dev *dev);   //释放input_dev这个结构体, 一般在驱动出口函数写

通过以上两个函数实现。

 

学习到这里开始编写代码。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/gpio.h>
 
 
 
 
static struct input_dev * button_dev;
static struct pin_desc *irq_pd = NULL;
static struct timer_list buttons_timer;
 
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies + HZ/100);
	return IRQ_RETVAL (IRQ_HANDLED);
 
}
 
struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};
 
struct pin_desc pins_desc[5] = {
	{IRQ_EINT(0), "s1", S3C64XX_GPN(0), KEY_L},
	{IRQ_EINT(1), "s2", S3C64XX_GPN(1), KEY_S},
	{IRQ_EINT(2), "s3", S3C64XX_GPN(2), KEY_ENTER},
	{IRQ_EINT(3), "s4", S3C64XX_GPN(3), KEY_LEFTSHIFT},
	{IRQ_EINT(4), "s5", S3C64XX_GPN(4), KEY_ESC},
};
 
static void buttons_timer_function(unsigned long data)
{
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;
	if (!pindesc)
		return;
	pinval = gpio_get_value(pindesc->pin);
	if (pinval)
	{
	/* 松开的 *//* 最后一个参数0表示松开1表示按下 */
		input_event(button_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(button_dev);
	}
	else 
	{
	/* 按下的 */
		input_event(button_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(button_dev);
	}
}
 
static int button_init(void)
{
	int i = 0;
	/*1. 分配一个input_dev结构体 */
	button_dev = input_allocate_device();
	/* 2.设置 能产生按键类事件*/
		/* 哪类事件 */
	set_bit(EV_KEY, button_dev->evbit);
	/* 重复类事件 */
	set_bit(EV_REP, button_dev->evbit);
		/* 能产生哪些事件L,S,ENTER,SHIFT */
 
	for (i = 0; i < 5; i++)
	{
		
	set_bit(pins_desc[i].key_val, button_dev->keybit);
	}
 
	/* 3.注册 */
	input_register_device(button_dev);
	
	/* 4.硬件相关的操作 */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	buttons_timer.expires  = jiffies + HZ/100;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 5; i++)
	{
		request_irq(pins_desc[i].irq, buttons_irq,  IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, pins_desc[i].name, &pins_desc[i]);		
 
	}
	return 0;
 
}
 
static void button_exit(void)
{
	int i; 
	for (i = 0; i < 5; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}
	del_timer(&buttons_timer);
	 
	input_unregister_device(button_dev);
	input_free_device(button_dev);
}
 
 
module_init(button_init);
module_exit(button_exit);
 
MODULE_LICENSE("GPL");

 

将程序编译并且insmod之后,执行cat /dev/tty1,然后按一下按键,会发现你按的按键值显示在屏幕上。

不过要注意是当你按下代表ENTER的那个键的时候你之前输入的值才会显示在屏幕上。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值