4. 遥控器设备

4.1. 遥控器核心

遥控器核心实现了接收和发送遥控器键盘按键和鼠标事件的基础设施。

每次按下遥控器上的按键时,都会产生一个扫描码。此外,在大多数硬件上,持续按住一个按键超过几十毫秒会产生一个重复按键事件。这有点类似于 Linux 内部处理普通键盘或鼠标的方式[1]。因此,遥控器核心是在 Linux input/evdev 接口之上实现的。

然而,大多数遥控器使用红外 (IR) 来传输信号。由于有几种协议用于调制红外信号,核心的一个重要部分专门用于调整驱动程序和核心系统,以支持发射器使用的红外协议。

红外传输是通过使用载波闪烁红外发射器来完成的。载波可以通过 IR 发射器硬件打开或关闭。当载波打开时,称为脉冲。当载波关闭时,称为空间

换句话说,典型的红外传输可以看作是一系列脉冲空间事件,每个事件都有给定的持续时间。

载波参数(频率、占空比)以及脉冲空间事件的间隔取决于协议。例如,NEC 协议使用 38kHz 的载波,传输以 9ms 脉冲和 4.5ms 空间开始。然后它传输 16 位扫描码,其中 8 位用于地址(通常对于给定的遥控器来说是一个固定的数字),后跟 8 位代码。“1”位用 560µs 脉冲后跟 1690µs 空间调制,“0”位用 560µs 脉冲后跟 560µs 空间调制。

在接收器端,可以使用简单的低通滤波器将接收到的信号转换为一系列脉冲/空间事件,从而滤除载波频率。因此,接收器并不关心载波的实际频率参数:它所要做的就是测量它接收到脉冲/空间事件的时间量。因此,一个简单的 IR 接收器硬件将只向内核提供这些事件的时序序列。具有此类接收器的硬件的驱动程序由 RC_DRIVER_IR_RAW 标识,如 rc_driver_type[2] 中定义的那样。其他硬件配备了一个微控制器,可以解码脉冲/空间序列并将扫描码返回给内核。这种接收器由 RC_DRIVER_SCANCODE 标识。

当 RC 核心接收到由 RC_DRIVER_IR_RAW IR 接收器产生的事件时,它需要解码 IR 协议,以便获得相应的扫描码。RC 核心支持的协议在枚举 rc_proto 中定义。

当 RC 代码接收到扫描码时(无论是直接通过 RC_DRIVER_SCANCODE 类型的驱动程序,还是通过其 IR 解码器),它需要将其转换为 Linux 输入事件代码。这是通过映射表完成的。

内核支持大多数媒体设备上可用的映射表。它还支持通过一些 sysfs 节点在运行时加载表。有关更多详细信息,请参见 RC 用户空间 API

4.1.1. 遥控器数据结构和函数

enum rc_driver_type

RC 驱动程序的类型。

常量

RC_DRIVER_SCANCODE

驱动程序或硬件生成扫描码。

RC_DRIVER_IR_RAW

驱动程序或硬件生成脉冲/空间序列。它需要一个红外脉冲/空间解码器

RC_DRIVER_IR_RAW_TX

仅设备发射器,驱动程序需要脉冲/空间数据序列。

struct rc_scancode_filter

过滤扫描码。

定义:

struct rc_scancode_filter {
    u32 data;
    u32 mask;
};

成员

data

要匹配的扫描码数据。

mask

要比较的扫描码位的掩码。

enum rc_filter_type

过滤器类型常量。

常量

RC_FILTER_NORMAL

用于正常操作的过滤器。

RC_FILTER_WAKEUP

用于从挂起状态唤醒的过滤器。

RC_FILTER_MAX

过滤器类型的数量。

struct lirc_fh

表示一个打开的 lirc 文件

定义:

struct lirc_fh {
    struct list_head list;
    struct rc_dev *rc;
    unsigned int *rawir;
    struct lirc_scancode *scancodes;
    wait_queue_head_t wait_poll;
    u32 carrier_low;
    u8 send_mode;
    u8 rec_mode;
};

成员

list

打开的文件句柄列表

rc

此 lirc 字符设备的 rcdev

rawir

传入原始 IR 的队列

scancodes

传入解码的扫描码的队列

wait_poll

lirc 设备的轮询结构

carrier_low

设置载波范围时,必须先使用 ioctl 设置低端,然后使用另一个 ioctl 设置高端

send_mode

发送的 lirc 模式,可以是 LIRC_MODE_SCANCODE 或 LIRC_MODE_PULSE

rec_mode

接收的 lirc 模式,可以是 LIRC_MODE_SCANCODE 或 LIRC_MODE_MODE2

struct rc_dev

表示遥控设备

定义:

struct rc_dev {
    struct device                   dev;
    bool managed_alloc;
    bool registered;
    bool idle;
    bool encode_wakeup;
    unsigned int                    minor;
    const struct attribute_group    *sysfs_groups[5];
    const char                      *device_name;
    const char                      *input_phys;
    struct input_id                 input_id;
    const char                      *driver_name;
    const char                      *map_name;
    struct rc_map                   rc_map;
    struct mutex                    lock;
    struct ir_raw_event_ctrl        *raw;
    struct input_dev                *input_dev;
    enum rc_driver_type             driver_type;
    u32 users;
    u64 allowed_protocols;
    u64 enabled_protocols;
    u64 allowed_wakeup_protocols;
    enum rc_proto                   wakeup_protocol;
    struct rc_scancode_filter       scancode_filter;
    struct rc_scancode_filter       scancode_wakeup_filter;
    u32 scancode_mask;
    void *priv;
    spinlock_t keylock;
    bool keypressed;
    u8 last_toggle;
    u32 last_keycode;
    enum rc_proto                   last_protocol;
    u64 last_scancode;
    unsigned long                   keyup_jiffies;
    struct timer_list               timer_keyup;
    struct timer_list               timer_repeat;
    u32 timeout;
    u32 min_timeout;
    u32 max_timeout;
    u32 rx_resolution;
#ifdef CONFIG_LIRC;
    struct device                   lirc_dev;
    struct cdev                     lirc_cdev;
    ktime_t gap_start;
    spinlock_t lirc_fh_lock;
    struct list_head                lirc_fh;
#endif;
    int (*change_protocol)(struct rc_dev *dev, u64 *rc_proto);
    int (*open)(struct rc_dev *dev);
    void (*close)(struct rc_dev *dev);
    int (*s_tx_mask)(struct rc_dev *dev, u32 mask);
    int (*s_tx_carrier)(struct rc_dev *dev, u32 carrier);
    int (*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle);
    int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max);
    int (*tx_ir)(struct rc_dev *dev, unsigned *txbuf, unsigned n);
    void (*s_idle)(struct rc_dev *dev, bool enable);
    int (*s_wideband_receiver)(struct rc_dev *dev, int enable);
    int (*s_carrier_report) (struct rc_dev *dev, int enable);
    int (*s_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
    int (*s_wakeup_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
    int (*s_timeout)(struct rc_dev *dev, unsigned int timeout);
};

成员

dev

驱动程序模型对此设备的看法

managed_alloc

使用 devm_rc_allocate_device 创建 rc_dev

registered

rc_register_device() 设置为 true,由 rc_unregister_device 设置为 false

idle

用于跟踪 RX 状态

encode_wakeup

唤醒过滤使用 IR 编码 API,因此允许的唤醒协议是所有原始编码器的集合

minor

唯一的次要遥控设备号

sysfs_groups

sysfs 属性组

device_name

rc 子设备的名称

input_phys

输入子设备的物理路径

input_id

输入子设备的 ID(结构 input_id)

driver_name

注册此设备的硬件驱动程序的名称

map_name

默认键映射的名称

rc_map

当前扫描/按键表

lock

用于确保我们在任何人调用 show_protocols 或 store_protocols 之前填写了所有协议详细信息

raw

原始脉冲/空间设备的附加数据

input_dev

用于将事件传达给用户空间的输入子设备

driver_type

指定协议解码是在硬件还是软件中完成

users

设备的当前用户数

allowed_protocols

带有支持的 RC_PROTO_BIT_* 协议的位掩码

enabled_protocols

带有启用的 RC_PROTO_BIT_* 协议的位掩码

allowed_wakeup_protocols

带有支持的 RC_PROTO_BIT_* 唤醒协议的位掩码

wakeup_protocol

启用的 RC_PROTO_* 唤醒协议,如果禁用,则为 RC_PROTO_UNKNOWN。

scancode_filter

扫描码过滤器

scancode_wakeup_filter

扫描码唤醒过滤器

scancode_mask

某些硬件解码器无法向应用程序提供完整的扫描码。由于这是一个硬件限制,我们对此无能为力。然而,由于相同的按键代码表可以与其他设备一起使用,因此提供了一个掩码以允许其使用。驱动程序通常应将此字段留空

priv

驱动程序特定数据

keylock

保护结构的其余成员

keypressed

当前是否按下某个按键

last_toggle

最后一个命令的切换值

last_keycode

上次按键的按键代码

last_protocol

上次按键的协议

last_scancode

上次按键的扫描码

keyup_jiffies

应释放当前按键的时间(以 jiffies 为单位)

timer_keyup

用于释放按键的计时器

timer_repeat

用于自动重复事件的计时器。这是 CEC 所必需的,CEC 具有非标准重复。

timeout

设备停止发送数据后的可选时间

min_timeout

设备支持的最小超时

max_timeout

设备支持的最大超时

rx_resolution

输入采样器的分辨率(以微秒为单位)

lirc_dev

lirc 设备

lirc_cdev

lirc 字符 cdev

gap_start

如果非零,则超时后间隙的开始时间

lirc_fh_lock

保护 lirc_fh 列表

lirc_fh

打开的文件列表

change_protocol

允许更改硬件解码器上使用的协议

open

允许驱动程序在打开 IR 输入设备时启用轮询/irq 的回调。

close

允许驱动程序在打开 IR 输入设备时禁用轮询/irq 的回调。

s_tx_mask

设置发射器掩码(对于具有多个 tx 输出的设备)

s_tx_carrier

设置发射载波频率

s_tx_duty_cycle

设置发射占空比 (0% - 100%)

s_rx_carrier_range

通知驱动程序它应该处理的载波

tx_ir

传输红外

s_idle

启用/禁用硬件空闲模式,在此模式下,设备不会中断主机,直到它看到 IR 脉冲

s_wideband_receiver

启用用于学习的宽带接收器

s_carrier_report

启用载波报告

s_filter

设置扫描码过滤器

s_wakeup_filter

设置唤醒扫描码过滤器。如果掩码为零,则应禁用唤醒。如果掩码非零,则 wakeup_protocol 将设置为有效协议。

s_timeout

以微秒为单位设置硬件超时

struct rc_dev *rc_allocate_device(enum rc_driver_type)

分配一个 RC 设备

参数

enum rc_driver_type

指定要分配的 RC 输出类型,返回指向 struct rc_dev 的指针。

struct rc_dev *devm_rc_allocate_device(struct device *dev, enum rc_driver_type)

托管 RC 设备分配

参数

struct device *dev

指向 struct device 的指针

enum rc_driver_type

指定要分配的 RC 输出类型,返回指向 struct rc_dev 的指针。

void rc_free_device(struct rc_dev *dev)

释放一个 RC 设备

参数

struct rc_dev *dev

指向 struct rc_dev 的指针。

int rc_register_device(struct rc_dev *dev)

注册一个 RC 设备

参数

struct rc_dev *dev

指向 struct rc_dev 的指针。

int devm_rc_register_device(struct device *parent, struct rc_dev *dev)

管理 RC 设备的注册

参数

struct device *parent

指向 struct device 的指针。

struct rc_dev *dev

指向 struct rc_dev 的指针。

void rc_unregister_device(struct rc_dev *dev)

注销一个 RC 设备

参数

struct rc_dev *dev

指向 struct rc_dev 的指针。

struct rc_map_table

表示扫描码/按键代码对

定义:

struct rc_map_table {
    u64 scancode;
    u32 keycode;
};

成员

scancode

扫描码 (u64)

keycode

Linux 输入按键代码

struct rc_map

表示按键代码映射表

定义:

struct rc_map {
    struct rc_map_table     *scan;
    unsigned int            size;
    unsigned int            len;
    unsigned int            alloc;
    enum rc_proto           rc_proto;
    const char              *name;
    spinlock_t lock;
};

成员

scan

指向结构 rc_map_table 的指针

size

最大条目数

len

正在使用的条目数

alloc

*scan 的大小(以字节为单位)

rc_proto

遥控器协议的类型,如枚举 rc_proto 中定义

name

按键映射表的名称

lock

用于保护对此结构的访问的锁

struct rc_map_list

已注册的 rc_map 映射的列表

定义:

struct rc_map_list {
    struct list_head         list;
    struct rc_map map;
};

成员

list

指向结构 list_head 的指针

map

指向结构 rc_map 的指针

int rc_map_register(struct rc_map_list *map)

注册一个遥控器扫描码映射

参数

struct rc_map_list *map

指向 struct rc_map_list 的指针

void rc_map_unregister(struct rc_map_list *map)

注销一个遥控器扫描码映射

参数

struct rc_map_list *map

指向 struct rc_map_list 的指针

struct rc_map *rc_map_get(const char *name)

从其名称获取一个 RC 映射

参数

const char *name

RC 扫描码映射的名称