6.1. 简介

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

也可以将 BPF 程序附加到 LIRC 设备,用于将原始红外数据解码为扫描码。

驱动程序向 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:可以接收原始红外数据和传输红外数据,以及查询 LIRC 设备功能。

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

6.2. LIRC 模式

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

LIRC_MODE_SCANCODE

此模式用于发送和接收红外数据。

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

对于接收,您可以从 LIRC 设备读取 struct lirc_scancodescancode 字段设置为接收到的扫描码,红外协议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 值返回给用户空间。

此模式仅用于红外接收。

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

LIRC_MODE2_PULSE

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

LIRC_MODE2_SPACE

表示以微秒为单位的红外不存在,也称为间隙

LIRC_MODE2_FREQUENCY

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

LIRC_MODE2_TIMEOUT

由于未检测到红外信号,使用 ioctl LIRC_GET_REC_TIMEOUT 和 LIRC_SET_REC_TIMEOUT 设置的超时到期时,将发送此数据包,其中包含未检测到红外信号的微秒数。

LIRC_MODE2_OVERFLOW

表示红外接收器遇到溢出,并且丢失了一些红外信号。此后的红外数据应再次正确。实际值并不重要,但内核将其设置为 0xffffff 以与 lircd 兼容。

LIRC_MODE_PULSE

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

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

此模式仅用于红外发送。

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

使用 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 的红外解码器

内核支持解码最常见的 红外协议,但有许多协议不受支持。为了支持这些协议,可以加载一个 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。一次最多可以将 64 个 BPF 程序附加到单个 LIRC 设备。