-
简介
- input 子系统就是管理输入的子系统,和pinctrl、gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。
- 按键、鼠标、键盘、触摸屏等都属于输入设备,linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。
- 输入设备本质上还是字符设备,只是在此基础上套上了input框架,用户只需要负责上报输入事件,比如按键值、坐标等信息。
- 对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可
- 为此input子系统分为 input驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点
input子系统框架
编写input驱动我们只关心内核层,在内核层注册input子系统,然后进行事件上报就可以了。
-
input驱动实现
- 向内核注册input设备
input子系统的所有设备主设备号都为 13,我们在使用input子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个input_device即可
*****************input_dev****注册流程
- 定义input_dev结构体
struct input_dev * input_dev;
struct input_dev {
const char *name; //设备名字 不重要,可写不写
const char *phys; //设备物理地址 不重要
const char *uniq; //不重要可不写
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
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)]; //声音
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; //压力
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; //开关
unsigned int hint_events_per_packet; //平均事件数
unsigned int keycodemax; //支持最大按键数
unsigned int keycodesize; //每个键值字节数
void *keycode; //键值起始地址
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff; //设备关联的反馈结构,如果设备支持
struct input_dev_poller *poller;
unsigned int repeat_key; //最近一次按键值,用于连击
struct timer_list timer; //自动连击计时器
int rep[REP_CNT]; //自动连击参数
struct input_mt *mt; //多点触控区域
struct input_absinfo *absinfo; //存放绝对值坐标的相关参数数组
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)]; //反应设备当前的声音状态
unsigned long sw[BITS_TO_LONGS(SW_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);//事件处理函数,主要是接收用户下发的命令,如点亮led
struct input_handle __rcu *grab; //当前占有设备的input_handle
spinlock_t event_lock; //事件锁
struct mutex mutex; //互斥锁
unsigned int users; //打开该设备的用户数量(input_handle)
bool going_away; //标记正在销毁的设备
struct device dev; //一般设备
struct list_head h_list; //设备所支持的input handle
struct list_head node; //用于将此input_dev连接到input_dev_list
unsigned int num_vals; //当前帧中排队的值数
unsigned int max_vals; //队列最大的帧数
struct input_value *vals; //当前帧中排队的数组
bool devres_managed; //表示设备被devres 框架管理,不需要明确取消和释放
ktime_t timestamp[INPUT_CLK_MAX];
}
- 初始化input_dev结构体
input_dev=input_allocate_device();
input_allocate_device()函数,给input_dev分配空间,并作了部分初始化。
- 添加监测设备的信息
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
set_bit(KEY_1,input_dev->keybit);
事件类型
define EV_SYN 0x00 /* 同步事件 */
define EV_KEY 0x01 /* 按键事件 */
define EV_REL 0x02 /* 相对坐标事件 */
define EV_ABS 0x03 /* 绝对坐标事件 */
define EV_MSC 0x04 /* 杂项(其他)事件 */
define EV_SW 0x05 /* 开关事件 */
define EV_LED 0x11 /* LED */
define EV_SND 0x12 /* sound(声音) */
define EV_REP 0x14 /* 重复事件 */
define EV_FF 0x15 /* 压力事件 */
define EV_PWR 0x16 /* 电源事件 */
define EV_FF_STATUS 0x17 /* 压力状态事件 */
支持以下按键(部分)
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
向核心结构体里添加了按键,并且可连续触发,按键code为1。
- 注册input设备
ret=input_register_device(input_dev);
参数为定义的input_dev结构体
返回值:成功0/失败非0
- 上传事件
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
参数1:核心结构体,
参数2:事件类型,
参数3:事件编码,
参数4:事件值
无返回值
- 同步事件
此操作必须有,否则缓冲区不满,返回不了当前操作。
void input_sync(struct input_dev *dev)
参数:核心结构体
无返回值
此函数也是调用了input_event();
代码实列
#include "linux/input.h"
#include "linux/gpio.h"
#include "linux/input-event-codes.h"
#include "linux/interrupt.h"
#include "linux/irq.h"
#include "linux/jiffies.h"
#include "linux/module.h"
#include "linux/of_gpio.h"
#include "linux/platform_device.h"
#include "linux/timer.h"
#include "linux/types.h"
int pin;
int irq;
struct input_dev * input_dev;
struct timer_list timer;
uint8_t value;
irqreturn_t fun_callback(int irq, void *arg)
{
value=gpio_get_value(pin);
mod_timer(&timer,jiffies+msecs_to_jiffies(10));
return 0;
}
void func(struct timer_list *tl)
{
if(value)
{
input_event(input_dev,EV_KEY,KEY_1,1);
}
else
{
input_event(input_dev,EV_KEY,KEY_1,0);
}
input_sync(input_dev);
}
static int probe(struct platform_device *pd)
{
pin=of_get_named_gpio(pd->dev.of_node,"key_pin",0);
printk("pin=%d\r\n",pin);
gpio_request(pin,"key_pim");
gpio_direction_input(pin);
irq= platform_get_irq(pd,0);
printk("irq=%d\r\n",irq);
int ret= devm_request_irq(&pd->dev, irq,fun_callback,IRQ_TYPE_EDGE_BOTH,"key_irq",NULL);
if(ret)
{
}
input_dev=input_allocate_device();
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
set_bit(KEY_1,input_dev->keybit);
ret=input_register_device(input_dev);
if(ret)
{
}
timer_setup(&timer,func, 0);
return 0;
}
struct of_device_id match={
.compatible="key",
};
struct platform_driver drv={
.driver = {
.name = "key",
.of_match_table=&match,
},
.probe=probe,
};
int __init input_init(void)
{
return platform_driver_register(&drv);
}
void __exit input_exit(void)
{
}
module_init(input_init);
module_exit(input_exit);
MODULE_LICENSE("GPL");