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 核心还支持仅具有红外发射器而没有任何接收器的设备。目前,所有此类设备仅在原始 TX 模式下工作。此类硬件被标识为 RC_DRIVER_IR_RAW_TX
。
当 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设置为falsechange_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
的指针。
参数
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 扫描码映射的名称