Linux输入子系统
来源:互联网 发布:网络封包助手 编辑:程序博客网 时间:2024/06/06 17:14
为何需要input子系统
Linux系统支持的许多设备,都是输入性的设备,如按键、触摸屏、键盘、鼠标。本质上都是一个个的字符设备驱动,Linux系统为这些设备统一管理实现了input子系统,方便驱动和应用的开发。
input子系统软件架构
linux输入子系统主要分三层:driver层,core层, event hander层。
Driver层根据core层提供的接口,向上报告发生的按键动作。然后core根据驱动的类型,分派这个报告给对应的event hander进行处理。
event hander层把数据变化反应到设备模型的文件中(事件缓冲区)。并通知在这些设备模型文件上等待的进程。
input子系统工作原理
注册匹配过程
device注册:
int input_register_device(struct input_dev *dev){ ... //加到input_dev_list list_add_tail(&dev->node, &input_dev_list); //从input_handler_list匹配 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); ...}
handle注册:
int input_register_handler(struct input_handler *handler){ struct input_dev *dev; int error; error = mutex_lock_interruptible(&input_mutex); if (error) return error; INIT_LIST_HEAD(&handler->h_list); //加入input_handler_list list_add_tail(&handler->node, &input_handler_list); //依次对input_dev_list的dev做匹配 list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return 0;}
input_attach_handler就是根据id匹配的过程。
读取过程
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size()) return -EINVAL;for (;;) { if (!evdev->exist) return -ENODEV; if (client->packet_head == client->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; /* * count == 0 is special - no IO is done but we check * for error conditions (see above). */ if (count == 0) break; while (read + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event)) return -EFAULT; read += input_event_size(); } if (read) break; //睡眠等待数据 if (!(file->f_flags & O_NONBLOCK)) { error = wait_event_interruptible(evdev->wait, client->packet_head != client->tail || !evdev->exist); if (error) return error; }}return read;
}
事件上报
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value){ input_event(dev, EV_KEY, code, !!value);}static inline void input_sync(struct input_dev *dev){ input_event(dev, EV_SYN, SYN_REPORT, 0);}
其实都是调用到input_event,最后调用handler->event
static void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count){ struct evdev *evdev = handle->private; struct evdev_client *client; ktime_t time_mono, time_real; time_mono = ktime_get(); time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); rcu_read_lock(); client = rcu_dereference(evdev->grab); if (client) evdev_pass_values(client, vals, count, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_values(client, vals, count, time_mono, time_real); rcu_read_unlock();}static void evdev_pass_values(struct evdev_client *client, const struct input_value *vals, unsigned int count, ktime_t mono, ktime_t real){ struct evdev *evdev = client->evdev; const struct input_value *v; struct input_event event; bool wakeup = false; event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real); /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); for (v = vals; v != vals + count; v++) { event.type = v->type; event.code = v->code; event.value = v->value; __pass_event(client, &event); if (v->type == EV_SYN && v->code == SYN_REPORT) wakeup = true; } spin_unlock(&client->buffer_lock); if (wakeup) wake_up_interruptible(&evdev->wait);//唤醒进程}
如何写input子系统设备驱动
分配一个input_dev结构体
ts->input_dev = input_allocate_device();
设置
ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);
注册
ret = input_register_device(ts->input_dev);
硬件相关的代码,比如在中断服务程序里上报事件
static irqreturn_t goodix_ts_irq_hanbler(int irq, void *dev_id){ struct goodix_ts_data *ts = (struct goodix_ts_data *)dev_id; dprintk(DEBUG_INT_INFO,"==========------TS Interrupt-----============\n"); queue_work(goodix_wq, &ts->work); return IRQ_HANDLED;}static void goodix_ts_work_func(struct work_struct *work){ for (idx= 0; idx < GTP_MAX_KEY_NUM; idx++){ input_report_key(ts->input_dev, touch_key_array[idx], key_value & (0x01<<idx)); } //input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value)); input_sync(ts->input_dev);}
阅读全文
0 0
- Linux 输入子系统 input 子系统
- Linux输入子系统分析 -- 输入子系统初始化
- Linux输入子系统
- Linux输入子系统分析
- Linux输入子系统
- linux输入子系统
- linux 输入子系统 input_dev
- Linux输入子系统
- LINUX 输入子系统分析
- Linux输入子系统
- linux输入子系统介绍
- Linux输入子系统介绍
- LINUX 输入子系统分析
- Linux输入子系统
- Linux input输入子系统
- Linux输入子系统
- Linux输入子系统
- linux输入子系统
- Hadoop安装
- Latex-条目、编号、描述
- 丑数
- ALV Data_Changed事件应用一例
- 命令替换(Command Substitution)
- Linux输入子系统
- 又话指针
- Centos7 安装netcat
- 确定自己的技术路线
- 动态修改日志级别
- CodeForces 279C
- 回溯——“子集之和”问题
- VS中定义的DEBUG常量
- 单词分割(Word Splitting)