blob: efea07e8ddcfb5fe921bd5276dc4bff31a3108bc [file] [log] [blame]
/* Copyright (c) 2015-2016, 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 <reboot.h>
#include <sys/types.h>
#include <../../../app/aboot/recovery.h>
#include <../../../app/aboot/devinfo.h>
#define KEY_DETECT_FREQUENCY 50
static time_t before_time;
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 void (*keys_action_func)(struct select_msg_info* msg_info);
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 uint32_t verify_index_action[] = {
[0] = POWEROFF,
[1] = RESTART,
[2] = RECOVER,
[3] = FASTBOOT,
[4] = BACK,
};
static uint32_t fastboot_index_action[] = {
[0] = RESTART,
[1] = FASTBOOT,
[2] = RECOVER,
[3] = POWEROFF,
[4] = FFBM,
};
static uint32_t unlock_index_action[] = {
[0] = RECOVER,
[1] = RESTART,
};
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(struct select_msg_info* msg_info, int reason)
{
char ffbm_page_buffer[FFBM_MODE_BUF_SIZE];
fbcon_clear();
switch (reason) {
case RECOVER:
if (msg_info->info.msg_type == DISPLAY_MENU_UNLOCK) {
set_device_unlock_value(UNLOCK, TRUE);
} else if (msg_info->info.msg_type == DISPLAY_MENU_UNLOCK_CRITICAL) {
set_device_unlock_value(UNLOCK_CRITICAL, TRUE);
}
if (msg_info->info.msg_type == DISPLAY_MENU_UNLOCK ||
msg_info->info.msg_type == DISPLAY_MENU_UNLOCK_CRITICAL) {
/* 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);
break;
case RESTART:
reboot_device(0);
break;
case POWEROFF:
shutdown_device();
break;
case FASTBOOT:
reboot_device(FASTBOOT_MODE);
break;
case CONTINUE:
display_image_on_screen();
/* Continue boot, no need to detect the keys'status */
msg_info->info.is_exit = true;
break;
case BACK:
display_bootverify_menu_renew(msg_info, msg_info->last_msg_type);
before_time = current_time();
break;
case FFBM:
snprintf(ffbm_page_buffer, sizeof(ffbm_page_buffer), "ffbm-00");
write_misc(0, ffbm_page_buffer, sizeof(ffbm_page_buffer));
reboot_device(0);
break;
}
}
/* msg_lock need to be holded when call this function. */
static void update_volume_up_bg(struct select_msg_info* msg_info)
{
if (msg_info->info.option_index == msg_info->info.option_num - 1) {
fbcon_draw_msg_background(msg_info->info.option_start[0],
msg_info->info.option_end[0],
msg_info->info.option_bg[0], 0);
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_num - 1],
msg_info->info.option_end[msg_info->info.option_num - 1],
msg_info->info.option_bg[msg_info->info.option_num - 1], 1);
} else {
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_index],
msg_info->info.option_end[msg_info->info.option_index],
msg_info->info.option_bg[msg_info->info.option_index], 1);
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_index + 1],
msg_info->info.option_end[msg_info->info.option_index + 1],
msg_info->info.option_bg[msg_info->info.option_index + 1], 0);
}
}
/* msg_lock need to be holded when call this function. */
static void update_volume_down_bg(struct select_msg_info* msg_info)
{
if (msg_info->info.option_index == 0) {
fbcon_draw_msg_background(msg_info->info.option_start[0],
msg_info->info.option_end[0],
msg_info->info.option_bg[0], 1);
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_num - 1],
msg_info->info.option_end[msg_info->info.option_num - 1],
msg_info->info.option_bg[msg_info->info.option_num - 1], 0);
} else {
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_index],
msg_info->info.option_end[msg_info->info.option_index],
msg_info->info.option_bg[msg_info->info.option_index], 1);
fbcon_draw_msg_background(msg_info->info.option_start[msg_info->info.option_index - 1],
msg_info->info.option_end[msg_info->info.option_index - 1],
msg_info->info.option_bg[msg_info->info.option_index - 1], 0);
}
}
/* update select option's background when volume up key is pressed */
static void menu_volume_up_func (struct select_msg_info* msg_info)
{
if (msg_info->info.option_index == 0)
msg_info->info.option_index = msg_info->info.option_num - 1;
else
msg_info->info.option_index--;
if (msg_info->info.msg_type == DISPLAY_MENU_FASTBOOT) {
display_fastboot_menu_renew(msg_info);
} else {
update_volume_up_bg(msg_info);
}
}
/* update select option's background when volume down key is pressed */
static void menu_volume_down_func (struct select_msg_info* msg_info)
{
msg_info->info.option_index++;
if (msg_info->info.option_index >= msg_info->info.option_num)
msg_info->info.option_index = 0;
if (msg_info->info.msg_type == DISPLAY_MENU_FASTBOOT) {
display_fastboot_menu_renew(msg_info);
} else {
update_volume_down_bg(msg_info);
}
}
/* enter to boot verification option page if volume key is pressed */
static void boot_warning_volume_keys_func (struct select_msg_info* msg_info)
{
msg_info->last_msg_type = msg_info->info.msg_type;
display_bootverify_option_menu_renew(msg_info);
}
/* update device's status via select option */
static void power_key_func(struct select_msg_info* msg_info)
{
int reason = -1;
switch (msg_info->info.msg_type) {
case DISPLAY_MENU_YELLOW:
case DISPLAY_MENU_ORANGE:
case DISPLAY_MENU_RED:
case DISPLAY_MENU_LOGGING:
case DISPLAY_MENU_EIO:
reason = CONTINUE;
break;
case DISPLAY_MENU_MORE_OPTION:
if(msg_info->info.option_index < ARRAY_SIZE(verify_index_action))
reason = verify_index_action[msg_info->info.option_index];
break;
case DISPLAY_MENU_UNLOCK:
case DISPLAY_MENU_UNLOCK_CRITICAL:
if(msg_info->info.option_index < ARRAY_SIZE(unlock_index_action))
reason = unlock_index_action[msg_info->info.option_index];
break;
case DISPLAY_MENU_FASTBOOT:
if(msg_info->info.option_index < ARRAY_SIZE(fastboot_index_action))
reason = fastboot_index_action[msg_info->info.option_index];
break;
default:
dprintf(CRITICAL,"Unsupported menu type\n");
break;
}
if (reason != -1) {
update_device_status(msg_info, reason);
}
}
/* Initialize different page's function
* DISPLAY_MENU_UNLOCK/DISPLAY_MENU_UNLOCK_CRITICAL
* DISPLAY_MENU_MORE_OPTION/DISPLAY_MENU_FASTBOOT:
* 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
* DISPLAY_MENU_YELLOW/DISPLAY_MENU_ORANGE/DISPLAY_MENU_RED:
* 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[] = {
[DISPLAY_MENU_UNLOCK] = {
menu_volume_up_func,
menu_volume_down_func,
power_key_func,
},
[DISPLAY_MENU_UNLOCK_CRITICAL] = {
menu_volume_up_func,
menu_volume_down_func,
power_key_func,
},
[DISPLAY_MENU_YELLOW] = {
boot_warning_volume_keys_func,
boot_warning_volume_keys_func,
power_key_func,
},
[DISPLAY_MENU_ORANGE] = {
boot_warning_volume_keys_func,
boot_warning_volume_keys_func,
power_key_func,
},
[DISPLAY_MENU_RED] = {
boot_warning_volume_keys_func,
boot_warning_volume_keys_func,
power_key_func,
},
[DISPLAY_MENU_LOGGING] = {
boot_warning_volume_keys_func,
boot_warning_volume_keys_func,
power_key_func,
},
[DISPLAY_MENU_EIO] = {
boot_warning_volume_keys_func,
boot_warning_volume_keys_func,
power_key_func,
},
[DISPLAY_MENU_MORE_OPTION] = {
menu_volume_up_func,
menu_volume_down_func,
power_key_func,
},
[DISPLAY_MENU_FASTBOOT] = {
menu_volume_up_func,
menu_volume_down_func,
power_key_func,
},
};
void keys_detect_init()
{
/* Waiting for all keys are released */
while(1) {
if(!keys[VOLUME_UP].keys_pressed_func() &&
!keys[VOLUME_DOWN].keys_pressed_func() &&
!keys[POWER_KEY].keys_pressed_func()) {
break;
}
thread_sleep(KEY_DETECT_FREQUENCY);
}
before_time = current_time();
}
int select_msg_keys_detect(void *param) {
struct select_msg_info *msg_info = (struct select_msg_info*)param;
msg_lock_init();
keys_detect_init();
while(1) {
/* 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)) {
mutex_acquire(&msg_info->msg_lock);
menu_pages_action[msg_info->info.msg_type].up_action_func(msg_info);
mutex_release(&msg_info->msg_lock);
} else if (is_key_pressed(VOLUME_DOWN)) {
mutex_acquire(&msg_info->msg_lock);
menu_pages_action[msg_info->info.msg_type].down_action_func(msg_info);
mutex_release(&msg_info->msg_lock);
} else if (is_key_pressed(POWER_KEY)) {
mutex_acquire(&msg_info->msg_lock);
menu_pages_action[msg_info->info.msg_type].enter_action_func(msg_info);
mutex_release(&msg_info->msg_lock);
}
mutex_acquire(&msg_info->msg_lock);
/* Never time out if the timeout_time is 0 */
if(msg_info->info.timeout_time) {
if ((current_time() - before_time) > msg_info->info.timeout_time)
msg_info->info.is_exit = true;
}
if (msg_info->info.is_exit) {
msg_info->info.rel_exit = true;
mutex_release(&msg_info->msg_lock);
break;
}
mutex_release(&msg_info->msg_lock);
thread_sleep(KEY_DETECT_FREQUENCY);
}
return 0;
}