6.1. 简介

LIRC 代表 Linux 红外遥控。LIRC 设备接口是一个双向接口,用于在用户空间和内核空间之间传输原始 IR 和解码的扫描码数据。从根本上说,它只是一个字符设备(/dev/lircX,X = 0, 1, 2, ...),在其上定义了许多标准的 struct file_operations。关于来回传输原始 IR 和解码的扫描码,基本的操作是 read、write 和 ioctl。

还可以将 BPF 程序附加到 LIRC 设备,以将原始 IR 解码为扫描码。

驱动程序注册 LIRC 时的示例 dmesg 输出

$ dmesg |grep lirc_dev
rc rc0: lirc_dev: driver mceusb registered at minor = 0, raw IR receiver, raw IR transmitter

您应该看到的字符设备

$ ls -l /dev/lirc*
crw-rw---- 1 root root 248, 0 Jul 2 22:20 /dev/lirc0

请注意,v4l-utils 软件包包含用于处理 LIRC 设备的工具

  • ir-ctl:可以接收原始 IR 并发送 IR,以及查询 LIRC 设备功能。

  • ir-keytable:可以加载键映射;允许您设置 IR 内核协议;加载 BPF IR 解码器并测试 IR 解码。还提供了一些 BPF IR 解码器。

6.2. LIRC 模式

LIRC 支持一些接收和发送 IR 代码的模式,如下表所示。

LIRC_MODE_SCANCODE

此模式用于发送和接收 IR。

对于发送(又名传输),创建一个 struct lirc_scancode,其中 scancode 成员中设置所需的扫描码,rc_proto 设置为 IR 协议,所有其他成员设置为 0。将此结构写入 lirc 设备。

对于接收,您从 LIRC 设备读取 struct lirc_scancodescancode 字段设置为接收到的扫描码,并且 IR 协议rc_proto 中设置。如果扫描码映射到有效的键代码,则将其设置在 keycode 字段中,否则将其设置为 KEY_RESERVED

如果支持切换位的协议(例如 rc-5 和 rc-6)中设置了切换位,则 flags 可以设置 LIRC_SCANCODE_FLAG_TOGGLE,或者当接收到支持该协议的重复时(例如 nec),可以设置 LIRC_SCANCODE_FLAG_REPEAT

在 Sanyo 和 NEC 协议中,如果您按住遥控器上的按钮,则遥控器不会重复整个扫描码,而是发送一个较短的消息,其中不包含扫描码,这只是意味着按钮被按住,即“重复”。当接收到此消息时,将设置 LIRC_SCANCODE_FLAG_REPEAT,并重复扫描码和键代码。

使用 nec,无法区分“按钮保持”和“重复按下同一按钮”。rc-5 和 rc-6 协议具有切换位。当释放并再次按下按钮时,切换位会反转。如果设置了切换位,则设置 LIRC_SCANCODE_FLAG_TOGGLE

timestamp 字段填充了解码扫描码时的纳秒时间(以 CLOCK_MONOTONIC 为单位)。

LIRC_MODE_MODE2

驱动程序将脉冲和空间代码的序列作为一系列 u32 值返回给用户空间。

此模式仅用于 IR 接收。

高 8 位确定数据包类型,低 24 位确定有效负载。使用 LIRC_VALUE() 宏获取有效负载,宏 LIRC_MODE2() 将为您提供类型,它是以下之一

LIRC_MODE2_PULSE

表示以微秒为单位的 IR 的存在,也称为闪光

LIRC_MODE2_SPACE

表示以微秒为单位的 IR 不存在,也称为间隔

LIRC_MODE2_FREQUENCY

如果使用 ioctl LIRC_SET_MEASURE_CARRIER_MODE 启用了载波频率的测量,则此数据包会提供载波频率(以赫兹为单位)。

LIRC_MODE2_TIMEOUT

当由于未检测到 IR 而导致使用 ioctl LIRC_GET_REC_TIMEOUT 和 LIRC_SET_REC_TIMEOUT 设置的超时过期时,将发送此数据包,其中包含没有 IR 的微秒数。

LIRC_MODE2_OVERFLOW

表示 IR 接收器遇到溢出,并且缺少某些 IR。此后的 IR 数据应再次正确。实际值并不重要,但是内核将其设置为 0xffffff 以与 lircd 兼容。

LIRC_MODE_PULSE

在脉冲模式下,一系列脉冲/空间整数值使用 LIRC write() 写入 lirc 设备。

这些值是以微秒为单位的交替脉冲和空间长度。第一个和最后一个条目必须是脉冲,因此必须有奇数个条目。

此模式仅用于 IR 发送。

6.3. LIRC_MODE_SCANCODE 使用的数据类型

struct lirc_scancode

解码的扫描码和协议,用于 LIRC_MODE_SCANCODE

定义:

struct lirc_scancode {
    __u64 timestamp;
    __u16 flags;
    __u16 rc_proto;
    __u32 keycode;
    __u64 scancode;
};

成员

timestamp

解码 IR 时,使用 CLOCK_MONOTONIC 的纳秒时间戳。

flags

传输时应为 0。当接收扫描码时,可以根据协议设置 LIRC_SCANCODE_FLAG_TOGGLE 或 LIRC_SCANCODE_FLAG_REPEAT

rc_proto

请参阅 enum rc_proto

keycode

转换的键代码。对于传输,设置为 0。

scancode

接收或要发送的扫描码

enum rc_proto

遥控器协议

常量

RC_PROTO_UNKNOWN

协议未知

RC_PROTO_OTHER

协议已知但专有

RC_PROTO_RC5

Philips RC5 协议

RC_PROTO_RC5X_20

Philips RC5x 20 位协议

RC_PROTO_RC5_SZ

RC5 的 StreamZap 变体

RC_PROTO_JVC

JVC 协议

RC_PROTO_SONY12

Sony 12 位协议

RC_PROTO_SONY15

Sony 15 位协议

RC_PROTO_SONY20

Sony 20 位协议

RC_PROTO_NEC

NEC 协议

RC_PROTO_NECX

扩展 NEC 协议

RC_PROTO_NEC32

NEC 32 位协议

RC_PROTO_SANYO

Sanyo 协议

RC_PROTO_MCIR2_KBD

类似 RC6 的 MCE 键盘

RC_PROTO_MCIR2_MSE

类似 RC6 的 MCE 鼠标

RC_PROTO_RC6_0

Philips RC6-0-16 协议

RC_PROTO_RC6_6A_20

Philips RC6-6A-20 协议

RC_PROTO_RC6_6A_24

Philips RC6-6A-24 协议

RC_PROTO_RC6_6A_32

Philips RC6-6A-32 协议

RC_PROTO_RC6_MCE

MCE (Philips RC6-6A-32 子类型) 协议

RC_PROTO_SHARP

Sharp 协议

RC_PROTO_XMP

XMP 协议

RC_PROTO_CEC

CEC 协议

RC_PROTO_IMON

iMon Pad 协议

RC_PROTO_RCMM12

RC-MM 协议 12 位

RC_PROTO_RCMM24

RC-MM 协议 24 位

RC_PROTO_RCMM32

RC-MM 协议 32 位

RC_PROTO_XBOX_DVD

Xbox DVD 电影播放套件协议

RC_PROTO_MAX

enum rc_proto 的最大值

6.4. 基于 BPF 的 IR 解码器

内核支持解码最常见的红外协议,但仍有许多协议不支持。为了支持这些协议,可以加载一个 BPF 程序来进行解码。这只能在支持读取原始红外数据的 LIRC 设备上进行。

首先,使用带有 BPF_LOAD_PROG 参数的 bpf(2) 系统调用,必须加载类型为 BPF_PROG_TYPE_LIRC_MODE2 的程序。一旦附加到 LIRC 设备,该程序将在 LIRC 设备上的每次脉冲、间隔或超时事件时被调用。BPF 程序的上下文是指向无符号整数的指针,该整数是一个 LIRC_MODE_MODE2 值。当程序解码了扫描码后,可以使用 BPF 函数 bpf_rc_keydown()bpf_rc_repeat() 来提交。可以使用 bpf_rc_pointer_rel() 来报告鼠标或指针的移动。

一旦你获得了 BPF_PROG_TYPE_LIRC_MODE2 BPF 程序的文件描述符,就可以使用 bpf(2) 系统调用将其附加到 LIRC 设备。目标必须是 LIRC 设备的文件描述符,并且附加类型必须是 BPF_LIRC_MODE2。一次最多可以向单个 LIRC 设备附加 64 个 BPF 程序。