4. 遥控器设备

4.1. 遥控器核心

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

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

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

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

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

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

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

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

当 RC 代码接收到扫描码(直接由 RC_DRIVER_SCANCODE 类型的驱动程序接收,或通过其红外解码器接收)时,它需要将其转换为 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;
    int carrier_low;
    unsigned int *rawir;
    struct lirc_scancode *scancodes;
    wait_queue_head_t wait_poll;
    u8 send_mode;
    u8 rec_mode;
};

成员

list

打开文件句柄的列表

rc

此 lirc 字符设备的 rcdev

carrier_low

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

rawir

用于传入原始 IR 的队列

scancodes

用于传入解码扫描码的队列

wait_poll

lirc 设备的轮询结构

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;
    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;
    unsigned int                    minor;
    struct ir_raw_event_ctrl        *raw;
    struct input_dev                *input_dev;
    enum rc_driver_type             driver_type;
    bool idle;
    bool encode_wakeup;
    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;
    u32 users;
    void *priv;
    spinlock_t keylock;
    bool keypressed;
    unsigned long                   keyup_jiffies;
    struct timer_list               timer_keyup;
    struct timer_list               timer_repeat;
    u32 last_keycode;
    enum rc_proto                   last_protocol;
    u64 last_scancode;
    u8 last_toggle;
    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;
    bool registered;
    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

sysfs_groups

sysfs 属性组

device_name

rc 子设备的名称

input_phys

输入子设备的物理路径

input_id

输入子设备的 ID (struct input_id)

driver_name

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

map_name

默认键映射的名称

rc_map

当前扫描/键表

lock

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

minor

唯一的次要遥控设备号

raw

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

input_dev

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

driver_type

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

idle

用于跟踪 RX 状态

encode_wakeup

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

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

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

用户

当前使用该设备的用户数量

私有数据

驱动程序特定的数据

键锁

保护结构体的其余成员

按键按下

指示当前是否有按键被按下

keyup_jiffies

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

timer_keyup

用于释放按键的定时器

timer_repeat

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

last_keycode

上次按键的键码

last_protocol

上次按键的协议

last_scancode

上次按键的扫描码

last_toggle

上次命令的切换值

超时

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

min_timeout

设备支持的最小超时时间

max_timeout

设备支持的最大超时时间

rx_resolution

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

lirc_dev

lirc设备

lirc_cdev

lirc字符设备

gap_start

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

lirc_fh_lock

保护lirc_fh列表

lirc_fh

打开的文件列表

registered

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

change_protocol

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

open

回调函数,允许驱动程序在打开IR输入设备时启用轮询/中断。

close

回调函数,允许驱动程序在打开IR输入设备时禁用轮询/中断。

s_tx_mask

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

s_tx_carrier

设置发射载波频率

s_tx_duty_cycle

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

s_rx_carrier_range

通知驱动程序预期处理的载波

tx_ir

发射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 扫描码映射的名称