Linux USB 输入子系统,第一部分
Linux USB 输入子系统是一种统一的方式来管理所有输入设备。对于 Linux 来说,这是一种相对较新的方法,该系统部分整合在内核版本 2.4 中,并在 2.5 开发系列中完全集成。
本文涵盖四个基本领域:输入子系统作用的描述、关于开发的简短历史回顾、内核中输入子系统的实现描述,以及用户空间 API 的概述,以及如何在程序中使用它。本文讨论前三个领域。用户空间 API,即最后一个主题,将在本文的第二部分中讨论。
输入子系统是 Linux 内核的一部分,它管理各种输入设备(例如键盘、鼠标、操纵杆、平板电脑和各种其他设备),用户使用这些设备与内核、命令行和图形用户界面进行交互。该子系统包含在内核中,因为这些设备通常通过特殊的硬件接口(例如串行端口、PS/2 端口、Apple Desktop Bus 和通用串行总线)访问,这些接口受到内核的保护和管理。然后,内核通过一系列定义的 API,以一致的、设备无关的方式向用户空间公开用户输入。
Linux 输入子系统主要是 Vojtech Pavlik 的工作成果,他从早期为 Linux 开发操纵杆支持以及后来支持 USB 的工作中看到了对灵活输入系统的需求。输入子系统的首次集成取代了 2.3 开发内核系列中现有的操纵杆和 USB 驱动程序。这种支持延续到了 2.4 版本,并且 2.4 系列中的输入支持基本上仅限于操纵杆和 USB 输入设备。
2.5 开发内核系列完全集成了输入子系统。本教程基于完全集成,这将是 2.6 稳定内核的输入 API。虽然在本文撰写时,2.4 和 2.5 内核之间的用户空间 API 存在一些差异,但正在进行的工作是为了协调它们——主要是通过更新 2.4 内核。
输入子系统的三个要素是 输入核心、驱动程序 和 事件处理程序。它们之间的关系如图 1 所示。请注意,虽然正常的路径是从底层硬件到驱动程序,驱动程序到输入核心,输入核心到处理程序,处理程序到用户空间,但通常也存在返回路径。此返回路径允许执行诸如设置键盘上的 LED 和为力反馈操纵杆提供运动命令之类的操作。两个方向都使用相同的事件定义,但 type 标识符不同。
各种要素之间的交互是通过 事件 进行的,事件以结构体的形式实现(见列表 1)。第一个字段 (time) 是一个简单的时间戳,而其他字段更有趣。type 字段显示了正在报告的事件的通用类型,例如,按键或按钮按下、相对运动(如移动鼠标)或绝对运动(如移动操纵杆)。code 字段告诉您正在操作哪个按钮或轴,而 value 字段告诉您状态或运动是什么。例如,如果 type 是按键或按钮,则 code 告诉您它是哪个按键或按钮,而 value 告诉您按钮是否被按下或释放。类似地,如果 type 是相对轴,则 code 告诉您是哪个轴,而 value 告诉您在该轴上有多少运动以及运动的方向。如果您沿对角线方向移动鼠标,同时移动滚轮,您将在每次更新中获得三个相对事件:一个用于垂直方向(Y 轴)的运动,一个用于水平方向(X 轴)的运动,以及一个用于滚轮的运动。
事件处理程序提供与用户空间的接口,将标准事件格式转换为特定 API 所需的格式。处理程序通常也负责设备节点(/dev 条目)。最常见的处理程序是键盘处理程序,它是大多数程序员(尤其是 C 程序员)熟悉的“标准输入”。
驱动程序通常与底层硬件接口,例如 USB、PCI 内存或 I/O 区域,或串行端口 I/O 区域。它们在将用户输入的底层硬件版本发送到输入核心之前,将其转换为标准事件格式。输入核心使用标准的内核插件设计,input_register_device() 用于添加每个设备,input_unregister_device() 用于删除它。这些调用的参数是 input_dev 结构体,如列表 1 所示。虽然此结构体看起来相当大,但大多数条目都用于允许驱动程序指定设备的功能,例如设备可以发送或接收哪些事件类型和代码。
除了管理驱动程序和处理程序之外,输入核心还导出一个有用的 /proc 文件系统接口,可用于查看当前活动的设备和处理程序。以下是来自 /proc/bus/input/devices 的一个典型示例,显示了 USB 鼠标
I: Bus=0003 Vendor=046d Product=c002 Version=0120 N: Name="Logitech USB-PS/2 Mouse M-BA47" P: Phys=usb-00:01.2-2.2/input0 H: Handlers=mouse0 event2 B: EV=7 B: KEY=f0000 0 0 0 0 0 0 0 0 B: REL=103
I: 行是身份信息——显示总线类型 3(即 USB)以及鼠标中 USB 描述符中的供应商、产品和版本信息。N: 行显示名称,在本例中是由 USB 描述符提供的字符串。P: 行显示物理设备信息;这里,它是结构信息,由 USB 控制器的 PCI 地址、USB 树和输入接口组成。input0 部分表示这是物理设备的第一个逻辑输入设备。某些设备(例如多媒体键盘)可以将物理设备的一部分映射到一个逻辑输入设备,并将另一部分映射到第二个逻辑输入设备。H: 行显示与此设备关联的处理程序驱动程序;我们将在本文后面更详细地讨论这一点。各种 B: 行显示了标识设备功能的位字段,在本例中,一些用于按钮的按键和用于滚球和滚轮的相对轴。
此 /proc 接口是测试一些简单驱动程序的有用方法。让我们考虑一个驱动程序的示例,该驱动程序在 init 上注册并在删除时注销,如列表 2 所示。这使用 init_input_dev() 进行一些初步初始化。它设置名称、物理和标识描述符,然后设置位数组以指示设备能够提供一种类型的事件(EV_KEY 表示按钮和按键),以及两个可能的代码(KEY_A 和 KEY_B,表示按键标签)。然后,初始化例程向输入核心注册设备。如果您将此代码添加到内核(使用 modprobe),您可以看到新设备已添加到 /proc/bus/input/devices,如下所示
I: Bus=0019 Vendor=0001 Product=0001 Version=0100 N: Name="Example 1 device" P: Phys=A/Fake/Path H: Handlers=kbd event3 B: EV=3 B: KEY=10000 40000000
如果我们实际上想要从设备驱动程序向输入核心发送事件,我们需要调用 input_event 或便利包装器之一,例如 <linux/input.h> 中提供的 input_report_key 或 input_report_abs。列表 3 中显示了一个执行此操作的代码示例。此示例与前一个示例基本相同,只是我们添加了一个调用 ex2_timeout() 的计时器。这个新的例程发送四个 KEY_A 按下事件和四个 KEY_B 按下事件。请注意,总共创建了 16 个按键按下事件,因为每次按下和每次释放都会创建一个单独的事件。这些事件被传递到输入核心,然后传递到键盘处理程序,这将导致模式“aaaabbbb”或“AAAABBBB”(取决于 Shift 键的选择)传输到控制台或命令行。然后将计时器设置为在四秒后运行,无限循环。四秒的延迟旨在为您提供足够的时间在您看到足够的模式后删除模块。如果您减少延迟,请确保您有另一种访问系统的方式,例如 SSH 连接。另请注意对 input_sync 函数的调用。此函数用于通知事件处理程序(在本例中为键盘处理程序)设备已传输内部一致的数据集。处理程序可以选择缓冲事件,直到调用 input_sync。
让我们看一下驱动程序的最后一个示例,这次展示如何提供相对信息,如列表 4 所示。此示例是一个模拟鼠标的驱动程序。初始设置将设备配置为具有两个相对轴(REL_X 和 REL_Y)和一个按键(BTN_LEFT)。与之前的示例一样,我们使用计时器来运行 ex3_timeout。然后,此计时器调用 input_report_rel 以提供小的相对运动(五个单位步长——相对运动是函数的第三个参数),包括向右 30 步、向下 30 步、向左 30 步和向上 30 步,因此光标以正方形模式移动。为了提供运动的错觉,超时时间仅为 20 毫秒。再次注意对 input_sync 的调用,它用于确保输入处理程序仅处理构成一致事件集的事件。此规范在我们之前的示例中并非严格必要。但是,它绝对是向输入核心传达诸如相对运动之类的信息所必需的,因为可能需要多个轴来表示运动。如果您沿对角线移动,您将执行类似的操作
... input_report_rel(..., REL_X, ...); input_report_rel(..., REL_Y, ...); input_sync(...); ...
这确保鼠标将沿对角线移动,而不是先横向移动,然后再向上移动。
在上一节中,我们看到设备驱动程序基本上位于硬件和输入核心之间,将硬件事件(通常是中断)转换为输入事件。为了使用这些输入事件,我们使用处理程序,它们提供用户空间接口。
输入子系统包含您可能需要的大多数处理程序:用于提供控制台的键盘处理程序、用于 X Window 系统等应用程序的鼠标处理程序、用于游戏的操纵杆处理程序以及触摸屏处理程序。还有一个通用的处理程序,称为事件处理程序,它基本上向用户空间提供输入事件。这意味着您几乎永远不需要在内核中编写处理程序,因为您可以使用事件处理程序和用户空间中的等效代码来完成相同的事情。本文的第二部分将介绍此 API 讨论。

Brad Hards 是 Sigma Bravo 的技术主管,Sigma Bravo 是一家位于堪培拉的小型专业服务公司。除了 Linux 之外,他的技术重点还包括飞机系统集成和认证、GPS 和电子战。关于本文的评论可以发送至 bradh@frogmouth.net。