不追求面面俱到,我也是行走在道路上,点到为止。本篇基于realtek TV方案分析。
简述:
我会从linux kernel 和 android 两个部分分析,在我看来这个两个部分就是从android 目录下的Generic.kl(或自定义kl) 分离开来的。
工作中会遇到的问题:
kernel:1、驱动已经添加OK,需要添加新的按键码
android:2、添加一个新的android keycode
3、新输入设备如何选择相应的XXX.kl文件
4、按键进入处理队列前会怎么走(interceptKeyBeforeQueueing)
——————————————————————————————————————————————————————————————————————
kernel:
——————————————————————————————————————————————————————————————————————
kernel总是那么专注,分析总是以module_init开始,以module_exit结束。
路径:linux-3.7.2\arch\arm\bsp-realtek\mach-rtd298x\Irrc.c
module_init(ir_init_module);
module_exit(ir_cleanup_module);
先来看ir_init_module:
|
venus_ir_input_init:设备文件初始化,其中 data->input_dev->name = "venus_IR_input"; 这个名字会用于android输入设备使用xxx.kl文件的名字
data->input_dev->keybit:输入子系统在发送按键前首先会检测要发送的按键值keycode,是否在keybit位图中有注册,所以
set_bit(rtk_mk5_tv_key_table.keys[i].keycode, data->input_dev->keybit);有注册的过程(问题1)
... ... ...
|
if(request_irq(IRQ_ISO,
IR_interrupt_handler,
IRQF_SHARED,
"Venus_IR",
IR_interrupt_handler)) {
printk(KERN_ERR "IR: cannot register IRQ %d\n", IRQ_ISO);
result = -EIO;
goto fail_alloc_irq;
} : 这里的中断handler 是这个设备驱动真正做事的部分,中断到来,会读取相应寄存器的数据
while(examine_ir_avail(®Value, &irrp2Value, &dataRepeat) == 0) {
received = repeat_key_handle(regValue, irrp2Value, dataRepeat);} // 检测数据是否有效,如果有效进去发键流程,调用输入子系统的
input_event()。
所以移植新的遥控器,首先要定义scancode: keycode的映射表,同时初始化时keycode的值要初始化到input_dev->keybit的映射位,
发键是更具scancode 找到对应的keycode ,然后调用input 子系统的接口input_event()发送出去。
——————————————————————————————————————————————————————————————————————
********
********
——————————————————————————————————————————————————————————————————————
android :
——————————————————————————————————————————————————————————————————————
添加新的遥控按键码涉及的文件有(xxx.kl(Generic.kl 属于系统自带不建议修改),KeycodeLabels.h,keycodes.h,attrs.xml,KeyEvent.java)
android 键盘的消息处理机制的技术分析已经很多,就不去分析这个类别直接的启动流程,已经读thread,分发thread是如何创建的了,InputReader.cpp的
InputReader::loopOnce()开始。
一、新设备的加入,映射解析相应的 .kl文件过程
mEventHub->getEvents :这个方法里会去读取一个按键,或检测是否有新的设备加入,下面来分析下。
EventHub::getEvents()——>EventHub::scanDevicesLocked()——>EventHub::scanDirLocked(DEVICE_PATH) //DEVICE_PATH: 设备目录/dev/input
——>EventHub::openDeviceLocked(const char *devicePath)//这个里面首先会根据传入的设备文件路径,打开读取设备文件名,
也就是之前kernel里设置的data->input_dev->name = "venus_IR_input";
/*loadConfigurationLocked(device)获取kl文件路径inputeDevice.cpp,getInputDeviceConfigurationFilePathByDeviceIdentifier
通过查询 /system/usr/keylayout 目录查询venus_IR_input.kl 文件
*/
——>EventHub::loadKeyMapLocked(Device* device)——>device->keyMap.load(device->identifier, device->configuration)
——>keyboard.cpp,KeyMap::load()——>KeyMap::probeKeyMap()——>KeyMap::loadKeyLayout——>KeyLayoutMap.cpp
KeyLayoutMap::load()——> KeyLayoutMap::Parser::parse()——>KeyLayoutMap::Parser::parseKey()——>Keyboard.cpp,
getKeyCodeByLabel()——>
lookupValueByLabel(label, KEYCODES) //KEYCODES正是我们在KeycodeLabels.h 这需要添加按键的table表,只有自定义的
// xxx.kl 文件里的keycode 字符串在KEYCODES全部能找到,才会成功用到xxx.kl,否则会用Generic.kl
/* // Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK;
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}*/
至此新设备xxx.kl的keylayout的文件已经映射完成。
二、 按键输入的调用流程。
看下InputReaderThread线程:
InputReader.cpp InputReader::loopOnce() ——> //
InputReader::processEventsLocked()——>InputReader::processEventsForDeviceLocked()——>InputDevice::process——>KeyboardInputMapper::process
——>KeyboardInputMapper::process()——> getListener()->notifyKey()=InputDispatcher::notifyKey()——>NativeInputManager::interceptKeyBeforeQueueing()
——>PhoneWindowManager.java:interceptKeyBeforeQueueing()// 在新的keyevent加入处理队列之前,如果需要特殊处理可以在interceptKeyBeforeQueueing中过滤
后面将新获取到的输入事件enqueueInboundEventLocked,并唤醒dispatch线程。
今天到此结束,InputDispatcherThread下回在研究
——————————————————————————————————————————————————————————————————————