定制固件通常都希望能够接收遥控器按键输入,这样就可以通过遥控器来控制某些功能,如启动/停止下载等。目前在各论坛上已经有了一些方法,我在这里介绍一种内核层面的实现方法。
先简单说一下遥控器是如何工作的。内核里有一个红外线接收器驱动程序,它的任务是接收并暂存遥控器按键供应用程序读取。每一个遥控器按键对应一个32位的代码,称为按键码。驱动程序每接收到一次按键,会在缓冲区内存储两个32位整数:第一个是按键码,第二个是重复标志(1表示重复按键,0则不是)。应用程序通过字符设备/dev/venus_irrp来读取遥控器按键码和重复标志。/dev/venus_irrp还支持ioctrl,应用程序可通过ioctrl设置一些驱动程序参数,如遥控器协议类型、驱动模式等。
通过修改红外线接收器驱动程序,可以实现以下功能:
1. 增加一个“影子”设备:/dev/shadow_irrp。所有接收到的按键码都一式两份分别发送到/dev
/venus_irrp和/dev/shadow_irrp。
2. 提供一个sysfs接口:/sys/devices/platform/VenusIR/bypass,用于封锁或解锁/dev/venus_irrp。所谓封锁就是遥控器按键码不再发送到/dev/venus_irrp。当然任何时候“影子”设备都能接收到按键码(转发按键除外)。
封锁方法:echo 1 > /sys/devices/platform/VenusIR/bypass
解锁方法:echo 0 > /sys/devices/platform/VenusIR/bypass
3. 提供一个sysfs接口:/sys/devices/platform/VenusIR/fakekey,用于发送“伪造”的按键码到/dev/venus_irrp,从而实现转发。
转发方法,以按键代码“aa557f80”为例:
echo 'aa557f80' > /sys/devices/platform/VenusIR/fakekey
通过新的驱动程序,可以实现两种遥控模式:并行模式和过滤转发模式。
并行模式是指/dev/venus_irrp和/dev/shadow_irrp同时接收相同的按键码,dvdplayer使用vensu_irrp,自己开发的脚本或程序使用shadow_irrp,互不相干。而过滤转发模式则是封锁venus_irrp,按键码只发送到“影子”设备,经过程序过滤以后再转发到vensu_irrp。我们可以根据不同的定制需求选择两者之一,也可以随时在两种模式之间切换。
我们知道接收器的驱动程序在官方固件里是编入内核的,因此要使用新的驱动应该是要替换官方内核的。考虑到替换内核的操作比较复杂,必需重刷固件,而且目前我们手上的内核源码很可能与官方的不同,有潜在的兼容性风险,我们有必要另辟蹊径。我的策略是把新的驱动程序编译成模块,在模块初始化时通过内核符号表查到原驱动程序的退出清理函数venus_ir_cleanup_module()的地址,然后调用这个函数让它完成清理工作,接着新的驱动程序开始初始化,从而完成驱动程序的动态替换。
驱动程序源代码:
venus_ir_new.tar.zip编译好的模块:
venus_ir_new.ko.zip编译源码时需要修改Makefile中的KERNELDIR变量为你的内核源码目录。
注意事项:新的驱动程序模块必需在dvdplayer启动之前加载,否则dvdplayer已经打开了/dev/venus_irrp设备,驱动程序的替换会失败。因此建议将模块加载的命令放在/usr/local/etc/rcS中启动dvdplayer之前。
English Translation Follows:It is usually desirable that customized firmware is able to interact with the remote control,
so that we could control the customized features from the remote, like
starting/stopping download. There were a couple of methods floating around
various forums, here I'd like to introduce an implementation at the
kernel level.
Before we start, let's talk a little bit about how remote control
works. There is an IR receiver driver in the kernel, it's job is to
receive key press from the remote and buffer it for application to
read. Every key on the remote has a 32-bit key code, each time the
driver receives a key press, it puts 2 32-bit integers in a
buffer(kernel fifo), the 1st int is the key code, the 2nd is a repeat
flag(1 means it's a repeated key, 0 otherwise). Application reads the
key code and repeat flag from a character device: /dev/venus_irrp. This
device supports ioctrl as well, application can set some driver
parameters through the ioctrl interface, like the IR protocol, driver
mode etc.
By modifying the IR driver, we can:
1. Add a shadow device: /dev/shadow_irrp. Every key code the driver
receives is sent to both /dev/venus_irrp and /dev/shadow_irrp.
2. Provide a sysfs interface: /sys/devices/platform/VenusIR/bypass,
through which we can select to bypass /dev/venus_irrp or not. Under any
circumstance, the shadow device always receives the key codes(except
fake keys, which we'll talk about later).
to bypass venus_irrp: echo 1 > /sys/devices/platform/VenusIR/bypass
back to normal: echo 0 > /sys/devices/platform/VenusIR/bypass
3. Provide a sysfs interface: /sys/devices/platform/VenusIR/fakekey,
through which we can send fake keys to /dev/venus_irrp, the so called
key-forwarding.
Take the key code 'aa557f80' as an example, to forward the key:
echo 'aa557f80' > /sys/devices/platform/VenusIR/fakekey
With the new driver, we can implement 2 remote control modes: Parallel Mode and Key-forwarding Mode
In parallel mode, both /dev/vensu_irrp and /dev/shadow_irrp receive the
same key code sequence, the dvdplayer uses vensu_irrp and our own
script/application uses shadow_irrp, without interfering each other. While
in key-forwarding mode, key codes are only sent to the shadow device,
after being processed by our own application, it's at the app's disposition
to forward it or not. We can choose to use one of the two modes, or to
switching in between at any time.
We know that the venus_ir driver was built into the kernel, we may have
to replace the whole kernel to use the new driver. However, replacing
kernel is a bit complicated for average users, it requires re-flash the
firmware, and the kernel source code we have may not be the same as the
official one, there is a risk of incompatibility, we have to find some
other way. My trick is to compile the new driver as module, when the
module initialize itself, it first looks up the kernel symbol
table(kallsyms) to find and call the cleanup function of the built-in
driver: venus_ir_cleanup_module(), after that it continues its own
initialization, thereby the built-in driver is replaced.
new driver source code:
venus_ir_new.tar.zip
the compiled kernel module:
venus_ir_new.ko.zip
When compiling from source, you may need to modify the KERNELDIR in Makefile to where the kernel source is located.
NOTE: The new driver module
must be loaded before starting dvdplayer, otherwise dvdplayer may have
already opened /dev/venus_irrp which could prevent the built-in driver
from being replaced. You may want to place the module loading command
in /usr/local/etc/rcS, just before starting dvdplayer.