输入子系统框架简述

输入子系统框架简述

1 引入输入子系统的目的
2 输入子系统框架
3 输入子系统之核心层简述
4 输入子系统之事件处理层简述
5 输入子系统之驱动层简述

一 引入输入子系统的目的

个人理解是为了 将输入设备的功能直接提供给用户空间,如果按照普通字符设备的方式编写输入设备,那么我们自己的驱动会生成我们自己命名的设备节点,只有我们自己知道设备节点名称,也就是只有我们自己可以打开这个设备节点,此时这种驱动程序只能自己使用。如果想使自己的驱动程序生成的节点成为“公共的”,即生成的节点可以直接被打开使用,各种应用程序都可以调用该节点,那么就引入 “输入子系统”

二 输入子系统框架

输入子系统分为三部分:
1:设备层 matrix_key.c / gpio_keys.c ::和硬件相关的底层驱动(我们需要实现的部分),把底层硬件的输入事件 上报给核心层
2:核心层 input.c   :为 设备驱动层输入设备 以及 事件处理层 提供注册和操作的接口
3:事件处理层 Evdev.c :用于与用户空间进行交互

三 输入子系统之核心层简述

3.1 核心层功能
1创建注册字符设备
2为设备驱动层提供注册操作接口
3为事件处理层提供注册操作接口
4为 设备驱动层 与 事件处理层 提供匹配函数接口
3.2 关键函数举例:
//创建字符设备结构体  : truct file_operations input_handlers_fileops
//注册字符设备  : __init input_init
//创建 proc 文件系统相关  : __init input_proc_init(void)
//用于匹配 底层驱动 与 上层handler (匹配成功则调用 handler的connect函数) :input_attach_handler
//进行 handler 与 device(输入设备) 的匹配  :struct input_device_id *input_match_device
3.3 关键函数说明
//注册字符设备
    static int __init input_init(void)
{
    int err;
//创建设备类
    err = class_register(&input_class); 
    if (err) {
        pr_err("unable to register input_dev class\n");
        return err;
    }
//proc文件系统相关
    err = input_proc_init();
    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;
}


//创建 proc 文件系统相关
static int __init input_proc_init(void)
{
    struct proc_dir_entry *entry;

//在  /proc/bus 目录下创建 input目录
    proc_bus_input_dir = proc_mkdir("bus/input", NULL); 
    if (!proc_bus_input_dir)
        return -ENOMEM;

//在  /proc/bus/input 目录下创建 devices 文件
    entry = proc_create("devices", 0, proc_bus_input_dir,
                &input_devices_fileops);
    if (!entry)
        goto fail1;

//在  /proc/bus/input 目录下创建 handlers 文件
    entry = proc_create("handlers", 0, proc_bus_input_dir,
                &input_handlers_fileops);
    if (!entry)
        goto fail2;

    return 0;

 fail2: remove_proc_entry("devices", proc_bus_input_dir);
 fail1: remove_proc_entry("bus/input", NULL);
    return -ENOMEM;
}


//创建字符设备结构体
static const struct file_operations input_handlers_fileops = {
    .owner      = THIS_MODULE,
    .open       = input_proc_handlers_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = seq_release,
};

//用于匹配 底层驱动 与 上层handler (匹配成功则调用 handler的connect函数)
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;

//进行 handler 与 device(输入设备) 的匹配
id = input_match_device(handler, dev);
if (!id)
    return -ENODEV;

//如果匹配成功 则调用 handler中的connect函数进行连接
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;

//遍历 handler->id_table 中的全部 input_device_id(输入设备ID)
for (id = handler->id_table; id->flags || id->driver_info; id++) {

    if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
        if (id->bustype != dev->id.bustype)
            continue;

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

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

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

    if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
        continue;

    if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
        continue;

    if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
        continue;

    if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
        continue;

    if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
        continue;

    if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
        continue;

    if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
        continue;

    if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
        continue;

    if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
        continue;

    if (!handler->match || handler->match(handler, dev))
        return id;
}

return NULL;
}

3.4 小结:

核心层为 设备驱动层 提供的接口有:
//分配 input_dev 结构体
input_allocate_device(void) :

//设置 输入设备所上报的事件
input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

//向 核心层 注册设备
int input_register_device(struct input_dev *dev)

核心层为 事件层 提供的接口有:

input_register_handler :事件层向核心层注册 handler  (handler 与 input_dev成对应关系)

input_regiser_handle :事件层向核心层注册 handle (不同于 handler,handle是用于记录匹配成功的 handler与input_dev) handle是用于记录匹配成功的 handler与input_dev,可以通过 handle获得 input_dev 以及 handler的信息
核心层关键点1

输入系统注册设备时,设备注册 与 handler注册 后的匹配过过程 和 platform平台有些相似,设备
与 handler 注册时都会各自把 dev 或 handler 挂在各自设备链表 或 事件链表上,然后去遍历对方
的事件链表 或 设备链表,input_dev 与 handler是多对多的关系

核心层关键点2

input_dev_list 链表上的 input_dev设备节点
input_handler_list 链表上的 handler 事件节点
input_dev_list 链表 和 input_handler_list 链表 上对应的节点都会有一个 匹配记录结构体 handle,
用于记录匹配信息,所以 两个链表上的节点 可以通过handle 互相访问;

核心层关键点3

核心层总结:
1创建注册字符设备
2为设备驱动层提供注册操作接口
3为事件处理层提供注册操作接口
4为 设备驱动层 与 事件处理层 提供匹配函数接口

四 输入子系统之事件处理层简述

注意 :此处 事件处理层 只做简单描述,主要是为了加强印象,日后有时间补充。
1 事件处理层描述:
输入子系统上层其实是由多个事件(handler)组成的,各个事件之间的关系是平行关系,互不干扰,用的较多的是event,此处以event为例,事件处理层 属于 input输入子系统上层
2 事件处理层主要框架简述
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 },   /* Matches all devices */
{ },            /* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

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,
};

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

static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

五 输入子系统之驱动层简述

注意:驱动层input输入子系统的工作很简单,主要在 prob函数 和 中断函数中
static int matrix_keypad_probe(struct platform_device *pdev)
{
    //输入设备结构体
    struct input_dev *input_dev; 

    //将会分配一个 input_dev 设备结构体,并且在 /sys/class/input/input-n 下创建设备属性文件
    input_dev = input_allocate_device();

    //初始化  设置 input 输入设备结构体
    input_dev->name     = pdev->name;
    input_dev->id.bustype   = BUS_HOST;
    input_dev->dev.parent   = &pdev->dev;
    input_dev->open     = matrix_keypad_start;
    input_dev->close    = matrix_keypad_stop;

    //注册 input输入设备
    err = input_register_device(keypad->input_dev);
}

    //上报事件
    XXXX_interrupt
{
    //提交输入事件
    input_event(input_dev, EV_MSC, MSC_SCAN, code);

    //提交按键值
    input_report_key(input_dev,keycodes[code],new_state[col] & (1 << row));

    //同步
    input_sync(input_dev);
}
输入系统大致流程:
设备驱动 --> 核心层 --> 事件处理层 --> 用户空间

猜你喜欢

转载自blog.csdn.net/linuxarmbiggod/article/details/80196046