在高通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. 通俗易懂的生产环境Web应用架构介绍

    前言 看见一篇非常通俗易懂且适合新手阅读的Web应用架构文章,我将其手工翻译了出来,分享给大家. 也可以去阅读英文原文,标题为,贴出链接: https://stephenmann.io/post/wh ...

  2. 51k+ Star!动画图解、一键运行的数据结构与算法教程!

    大家好,我是 Java陈序员. 我们都知道,<数据结构与算法> -- 是程序员的必修课. 无论是使用什么编程语音,亦或者是前后端开发,都需要修好<数据结构与算法>这门课! 在各 ...

  3. SAP HANA计算视图

    Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. 越来越多的SAP用户正在将SAP HANA实施为现有SAP BW的基础和数据库. ...

  4. 一个用Python将视频变为表情包的工具

    这是一个将视频转变为表情包的工具,现实生活中当我们看到一段搞笑的视频,我们可以将这段视频喂给这段程序,生成gif表情包,这样就可以用来舍友斗图了 1.一些限制 1.这个程序不能转化超过15秒以上的视频 ...

  5. dotnet build error CS5001: Program does not contain a static 'Main' method suitable for an entry point

    前言 Docker环境编译.Net6项目,出现诡异的CS5001 Program does not contain a static 'Main' method suitable for an ent ...

  6. ITSM2023年十大功能趋势[采和]

    总体描述:更加人性化,引入自动化相关的设计和技术,更加实用好用.1. 100%服务目录服务目录必须完全贴合用户方的运维实际开展的 服务清单,而不是想当然的抄书或者臆想!都2023年了,还有完全不着调的 ...

  7. Linux下vim的常用命令总结

    vim按d表示剪切 按dd剪切一行 vim命令:命令模式 /关键字 n继续向下查找 vim的多行注释: 1.按ctrl + v进入 visual block模式 2.按上下选中要注释的行 3.按大写字 ...

  8. C语言:判断是否为素数,并且打印素数表

    /*     构造素数表, 只需要用数字除以已经判断出来的数是否能整除就行,     不需要除以这个数之前所有的数字,     前提是这个数除以的素数是要比他自己小的      */ 注意一点:int ...

  9. 如何使用 JavaScript 获取当前页面帧率 FPS

    可以通过计算每秒 window.requestAnimationFrame 的调用频率来做为 FPS 值.它接收一个回调函数,该回调函数会在浏览器下一次重绘之前执行.所以只要我们循环调用并记录单位时间 ...

  10. C#老码农的职业生涯

    开头白 大家好,我是tibos,19年10月1号由深圳回武汉的码农,目前入职武汉福禄网络,最近刷到的年终总结也比较多,赶在这最后一天,我也来凑个热闹 心路历程 -> 菜鸟入江湖 13年开启码农的 ...