Linux输入子系统

为何需要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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值