Linux启动——初始化input子系统

在Linux内核中,input.c(位于drivers/input/目录)是输入子系统的核心文件,负责管理键盘、鼠标、触摸屏等输入设备。它的启动和初始化过程与内核启动流程紧密相关。以下是详细解析:

1. 输入子系统概述

Linux输入子系统采用分层架构:

  • 设备驱动层:硬件设备的具体驱动(如键盘、鼠标驱动)。
  • 核心层input.c实现的核心逻辑,管理设备注册与事件处理。
  • 事件处理层:将输入事件传递给用户空间(如evdevjoydev)。

2. input.c的初始化流程

2.1 内核编译阶段
  • 配置选项:通过CONFIG_INPUT内核配置选项启用输入子系统。
  • 模块化支持:部分驱动可编译为模块(如CONFIG_INPUT_MOUSE)。
2.2 内核启动阶段

input.c的初始化始于内核启动时的子系统注册过程:

  1. input_init()函数调用
    start_kernel()(内核主初始化函数)中,会调用input_init()注册输入子系统:

    // init/main.c
    asmlinkage __visible void __init start_kernel(void)
    {
        // ...
        input_init();  // 初始化输入子系统
        // ...
    }
    
  2. input_init()实现
    input_init()位于drivers/input/input.c,主要完成:

    // drivers/input/input.c
    static int __init input_init(void)
    {
        int err;
        
        // 创建输入子系统的sysfs目录
        input_class = class_create(THIS_MODULE, "input");
        if (IS_ERR(input_class))
            return PTR_ERR(input_class);
        
        // 注册输入子系统的设备类型
        input_class->devnode = input_devnode;
        
        // 初始化输入设备列表和事件队列
        INIT_LIST_HEAD(&input_dev_list);
        init_rwsem(&input_mutex);
        
        // 注册input核心到sysfs
        err = bus_register(&input_bus_type);
        if (err)
            goto fail1;
        
        // 注册输入设备的属性
        err = input_register_handle_types();
        if (err)
            goto fail2;
        
        // 初始化输入子系统的工作队列
        input_wq = alloc_workqueue("input", WQ_HIGHPRI, 0);
        if (!input_wq) {
            err = -ENOMEM;
            goto fail3;
        }
        
        return 0;
        
    fail3:
        input_unregister_handle_types();
    fail2:
        bus_unregister(&input_bus_type);
    fail1:
        class_destroy(input_class);
        return err;
    }
    subsys_initcall(input_init);  // 标记为子系统初始化函数
    
  3. 设备驱动注册
    输入设备驱动(如键盘、鼠标驱动)在初始化时会向输入子系统注册:

    // 示例:键盘驱动注册
    static int __init keyboard_init(void)
    {
        struct input_dev *dev;
        
        // 分配输入设备结构体
        dev = input_allocate_device();
        if (!dev)
            return -ENOMEM;
        
        // 设置设备属性(如支持的事件类型)
        dev->name = "My Keyboard";
        dev->id.bustype = BUS_USB;
        dev->id.vendor  = 0x1234;
        dev->id.product = 0x5678;
        
        // 设置设备支持的按键
        set_bit(KEY_A, dev->keybit);
        set_bit(KEY_B, dev->keybit);
        // ...
        
        // 注册设备到输入子系统
        return input_register_device(dev);
    }
    

3. 输入事件处理流程

  1. 硬件触发
    当用户操作输入设备(如按下按键)时,硬件产生中断。

  2. 中断处理
    设备驱动的中断处理函数捕获事件,并通过input_report_key()等函数向输入子系统报告:

    // 键盘中断处理函数示例
    static irqreturn_t keyboard_irq(int irq, void *dev_id)
    {
        struct input_dev *dev = dev_id;
        int key_code = read_key_code();  // 读取按键码
        
        // 报告按键事件
        input_report_key(dev, key_code, 1);  // 1表示按下
        input_sync(dev);  // 同步事件
        
        return IRQ_HANDLED;
    }
    
  3. 事件传递
    输入子系统将事件通过evdev等处理层传递给用户空间:

    • 用户空间通过/dev/input/eventX设备文件读取事件。
    • 应用程序(如Xorg、Wayland)处理事件并更新界面。

4. 输入子系统的关键数据结构

  • struct input_dev
    表示一个输入设备,包含设备信息、支持的事件类型和回调函数。

  • struct input_handle
    连接输入设备和事件处理程序(如evdev)。

  • struct input_handler
    定义事件处理逻辑(如evdev_handler处理通用输入事件)。

5. 总结

input.c的启动是内核初始化的一部分,主要流程为:

  1. 内核启动:调用start_kernel(),其中包含input_init()
  2. 子系统注册input_init()创建sysfs目录、初始化设备列表和工作队列。
  3. 设备驱动注册:各类输入设备驱动向输入子系统注册,声明支持的事件类型。
  4. 事件处理:设备触发事件后,驱动报告给输入子系统,再由处理层传递到用户空间。

理解这一流程有助于开发输入设备驱动或调试输入相关问题。

你可能感兴趣的:(linux,运维,服务器)