Linux中的input子系统
input子系统为一些常用的小规模数据传输的设备提供统一的数据上报系统,把数据以统一的格式上传到用户空间。
适用于input子系统的设备有摇杆,鼠标,键盘,触摸屏等。
参考文档:/home/linux/linux-5.10.61/Documentation/input (可以用uname -r查看内核版本并进入相应的路径下)
input设备驱动层:
input设备驱动层提供对硬件各寄存器的读写访问和将底层硬件的状态的变化转换为标准的输入事件,再通过核心层提交给事件处理器。
input核心层:
input核心层对下层提供了设备驱动层的编程接口,对上友提供了事件处理层的编程接口。
input事件处理层:
事件处理层为用户空间的应用程序提供了统一的访问设备的接口以及对驱动层提交来的事件进行处理。
input子系统相关结构体
查看源码和追代码工具为source insight
为输入设备实现接口的结构体
input_handelr结构体
struct input_handler {
void *private;//驱动程序的私有数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//事件处理函数 被内核调用,不能被中断打断
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);//事件序列处理器 被内核调用,不能被中断打断
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;
const char *name;
const struct input_device_id *id_table;
struct list_head h_list;//以链表的形式存在
struct list_head node;
};
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)];
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)];
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;
ktime_t timestamp[INPUT_CLK_MAX];
};
input_handle结构体
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
三个结构体之间的联系
input_event结构体
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;
unsigned int __pad;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;
__u16 code;
__s32 value;
};
input子系统实现
驱动程序可参考 /home/linux/linux-5.10.61/Documentation/input(uname -r 查看内核版本并进入)
【1】创建input_dev结构体变量
struct input_dev *input_allocate_device*(void);
【2】初始化input_dev结构体变量
(1)name
(2)事件类型
(3)子事件类型
【3】注册input_dev结构体变量
int input_register_device(struct input_dev *dev);
【4】上报事件
【5】注销input_dev结构体变量
void input_unregister_device(struct input_dev *);
【6】释放input_dev结构体变量
void input_free_device(struct input_dev *dev);
常用的上报事件
input子系统事件上报类型
input_allocate_device函数内部实现
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(-1);
struct input_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
timer_setup(&dev->timer, NULL, 0);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
dev_set_name(&dev->dev, "input%lu",
(unsigned long)atomic_inc_return(&input_no));
__module_get(THIS_MODULE);
}
return dev;
}
EXPORT_SYMBOL(input_allocate_device);
整体代码实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/timer.h>
/*
如果在ubuntu上看到现象
ctrl+alt+F1(虚拟终端)
ctrl+alt+F7(退出虚拟终端)
如果想在板子上看到现象
exec 0</dev/tty1
*/
//分配定时器对象
struct timer_list timer;
// 1.分配输入子系统对象
struct input_dev* input;
void timer_function(struct timer_list* list)
{
//定时器处理函数
static int value = 0;
value = value ? 0 : 1;
// 5.上报数据
input_report_key(input, KEY_L, value);
input_report_key(input, KEY_S, value);
input_report_key(input, KEY_ENTER, value);
input_sync(input);
mod_timer(&timer, jiffies + HZ);
}
static int __init timer_input_init(void)
{
int error = 0;
// 2.申请内存空间
input = input_allocate_device();
if (error) {
printk("alloc input memory error\n");
error = -ENOMEM;
goto ERR1;
}
// 3.输入子系统初始化
// 3.1设置事件类型为按键事件
set_bit(EV_KEY, input->evbit); //按键事件
set_bit(EV_SYN, input->evbit); //同步事件
// 3.2设置哪个按键
set_bit(KEY_L, input->keybit); // L按键
set_bit(KEY_S, input->keybit); // S按键
set_bit(KEY_ENTER, input->keybit); // enter按键
// 4.注册
error = input_register_device(input);
if (error) {
printk("register input error\n");
goto err_free_dev;
}
timer.expires = jiffies + HZ; //设置定时时间
timer_setup(&timer, timer_function, 0); //初始化定时器
add_timer(&timer); //启动定时器
return 0;
err_free_dev:
input_free_device(input);
ERR1:
return error;
}
static void __exit timer_input_exit(void)
{
del_timer(&timer);
input_unregister_device(input);
input_free_device(input);
}
module_init(timer_input_init);
module_exit(timer_input_exit);
MODULE_LICENSE("GPL");