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 核心还支持只有 IR 发射器而没有任何接收器的设备。目前,所有此类设备仅在原始 TX 模式下工作。这种硬件被标识为 RC_DRIVER_IR_RAW_TX
。
当 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 设置为 falseidle
用于跟踪 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
的指针。
参数
struct rc_dev *dev
指向
struct rc_dev
的指针。
参数
struct rc_dev *dev
指向
struct rc_dev
的指针。
参数
struct device *parent
指向
struct device
的指针。struct rc_dev *dev
指向
struct rc_dev
的指针。
参数
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 {
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
的指针
参数
const char *name
RC 扫描码映射的名称