一、Linux输入子系统框架

1. 查看系统中输入设备的信息
# cat /proc/bus/input/devices 查看系统中所有的输入设备节点的详细信息
# cat /proc/bus/input/handlers 查看系统中所有注册的handler的信息
# ls /sys/class/input/ 查看系统中所有的输入设备节点在/sysfs中的信息。ls -l可以看出sysfs文件路径名,有的包含外设接口。注意权限可读可写。
# ls /dev/input/ 查看系统中所有的输入设备的设备文件

2. Linux内核中输入子系统框架

输入子系统的核心层是input.c,它里面 register_chrdev(INPUT_MAJOR, "input", &input_fops); 是系统调用访问输入设备驱动的入口,因为对驱动的查找是通过主设备号进行的。在 input_fops.open() 中它会根据次设备号的分布来决定 struct file->f_op 分派为对应 handler 中的 file_operations 结构体,从而将对应的输入设备测操作交由对应的 handler 去处理。注:5.10内核中注册没有 input_fops 成员了。

handler通过 input_register_handler() 来注册,evdev.c 注册的设备的次设备号 [64, 93],对应的设备节点名为 eventX; mousedev.c 注册的设备的次设备号为 [32, 63],其中 [32, 62] 对应的设备节点名为 mouseX,63对应的设备节点名为 mice; joydev.c 注册的设备的次设备号为[0, 15],对应的设备节点名为 jsX。

输入设备的驱动使用 input_register_device(struct input_dev *dev) 来注册一个 input_dev 结构,上面的文件通过 input_register_handler(struct input_handler *handler)
来注册一个 input_handler 结构。任意一端的注册都会触发匹配,匹配上后就会触发 handler->connect() 被调用,connect() 中传的参数id是device和handler匹配上的那个id。在这个函数中一般会创建/dev/下的设备节点,因为此时设备和驱动匹配上了才能支持上层的读写,才需要创建设备文件。初始化一个 input_handle 结构(注意不是 input_handler),并调用 input_register_handle(struct input_handle *handle) 进行注册,一个 handle 表示一对匹配上的 input_dev 和 input_handler,保存有指定他两个的指针(input_handle 的存在并没有太大意义)。

shell@tiny4412:/dev/input # ls -l
total 0
crw-rw----    1 0        1004       13,  64 Jan  1 12:00 event0
crw-rw----    1 0        1004       13,  65 Jan  1 12:00 event1
crw-rw----    1 0        1004       13,  66 Jan  1 12:00 event2
crw-rw----    1 0        1004       13,  67 Jan  1 12:00 event3
crw-rw----    1 0        1004       13,  68 Jan  1 12:00 event4
crw-rw----    1 0        1004       13,  63 Jan  1 12:00 mice
crw-rw----    1 0        1004       13,  32 Jan  1 12:00 mouse0

由上可知,所有的输入设备的主设备号为相同,次设备号来区分对应的是哪个handler(匹配的id_table中的id也不同,也就是是不同的device),eventX对应的handler是evdev.c(也不一定,cat /proc/bus/input/handlers 不一定有evdev),mice和mouse0对应的是mousedev.c,靠次设备号区分的。

3. 对于eventX设备节点来说,对应的是evdev.c, 设备驱动中使用input_sync()时,evdev.c中阻塞的App会被唤醒,然后App从缓冲区中读取数据。

input_sync
    input_event(dev, EV_SYN, SYN_REPORT, 0);
        input_handle_event(dev, type, code, value);
            input_pass_event(dev, type, code, value);
                handler->event(handle, type, code, value);
                    evdev_event //evdev.c
                        wake_up_interruptible(&evdev->wait); //唤醒阻塞在读的App

4. evdev.c提供的是原始的数据的读写接口,原始的数据Android中只使用了它。mousedev.c、keyboard.c是加工后的数据,可以使用/dev/mouseX来获取鼠标加工后的数据。

 

二、单点触摸模拟器驱动

1. 驱动上报事件到 evtest.c 中的缓冲中,当缓冲中有数据的时候就会唤醒读进程。由于是模拟的驱动,不会上报事件,因此我我们是用App向缓冲中直接写入按键事件数据来模拟事件的上报,App可用Android自带的 sendevent 程序向 eventX 中写入数据,触发 evdev_write() 调用来向缓存区写数据。

2. 输入设备驱动中需要为Android构造一些VID/PID和name信息,因为Linux上报的扫描码转换为 AKEYCODE 码时需要根据name来加载配置文件,配置文件分为.idc .ly .kcm文件。其中后两个与按键KEY事件有关,第一个与Touch事件有关。详情见:https://source.android.com/devices/input/key-layout-files

3. 驱动Demo

/* 参考: drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/input.h>


static struct input_dev *input_emulator_dev;

static int input_emulator_init(void)
{
    int i;

    /* 1.分配一个设备结构体 */
    input_emulator_dev = input_allocate_device();;

    /* 2.设置 */
    /* 2.1 能产生哪类事件 */
    set_bit(EV_KEY, input_emulator_dev->evbit); /*按键事件*/
    set_bit(EV_REP, input_emulator_dev->evbit); /*连续按着不松手循环产生按键事件*/

    /* 2.2 设置能产生所有按键事件(参考sysrq.c中的设置更优雅) */
    for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++)
        input_emulator_dev->keybit[i] = ~0UL;

    /*
     * 2.3 为Android构造一些设备信息,通过它来寻找映射,见:
     * https://source.android.com/devices/input/key-layout-files
     */
    input_emulator_dev->name = "InputEmulator";
    input_emulator_dev->id.bustype = 1;
    input_emulator_dev->id.vendor  = 0x1234;
    input_emulator_dev->id.product = 0x5678;
    input_emulator_dev->id.version = 1;

    /* 3. 注册 */
    input_register_device(input_emulator_dev);

    return 0;
}

static void input_emulator_exit(void)
{
    input_unregister_device(input_emulator_dev);
    input_free_device(input_emulator_dev);
}

module_init(input_emulator_init);
module_exit(input_emulator_exit);
MODULE_LICENSE("GPL");

Android中编译驱动模块的方法和Linux中一样,Makefile文件:

Android输入系统(2)——input驱动分析(Multi-Touch,配置文件)
KERN_DIR = /media/ubuntu/works/tiny4412/linux-3.0.86

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += InputEmulator.o
View Code

相关文章:

  • 2021-11-15
  • 2021-11-02
  • 2021-05-06
  • 2022-12-23
  • 2021-04-15
  • 2021-11-25
  • 2022-02-07
猜你喜欢
  • 2021-10-15
  • 2022-12-23
  • 2021-07-24
  • 2021-07-20
  • 2021-06-21
  • 2022-02-07
  • 2021-09-14
相关资源
相似解决方案