主要通过两个文件来分析ir从linux到Android流程,基于mtk Android方案,这两个文件是ir_config.ini、Vendor_xxx_product_xxx.kl。
kernel
文章不是讲解驱动,所以只列出了ir的基本流程。
MTK为遥控器做了数据分离,可以通过ir_config.ini配置所需要的遥控器而不需要修改kernel。
ir作为input设备注册:
int mstar_ir_register_device(struct platform_device *pdev){
...
dev->input_dev = input_allocate_device();//申请input dev
...
dev->input_dev->id.vendor = INPUTID_VENDOR;
dev->input_dev->id.product = INPUTID_PRODUCT;
dev->input_dev->id.version = INPUTID_VERSION;
...
ret = input_register_device(dev->input_dev);//注册input设备
}
linux有预先定义好的按键,定义在vim include/uapi/linux/input-event-codes.h中,在/drv/ir_mirc/ir_dynamic_config/input_keys_available.h中绑定key的name和input-event-code.h的映射值
#define KEYMAP_PAIR(x) {#x,x} //{"KEY_DOWN",KEY_DOWN}
#define KEY_DOWN 108 //input-event-code.h
...
KEYMAP_PAIR(KEY_DOWN) //input_keys_available.h
根据ir_config.ini填充dev,具体解析ini文件是由ir_dynamic_config_main.c 来完成的:
static char *purelinux_config_file = "/config/ir_config.ini" //配置文件路径
加载key:
static void load_ir_config_thread(struct work_struct *work)
ir_config的结构如下:
//ir_config.ini
KEY_UP = 0x12 # up
KEY_DOWN = 0x34 # down
KEY_LEFT = 0x56 # left
KEY_RIGHT = 0x78 # right
KEY_ENTER = 0x90 # Enter
//ir_dynamic_config_main.c
//KEY_UP是name,0x12是遥控器实际键值,遍历IR_KeysList,获取键值
if (!strcmp(k->name, IR_KeysList[i].key))
{
table->keycode = IR_KeysList[i].value;
}
//向设备中增加配置的按键信息
MIRC_IRCustomer_Add(curr_ir_profile);
申请中断:
ret = request_irq(INT_NUM_IR_ALL, _MDrv_IR_ISR, SA_INTERRUPT, "IR", &IRDev);
当操作遥控器时产生中断,中断函数会判断得到的数据是否符合配置的遥控器头码,并在按下和松开时report按键。发送按键则是依照input设备report流程调用input_report_key来发送按键。
Android
可以通过cat /proc/bus/input/devices来查看ir对应/dev/input下的哪个设备:
130|console:/ # cat /proc/bus/input/devices
I: Bus=0018 Vendor=3697 Product=0001 Version=0001
P: Phys=/dev/ir
S: Sysfs=/devices/ir/input/input0
U: Uniq=
H: Handlers=kbd event0 mstar_ir
B: PROP=0
B: EV=100013
B: KEY=ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffbff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
B: MSC=10
以上信息显示ir对应/dev/input/event0,Vendor:3697,Product:0001,Version:0001,这些信息在Android绑定设备的时候都会用到。
Android通过启动InputReaderThread来读取按键消息,具体实现:
//frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
在loopOnce中调用EventHub的getEvents方法来获取kernel的按键信息。
EventHub.cpp中会加入/dev/input的所有设备:
status_t EventHub::scanDirLocked(const char *dirname){
...
//该函数会遍历/dev/input下所有设备文件,并添加到device list中
}
status_t EventHub::openDeviceLocked(const char *devicePath){
...
//打开设备,并获取设备相关信息
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
...
}
Vendor_xxx_Product_xxx.kl是什么时候起作用的呢?在openDeviceLocked打开设备的时候会绑定对应的kl文件
//EventHub.cpp
keyMapStatus = loadKeyMapLocked(device);
//Keyboard.cpp
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name) {
String8 path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
...
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
...
keyLayoutFile.setTo(path);
return OK;
}
如何确定kl文件的名称,上述loadKeyLayout函数中定义的getpath传入的name如果是空,就会调用getInputDeviceConfigurationFilePathByDeviceIdentifier函数根据获取到的设备信息来决定keylayout的路径:
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
确定了kl文件,接下来就是找到keycode对应Android的映射值。
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode)
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags)
该值就是对应KeyEvent.java里的值。