| /* Copyright 2013 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* AP hang detect logic */ |
| |
| #include "ap_hang_detect.h" |
| #include "chipset.h" |
| #include "common.h" |
| #include "console.h" |
| #include "hooks.h" |
| #include "host_command.h" |
| #include "lid_switch.h" |
| #include "power_button.h" |
| #include "timer.h" |
| #include "util.h" |
| |
| /* Console output macros */ |
| #define CPUTS(outstr) cputs(CC_CHIPSET, outstr) |
| #define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) |
| |
| static struct ec_params_hang_detect hdparams; |
| |
| static int active; /* Is hang detect timer active / counting? */ |
| static int timeout_will_reboot; /* Will the deferred call reboot the AP? */ |
| |
| /** |
| * Handle the hang detect timer expiring. |
| */ |
| static void hang_detect_deferred(void); |
| DECLARE_DEFERRED(hang_detect_deferred); |
| |
| static void hang_detect_deferred(void) |
| { |
| /* If we're no longer active, nothing to do */ |
| if (!active) |
| return; |
| |
| /* If we're rebooting the AP, stop hang detection */ |
| if (timeout_will_reboot) { |
| CPRINTS("hang detect triggering warm reboot"); |
| host_set_single_event(EC_HOST_EVENT_HANG_REBOOT); |
| chipset_reset(CHIPSET_RESET_HANG_REBOOT); |
| active = 0; |
| return; |
| } |
| |
| /* Otherwise, we're starting with the host event */ |
| CPRINTS("hang detect sending host event"); |
| host_set_single_event(EC_HOST_EVENT_HANG_DETECT); |
| |
| /* If we're also rebooting, defer for the remaining delay */ |
| if (hdparams.warm_reboot_timeout_msec) { |
| CPRINTS("hang detect continuing (for reboot)"); |
| timeout_will_reboot = 1; |
| hook_call_deferred(&hang_detect_deferred_data, |
| (hdparams.warm_reboot_timeout_msec - |
| hdparams.host_event_timeout_msec) * MSEC); |
| } else { |
| /* Not rebooting, so go back to idle */ |
| active = 0; |
| } |
| } |
| |
| /** |
| * Start the hang detect timers. |
| */ |
| static void hang_detect_start(const char *why) |
| { |
| /* If already active, don't restart timer */ |
| if (active) |
| return; |
| |
| if (hdparams.host_event_timeout_msec) { |
| CPRINTS("hang detect started on %s (for event)", why); |
| timeout_will_reboot = 0; |
| active = 1; |
| hook_call_deferred(&hang_detect_deferred_data, |
| hdparams.host_event_timeout_msec * MSEC); |
| } else if (hdparams.warm_reboot_timeout_msec) { |
| CPRINTS("hang detect started on %s (for reboot)", why); |
| timeout_will_reboot = 1; |
| active = 1; |
| hook_call_deferred(&hang_detect_deferred_data, |
| hdparams.warm_reboot_timeout_msec * MSEC); |
| } |
| } |
| |
| /** |
| * Stop the hang detect timers. |
| */ |
| static void hang_detect_stop(const char *why) |
| { |
| if (active) |
| CPRINTS("hang detect stopped on %s", why); |
| |
| active = 0; |
| } |
| |
| void hang_detect_stop_on_host_command(void) |
| { |
| if (hdparams.flags & EC_HANG_STOP_ON_HOST_COMMAND) |
| hang_detect_stop("host cmd"); |
| } |
| |
| /*****************************************************************************/ |
| /* Hooks */ |
| |
| static void hang_detect_power_button(void) |
| { |
| if (power_button_is_pressed()) { |
| if (hdparams.flags & EC_HANG_START_ON_POWER_PRESS) |
| hang_detect_start("power button"); |
| } else { |
| if (hdparams.flags & EC_HANG_STOP_ON_POWER_RELEASE) |
| hang_detect_stop("power button"); |
| } |
| } |
| DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, hang_detect_power_button, |
| HOOK_PRIO_DEFAULT); |
| |
| static void hang_detect_lid(void) |
| { |
| if (lid_is_open()) { |
| if (hdparams.flags & EC_HANG_START_ON_LID_OPEN) |
| hang_detect_start("lid open"); |
| } else { |
| if (hdparams.flags & EC_HANG_START_ON_LID_CLOSE) |
| hang_detect_start("lid close"); |
| } |
| } |
| DECLARE_HOOK(HOOK_LID_CHANGE, hang_detect_lid, HOOK_PRIO_DEFAULT); |
| |
| static void hang_detect_resume(void) |
| { |
| if (hdparams.flags & EC_HANG_START_ON_RESUME) |
| hang_detect_start("resume"); |
| } |
| DECLARE_HOOK(HOOK_CHIPSET_RESUME, hang_detect_resume, HOOK_PRIO_DEFAULT); |
| |
| static void hang_detect_suspend(void) |
| { |
| if (hdparams.flags & EC_HANG_STOP_ON_SUSPEND) |
| hang_detect_stop("suspend"); |
| } |
| DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, hang_detect_suspend, HOOK_PRIO_DEFAULT); |
| |
| static void hang_detect_shutdown(void) |
| { |
| /* Stop the timers */ |
| hang_detect_stop("shutdown"); |
| |
| /* Disable hang detection; it must be enabled every boot */ |
| memset(&hdparams, 0, sizeof(hdparams)); |
| } |
| DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, hang_detect_shutdown, HOOK_PRIO_DEFAULT); |
| |
| /*****************************************************************************/ |
| /* Host command */ |
| |
| static int hang_detect_host_command(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_hang_detect *p = args->params; |
| |
| /* Handle stopping hang timer on request */ |
| if (p->flags & EC_HANG_STOP_NOW) { |
| hang_detect_stop("ap request"); |
| |
| /* Ignore the other params */ |
| return EC_RES_SUCCESS; |
| } |
| |
| /* Handle starting hang timer on request */ |
| if (p->flags & EC_HANG_START_NOW) { |
| hang_detect_start("ap request"); |
| |
| /* Ignore the other params */ |
| return EC_RES_SUCCESS; |
| } |
| |
| /* If hang detect transitioning to disabled, stop timers */ |
| if (hdparams.flags && !p->flags) |
| hang_detect_stop("ap flags=0"); |
| |
| /* Save new params */ |
| hdparams = *p; |
| CPRINTS("hang detect flags=0x%x, event=%d ms, reboot=%d ms", |
| hdparams.flags, hdparams.host_event_timeout_msec, |
| hdparams.warm_reboot_timeout_msec); |
| |
| /* |
| * If warm reboot timeout is shorter than host event timeout, ignore |
| * the host event timeout because a warm reboot will win. |
| */ |
| if (hdparams.warm_reboot_timeout_msec && |
| hdparams.warm_reboot_timeout_msec <= |
| hdparams.host_event_timeout_msec) |
| hdparams.host_event_timeout_msec = 0; |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_HANG_DETECT, |
| hang_detect_host_command, |
| EC_VER_MASK(0)); |
| |
| /*****************************************************************************/ |
| /* Console command */ |
| |
| static int command_hang_detect(int argc, char **argv) |
| { |
| ccprintf("flags: 0x%x\n", hdparams.flags); |
| |
| ccputs("event: "); |
| if (hdparams.host_event_timeout_msec) |
| ccprintf("%d ms\n", hdparams.host_event_timeout_msec); |
| else |
| ccputs("disabled\n"); |
| |
| ccputs("reboot: "); |
| if (hdparams.warm_reboot_timeout_msec) |
| ccprintf("%d ms\n", hdparams.warm_reboot_timeout_msec); |
| else |
| ccputs("disabled\n"); |
| |
| ccputs("status: "); |
| if (active) |
| ccprintf("active for %s\n", |
| timeout_will_reboot ? "reboot" : "event"); |
| else |
| ccputs("inactive\n"); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(hangdet, command_hang_detect, |
| NULL, |
| "Print hang detect state"); |