在高通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、找到我们需要执行的按钮(例如,passfailed

通过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模式中增强交互方式的更多相关文章

  1. Android上HDMI介绍(基于高通平台)

    本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制.overlay在这里只是简单的介绍,后续会有文章再专门详述. 我没记错的话,高通从7X30开始,平台就可以支持HDMI ...

  2. 高通平台msm8909 LK 实现LCD 兼容

    前段时间小米出现红米note2 换屏门,现在我们公司也要上演了:有两个供应商提供不同IC 的LCD panel. 软件区分的办法是读取LCD IC 的ID 寄存器,下面解析高通平台LK中LCD兼容的过 ...

  3. 在高通平台Android环境下编译内核模块【转】

    本文转载自:http://blog.xeonxu.info/blog/2012/12/04/zai-gao-tong-ping-tai-androidhuan-jing-xia-bian-yi-nei ...

  4. 高通cDSP简单编程例子(实现查询高通cDSP使用率、签名),RK3588 npu使用率查询

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  5. 高通平台FastMMI(FFBM模式)简介与进入方法

    参考: http://blog.csdn.net/tfslovexizi/article/details/51499979 http://www.voidcn.com/blog/jimbo_lee/a ...

  6. 高通方案的Android设备几种开机模式的进入与退出

    高通方案的Android设备主要有以下几种开机模式,Android.EDL.Fastboot.Recovery和FFBM,其进入及退出的方式如下表. 开机模式 屏幕显示 冷启动 热启动 按键退出 命令 ...

  7. 高通电池管理基于qpnp-vm-bms电压模式

    CV:Constant Voltage恒压 SMMB charger:Switch-ModeBattery Charger and Boost peripheral开关模式电池充电器和升压外围设备 O ...

  8. 高通ASOC中的machine驱动

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

  9. 高通ASOC中的codec驱动

    ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...

  10. 高通Audio中ASOC的machine驱动(一)

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

随机推荐

  1. 登录信息localStorage存储

    localStorage拓展了cookie的4K限制,与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,ses ...

  2. 使用 Docker 部署 TaleBook 私人书籍管理系统

    1)项目介绍 GitHub:https://github.com/talebook/talebook Talebook 是一个简洁但强大的私人书籍管理系统.它基于 Calibre 项目构建,具备书籍管 ...

  3. List集合中获取重复元素

    一.方法1 ## 测试数据 List<String> words = Arrays.asList("a", "b", "c", ...

  4. ubuntu编译与安装 OpenSSL-1.0.0

    apt-get purge openssl rm -rf /etc/ssl #删除配置文件 编译与安装 OpenSSL prefix 是安装目录,openssldir 是配置文件目录,另外建议安装两次 ...

  5. ETSI GS MEC 013,UE 位置 API

    目录 文章目录 目录 版本 功能理解 Relation with OMA APIs Relation with OMA API for Zonal Presence Relation with OMA ...

  6. vue3:modal组件开发

    项目环境 @vue/cli 4.5.8 最终效果 需求分析 显示/隐藏 点击遮罩层能否关闭 宽度和zIndex自定义 标题栏 -显示标题和关闭按钮 主体 底部 -内置取消和确定功能 前置知识 tele ...

  7. linux下YUM工具的使用:yum安装/升级/查看/搜索/卸载软件包

    目录 一.关于软件包 二.关于YUM 三.yum工具的使用 3.1 yum安装软件功能 3.2 yum升级软件包功能 3.3 yum查看,搜索功能 3.4 yum卸载功能 3.5 yum安装软件包组功 ...

  8. 工作面试老大难-MySQL中的锁类型

    MySQL 是支持ACID特性的数据库.我们都知道"C"代表Consistent,当不同事务操作同一行记录时,为了保证一致性,需要对记录加锁.在MySQL 中,不同的引擎下的锁行为 ...

  9. 2024盘古石取证比赛(APK)

    题目列表 使用软件: Notepad++,火眼证据分析软件,雷电分析app,DB browser for SQLCipher 1. 分析伏季雅的手机检材,手机中诈骗APP的包名是:[答案格式:abc. ...

  10. C#简易商城收银系统v1.0(2-1)

    C#简易商城收银系统v1.0(2-1) 当初: 面向编程对象的好处及应用简单工厂模式(继承,多态) 现在: 制作一个简易的收银窗体应用程序 可以参考之前的 计算器 随笔 创建窗体程序 客户端代码 us ...