常见输入设备
常见输入设备:按键、键盘、触摸屏、鼠标 等。典型的字符设备驱动。
内核中一个通用的按键驱动:
drivers/input/keyboard/gpio_keys.c基于input架构实现了一个通用的GPIO按键驱动
用户空间接口
- /dev/input/event0/1/2/…
- /dev/input/mouse0/1/2/…
- /dev/input/sj0/1/2/…
- …
输入子系统框架
—— 《linux设备驱动开发详解》
输入子系统实现内核文件:drivers\input\input.c
分层模型
-
核心层
drivers/input/input.c
, 创建input设备类,input设备的主设备号为#define INPUT_MAJOR 13
- 根据输入设备种类、分发事件到不同事件处理器
-
事件处理层
提供具体设备的操作接口,为输入设备(input_dev)创建具体设备文件
-
通用事件处理器(drivers/input/evdev.c)
-
鼠标事件处理器(drivers/input/mousedev.c)
-
摇杆事件处理器(drivers/input/joydev.c)
-
…
-
核心数据结构
linux中使用struct input_dev
描述输入设备
include\linux\input.h
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)];
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;
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)];
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);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
- evbit 表示输入事件类型,事件类型定义 include/uapi/linux/input.h
#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 /* 压力状态事件 */
linux使用struct input_event统一描述输入事件
include\uapi\linux\input.h
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;
__u16 code;
__s32 value;
};
- time,事件发生的事件
- type,事件的类型,例如按键、触摸
- code,事件编码,比如事件类型是按键的话,这就是按键编码值
- value,事件值,如按键事件,value为0表示释放,1表示按下。
输入核心层提供了底层设备驱动所需要的API
分配/释放输入设备
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);
注册/注销输入设备
int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
向系统报告事件
/* 报告指定 type 、 code 的输入事件 */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
/* 报告键值 */
void input_report_key(struct input_dev *dev, unsigned int code, int value);
/* 报告相对坐标 */
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
/* 报告绝对坐标 */
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
/* 报告同步事件 */
void input_sync(struct input_dev *dev);
Linux VFS接口与字符设备相关的file_operations字符设备操作结构体相关的代码在drivers/input/evdev.c实现。所以使用输入子系统后,不需要驱动工程师去实现输入设备那些open、read等操作函数。
应用层读取输入子系统输入
示例:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>
#define DEV_NAME "/dev/input/event0"
static struct input_event in_event;
int main(int argc, char *argv[])
{
int fd;
int ret = 0;
fd = open(DEV_NAME, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", DEV_NAME);
return -1;
}
while (1)
{
ret = read(fd, &in_event, sizeof(in_event));
if (ret == sizeof(in_event)) /* 读取数据成功 */
{
switch (in_event.type)
{
case EV_KEY: /* 按键事件类型 */
if (inputevent.code < BTN_MISC)
{
printf("button %d %s\r\n", in_event.code,
inputevent.value ? "pressed" : "release");
}
else
{
printf("button %d %s\r\n", in_event.code,
in_event.value ? "press" : "release");
}
break;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
}
else
{
printf("读取数据失败\r\n");
}
}
return 0;
}