在高通Fastmmi模式中增强交互方式
在高通Fastmmi模式中增强交互方式
背景
由于之前工厂抱怨 FCT模式不好用。
之前的FCT测试是这样子的:PCBA上夹具,连接USB。
同时,使用上位机程序(ATE)发送指令,人工判断结果以后,发送结果;以及下一条测试指令的情况。
可见,测试一条指令所需的交互次数很多。
现在要求减少AT指令的交互测试,思路有2种:
1、做成自动化的,不再人工发送指令
2、通过现有的功能(例如按键)实现模拟点击确认交互方式的指令
大概是这样子的:
机器 放上 夹具,连接USB
进入 Fastmmi 模式以后 ,自动执行每一项测试项,
- 如果指令能够自动返回,则继续下一项
- 如果指令需要人工判断,则通过定义的按键来 显示 成功或者失败;如果 没有按键,则一直等待,否则就进入下一项
- 循环,直到结束测试。
行动目标
1、即使PC不再发送AT指令,机器自己也能运行自动测试(满足某些情况下)。
2、按下指定的按键,能够直接判断测试项的结果。
行动思路
框架了解
整个MMI的框架如下:
- MMI Core: Core manages all MMI modules and is responsible for UI control, also responses Diag request from DIAG service.
MMI core is on: /dev/socket/mmi, running as server role. Agent and Diag will connect server
socket
- MMI Agent: Agent loads each MMI module (mmi_xxx.so) in a single process and communicates with MMI Core via socket.
- MMI Diag: Diag handles diag command from Host PC and communicates with MMI core via socket.
- MMI UI: UI is part of MMI core, MMI core responsible for drawing UI components.
源码树如下:
The FASTMMI code is in the folder vendor/qcom/proprietary/fastmmi. Check the source file structure below.
.
├── Android.mk
├── configure.ac
├── libmmi
...
├── Makefile.am
├── mmi
...
├── mmi.mk
├── module
│ ├── cpu
│ │ ├── Android.mk
│ │ ├── cpu.cpp
│ │ └── Makefile.am
...
│ └── wifi
│ ├── Android.mk
│ ├── Makefile.am
│ ├── wifi.cpp
│ └── wifi_uav.cpp
└── res
...
├── layout
│ ├── ...
│ ├── layout_wifi.xml
│ ├── main_wear.xml
│ └── main.xml
├── raw
│ ├── Android.mk
│ ├── DroidSansFallback.ttf
│ ├── LICENSE
│ ├── NOTICE
│ └── qualsound.wav
├── values
│ ├── Android.mk
│ ├── path_config_android.xml
│ ├── path_config_le.xml
│ ├── strings.xml
│ └── strings-zh-rCN.xml
└── wifi_config
├── Android.mk
└── wpa_supplicant_test.conf
- libmmi: is the UI controller library, like button, window, text ...
- mmi :is the main application.
- module: is the individual mmi test cases.
- res: is the config and layout resource, and so on.
流程分析
fastmmi的资料比较少,因此,只能基于现有的代码来进行分析推断。
流程很简单,fastmmi初始化,创建了包括ate_test_thread
(默认的处理串口数据线程)。at_costomer_thread
(新增的FCT专用指令函数)在内的各种线程。
从发送AT指令的线程入手:
路径:vendor/qcom/proprietary/fastmmi/mmi/mmi.cpp
实现了从各路串口中接收数据到buff,根据buff内容的不同而进行处理,我们修改FASTMMI的交互方式的关键就在于:
1、判断现在的layout(图层)是否存在
通过
get_main_module
找到主界面(最后需要release_cur_layout
释放互斥资源)通过
lay = g_layout_map[mod->config_list[KEY_LAYOUT]];
判断当前的lay是否存在(子界面)
2、找到我们需要执行的按钮(例如,pass
,failed
)
通过
layout.find_button_by_name
方法找到对应的按钮
3、通过fastmmi的流程来“模拟”点击。
获取按钮的功能:
r->cb = btn->get_cb();
执行按钮的功能:
r->cb(r->module);
即:
/* step one: Get layout */
module_info *mod = get_main_module();
if(mod == NULL) {
ALOGE("%s Not main screen OR Null point",__FUNCTION__);
break;
}
layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];
if(lay == NULL || lay->m_listview == NULL) {
ALOGE("%s No Main layout",__FUNCTION__);
break;
}
/* step two: Find button */
layout *curlay = acquire_cur_layout();
button *btn = curlay->find_button_by_name("xxx");
if(btn==NULL)
{
ALOGE("btn xxx not found");
break;
}
/* step three: exec button click */
runnable_t *r = new runnable_t;
r->cb = btn->get_cb();
release_cur_layout();
if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
module_info *rmod = (module_info *)(r->module);
r->cb(r->module);
}else {
ALOGE("btn function not found");
break;
}
操作记录
使用什么软件,做了什么操作,改动了什么代码
根据上面的思路,发现FCT自动进入了GPS。不知道怎么回事(恢复了默认版本也一样),后面我使用r54分支进行调试。没有这个问题。
好像是因为我的判断逻辑有错误导致的
实现按键交互
路径:vendor/qcom/proprietary/fastmmi/mmi/input.cpp
确保按键能够向下传递
我们最终的目的是为了能够在key_callback
中添加我们需要的特殊处理。首先,需要关心按键事件能否向下传递。
下列的2个函数实现了 按键事件处理以及是否向下传递分发(类似QT的事件发放):
int input_callback(int fd, uint32_t revents, void *data) {
struct input_event ev;
int retval;
retval = ev_get_input(fd, revents, &ev);
if(retval < 0) {
MMI_ALOGE("input event get fail\n");
return -1;
}
/**Adjust the value to match LCD resolution*/
adjust_ev(&ev);
/**Convert virtual key to KEY code*/
hook_vkey(&ev, &g_key_map);
/**Call listener, if return False mean stop here,
* if return true mean continue process event.
*/
if(!invoke_listener(ev)) // 如果 invoke_listener 处理了,则不再向下传递
return 0;
if(ev.type == EV_KEY) {
key_callback(ev.type, ev.code, ev.value);
} else if(ev.type == EV_SW) {
sw_callback(ev.type, ev.code, ev.value);
} else if(ev.type == EV_ABS || ev.type == EV_SYN) {
touch_callback(&ev);
}
return 0;
}
static bool invoke_listener(input_event ev) {
bool ret = true;
pthread_mutex_lock(&g_listener_mutex);
if(g_input_listener != NULL)
ret = g_input_listener->dispatch_event(ev);
pthread_mutex_unlock(&g_listener_mutex);
return ret;
}
由于invoke_listener
调用了dispatch_event
,我们继续看dispatch_event
是如何对某些按键进行特殊处理的。
路径:vendor/qcom/proprietary/fastmmi/mmi/input_listener_key.cpp
bool input_listener_key::dispatch_event(input_event ev) {
layout *lay = this->get_lay();
char btn_name[64] = { 0 };
__u16 type = ev.type;
__u16 code = ev.code;
__u32 value = ev.value;
mod_ev_t modev;
modev.mod = this->get_module();
int down = ! !value;
if(type == EV_KEY) {
switch (code) {
case KEY_BACK: // 在 fastmmi中, KEY_BACK 对应的按键应该是 fail
strlcpy(btn_name, KEY_FAIL, sizeof(btn_name));
break;
case KEY_HOMEPAGE:
ev.code = KEY_HOME; //change the code to KEY_HOMEPAGE
// ...
default:
break;
}
}
// ...
button *btn = lay->find_button_by_name(btn_name);
if(btn != NULL) {
if(down) {
MMI_ALOGI("button(%s) press down, code=%d", btn->get_name(), code);
btn->set_color(255, 0, 0, 125);
cb_t cb = this->get_cb();
modev.ev = &ev;
if(cb != NULL)
cb(&modev);
} else {
MMI_ALOGI("button(%s) release, code=%d", btn->get_name(), code);
btn->set_color(255, 0, 0, 255);
}
MMI_ALOGW("Button Pushed");
invalidate();
} else {
MMI_ALOGW("Not find button(%s) in layout(%s)", btn_name, lay->get_layout_path());
}
#if 0 // 解除对 back 键的屏蔽。
// 关闭这2行,以使得返回值为真,则最后能够将事件传递下去
if(code == KEY_BACK)
return false;
#endif
return true;
}
处理按键
现在,按下的按键能够调用key_callback
了。我们只需要在这里进行我们要的特殊处理即可。
路径:vendor/qcom/proprietary/fastmmi/mmi/input.cpp
省略了针对 有关值的定义以及函数声明。
static int key_callback(int type, int code, int value) {
int down = ! !value;
if(type != EV_KEY) {
return 0;
}
if(!down) {
return 0;
}
MMI_ALOGI("key:%d release", code);
switch (code)
{
#if 1
// 用于确认结果
case KEY_BACK :
mark_this_test_module_result(module_failed); // 新增的函数
break; case KEY_ENTER:
mark_this_test_module_result(module_success);
break; default:
return 0;
break;
}
#endif
return 0;
}
按照之前说的“流程分析”,我是这么写的:
很简单,还是按照3步走。
// vendor/qcom/proprietary/fastmmi/mmi/mmi.cpp
// 用于手动标记测试结果成功或者失败
void mark_this_test_module_result(bool result)
{
int key_test_result = 0;
/* step one: Get layout */
module_info *mod = get_main_module();
if(mod == NULL) {
MMI_ALOGE("%s Not main screen OR Null point",__FUNCTION__);
return;
}
layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
if(lay == NULL || lay->m_listview == NULL) {
MMI_ALOGE("%s No Main layout",__FUNCTION__);
return;
}
/* step two: Find button */
layout *curlay = acquire_cur_layout();
button *btn_pass = curlay->find_button_by_name("pass");
button *btn_fail = curlay->find_button_by_name("fail");
if(btn_pass == NULL && btn_fail == NULL){
MMI_ALOGE("[%s] FCT Confirm Result Button is NULL", __FUNCTION__);
release_cur_layout();
return ;
}
/* step three: exec button click */
runnable_t *r = new runnable_t;
button *btn = NULL;
r->cb = NULL;
r->module = NULL;
if(result == module_success) {
MMI_ALOGI("[%s] FCT Confirm Result Button is PASS", __FUNCTION__);
btn = btn_pass;
btn->set_disabled(true);
r->cb = btn->get_cb();
}else if( result == module_failed) {
MMI_ALOGI("[%s] FCT Confirm Result Button is FAIL", __FUNCTION__);
btn = btn_fail;
btn->set_disabled(true);
r->cb = btn->get_cb();
}
r->module = curlay->module;
MMI_ALOGE("[%s]:[%s] Receive Terminal Signal:(E)", __FUNCTION__, curlay->module->module);
release_cur_layout();
if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
module_info *rmod = (module_info *)(r->module);
ALOGI("[%s] Callback Activated Module:%s", __FUNCTION__, rmod->module);
r->cb(r->module);
}
if(btn != NULL && btn->get_disabled()){
ALOGI("[%s] Btn Set Enable", __FUNCTION__);
btn->set_disabled(false);
btn = NULL;
}
return;
}
这样子就完成了。
新建线程用于自动交互
目的:实现在某个条件下,定期轮询获取需要执行的条目即可。
做法:
1、实现对应的功能,并实现每次调用某个函数则得到不同的结果。
2、按着fastmmi的规范实现模拟按键功能点击(参考如上)
3、在某个时候启动这条线程
// 自动测试 功能 添加
struct test_item_for_keypad_ui {
char * module_name; // 对应的界面
char * item_name; // 测试项名称
//int need_comfirm; // 如果这一项需要人工确认,则为1,能够自动测试,则为0
};
#define TEST_TIEM_ARRAY_SIZE(obj) (sizeof((obj))/sizeof(struct test_item_for_keypad_ui))
struct test_item_for_keypad_ui fct_test_item_array[] =
{
{"KEY", "Key_Start", 1 }, // 需要处理对应的按键测试内容
{"LCD", "Lcd_Start", 1 },
{"LCM_BACKLIGHT", "Lcd_Backlight_Start", 1 },
{"LED", "Led_Start", 1 },
{"BUTTON_BACKLIGHT", "Button_Backlight_Start", 1 },
{"BLUETOOTH", "BT_Start", 0 },
{"WIFI", "WIFI_Start", 0 },
{"GPS", "GPS_Start", 0 },
{"NETSIGNAL", "Netsignal_Start", 0 },
{"SIMCARD1", "Sim_Start", 0 },
{"SDCARD", "Sd_Start", 0 },
{"BATTERY", "Battery_Start", 0 },
{"PRIMARY MIC", "Primary_Mic_Start", 0 },
{"SPEAKER", "Speaker_Start" , 0 },
{"HANDSET PLAY", "Headset_Start", 0 },
{"HEADSET MIC", "Headset_Mic_Start" , 0 },
{"GSENSOR", "Gsensor_Start", 1 }, // 由于这一项会在内部提前结束,因此必须放在最后(调试这块的功能不再本文关心的范围内)
// 以下部分 算是 保留项目
//{"VERSION", "Version_Start" },
};
static int item_index = 0;
int get_next_test_item(char*buff)
{
if(!buff) return -1;
if((item_index ) >= TEST_TIEM_ARRAY_SIZE(fct_test_item_array))
{
ALOGE("full\n");
sleep(5);
return -1;
}
ALOGE("get cmd buff from array : [%s]\n", fct_test_item_array[item_index].item_name);
ALOGE("item_index / Max : %d/%ld\n", item_index+1, TEST_TIEM_ARRAY_SIZE(fct_test_item_array));
sprintf(buff, "%s", fct_test_item_array[item_index].item_name);
item_index ++;
return 1;
}
void reset_test_item_index(void)
{
item_index = 0;
}
void get_back_this_test_item(void)
{
if(item_index)
item_index--;
}
int is_buff_in_test_array(char *buff)
{
int i;
for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
{
if(!strcmp(buff, fct_test_item_array[i].item_name))
return 1;
}
return 0;
}
int is_mod_in_test_array(char *mod_name, char*buff)
{
int i;
if(!mod_name || !buff) return -1;
for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
{
// 找到对应的模块
if(!strcmp(mod_name, fct_test_item_array[i].module_name))
{
// 判断此时的命令是否匹配
if(!strcmp(buff, fct_test_item_array[i].item_name))
return 1;
}
}
return 0;
}
/*! \enum test_thread_loop_type
*
* 判断auto_loop_for_each_cmd_thread进入了哪个if
*/
enum test_thread_loop_type {
loop_no_set, // 默认状态
loop_for_end, // 命令结束
loop_for_build_in_auto_test, // 内置的自动测试命令
loop_for_page_up_down, // 翻页命令
loop_for_get_result, // 获取总结果的命令
loop_for_test_single_item, // 匹配测试的每一项
};
static void *auto_loop_for_each_cmd_thread(void *)
{
int loop_type = loop_no_set;
char buff[255];
void *ate_module = NULL;
bool ate_test = false;
button *btn = NULL;
int ret;
ALOGE("Schips create auto_loop_for_each_cmd_thread \n");
signal(SIGUSR1, signal_handler);
while(1)
{
sleep(1);
memset(buff,0,255);
// wait_for_auto_test(); 实现这个接口的阻塞等待与唤醒即可完成在某个时候自动执行
loop_while:
while((get_next_test_item(buff)==1))
{
ALOGE("this cmd is [%s] \n",buff);
button *btn = NULL;
ate_test = false;
/*step one:Get the main layout */
module_info *mod = get_main_module();
if(mod == NULL) {
ALOGE("%s Not main screen",__FUNCTION__);
get_back_this_test_item();
break;
}
layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
if(lay == NULL || lay->m_listview == NULL) {
ALOGE("%s No Main layout",__FUNCTION__);
get_back_this_test_item();
break;
} else
{
// 检查 当前是否 有 正在运行的模块
list < item_t * >*items = lay->m_listview->get_items();
list < item_t * >::iterator iter;
for(iter = items->begin(); iter != items->end(); iter++)
{
item_t *item = (item_t *) (*iter);
module_info *tmod = item->mod;
if(tmod->running_state == MODULE_RUNNING)
{
ALOGI("[%s] FCT module [%s] is in running,please waiting", __FUNCTION__, tmod->module);
ate_test = true;
get_back_this_test_item();
sleep(1);
goto loop_while;
}
}
}
runnable_t *r = new runnable_t;
r->cb = NULL;
r->module = NULL;
if(is_buff_in_test_array(buff)) // !strcmp(buff,"Key_Start") || !strcmp(buff,"Lcd_Start") || ...
{
ALOGI("[%s][%d] Receive ATE Test Command:%s", __FUNCTION__, __LINE__, buff);
/*step two:check modules running state */ // 由于 这里还需要进行界面切换,因此需要先判断这一步。
if(lay != NULL && lay->m_listview != NULL)
{
list < item_t * >*items = lay->m_listview->get_items();
list < item_t * >::iterator iter;
for(iter = items->begin(); iter != items->end(); iter++)
{
item_t *item = (item_t *) (*iter);
module_info *tmod = item->mod;
if(tmod->running_state == MODULE_RUNNING){
ALOGI("[%s] FCT module %s is in running,please waiting", __FUNCTION__, tmod->module);
ate_test = true;
}
if(!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start"))
{
clear_all_pushed_keys();
}
#if 0
if((!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start")) ||
(!strcmp(tmod->module,"LCD") && !strcmp(buff,"Lcd_Start")) ||
// ...
#else
if(is_mod_in_test_array(tmod->module, buff))
#endif
{
ALOGI("[%s] : [%s]-module, cmd is [%s]", __FUNCTION__, tmod->module, buff);
ate_module = tmod;
r->module = ate_module;
r->cb = lay->m_listview->get_cb();
//set_mmi_response(resp_buf_ok);
loop_type = loop_for_test_single_item;
}
}
}
}
if(ate_test)
{
ate_module = NULL;
r->module = NULL;
r->cb = NULL;
get_back_this_test_item();
break;
}
// 执行动作
if(loop_type == loop_no_set)
{
ALOGE("[%s][%d] Nothing to do,Start Next Loop", __FUNCTION__, __LINE__);
}else if((r != NULL) && (r->cb != NULL) && (r->module != NULL))
{
module_info *rmod = (module_info *)(r->module);
ALOGI("[%s] Callback Activated Module:%s Command:%s", __FUNCTION__, rmod->module, buff);
ALOGE("[%s][%d] cmd is [%s]", __FUNCTION__, __LINE__, buff);
// 只关心单项测试
if(loop_type == loop_for_test_single_item)
{
r->cb(r->module);
}
}
if(btn != NULL && btn->get_disabled())
{
ALOGI("[%s] Btn Set Enable", __FUNCTION__);
btn->set_disabled(false);
btn = NULL;
}
sleep(2);
}
}
return NULL;
}
附录:分析r->cb(r->module);
fastmmi是如何实现r->cb(r->module);
的,其实我也很好奇,因为没有找到具体的按键功能实现。
所以特意翻了一下代码,看了一下,大概知道是,如果“有添加布局的需求”,那么可以好好研究一下:
1、标记按钮对应的按键功能。
2、实现对应的方法,并绑定功能与对应的组件。(通过C++中STL的map的方式)
其中涉及到 xml 的解析就不说了,纯应用层的东西,很多途径可以实现。
mmi/config.cpp:166: } else if(!xmlStrcmp(attr->name, (const xmlChar *) "onclick")) {
in vendor/qcom/proprietary/fastmmi
===============================
# res/layout/layout_xxx.xml (任意一个)
<layout>
<!--
....
-->
<include layout="footer.xml"/>
</layout>
===============================
# res/layout/footer.xml
<button
name="pass"
onclick="do_pass"
text="btn_pass"
h_rel="16"
w_rel="49"
x_rel="0"
y_rel="84"
color="0x007D7Dff" />
===============================
# mmi/func_map.cpp:
void process_exit(void *m) {
if(m == NULL) return;
module_info *mod = (module_info *) m;
mod->running_state = MODULE_IDLE;
flush_result();
module_cleanup(mod);
ALOGI("[%s] Test finished with result =%d ", mod->module, mod->result);
launch_main();
usleep(100);
sem_post(&g_sem_mod_complete);
}
void process_exit(void *m, int result) {
if(m == NULL) {
MMI_ALOGE("Invalid parameter");
return;
}
module_info *mod = (module_info *) m;
time(&mod->last_time);
mod->duration = difftime(mod->last_time, mod->start_time);
mod->result = result;
MMI_ALOGI("[%s] Test finished with result=%s, test duration=%f seconds",
mod->module, MMI_TEST_RESULT(result), mod->duration);
process_exit(m);
}
static void do_pass(void *m) {
process_exit(m, SUCCESS);
}
static void do_fail(void *m) {
sem_post(&g_sem_confirm);
process_exit(m, FAILED);
}
static func_map_t func_list[] = {
{"do_cancel", do_cancel},
{"do_extra_cmd", do_extra_cmd},
{"do_fail", do_fail},
{"do_ok", do_ok},
{"do_report", do_report},
{"do_page_down", do_page_down},
{"do_page_up", do_page_up},
{"do_pass", do_pass},
{"switch_module", switch_module},
{"do_reboot", do_reboot},
{"do_run_all", do_run_all},
{"do_reset", do_reset},
{"do_show_fail", do_show_fail},
{"do_show_all", do_show_all},
#ifdef ANDROID
{"do_next", do_next},
#endif
{"do_exit", do_exit},
{"onchange_poweroff", onchange_poweroff},
{"onchange_reboot_ffbm", onchange_reboot_ffbm},
{"onchange_reboot_android", onchange_reboot_android},
};
static unordered_map < string, cb_t > func_map;
void create_func_map() {
uint32_t i = 0;
for(i = 0; i < sizeof(func_list) / sizeof(func_map_t); i++) {
func_map[(string) func_list[i].name] = func_list[i].cb;
}
}
cb_t get_cb(string func_name) {
return func_map[func_name];
}
===============================
# libmmi/common.h
typedef void (*cb_t) (void *);
class module_info {
public:
char module[64];
int socket_fd;
int result;
pid_t pid;
int mode;
int running_state;
extra_cmd_t extracmd;
time_t start_time; //start test time
double duration; //test duration
time_t last_time; //last time to modify test result data
char data[SIZE_512]; //module test data
unordered_map < string, string > config_list;
module_info(char *mod) {
if(mod != NULL)
strlcpy(module, mod, sizeof(module));
memset(data, 0, sizeof(data));
result = INT_MAX;
pid = -1;
socket_fd = -1;
extracmd.is_valid = false;
running_state = MODULE_IDLE;
}
};
typedef struct {
char name[32];
cb_t cb;
} func_map_t;
===============================
# mmi/config.cpp
static void parse_button(xmlNodePtr node, button * btn) {
xmlAttrPtr attr;
rect_t rect;
attr = node->properties;
while(attr != NULL) {
char *value = (char *) xmlGetProp(node, (const xmlChar *) attr->name);
if(value != NULL) {
if(!xmlStrcmp(attr->name, (const xmlChar *) "name")) {
btn->set_name(value);
} else if(!xmlStrcmp(attr->name, (const xmlChar *) "text"))
btn->set_text(get_string(value));
else if(!xmlStrcmp(attr->name, (const xmlChar *) "image")) {
btn->set_image(value);
} else if(!xmlStrcmp(attr->name, (const xmlChar *) "onclick")) { // 这里对应的是值是 do_pass、do_failed 等
btn->set_cb(get_cb(value));
} else if // ...
} else if(!xmlStrcmp(attr->name, (const xmlChar *) "visibility")) {
if(!strcmp("invisible", value))
btn->set_visibility(false);
else
btn->set_visibility(true);
}
xmlFree(value);
}
attr = attr->next;
}
btn->set_rect(&rect);
}
注:fastmmi 的代码看着还是可圈可点的,供学习的地方也很多。这里提到的事件分发和功能组件化映射只是其中的一小部分。
附录:机器上的按键值
getevent -l
===============================================
add device 5: /dev/input/event3
name: "gpio-keys"
# .
/dev/input/event3: EV_KEY KEY_F2 DOWN
/dev/input/event3: EV_SYN SYN_REPORT 00000000
/dev/input/event3: EV_KEY KEY_F2 UP
/dev/input/event3: EV_SYN SYN_REPORT 00000000
# PTT
/dev/input/event3: EV_KEY KEY_F1 DOWN
/dev/input/event3: EV_SYN SYN_REPORT 00000000
/dev/input/event3: EV_KEY KEY_F1 UP
# ..
/dev/input/event3: EV_SYN SYN_REPORT 00000000
/dev/input/event3: EV_KEY KEY_F3 DOWN
/dev/input/event3: EV_SYN SYN_REPORT 00000000
/dev/input/event3: EV_KEY KEY_F3 UP
/dev/input/event3: EV_SYN SYN_REPORT 00000000
# emergency
/dev/input/event3: EV_KEY KEY_F4 DOWN
/dev/input/event3: EV_SYN SYN_REPORT 00000000
/dev/input/event3: EV_KEY KEY_F4 UP
/dev/input/event3: EV_SYN SYN_REPORT 00000000
===============================================
add device 3: /dev/input/event2
name: "qpnp_pon"
# trick-power
旋钮 的 按键电源键(POWER, OK)
/dev/input/event2: EV_KEY KEY_POWER DOWN
/dev/input/event2: EV_SYN SYN_REPORT 00000000
/dev/input/event2: EV_KEY KEY_POWER UP
/dev/input/event2: EV_SYN SYN_REPORT 00000000
===============================================
add device 4: /dev/input/event1
name: "gpiokey-pulley"
旋钮 相关 : 音量 上下(OK)
# anticlockwise
/dev/input/event1: EV_KEY KEY_VOLUMEUP DOWN
/dev/input/event1: EV_SYN SYN_REPORT 00000000
/dev/input/event1: EV_KEY KEY_VOLUMEUP UP
/dev/input/event1: EV_SYN SYN_REPORT 00000000
# clockwise
/dev/input/event1: EV_KEY KEY_VOLUMEDOWN DOWN
/dev/input/event1: EV_SYN SYN_REPORT 00000000
/dev/input/event1: EV_KEY KEY_VOLUMEDOWN UP
/dev/input/event1: EV_SYN SYN_REPORT 00000000
===============================================
add device 6: /dev/input/event0
name: "aw9523-keys"
.. ↑ ..
← →
拨号键 电源键
1 2 3
4 5 6
7 8 9
* 0 #
# ..
/dev/input/event0: EV_KEY KEY_ENTER DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_ENTER UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# ↑
/dev/input/event0: EV_KEY KEY_UP DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_UP UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# ..
/dev/input/event0: EV_KEY KEY_BACK DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_BACK UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# ←
/dev/input/event0: EV_KEY KEY_LEFT DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_LEFT UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# →
/dev/input/event0: EV_KEY KEY_RIGHT DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_RIGHT UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 拨号键
/dev/input/event0: EV_KEY KEY_SEND DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_SEND UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# ↓
/dev/input/event0: EV_KEY KEY_DOWN DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_DOWN UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 电源键(挂断键)
/dev/input/event0: EV_KEY KEY_ESC DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_ESC UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 1
/dev/input/event0: EV_KEY KEY_1 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_1 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 2
/dev/input/event0: EV_KEY KEY_2 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_2 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 3
/dev/input/event0: EV_KEY KEY_3 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_3 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 4
/dev/input/event0: EV_KEY KEY_4 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_4 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 5
/dev/input/event0: EV_KEY KEY_5 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_5 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 6
/dev/input/event0: EV_KEY KEY_6 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_6 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 7
/dev/input/event0: EV_KEY KEY_7 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_7 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 8
/dev/input/event0: EV_KEY KEY_8 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_8 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 9
/dev/input/event0: EV_KEY KEY_9 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_9 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# *
/dev/input/event0: EV_KEY KEY_NUMERIC_STAR DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_NUMERIC_STAR UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# 0
/dev/input/event0: EV_KEY KEY_0 DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_0 UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
# #
/dev/input/event0: EV_KEY KEY_NUMERIC_POUND DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_KEY KEY_NUMERIC_POUND UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
在高通Fastmmi模式中增强交互方式的更多相关文章
- Android上HDMI介绍(基于高通平台)
本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制.overlay在这里只是简单的介绍,后续会有文章再专门详述. 我没记错的话,高通从7X30开始,平台就可以支持HDMI ...
- 高通平台msm8909 LK 实现LCD 兼容
前段时间小米出现红米note2 换屏门,现在我们公司也要上演了:有两个供应商提供不同IC 的LCD panel. 软件区分的办法是读取LCD IC 的ID 寄存器,下面解析高通平台LK中LCD兼容的过 ...
- 在高通平台Android环境下编译内核模块【转】
本文转载自:http://blog.xeonxu.info/blog/2012/12/04/zai-gao-tong-ping-tai-androidhuan-jing-xia-bian-yi-nei ...
- 高通cDSP简单编程例子(实现查询高通cDSP使用率、签名),RK3588 npu使用率查询
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- 高通平台FastMMI(FFBM模式)简介与进入方法
参考: http://blog.csdn.net/tfslovexizi/article/details/51499979 http://www.voidcn.com/blog/jimbo_lee/a ...
- 高通方案的Android设备几种开机模式的进入与退出
高通方案的Android设备主要有以下几种开机模式,Android.EDL.Fastboot.Recovery和FFBM,其进入及退出的方式如下表. 开机模式 屏幕显示 冷启动 热启动 按键退出 命令 ...
- 高通电池管理基于qpnp-vm-bms电压模式
CV:Constant Voltage恒压 SMMB charger:Switch-ModeBattery Charger and Boost peripheral开关模式电池充电器和升压外围设备 O ...
- 高通ASOC中的machine驱动
ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...
- 高通ASOC中的codec驱动
ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...
- 高通Audio中ASOC的machine驱动(一)
ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...
随机推荐
- 登录信息localStorage存储
localStorage拓展了cookie的4K限制,与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,ses ...
- 使用 Docker 部署 TaleBook 私人书籍管理系统
1)项目介绍 GitHub:https://github.com/talebook/talebook Talebook 是一个简洁但强大的私人书籍管理系统.它基于 Calibre 项目构建,具备书籍管 ...
- List集合中获取重复元素
一.方法1 ## 测试数据 List<String> words = Arrays.asList("a", "b", "c", ...
- ubuntu编译与安装 OpenSSL-1.0.0
apt-get purge openssl rm -rf /etc/ssl #删除配置文件 编译与安装 OpenSSL prefix 是安装目录,openssldir 是配置文件目录,另外建议安装两次 ...
- ETSI GS MEC 013,UE 位置 API
目录 文章目录 目录 版本 功能理解 Relation with OMA APIs Relation with OMA API for Zonal Presence Relation with OMA ...
- vue3:modal组件开发
项目环境 @vue/cli 4.5.8 最终效果 需求分析 显示/隐藏 点击遮罩层能否关闭 宽度和zIndex自定义 标题栏 -显示标题和关闭按钮 主体 底部 -内置取消和确定功能 前置知识 tele ...
- linux下YUM工具的使用:yum安装/升级/查看/搜索/卸载软件包
目录 一.关于软件包 二.关于YUM 三.yum工具的使用 3.1 yum安装软件功能 3.2 yum升级软件包功能 3.3 yum查看,搜索功能 3.4 yum卸载功能 3.5 yum安装软件包组功 ...
- 工作面试老大难-MySQL中的锁类型
MySQL 是支持ACID特性的数据库.我们都知道"C"代表Consistent,当不同事务操作同一行记录时,为了保证一致性,需要对记录加锁.在MySQL 中,不同的引擎下的锁行为 ...
- 2024盘古石取证比赛(APK)
题目列表 使用软件: Notepad++,火眼证据分析软件,雷电分析app,DB browser for SQLCipher 1. 分析伏季雅的手机检材,手机中诈骗APP的包名是:[答案格式:abc. ...
- C#简易商城收银系统v1.0(2-1)
C#简易商城收银系统v1.0(2-1) 当初: 面向编程对象的好处及应用简单工厂模式(继承,多态) 现在: 制作一个简易的收银窗体应用程序 可以参考之前的 计算器 随笔 创建窗体程序 客户端代码 us ...