【Linux】浅析Input子系统

news2024/11/20 9:17:01

文章目录

  • 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,旧版的输入设备驱动程序可以与新版的输入子系统进行兼容,从而实现输入设备的正常工作。这对于一些老旧的硬件设备来说尤为重要,因为它们的驱动程序可能已经过时,无法直接与新版的输入子系统兼容。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ADManager Plus:简化 Active Directory 管理的完美工具

在企业中&#xff0c;Active Directory&#xff08;AD&#xff09;是一个非常重要的组件&#xff0c;用于管理和控制所有计算机和用户的访问权限。然而&#xff0c;AD的管理和维护需要一定的技术能力和时间成本。为了简化这个过程&#xff0c;ManageEngine 推出了 ADManager Pl…

ASIC-WORLD Verilog(2)FPGA的设计流程

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…

如何在 DevOps 中进行 API 全生命周期管理?

随着 DevOps 理念在中国企业当中的普及和发展&#xff0c;中国企业 DevOps 落地成熟度不断提升&#xff0c;根据中国信通院的数据已有近 6 成企业向全生命周期管理迈进。 而在研发全生命周期管理之中&#xff0c;API 管理的地位愈发显得重要。随着 API 数量的大幅增长&#xf…

针对电商物流网络包裹应急调运与结构优化问题的解题【思路数据代码详解】

文章目录C 题 电商物流网络包裹应急调运与结构优化问题解题思路数据进行预处理将数据转换为 stationary 数据底部C 题 电商物流网络包裹应急调运与结构优化问题 (赛题出来以后第一时间在CSDN分享&#xff0c;文章底部) 最新进度在文章最下方卡片&#xff0c;加入获取思路数据代…

【Spring Cloud Alibaba】4.创建服务消费者

文章目录简介开始搭建创建项目修改POM文件添加启动类添加配置项添加Controller添加配置文件启动项目测试访问Nacos访问接口查看端点检查简介 接下来我们创建一个服务消费者&#xff0c;本操作先要完成之前的步骤&#xff0c;详情请参照【Spring Cloud Alibaba】Spring Cloud A…

深度学习代码,对coco数据集evaluate时,spice评估总是报错,解决如下:

在跑evaluate.py时&#xff0c;发现bleu&#xff0c;METEOR&#xff0c;ROUGE_L都能成功&#xff0c;就是spice评估总是报错&#xff0c;我的报错内容如下&#xff1a; 就找了好久&#xff0c;也问了chatgpt啥的&#xff0c;尝试很很多&#xff0c;最终发现是java版本太高了&am…

数字化转型导师坚鹏:金融机构数据治理政策解读及问题解决之道

金融机构数据治理政策解读及问题解决之道课程背景&#xff1a; 很多金融机构存在以下问题&#xff1a; 不知道如何准确理解银保监会数据治理相关政策及要求&#xff1f; 不清楚如何有效解决监管数据治理常见问题及提升之道&#xff1f; 不清楚如何有效落实银保监会数据治理相…

算法模板(2):数据结构(3) 复杂数据结构1

复杂数据结构&#xff08;1&#xff09; 1. Splay 基本概念 什么是 Splay Splay 是一种二叉查找树&#xff0c;它通过不断将某个节点旋转到根节点&#xff0c;使得整棵树仍然满足二叉查找树的性质&#xff0c;并且保持平衡而不至于退化为链. 旋转操作 为了使 Splay 保持平…

虚拟主持人是什么?有哪些应用场景?

虚拟主持人是一种通动作捕捉技术或人工智能等技术&#xff0c;来驱动虚拟形象模拟真实主持人的动作、声音和表情&#xff0c;为各种场合提供主持服务的数字化角色。虚拟主持人的外貌、服装、发型、口音等都可以根据客户的需求和喜好进行定制&#xff0c;甚至可以创建出具有明星…

冶炼金属(十四届蓝桥杯B组)(超级详细)

目录 1、冶炼金属 解法一&#xff1a;(二分算法) 解法二&#xff1a;&#xff08;数学公式推导&#xff09; 2、永远不要试图用拖延来期待事情发生改变&#xff01;&#xff01;&#xff01; 1、冶炼金属 小蓝有一个神奇的炉子用于将普通金属 O冶炼成为一种特殊金属 X。 这…

OceanBase 4.1 发版 | 一个面向开发者的里程碑版本

欢迎访问 OceanBase 官网获取更多信息&#xff1a;https://www.oceanbase.com/ 2022 年 8 月&#xff0c;OceanBase发布了 4.0 版本&#xff08;小鱼&#xff09;&#xff0c;作为业内首个单机分布式一体化架构&#xff0c;兼顾了分布式架构的扩展性和集中式架构的性能优势&…

Iptables安全防火墙企业实战

1. IPtables入门简介 Netfilter/Iptables(以下简称Iptables)是unix/linux自带的一款优秀且开放源代码的完全自由的基于包过滤的防火墙工具&#xff0c;它的功能十分强大&#xff0c;使用非常灵活&#xff0c;可以对流入和流出服务器的数据包进行很精细的控制。Iptables主要工作…

用通俗易懂的话来解释什么是Socket

前言 我在去年就学习过Java中Socket的使用&#xff0c;但对于Socket的理解一直都是迷迷糊糊的。看了网上很多关于Socket的介绍&#xff0c;看完还是不太理解到底什么是Socket&#xff0c;还是很迷。直到最近在学习计算机网络&#xff0c;我才对Socket有了一个更深地理解。之前…

sqoop数据导出、脚本使用

目录 准备表与数据 数据导出 脚本调用 准备表与数据 mysql表 CREATE TABLE user (id int(20),name varchar(20) )ENGINEINNODB DEFAULT CHARSETutf8; hive表 create table users( id bigint, name string ) row format delimited fields terminated by "\t";…

消息中间件RabbitMQ---Docker安装RabbitMQ、以及RabbitMQ的基本使用【二】

1、安装 1.1 拉取镜像 docker pull rabbitmq:management1.2 查看镜像 docker images1.3 创建容器 docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management1.4 浏览器访问 登录进去后的效…

【C++】16.红黑树

1.红黑树 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或B lack。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因而…

UNIAPP实战项目笔记60 前端使用token来验证是否已经登录

UNIAPP实战项目笔记60 前端使用token来验证是否已经登录 获取数据库中用户是否有token值,并存入 store.user.token中,有值代表已经登录 detail页面 通过验证token拦截未登录用户bin跳转到登录页面 实际案例图片 后端接口文件 index.js var express require(express); var rou…

Python-入门基础篇(1)

这里使用的编译器是pycharm 打开pycharm 新建项目 选择一个文件路径&#xff08;文件名字不要带中文&#xff09; 新建一个.py 文件 这里就要编写代码啦 Python入门最基础的一些函数 print() 该函数可以输出内容到控制台。在括号中输入要输出的内容&#xff0c;用逗号隔开…

RocketMQ延迟消息源码分析

写作目的 第一个原因&#xff1a;最近玩哔哩哔哩遇到一个RocketMQ的Contributor&#xff0c;一开始不知道他是Contributor&#xff0c;后来问到延迟消息的时候这块还不是很了解&#xff0c;他告诉我学习要系统&#xff0c;你既然了解事务消息那我理解应该也了解延迟消息&#…

BGP路由实验

要求1 使用 Preval 策略 [r4]ip ip-prefix PV permit 192.168.10.0 24 [r4]route-policy PV permit node 10 [r4-route-policy]if-match ip-prefix PV [r4-route-policy]apply preferred-value 100 [r4]route-policy PV permit node 20 [r4-bgp]peer 24.0.0.2 route-policy…