platform: msm_shared: add API to show menu on screen in lk

This change add API to show menu on screen and add keys detection
to switch diffrent menu.

Change-Id: I2cd1416302a65e855a68f45d08ad9ec3f7532b2e
diff --git a/platform/msm_shared/display_menu.c b/platform/msm_shared/display_menu.c
new file mode 100644
index 0000000..2a792b2
--- /dev/null
+++ b/platform/msm_shared/display_menu.c
@@ -0,0 +1,339 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*	notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*	copyright notice, this list of conditions and the following
+*	disclaimer in the documentation and/or other materials provided
+*	with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*	contributors may be used to endorse or promote products derived
+*	from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <debug.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <openssl/evp.h>
+#include <dev/fbcon.h>
+#include <kernel/thread.h>
+#include <display_menu.h>
+#include <menu_keys_detect.h>
+#include <boot_verifier.h>
+#include <string.h>
+#include <platform.h>
+
+#define UNLOCK_OPTION_NUM		2
+#define BOOT_VERIFY_OPTION_NUM		5
+
+static char *unlock_menu_common_msg = "If you unlock the bootloader, "\
+				"you will be able to install "\
+				"custom operating system on this phone.\n\n"\
+				"A custom OS is not subject to the same testing "\
+				"as the original OS, "\
+				"and can cause your phone and installed "\
+				"applications to stop working properly.\n\n"\
+				"To prevent unauthorized access to your personal data, "\
+				"unlocking the bootloader will also delete all personal "\
+				"data from your phone(a \"fatory data reset\").\n\n"\
+				"Press the Volume Up/Down buttons to select Yes "\
+				"or No. Then press the Power button to continue.\n";
+
+static char *yellow_warning_msg = "Your device has loaded a diffrent operating "\
+				"system\n\nTo learn more, visit:\n";
+
+static char *orange_warning_msg = "Your device has been unlocker and cann't "\
+				"be trusted\n\nTo learn more, visit:\n";
+
+static char *red_warning_msg = "Your device has failed verification and may "\
+				"not work properly\n\nTo learn more, visit:\n";
+
+static bool is_thread_start = false;
+struct select_msg_info msg_info;
+
+static char *option_menu[] = {
+		[POWEROFF] = "Power off\n",
+		[RESTART] = "Restart\n",
+		[RECOVER] = "Recovery\n",
+		[FASTBOOT] = "Fastboot\n",
+		[BACK] = "Back to previous page\n"};
+
+static int big_factor = 2;
+static int common_factor = 1;
+
+void wait_for_users_action()
+{
+	struct select_msg_info *select_msg;
+	select_msg = &msg_info;
+
+	while(1) {
+		if (select_msg->msg_timeout == true &&
+			select_msg->msg_volume_key_pressed == false)
+			break;
+		if (select_msg->msg_power_key_pressed == true)
+			break;
+
+		thread_sleep(10);
+	}
+	fbcon_clear();
+	display_image_on_screen();
+}
+
+static void set_message_factor()
+{
+	uint32_t tmp_factor = 0;
+	uint32_t max_x_count = 40;
+	uint32_t max_x = fbcon_get_max_x();
+
+	max_x = fbcon_get_max_x();
+	tmp_factor = max_x/max_x_count;
+
+	if(tmp_factor <= 1) {
+		big_factor = 2;
+		common_factor = 1;
+	} else {
+		big_factor = tmp_factor*2;
+		common_factor = tmp_factor;
+	}
+}
+
+static void display_fbcon_menu_message(char *str, unsigned type,
+	unsigned scale_factor)
+{
+	while(*str != 0) {
+		fbcon_putc_factor(*str++, type, scale_factor);
+	}
+}
+
+static char *str_align_right(char *str, int factor)
+{
+	uint32_t max_x = 0;
+	int diff = 0;
+	int i = 0;
+	char *str_target = NULL;
+
+	max_x = fbcon_get_max_x();
+	if (!str_target && max_x) {
+		str_target = malloc(max_x);
+	}
+
+	if (str_target) {
+		memset(str_target, 0, max_x);
+		if ( max_x/factor > strlen(str)) {
+			if (factor == 1)
+				diff = max_x/factor - strlen(str) - 1;
+			else
+				diff = max_x/factor - strlen(str);
+			for (i = 0; i < diff; i++) {
+				strcat(str_target, " ");
+			}
+			strcat(str_target, str);
+			return str_target;
+		} else {
+			free(str_target);
+			return str;
+		}
+	}
+	return str;
+}
+
+void display_unlock_menu(struct select_msg_info *unlock_msg_info)
+{
+	fbcon_clear();
+	memset(unlock_msg_info, 0, sizeof(struct select_msg_info));
+	display_fbcon_menu_message("Unlock bootloader?\n",
+		FBCON_UNLOCK_TITLE_MSG, big_factor);
+	fbcon_draw_line();
+
+	display_fbcon_menu_message(unlock_menu_common_msg,
+		FBCON_COMMON_MSG, common_factor);
+	fbcon_draw_line();
+	unlock_msg_info->option_start[0] = fbcon_get_current_line();
+	display_fbcon_menu_message("Yes\n",
+		FBCON_COMMON_MSG, big_factor);
+	unlock_msg_info->option_bg[0] = fbcon_get_current_bg();
+	display_fbcon_menu_message("Unlock bootloader(may void warranty)\n",
+		FBCON_COMMON_MSG, common_factor);
+	unlock_msg_info->option_end[0] = fbcon_get_current_line();
+	fbcon_draw_line();
+	unlock_msg_info->option_start[1] = fbcon_get_current_line();
+	display_fbcon_menu_message("No\n",
+		FBCON_COMMON_MSG, big_factor);
+	unlock_msg_info->option_bg[1] = fbcon_get_current_bg();
+	display_fbcon_menu_message("Do not unlock bootloader and restart phone\n",
+		FBCON_COMMON_MSG, common_factor);
+	unlock_msg_info->option_end[1] = fbcon_get_current_line();
+	fbcon_draw_line();
+
+	unlock_msg_info->msg_type = DISPLAY_MENU_UNLOCK;
+	unlock_msg_info->option_num = UNLOCK_OPTION_NUM;
+}
+
+void display_boot_verified_menu(struct select_msg_info *msg_info, int type)
+{
+	int msg_type = FBCON_COMMON_MSG;
+	char *warning_msg = NULL;
+	unsigned char* fp_buf = NULL;
+	char fp_str_temp[EVP_MAX_MD_SIZE] = {'\0'};
+	char fp_str[EVP_MAX_MD_SIZE*2] = {'\0'};
+	char str_temp[8];
+
+	char str1[]= "Start >";
+	char str2[] = "Continue boot";
+	char *str_target = NULL;
+	uint32 fp_size = 0;
+	unsigned int i = 0;
+
+	fbcon_clear();
+	memset(msg_info, 0, sizeof(struct select_msg_info));
+
+	switch (type) {
+	case DISPLAY_MENU_RED:
+		msg_type = FBCON_RED_MSG;
+		warning_msg = red_warning_msg;
+		break;
+        case DISPLAY_MENU_YELLOW:
+		msg_type = FBCON_YELLOW_MSG;
+		warning_msg = yellow_warning_msg;
+                break;
+	case DISPLAY_MENU_ORANGE:
+		msg_type = FBCON_ORANGE_MSG;
+		warning_msg = orange_warning_msg;
+                break;
+	}
+
+	/* Align Right */
+	str_target = str_align_right(str1, big_factor);
+	display_fbcon_menu_message(str_target, FBCON_TITLE_MSG, big_factor);
+
+	str_target = str_align_right(str2, common_factor);
+	display_fbcon_menu_message(str_target, FBCON_TITLE_MSG, common_factor);
+
+	display_fbcon_menu_message("\n< More options\n",
+		FBCON_COMMON_MSG, common_factor);
+	display_fbcon_menu_message("press VOLUME keys\n\n",
+		FBCON_SUBTITLE_MSG, common_factor);
+
+	display_fbcon_menu_message(warning_msg, FBCON_COMMON_MSG, common_factor);
+
+	display_fbcon_menu_message("g.co/placeholder\n",
+		msg_type, common_factor);
+
+#if VERIFIED_BOOT
+	if (type == DISPLAY_MENU_YELLOW) {
+		fp_buf = get_boot_fingerprint(&fp_size);
+		if (fp_buf != NULL) {
+			strncpy(fp_str_temp, (char const *)fp_buf, fp_size);
+			for (i = 0; i < fp_size; i++) {
+				if(i == fp_size - 1)
+					sprintf(str_temp, "%02x", fp_str_temp[i]);
+				else
+					sprintf(str_temp, "%02x-", fp_str_temp[i]);
+
+				strcat(fp_str, str_temp);
+			}
+		}
+		display_fbcon_menu_message("ID:", FBCON_COMMON_MSG, common_factor);
+		display_fbcon_menu_message(fp_str, FBCON_COMMON_MSG, common_factor);
+	}
+#endif
+
+	display_fbcon_menu_message("\n\nIf no key pressed:\n"\
+		"Your device will boot in 5 seconds\n\n", FBCON_COMMON_MSG, common_factor);
+
+	msg_info->msg_type = type;
+	if(str_target) {
+		free(str_target);
+	}
+}
+
+void display_boot_verified_option(struct select_msg_info *msg_info)
+{
+	int i = 0;
+	fbcon_clear();
+	memset(msg_info, 0, sizeof(struct select_msg_info));
+
+	display_fbcon_menu_message("Options menu:\n\n",
+		FBCON_COMMON_MSG, big_factor);
+	display_fbcon_menu_message("Press volume key to select, and "\
+		"press power key to select\n\n", FBCON_COMMON_MSG, common_factor);
+
+	for (i = 0; i < BOOT_VERIFY_OPTION_NUM; i++) {
+		fbcon_draw_line();
+		msg_info->option_start[i] = fbcon_get_current_line();
+		display_fbcon_menu_message(option_menu[i],
+			FBCON_COMMON_MSG, common_factor);
+		msg_info->option_bg[i]= fbcon_get_current_bg();
+		msg_info->option_end[i]= fbcon_get_current_line();
+	}
+
+	fbcon_draw_line();
+	msg_info->msg_type = DISPLAY_MENU_MORE_OPTION;
+	msg_info->option_num = BOOT_VERIFY_OPTION_NUM;
+}
+
+static void display_menu_thread_start(struct select_msg_info *msg_info)
+{
+	thread_t *thr;
+
+	if (!is_thread_start) {
+		thr = thread_create("selectkeydetect", &select_msg_keys_detect,
+			(void*)msg_info, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+		if (!thr) {
+			dprintf(CRITICAL, "ERROR: creat device status detect thread failed!!\n");
+			return;
+		}
+		thread_resume(thr);
+	}
+
+	is_thread_start = true;
+}
+
+void display_menu_thread(int type)
+{
+	struct select_msg_info *display_menu_msg_info;
+	display_menu_msg_info = &msg_info;
+	int menu_type = 0;
+
+	set_message_factor();
+	if (type == DISPLAY_THREAD_UNLOCK) {
+		display_unlock_menu(display_menu_msg_info);
+
+		dprintf(INFO, "creating oem unlock keys detect thread\n");
+		display_menu_thread_start(display_menu_msg_info);
+	} else {
+	#if VERIFIED_BOOT
+		if (boot_verify_get_state() == ORANGE) {
+			menu_type = DISPLAY_MENU_ORANGE;
+		} else if (boot_verify_get_state() == YELLOW) {
+			menu_type = DISPLAY_MENU_YELLOW;
+		} else if (boot_verify_get_state() == RED) {
+			menu_type = DISPLAY_MENU_RED;
+		}
+		display_boot_verified_menu(display_menu_msg_info, menu_type);
+
+		dprintf(INFO, "creating device state keys detect thread\n");
+		display_menu_thread_start(display_menu_msg_info);
+
+	#else
+		dprintf(CRITICAL, "VERIFIED_BOOT is not enable!!\n");
+		return;
+	#endif
+	}
+}
+
diff --git a/platform/msm_shared/include/display_menu.h b/platform/msm_shared/include/display_menu.h
new file mode 100644
index 0000000..2350fe9
--- /dev/null
+++ b/platform/msm_shared/include/display_menu.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*	notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*	copyright notice, this list of conditions and the following
+*	disclaimer in the documentation and/or other materials provided
+*	with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*	contributors may be used to endorse or promote products derived
+*	from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __PLATFORM_MSM_SHARED_DISPLAY_MENU_H
+#define __PLATFORM_MSM_SHARED_DISPLAY_MENU_H
+
+#include <openssl/evp.h>
+
+#define SELECT_OPTION_MAX	5
+
+enum display_menu_type {
+	DISPLAY_MENU_YELLOW = 0,
+	DISPLAY_MENU_ORANGE,
+	DISPLAY_MENU_RED,
+	DISPLAY_MENU_MORE_OPTION,
+	DISPLAY_MENU_UNLOCK,
+};
+
+struct select_msg_info {
+	uint32_t	option_start[SELECT_OPTION_MAX];
+	uint32_t	option_end[SELECT_OPTION_MAX];
+	uint32_t	option_bg[SELECT_OPTION_MAX];
+	uint32_t	option_num;
+	uint32_t	msg_type;
+	bool		msg_timeout;
+	bool		msg_power_key_pressed;
+	bool		msg_volume_key_pressed;
+};
+
+enum display_thread_type {
+	DISPLAY_THREAD_UNLOCK = 0,
+	DISPLAY_THREAD_BOOT_STATE,
+};
+
+void wait_for_users_action(void);
+void display_unlock_menu(struct select_msg_info *msg_info);
+void display_boot_verified_menu(struct select_msg_info *msg_info, int type);
+void display_boot_verified_option(struct select_msg_info *msg_info);
+void display_menu_thread(int type);
+#endif				/* __PLATFORM_MSM_SHARED_DISPLAY_MENU_H */
diff --git a/platform/msm_shared/include/menu_keys_detect.h b/platform/msm_shared/include/menu_keys_detect.h
new file mode 100644
index 0000000..31a5661
--- /dev/null
+++ b/platform/msm_shared/include/menu_keys_detect.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*	notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*	copyright notice, this list of conditions and the following
+*	disclaimer in the documentation and/or other materials provided
+*	with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*	contributors may be used to endorse or promote products derived
+*	from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __PLATFORM_MSM_SHARED_KEYS_DETECT_H
+#define __PLATFORM_MSM_SHARED_KEYS_DETECT_H
+
+enum device_select_option {
+	POWEROFF = 0,
+	RESTART,
+	RECOVER,
+	FASTBOOT,
+	BACK,
+
+	CONTINUE,
+};
+
+enum keys_option {
+	VOLUME_UP = 0,
+	VOLUME_DOWN,
+	POWER_KEY,
+};
+
+enum pages_type {
+	UNLOCK_PAGE = 0,
+	BOOT_VERIFY_PAGE1,
+	BOOT_VERIFY_PAGE2,
+};
+
+int select_msg_keys_detect(void *param);
+void keys_detect_init();
+#endif				/* __PLATFORM_MSM_SHARED_KEYS_DETECT_H */
diff --git a/platform/msm_shared/menu_keys_detect.c b/platform/msm_shared/menu_keys_detect.c
new file mode 100644
index 0000000..472814d
--- /dev/null
+++ b/platform/msm_shared/menu_keys_detect.c
@@ -0,0 +1,325 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*	notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*	copyright notice, this list of conditions and the following
+*	disclaimer in the documentation and/or other materials provided
+*	with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*	contributors may be used to endorse or promote products derived
+*	from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <debug.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <pm8x41.h>
+#include <pm8x41_hw.h>
+#include <kernel/timer.h>
+#include <platform/timer.h>
+#include <kernel/thread.h>
+#include <dev/keys.h>
+#include <dev/fbcon.h>
+#include <menu_keys_detect.h>
+#include <display_menu.h>
+#include <platform/gpio.h>
+#include <platform/iomap.h>
+#include <platform.h>
+#include <../../../app/aboot/recovery.h>
+
+#define KEY_DETECT_FREQUENCY		50
+#define KEY_PRESS_TIMEOUT		5000
+
+#define RECOVERY_MODE   0x77665502
+#define FASTBOOT_MODE   0x77665500
+
+static uint32_t wait_time = 0;
+static int old_device_type = -1;
+
+extern int target_volume_up();
+extern uint32_t target_volume_down();
+extern void reboot_device(unsigned reboot_reason);
+extern void shutdown_device();
+
+typedef uint32_t (*keys_detect_func)(void);
+typedef uint32_t (*keys_action_func)(struct select_msg_info* msg_info,
+	uint32_t option_index);
+
+struct keys_stru {
+	int type;
+	keys_detect_func keys_pressed_func;
+};
+
+struct keys_stru keys[] = {
+	{VOLUME_UP, (uint32_t (*)(void))target_volume_up},
+	{VOLUME_DOWN, target_volume_down},
+	{POWER_KEY, pm8x41_get_pwrkey_is_pressed},
+};
+
+struct pages_action {
+	keys_action_func up_action_func;
+	keys_action_func down_action_func;
+	keys_action_func enter_action_func;
+};
+
+static int is_key_pressed(int keys_type)
+{
+	int count = 0;
+
+	if (keys[keys_type].keys_pressed_func()) {
+		/*if key is pressed, wait for 1 second to see if it is released*/
+		while(count++ < 10 && keys[keys_type].keys_pressed_func())
+			thread_sleep(100);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void update_device_status(unsigned reason)
+{
+	if (reason == RECOVER) {
+		/* wipe data */
+		struct recovery_message msg;
+
+		snprintf(msg.recovery, sizeof(msg.recovery), "recovery\n--wipe_data");
+		write_misc(0, &msg, sizeof(msg));
+
+		reboot_device(RECOVERY_MODE);
+	} else if (reason == RESTART) {
+		reboot_device(0);
+	} else if (reason == POWEROFF) {
+		shutdown_device();
+	} else if (reason == FASTBOOT) {
+		reboot_device(FASTBOOT_MODE);
+	} else if (reason == CONTINUE) {
+		fbcon_clear();
+		display_image_on_screen();
+	}
+}
+
+static void update_volume_up_bg(struct select_msg_info* msg_info, uint32_t option_index)
+{
+	if (option_index == msg_info->option_num - 1) {
+		fbcon_draw_msg_background(msg_info->option_start[0],
+			msg_info->option_end[0],
+			msg_info->option_bg[0], 0);
+
+		fbcon_draw_msg_background(msg_info->option_start[msg_info->option_num - 1],
+			msg_info->option_end[msg_info->option_num - 1],
+			msg_info->option_bg[msg_info->option_num - 1], 1);
+	} else {
+		fbcon_draw_msg_background(msg_info->option_start[option_index],
+			msg_info->option_end[option_index],
+			msg_info->option_bg[option_index], 1);
+
+		fbcon_draw_msg_background(msg_info->option_start[option_index + 1],
+			msg_info->option_end[option_index + 1],
+			msg_info->option_bg[option_index + 1], 0);
+	}
+}
+
+static void update_volume_down_bg(struct select_msg_info* msg_info, uint32_t option_index)
+{
+	if (option_index == 0) {
+		fbcon_draw_msg_background(msg_info->option_start[0],
+			msg_info->option_end[0],
+			msg_info->option_bg[0], 1);
+
+		fbcon_draw_msg_background(msg_info->option_start[msg_info->option_num - 1],
+			msg_info->option_end[msg_info->option_num - 1],
+			msg_info->option_bg[msg_info->option_num - 1], 0);
+	} else {
+		fbcon_draw_msg_background(msg_info->option_start[option_index],
+			msg_info->option_end[option_index],
+			msg_info->option_bg[option_index], 1);
+
+		fbcon_draw_msg_background(msg_info->option_start[option_index - 1],
+			msg_info->option_end[option_index - 1],
+			msg_info->option_bg[option_index - 1], 0);
+	}
+}
+
+/* update select option's background when volume up key is pressed */
+static uint32_t menu_volume_up_func (struct select_msg_info* msg_info,
+	uint32_t option_index)
+{
+	if (option_index == msg_info->option_num ||
+		option_index == 0) {
+		option_index = msg_info->option_num - 1;
+	} else if (option_index > 0) {
+		option_index--;
+	}
+
+	update_volume_up_bg(msg_info, option_index);
+
+	return option_index;
+}
+
+/* update select option's background when volume down key is pressed */
+static uint32_t menu_volume_down_func (struct select_msg_info* msg_info,
+	uint32_t option_index)
+{
+	option_index++;
+	if (option_index >= msg_info->option_num)
+		option_index = 0;
+
+	update_volume_down_bg(msg_info, option_index);
+
+	return option_index;
+}
+
+/* enter to boot verify page2 if volume key is pressed */
+static uint32_t boot_page1_volume_keys_func (struct select_msg_info* msg_info,
+	uint32_t option_index)
+{
+	keys_detect_init();
+	old_device_type = msg_info->msg_type;
+	display_boot_verified_option(msg_info);
+	msg_info->msg_volume_key_pressed = true;
+	option_index = msg_info->option_num;
+
+	return option_index;
+}
+
+/* update device's status via select option */
+static uint32_t unlock_power_key_func (struct select_msg_info* msg_info,
+	uint32_t option_index)
+{
+	int device_state = -1;
+	if (option_index == 0)
+		device_state = RECOVER;
+	else if (option_index == 1)
+		device_state = RESTART;
+
+	update_device_status(device_state);
+	return 0;
+}
+
+/* continue booting when power key is pressed at boot-verify page1 */
+static uint32_t boot_page1_power_key_func (struct select_msg_info* msg_info,
+	uint32_t option_index){
+	update_device_status(CONTINUE);
+	return option_index;
+}
+
+/* update device's status via select option */
+static uint32_t boot_page2_power_key_func (struct select_msg_info* msg_info,
+	uint32_t option_index)
+{
+	if (option_index == BACK) {
+		wait_time = 0;
+		msg_info->msg_timeout = false;
+		option_index = msg_info->option_num;
+		display_boot_verified_menu(msg_info,
+			old_device_type);
+	} else {
+		msg_info->msg_power_key_pressed = true;
+		update_device_status(option_index);
+	}
+	return option_index;
+}
+
+/* initialize different page's function
+ * UNLOCK_PAGE/BOOT_VERIFY_PAGE2:
+ *	up_action_func: update select option's background when volume up
+ *	is pressed
+ *	down_action_func: update select option's background when volume up
+ *	is pressed
+ *	enter_action_func: update device's status via select option
+ * BOOT_VERIFY_PAGE1:
+ *	up_action_func/down_action_func: enter BOOT_VERIFY_PAGE2 when volume
+ *	key is pressed
+ *	enter_action_func: continue booting
+ */
+static struct pages_action menu_pages_action[] = {
+	[UNLOCK_PAGE] = {
+		menu_volume_up_func,
+		menu_volume_down_func,
+		unlock_power_key_func,
+	},
+	[BOOT_VERIFY_PAGE1] = {
+		boot_page1_volume_keys_func,
+		boot_page1_volume_keys_func,
+		boot_page1_power_key_func,
+	},
+	[BOOT_VERIFY_PAGE2] = {
+		menu_volume_up_func,
+		menu_volume_down_func,
+		boot_page2_power_key_func,
+	},
+};
+
+void keys_detect_init()
+{
+	wait_time = 0;
+}
+
+int select_msg_keys_detect(void *param) {
+	struct select_msg_info *msg_info = (struct select_msg_info*)param;
+	uint32_t current_page_index;
+	uint32_t option_index = msg_info->option_num;
+
+	keys_detect_init();
+	while(1) {
+		/* get page's index via different message type */
+		switch(msg_info->msg_type) {
+		case DISPLAY_MENU_UNLOCK:
+			current_page_index = UNLOCK_PAGE;
+			break;
+		case DISPLAY_MENU_MORE_OPTION:
+			current_page_index = BOOT_VERIFY_PAGE2;
+			break;
+		default:
+			current_page_index = BOOT_VERIFY_PAGE1;
+			break;
+		}
+
+		/* device will continue booting when user has no action
+		 * on BOOT_VERIFY_PAGE1
+		 */
+		if (wait_time > KEY_PRESS_TIMEOUT)
+			msg_info->msg_timeout = true;
+
+		/* 1: update select option's index, default it is the total option number
+		 *  volume up: index decrease, the option will scroll up from
+		 * 	the bottom to top if the key is pressed firstly.
+		 *	eg: 5->4->3->2->1->0
+		 *  volume down: index increase, the option will scroll down from
+		 * 	the bottom to top if the key is pressed firstly.
+		 *	eg: 5->0
+		 * 2: update device's status via select option's index
+		 */
+		if (is_key_pressed(VOLUME_UP)) {
+			option_index =
+				menu_pages_action[current_page_index].up_action_func(msg_info, option_index);
+		} else if (is_key_pressed(VOLUME_DOWN)) {
+			option_index =
+				menu_pages_action[current_page_index].down_action_func(msg_info, option_index);
+		} else if (is_key_pressed(POWER_KEY)) {
+			option_index =
+				menu_pages_action[current_page_index].enter_action_func(msg_info, option_index);
+		}
+
+		wait_time += KEY_DETECT_FREQUENCY;
+		thread_sleep(KEY_DETECT_FREQUENCY);
+	}
+
+	return 0;
+}
diff --git a/platform/msm_shared/rules.mk b/platform/msm_shared/rules.mk
index b20e32f..cd597c1 100644
--- a/platform/msm_shared/rules.mk
+++ b/platform/msm_shared/rules.mk
@@ -54,6 +54,12 @@
 	$(LOCAL_DIR)/boot_verifier.o
 endif
 
+ifeq ($(ENABLE_FBCON_DISPLAY_MSG),1)
+OBJS += \
+	$(LOCAL_DIR)/menu_keys_detect.o \
+	$(LOCAL_DIR)/display_menu.o
+endif
+
 ifeq ($(ENABLE_GLINK_SUPPORT),1)
 OBJS += \
 		$(LOCAL_DIR)/rpm-ipc.o \