linux3.4.2 输入子系统之按键驱动

目录

 

1.  编写一个普通的按键类型的input子系统驱动

2. 事件类型Event types

3. input_dev结构体各成员变量

4 将按键驱动通过输入子系统模拟键盘上的L S Enter 空格按键

5 测试按键驱动


1.  编写一个普通的按键类型的input子系统驱动

更多的可以参考linux源码下的:Documentation/input/input-programming.txt

#include <linux/input.h>          //需包含头文件
static struct input_dev *keys_dev;//定义input_dev结构体

keys_dev = input_allocate_device();//分配input_dev
set_bit(EV_KEY, keys_dev->evbit);  //设置evbit(Event types)
set_bit(KEY_L, keys_dev->keybit);  //设置keybit,可以所有键盘上的值
input_register_device(keys_dev);   //注册input_dev

input_event(keys_dev, EV_KEY, irq_pd->key_val, 0);//上报按键事件
input_sync(keys_dev);//将事件同步到内核

input_unregister_device(keys_dev);//注销input_dev
input_free_device(keys_dev);//释放input_dev

2. 事件类型Event types

更多的可以参考linux源码下的:Documentation/input/event-codes.txt

EV_SYN

0X0用于事件间的分割标志。事件可能按时间或空间进行分割,就像在多点触摸协议中的例子。
EV_KEY        0X1用来描述键盘,按键或者类似键盘设备的状态变化。
EV_REL0X2用来描述相对坐标轴上数值的变化,例如:鼠标向左方移动了5个单位。
EV_ABS0X3用来描述相对坐标轴上数值的变化,例如:描述触摸屏上坐标的值。
EV_MSC0X4当不能匹配现有的类型时,使用该类型进行描述。
EV_SW0X5用来描述具备两种状态的输入开关。
EV_LED0X11用于控制设备上的LED灯的开和关。
EV_SND0X12用来给设备输出提示声音。
EV_REP0X14用于可以自动重复的设备(autorepeating)。
EV_FF0X15用来给输入设备发送强制回馈命令。
EV_PWR0X16特别用于电源开关的输入。.
EV_FF_STATU0X17用于接收设备的强制反馈状态。
EV_MAX0X1F 
EV_CNT

(EV_MAX+1)

 

3. input_dev结构体各成员变量

/* include/linux/input.h */
struct input_dev {
 const char *name;//设备名
 const char *phys;
 const char *uniq;
 struct input_id id;//用于匹配事件处理层handler
 
 unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//用于记录支持的事件类型的位图
 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//记录支持的按键值的位图
 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//记录支持的相对坐标的位图
 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//记录支持的绝对坐标的位图
 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//led
 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//beep
 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
 
 unsigned int keycodemax;//支持的按键值的个数
 unsigned int keycodesize;//每个键值的字节数
 void *keycode;//存储按键值的数组首地址
 int (*setkeycode)(struct input_dev *dev,
     unsigned int scancode, unsigned int keycode);//修改键值的函数,可选
 int (*getkeycode)(struct input_dev *dev,
     unsigned int scancode, unsigned int *keycode);//获取扫描码的键值,可选
 
 struct ff_device *ff;
 
 unsigned int repeat_key;//最近一次按键值,用于连击
 struct timer_list timer;//自动连击计时器
 
 int sync;//最后一次同步后没有新的事件置1
 
 int abs[ABS_CNT];//当前各个坐标的值
 int rep[REP_MAX + 1];//自动连击的参数
 
 unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反映当前按键状态的位图
 unsigned long led[BITS_TO_LONGS(LED_CNT)];//反映当前led状态的位图
 unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映当前beep状态的位图
 unsigned long sw[BITS_TO_LONGS(SW_CNT)];
 
   /*tp驱动代码里一般使用input_set_abs_params函数设置
     函数参数从右往左依次代表输入设备指针、坐标轴、最小值、最大值、分辨率、基准值。
      最后两个参数也可以填为0,代表设备非常精确并且总能精确的回到中心位置。*/
 int absmax[ABS_CNT];//记录各个坐标的最大值
 int absmin[ABS_CNT];//记录各个坐标的最小值
 int absfuzz[ABS_CNT];//记录各个坐标的分辨率
 int absflat[ABS_CNT];//记录各个坐标的基准值
 int absres[ABS_CNT];
 
 int (*open)(struct input_dev *dev);//打开函数
 void (*close)(struct input_dev *dev);//关闭函数
 int (*flush)(struct input_dev *dev, struct file *file);//断开连接时冲洗数据
 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);//回调函数,可选
 
 struct input_handle *grab;
 
 spinlock_t event_lock;
 struct mutex mutex;
 
 unsigned int users;
 bool going_away;
 
 struct device dev;
 
 struct list_head h_list;//handle链表
 struct list_head node;//input_dev链表
};

4 将按键驱动通过输入子系统模拟键盘上的L S Enter 空格按键

#include <linux/module.h>  //定义了THIS_MODULE宏
#include <linux/fs.h>      //定义了file_operations结构体
#include <linux/device.h>  //定义了class_create/device_create/class_destory/device_destory函数
                           //定义了class 与 class_device结构体
#include <linux/interrupt.h>//定义了IRQF_TRIGGER_RISING 宏
#include <linux/input.h>   //定义了按键相关
#include <linux/gpio.h>    //包含了s3c2410_gpio_cfgpin等io操作函数
#include <asm/uaccess.h>   //定义了copy_to_user函数
#include <asm/io.h>        //定义了ioremap 与iounremap函数
#include <mach/regs-gpio.h>//包含了GPIO相关宏
#include <mach/irqs.h>     //包含了中断相关定义

struct pin_desc_s{
	int irq;	//按键的外部中断标志位, 可在irqs.h中查找有那些中断
	char *name;	//名称,一段字符串,自己定义.
	unsigned int pin;//引脚
	unsigned int key_val;//键值,键盘上每个按键(0~9,a~z...)都有一个对应的键值,用于判断按下了那个键.
};

struct pin_desc_s pins_desc[4] = {
	{IRQ_EINT0,  "S2", S3C2410_GPF(0),  KEY_L},
	{IRQ_EINT2,  "S3", S3C2410_GPF(2),  KEY_S},
	{IRQ_EINT11, "S4", S3C2410_GPG(3),  KEY_ENTER},
	{IRQ_EINT19, "S5", S3C2410_GPG(11), KEY_LEFTSHIFT},
};

static struct input_dev *keys_dev;
static struct pin_desc_s *irq_pd;//在中断发生时,用于保存现场.
static struct timer_list keys_timer;

/**
 * 超时处理函数
 */
void keys_timer_handler(unsigned long data)
{
	unsigned int pinval;

	if(NULL == irq_pd){
		return;
	}
	pinval = s3c2410_gpio_getpin(irq_pd->pin);
	if (pinval){/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		input_event(keys_dev, EV_KEY, irq_pd->key_val, 0);
		input_sync(keys_dev);
	}
	else{/* 按下 */
		input_event(keys_dev, EV_KEY, irq_pd->key_val, 1);
		input_sync(keys_dev);
	}
}

/**
 * 按键中断处理函数
 */
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{
	irq_pd = (struct pin_desc_s *)dev_id;//保存发生中断的引脚相关信息,以便在超时处理函数中使用.
	//jiffies记录自系统启动一来产生的节拍数
	//HZ-1s,HZ/100 = 10ms
	mod_timer(&keys_timer, jiffies+HZ/100);//修改定时器,10ms后启动定时器
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int key_drv_init(void)
{
	unsigned char i = 0;
	//分配,设置,注册一个input_dev结构体
	keys_dev = input_allocate_device();
	set_bit(EV_KEY, keys_dev->evbit);  //设置能产生按键类事件
	set_bit(EV_REP, keys_dev->evbit);  //设置键盘重复按键类事件
	set_bit(KEY_L, keys_dev->keybit);  //支持L按键
	set_bit(KEY_S, keys_dev->keybit);  //支持按键S
	set_bit(KEY_ENTER, keys_dev->keybit);//支持按键enter
	set_bit(KEY_LEFTSHIFT, keys_dev->keybit);//支持按键左边的shift
	input_register_device(keys_dev);//注册
    
	//初始化定时器,防抖动
	init_timer(&keys_timer);
	keys_timer.function = keys_timer_handler;//定时器的处理函数
	add_timer(&keys_timer);

	/* 注册四个中断 */
	for (i = 0; i < 4; i++){
		request_irq(
				pins_desc[i].irq,//中断号
				key_irq_handler,//中断处理函数
				(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING),//触发中断的条件
				pins_desc[i].name,//中断名,自己定义的一段字符串
				&pins_desc[i]);//发生中断时,该指针会传递给中断函数.
	}
	return 0;
}

static void key_drv_exit(void)
{
	int i;
	for (i = 0; i < 4; i++){
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}
	del_timer(&keys_timer);
	input_unregister_device(keys_dev);
	input_free_device(keys_dev);
}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");

5 测试按键驱动

驱动安装后,可以在/dev/input/目录下查看是否安装成功。

/driver # ls -l /dev/input/event*
crw-rw----    1 0  0  13,  64 Jan  1 00:00 /dev/input/event0 //系统自带触摸屏驱动
crw-rw----    1 0  0  13,  65 Jan  1 07:58 /dev/input/event1 //我们编写的按键驱动

测试方式1: 输入命令 exec 0</dev/tty1  在开发板将标准收入改为按键,此时在开发板可以通过按键执行ls命令

测试方式2: 输入hexdump /dev/input/event1 按开发板上的按键,有如下输出:

       |   秒     |   微秒 |按键类| 键值|  value |
0000000 761d 0000 e479 0003 0001 0026 0001 0000   按下                              
0000010 761d 0000 e48b 0003 0000 0000 0000 0000   同步值                              
0000020 761d 0000 921e 0005 0001 0026 0000 0000   松开                         
0000030 761d 0000 9230 0005 0000 0000 0000 0000   同步值   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值