Android Input系统介绍与kcm与kl文件的定制
来源:互联网 发布:菜鸟网络盈利模式 编辑:程序博客网 时间:2024/05/16 18:54
这篇文章将以TOUCH DRIVER为例子,介绍INPUT系统的DRIVER上报过程,另外会把主要精力放在KCM与KL文件的定制原理中。
PART 1 : INPUT系统输入事件处理机制
linux中input系统的主设备号是 13(可用ls –l cmd查看)
次设备号:
0-31 joystick(游戏杆)
32-62 mouse(鼠标)
63 mice(鼠标)
64-95 事件(Event)设备 (touch,keyboard…)
Kernel中架构大致如下:
设备驱动层 核心层 事件层 用户空间
Mouse Handler
Input设备<–> Input Core <–> keyboard Handler <–> 设备节点访问
TP Handler
Input 系统kernel driver中的核心文件为linux/kernel/include/linux/input.h (Android 4.0版本之前)或 linux/kernel/include/linux/uapi/input.h(Android 4.3或更新版本,其中详细定义了Input 系统上报的基本格式和数据结构。
下面我们开始了解下这些基本的格式和数据结构。
Input event 基本格式:
Type code value
数据结构
/**
* struct input_value – input value representation
* @type: type of value (EV_KEY, EV_ABS, etc)
* @code: the value code
* @value: the value
*/
struct input_value {
__u16 type;
__u16 code;
__s32 value;
};
分析kernel中的Input上报事件,有个十分好用的工具小助手:getevent tool
具体的使用方法有兴趣的朋友可以自行百度,这里我仅介绍一个最常用的命令:
getevent -i
这个命令会详细的将系统中所有input类别和对应的driver node详尽的列出来,是我们的好帮手!
Tips:通过cat /sys/class/input/input(X)/name也可获取对应的input name
PART 2 :TP协议及DRIVER处理过程
Touch 基本协议 –multi-touch-protocol(type A and B) The multi-touch-protocol is divided into two types, depending on the capabilities of the hardware.
For devices handling anonymous contacts (type A) :硬件无法直接区分出触摸点,故直接将得到的raw档上传给user space处理。
For devices capable of tracking identifiable contacts (type B) :硬件有能力区分出触摸点,通过slot的方式处理多点讯息。 Get detail
reference: /code dir/linux/kernel/Documentation/input/multi-touch-protocol.txt
Protocol Example A
——————
Here is what a minimal event sequence for a two-contact touch would look
like for a type A device:
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
Protocol Example B
——————
Here is what a minimal event sequence for a two-contact touch would look
like for a type B device:
ABS_MT_SLOT 0 //ABS_MT_SLOT 一个slot代表一个触摸点
ABS_MT_TRACKING_ID 45 //一个触摸点会绑定一个TRACKING_ID
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT
Here is the sequence after moving contact 45 in the x direction:
ABS_MT_SLOT 0
ABS_MT_POSITION_X x[0]
SYN_REPORT //这是一种省略的写法,可简单理解为y轴,
//TRACKING_ID等均和上次的报点一样,仅x轴变化
Here is the sequence after lifting the contact in slot 0:
ABS_MT_TRACKING_ID -1 //通过将TRACKING_ID设为-1,可以移除slot和TRACKING_ID 45
//间的联系,这样slot 0就可以和下一个TRACKING_ID关联了。
SYN_REPORT //注意:TRACKING_ID省略说明和上次一样,即为0。
Finally, here is the sequence after lifting the second contact:
ABS_MT_SLOT 1 //同理移除slot 1与TRACKING_ID 46间的联系做法如此
ABS_MT_TRACKING_ID -1
SYN_REPORT
input设备核心结构体
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
…..
};
/**
* struct input_dev – represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @propbit: bitmap of device properties and quirks
* @evbit: bitmap of types of events supported by the device
* (EV_KEY,EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* (KEY_HOME, KEY_MENU, etc. )
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
….
*/
TP driver 实例(ftxxxx_ts.c) 核心函数分析:
TP prob函数中的关键初始化部分:
input_dev = input_allocate_device();
ftxxxx_ts->input_dev = input_dev;
set_bit(KEY_BACK, input_dev->keybit); //注册TP上的key键
set_bit(KEY_HOME, input_dev->keybit);
set_bit(KEY_MENU, input_dev->keybit);
set_bit(KEY_POWER, input_dev->keybit); //双击唤醒
__set_bit(EV_ABS, input_dev->evbit); //@evbit: bitmap of types of events supported by the device (EV_KEY,EV_REL, etc.)
__set_bit(EV_KEY, input_dev->evbit);
input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS, 0); //#define CFG_MAX_TOUCH_POINTS 10
//*****void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0); //(Finger Size) #define PRESS_MAX 0xFF
//****补充: input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 31, 0, 0); //(Touch Size)
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, TOUCH_MAX_X, 0, 0); //#define TOUCH_MAX_X 480
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, TOUCH_MAX_Y, 0, 0); //#define TOUCH_MAX_Y 854
input_dev->name = TOUCH_INPUT_NAME; //#define TOUCH_INPUT_NAME “himax-touchscreen”
err = input_register_device(input_dev);
当有人触摸tp后,input event的上报过程:
tp上的key部分:
input_report_key(data->input_dev, KEY_BACK, 1);
input_sync(data->input_dev);
tp部分
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
input_report_abs(data->input_dev, ABS_MT_PRESSURE, event->pres);
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->au16_x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]);
相对原始的格式如下:
input_report_key(ts->input_dev, BTN_TOUCH, 1); // touch down
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); //ID of touched point
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, area); //Finger Size
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, press); // Pressure
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); // X axis
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); // Y axis
input_mt_sync(ts->input_dev);
input_sync(ts->input_dev);
PART 3:IDC KL KCM文件介绍及流程分析
Idc (Input Device Configuration)
输入设备配置文件,它包含设备具体的配置属性,这些属性影响输入设备的行为。
Android基于输入设备驱动汇报的事件类型(绝对或者相对坐标)和属性来检测和配置大部分输入设备,然而有些分类是模棱两可的,如:多点触摸屏和touch pad都支持EV_ABS事件类型 和ABS_MT_POSITION_X和ABS_MT_POSTION_Y事件,然而这两类设备的使用是不同的,且不总是能自动判断。
所以,需要对android系统提供另外的信息来指示该设备上报event的真正含义。因此,触摸设备,特别是内嵌的touch screen,一般都需要有idc文件。
kl (KeyLayout) kcm (KeyCharacterMap)
用来定义按键布局文件和按键字符映射需要的配置文件,这些配置文件的后缀名分别为kl和kcm。
当有按键响应时,内核传给ANDROID的是scancode,ANDROID将scancode经配置表(如qwerty.kl)找到keycode标识符,然后由内部表KEYCODES列表找到keycode的数字值,再由keycode的数字值经字符配置表(如qwerty.kcm)找到对应的字符值。
让我们来做个实验:将tp 上的多功能键更换为其他按键功能
首先,我介绍下TP 的 KL文件中各项所对应的意义:
</system/usr/keylayout/himax-touchscreen.kl>
key 158 BACK VIRTUAL WAKE_DROPPED
key 102 HOME VIRTUAL WAKE
key 139 APP_SWITCH VIRTUAL WAKE_DROPPED
第一列Key: 表示一行有效的开始,注释行用#开头
第二列表示Scancode: 是键盘物理设备的矩阵扫描码值
第三列表示系统里面的按键码Keycode
第四列表示KeyCode的Flag信息,可有可无(如果是虚拟按键,可多补上一个VIRTUAL参数,这样就能让其点击时发生震动,例如virtual这个参数是在phonewindowsmanager.java中处理,确定是否需要启动按键反馈功能,其他的参数则分别会在各自的某个环节中进行处理。)
常见如下三种状态:
* 空 没有附加信息
* WAKE 当机器处理Sleep状态,可以唤醒,按键信息继续被处理
* WAKE_DROPPED 当机器处于Sleep状态,可以唤醒,但是丢弃按键信息
我们只需要将第二列所对应的功能做个替换,然后push回机器里,重启后就可以发现,功能键被替换掉了。很好玩吧?哈
下面我们开始代码级剖析EventHub.cpp中的核心代码
我们将会逐步解剖核心函数:openDeviceLocked()的所作所为
1:通过ioctl去扫描input设备的详细讯息:bus、vendor、product、version、name、location、descriptor、driver、是否是KEY设备,ads设备,res设备等等 有了这些讯息,Eventhub就有能力通过这些讯息对这个设备上报的event进行针对性解析。
Ex:
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) – 1), &buffer) < 1) {
//fprintf(stderr, “could not get device name for %s, %s\n”, devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) – 1] = ‘\0’;
identifier.name.setTo(buffer);
}
2:通过loadConfigurationLocked(device)函数查找该设备对应的idc文件。
该函数中调用了函数getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier , type)来进行具体解析,注意其中的type参数说明需要查找的文件是idc,kl或kcm三者中的哪种。
在loadConfigurationLocked函数中,默认传入的type为查找idc文件。
由于后面查找kl和kcm也会用到这个函数,所以我们先分析它究竟做了什么
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format(“Vendor_%04x_Product_%04x_Version_%04x“,
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
}
}
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format(“Vendor_%04x_Product_%04x“,
deviceIdentifier.vendor, deviceIdentifier.product),
type));
if (!productPath.isEmpty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}
当中函数
String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
path.setTo(getenv(“ANDROID_ROOT”));
path.append(“/usr/“);
appendInputDeviceConfigurationFileRelativePath(path, name, type); //核心函数
if (!access(path.string(), R_OK)) {
ALOGD(“Found”);
return path;
}
// Search user repository. // TODO Should only look here if not in safe mode.
path.setTo(getenv(“ANDROID_DATA”));
path.append(“/system/devices/“); //Will never into this situation
appendInputDeviceConfigurationFileRelativePath(path, name, type);
if (!access(path.string(), R_OK)) {
ALOGD(“Found”);
return path;
}
//IF Not found.
……
return String8();
}
函数 appendInputDeviceConfigurationFileRelativePath{
…
for (size_t i = 0; i < name.length(); i++) {
char ch = name[i];
if (!isValidNameChar(ch)) {
ch = ‘_’;
}
path.append(&ch, 1);
}
….
}
函数
static bool isValidNameChar(char ch) {
return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == ‘-‘ || ch == ‘_’);
}
可见,如果input设备name中功能含有空格,这个函数会把其中的空格直接补成下划线”_”。
想必到这里,大家就能明白,为什么有的input设备名称是带空格的,可对应的kl文件都是下划线,这样一来,也是能够匹配上的!
3:查找完idc文件后,下面会继续调用loadKeyMapLocked函数调来查找kl和kcm文件。
该函数中调用到了
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) 函数
该函数中会逐步调用如下两个函数:
loadKeyLayout(deviceIdenfifier, keyLayoutName);
loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
这两个函数的目的是扫描该设备对应的kl和kcm文件。
实际上,这两个函数中同样都调用到了前面所分析的getInputDeviceConfigurationFilePathByDeviceIdentifier函数,用来进行文件匹配。区别只是传入其中的type参数不同,从而说明分别需要查找的文件是kl和kcm。
补充:
若在
loadKeyLayout(deviceIdenfifier, keyLayoutName);
和
loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
两个函数的基本查找过程中,发现找不到kcm和kl文件
则会在查询过程的最后,启动两种最终的匹配尝试方法,即:查找以Generic或者Virtual开头的kcm和kl文件。
(注意,前面介绍的对于idc文件搜索过程,是不会去做这种尝试的,如果匹配不到就直接附空)
代码截取:
if (probeKeyMap(deviceIdenfifier, String8(“Generic”))) {
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8(“Virtual”))) {
return OK;
}
Tips:
特别需要留意的一点是,目前只有在EventHub初始化时,发现该设备是keyboard或Joystick设备(gpio key也算是keyboard 设备),才会调用loadKeyMapLocked函数去初始化key转化机制。(like gsensor、psensor .etc 则不会)
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}
PART 4:虚拟按键功能的实现原理
虚拟按键指的是例如nexus手机下方的三个虚拟按键(返回,home,菜单)
实现虚拟按键的方法常见的有如下两种:
1. 在TP driver中直接报键值(kernel中处理)
(前面已经讲过)
2. TP driver上报一个坐标,上层通过KL的解析来判定是哪个键。
我们主要来介绍这种情况
TP虚拟按键的kl 被存放在/sys/board_properties/virtualkeys.devicename中,pull出来后可以看看内容:
(以milestone为例)
0x01:158:32:906:63:57:
0x01:139:162:906:89:57:
0x01:102:292:906:89:57:
0x01:217:439:906:63:57
格式解释如下:
0x01: A version code. Must always be 0x01.
<Linux key code>: The Linux key code of the virtual key.
<centerX>: The X pixel coordinate of the center of the virtual key.
<centerY>: The Y pixel coordinate of the center of the virtual key.
<width>: The width of the virtual key in pixels. <height>: The height of the virtual key in pixels.
丛上面可以看出,该设备定义了back,menu,home,search四个按键,具体的区域也是一清二楚。
通过上面的流程下来,想必大家对input子系统的大概流程有了一定的了解,细节太多不太好展开。
如果有疑问,大家可以与我在交流互动。
- Android Input系统介绍与kcm与kl文件的定制
- android 添加按键(一) kl文件 kcm文件
- android input系统如何导入kl文件
- 关于电容屏幕驱动的几个文件ft5x06-ts.idc/ft5x06-ts.kcm/ft5x06-ts.kl的认识
- android kl 文件的作用
- 理解kcm文件的意义
- 理解kcm文件的意义
- android Generic.kcm的使用
- Android系统-按键字符表(*.kcm)
- KL变换与PCA的关系
- android kl文件
- 深入浅出 - Android系统移植与平台开发(十一)- Android系统的定制
- 深入浅出 - Android系统移植与平台开发(1)- Android系统的定制
- 深入浅出 - Android系统移植与平台开发(十一)- Android系统的定制
- 深入浅出 - Android系统移植与平台开发(十一)- Android系统的定制
- 定制core文件的文件名与目录
- Android系统介绍与移植
- Android系统介绍与框架
- Delphi读写UTF-8、Unicode格式文本文件
- Node.js 事件循环
- C#操作XML
- Dijkstra-算法-----单源最短路
- listView系列之分多种布局显示
- Android Input系统介绍与kcm与kl文件的定制
- 百度富文本编辑器的使用
- 城市最短路径
- 第一个独立的小岛数
- python 正则表达式
- 数据密集型系统架构设计
- 安卓应用底部菜单栏+fragment
- ubuntu下用postfix搭建邮件服务器
- tomcat6 中context配置,使用tomcat 数据源