static void onewire_led_tick(void) { static enum led_color current_color = LED_COLOR_COUNT; static int tick_count; enum led_color new_color = LED_OFF; uint32_t chflags = charge_get_flags(); tick_count++; if (!(chflags & CHARGE_FLAG_EXTERNAL_POWER)) { /* AC isn't present, so the power LED on the AC plug is off */ current_color = LED_OFF; return; } /* Translate charge state to LED color */ switch (charge_get_state()) { case PWR_STATE_IDLE: if (chflags & CHARGE_FLAG_FORCE_IDLE) new_color = (tick_count & 1) ? LED_GREEN : LED_OFF; else new_color = LED_GREEN; break; case PWR_STATE_CHARGE: new_color = LED_YELLOW; break; case PWR_STATE_CHARGE_NEAR_FULL: new_color = LED_GREEN; break; case PWR_STATE_ERROR: new_color = LED_RED; break; default: /* Other states don't change LED color */ break; } /* * The power adapter on link can partially unplug and lose its LED * state. There's no way to detect this, so just assume it forgets its * state every 10 seconds. */ if (!(tick_count % 10)) current_color = LED_COLOR_COUNT; /* If current color is still correct, leave now */ if (new_color == current_color) return; /* Update LED */ if (!led_set(new_color)) current_color = new_color; }
static void pd_exchange_update_ec_status(struct ec_params_pd_status *ec_status) { /* Send PD charge state and battery state of charge */ #ifdef CONFIG_HOSTCMD_PD_CHG_CTRL ec_status->charge_state = charge_state; #endif if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE) ec_status->batt_soc = charge_get_percent(); else ec_status->batt_soc = -1; }
static int charge_force_idle(int enable) { if (enable) { /* * Force-idle state is only meaningful if external power is * present. If it's not present we can't charge anyway... */ if (!(charge_get_flags() & CHARGE_FLAG_EXTERNAL_POWER)) return EC_ERROR_UNKNOWN; charger_post_init(); } state_machine_force_idle = enable; return EC_SUCCESS; }
static void glados_led_set_battery(void) { static int battery_ticks; uint32_t chflags = charge_get_flags(); battery_ticks++; /* BAT LED behavior: * Same as the chromeos spec * Green/Amber for CHARGE_FLAG_FORCE_IDLE */ switch (charge_get_state()) { case PWR_STATE_CHARGE: glados_led_set_color_battery(LED_AMBER); break; case PWR_STATE_DISCHARGE: /* Less than 3%, blink one second every two second */ if (charge_get_percent() < CRITICAL_LOW_BATTERY_PERCENTAGE) glados_led_set_color_battery( (battery_ticks % LED_TOTAL_2SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_AMBER : LED_OFF); /* Less than 10%, blink one second every four seconds */ else if (charge_get_percent() < LOW_BATTERY_PERCENTAGE) glados_led_set_color_battery( (battery_ticks % LED_TOTAL_4SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_AMBER : LED_OFF); else glados_led_set_color_battery(LED_OFF); break; case PWR_STATE_ERROR: glados_led_set_color_battery( (battery_ticks % LED_TOTAL_2SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_RED : LED_OFF); break; case PWR_STATE_CHARGE_NEAR_FULL: glados_led_set_color_battery(LED_GREEN); break; case PWR_STATE_IDLE: /* External power connected in IDLE */ if (chflags & CHARGE_FLAG_FORCE_IDLE) glados_led_set_color_battery( (battery_ticks % LED_TOTAL_4SECS_TICKS < LED_ON_2SECS_TICKS) ? LED_GREEN : LED_AMBER); else glados_led_set_color_battery(LED_GREEN); break; default: /* Other states don't alter LED behavior */ break; } }
static void big_led_set_battery(void) { static int battery_second; uint32_t chflags = charge_get_flags(); battery_second++; /* BAT LED behavior: * Fully charged / idle: Blue * Force idle (for factory): 2 secs of blue, 2 secs of yellow * Under charging: Orange * Battery low (10%): Orange in breeze mode (1 sec on, 3 sec off) * Battery critical low (less than 3%) or abnormal battery * situation: Orange in blinking mode (1 sec on, 1 sec off) * Using battery or not connected to AC power: OFF */ switch (charge_get_state()) { case PWR_STATE_CHARGE: bat_led_set_color(LED_ORANGE); break; case PWR_STATE_DISCHARGE: if (charge_get_percent() < 3) bat_led_set_color((battery_second & 1) ? LED_OFF : LED_ORANGE); else if (charge_get_percent() < 10) bat_led_set_color((battery_second & 3) ? LED_OFF : LED_ORANGE); else bat_led_set_color(LED_OFF); break; case PWR_STATE_ERROR: bat_led_set_color((battery_second & 1) ? LED_OFF : LED_ORANGE); break; case PWR_STATE_CHARGE_NEAR_FULL: bat_led_set_color(LED_BLUE); break; case PWR_STATE_IDLE: /* External power connected in IDLE. */ if (chflags & CHARGE_FLAG_FORCE_IDLE) bat_led_set_color( (battery_second & 0x2) ? LED_BLUE : LED_ORANGE); else bat_led_set_color(LED_BLUE); break; default: /* Other states don't alter LED behavior */ break; } }
static void std_led_set_battery(void) { static int battery_second; uint32_t chflags = charge_get_flags(); battery_second++; /* BAT LED behavior: * Same as the chromeos spec * Green/Amber for CHARGE_FLAG_FORCE_IDLE */ switch (charge_get_state()) { case PWR_STATE_CHARGE: bat_led_set_color(LED_AMBER); break; case PWR_STATE_DISCHARGE: if (charge_get_percent() < 3) bat_led_set_color((battery_second & 1) ? LED_OFF : LED_AMBER); else if (charge_get_percent() < 10) bat_led_set_color((battery_second & 3) ? LED_OFF : LED_AMBER); else bat_led_set_color(LED_OFF); break; case PWR_STATE_ERROR: bat_led_set_color((battery_second & 1) ? LED_OFF : LED_RED); break; case PWR_STATE_CHARGE_NEAR_FULL: bat_led_set_color(LED_GREEN); break; case PWR_STATE_IDLE: /* External power connected in IDLE. */ if (chflags & CHARGE_FLAG_FORCE_IDLE) bat_led_set_color( (battery_second & 0x2) ? LED_GREEN : LED_AMBER); else bat_led_set_color(LED_GREEN); break; default: /* Other states don't alter LED behavior */ break; } }
/** * Called by hook task every 250 ms */ static void led_tick(void) { static unsigned ticks; int chstate = charge_get_state(); ticks++; /* If we don't control the LED, nothing to do */ if (!led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) return; /* If charging error, blink orange, 25% duty cycle, 4 sec period */ if (chstate == PWR_STATE_ERROR) { set_color((ticks % 16) < 4 ? LED_ORANGE : LED_OFF); return; } /* If charge-force-idle, blink green, 50% duty cycle, 2 sec period */ if (chstate == PWR_STATE_IDLE && (charge_get_flags() & CHARGE_FLAG_FORCE_IDLE)) { set_color((ticks & 0x4) ? LED_GREEN : LED_OFF); return; } /* If the system is charging, solid orange */ if (chstate == PWR_STATE_CHARGE) { set_color(LED_ORANGE); return; } /* If AC connected and fully charged (or close to it), solid green */ if (chstate == PWR_STATE_CHARGE_NEAR_FULL || chstate == PWR_STATE_IDLE) { set_color(LED_GREEN); return; } /* Otherwise, system is off and AC not connected, LED off */ set_color(LED_OFF); }
static int test_external_funcs(void) { int rv, temp; uint32_t flags; int state; /* Connect the AC */ test_setup(1); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Invalid or do-nothing commands first */ UART_INJECT("chg\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg blahblah\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg idle\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); UART_INJECT("chg idle blargh\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Now let's force idle on and off */ UART_INJECT("chg idle on\n"); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(flags & CHARGE_FLAG_FORCE_IDLE); UART_INJECT("chg idle off\n"); wait_charging_state(); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* and the rest */ TEST_ASSERT(charge_get_state() == PWR_STATE_CHARGE); TEST_ASSERT(!charge_want_shutdown()); TEST_ASSERT(charge_get_percent() == 50); temp = 0; rv = charge_temp_sensor_get_val(0, &temp); TEST_ASSERT(rv == EC_SUCCESS); TEST_ASSERT(K_TO_C(temp) == 25); return EC_SUCCESS; }
static int test_charge_state(void) { enum charge_state state; uint32_t flags; /* On AC */ test_setup(1); ccprintf("[CHARGING TEST] AC on\n"); /* Detach battery, charging error */ ccprintf("[CHARGING TEST] Detach battery\n"); TEST_ASSERT(test_detach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR) == EC_SUCCESS); msleep(BATTERY_DETACH_DELAY); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_ERROR); /* Attach battery again, charging */ ccprintf("[CHARGING TEST] Attach battery\n"); test_attach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR); /* And changing full capacity should trigger a host event */ ev_clear(EC_HOST_EVENT_BATTERY); sb_write(SB_FULL_CHARGE_CAPACITY, 0xeff0); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY)); /* Unplug AC, discharging at 1000mAh */ ccprintf("[CHARGING TEST] AC off\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); flags = charge_get_flags(); TEST_ASSERT(!(flags & CHARGE_FLAG_EXTERNAL_POWER)); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Discharging waaaay overtemp is ignored */ ccprintf("[CHARGING TEST] AC off, batt temp = 0xffff\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, 0xffff); state = wait_charging_state(); TEST_ASSERT(!is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Discharging overtemp */ ccprintf("[CHARGING TEST] AC off, batt temp = 90 C\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90)); state = wait_charging_state(); TEST_ASSERT(is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Force idle */ ccprintf("[CHARGING TEST] AC on, force idle\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); charge_control(CHARGE_CONTROL_IDLE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(flags & CHARGE_FLAG_FORCE_IDLE); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); /* Force discharge */ ccprintf("[CHARGING TEST] AC on, force discharge\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); charge_control(CHARGE_CONTROL_DISCHARGE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); TEST_ASSERT(is_force_discharge); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(!is_force_discharge); return EC_SUCCESS; }
/** * Common handler for charging states. * * This handler gets battery charging parameters, charger state, ac state, and * timestamp. It also fills memory map and issues power events on state change. */ static int state_common(struct charge_state_context *ctx) { int rv, d; struct charge_state_data *curr = &ctx->curr; struct charge_state_data *prev = &ctx->prev; struct batt_params *batt = &ctx->curr.batt; uint8_t *batt_flags = ctx->memmap_batt_flags; /* Copy previous state and init new state */ ctx->prev = ctx->curr; curr->ts = get_time(); curr->error = 0; /* Detect AC change */ curr->ac = charge_get_flags() & CHARGE_FLAG_EXTERNAL_POWER; if (curr->ac != prev->ac) { if (curr->ac) { /* AC on * Initialize charger to power on reset mode */ rv = charger_post_init(); if (rv) curr->error |= F_CHARGER_INIT; } } if (curr->ac) { *batt_flags |= EC_BATT_FLAG_AC_PRESENT; if (charger_get_voltage(&curr->charging_voltage)) { charge_request(0, 0); curr->error |= F_CHARGER_VOLTAGE; } if (charger_get_current(&curr->charging_current)) { charge_request(0, 0); curr->error |= F_CHARGER_CURRENT; } #ifdef CONFIG_CHARGER_EN_GPIO if (!charge_get_charger_en_gpio()) { curr->charging_voltage = 0; curr->charging_current = 0; } #endif } else { *batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* AC disconnected should get us out of force idle mode. */ state_machine_force_idle = 0; } #if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ defined(CONFIG_BATTERY_PRESENT_GPIO) if (battery_is_present() == BP_NO) { curr->error |= F_BATTERY_NOT_CONNECTED; return curr->error; } #endif /* Read params and see if battery is responsive */ battery_get_params(batt); if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { /* Check low battery condition and retry */ if (curr->ac && ctx->battery_responsive && !(curr->error & F_CHARGER_MASK)) { ctx->battery_responsive = 0; /* * Try to revive ultra low voltage pack. Charge * battery pack with minimum current and maximum * voltage for 30 seconds. */ charge_request(ctx->battery->voltage_max, ctx->battery->precharge_current); for (d = 0; d < PRECHARGE_TIMEOUT; d++) { sleep(1); battery_get_params(batt); if (batt->flags & BATT_FLAG_RESPONSIVE) { ctx->battery_responsive = 1; break; } } } /* Set error if battery is still unresponsive */ if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { curr->error |= F_BATTERY_UNRESPONSIVE; return curr->error; } } else { ctx->battery_responsive = 1; } /* Translate flags */ if (batt->flags & BATT_FLAG_BAD_ANY) curr->error |= F_BATTERY_GET_PARAMS; if (batt->flags & BATT_FLAG_BAD_VOLTAGE) curr->error |= F_BATTERY_VOLTAGE; if (batt->flags & BATT_FLAG_BAD_STATE_OF_CHARGE) curr->error |= F_BATTERY_STATE_OF_CHARGE; *ctx->memmap_batt_volt = batt->voltage; /* Memory mapped value: discharge rate */ *ctx->memmap_batt_rate = batt->current < 0 ? -batt->current : batt->current; /* Fake state of charge if necessary */ if (fake_state_of_charge >= 0) { batt->state_of_charge = fake_state_of_charge; curr->error &= ~F_BATTERY_STATE_OF_CHARGE; } if (batt->state_of_charge != prev->batt.state_of_charge) { rv = battery_full_charge_capacity(&d); if (!rv && d != *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)) { *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = d; /* Notify host to re-read battery information */ host_set_single_event(EC_HOST_EVENT_BATTERY); } } /* Prevent deep discharging */ if (!curr->ac) { if ((batt->state_of_charge < BATTERY_LEVEL_SHUTDOWN && !(curr->error & F_BATTERY_STATE_OF_CHARGE)) || (batt->voltage <= ctx->battery->voltage_min && !(curr->error & F_BATTERY_VOLTAGE))) low_battery_shutdown(ctx); } /* Check battery presence */ if (curr->error & F_BATTERY_MASK) { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_BATT_PRESENT; return curr->error; } *ctx->memmap_batt_flags |= EC_BATT_FLAG_BATT_PRESENT; /* Battery charge level low */ if (batt->state_of_charge <= BATTERY_LEVEL_LOW && prev->batt.state_of_charge > BATTERY_LEVEL_LOW) host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); /* Battery charge level critical */ if (batt->state_of_charge <= BATTERY_LEVEL_CRITICAL) { *ctx->memmap_batt_flags |= EC_BATT_FLAG_LEVEL_CRITICAL; /* Send battery critical host event */ if (prev->batt.state_of_charge > BATTERY_LEVEL_CRITICAL) host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); } else { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_LEVEL_CRITICAL; } #ifdef CONFIG_BATTERY_OVERRIDE_PARAMS /* Apply battery pack vendor charging method */ battery_override_params(batt); #endif #ifdef CONFIG_CHARGER_CURRENT_LIMIT if (batt->desired_current > CONFIG_CHARGER_CURRENT_LIMIT) batt->desired_current = CONFIG_CHARGER_CURRENT_LIMIT; #endif if (batt->desired_current > user_current_limit) batt->desired_current = user_current_limit; if (fake_state_of_charge >= 0) *ctx->memmap_batt_cap = fake_state_of_charge * *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) / 100; else if (battery_remaining_capacity(&d)) ctx->curr.error |= F_BATTERY_CAPACITY; else *ctx->memmap_batt_cap = d; return ctx->curr.error; }
static void pd_exchange_status(void) { struct ec_params_pd_status ec_status; struct ec_response_pd_status pd_status; int rv = 0; #ifdef CONFIG_HOSTCMD_PD_PANIC static int pd_in_rw; #endif /* Send PD charge state and battery state of charge */ ec_status.charge_state = charge_state; if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE) ec_status.batt_soc = charge_get_percent(); else ec_status.batt_soc = -1; rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, &ec_status, sizeof(struct ec_params_pd_status), &pd_status, sizeof(struct ec_response_pd_status)); /* If PD doesn't support new command version, try old version */ if (rv == -EC_RES_INVALID_VERSION) rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status, sizeof(struct ec_params_pd_status), &pd_status, sizeof(struct ec_response_pd_status)); if (rv < 0) { CPRINTS("Host command to PD MCU failed"); return; } #ifdef CONFIG_HOSTCMD_PD_PANIC /* * Check if PD MCU is in RW. If PD MCU was in RW and is now in RO * AND it did not sysjump to RO, then it must have crashed, and * therefore we should panic as well. */ if (pd_status.status & PD_STATUS_IN_RW) { pd_in_rw = 1; } else if (pd_in_rw && !(pd_status.status & PD_STATUS_JUMPED_TO_IMAGE)) { panic_printf("PD crash"); software_panic(PANIC_SW_PD_CRASH, 0); } #endif #ifdef HAS_TASK_LIGHTBAR /* * If charge port has changed, and it was initialized, then show * battery status on lightbar. */ if (pd_status.active_charge_port != charge_port) { if (charge_port != CHARGE_PORT_UNINITIALIZED) { charge_port = pd_status.active_charge_port; lightbar_sequence(LIGHTBAR_TAP); } else { charge_port = pd_status.active_charge_port; } } #else /* Store the active charge port */ charge_port = pd_status.active_charge_port; #endif /* Set input current limit */ rv = charge_set_input_current_limit(MAX(pd_status.curr_lim_ma, CONFIG_CHARGER_INPUT_CURRENT)); if (rv < 0) CPRINTS("Failed to set input current limit from PD MCU"); /* If PD is signalling host event, then pass it up to AP */ if (pd_status.status & PD_STATUS_HOST_EVENT) host_set_single_event(EC_HOST_EVENT_PD_MCU); }
static void led_set_battery(void) { static int battery_ticks; static int suspend_ticks; static int previous_state_suspend; uint32_t chflags = charge_get_flags(); battery_ticks++; suspend_ticks++; switch (charge_get_state()) { case PWR_STATE_CHARGE: led_set_color_battery(LED_AMBER); break; case PWR_STATE_DISCHARGE: /* Less than 3%, blink one second every two second */ if (!chipset_in_state(CHIPSET_STATE_ANY_OFF) && charge_get_percent() < CRITICAL_LOW_BATTERY_PERCENTAGE) led_set_color_battery( (battery_ticks % LED_TOTAL_2SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_AMBER : LED_OFF); /* Less than 10%, blink one second every four seconds */ else if (!chipset_in_state(CHIPSET_STATE_ANY_OFF) && charge_get_percent() < LOW_BATTERY_PERCENTAGE) led_set_color_battery( (battery_ticks % LED_TOTAL_4SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_AMBER : LED_OFF); else { if (chipset_in_state(CHIPSET_STATE_SUSPEND | CHIPSET_STATE_STANDBY)) { if (!previous_state_suspend) suspend_ticks = 0; /* Blink once every four seconds. */ led_set_color_battery( (suspend_ticks % LED_TOTAL_4SECS_TICKS) < LED_ON_1SEC_TICKS ? LED_AMBER : LED_OFF); previous_state_suspend = 1; return; } if (chipset_in_state(CHIPSET_STATE_ON)) led_set_color_battery(LED_BLUE); else led_set_color_battery(LED_OFF); } break; case PWR_STATE_ERROR: led_set_color_battery( (battery_ticks % LED_TOTAL_2SECS_TICKS < LED_ON_1SEC_TICKS) ? LED_AMBER : LED_OFF); break; case PWR_STATE_CHARGE_NEAR_FULL: led_set_color_battery(LED_BLUE); break; case PWR_STATE_IDLE: /* External power connected in IDLE */ if (chflags & CHARGE_FLAG_FORCE_IDLE) led_set_color_battery( (battery_ticks % LED_TOTAL_4SECS_TICKS < LED_ON_2SECS_TICKS) ? LED_AMBER : LED_BLUE); else led_set_color_battery(LED_BLUE); break; default: /* Other states don't alter LED behavior */ break; } previous_state_suspend = 0; }