【Linux】浅析Input子系统

文章目录

  • 1. 框架
    • 1.1 数据结构
    • 1.2 evdev_handler
    • 1.3 evdev_init
    • 1.4 input_register_handler
  • 2. 应用如何打开节点并读取到事件数据
    • 2.1 evdev_fops
    • 2.2 evdev_open
    • 2.3 evdev_release
    • 2.4 evdev_read
    • 2.5 evdev_write
    • 2.6 evdev_poll
    • 2.7 evdev_fasync
    • 2.8 evdev_ioctl
    • 2.9 evdev_ioctl_compat
    • 2.10 总结
  • 3. Driver 如何注册input设备并上报事件数据
    • 3.1 申请input device
      • 3.1.1 input_allocate_device
    • 3.2 注册input device
      • 3.2.1 input_register_device
    • 3.3 给input device 赋予属性
      • 3.3.1 input_set_abs_params
    • 3.4 input_report
      • 3.4.1 驱动程序实例
      • 3.4.2 事件流转分析
        • 3.4.2.1 分析input_event
        • 3.4.2.2 dispositon分析
        • 3.4.2.3 接着分析input_handle_event
        • 3.4.2.4 分析 input_pass_values
        • 3.4.2.5 分析 input_to_handler
        • 3.4.2.6 分析 evdev_event(s)
        • 3.4.2.7 分析 evdev_pass_values
        • 3.4.2.8 环形缓冲区
        • 3.4.2.9 分析__pass_event
  • 4. 应用打开节点并注入事件数据
    • 4.1 事件注入
    • 4.2 事件流向input_handler
    • 4.3 事件流向input_device
  • 5. input.c
    • 5.1 核心数据结构
      • 5.1.1 input_dev
      • 5.1.2 input_handler
      • 5.1.3 input_handle
      • 5.1.4 ff_device
      • 5.1.5 input_device_id
      • 5.1.6 ops
    • 5.2 功能细节
      • 5.2.1 Init 和 exit
      • 5.2.2 softrepeat 事件重复上报机制
  • 6. input-mt.c
    • 6.1 数据结构
      • 6.1.1 input_mt_slot
      • 6.1.2 input_mt
    • 6.2 init
      • 6.2.1 input_mt_init_slots
    • 6.3 TypeB report 实例
      • 6.3.1 input_mt_slot
      • 6.3.2 input_mt_report_slot_state
      • 6.3.3 input_report_abs
      • 6.3.4 input_sync
  • 7. input_polldev.c
    • 7.1 数据结构
      • 7.1.1 input_polled_dev
      • 7.1.2 input_dev_poller
    • 7.2 驱动程序实例
    • 7.3 input_allocate_polled_device
    • 7.4 input_register_polled_device
      • 7.4.1 input_polled_device_work
      • 7.4.2 input_open_polled_device
      • 7.4.3 input_close_polled_device
    • 7.5 设定/查询轮询间隔参数
    • 7.6 实验
  • 8. input_poller.c
    • 8.1 数据结构
    • 8.2 驱动程序实例
    • 8.3 input_setup_polling
    • 8.4 input_dev_poller_finalize
    • 8.5 input_dev_poller_start
    • 8.6 input_dev_poller_stop
    • 8.7 设定/查询轮询间隔参数
      • 8.7.1 驱动作者调用函数
      • 8.7.2 应用程序访问节点
  • 9. Keymap
  • 10. input-compat.c

基于Linux Kernel 5.10
src path: https://elixir.bootlin.com/linux/v5.10/source/drivers/input

1. 框架

在这里插入图片描述
可以用三个数据结构来描述框架,这里简单写

// ===== 输入子系统, 核心层 =====
文件 /drivers/input/input.c 内, 关键函数
    input_init              // 初始化
    class_register          // 注册类
    register_chrdev         // 注册设备(主设备号13)

    input_register_device   // 注册硬件设备 input_dev
    input_register_handler  // 注册软件抽象 input_handler
    input_register_handle   // 注册连接     input_handle


// ===== 软件抽象层, 系统已实现 =====
1. static 初始化一个 input_handler 全局变量
2. 注册此变量, input_register_handler
3. 实现 event connect 等函数

struct input_handler {
    event,connect, disconnect, start        // 函数具体实现的指针
    int minor;                              // 次设备号
    const char *name;                       // 显示在proc/bus/input/handlers

    const struct input_device_id *id_table; // 驱动支持的id表(用于匹配input_dev)
    const struct input_device_id *blacklist;// id表黑名单

    struct list_head    h_list;             // 存放input_handle(没有r)的链表
    struct list_head    node;               // 存放input_handler自身的链表
};


// ===== 连接层, 系统已实现 =====
多个文件 /drivers/input/*dev.c 内*/
1. *dev_connect里分配 input_handle(没有r)变量
2. 设置/初始化此变量
3. 注册, input_register_handle(没有r)

struct input_handle(没有r) {
    void *private;                          // 私有数据, 指向了父指针
    struct input_dev *dev;                  // 指向input_dev
    struct input_handler *handler;          // 指向 input_handler

    struct list_head    d_node;             // 存放input_dev->h_list的链表
    struct list_head    h_node;             // 存放input_handler->h_list的链表
};

// ===== 硬件设备层, 自己写 =====
1. 分配一个input_dev变量
2. 设置/初始化此变量
3. 注册, input_register_device
4. 硬件相关代码, open, close, event, sync等等.

struct input_dev {
    const char *name;                       // 设备描述
    const char *phys;                       // 设备路径
    struct input_id id;                     // 总线类型. 供应商/产品/版本信息. 用于匹配 input_handler

    unsigned long evbit[NBITS(EV_MAX)];     // 记录支持的事件类型位图
    unsigned long keybit[NBITS(KEY_MAX)];   // 记录支持的按键值位图
    unsigned long relbit[NBITS(REL_MAX)];   // 记录支持的相对坐标位图, 如滚轮
    unsigned long absbit[NBITS(ABS_MAX)];   // 记录支持的绝对坐标位图, 如触摸屏

    struct list_head    h_list;             // 存放input_handle(没有r)的链表
    struct list_head    node;               // 存放input_dev自身的链表
};

可以用下面的图描述这三者关系
在这里插入图片描述
Linux 中 input_handler、input_handle和input_dev 之间的联系
在 Linux 中,input_handler、input_handle 和 input_dev 是与输入子系统相关的三个重要概念,它们之间的联系如下:

  1. input_dev:input_dev 是输入设备的抽象表示,它包含了输入设备的各种属性和状态信息,如设备名称、设备类型、设备 ID、事件类型、事件码等。input_dev 通常由输入设备驱动程序创建,并在注册到输入子系统后被使用。
  2. input_handle:input_handle 是输入设备的句柄,它用于标识一个输入设备的实例。每个输入设备都有一个唯一的 input_handle,它由输入子系统分配并在注册时返回给输入设备驱动程序。input_handle 可以用于在输入子系统中查找和操作输入设备。
  3. input_handler:input_handler 是输入事件的处理程序,它负责处理输入设备产生的事件。每个输入设备都可以有一个或多个 input_handler,它们按照优先级顺序依次处理输入事件。input_handler 可以是内核中的一个模块或者用户空间中的一个应用程序。
    综上所述,input_dev、input_handle 和 input_handler 是 Linux 输入子系统中的三个重要概念,它们之间的联系是:input_dev 表示输入设备的抽象表示,input_handle 是输入设备的句柄,用于标识一个输入设备的实例,input_handler 是输入事件的处理程序,负责处理输入设备产生的事件。

接下来用evdev举例看看这三者关系是如何建立的

1.1 数据结构

struct evdev {
        int open;                                // open,当用户open此设备时,open的值加1
        struct input_handle handle;              // 包括了匹配的 dev 与 handler
        struct evdev_client __rcu *grab;         // 可以指定grab evdev_client,这样只会有此client获得input事件数据
        struct list_head client_list;            // 用于把所有client链接在一起
        spinlock_t client_lock; /* protects client_list */
        struct mutex mutex;
        struct device dev;                       // 用来嵌入到设备模型中
        struct cdev cdev;                        // 字符设备
        bool exist;                              // evdev init成功
};
struct evdev_client {
        unsigned int head;              // 头指针
        unsigned int tail;              // 尾指针
        unsigned int packet_head;       // 包指针
        spinlock_t buffer_lock;         /* protects access to buffer, head and tail */
        wait_queue_head_t wait;         //等待队列头
        struct fasync_struct *fasync;   //异步通知机制
        struct evdev *evdev;            //client的evdev
        struct list_head node;          //连接同一evdev的其他client
        enum input_clock_type clk_type;
        bool revoked;                   //client被注销则置1
        unsigned long *evmasks[EV_CNT];
        unsigned int bufsize;           // 循环队列大小,此size向上对齐2的幂
        struct input_event buffer[];    // 循环队列数组
};

evdev和evdev_client的关系
在这里插入图片描述

1.2 evdev_handler

static struct input_handler evdev_handler = {
        .event                = evdev_event,
        .events                = evdev_events,
        .connect        = evdev_connect,
        .disconnect        = evdev_disconnect,
        .legacy_minors        = true,
        .minor                = EVDEV_MINOR_BASE,
        .name                = "evdev",
        .id_table        = evdev_ids,
};

1.3 evdev_init

static int __init evdev_init(void)
{
        return input_register_handler(&evdev_handler);
}

1.4 input_register_handler

/**
 * input_register_handler - register a new input handler
 * @handler: handler to be registered
 *
 * This function registers a new input handler (interface) for input
 * devices in the system and attaches it to all input devices that
 * are compatible with the handler.
 */
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);

        list_add_tail(&handler->node, &input_handler_list);

        /* 根据handler从input_dev_list匹配dev来执行connect */
        list_for_each_entry(dev, &input_dev_list, node)
                input_attach_handler(dev, handler);    

        /* wakeup thread in poll_wait */
        input_wakeup_procfs_readers();

        mutex_unlock(&input_mutex);
        return 0;
}
EXPORT_SYMBOL(input_register_handler);
👇
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
        const struct input_device_id *id;
        int error;

        id = input_match_device(handler, dev);
        if (!id)
                return -ENODEV;

        error = handler->connect(handler, dev, id);
        if (error && error != -ENODEV)
                pr_err("failed to attach handler %s to device %s, error: %d\n",
                       handler->name, kobject_name(&dev->dev.kobj), error);

        return error;
}
👇
static const struct input_device_id *input_match_device(struct input_handler *handler,
                                                        struct input_dev *dev)
{
        const struct input_device_id *id;

        for (id = handler->id_table; id->flags || id->driver_info; id++) {
                if (input_match_device_id(dev, id) &&
                    (!handler->match || handler->match(handler, dev))) {
                        return id;
                }
        }

        return NULL;
}
👇
bool input_match_device_id(const struct input_dev *dev,
                           const struct input_device_id *id)
{
        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
                if (id->bustype != dev->id.bustype)
                        return false;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
                if (id->vendor != dev->id.vendor)
                        return false;

        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
                if (id->product != dev->id.product)
                        return false;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
                if (id->version != dev->id.version)
                        return false;

        if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
            !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
            !bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
            !bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
            !bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
            !bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
            !bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
            !bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
            !bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
            !bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
                return false;
        }

        return true;
}
EXPORT_SYMBOL(input_match_device_id);

看完了match再来看connect
evdev_connect

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
                         const struct input_device_id *id)
{
        struct evdev *evdev;
        int minor;
        int dev_no;
        int error;

        minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
        if (minor < 0) {
                error = minor;
                pr_err("failed to reserve new minor: %d\n", error);
                return error;
        }

        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
        if (!evdev) {
                error = -ENOMEM;
                goto err_free_minor;
        }

        INIT_LIST_HEAD(&evdev->client_list);
        spin_lock_init(&evdev->client_lock);
        mutex_init(&evdev->mutex);
        evdev->exist = true;

        dev_no = minor;
        /* Normalize device number if it falls into legacy range */
        if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
                dev_no -= EVDEV_MINOR_BASE;
        dev_set_name(&evdev->dev, "event%d", dev_no);

        //给 handle 匹配 device 和 handler
        evdev->handle.dev = input_get_device(dev);
        evdev->handle.name = dev_name(&evdev->dev);
        evdev->handle.handler = handler;            //evdev_handler
        evdev->handle.private = evdev;

        //创建device
        evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
        evdev->dev.class = &input_class;
        evdev->dev.parent = &dev->dev;
        evdev->dev.release = evdev_free;
        //初始化device,kobj mutex list pm node ...
        device_initialize(&evdev->dev);

        //将 handle 挂在 device 和 handler的链表上
        error = input_register_handle(&evdev->handle);
        if (error)
                goto err_free_evdev;

        //初始化字符设备 list kobj ops
        cdev_init(&evdev->cdev, &evdev_fops);

        //调用 cdev_add 和 device_add
        //cdev_add: 给cdev添加 dev_t,添加到 cdev_map
        //device_add: 创建节点 /dev/input/eventX
        error = cdev_device_add(&evdev->cdev, &evdev->dev);
        if (error)
                goto err_cleanup_evdev;

        return 0;

 err_cleanup_evdev:
        evdev_cleanup(evdev);
        input_unregister_handle(&evdev->handle);
 err_free_evdev:
        put_device(&evdev->dev);
 err_free_minor:
        input_free_minor(minor);
        return error;
}

至此三者的链接就建立完毕了,呈现出这样的关系

在这里插入图片描述

2. 应用如何打开节点并读取到事件数据

在这里,应用作为input事件的消费者,可以通过打开节点的方式获取到事件数据

2.1 evdev_fops

/dev/input/eventX
static const struct file_operations evdev_fops = {
        .owner                = THIS_MODULE,
        .read                = evdev_read,
        .write                = evdev_write,
        .poll                = evdev_poll,
        .open                = evdev_open,
        .release        = evdev_release,
        .unlocked_ioctl        = evdev_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl        = evdev_ioctl_compat,
#endif
        .fasync                = evdev_fasync,
        .llseek                = no_llseek,
};

2.2 evdev_open

核心动作是创建client将其初始化后挂入evdev的client_list

static int evdev_open(struct inode *inode, struct file *file)
{
        struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
        unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);//hint_events_per_packet估算buffersize
        struct evdev_client *client;//一个监听线程对应一个client
        int error;

        client = kvzalloc(struct_size(client, buffer, bufsize), GFP_KERNEL);
        if (!client)
                return -ENOMEM;

        init_waitqueue_head(&client->wait);   //等待队列
        client->bufsize = bufsize;            //设定bufsize
        spin_lock_init(&client->buffer_lock); //buffer_lock
        client->evdev = evdev;                //建立链接
        evdev_attach_client(evdev, client);   //将client挂入evdev的client_list

        //evdev_open_device: evdev->open ++, input_open_device(&evdev->handle);
        //input_open_device:handle->open++
        error = evdev_open_device(evdev);
        if (error)
                goto err_free_client;

        file->private_data = client;
        //设定file的f_mode 使可读写等
        stream_open(inode, file);

        return 0;

 err_free_client:
        evdev_detach_client(evdev, client);
        kvfree(client);
        return error;
}

2.3 evdev_release

核心动作是将client从client_list删除

static int evdev_release(struct inode *inode, struct file *file)
{
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        unsigned int i;

        mutex_lock(&evdev->mutex);

        if (evdev->exist && !client->revoked)
                input_flush_device(&evdev->handle, file);

        /* 如果此client grab此evdev则执行ungrab操作 */
        evdev_ungrab(evdev, client);
        mutex_unlock(&evdev->mutex);

        /* 将client从 client_list 上删除 */
        evdev_detach_client(evdev, client);

        for (i = 0; i < EV_CNT; ++i)
                bitmap_free(client->evmasks[i]);

        kvfree(client);

        /* 检查执行input_close_device */
        evdev_close_device(evdev);

        return 0;
}
static void evdev_close_device(struct evdev *evdev)
{
        mutex_lock(&evdev->mutex);

        /* evdev存在 并且 evdev没有open的device了,则close此handle对应的device */
        if (evdev->exist && !--evdev->open)
                input_close_device(&evdev->handle);

        mutex_unlock(&evdev->mutex);
}
/**
 * input_close_device - close input device
 * @handle: handle through which device is being accessed
 *
 * This function should be called by input handlers when they
 * want to stop receive events from given input device.
 */
void input_close_device(struct input_handle *handle)
{
        struct input_dev *dev = handle->dev;

        mutex_lock(&dev->mutex);

        __input_release_device(handle);

        if (!--dev->users) {
                if (dev->poller)
                        input_dev_poller_stop(dev->poller);

                if (dev->close)
                        dev->close(dev);
        }

        if (!--handle->open) {
                /*
                 * synchronize_rcu() makes sure that input_pass_event()
                 * completed and that no more input events are delivered
                 * through this handle
                 */
                synchronize_rcu();
        }

        mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL(input_close_device);

2.4 evdev_read

应用程序实例

int main(int argc, char **argv)
{
    int len;
    struct input_event event;
    ......
    while (1)
    {
            len = read(fd, &event, sizeof(event));        //如果使用阻塞方式未读取到信息内核将处于休眠态
                                                          //如果使用非阻塞方式且未读取到信息将进入else分支
            if (len == sizeof(event))
            {
                    printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
            }
            else
            {        
                    printf("read err %d\n", len);
            }
    }
}

        

内核函数分析

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;   //存放读取的event
        size_t read = 0;            //读到数据字节数
        int error;

        if (count != 0 && count < input_event_size())
                return -EINVAL;

        for (;;) {
                /* evdev不存在或client被注销 */
                if (!evdev->exist || client->revoked)
                        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).
                 */
                /* 读0字节直接返回 */
                if (count == 0)
                        break;

                /* 
                 * 读取到字节数+一个event字节数 <= 用户读取的字节数 且 缓冲区中有event
                 * 则会将缓冲区读取到的event发送到用户空间,并更新读取到的字节数  
                 */
                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)) {
                        /* 将用户读取线程插入等待队列,满足以下条件之一结束等待
                         *    1.缓冲区不为空
                         *    2.evdev不存在,此时应该是被注销了
                         *    3.client被注销
                         */
                        error = wait_event_interruptible(client->wait,
                                        client->packet_head != client->tail ||
                                        !evdev->exist || client->revoked);
                        if (error)
                                return error;
                }
        }

        return read;
}

2.5 evdev_write

对应事件注入,应用程序实例如下

    struct input_event ev;
    
    memset(&ev, 0, sizeof(struct input_event));
    ev.type = EV_ABS;
    gettimeofday(&ev.time, NULL); 
    ev.code = ABS_X;
    ev.value = 0x000001f8;
    if (write(fd, &ev, sizeof(struct input_event)) < 0) {
        printf("write error\n");
    }

内核函数分析

static ssize_t evdev_write(struct file *file, const 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;
        int retval = 0;//从用户空间读取字节数

        /* 用户写字节数 < event 字节数 返回inval */
        if (count != 0 && count < input_event_size())
                return -EINVAL;

        retval = mutex_lock_interruptible(&evdev->mutex);
        if (retval)
                return retval;

        /* evdev如果不存在和client被注销返回 nodev */
        if (!evdev->exist || client->revoked) {
                retval = -ENODEV;
                goto out;
        }

        /* 
         * 如从用户空间读取字节数+一个event字节数 <= 用户写入的字节数
         * 则按event注入节点
         */
        while (retval + input_event_size() <= count) {

                if (input_event_from_user(buffer + retval, &event)) {
                        retval = -EFAULT;
                        goto out;
                }
                retval += input_event_size();

                input_inject_event(&evdev->handle,
                                   event.type, event.code, event.value);
                cond_resched();//调度
        }

 out:
        mutex_unlock(&evdev->mutex);
        return retval;
}

2.6 evdev_poll

/* ./03_input_read_poll /dev/input/event0 */
int main(int argc, char **argv)
{
        //int fd;
        int err;
        int len;
        int ret;
        int i;
        unsigned char byte;
        int bit;
        struct input_id id;
        unsigned int evbit[2];
        struct input_event event;
        
        char *ev_names[] = {
                "EV_SYN ",
                "EV_KEY ",
                "EV_REL ",
                "EV_ABS ",
                "EV_MSC ",
                "EV_SW        ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "EV_LED ",
                "EV_SND ",
                "NULL ",
                "EV_REP ",
                "EV_FF        ",
                "EV_PWR ",
                };
        /*输入参数解析和设备ID信息获取输出*/
        if (argc < 2)
        {
                printf("Usage: %s <dev>\n", argv[0]);
                return -1;
        }
        int num_devices = argc - 1;
        int fd[num_devices];
        struct pollfd fds[num_devices];
        nfds_t nfds = num_devices;//poll设备数量
        
        //遍历输出各节点的input_id和ev_bit信息
        for(int i = 0; i < num_devices; i ++)
        {
                fd[i] = open(argv[i+1], O_RDWR | O_NONBLOCK);        
                if (fd[i] < 0)
                {
                        printf("open %s err\n", argv[i]);
                        return -1;
                }
                err = ioctl(fd[i], EVIOCGID, &id);
                if (err == 0)
                {
                        printf("dev:%s\n", argv[i+1] );                
                        printf("bustype = 0x%x\n", id.bustype );
                        printf("vendor        = 0x%x\n", id.vendor  );
                        printf("product = 0x%x\n", id.product );
                        printf("version = 0x%x\n", id.version );
                }
                len = ioctl(fd[i], EVIOCGBIT(0, sizeof(evbit)), &evbit);
                if (len > 0 && len <= sizeof(evbit))
                {
                        printf("support ev type: ");
                        for (int j = 0; j < len; j++)
                        {
                                byte = ((unsigned char *)evbit)[j];
                                for (bit = 0; bit < 8; bit++)
                                {
                                        if (byte & (1<<bit)) {
                                                printf("%s ", ev_names[j*8 + bit]);
                                        }
                                }
                        }
                        printf("\n");
                }
                printf("\n");
        }

        /*使用poll函数完成多个设备节点信息实时读取*/
        while (1)
        {
                //poll所有的设备
                for(int i = 0; i < num_devices; i ++)
                {
                        fds[i].fd = fd[i];
                        fds[i].events  = POLLIN;
                        fds[i].revents = 0;
                        ret = poll(fds, nfds, 100);
                }

                if (ret > 0)
                {
                        //输出revents == POLLIN的设备的input_event
                        for(int i = 0; i < num_devices; i++)
                        {
                                if (fds[i].revents == POLLIN)
                                {
                                        while (read(fd[i], &event, sizeof(event)) == sizeof(event))
                                        {
                                                printf("get event:dev:%s type = 0x%x, code = 0x%x, value = 0x%x\n", argv[i+1], event.type, event.code, event.value);
                                        }
                                }
                        }
                }
                else if (ret == 0)
                {
                        //printf("time out\n");
                }
                else
                {
                        printf("poll err\n");
                }
                
        }

        return 0;
}

内核函数分析

/* No kernel lock - fine */
static __poll_t evdev_poll(struct file *file, poll_table *wait)
{
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        __poll_t mask;

        /* 休眠直到 wakeup client->wait */
        poll_wait(file, &client->wait, wait);

        /* 设备存在且client未被注销 */
        if (evdev->exist && !client->revoked)
                mask = EPOLLOUT | EPOLLWRNORM;
        else
                mask = EPOLLHUP | EPOLLERR;

        /* 如果缓冲区不为空则返回 EPOLLIN | EPOLLRDNORM */
        if (client->packet_head != client->tail)
                mask |= EPOLLIN | EPOLLRDNORM;

        return mask;
}

/*
    EPOLLIN         读就绪
    EPOLLOUT        写就绪
    EPOLLPRI        有数据紧急读取
    EPOLLERR        assoc. fd有错误情况发生
    EPOLLHUP        assoc. fd发生挂起
    EPOLLRT         设置边缘触发(ET)(默认的是水平触发)
    EPOLLONESHOT    设置为 one-short 行为,一个事件(event)被拉出后,对应的fd在内部被禁用
    EPOLLRDNORM     和 EPOLLIN 相等
    EPOLLRDBAND     优先读取的数据带(data band)
    EPOLLWRNORM     和 EPOLLOUT 相等
    EPOLLWRBAND     优先写的数据带(data band)
    EPOLLMSG        忽视
*/

2.7 evdev_fasync

应用程序实例

/* 存放驱动设备文件 */
static int fd;
/* SGIO信号对应函数 */
static void sig_func(int sig)
{
        int val;
        read(fd, &val, 4);
        printf("get button : 0x%x\n", val);
}

int main(int argc, char **argv)
{
        int val;
        struct pollfd fds[1];
        int timeout_ms = 5000;
        int ret;
        int        flags;
        
        /* 1. 判断参数 */
        if (argc != 2) 
        {
                printf("Usage: %s <dev>\n", argv[0]);
                return -1;
        }

        /* 给信号注册函数,当线程收到SIGIO信号时执行sig_func函数 */
        signal(SIGIO, sig_func);

        /* 2. 打开文件 */
        fd = open(argv[1], O_RDWR);
        if (fd == -1)
        {
                printf("can not open file %s\n", argv[1]);
                return -1;
        }

        /* 向内核的文件系统层次传递PID */
        fcntl(fd, F_SETOWN, getpid());
        /* 读取驱动程序中的flag */
        flags = fcntl(fd, F_GETFL);
        /* 设置驱动flag中FASYNC位为1,此操作会导致驱动中fasync函数被调用 */
        fcntl(fd, F_SETFL, flags | FASYNC);

        while (1)
        {
                sleep(2);
        }
        
        close(fd);
        
        return 0;
}

内核函数分析

static int evdev_fasync(int fd, struct file *file, int on)
{
        struct evdev_client *client = file->private_data;

        /* 初始化client->fasync */
        return fasync_helper(fd, file, on, &client->fasync);
}
👇
下面的函数在device上报事件数据时会调用
static void __pass_event(struct evdev_client *client,
                         const struct input_event *event)
{
        client->buffer[client->head++] = *event;
        client->head &= client->bufsize - 1;

        if (unlikely(client->head == client->tail)) {
                /*
                 * This effectively "drops" all unconsumed events, leaving
                 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
                 */
                client->tail = (client->head - 2) & (client->bufsize - 1);

                client->buffer[client->tail] = (struct input_event) {
                        .input_event_sec = event->input_event_sec,
                        .input_event_usec = event->input_event_usec,
                        .type = EV_SYN,
                        .code = SYN_DROPPED,
                        .value = 0,
                };

                client->packet_head = client->tail;
        }

        if (event->type == EV_SYN && event->code == SYN_REPORT) {
                client->packet_head = client->head;
                /* 向监听线程发SIGIO信号 */
                kill_fasync(&client->fasync, SIGIO, POLL_IN);
        }
}

2.8 evdev_ioctl

应用程序实例

int main(int argc, char **argv)
{
        int fd;
        int err;
        int len;
        int i;
        unsigned char byte;
        int bit;
        struct input_id id;
        unsigned int evbit[2];
        char *ev_names[] = {
                "EV_SYN ",
                "EV_KEY ",
                "EV_REL ",
                "EV_ABS ",
                "EV_MSC ",
                "EV_SW        ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "NULL ",
                "EV_LED ",
                "EV_SND ",
                "NULL ",
                "EV_REP ",
                "EV_FF        ",
                "EV_PWR ",
                };
        
        if (argc != 2)
        {
                printf("Usage: %s <dev>\n", argv[0]);
                return -1;
        }

        fd = open(argv[1], O_RDWR);
        if (fd < 0)
        {
                printf("open %s err\n", argv[1]);
                return -1;
        }
        //获取fd设备节点的id信息
        err = ioctl(fd, EVIOCGID, &id);
        if (err == 0)
        {
                printf("bustype = 0x%x\n", id.bustype );
                printf("vendor        = 0x%x\n", id.vendor  );
                printf("product = 0x%x\n", id.product );
                printf("version = 0x%x\n", id.version );
        }

        //获取fd设备节点的evbit信息
        len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
        if (len > 0 && len <= sizeof(evbit))
        {
                //根据evbit信息判断输出节点支持的事件类型
                printf("support ev type: ");
                for (i = 0; i < len; i++)
                {
                        byte = ((unsigned char *)evbit)[i];
                        for (bit = 0; bit < 8; bit++)
                        {
                                if (byte & (1<<bit)) {
                                        printf("%s ", ev_names[i*8 + bit]);
                                }
                        }
                }
                printf("\n");
        }

        return 0;
}

内核函数分析

static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
        return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
}
👇
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
                                void __user *p, int compat_mode)
{
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        int retval;

        retval = mutex_lock_interruptible(&evdev->mutex);
        if (retval)
                return retval;

        if (!evdev->exist || client->revoked) {
                retval = -ENODEV;
                goto out;
        }

        retval = evdev_do_ioctl(file, cmd, p, compat_mode);

 out:
        mutex_unlock(&evdev->mutex);
        return retval;
}
👇
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
                           void __user *p, int compat_mode)
{
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        struct input_dev *dev = evdev->handle.dev;
        struct input_absinfo abs;
        struct input_mask mask;
        struct ff_effect effect;
        int __user *ip = (int __user *)p;
        unsigned int i, t, u, v;
        unsigned int size;
        int error;

        /* First we check for fixed-length commands */
        switch (cmd) {

        case EVIOCGVERSION:
                return put_user(EV_VERSION, ip);

        case EVIOCGID:
                if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
                        return -EFAULT;
                return 0;

        case EVIOCGREP:
                if (!test_bit(EV_REP, dev->evbit))
                        return -ENOSYS;
                if (put_user(dev->rep[REP_DELAY], ip))
                        return -EFAULT;
                if (put_user(dev->rep[REP_PERIOD], ip + 1))
                        return -EFAULT;
                return 0;

        case EVIOCSREP:
                if (!test_bit(EV_REP, dev->evbit))
                        return -ENOSYS;
                if (get_user(u, ip))
                        return -EFAULT;
                if (get_user(v, ip + 1))
                        return -EFAULT;

                input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
                input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);

                return 0;

        case EVIOCRMFF:
                return input_ff_erase(dev, (int)(unsigned long) p, file);

        case EVIOCGEFFECTS:
                i = test_bit(EV_FF, dev->evbit) ?
                                dev->ff->max_effects : 0;
                if (put_user(i, ip))
                        return -EFAULT;
                return 0;

        case EVIOCGRAB:
                if (p)
                        return evdev_grab(evdev, client);
                else
                        return evdev_ungrab(evdev, client);

        case EVIOCREVOKE:
                if (p)
                        return -EINVAL;
                else
                        return evdev_revoke(evdev, client, file);

        case EVIOCGMASK: {
                void __user *codes_ptr;

                if (copy_from_user(&mask, p, sizeof(mask)))
                        return -EFAULT;

                codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
                return evdev_get_mask(client,
                                      mask.type, codes_ptr, mask.codes_size,
                                      compat_mode);
        }

        case EVIOCSMASK: {
                const void __user *codes_ptr;

                if (copy_from_user(&mask, p, sizeof(mask)))
                        return -EFAULT;

                codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
                return evdev_set_mask(client,
                                      mask.type, codes_ptr, mask.codes_size,
                                      compat_mode);
        }

        case EVIOCSCLOCKID:
                if (copy_from_user(&i, p, sizeof(unsigned int)))
                        return -EFAULT;

                return evdev_set_clk_type(client, i);

        case EVIOCGKEYCODE:
                return evdev_handle_get_keycode(dev, p);

        case EVIOCSKEYCODE:
                return evdev_handle_set_keycode(dev, p);

        case EVIOCGKEYCODE_V2:
                return evdev_handle_get_keycode_v2(dev, p);

        case EVIOCSKEYCODE_V2:
                return evdev_handle_set_keycode_v2(dev, p);
        }

        size = _IOC_SIZE(cmd);

        /* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr)        ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
        switch (EVIOC_MASK_SIZE(cmd)) {

        case EVIOCGPROP(0):
                return bits_to_user(dev->propbit, INPUT_PROP_MAX,
                                    size, p, compat_mode);

        case EVIOCGMTSLOTS(0):
                return evdev_handle_mt_request(dev, size, ip);

        case EVIOCGKEY(0):
                return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
                                            KEY_MAX, size, p, compat_mode);

        case EVIOCGLED(0):
                return evdev_handle_get_val(client, dev, EV_LED, dev->led,
                                            LED_MAX, size, p, compat_mode);

        case EVIOCGSND(0):
                return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
                                            SND_MAX, size, p, compat_mode);

        case EVIOCGSW(0):
                return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
                                            SW_MAX, size, p, compat_mode);

        case EVIOCGNAME(0):
                return str_to_user(dev->name, size, p);

        case EVIOCGPHYS(0):
                return str_to_user(dev->phys, size, p);

        case EVIOCGUNIQ(0):
                return str_to_user(dev->uniq, size, p);

        case EVIOC_MASK_SIZE(EVIOCSFF):
                if (input_ff_effect_from_user(p, size, &effect))
                        return -EFAULT;

                error = input_ff_upload(dev, &effect, file);
                if (error)
                        return error;

                if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
                        return -EFAULT;

                return 0;
        }

        /* Multi-number variable-length handlers */
        if (_IOC_TYPE(cmd) != 'E')
                return -EINVAL;

        if (_IOC_DIR(cmd) == _IOC_READ) {

                if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
                        return handle_eviocgbit(dev,
                                                _IOC_NR(cmd) & EV_MAX, size,
                                                p, compat_mode);

                if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {

                        if (!dev->absinfo)
                                return -EINVAL;

                        t = _IOC_NR(cmd) & ABS_MAX;
                        abs = dev->absinfo[t];

                        if (copy_to_user(p, &abs, min_t(size_t,
                                        size, sizeof(struct input_absinfo))))
                                return -EFAULT;

                        return 0;
                }
        }

        if (_IOC_DIR(cmd) == _IOC_WRITE) {

                if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {

                        if (!dev->absinfo)
                                return -EINVAL;

                        t = _IOC_NR(cmd) & ABS_MAX;

                        if (copy_from_user(&abs, p, min_t(size_t,
                                        size, sizeof(struct input_absinfo))))
                                return -EFAULT;

                        if (size < sizeof(struct input_absinfo))
                                abs.resolution = 0;

                        /* We can't change number of reserved MT slots */
                        if (t == ABS_MT_SLOT)
                                return -EINVAL;

                        /*
                         * Take event lock to ensure that we are not
                         * changing device parameters in the middle
                         * of event.
                         */
                        spin_lock_irq(&dev->event_lock);
                        dev->absinfo[t] = abs;
                        spin_unlock_irq(&dev->event_lock);

                        return 0;
                }
        }

        return -EINVAL;
}

2.9 evdev_ioctl_compat

逻辑同上evdev_ioctl,多了数据类型转换和标志位compat_mode置1

static long evdev_ioctl_compat(struct file *file,
                                unsigned int cmd, unsigned long arg)
{
        return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
}

2.10 总结

从上面的分析不难看出,应用程序读取节点event数据的核心逻辑就是判断client->packet_head != client->tail client->buffer不为空,就从buffer中读取event事件。
如果client->buffer为空,poll和阻塞read会选择将线程挂入client->wait休眠,device再次上报事件时会唤醒这些线程。

3. Driver 如何注册input设备并上报事件数据

在这里,device作为事件的生产者,需要通过driver中的逻辑完成input事件的上报
申请input device > 设定input device 的属性 > 注册input device > 上报事件

3.1 申请input device

3.1.1 input_allocate_device

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
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);

3.2 注册input device

3.2.1 input_register_device

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 *
 * Note that this function is also used to register managed input devices
 * (ones allocated with devm_input_allocate_device()). Such managed input
 * devices need not be explicitly unregistered or freed, their tear down
 * is controlled by the devres infrastructure. It is also worth noting
 * that tear down of managed input devices is internally a 2-step process:
 * registered managed input device is first unregistered, but stays in
 * memory and can still handle input_event() calls (although events will
 * not be delivered anywhere). The freeing of managed input device will
 * happen later, when devres stack is unwound to the point where device
 * allocation was made.
 */
 int input_register_device(struct input_dev *dev)
 {
        struct input_devres *devres = NULL;
        struct input_handler *handler;
        unsigned int packet_size;
        const char *path;
        int error;

        if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
                dev_err(&dev->dev,
                        "Absolute device without dev->absinfo, refusing to register\n");
                return -EINVAL;
        }

        if (dev->devres_managed) {//devres_managed device生命周期跟随driver
                devres = devres_alloc(devm_input_device_unregister,
                                      sizeof(*devres), GFP_KERNEL);
                if (!devres)
                        return -ENOMEM;

                devres->input = dev;
        }

        /* Every input device generates EV_SYN/SYN_REPORT events. */
        __set_bit(EV_SYN, dev->evbit);//设定支持EV_SYN

        /* KEY_RESERVED is not supposed to be transmitted to userspace. */
        __clear_bit(KEY_RESERVED, dev->keybit);//设定不支持KEY_RESERVED

        /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
        input_cleanse_bitmasks(dev);//清除不支持type的evbit

        packet_size = input_estimate_events_per_packet(dev);//估算packet_size
        if (dev->hint_events_per_packet < packet_size)//支持driver自定义input_dev的packet_size
                dev->hint_events_per_packet = packet_size;

        dev->max_vals = dev->hint_events_per_packet + 2;//一帧最多可以插入的value
        dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
        if (!dev->vals) {
                error = -ENOMEM;
                goto err_devres_free;
        }

        /*
         * If delay and period are pre-set by the driver, then autorepeating
         * is handled by the driver itself and we don't do it in input.c.
         */
        if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
                input_enable_softrepeat(dev, 250, 33);//设定dev,250ms第一次自动上报,33ms为周期自动上报

        if (!dev->getkeycode)
                dev->getkeycode = input_default_getkeycode;

        if (!dev->setkeycode)
                dev->setkeycode = input_default_setkeycode;

        if (dev->poller)
                input_dev_poller_finalize(dev->poller);

        error = device_add(&dev->dev);
        if (error)
                goto err_free_vals;

        path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
        pr_info("%s as %s\n",
                dev->name ? dev->name : "Unspecified device",
                path ? path : "N/A");
        kfree(path);

        error = mutex_lock_interruptible(&input_mutex);
        if (error)
                goto err_device_del;

        list_add_tail(&dev->node, &input_dev_list);

        list_for_each_entry(handler, &input_handler_list, node)
                input_attach_handler(dev, handler);

        input_wakeup_procfs_readers();

        mutex_unlock(&input_mutex);

        if (dev->devres_managed) {
                dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
                        __func__, dev_name(&dev->dev));
                devres_add(dev->dev.parent, devres);
        }
        return 0;

err_device_del:
        device_del(&dev->dev);
err_free_vals:
        kfree(dev->vals);
        dev->vals = NULL;
err_devres_free:
        devres_free(devres);
        return error;
}
EXPORT_SYMBOL(input_register_device);

3.3 给input device 赋予属性

属性分为 type code value 三种,举个例子

type:
#define EV_SYN                        0x00                                                                                                //同步事件 Synchronize
code:
/*
 * Synchronization events.
 */
#define SYN_REPORT                0                                                                                                //用于将事件同步并分离为同时发生的输入数据变化的数据包。
#define SYN_CONFIG                1                                                                                                        //To Be Discussed 待讨论
#define SYN_MT_REPORT                2                                                                                //用于同步和分离触摸事件。
#define SYN_DROPPED                3                                                                                        //用于指示 evdev 客户端的事件队列中的缓冲区溢出。
#define SYN_MAX                        0xf                                                                                                //SYN类型事件最大个数和提供位掩码支持
#define SYN_CNT                        (SYN_MAX+1)                                                //SYN类型事件支持事件个数 count
value:
//value根据事件的含义不同而不同
//例如 SYN_CONFIG 表示这是一个config事件它的value可以随意指定

Ref: https://blog.csdn.net/weixin_43444989/article/details/122597029
可以直接操作位图赋值

    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->propbit[0] = BIT(INPUT_PROP_DIRECT);

也可以使用一些接口

//设定type为EV_ABS的code的 最小值、最大值、噪声和flat
//噪声值见input_defuzz_abs_event
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
                          int min, int max, int fuzz, int flat)
//将设备标记为能够处理特定事件 ,设定 code
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
//键盘相关 没用过
int input_set_keycode(struct input_dev *dev,
                      const struct input_keymap_entry *ke)
//设定时间戳,在input_report_key API 中会被调用
void input_set_timestamp(struct input_dev *dev, ktime_t timestamp)

3.3.1 input_set_abs_params

相关数据结构

/**
 * struct input_absinfo - used by EVIOCGABS/EVIOCSABS ioctls
 * @value: latest reported value for the axis.
 * @minimum: specifies minimum value for the axis.
 * @maximum: specifies maximum value for the axis.
 * @fuzz: specifies fuzz value that is used to filter noise from
 *        the event stream.
 * @flat: values that are within this value will be discarded by
 *        joydev interface and reported as 0 instead.
 * @resolution: specifies resolution for the values reported for
 *        the axis.
 *
 * Note that input core does not clamp reported values to the
 * [minimum, maximum] limits, such task is left to userspace.
 *
 * The default resolution for main axes (ABS_X, ABS_Y, ABS_Z)
 * is reported in units per millimeter (units/mm), resolution
 * for rotational axes (ABS_RX, ABS_RY, ABS_RZ) is reported
 * in units per radian.
 * When INPUT_PROP_ACCELEROMETER is set the resolution changes.
 * The main axes (ABS_X, ABS_Y, ABS_Z) are then reported in
 * in units per g (units/g) and in units per degree per second
 * (units/deg/s) for rotational axes (ABS_RX, ABS_RY, ABS_RZ).
 */
struct input_absinfo {
        __s32 value;
        __s32 minimum;
        __s32 maximum;
        __s32 fuzz;
        __s32 flat;
        __s32 resolution;
};

函数分析

#define ABS_MAX                        0x3f
#define ABS_CNT                        (ABS_MAX+1)

/**
 * input_alloc_absinfo - allocates array of input_absinfo structs
 * @dev: the input device emitting absolute events
 *
 * If the absinfo struct the caller asked for is already allocated, this
 * functions will not do anything.
 */
void input_alloc_absinfo(struct input_dev *dev)
{
        /* 如果input_dev->absinfo存在则直接返回 */
        if (dev->absinfo)
                return;
        /* 给所有的ABS event申请absinfo */
        dev->absinfo = kcalloc(ABS_CNT, sizeof(*dev->absinfo), GFP_KERNEL);
        if (!dev->absinfo) {
                dev_err(dev->dev.parent ?: &dev->dev,
                        "%s: unable to allocate memory\n", __func__);
                /*
                 * We will handle this allocation failure in
                 * input_register_device() when we refuse to register input
                 * device with ABS bits but without absinfo.
                 * 
                 * 当我们拒绝注册具有 ABS 位但没有 absinfo 的输入设备时,
                 * 我们将在 input_register_device() 中处理此分配失败。
                 */
        }
}
EXPORT_SYMBOL(input_alloc_absinfo);

void input_set_abs_params(struct input_dev *dev, unsigned int axis,
                          int min, int max, int fuzz, int flat)
{
        struct input_absinfo *absinfo;

        /* 为input_dev申请absinfo空间 */
        input_alloc_absinfo(dev);
        /* 如果申请失败直接返回 */
        if (!dev->absinfo)
                return;

        /* 将ABS event 设定参数存入absinfo结构体对应成员内 */
        absinfo = &dev->absinfo[axis];
        absinfo->minimum = min;
        absinfo->maximum = max;
        absinfo->fuzz = fuzz;
        absinfo->flat = flat;

        /* input_dev 位图置位 */
        __set_bit(EV_ABS, dev->evbit);
        __set_bit(axis, dev->absbit);
}
EXPORT_SYMBOL(input_set_abs_params);

3.4 input_report

3.4.1 驱动程序实例

input_report_abs(ts->pen_input_dev, ABS_X, pen_x);
input_report_abs(ts->pen_input_dev, ABS_Y, pen_y);
input_report_abs(ts->pen_input_dev, ABS_PRESSURE, pen_pressure);
...
input_sync(ts->pen_input_dev);

3.4.2 事件流转分析

input_report_abs input_sync是封装了input_event函数

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
        input_event(dev, EV_ABS, code, value);
}
static inline void input_sync(struct input_dev *dev)
{
        input_event(dev, EV_SYN, SYN_REPORT, 0);
}

3.4.2.1 分析input_event

input_event开始着手分析事件流转

/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * This function should be used by drivers implementing various input
 * devices to report input events. See also input_inject_event().
 *
 * NOTE: input_event() may be safely used right after input device was
 * allocated with input_allocate_device(), even before it is registered
 * with input_register_device(), but the event will not reach any of the
 * input handlers. Such early invocation of input_event() may be used
 * to 'seed' initial state of a switch or initial position of absolute
 * axis, etc.
 */
void input_event(struct input_dev *dev,
                 unsigned int type, unsigned int code, int value)
{
        unsigned long flags;

        if (is_event_supported(type, dev->evbit, EV_MAX)) {

                spin_lock_irqsave(&dev->event_lock, flags);
                input_handle_event(dev, type, code, value);
                spin_unlock_irqrestore(&dev->event_lock, flags);
        }
}
EXPORT_SYMBOL(input_event);
👇
#define INPUT_IGNORE_EVENT        0        //忽略
#define INPUT_PASS_TO_HANDLERS        1    //交给handler处理
#define INPUT_PASS_TO_DEVICE        2      //交给device处理
#define INPUT_SLOT                4        //需要刷新挂起的slot事件
#define INPUT_FLUSH                8       //需要handler立即处理 只有SYN_REPORT有
#define INPUT_PASS_TO_ALL        (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static void input_handle_event(struct input_dev *dev,
                               unsigned int type, unsigned int code, int value)
{
        /* 获取disposition,根据disposition来判断event的传递方向 */
        int disposition = input_get_disposition(dev, type, code, &value);

        ...
}

3.4.2.2 dispositon分析

Disposition 可以理解为事件的性质,该标志决定了事件的流向

input_sync dispositon分析
👇
disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
input_report_abs dispositon分析
👇
if (is_event_supported(code, dev->absbit, ABS_MAX))
                        disposition = input_handle_abs_event(dev, code, &value);
👇
static int input_handle_abs_event(struct input_dev *dev,
                                  unsigned int code, int *pval)
{
        struct input_mt *mt = dev->mt;
        bool is_mt_event;
        int *pold;

        /* 如果是ABS_MT_SLOT则ingnor,在真实touch数据上报时一并上报 */
        if (code == ABS_MT_SLOT) {dev->mt
                /*
                 * "Stage" the event; we'll flush it later, when we
                 * get actual touch data.
                 */
                 
                 /* 
                  *  mt和 slot value 都存在,且slot value < 设备最大支持slot数
                  *  设定mt->slot = slot value,即设定当前传输的slot value
                  */
                if (mt && *pval >= 0 && *pval < mt->num_slots)
                        mt->slot = *pval;

                return INPUT_IGNORE_EVENT;
        }

        /* 
         * code为除 ABS_MT_SLOT 以外的其他ABS_MT事件则置1
         * 这里是为了过滤非MT事件 
         */
        is_mt_event = input_is_mt_value(code);

        if (!is_mt_event) {
                /* 
                 * 是ABS_MT_SLOT或其他ABS类型事件
                 * 则 pold 指向event的absinfo value
                 */
                pold = &dev->absinfo[code].value;
        } else if (mt) {
                /* 
                 * 是非ABS_MT_SLOT的其他ABS_MT事件
                 * 则pold 指向第slot value个slot的事件value
                 * .abs[]:此int数组存放ABS_MT事件的value
                 */
                pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
        } else {
                /*
                 * Bypass filtering for multi-touch events when
                 * not employing slots.
                 * 不使用slot时绕过多点触摸事件的过滤。
                 */
                pold = NULL;
        }

        if (pold) {
                /* 根据设定噪声值结合*pold调整*pval */
                *pval = input_defuzz_abs_event(*pval, *pold,
                                                dev->absinfo[code].fuzz);
                /* 实现重复键值不报功能 */
                if (*pold == *pval)
                        return INPUT_IGNORE_EVENT;

                *pold = *pval;
        }

        /* Flush pending "slot" event */
        /* 
         * code是ABS_MT事件 且 dev使用mt 且 dev当前传输的slot value不等于absinfo中保存的上一次slot value
         * 则将dev当前传输的slot value存入absinfo
         *
         */
        if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
                input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
                return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
        }

        return INPUT_PASS_TO_HANDLERS;
}

tips:关于input_abs_set_xxx和input_abs_get_xxx
在input.h中使用宏定义了这一系列函数,作用可以概括为从input_dev->absinfo中设定或者获取数据

#define INPUT_GENERATE_ABS_ACCESSORS(_suffix, _item)                        \
static inline int input_abs_get_##_suffix(struct input_dev *dev,        \
                                          unsigned int axis)                \
{                                                                        \
        return dev->absinfo ? dev->absinfo[axis]._item : 0;                \
}                                                                        \
                                                                        \
static inline void input_abs_set_##_suffix(struct input_dev *dev,        \
                                           unsigned int axis, int val)        \
{                                                                        \
        input_alloc_absinfo(dev);                                        \
        if (dev->absinfo)                                                \
                dev->absinfo[axis]._item = val;                                \
}

INPUT_GENERATE_ABS_ACCESSORS(val, value)
INPUT_GENERATE_ABS_ACCESSORS(min, minimum)
INPUT_GENERATE_ABS_ACCESSORS(max, maximum)
INPUT_GENERATE_ABS_ACCESSORS(fuzz, fuzz)
INPUT_GENERATE_ABS_ACCESSORS(flat, flat)
INPUT_GENERATE_ABS_ACCESSORS(res, resolution)

3.4.2.3 接着分析input_handle_event


#define INPUT_IGNORE_EVENT        0        //忽略
#define INPUT_PASS_TO_HANDLERS        1    //交给handler处理
#define INPUT_PASS_TO_DEVICE        2      //交给device处理
#define INPUT_SLOT                4        //需要刷新挂起的slot事件
#define INPUT_FLUSH                8       //需要handler立即处理 只有SYN_REPORT有
#define INPUT_PASS_TO_ALL        (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static void input_handle_event(struct input_dev *dev,
                               unsigned int type, unsigned int code, int value)
{
        /* 获取disposition,根据disposition来判断event的传递方向 */
        int disposition = input_get_disposition(dev, type, code, &value);

        /* add_input_randomness对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,所以对熵池是有贡献的 */
        if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
                add_input_randomness(type, code, value);

        /* 如果是向节点注入事件,则调用dev的event函数指针 */
        if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
                dev->event(dev, type, code, value);

        /* 如果dev的input_values为0则直接返回 */
        if (!dev->vals)
                return;
        /* 交给handler处理 */
        if (disposition & INPUT_PASS_TO_HANDLERS) {
                struct input_value *v;
                /* 需要刷新挂起的slot事件 */
                if (disposition & INPUT_SLOT) {
                        v = &dev->vals[dev->num_vals++];
                        v->type = EV_ABS;
                        v->code = ABS_MT_SLOT;
                        v->value = dev->mt->slot;//slot value
                }

                v = &dev->vals[dev->num_vals++];
                v->type = type;
                v->code = code;
                v->value = value;
        }

        /* EV_SYN SYN_REPORT  事件触发INPUT_FLUSH */
        if (disposition & INPUT_FLUSH) {
                /* 如果需要上报的input_values数量大于等于2,则input_pass_values */
                if (dev->num_vals >= 2)
                        input_pass_values(dev, dev->vals, dev->num_vals);
                /* 清空 input_dev->vals 计数变量 */
                dev->num_vals = 0;
                /*
                 * Reset the timestamp on flush so we won't end up
                 * with a stale one. Note we only need to reset the
                 * monolithic one as we use its presence when deciding
                 * whether to generate a synthetic timestamp.
                 */
                dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0);
        } else if (dev->num_vals >= dev->max_vals - 2) {
        /* 
         * 如果需要上报的input_values数量大于等于 max_vals - 2 
         * 则将EV_SYN SYN_REPORT 1 塞入input_values 并立即执行 input_pass_values
         */
                dev->vals[dev->num_vals++] = input_value_sync;
                input_pass_values(dev, dev->vals, dev->num_vals);
                dev->num_vals = 0;
        }

}

3.4.2.4 分析 input_pass_values

/*
 * Pass values first through all filters and then, if event has not been
 * filtered out, through all open handles. This function is called with
 * dev->event_lock held and interrupts disabled.
 */
static void input_pass_values(struct input_dev *dev,
                              struct input_value *vals, unsigned int count)
{
        struct input_handle *handle;
        struct input_value *v;

        if (!count)
                return;

        rcu_read_lock();

        /* 
         * 检查grab是否存在
         * grab:当前已抓取设备的输入句柄(通过 EVIOCGRAB ioctl)input_grab_device
         *       当句柄抓住设备时,它成为来自设备的所有输入事件的唯一接收者
         */
        handle = rcu_dereference(dev->grab);
        if (handle) {
                count = input_to_handler(handle, vals, count);
        } else {
                /* 
                 * 根据input_dev.h_list上存放的input_handle.d_node地址
                 * 从而遍历input_handle
                 */
                list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                        if (handle->open) {
                        /* 如果使用handle的client不为0 */
                                count = input_to_handler(handle, vals, count);
                                if (!count)
                                        break;
                        }
        }

        rcu_read_unlock();

        /* trigger auto repeat for key events */
        /* 分析见Softrepeat 事件重复上报机制 */
        if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
                for (v = vals; v != vals + count; v++) {
                        if (v->type == EV_KEY && v->value != 2) {
                                if (v->value)
                                        input_start_autorepeat(dev, v->code);
                                else
                                        input_stop_autorepeat(dev);
                        }
                }
        }
}

3.4.2.5 分析 input_to_handler

/*
 * Pass event first through all filters and then, if event has not been
 * filtered out, through all open handles. This function is called with
 * dev->event_lock held and interrupts disabled.
 */
static unsigned int input_to_handler(struct input_handle *handle,
                        struct input_value *vals, unsigned int count)
{
        struct input_handler *handler = handle->handler;
        struct input_value *end = vals;
        struct input_value *v;

        /* handler如果有filter则优先从filter传递事件 */
        if (handler->filter) {
                for (v = vals; v != vals + count; v++) {
                        if (handler->filter(handle, v->type, v->code, v->value))
                                continue;
                        if (end != v)
                                *end = *v;
                        end++;
                }
                count = end - vals;
        }

        if (!count)
                return 0;

        /* handler如果有events 从events直接传输count个input_value */
        if (handler->events)
                handler->events(handle, vals, count);
        /* 否则遍历传输每个input_value */
        else if (handler->event)
                for (v = vals; v != vals + count; v++)
                        handler->event(handle, v->type, v->code, v->value);

        return count;
}

3.4.2.6 分析 evdev_event(s)

/*
 * Pass incoming event to all connected clients.
 */
static void evdev_event(struct input_handle *handle,
                        unsigned int type, unsigned int code, int value)
{
        struct input_value vals[] = { { type, code, value } };

        evdev_events(handle, vals, 1);
}

这里主要分析evdev_events


/*
 * Pass incoming events to all connected clients.
 */
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 *ev_time = input_get_timestamp(handle->dev);

        rcu_read_lock();

        /* 
         * 检查是否有client grab 此handler
         */
        client = rcu_dereference(evdev->grab);

        /* 如果有client grab 此handler */
        if (client)
                /* 发送input事件数据到此client */
                evdev_pass_values(client, vals, count, ev_time);
        else
                /* 发送input事件数据到所有client */
                list_for_each_entry_rcu(client, &evdev->client_list, node)
                        evdev_pass_values(client, vals, count, ev_time);

        rcu_read_unlock();
}

3.4.2.7 分析 evdev_pass_values

static void evdev_pass_values(struct evdev_client *client,
                        const struct input_value *vals, unsigned int count,
                        ktime_t *ev_time)
{
        const struct input_value *v;
        struct input_event event;
        struct timespec64 ts;
        bool wakeup = false;

        /* 如果client被注销则直接返回 */
        if (client->revoked)
                return;

        ts = ktime_to_timespec64(ev_time[client->clk_type]);
        event.input_event_sec = ts.tv_sec;
        event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;

        /* Interrupts are disabled, just acquire the lock. */
        spin_lock(&client->buffer_lock);

        for (v = vals; v != vals + count; v++) {
                /* 过滤事件,事件类型通过 EVIOCSMASK ioctl来设定 */
                if (__evdev_is_filtered(client, v->type, v->code))
                        continue;

                if (v->type == EV_SYN && v->code == SYN_REPORT) {
                        /* drop empty SYN_REPORT */
                        /*  
                         * 事件为EV_SYN SYN_REPORT 且 buffer中为空
                         * 则跳出上报
                         */
                        if (client->packet_head == client->head)
                                continue;
                        /* 只有事件为EV_SYN SYN_REPORT 才需要唤醒client */
                        wakeup = true;
                }

                /* 向buffer中插入input事件数据 */
                event.type = v->type;
                event.code = v->code;
                event.value = v->value;
                __pass_event(client, &event);
        }

        spin_unlock(&client->buffer_lock);

        /* 当SYN_REPORT事件插入buffer后就可以唤醒client了 */
        if (wakeup)
                /* 唤醒在poll等待队列的进程 */
                wake_up_interruptible_poll(&client->wait,
                        EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
}

3.4.2.8 环形缓冲区

在分析__pass_event函数之前,需要知道client的buffer是如何运作的
在事件处理层(evdev.c)中结构体evdev_client定义了一个环形缓冲区(circular buffer),其原理是用数组的方式实现了一个先进先出的循环队列(circular queue),用以缓存内核驱动上报给用户层的input_event事件

struct evdev_client {
        unsigned int head;              // 头指针
        unsigned int tail;              // 尾指针
        unsigned int packet_head;       // 包指针
        spinlock_t buffer_lock;         /* protects access to buffer, head and tail */
        wait_queue_head_t wait;
        struct fasync_struct *fasync;
        struct evdev *evdev;
        struct list_head node;
        enum input_clock_type clk_type;
        bool revoked;                   
        unsigned long *evmasks[EV_CNT];
        unsigned int bufsize;           // 循环队列大小,此size值向上对齐2的幂
        struct input_event buffer[];    // 循环队列数组
};

evdev_client对象维护了三个偏移量:head、tail以及packet_head。head、tail作为循环队列的头尾指针记录入口与出口偏移,那么包指针packet_head有什么作用呢?
packet_head:内核驱动处理一次输入,可能上报一到多个input_event事件,为表示处理完成,会在上报这些input_event事件后再上报一次同步事件。头指针head以input_event事件为单位,记录缓冲区的入口偏移量,而包指针packet_head则以“数据包”(一到多个input_event事件)为单位,记录缓冲区的入口偏移量。

在这里插入图片描述

  • 循环队列入队算法:
head++;
head &= bufsize - 1;
  • 循环队列出队算法:
tail++;
tail &= bufsize - 1;
  • 循环队列已满条件:
head == tail
  • 循环队列为空条件:
packet_head == tail
  • “求余”和“求与”
    为解决头尾指针的上溢和下溢现象,使队列的元素空间可重复使用,一般循环队列的出入队算法都采用“求余”操作:
head = (head + 1) % bufsize; // 入队
tail = (tail + 1) % bufsize; // 出队

为避免计算代价高昂的“求余”操作,使内核运作更高效,input子系统的环形缓冲区采用了“求与”算法,这要求bufsize必须为2的幂,在后文中可以看到bufsize的值实际上是为64或者8的n倍,符合“求与”运算的要求。

3.4.2.9 分析__pass_event

static void __pass_event(struct evdev_client *client,
                         const struct input_event *event)
{
        /* input event 数据入队列 */
        client->buffer[client->head++] = *event;
        client->head &= client->bufsize - 1;

        /* 
         * 如果缓冲区已满
         * 则丢弃缓冲区中的事件并向缓冲区中塞入SYN_DROPPED事件
         */
        if (unlikely(client->head == client->tail)) {
                /*
                 * This effectively "drops" all unconsumed events, leaving
                 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
                 */
                /* 只留下本次插入的input事件和SYN_DROPPED事件在缓冲区中 */
                client->tail = (client->head - 2) & (client->bufsize - 1);

                client->buffer[client->tail] = (struct input_event) {
                        .input_event_sec = event->input_event_sec,
                        .input_event_usec = event->input_event_usec,
                        .type = EV_SYN,
                        .code = SYN_DROPPED,
                        .value = 0,
                };

                client->packet_head = client->tail;
        }

        /* 如果是SYN_REPORT事件 */
        if (event->type == EV_SYN && event->code == SYN_REPORT) {
                /* 刷新包指针 */
                client->packet_head = client->head;
                /* 发送SIGIO和POLL_IN信号给client线程 */
                kill_fasync(&client->fasync, SIGIO, POLL_IN);
        }
}

4. 应用打开节点并注入事件数据

在这里,应用作为input事件的生产者,此时根据事件的不同input事件的消费者可以是应用也可以是device
前面在evdev_write简单写了,现在详细写下
应用注入事件后,事件的流转有两个方向

  • 流向input_handler即向所有client上报注入的事件
  • 流向input_device即回调device的event函数

4.1 事件注入

书接上回evdev_write

static ssize_t evdev_write(struct file *file, const 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;
        int retval = 0;//从用户空间读取字节数

        /* 用户写字节数 < event 字节数 返回inval */
        if (count != 0 && count < input_event_size())
                return -EINVAL;

        retval = mutex_lock_interruptible(&evdev->mutex);
        if (retval)
                return retval;

        /* evdev如果不存在和client被注销返回 nodev */
        if (!evdev->exist || client->revoked) {
                retval = -ENODEV;
                goto out;
        }

        /* 
         * 如从用户空间读取字节数+一个event字节数 <= 用户写入的字节数
         * 则按event注入节点
         */
        while (retval + input_event_size() <= count) {

                if (input_event_from_user(buffer + retval, &event)) {
                        retval = -EFAULT;
                        goto out;
                }
                retval += input_event_size();

                input_inject_event(&evdev->handle,
                                   event.type, event.code, event.value);
                cond_resched();//调度
        }

 out:
        mutex_unlock(&evdev->mutex);
        return retval;
}

input_inject_event是handler注入事件的入口

/**
 * input_inject_event() - send input event from input handler
 * @handle: input handle to send event through
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * Similar to input_event() but will ignore event if device is
 * "grabbed" and handle injecting event is not the one that owns
 * the device.
 */
void input_inject_event(struct input_handle *handle,
                        unsigned int type, unsigned int code, int value)
{
        struct input_dev *dev = handle->dev;
        struct input_handle *grab;
        unsigned long flags;

        /* 如果事件合法且device支持此事件 */
        if (is_event_supported(type, dev->evbit, EV_MAX)) {
                spin_lock_irqsave(&dev->event_lock, flags);

                rcu_read_lock();
                /* 此device是否有handle grab */
                grab = rcu_dereference(dev->grab);
                /* 没有handle grab device 或者 此handle grab device */
                if (!grab || grab == handle)
                        /* 开始事件流转 */
                        input_handle_event(dev, type, code, value);
                rcu_read_unlock();

                spin_unlock_irqrestore(&dev->event_lock, flags);
        }
}
EXPORT_SYMBOL(input_inject_event);

4.2 事件流向input_handler

流向input_handler的事件需要disposition满足下列条件指一

  • INPUT_PASS_TO_HANDLERS
  • INPUT_PASS_TO_ALL
    所有设备支持的事件都会流向input_handler,事件的后续流向见上面 《3.4.2.2 接着分析input_handle_event》章节

4.3 事件流向input_device

流向input_handler的事件需要disposition满足下列条件指一

  • INPUT_PASS_TO_DEVICE
  • INPUT_PASS_TO_ALL
    只有下面列举的事件会流向input_device

在这里插入图片描述
如果input_device 定义了event函数则会回调此函数


#define INPUT_IGNORE_EVENT        0        //忽略
#define INPUT_PASS_TO_HANDLERS        1    //交给handler处理
#define INPUT_PASS_TO_DEVICE        2      //交给device处理
#define INPUT_SLOT                4        //需要刷新挂起的slot事件
#define INPUT_FLUSH                8       //需要handler立即处理 只有SYN_REPORT有
#define INPUT_PASS_TO_ALL        (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static void input_handle_event(struct input_dev *dev,
                               unsigned int type, unsigned int code, int value)
{
        /* 获取disposition,根据disposition来判断event的传递方向 */
        int disposition = input_get_disposition(dev, type, code, &value);
        ...
        /* 如果是向节点注入事件,则调用dev的event函数指针 */
        if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
                dev->event(dev, type, code, value);
        ...
}

5. input.c

5.1 核心数据结构

5.1.1 input_dev

struct input_dev {
        const char *name;  // 输入设备私有指针,一般指向用于描述设备驱动层的设备结构
        const char *phys;  // 提供给用户的输入设备的名称
        const char *uniq;  // 提供给编程者的设备节点的名称  文件路径,比如 input/buttons
        struct input_id id;// 指定唯一的ID号,就像MAC地址一样

        unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];//位图,记录设备支持的事件类型(可以多选)

        /* 
         *  #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 
         *  #define EV_REP          0x14<span style="white-space:pre">    </span>//重复上报 
         *  #define EV_FF           0x15 
         *  #define EV_PWR          0x16 
         *  #define EV_FF_STATUS    0x17 
         *  #define EV_MAX          0x1f 
         */ 

        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;           //用来估算evdev buffersize

        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_mt {
         *       int trkid;
         *       int num_slots;
         *       int slot;
         *       unsigned int flags;
         *       unsigned int frame;
         *       int *red;
         *       struct input_mt_slot slots[];
         *   };
         *
         *   struct input_mt_slot {
         *       int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
         *       unsigned int frame;
         *       unsigned int key;
         *   };
         *
         */

        struct input_absinfo *absinfo;                   //&struct input_absinfo 元素数组,其中包含有关绝对轴的信息(当前值、最小值、最大值、平坦度、模糊度、分辨率)
        /* 
         *    struct input_absinfo {
         *       __s32 value;
         *       __s32 minimum;
         *       __s32 maximum;
         *       __s32 fuzz;
         *       __s32 flat;
         *       __s32 resolution;
         *   };
         */

        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); //事件处理  

        struct input_handle __rcu *grab;    //当前已抓取设备的输入句柄(通过 EVIOCGRAB ioctl)。 当句柄抓住设备时,它成为来自设备的所有输入事件的唯一接收者

        spinlock_t event_lock; //当输入核心接收并处理设备的新事件(在 input_event() 中)时,将采用此自旋锁。在设备注册到 input core 之后访问和/或修改设备参数(例如 keymap 或 absmin、absmax、absfuzz 等)的代码必须使用此锁。
        struct mutex mutex;    //用于open、close函数的连续访问互斥  

        unsigned int users;    //存储打开此设备的用户(input handler)的数量。 input_open_device() 和 input_close_device() 使用它来确保 dev->open() 仅在第一个用户打开设备时调用,而 dev->close() 在最后一个用户关闭设备时调用
        bool going_away;       //标记处于注销过程中的设备并导致 input_open_device*() 失败并显示 -ENODEV。

        struct device dev;     //此设备的驱动程序模型视图

        struct list_head        h_list; //与设备关联的输入句柄列表。 访问列表时,必须保持 dev->mutex
        struct list_head        node;   //用于将设备放入 input_dev_list

        unsigned int num_vals;   //当前帧中插入value的数量
        unsigned int max_vals;   //当前帧中插入value的最大数量
        struct input_value *vals;//当前帧中插入的value数组

        bool devres_managed;    //表示设备由 devres 框架管理,不需要显式注销或释放。

        ktime_t timestamp[INPUT_CLK_MAX]; //存储由驱动程序调用的 input_set_timestamp 设置的时间戳
};

5.1.2 input_handler

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);
        //在将设备的 id 与处理程序的 id_table 进行比较后调用,以在设备和处理程序之间进行细粒度匹配
        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);
        //启动给定句柄的处理程序。这个函数由input core 在 connect() 方法之后调用,并且当一个进程* “抓取”一个设备释放它
        void (*start)(struct input_handle *handle);

        bool legacy_minors;        //使用传统次要范围的驱动程序设置为 %true 
        int minor;
        const char *name;        //处理程序的名称,显示在 /proc/bus/input/handlers 

        const struct input_device_id *id_table;    //驱动支持的id表(用于匹配input_dev)

        struct list_head        h_list;            //存放input_handle(没有r)的链表
        struct list_head        node;              //存放input_handler自身的链表
};

5.1.3 input_handle

struct input_handle {

        void *private;                        // 私有数据, 指向了父指针

        int open;                             //计数器显示句柄是否“打开”,即应该从其设备传递事件.
        const char *name;                     //给定的名称到创建它的处理程序的句柄

        struct input_dev *dev;                //指向input_dev
        struct input_handler *handler;        //指向 input_handler

        struct list_head        d_node;       //存放input_dev->h_list的链表
        struct list_head        h_node;       //存放input_handler->h_list的链表
};

5.1.4 ff_device

struct ff_device {        //输入设备的强制反馈部分
        //调用以将新效果上传到设备
        int (*upload)(struct input_dev *dev, struct ff_effect *effect,
                      struct ff_effect *old);
        //调用以从设备中删除效果
        int (*erase)(struct input_dev *dev, int effect_id);
        //调用以请求设备开始播放指定效果
        int (*playback)(struct input_dev *dev, int effect_id, int value);
        //调用设置指定增益
        void (*set_gain)(struct input_dev *dev, u16 gain);
        //调用自动居中设备
        void (*set_autocenter)(struct input_dev *dev, u16 magnitude);
        //当父输入设备被销毁时由输入核心调用
        void (*destroy)(struct ff_device *);
        //驱动程序特定的数据,将自动释放
        void *private;
        //设备真正支持的力反馈功能位图(不像 input_dev->ffbit 中的那样模拟)
        unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
        //用于序列化访问设备的互斥锁
        struct mutex mutex;
        //设备支持的最大效果数
        int max_effects;
        //指向当前加载到设备中的效果数组的指针
        struct ff_effect *effects;
        //效果所有者数组;当文件句柄拥有效果关闭时,效果会自动删除
        struct file *effect_owners[];

        /* 每个强制反馈设备都必须实现upload()playback()
        * 方法;erase()是可选的。set_gain() 和 set_autocenter() 
        * 只有在驱动程序设置 FF_GAIN 和 FF_AUTOCENTER 
        * 位时才需要实现。
        * 
        * 请注意,在调用 play()、set_gain() 和 set_autocenter() 时
        * dev->event_lock 自旋锁保持并中断,因此可能不会睡眠。
        */
};

5.1.5 input_device_id

struct input_device_id {

        kernel_ulong_t flags;

        /* 这些ID需要驱动程序作者指定 */
        __u16 bustype;
        __u16 vendor;
        __u16 product;
        __u16 version;

        kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1];

        kernel_ulong_t driver_info;
};

5.1.6 ops

static const struct proc_ops input_handlers_proc_ops = {
        .proc_open        = input_proc_handlers_open,
        .proc_read        = seq_read,
        .proc_lseek        = seq_lseek,
        .proc_release        = seq_release,
};

static const struct seq_operations input_devices_seq_ops = {
        .start        = input_devices_seq_start,
        .next        = input_devices_seq_next,
        .stop        = input_seq_stop,
        .show        = input_devices_seq_show,
};

static const struct proc_ops input_devices_proc_ops = {
        .proc_open        = input_proc_devices_open,
        .proc_poll        = input_proc_devices_poll,
        .proc_read        = seq_read,
        .proc_lseek        = seq_lseek,
        .proc_release        = seq_release,
};

static const struct seq_operations input_handlers_seq_ops = {
        .start        = input_handlers_seq_start,
        .next        = input_handlers_seq_next,
        .stop        = input_seq_stop,
        .show        = input_handlers_seq_show,
};

static const struct dev_pm_ops input_dev_pm_ops = {
        .suspend        = input_dev_suspend,
        .resume                = input_dev_resume,
        .freeze                = input_dev_freeze,
        .poweroff        = input_dev_poweroff,
        .restore        = input_dev_resume,
};

static const struct device_type input_dev_type = {
        .groups                = input_dev_attr_groups,
        .release        = input_dev_release,
        .uevent                = input_dev_uevent,
#ifdef CONFIG_PM_SLEEP
        .pm                = &input_dev_pm_ops,
#endif
};

5.2 功能细节

5.2.1 Init 和 exit

static int __init input_init(void)
{
        int err;

        err = class_register(&input_class); // create /dev/input
        if (err) {
                pr_err("unable to register input_dev class\n");
                return err;
        }

        err = input_proc_init(); //create /proc/bus/input/device + handler
        if (err)
                goto fail1;

        err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                                     INPUT_MAX_CHAR_DEVICES, "input");
        if (err) {
                pr_err("unable to register char major %d", INPUT_MAJOR);
                goto fail2;
        }

        return 0;

 fail2:        input_proc_exit();
 fail1:        class_unregister(&input_class);
        return err;
}
static void __exit input_exit(void)
{
        input_proc_exit();
        unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                                 INPUT_MAX_CHAR_DEVICES);
        class_unregister(&input_class);
}

5.2.2 softrepeat 事件重复上报机制

如果 设置了 __set_bit(EV_REP, input->evbit); 也就是重复报告,它的工作机制是这样的:

如果按键报告了input_event(input, type, button->code, 1); 之后, 
在250ms (可以改)后,依然没有报告 input_event(input, type, button->code, 0);
则 input 会每隔 33ms 继续报告一次 input_event(input, type, button->code, 2);
直到 报告了 input_event(input, type, button->code, 0); 才停止 ,
这就是我们按住一个按键不松开时,会一直打印键值的原因; 

使用一个timer实现事件重复上报机制

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
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);//timer在这里setup
                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);


这里是入口
👇
int input_register_device(struct input_dev *dev)
{
        /*
         * If delay and period are pre-set by the driver, then autorepeating
         * is handled by the driver itself and we don't do it in input.c.
         */
        if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])//如果用户设定时间参数input.c不会执行重复上报,需要用户自己实现
                input_enable_softrepeat(dev, 250, 33);
}
/**
 * input_enable_softrepeat - enable software autorepeat
 * @dev: input device
 * @delay: repeat delay
 * @period: repeat period
 *
 * Enable software autorepeat on the input device.
 */
void input_enable_softrepeat(struct input_dev *dev, int delay, int period)
{
        dev->timer.function = input_repeat_key;//Softrepeat 函数 
        dev->rep[REP_DELAY] = delay;//第一次上报timer时间
        dev->rep[REP_PERIOD] = period;//自动上报timer时间
}
EXPORT_SYMBOL(input_enable_softrepeat);
👇
static void input_repeat_key(struct timer_list *t)
{
        struct input_dev *dev = from_timer(dev, t, timer);
        unsigned long flags;

        spin_lock_irqsave(&dev->event_lock, flags);

        if (test_bit(dev->repeat_key, dev->key) &&
            is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
                struct input_value vals[] =  {
                        { EV_KEY, dev->repeat_key, 2 },//type code value
                        input_value_sync
                };

                input_set_timestamp(dev, ktime_get());
                input_pass_values(dev, vals, ARRAY_SIZE(vals));

                if (dev->rep[REP_PERIOD])//设定timer,下次上报
                        mod_timer(&dev->timer, jiffies +
                                        msecs_to_jiffies(dev->rep[REP_PERIOD]));
        }

        spin_unlock_irqrestore(&dev->event_lock, flags);
}
第一次上报在什么时候?
👇
/*
 * Pass values first through all filters and then, if event has not been
 * filtered out, through all open handles. This function is called with
 * dev->event_lock held and interrupts disabled.
 */
static void input_pass_values(struct input_dev *dev,
                              struct input_value *vals, unsigned int count)
{
        struct input_handle *handle;
        struct input_value *v;
......
        /* trigger auto repeat for key events */
        if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
                for (v = vals; v != vals + count; v++) {//遍历input_value
                        if (v->type == EV_KEY && v->value != 2) {//上报1后
                                if (v->value)//没有上报0
                                        input_start_autorepeat(dev, v->code);
                                else//上报0了
                                        input_stop_autorepeat(dev);
                        }
                }
        }
}
设定dev->rep[REP_DELAY]ms后第一次上报
👇
static void input_start_autorepeat(struct input_dev *dev, int code)
{
        if (test_bit(EV_REP, dev->evbit) &&
            dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
            dev->timer.function) {
                dev->repeat_key = code;
                mod_timer(&dev->timer,
                          jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
        }
}
后面在按周期循环执行
👇
static void input_repeat_key(struct timer_list *t)
直到上报0了,执行
👇
static void input_stop_autorepeat(struct input_dev *dev)
{
        del_timer(&dev->timer);
}

6. input-mt.c

首先需要了解多点触控协议 https://tinylab-1.gitbook.io/linux-doc/zh-cn/input/multi-touch-protocol

6.1 数据结构

6.1.1 input_mt_slot

mt设备slot数据

#define ABS_MT_SLOT                0x2f        /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR        0x30        /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR        0x31        /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR        0x32        /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR        0x33        /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION        0x34        /* Ellipse orientation */
#define ABS_MT_POSITION_X        0x35        /* Center X touch position */
#define ABS_MT_POSITION_Y        0x36        /* Center Y touch position */
#define ABS_MT_TOOL_TYPE        0x37        /* Type of touching device */
#define ABS_MT_BLOB_ID                0x38        /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID        0x39        /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE                0x3a        /* Pressure on contact area */
#define ABS_MT_DISTANCE                0x3b        /* Contact hover distance */
#define ABS_MT_TOOL_X                0x3c        /* Center X tool position */
#define ABS_MT_TOOL_Y                0x3d        /* Center Y tool position */

#define ABS_MT_FIRST                ABS_MT_TOUCH_MAJOR
#define ABS_MT_LAST                ABS_MT_TOOL_Y
/**
 * struct input_mt_slot - represents the state of an input MT slot
 * @abs: holds current values of ABS_MT axes for this slot
 * @frame: last frame at which input_mt_report_slot_state() was called
 * @key: optional driver designation of this slot
 */
struct input_mt_slot {
        int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];//存放ABS_MT事件的value
        unsigned int frame;                     //该slot的frame序号
        unsigned int key;                       //通过此key驱动可以调用接口查询匹配的slot, dev->mt->slot->key
};

6.1.2 input_mt

相当于input设备的mt私有数据

#define INPUT_MT_POINTER               0x0001        /* pointer device, e.g. trackpad */
#define INPUT_MT_DIRECT                0x0002        /* direct device, e.g. touchscreen */
#define INPUT_MT_DROP_UNUSED           0x0004        /* drop contacts not seen in frame */
#define INPUT_MT_TRACK                 0x0008        /* use in-kernel tracking */
#define INPUT_MT_SEMI_MT               0x0010        /* semi-mt device, finger count handled manually */

/**
 * struct input_mt - state of tracked contacts
 * @trkid: stores MT tracking ID for the next contact
 * @num_slots: number of MT slots the device uses
 * @slot: MT slot currently being transmitted
 * @flags: input_mt operation flags
 * @frame: increases every time input_mt_sync_frame() is called
 * @red: reduced cost matrix for in-kernel tracking
 * @slots: array of slots holding current values of tracked contacts
 */
struct input_mt {
        int trkid;            //跟踪多点触控使用的ID,每一次触控追踪都会分配一个新的ID
        int num_slots;        //设备使用的slot数量
        int slot;             //设备当前传输使用的slot
        unsigned int flags;   //见上述宏
        unsigned int frame;   //每次调用 input_mt_sync_frame() 时增加
        int *red;             //减少内核跟踪的成本矩阵
        struct input_mt_slot slots[];//存放ABS_MT事件数据
};

6.2 init

//e.g. init
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->propbit[0] = BIT(INPUT_PROP_DIRECT);
input_mt_init_slots(ts->input_dev, 10, 0);

6.2.1 input_mt_init_slots


/**
 * input_mt_init_slots() - initialize MT input slots
 * @dev: input device supporting MT events and finger tracking
 * @num_slots: number of slots used by the device
 * @flags: mt tasks to handle in core
 *
 * This function allocates all necessary memory for MT slot handling
 * in the input device, prepares the ABS_MT_SLOT and
 * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers.
 * Depending on the flags set, it also performs pointer emulation and
 * frame synchronization.
 *
 * May be called repeatedly. Returns -EINVAL if attempting to
 * reinitialize with a different number of slots.
 */
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
                        unsigned int flags)
{
        struct input_mt *mt = dev->mt;
        int i;

        /* 如果num_slots == 0 直接返回 */
        if (!num_slots)
                return 0;
        /* 如果尝试使用不同数量的slot重新初始化,则返回 -EINVAL。 */
        if (mt)
                return mt->num_slots != num_slots ? -EINVAL : 0;

        /* 依据mt->slots[num_slots]申请该结构体变量所需的内存大小 */
        mt = kzalloc(struct_size(mt, slots, num_slots), GFP_KERNEL);
        if (!mt)
                goto err_mem;
        /* 存储num_slots和flags到input_mt */
        mt->num_slots = num_slots;
        mt->flags = flags;
        /* 设定input_dev->absinfo input_dev->absbit input_dev->evbit */
        input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
        input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);

        /* 根据flags设定设备属性,参数及内核跟踪功能 */
        if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
                __set_bit(EV_KEY, dev->evbit);
                __set_bit(BTN_TOUCH, dev->keybit);

                copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
                copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
                copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
        }
        if (flags & INPUT_MT_POINTER) {
                __set_bit(BTN_TOOL_FINGER, dev->keybit);
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
                if (num_slots >= 3)
                        __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
                if (num_slots >= 4)
                        __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
                if (num_slots >= 5)
                        __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
                __set_bit(INPUT_PROP_POINTER, dev->propbit);
        }
        if (flags & INPUT_MT_DIRECT)
                __set_bit(INPUT_PROP_DIRECT, dev->propbit);
        if (flags & INPUT_MT_SEMI_MT)
                __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
        if (flags & INPUT_MT_TRACK) {
                unsigned int n2 = num_slots * num_slots;
                mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
                if (!mt->red)
                        goto err_mem;
        }

        /* Mark slots as 'inactive' */
        for (i = 0; i < num_slots; i++)
                input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);

        /* Mark slots as 'unused' */
        mt->frame = 1;

        dev->mt = mt;
        return 0;
err_mem:
        kfree(mt);
        return -ENOMEM;
}
EXPORT_SYMBOL(input_mt_init_slots);

6.3 TypeB report 实例


/******************** report ********************/
input_mt_slot(ts->input_dev, input_id - 1);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p);
...
input_sync(ts->input_dev);

/******************** release ********************/
/* 遍历slot寻找到release的point后执行release动作 */
for (i = 0; i < ts->max_touch_num; i++) {
    if (press_id[i] != 1) {
        input_mt_slot(ts->input_dev, i);
        input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
        input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
        input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
    }
}
...
input_sync(ts->input_dev);

/******************** event e.g. ********************/
/dev/input/event10: EV_ABS       ABS_MT_TRACKING_ID   0000005d            
/dev/input/event10: EV_ABS       ABS_MT_POSITION_X    000017a2            
/dev/input/event10: EV_ABS       ABS_MT_POSITION_Y    00005122            
/dev/input/event10: EV_ABS       ABS_MT_TOUCH_MAJOR   00000006            
/dev/input/event10: EV_ABS       ABS_MT_PRESSURE      00000001            
/dev/input/event10: EV_KEY       BTN_TOUCH            DOWN                
/dev/input/event10: EV_SYN       SYN_REPORT           00000000            
/dev/input/event10: EV_ABS       ABS_MT_TOUCH_MAJOR   00000000            
/dev/input/event10: EV_ABS       ABS_MT_PRESSURE      00000000            
/dev/input/event10: EV_ABS       ABS_MT_TRACKING_ID   ffffffff            
/dev/input/event10: EV_KEY       BTN_TOUCH            UP                  
/dev/input/event10: EV_SYN       SYN_REPORT           00000000 

6.3.1 input_mt_slot

input_mt_slot(ts->input_dev, input_id - 1);
static inline void input_mt_slot(struct input_dev *dev, int slot)
{
        input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}
|->input_mt_slot
    |->input_event
        |->input_handle_event
            |->input_get_disposition
                |->input_handle_abs_event
            |->[pending slot event] ... if (disposition & INPUT_SLOT)

详细分析见 《3.4.2 事件流转分析》
在这里直接说结论:

  • ABS_MT_SLOT事件不会单独被上报即使其之后跟随SYN_REPORT事件,实现如下
    • ABS_MT_SLOT事件value会被存入input_dev->mt->slot 并 ignore
    • 下一次ABS_MT事件来时,如果input_dev->mt->slot(当前slot值)和input_dev->absinfo[ABS_MT_SLOT](上一次slot值)如果不相等,则更新 input_dev->absinfo[ABS_MT_SLOT]并将ABS_MT_SLOT挂起存入input_dev->vals
  • 下次SYN_REPORT来时会将ABS_MT_SLOT事件流转至evdev_client->buffer中

6.3.2 input_mt_report_slot_state

input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
|->input_mt_report_slot_state
    |->input_mt_get_value
    |->input_mt_new_trkid
    |->input_event
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
|->input_mt_report_slot_state
    |->input_event
/**
 * input_mt_report_slot_state() - report contact state
 * @dev: input device with allocated MT slots
 * @tool_type: the tool type to use in this slot
 * @active: true if contact is active, false otherwise
 *
 * Reports a contact via ABS_MT_TRACKING_ID, and optionally
 * ABS_MT_TOOL_TYPE. If active is true and the slot is currently
 * inactive, or if the tool type is changed, a new tracking id is
 * assigned to the slot. The tool type is only reported if the
 * corresponding absbit field is set.
 *
 * 通过 ABS_MT_TRACKING_ID 和可选的 ABS_MT_TOOL_TYPE 报告联系人。
 * 如果 active 为 true 并且插槽当前处于非活动状态,或者如果工具类型已更改,
 * 则会为插槽分配一个新的跟踪 ID。 只有设置了相应的 absbit 字段,才会报告工具类型。
 *
 * Returns true if contact is active.
 */
bool input_mt_report_slot_state(struct input_dev *dev,
                                unsigned int tool_type, bool active)
{
        struct input_mt *mt = dev->mt;
        struct input_mt_slot *slot;
        int id;

        /* 如果mt数据不存在则返回 */
        if (!mt)
                return false;

        /* 获取当前slot的slot数据 */
        slot = &mt->slots[mt->slot];
        /* 对齐slot frame序号到当前frame */
        slot->frame = mt->frame;

        /* 如果active == 0 */
        if (!active) {
                /* 上报当前slot为非使用状态 */
                input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
                return false;
        }

        /* 获取当前slot->abs 中保存的 ABS_MT_TRACKING_ID value */
        id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
        /* 如果此slot未启用则分配ID */
        if (id < 0)
                id = input_mt_new_trkid(mt);

        /* 上报ABS_MT_TRACKING_ID */
        input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
        /* 上报ABS_MT_TOOL_TYPE */
        input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);

        return true;
}
EXPORT_SYMBOL(input_mt_report_slot_state);

6.3.3 input_report_abs

input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
|->input_report_abs
    |->input_event
        |->input_handle_event
            |->input_get_disposition
                |->input_handle_abs_event
        |->[store value to input_dev->vals] ... if (disposition & INPUT_PASS_TO_HANDLERS)

详细分析见《3.4.2 事件流转分析》
在这里直接说结论:

  • ABS_MT事件数据会被挂入input_dev->vals中
  • ABS_MT事件数据会随着下一次SYN_REPORT事件到来被流转到evdev_client->buffer中

6.3.4 input_sync

input_sync(ts->input_dev);
|->input_sync
    |->input_event
        |->input_handle_event
            |->input_get_disposition
        |->input_pass_values
            |->input_to_handler
                    |->evdev_events
                        |->evdev_pass_values
                            |->__evdev_is_filtered
                        |->__pass_event
                            |->[store values to evdev_client->buffer]
                            |->kill_fasync
                        |->wake_up_interruptible_poll

详细分析见《3.4.2 事件流转分析》
在这里直接说结论:

  • 只有在input_dev->vals中存在>=2个成员,事件才会被流转,目的是不上报单一的SYN_REPORT事件给evdev_client->buffer
  • evdev接收到SYN_REPORT事件会唤醒block read 和 poll 节点的evdev_client并会发送SIGIO信号给evdev_client->fasync保存的PID所指向的线程

7. input_polldev.c

轮询输入设备提供了一个框架,用于支持不会触发中断但需要定期扫描或轮询以检测其状态变化的简单硬件输入设备。
它可以定期轮询硬件状态的输入设备驱动程序

7.1 数据结构

7.1.1 input_polled_dev


/*
 * 轮询输入设备提供了一个框架,
 * 用于支持不会触发中断但需要定期扫描或轮询以检测其状态变化的简单输入设备。 
 */
struct input_polled_dev {
        void *private;                                // 私有驱动数据

        void (*open)(struct input_polled_dev *dev);    // 驱动提供的方法,用于准备设备进行轮询(启用设备并可能刷新设备状态)
        void (*close)(struct input_polled_dev *dev);   // 驱动提供的方法,在设备不再被轮询时调用。用于将设备置于低功耗模式
        void (*poll)(struct input_polled_dev *dev);    // 驱动提供的方法,轮询设备并发布输入事件(必需)
        unsigned int poll_interval; /* msec */         // 指定 poll() 方法应该被调用的频率。默认为 500 毫秒,除非在注册设备时被覆盖
        unsigned int poll_interval_max; /* msec */     // 指定轮询间隔的上限。默认为 poll_interval 的初始值
        unsigned int poll_interval_min; /* msec */     // 指定轮询间隔的下限。默认为 0

        struct input_dev *input;                       // 与轮询设备关联的输入设备结构。必须由驱动程序正确初始化(id、name、phys、bits)

/* private: */
        struct delayed_work work;                      // 延迟工作结构体,用于延迟执行轮询操作

        bool devres_managed;                           // 是否由设备资源管理器管理该结构体
};

7.1.2 input_dev_poller

struct input_dev_poller {
        void (*poll)(struct input_dev *dev);// 指向轮询器的回调函数,用于检查输入设备是否有新的事件

        unsigned int poll_interval;         // 轮询器的轮询间隔,以毫秒为单位
        unsigned int poll_interval_max;     // 轮询器的最小轮询间隔,以毫秒为单位
        unsigned int poll_interval_min;     // 轮询器的最大轮询间隔,以毫秒为单位

        struct input_dev *input;            // 指向该轮询器所属的输入设备的指针
        struct delayed_work work;           // 用于将轮询器的回调函数添加到工作队列中,以便在后台执行
};

7.2 驱动程序实例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/time64.h>
#include <linux/rtc.h>

#define POLL_INTERVAL 500

#define TEST_LOG(fmt, args...)                                      \
do {                                                                \
    struct timespec64 tv;                                           \
    unsigned long local_time;                                       \
    struct rtc_time tm;                                             \
    ktime_get_real_ts64(&tv);                                       \
    local_time = (u32)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));   \
    rtc_time64_to_tm(local_time, &tm);                              \
    pr_err("[%02d:%02d:%02d.%03zu] %s %d: " fmt, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_nsec/1000000, __func__, __LINE__, ##args); \
} while (0)

static struct input_polled_dev *polled_dev;
static struct input_dev *input_dev;

static void polled_dev_poll(struct input_polled_dev *dev)
{
    static state = 0;
    TEST_LOG("++\n");
    // 读取输入设备的状态
    state = !state; // 假设输入设备的状态为 0
    input_event(input_dev, EV_KEY, KEY_A, state);
    TEST_LOG("report event KEY_A %d\n", state);
    input_sync(input_dev);
    TEST_LOG("--\n");
}

static int __init input_poller_init(void)
{
    int error;

    // 创建输入设备
    TEST_LOG("++\n");
    input_dev = input_allocate_device();
    if (!input_dev) {
        TEST_LOG("Failed to allocate input device\n");
        return -ENOMEM;
    }

    // 设置输入设备的属性
    input_dev->name = "My Input Device";
    input_dev->id.bustype = BUS_VIRTUAL;
    input_dev->evbit[0] = BIT_MASK(EV_KEY);
    input_dev->keybit[BIT_WORD(KEY_A)] = BIT_MASK(KEY_A);
    input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);

    // 创建 input-poller 设备
    polled_dev = input_allocate_polled_device();
    if (!polled_dev) {
        TEST_LOG("Failed to allocate input-poller device\n");
        input_unregister_device(input_dev);
        return -ENOMEM;
    }

    // 设置 input-poller 设备的属性
    polled_dev->poll_interval = POLL_INTERVAL;
    polled_dev->poll = polled_dev_poll;
    polled_dev->input = input_dev;

    // 注册 input-poller 设备
    error = input_register_polled_device(polled_dev);
    if (error) {
        TEST_LOG("Failed to register input-poller device, err = %d\n", error);
        input_free_polled_device(polled_dev);
        input_unregister_device(input_dev);
        return error;
    }
    TEST_LOG("--\n");

    return 0;
}

static void __exit input_poller_exit(void)
{
    TEST_LOG("++\n");
    // 注销 input-poller 设备
    input_unregister_polled_device(polled_dev);

    // 注销输入设备
    input_unregister_device(input_dev);

    // 释放 input-poller 设备和输入设备的内存
    input_free_polled_device(polled_dev);
    input_free_device(input_dev);
    TEST_LOG("--\n");
}

module_init(input_poller_init);
module_exit(input_poller_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Input Poller Driver");

#define POLL_INTERVAL 50
static struct input_polled_dev *polled_dev;
static struct input_dev *input_dev;

static void polled_dev_poll(struct input_polled_dev *dev)
{
    // 读取输入设备的状态
    int state = 0; // 假设输入设备的状态为 0
    input_event(input_dev, EV_KEY, KEY_A, state);
    input_sync(input_dev);
}

static int __init input_poller_init(void)
{
    ...
    // 创建 input-poller 设备
    polled_dev = input_allocate_polled_device();
    // 设置 input-poller 设备的属性
    polled_dev->poll_interval = POLL_INTERVAL;
    polled_dev->poll = polled_dev_poll;
    polled_dev->input = input_dev;
    // 注册 input-poller 设备
    error = input_register_polled_device(polled_dev);
    ...
}

7.3 input_allocate_polled_device

/**
 * input_allocate_polled_device - allocate memory for polled device
 *
 * The function allocates memory for a polled device and also
 * for an input device associated with this polled device.
 */
struct input_polled_dev *input_allocate_polled_device(void)
{
        struct input_polled_dev *dev;

        dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL);
        if (!dev)
                return NULL;

        /* 给input_polled_dev申请input_dev,用于创建节点文件 */
        dev->input = input_allocate_device();
        if (!dev->input) {
                kfree(dev);
                return NULL;
        }

        return dev;
}
EXPORT_SYMBOL(input_allocate_polled_device);

7.4 input_register_polled_device

/**
 * input_register_polled_device - register polled device
 * @dev: device to register
 *
 * The function registers previously initialized polled input device
 * with input layer. The device should be allocated with call to
 * input_allocate_polled_device(). Callers should also set up poll()
 * method and set up capabilities (id, name, phys, bits) of the
 * corresponding input_dev structure.
 */
int input_register_polled_device(struct input_polled_dev *dev)
{
        struct input_polled_devres *devres = NULL;
        struct input_dev *input = dev->input;
        int error;

        /* 设备资源管理器管理 */
        if (dev->devres_managed) {
                devres = devres_alloc(devm_input_polldev_unregister,
                                      sizeof(*devres), GFP_KERNEL);
                if (!devres)
                        return -ENOMEM;

                devres->polldev = dev;
        }

        /* 
         * &input->dev->driver_data = dev
         * 设定input_dev私有数据为input_polled_dev
         */
        input_set_drvdata(input, dev);
        /* 初始化&dev->work */
        INIT_DELAYED_WORK(&dev->work, input_polled_device_work);

        /* 如果轮询间隔未定义,则设定500ms */
        if (!dev->poll_interval)
                dev->poll_interval = 500;
        /* 如果轮询间隔最大值未定义,则设定等于轮询间隔 */
        if (!dev->poll_interval_max)
                dev->poll_interval_max = dev->poll_interval;

        /* 设定input_dev的fops函数 */
        input->open = input_open_polled_device;
        input->close = input_close_polled_device;

        input->dev.groups = input_polldev_attribute_groups;

        /* 向input core 注册此input_dev */
        error = input_register_device(input);
        if (error) {
                devres_free(devres);
                return error;
        }

        /*
         * Take extra reference to the underlying input device so
         * that it survives call to input_unregister_polled_device()
         * and is deleted only after input_free_polled_device()
         * has been invoked. This is needed to ease task of freeing
         * sparse keymaps.
         *
         * 对底层输入设备进行额外引用,以便它在调用 input_unregister_polled_device() 后仍然存在,
         * 并且仅在调用 input_free_polled_device() 之后被删除。 这是减轻释放稀疏键映射的任务所必需的。
         */
        input_get_device(input);

        /* 设备资源管理器管理 */
        if (dev->devres_managed) {
                dev_dbg(input->dev.parent, "%s: registering %s with devres.\n",
                        __func__, dev_name(&input->dev));
                devres_add(input->dev.parent, devres);
        }

        return 0;
}

7.4.1 input_polled_device_work

static void input_polled_device_work(struct work_struct *work)
{
        struct input_polled_dev *dev =
                container_of(work, struct input_polled_dev, work.work);
        
        /* 调用input_polled_dev->poll中保存的回调函数 */
        dev->poll(dev);
        /* 开启下一次轮询调用,间隔为 poll_interval ms */
        input_polldev_queue_work(dev);
}

static void input_polldev_queue_work(struct input_polled_dev *dev)
{
        unsigned long delay;

        /* 计算delay事件 */
        delay = msecs_to_jiffies(dev->poll_interval);
        if (delay >= HZ)
                delay = round_jiffies_relative(delay);

        /* 在系统wq中轮询 */
        queue_delayed_work(system_freezable_wq, &dev->work, delay);
}

7.4.2 input_open_polled_device

|->evdev_open
    |->evdev_open_device
        |->input_open_device                轮询调用
            |->input_open_polled_device <----------------
                |->input_polldev_queue_work             |
                    |->queue_delayed_work               |
                        |->input_polldev_queue_work ----- 
static int input_open_polled_device(struct input_dev *input)
{
        struct input_polled_dev *dev = input_get_drvdata(input);

        /* 如果input_dev->open被赋值则调用input_dev->open */
        if (dev->open)
                dev->open(dev);

        /* Only start polling if polling is enabled */
        if (dev->poll_interval > 0) {
                /* 调用input_polled_dev->poll中保存的回调函数 */
                dev->poll(dev);
                 /* 开启下一次轮询调用,间隔为 poll_interval ms */
                input_polldev_queue_work(dev);
        }

        return 0;
}

7.4.3 input_close_polled_device

|->evdev_release
    |->evdev_close_device
        |->input_close_device
            |->input_close_polled_device
static void input_close_polled_device(struct input_dev *input)
{
        struct input_polled_dev *dev = input_get_drvdata(input);

        /* 取消工作 */
        cancel_delayed_work_sync(&dev->work);

        /* 如果input_dev->close被赋值则调用input_dev->close */
        if (dev->close)
                dev->close(dev);
}

7.5 设定/查询轮询间隔参数

除了初始化设定,这里仅仅支持用户程序访问节点设定和查询参数

/* SYSFS interface */

static ssize_t input_polldev_get_poll(struct device *dev,
                                      struct device_attribute *attr, char *buf)
{
        struct input_polled_dev *polldev = dev_get_drvdata(dev);

        return sprintf(buf, "%d\n", polldev->poll_interval);
}

static ssize_t input_polldev_set_poll(struct device *dev,
                                struct device_attribute *attr, const char *buf,
                                size_t count)
{
        struct input_polled_dev *polldev = dev_get_drvdata(dev);
        struct input_dev *input = polldev->input;
        unsigned int interval;
        int err;

        err = kstrtouint(buf, 0, &interval);
        if (err)
                return err;

        if (interval < polldev->poll_interval_min)
                return -EINVAL;

        if (interval > polldev->poll_interval_max)
                return -EINVAL;

        mutex_lock(&input->mutex);

        polldev->poll_interval = interval;

        if (input->users) {
                cancel_delayed_work_sync(&polldev->work);
                if (polldev->poll_interval > 0)
                        input_polldev_queue_work(polldev);
        }

        mutex_unlock(&input->mutex);

        return count;
}

static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll,
                                            input_polldev_set_poll);


static ssize_t input_polldev_get_max(struct device *dev,
                                     struct device_attribute *attr, char *buf)
{
        struct input_polled_dev *polldev = dev_get_drvdata(dev);

        return sprintf(buf, "%d\n", polldev->poll_interval_max);
}

static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL);

static ssize_t input_polldev_get_min(struct device *dev,
                                     struct device_attribute *attr, char *buf)
{
        struct input_polled_dev *polldev = dev_get_drvdata(dev);

        return sprintf(buf, "%d\n", polldev->poll_interval_min);
}

static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL);

static struct attribute *sysfs_attrs[] = {
        &dev_attr_poll.attr,
        &dev_attr_max.attr,
        &dev_attr_min.attr,
        NULL
};

static struct attribute_group input_polldev_attribute_group = {
        .attrs = sysfs_attrs
};

static const struct attribute_group *input_polldev_attribute_groups[] = {
        &input_polldev_attribute_group,
        NULL
};

7.6 实验

首先需要把input-poller模块编译进kernel或者编译成ko
如果是安卓设备在.config里添加即可,如果是直接使用内核的设备则需要在menuconfig里打开选项

CONFIG_INPUT_POLLDEV=m    //实验选用这个
or
CONFIG_INPUT_POLLDEV=y

然后将驱动程序编成ko,设备启动后可以看到被加载的ko
在这里插入图片描述

在这里插入图片描述

8. input_poller.c

input_poller.c是一个输入设备的事件驱动程序,它使用事件通知机制来检测输入设备是否有数据可读。当输入设备有数据可读时,它会触发一个事件通知,然后将数据传递给上层应用程序。
input_poller.c和input_polldev.c两者提供的机制没有什么区别,底层逻辑都是周期性调用其结构体成员中的poll函数指针指向的函数

8.1 数据结构

struct input_dev_poller {
        void (*poll)(struct input_dev *dev);

        unsigned int poll_interval;     //轮询间隔
        unsigned int poll_interval_max; //轮询间隔的最大值
        unsigned int poll_interval_min; //轮询间隔最小值

        struct input_dev *input;        //存放input_dev地址
        struct delayed_work work;       //延迟工作队列元素
};

8.2 驱动程序实例

https://elixir.bootlin.com/linux/v5.10/source/drivers/input/joystick/psxpad-spi.c#L350

static int psxpad_spi_probe(struct spi_device *spi)
{
    ...
        err = input_setup_polling(idev, psxpad_spi_poll);
        if (err) {
                dev_err(&spi->dev, "failed to set up polling: %d\n", err);
                return err;
        }

        /* poll interval is about 60fps */
        input_set_poll_interval(idev, 16);
        input_set_min_poll_interval(idev, 8);
        input_set_max_poll_interval(idev, 32);
         
        /* register input poll device */
        err = input_register_device(idev);
        if (err) {
                dev_err(&spi->dev,
                        "failed to register input device: %d\n", err);
                return err;
        }
    ...
}

8.3 input_setup_polling

int input_setup_polling(struct input_dev *dev,
                        void (*poll_fn)(struct input_dev *dev))
{
        struct input_dev_poller *poller;

        /* 创建input_dev_poller */
        poller = kzalloc(sizeof(*poller), GFP_KERNEL);
        if (!poller) {
                /*
                 * We want to show message even though kzalloc() may have
                 * printed backtrace as knowing what instance of input
                 * device we were dealing with is helpful.
                 * 我们想要显示消息,即使 kzalloc() 可能已经打印回溯
                 * 因为知道我们正在处理的输入设备实例是有帮助的。
                 */
                dev_err(dev->dev.parent ?: &dev->dev,
                        "%s: unable to allocate poller structure\n", __func__);
                return -ENOMEM;
        }

        /* 初始化延迟工作队列元素 */
        INIT_DELAYED_WORK(&poller->work, input_dev_poller_work);
        /* 给input_dev_poller绑定input_dev和poll函数 */
        poller->input = dev;
        poller->poll = poll_fn;

        /* 给input_dev绑定input_dev_poller */
        dev->poller = poller;
        return 0;
}
EXPORT_SYMBOL(input_setup_polling);

8.4 input_dev_poller_finalize

在input_dev注册时会调用input_dev_poller_finalize,主要作用是在驱动作者未设定的轮询间隔时设定轮询间隔

|->input_register_device
    |->input_dev_poller_finalize
void input_dev_poller_finalize(struct input_dev_poller *poller)
{
        if (!poller->poll_interval)
                poller->poll_interval = 500;
        if (!poller->poll_interval_max)
                poller->poll_interval_max = poller->poll_interval;
}

8.5 input_dev_poller_start

这里介绍应用程序是如何启用轮询的

|->evdev_open
    |->evdev_open_device
        |->input_open_device                轮询调用
            |->input_dev_poller_start <----------------
                |->input_dev_poller_queue_work        |
                    |->input_dev_poller_work          |
                        |->input_dev_poller_start ----- 
void input_dev_poller_start(struct input_dev_poller *poller)
{
        /* Only start polling if polling is enabled */
        if (poller->poll_interval > 0) {
                /* 调用input_dev_poller的poll函数 */
                poller->poll(poller->input);
                /* 建立轮询调用 */
                input_dev_poller_queue_work(poller);
        }
}

static void input_dev_poller_queue_work(struct input_dev_poller *poller)
{
        unsigned long delay;

        delay = msecs_to_jiffies(poller->poll_interval);
        if (delay >= HZ)
                delay = round_jiffies_relative(delay);

        /* poll_interval ms后执行 */
        queue_delayed_work(system_freezable_wq, &poller->work, delay);
}

static void input_dev_poller_work(struct work_struct *work)
{
        struct input_dev_poller *poller =
                container_of(work, struct input_dev_poller, work.work);

        /* 再执行poll */
        poller->poll(poller->input);
        /* 实现轮询调用 */
        input_dev_poller_queue_work(poller);
}

8.6 input_dev_poller_stop

这里介绍应用程序是如何结束轮询的

|->evdev_release
    |->evdev_close_device
        |->input_close_device
            |->input_dev_poller_stop
void input_dev_poller_stop(struct input_dev_poller *poller)
{
        cancel_delayed_work_sync(&poller->work);
}

8.7 设定/查询轮询间隔参数

参数设定/查询的路径有两个

  • 驱动作者调用函数
  • 应用程序访问节点

8.7.1 驱动作者调用函数

void input_set_poll_interval(struct input_dev *dev, unsigned int interval)
{
        /* 如果dev的poller存在 */
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval = interval;
}
EXPORT_SYMBOL(input_set_poll_interval);

void input_set_min_poll_interval(struct input_dev *dev, unsigned int interval)
{
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval_min = interval;
}
EXPORT_SYMBOL(input_set_min_poll_interval);

void input_set_max_poll_interval(struct input_dev *dev, unsigned int interval)
{
        if (input_dev_ensure_poller(dev))
                dev->poller->poll_interval_max = interval;
}
EXPORT_SYMBOL(input_set_max_poll_interval);

int input_get_poll_interval(struct input_dev *dev)
{
        if (!dev->poller)
                return -EINVAL;

        return dev->poller->poll_interval;
}
EXPORT_SYMBOL(input_get_poll_interval);

8.7.2 应用程序访问节点

/* SYSFS interface */

static ssize_t input_dev_get_poll_interval(struct device *dev,
                                           struct device_attribute *attr,
                                           char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval);
}

static ssize_t input_dev_set_poll_interval(struct device *dev,
                                           struct device_attribute *attr,
                                           const char *buf, size_t count)
{
        struct input_dev *input = to_input_dev(dev);
        struct input_dev_poller *poller = input->poller;
        unsigned int interval;
        int err;

        err = kstrtouint(buf, 0, &interval);
        if (err)
                return err;

        if (interval < poller->poll_interval_min)
                return -EINVAL;

        if (interval > poller->poll_interval_max)
                return -EINVAL;

        mutex_lock(&input->mutex);

        poller->poll_interval = interval;

        if (input->users) {
                cancel_delayed_work_sync(&poller->work);
                if (poller->poll_interval > 0)
                        input_dev_poller_queue_work(poller);
        }

        mutex_unlock(&input->mutex);

        return count;
}

static DEVICE_ATTR(poll, 0644,
                   input_dev_get_poll_interval, input_dev_set_poll_interval);

static ssize_t input_dev_get_poll_max(struct device *dev,
                                      struct device_attribute *attr, char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval_max);
}

static DEVICE_ATTR(max, 0444, input_dev_get_poll_max, NULL);

static ssize_t input_dev_get_poll_min(struct device *dev,
                                     struct device_attribute *attr, char *buf)
{
        struct input_dev *input = to_input_dev(dev);

        return sprintf(buf, "%d\n", input->poller->poll_interval_min);
}

static DEVICE_ATTR(min, 0444, input_dev_get_poll_min, NULL);

static umode_t input_poller_attrs_visible(struct kobject *kobj,
                                          struct attribute *attr, int n)
{
        struct device *dev = kobj_to_dev(kobj);
        struct input_dev *input = to_input_dev(dev);

        return input->poller ? attr->mode : 0;
}

static struct attribute *input_poller_attrs[] = {
        &dev_attr_poll.attr,
        &dev_attr_max.attr,
        &dev_attr_min.attr,
        NULL
};

struct attribute_group input_poller_attribute_group = {
        .is_visible        = input_poller_attrs_visible,
        .attrs                = input_poller_attrs,
};

9. Keymap

Linux input子系统中的Keymap是一个映射表,用于将输入设备上的按键映射到相应的字符或功能。它是一个非常重要的组件,因为它允许Linux系统正确地解释来自各种输入设备的输入信号。

Keymap通常由操作系统或应用程序提供,它们可以根据用户的需要进行自定义。例如,用户可以将某个按键映射到一个特定的字符或命令,或者将多个按键组合映射到一个功能键。

Keymap还可以用于解决不同键盘布局之间的差异。例如,美国键盘布局与德国键盘布局不同,因此Keymap可以确保在不同的键盘布局下,相同的按键映射到相同的字符或功能。

总之,Keymap是Linux输入子系统中的一个重要组件,它确保了输入设备的正确解释和使用。

10. input-compat.c

Linux input子系统中的input-compat是一个兼容性层,它的作用是为旧版的输入设备驱动程序提供兼容性支持,使它们能够在新版的Linux内核中正常工作。
具体来说,input-compat提供了一些函数和数据结构,用于将旧版的输入设备驱动程序与新版的输入子系统进行适配。例如,它提供了一个input_register_device函数,用于向输入子系统注册输入设备,同时还提供了一些与输入事件相关的数据结构,如input_event和input_absinfo等。
通过使用input-compat,旧版的输入设备驱动程序可以与新版的输入子系统进行兼容,从而实现输入设备的正常工作。这对于一些老旧的硬件设备来说尤为重要,因为它们的驱动程序可能已经过时,无法直接与新版的输入子系统兼容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/10398.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[考研数据结构]第3章之栈的基本知识与操作

文章目录 栈的基本概念 栈的实现 顺序栈 共享栈 链栈 栈的基本概念 栈的定义 栈&#xff08;Stack&#xff09;是只允许在一端进行插入或删除操作的线性表 相关术语 栈顶&#xff08;Top&#xff09;线性表允许进行插入或删除的那一端称之为栈顶栈底&#xff08;Bottom&…

【计算机网络-数据链路层】集线器、网桥、交换机

本文许多文字和图片使用了湖科大教书匠&#xff08;高军老师&#xff09;的 PPT&#xff0c;在此表示感谢。正是他让非科班的我能以奇妙的方式走进网络的世界。 文章目录1 【物理层】集线器&#xff08;Hub&#xff09;——共享式以太网1.1 为什么使用集线器&#xff1f;1.2 集…

macOS Monterey 12.6.5 (21G531) Boot ISO 原版可引导镜像

本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 2023 年 4 月 10 日&#xff08;北京…

ESXI 6.7全面系统教程~汇总

ESXI 6.7全面系统教程 许可证&#xff1a;0A65P-00HD0-375M1-M097M-22P7H esxi 是一个脱机系统&#xff0c;也是一个虚拟机系统与vmware 相比&#xff0c;它可以直接运行在硬件上&#xff0c;这样可以减少资源浪费&#xff0c;一般用于服务器上&#xff1b;下面是esxi 的完整…

stable-diffusion-webui-colab部署记录

stable-diffusion-webui-colab 该模型可以在网上云端部署stable-diffusion&#xff0c;减少本地部署的繁琐步骤降低配置要求的依赖。 一、进入stable-diffusion-webui-colab 1.网址&#xff1a;https://github.com/camenduru/stable-diffusion-webui-colab 在分支中选择driv…

我的创作纪念日:Unity CEO表示生成式AI将是Unity近期发展重点,发布神秘影片预告

PICK 未来的AI技术将会让人类迎来下一个生产力变革&#xff0c;这其中也包括生成型AI的突破性革新。各大公司也正在竞相推出AIGC工具&#xff0c;其中微软的Copilot、Adobe的Firefly、Github的chatGPT等引起了人们的关注。然而&#xff0c;游戏开发领域似乎还没有一款真正针对性…

Vulnhub:Digitalworld.local (Development)靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.130 信息收集 端口扫描 nmap -A -v -sV -T5 -p- --scripthttp-enum 192.168.111.130 查看网站首页源码 访问development目录&#xff0c;提示存在一个流量包 查看流量包发现另一个网站路径&#xff1a;/devel…

java继承类怎么写

继承类是通过把父类的方法和属性继承到一个类中&#xff0c;而子类的方法和属性是子类自己定义的。 Java中有一个很重要的概念叫做继承&#xff0c;这也是 Java语言的精髓所在。Java语言提供了一种机制&#xff0c;叫做派生类。在 Java中&#xff0c;如果没有实现了某个派生类方…

python 调用c++

python中调用c&#xff0c;函数参数用 int类型&#xff0c;返回值为类型1,且返回值为 false。 注意&#xff1a;如果你使用了C中的 false&#xff0c;则返回的是-1。 在 Python中调用C时&#xff0c;你会得到一个名为 bool的类&#xff0c;其中包含了两个成员变量&#xff1a; …

多智能体深度强化学习在移动边缘计算的联合多通道访问和任务卸载中的应用

多智能体深度强化学习在移动边缘计算的联合多通道访问和任务卸载中的应用主要贡献与相关工作比较的贡献三、系统模型&#xff08;only 2 pages&#xff09;3.1 网络模型3.2 通信模型3.3 计算模型3.3.1 本地计算3.3.2 卸载计算四、预备知识&#xff08;only 1 page&#xff09;五…

SpringCloud-Gateway网关搭建整合nacos配置中心实现动态路由整合sentinel实现服务限流熔点降级

官方文档(更多配置详情直接查看官方文档) 为什么需要服务网关 传统的单体架构中只需要开放一个服务给客户端调用&#xff0c;但是微服务架构中是将一个系统拆分成多个微服务&#xff0c;如果没有网关&#xff0c;客户端只能在本地记录每个微服务的调用地址&#xff0c;当需要调…

安全防御 --- 恶意代码、防病毒

一、恶意代码 1、按照传播方式分类 &#xff08;1&#xff09;病毒 概念&#xff1a;病毒是一种基于硬件和操作系统的程序&#xff0c;具有感染和破坏能力&#xff0c;这与病毒程序的结构有关。病毒攻击的宿主程序是病毒的栖身地&#xff0c;它是病毒传播的目的地&#xff0…

MySQL库的操作

文章目录&#xff1a;创建数据库字符集和校验规则查看系统默认字符集和校验规则查看数据库支持的字符集查看数据库支持的字符集校验规则校验规则对数据库的影响操作数据库查看数据库显示创建语句修改数据库删除数据库数据库的备份和还原表的备份和还原查看连接情况创建数据库 …

数据库基础

文章目录前言一、什么是数据库二、主流数据库三、基本使用1.连接服务器2.服务器,数据库,表关系3.使用案例4.数据逻辑存储四、MySQL架构五、SQL分类六、存储引擎1.存储引擎2.查看存储引擎3.存储引擎对比总结前言 正文开始!!! 一、什么是数据库 存储数据用文件就可以了,为什么还…

【并发编程】AQS源码

ReentrantLock 互斥锁,可重入 AQS是可以支持互斥锁和共享锁的&#xff0c;这里只分析互斥锁的源码 加锁 公平锁和非公平锁 公平锁 final void lock() {acquire(1); //抢占1把锁.}// AQS里面的方法public final void acquire(int arg) { if (!tryAcquire(arg) &&acq…

IP协议(网络层重点协议)

目录 一、IP协议报头格式 二、地址选择 1、IP地址 &#xff08;1&#xff09;格式 &#xff08;2&#xff09;组成 &#xff08;3&#xff09;分类 &#xff08;4&#xff09;子网掩码 三、路由选择 IP协议是网络层的协议&#xff0c;它主要完成两个方面的任务&#xf…

redis基础(6.0)数据结构、事务、常用组件等

1 概述 1.1 redis介绍 Redis 是互联网技术领域使用最为广泛的存储中间件&#xff0c;它是「Remote Dictionary Service」的首字母缩写&#xff0c;也就是「远程字典服务」。Redis 以其超高的性能、完美的文档、 简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。…

车载网络 - Autosar网络管理 - 常用缩写

为了方便大家日常工作中的使用和交流&#xff0c;每块专业规范或者文章中&#xff0c;都会有或多或少的缩写使用&#xff0c;然而如果一段时间没使用&#xff0c;经常会忘记这些缩写到底代表的是什么意思&#xff0c;为了方便后续内容的介绍&#xff0c;也为了我自己后面忘记后…

做自动化测试时所谓的“难点”

这篇关于自动化测试的文章&#xff0c;可能和你看到的大多数自动化的文章有所不同。我不是一位专职的自动化测试工程师&#xff0c;没有开发过自动化的工具或者框架&#xff0c;用的自动化的工具也不多&#xff0c;也没有做过开发&#xff0c;所以我讲不出那些现在很多人很看重…

JavaScript【一】JavaScript变量与数据类型

文章目录&#x1f31f;前言&#x1f31f;变量&#x1f31f; 变量是什么&#xff1f;&#x1f31f; 变量提升&#x1f31f; 声明变量&#x1f31f; JavaScript有三种声明方式&#x1f31f; 命名规范&#x1f31f; 注意&#x1f31f;数据类型以及运算&#x1f31f; 检测变量数据类…