Linux内核红外遥控子系统-LIRC

发布时间 : 星期五 文章Linux内核红外遥控子系统-LIRC更新完毕开始阅读

2. Linux 对红外遥控的支持

Linux上通过LIRC子系统对红外控制提供支持,它包含几个部分:lirc核心、协议原始脉冲解码器、按键映射表、红外输入设备驱动。LIRC代码在:

drivers/media/IR

按键映射表

红外输入设备驱动 LIRC核心 Linux input系统 原始脉冲解码器 2.1. 协议原始脉冲解码器模块

解码器模块实现用软件的方法对原始脉冲进行解码。解码器用一个ir_raw_handler结构表示。

struct ir_raw_handler { struct list_head list; int (*decode)(struct input_dev input_dev, struct ir_raw_event event); /* 解码函数 */ int (*raw_register)(struct input_dev input_dev); /* 注册函数 */ int (*raw_unregister)(struct input_dev input_dev); /* 卸载函数 */ }; 解码器通过注册和卸载函数:

int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) 当注册解码器时, ir_raw_handler的 raw_register函数被调用,所以可在其进行一些解码器初始化工作。相应的,卸载时 raw_unregister函数被调用。所有注册的解码器放在一个全局链表ir_raw_handler_list中,lirc会便利每个解码器对报告的波形进行解码,注意,此时当任何一个解码器返回一个错误,后面的解码器不会被执行,所以不要将不使用的解码器模块同时加载到内核中。

解码器的主体就是 decode函数,lirc核心会将驱动报告的每个脉冲一次一次的传递到decode 函

数,而decode 函数的实现就是一个状态机,每一次输入导致进入下一状态,直到一次解码完成,然后返回起始状态进行下次解码。

再LIRC中,每个脉冲(包括脉冲间隔)用一个ir_raw_event结构表示:

struct ir_raw_event { unsigned pulse:1; /* 是脉冲还是间隔 */ unsigned duration:31; /* 宽度,以ns为单位,一个0ns的脉冲表示重新开始解码 */ }; lirc中对于脉冲宽度的比较使用eq_margin()、geq_margin()函数,它允许宽度值在二分之一单元上下波动。

bool geq_margin(unsigned d1, unsigned d2, unsigned margin) bool eq_margin(unsigned d1, unsigned d2, unsigned margin) 解码器的完整实现可参考sony sirc解码器实现:ir-sony-decoder.c

2.2. 按键映射表模块

按键映射模块都放在keymaps目录下。

不同的遥控器有不同的按键映射,按键映射模块的作用就是将扫描码与Linux input系统标准事件

对应起来。

映射表的注册和卸载:

int ir_register_map(struct rc_keymap *map) void ir_unregister_map(struct rc_keymap *map) 按键映射模块的主体就是一个ir_scancode结构数组,每个元素是一对按键映射。

2.3. 红外输入设备驱动

红外输入设备驱动负责向LIRC核心报告脉冲或直接报告扫描码事件。 红外输入设备用ir_input_dev结构描述:

struct ir_input_dev { struct device dev; /* device */ char *driver_name; /* Name of the driver module */ struct ir_scancode_table rc_tab; /* scan/key table */ unsigned long devno; /* device number */ }; const struct ir_dev_props *props; /* Device properties */ struct ir_raw_event_ctrl *raw; /* for raw pulse/space events */ struct input_dev *input_dev; /* the input device associated with this device */ /* key info - needed by IR keycode handlers */ spinlock_t keylock; /* protects the below members */ bool keypressed; /* current state */ unsigned long keyup_jiffies; /* when should the current keypress be released? */ struct timer_list timer_keyup; /* timer for releasing a keypress */ u32 last_keycode; /* keycode of last command */ u32 last_scancode; /* scancode of last command */ u8 last_toggle; /* toggle of last command */ 设备注册和卸载:

int ir_input_register(struct input_dev *dev,const char *map_name, const struct ir_dev_props *props,const char *driver_name) void ir_input_unregister(struct input_dev *input_dev) 注册流程:

(1) 分配一个input设备,input_allocate_device();

(2) 对input设备进行一些初始化设置,但事件掩码不需要设置;

(3) 将分配的input设备结构的地址作为参数调用ir_input_register(),以后与lirc核心的交互

都是通过这个input设备结构的地址进行的。

ir_input_register ()函数的map参数指定要使用的按键映射表,所有映射表定义在rc-map.h中,比如RC_MAP_RC5_TV。

props参数可以为NULL,但若要使用解码器模块对原始脉冲解码(比如无法直接从硬件获得扫描码时),则要设置。比如:

static struct ir_dev_props irc_props = { .driver_type = RC_DRIVER_IR_RAW, /* 指定需要软件解码 */ .allowed_protos = IR_TYPE_SONY, }; input_dev = input_allocate_device(); if (!irc_input_dev) { ret = -ENOMEM; goto err_input_allocate_device; } input_dev->name = \ ret = ir_input_register(input_dev, RC_MAP_RC5_TV, &irc_props, NULL); if (ret) { goto err_ir_input_register; } input_dev->rep[REP_DELAY] = 400; input_dev->rep[REP_PERIOD] = 33; 对于可直接从硬件读取到扫描码的设备,可用以下函数包括扫描码事件:

void ir_keydown(struct input_dev *dev, int scancode, u8 toggle) 对于只能获得原始脉冲的设备,需先调用下面函数报告每个脉冲和脉冲间隔:

int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev); int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type) 第一个函数要求驱动自己填充 ir_raw_event结构,并且在报告第一个脉冲前,需要调用ir_raw_event_reset()函数重置解码器,当驱动认为一个完成的波形已报告完毕后,调用ir_raw_event_handle()启动解码;第二个函数自动生成一个 ir_raw_event结构,脉冲宽度根据前后两次调用ir_raw_event_store_edge()函数的时间间隔字段计算,并且会自动调用ir_raw_event_reset()和ir_raw_event_handle()函数。 type参数指定是何脉冲。

enum raw_event_type { IR_SPACE = (1 << 0), IR_PULSE = (1 << 1), IR_START_EVENT = (1 << 2), IR_STOP_EVENT = (1 << 3), }; 其他两个驱动常用的接口:

void ir_repeat(struct input_dev dev) /* 重复上次按键 */ u32 ir_g_keycode_from_table(struct input_dev input_dev, u32 scancode) /* 获得扫描码对应的按键*/

2.4. LIRC对按住按键时重复事件的处理

首先,重复是自动的,它使用了input子系统的REP功能。当第一次向lirc报一个扫描码事件时,lirc会向input子系统报告相应的按键事件,并启动一个定时器,该定时器在超时后会上报对应的按键松开事件;之后若在250ms(这个值等于input子系统的rep延时的默认值)内该设备又报告了同一个扫描码,这时只是将定时器再推后250ms,并不报告新的按键事件,也就是说按键的重复由input系统处理。这种设计主要是考虑到遥控器的限制,有的遥控器没有按键按下和松开之分(虽然在按下和松开时都有脉冲,但没有区分字段,而且有时可能会丢失信号)。

联系合同范文客服:xxxxx#qq.com(#替换为@)