diff options
Diffstat (limited to 'common/lightbar.c')
-rw-r--r-- | common/lightbar.c | 2068 |
1 files changed, 0 insertions, 2068 deletions
diff --git a/common/lightbar.c b/common/lightbar.c deleted file mode 100644 index f80287941d..0000000000 --- a/common/lightbar.c +++ /dev/null @@ -1,2068 +0,0 @@ -/* - * Copyright 2012 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. - * - * LED controls. - */ - -#ifdef LIGHTBAR_SIMULATION -#include "simulation.h" -#else -#include "battery.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "lb_common.h" -#include "lightbar.h" -#include "lid_switch.h" -#include "motion_sense.h" -#include "pwm.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#endif - -/* - * The Link lightbar had no version command, so defaulted to zero. We have - * added a couple of new commands, so we've updated the version. Any - * optional features in the current version should be marked with flags. - */ -#define LIGHTBAR_IMPLEMENTATION_VERSION 1 -#define LIGHTBAR_IMPLEMENTATION_FLAGS 0 - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LIGHTBAR, outstr) -#define CPRINTS(format, args...) cprints(CC_LIGHTBAR, format, ## args) - -#define FP_SCALE 10000 - -/******************************************************************************/ -/* Here's some state that we might want to maintain across sysjumps, just to - * prevent the lightbar from flashing during normal boot as the EC jumps from - * RO to RW. */ -static struct p_state { - /* What patterns are we showing? */ - enum lightbar_sequence cur_seq; - enum lightbar_sequence prev_seq; - - /* Quantized battery charge level: 0=low 1=med 2=high 3=full. */ - int battery_level; - int battery_percent; - - /* It's either charging or discharging. */ - int battery_is_charging; - - /* Is power-on prevented due to battery level? */ - int battery_is_power_on_prevented; - - /* Pattern variables for state S0. */ - uint16_t w0; /* primary phase */ - uint8_t ramp; /* ramp-in for S3->S0 */ - - uint8_t _pad0; /* next item is __packed */ - - /* Tweakable parameters. */ - union { - struct lightbar_params_v1 p; - struct { - struct lightbar_params_v2_timing timing; - struct lightbar_params_v2_tap tap; - struct lightbar_params_v2_oscillation osc; - struct lightbar_params_v2_brightness bright; - struct lightbar_params_v2_thresholds thlds; - struct lightbar_params_v2_colors colors; - } p_v2; - }; -} st; - -/* Each of the parameters must be less than 120 bytes - * (crbug.com/467716) - */ -#define MAX_PARAM_SIZE 120 -BUILD_ASSERT(sizeof(struct lightbar_params_v2_timing) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_tap) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_oscillation) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_brightness) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_thresholds) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_colors) <= MAX_PARAM_SIZE); -#undef MAX_PARAM_SIZE - -#define PRIMARY_BLUE 4 -#define PRIMARY_RED 5 -#define PRIMARY_YELLOW 6 -#define PRIMARY_GREEN 7 - -static const struct lightbar_params_v1 default_params = { - .google_ramp_up = 2500, - .google_ramp_down = 10000, - .s3s0_ramp_up = 2000, - .s0_tick_delay = { 45000, 30000 }, /* battery, AC */ - .s0a_tick_delay = { 5000, 3000 }, /* battery, AC */ - .s0s3_ramp_down = 2000, - .s3_sleep_for = 5 * SECOND, /* between checks */ - .s3_ramp_up = 2500, - .s3_ramp_down = 10000, - .s5_ramp_up = 2500, - .s5_ramp_down = 10000, - .tap_tick_delay = 5000, /* oscillation step time */ - .tap_gate_delay = 200 * MSEC, /* segment gating delay */ - .tap_display_time = 3 * SECOND, /* total sequence time */ - - /* TODO (crosbug.com/p/36996): remove unused tap_pct_red */ - .tap_pct_red = 14, /* below this is red */ - .tap_pct_green = 94, /* above this is green */ - .tap_seg_min_on = 35, /* min intensity (%) for "on" */ - .tap_seg_max_on = 100, /* max intensity (%) for "on" */ - .tap_seg_osc = 50, /* amplitude for charging osc */ - .tap_idx = {PRIMARY_RED, PRIMARY_YELLOW, PRIMARY_GREEN}, /* color */ - - .osc_min = { 0x60, 0x60 }, /* battery, AC */ - .osc_max = { 0xd0, 0xd0 }, /* battery, AC */ - .w_ofs = {24, 24}, /* phase offset, 256 == 2*PI */ - - .bright_bl_off_fixed = {0xcc, 0xff}, /* backlight off: battery, AC */ - .bright_bl_on_min = {0xcc, 0xff}, /* backlight on: battery, AC */ - .bright_bl_on_max = {0xcc, 0xff}, /* backlight on: battery, AC */ - - .battery_threshold = { 14, 40, 99 }, /* percent, lowest to highest */ - .s0_idx = { - /* battery: 0 = red, other = blue */ - { PRIMARY_RED, PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE }, - /* AC: always blue */ - { PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE } - }, - .s3_idx = { - /* battery: 0 = red, else off */ - { PRIMARY_RED, 0xff, 0xff, 0xff }, - /* AC: do nothing */ - { 0xff, 0xff, 0xff, 0xff } - }, - .s5_idx = PRIMARY_RED, /* flash red */ - .color = { - /* - * These values have been optically calibrated for the - * Samus LEDs to best match the official colors, described at - * https://sites.google.com/a/google.com/brandsite/the-colours - * See crosbug.com/p/33017 before making any changes. - */ - {0x34, 0x70, 0xb4}, /* 0: Google blue */ - {0xbc, 0x50, 0x2c}, /* 1: Google red */ - {0xd0, 0xe0, 0x00}, /* 2: Google yellow */ - {0x50, 0xa0, 0x40}, /* 3: Google green */ - /* These are primary colors */ - {0x00, 0x00, 0xff}, /* 4: full blue */ - {0xff, 0x00, 0x00}, /* 5: full red */ - {0xff, 0xff, 0x00}, /* 6: full yellow */ - {0x00, 0xff, 0x00}, /* 7: full green */ - }, -}; - -#define LB_SYSJUMP_TAG 0x4c42 /* "LB" */ -static void lightbar_preserve_state(void) -{ - system_add_jump_tag(LB_SYSJUMP_TAG, 0, sizeof(st), &st); -} -DECLARE_HOOK(HOOK_SYSJUMP, lightbar_preserve_state, HOOK_PRIO_DEFAULT); - -static void lightbar_restore_state(void) -{ - const uint8_t *old_state = 0; - int size; - - old_state = system_get_jump_tag(LB_SYSJUMP_TAG, 0, &size); - if (old_state && size == sizeof(st)) { - memcpy(&st, old_state, size); - CPRINTS("LB state restored: %d %d - %d %d/%d", - st.cur_seq, st.prev_seq, - st.battery_is_charging, - st.battery_percent, - st.battery_level); - } else { - st.cur_seq = st.prev_seq = LIGHTBAR_S5; - st.battery_percent = 100; - st.battery_level = LB_BATTERY_LEVELS - 1; - st.w0 = 0; - st.ramp = 0; - memcpy(&st.p, &default_params, sizeof(st.p)); - CPRINTS("LB state initialized"); - } -} - -/******************************************************************************/ -/* The patterns are generally dependent on the current battery level and AC - * state. These functions obtain that information, generally by querying the - * power manager task. In demo mode, the keyboard task forces changes to the - * state by calling the demo_* functions directly. */ -/******************************************************************************/ - -#ifdef CONFIG_PWM_KBLIGHT -static int last_backlight_level; -#endif -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING -test_export_static int google_color_id; -#endif - -static int demo_mode = DEMO_MODE_DEFAULT; - -static int quantize_battery_level(int pct) -{ - int i, bl = 0; - for (i = 0; i < LB_BATTERY_LEVELS - 1; i++) - if (pct >= st.p.battery_threshold[i]) - bl++; - return bl; -} - -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING -test_export_static int lux_level_to_google_color(const int lux) -{ - int i; - - if (!lid_is_open()) { - /* The lid shades the light sensor, use full brightness. */ - if (google_color_id != 0) { - google_color_id = 0; - return 1; - } else { - return 0; - } - } - - /* See if we need to decrease brightness */ - for (i = google_color_id; i < lb_brightness_levels_count ; i++) - if (lux >= lb_brightness_levels[i].lux_down) - break; - if (i > google_color_id) { - google_color_id = i; - return 1; - } - /* See if we need to increase brightness */ - for (i = google_color_id; i > 0; i--) - if (lux < lb_brightness_levels[i - 1].lux_up) - break; - if (i < google_color_id) { - google_color_id = i; - return 1; - } - return 0; -} -#endif - -/* - * Update the known state. - * Return 1 if something changes. - */ -static int get_battery_level(void) -{ - int pct = 0; - int bl, change = 0; - - if (demo_mode) - return 0; - -#ifdef HAS_TASK_CHARGER - st.battery_percent = pct = charge_get_percent(); - st.battery_is_charging = (PWR_STATE_DISCHARGE != charge_get_state()); - st.battery_is_power_on_prevented = charge_prevent_power_on(0); -#endif - - /* Find the new battery level */ - bl = quantize_battery_level(pct); - - /* Use some hysteresis to avoid flickering */ - if (bl < st.battery_level || - (bl > st.battery_level - && pct >= (st.p.battery_threshold[st.battery_level] + 1))) { - st.battery_level = bl; - change = 1; - } - -#ifdef CONFIG_PWM_KBLIGHT - /* - * With nothing else to go on, use the keyboard backlight level to * - * set the brightness. In general, if the keyboard backlight - * is OFF (which it is when ambient is bright), use max brightness for - * lightbar. If keyboard backlight is ON, use keyboard backlight - * brightness. That fails if the keyboard backlight is off because - * someone's watching a movie in the dark, of course. Ideally we should - * just let the AP control it directly. - */ - if (pwm_get_enabled(PWM_CH_KBLIGHT)) { - pct = pwm_get_duty(PWM_CH_KBLIGHT); - pct = (255 * pct) / 100; /* 00 - FF */ - if (pct > st.p.bright_bl_on_max[st.battery_is_charging]) - pct = st.p.bright_bl_on_max[st.battery_is_charging]; - else if (pct < st.p.bright_bl_on_min[st.battery_is_charging]) - pct = st.p.bright_bl_on_min[st.battery_is_charging]; - } else - pct = st.p.bright_bl_off_fixed[st.battery_is_charging]; - - if (pct != last_backlight_level) { - last_backlight_level = pct; - lb_set_brightness(pct); - change = 1; - } -#endif -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING - /* Read last value (in lux) collected by the motion sensor. */ - /* Convert lux into brightness percentage */ - if (lux_level_to_google_color(MOTION_SENSE_LUX)) { - memcpy(st.p.color, lb_brightness_levels[google_color_id].color, - sizeof(lb_brightness_levels[google_color_id].color)); - change = 1; - } -#endif - return change; -} - -/* Forcing functions for demo mode, called by the keyboard task. */ - -/* Up/Down keys */ -#define DEMO_CHARGE_STEP 1 -void demo_battery_level(int inc) -{ - if (!demo_mode) - return; - - st.battery_percent += DEMO_CHARGE_STEP * inc; - - if (st.battery_percent > 100) - st.battery_percent = 100; - else if (st.battery_percent < 0) - st.battery_percent = 0; - - st.battery_level = quantize_battery_level(st.battery_percent); - - CPRINTS("LB demo: battery_percent = %d%%, battery_level=%d", - st.battery_percent, st.battery_level); -} - -/* Left/Right keys */ - -void demo_is_charging(int ischarge) -{ - if (!demo_mode) - return; - - st.battery_is_charging = ischarge; - CPRINTS("LB demo: battery_is_charging=%d", - st.battery_is_charging); -} - -/* Bright/Dim keys */ -void demo_brightness(int inc) -{ - int b; - - if (!demo_mode) - return; - - b = lb_get_brightness() + (inc * 16); - if (b > 0xff) - b = 0xff; - else if (b < 0) - b = 0; - lb_set_brightness(b); -} - -/* T key */ -void demo_tap(void) -{ - if (!demo_mode) - return; - lightbar_sequence(LIGHTBAR_TAP); -} - -/******************************************************************************/ -/* Helper functions and data. */ -/******************************************************************************/ - -#define F(x) (x * FP_SCALE) -static const uint16_t _ramp_table[] = { - F(0.000000), F(0.002408), F(0.009607), F(0.021530), F(0.038060), - F(0.059039), F(0.084265), F(0.113495), F(0.146447), F(0.182803), - F(0.222215), F(0.264302), F(0.308658), F(0.354858), F(0.402455), - F(0.450991), F(0.500000), F(0.549009), F(0.597545), F(0.645142), - F(0.691342), F(0.735698), F(0.777785), F(0.817197), F(0.853553), - F(0.886505), F(0.915735), F(0.940961), F(0.961940), F(0.978470), - F(0.990393), F(0.997592), F(1.000000), -}; -#undef F - -/* This function provides a smooth ramp up from 0.0 to 1.0 and back to 0.0, - * for input from 0x00 to 0xff. */ -static inline int cycle_010(uint8_t i) -{ - uint8_t bucket, index; - - if (i == 128) - return FP_SCALE; - else if (i > 128) - i = 256 - i; - - bucket = i >> 2; - index = i & 0x3; - - return _ramp_table[bucket] + - ((_ramp_table[bucket + 1] - _ramp_table[bucket]) * index >> 2); -} - -/******************************************************************************/ -/* Here's where we keep messages waiting to be delivered to the lightbar task. - * If more than one is sent before the task responds, we only want to deliver - * the latest one. */ -static uint32_t pending_msg; -/* And here's the task event that we use to trigger delivery. */ -#define PENDING_MSG TASK_EVENT_CUSTOM_BIT(0) - -/* Interruptible delay. */ -#define WAIT_OR_RET(A) \ - do { \ - uint32_t msg = task_wait_event(A); \ - uint32_t p_msg = pending_msg; \ - if (msg & PENDING_MSG && p_msg != st.cur_seq) \ - return p_msg; \ - } while (0) - -/******************************************************************************/ -/* Here are the preprogrammed sequences. */ -/******************************************************************************/ - -/* Pulse google colors once, off to on to off. */ -static uint32_t pulse_google_colors(void) -{ - int w, i, r, g, b; - int f; - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_down); - } - - return 0; -} - -/* CPU is waking from sleep. */ -static uint32_t sequence_S3S0(void) -{ - int w, r, g, b; - int f, fmin; - int ci; - uint32_t res; - - lb_init(1); - lb_on(); - get_battery_level(); - - res = pulse_google_colors(); - if (res) - return res; - -#ifndef BLUE_PULSING - /* next sequence */ - return LIGHTBAR_S0; -#endif - - /* Ramp up to starting brightness, using S0 colors */ - ci = st.p.s0_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - ci = 0; - - fmin = st.p.osc_min[st.battery_is_charging] * FP_SCALE / 255; - - for (w = 0; w <= 128; w++) { - f = cycle_010(w) * fmin / FP_SCALE; - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3s0_ramp_up); - } - - /* Initial conditions */ - st.w0 = -256; /* start cycle_npn() quietly */ - st.ramp = 0; - - /* Ready for S0 */ - return LIGHTBAR_S0; -} - -#ifdef BLUE_PULSING - -/* This function provides a pulsing oscillation between -0.5 and +0.5. */ -static inline int cycle_npn(uint16_t i) -{ - if ((i / 256) % 4) - return -FP_SCALE / 2; - return cycle_010(i) - FP_SCALE / 2; -} - -/* CPU is fully on */ -static uint32_t sequence_S0(void) -{ - int tick, last_tick; - timestamp_t start, now; - uint8_t r, g, b; - int i, ci; - uint8_t w_ofs; - uint16_t w; - int f, fmin, fmax, base_s0, osc_s0, f_ramp; - - start = get_time(); - tick = last_tick = 0; - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - - while (1) { - now = get_time(); - - /* Only check the battery state every few seconds. The battery - * charging task doesn't update as quickly as we do, and isn't - * always valid for a bit after jumping from RO->RW. */ - tick = (now.le.lo - start.le.lo) / SECOND; - if (tick % 4 == 3 && tick != last_tick) { - get_battery_level(); - last_tick = tick; - } - - /* Calculate the colors */ - ci = st.p.s0_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - ci = 0; - w_ofs = st.p.w_ofs[st.battery_is_charging]; - fmin = st.p.osc_min[st.battery_is_charging] * FP_SCALE / 255; - fmax = st.p.osc_max[st.battery_is_charging] * FP_SCALE / 255; - base_s0 = (fmax + fmin) / 2; - osc_s0 = fmax - fmin; - f_ramp = st.ramp * FP_SCALE / 255; - - for (i = 0; i < NUM_LEDS; i++) { - w = st.w0 - i * w_ofs * f_ramp / FP_SCALE; - f = base_s0 + osc_s0 * cycle_npn(w) / FP_SCALE; - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - - /* Increment the phase */ - if (st.battery_is_charging) - st.w0--; - else - st.w0++; - - /* Continue ramping in if needed */ - if (st.ramp < 0xff) - st.ramp++; - - i = st.p.s0a_tick_delay[st.battery_is_charging]; - WAIT_OR_RET(i); - } - return 0; -} - -#else /* just simple google colors */ - -static uint32_t sequence_S0(void) -{ - int w, i, r, g, b; - int f, change; - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - - /* Ramp up */ - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_up); - } - - while (1) { - change = get_battery_level(); - - if (change) { - /* Not really low use google colors */ - if (st.battery_level) { - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r; - g = st.p.color[i].g; - b = st.p.color[i].b; - lb_set_rgb(i, r, g, b); - } - } else { - r = st.p.color[PRIMARY_RED].r; - g = st.p.color[PRIMARY_RED].g; - b = st.p.color[PRIMARY_RED].b; - lb_set_rgb(4, r, g, b); - } - } - - WAIT_OR_RET(1 * SECOND); - } - return 0; -} - -#endif - -/* CPU is going to sleep. */ -static uint32_t sequence_S0S3(void) -{ - int w, i, r, g, b; - int f; - uint8_t drop[NUM_LEDS][3]; - uint32_t res; - - /* Grab current colors */ - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &drop[i][0], &drop[i][1], &drop[i][2]); - - /* Fade down to black */ - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = drop[i][0] * f / FP_SCALE; - g = drop[i][1] * f / FP_SCALE; - b = drop[i][2] * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.s0s3_ramp_down); - } - - /* pulse once and done */ - res = pulse_google_colors(); - if (res) - return res; - - /* next sequence */ - return LIGHTBAR_S3; -} - -/* CPU is sleeping */ -static uint32_t sequence_S3(void) -{ - int r, g, b; - int w; - int f; - int ci; - - lb_off(); - lb_init(1); - lb_set_rgb(NUM_LEDS, 0, 0, 0); - get_battery_level(); - while (1) { - WAIT_OR_RET(st.p.s3_sleep_for); - - /* only pulse if we've been given a valid color index */ - ci = st.p.s3_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - continue; - - /* pulse once */ - lb_on(); - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3_ramp_down); - } - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_off(); - } - return 0; -} - - -/* CPU is powering up. We generally boot fast enough that we don't have time - * to do anything interesting in the S3 state, but go straight on to S0. */ -static uint32_t sequence_S5S3(void) -{ - /* The controllers need 100us after power is applied before they'll - * respond. Don't return early, because we still want to initialize the - * lightbar even if another message comes along while we're waiting. */ - usleep(100); - lb_init(1); - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - /* next sequence */ - return LIGHTBAR_S3; -} - -/* Sleep to off. The S3->S5 transition takes about 10msec, so just wait. */ -static uint32_t sequence_S3S5(void) -{ - lb_off(); - /* next sequence */ - return LIGHTBAR_S5; -} - -/* Pulse S5 color to indicate that the battery is so critically low that it - * must charge first before the system can power on. */ -static uint32_t pulse_s5_color(void) -{ - int r, g, b; - int f; - int w; - struct rgb_s *color = &st.p.color[st.p.s5_idx]; - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - r = color->r * f / FP_SCALE; - g = color->g * f / FP_SCALE; - b = color->b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s5_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - r = color->r * f / FP_SCALE; - g = color->g * f / FP_SCALE; - b = color->b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s5_ramp_down); - } - - return 0; -} - -/* CPU is off. Pulse the lightbar if a charger is attached and the battery is - * so low that the system cannot power on. Otherwise, the lightbar loses power - * when the CPU is in S5, so there's nothing to do. We'll just wait here until - * the state changes. */ -static uint32_t sequence_S5(void) -{ - int initialized = 0; - uint32_t res = 0; - - get_battery_level(); - while (1) { - if (!st.battery_is_power_on_prevented || - !st.battery_is_charging) - break; - - if (!initialized) { -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Request that lightbar power rails be turned on. */ - if (lb_power(1)) { - lb_set_rgb(NUM_LEDS, 0, 0, 0); - } -#endif - lb_on(); - initialized = 1; - } - - res = pulse_s5_color(); - if (res) - break; - } - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - if (initialized) - /* Suggest that the lightbar power rails can be shut down. */ - lb_power(0); -#endif - lb_off(); - if (!res) - WAIT_OR_RET(-1); - return res; -} - -/* The AP is going to poke at the lightbar directly, so we don't want the EC - * messing with it. We'll just sit here and ignore all other messages until - * we're told to continue (or until we think the AP is shutting down). - */ -static uint32_t sequence_STOP(void) -{ - uint32_t msg; - - do { - msg = task_wait_event(-1); - CPRINTS("LB %s() got pending_msg %d", __func__, pending_msg); - } while (msg != PENDING_MSG || ( - pending_msg != LIGHTBAR_RUN && - pending_msg != LIGHTBAR_S0S3 && - pending_msg != LIGHTBAR_S3 && - pending_msg != LIGHTBAR_S3S5 && - pending_msg != LIGHTBAR_S5)); - return 0; -} - -/* Telling us to run when we're already running should do nothing. */ -static uint32_t sequence_RUN(void) -{ - return 0; -} - -/* We shouldn't come here, but if we do it shouldn't hurt anything. This - * sequence is to indicate an internal error in the lightbar logic, not an - * error with the Chromebook itself. - */ -static uint32_t sequence_ERROR(void) -{ - lb_init(1); - lb_on(); - - lb_set_rgb(0, 255, 255, 255); - lb_set_rgb(1, 255, 0, 255); - lb_set_rgb(2, 0, 255, 255); - lb_set_rgb(3, 255, 255, 255); - - WAIT_OR_RET(10 * SECOND); - return 0; -} - -static const struct { - uint8_t led; - uint8_t r, g, b; - unsigned int delay; -} konami[] = { - - {1, 0xff, 0xff, 0x00, 0}, - {2, 0xff, 0xff, 0x00, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 100000}, - - {1, 0xff, 0xff, 0x00, 0}, - {2, 0xff, 0xff, 0x00, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0x00, 0xff, 0}, - {3, 0x00, 0x00, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0x00, 0xff, 0}, - {3, 0x00, 0x00, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0xff, 0x00, 0x00, 0}, - {1, 0xff, 0x00, 0x00, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {1, 0x00, 0x00, 0x00, 100000}, - - {2, 0x00, 0xff, 0x00, 0}, - {3, 0x00, 0xff, 0x00, 100000}, - {2, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0xff, 0x00, 0x00, 0}, - {1, 0xff, 0x00, 0x00, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {1, 0x00, 0x00, 0x00, 100000}, - - {2, 0x00, 0xff, 0x00, 0}, - {3, 0x00, 0xff, 0x00, 100000}, - {2, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0xff, 0xff, 0}, - {2, 0x00, 0xff, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 150000}, - - {1, 0xff, 0x00, 0xff, 0}, - {3, 0xff, 0x00, 0xff, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 250000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, -}; - -static uint32_t sequence_KONAMI_inner(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(konami); i++) { - lb_set_rgb(konami[i].led, - konami[i].r, konami[i].g, konami[i].b); - if (konami[i].delay) - WAIT_OR_RET(konami[i].delay); - } - - return 0; -} - -static uint32_t sequence_KONAMI(void) -{ - int tmp; - uint32_t r; - - /* First clear all segments */ - lb_set_rgb(NUM_LEDS, 0, 0, 0); - - /* Force brightness to max, then restore it */ - tmp = lb_get_brightness(); - lb_set_brightness(255); - r = sequence_KONAMI_inner(); - lb_set_brightness(tmp); - return r; -} - -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT -/* Returns 0.0 to 1.0 for val in [min, min + ofs] */ -static int range(int val, int min, int ofs) -{ - if (val <= min) - return 0; - if (val >= min+ofs) - return FP_SCALE; - return (val - min) * FP_SCALE / ofs; -} -#endif - -/* Handy constant */ -#define CUT (100 / NUM_LEDS) - -static uint32_t sequence_TAP_inner(int dir) -{ - enum { RED, YELLOW, GREEN } base_color; - timestamp_t start, now; - uint32_t elapsed_time = 0; - int i, l, ci, max_led; - int f_osc, f_mult; - int gi, gr, gate[NUM_LEDS] = {0, 0, 0, 0}; - uint8_t w = 0; -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT - int f_min, f_delta, f_power; - - f_min = st.p.tap_seg_min_on * FP_SCALE / 100; - f_delta = (st.p.tap_seg_max_on - st.p.tap_seg_min_on) * FP_SCALE / 100; -#endif - f_osc = st.p.tap_seg_osc * FP_SCALE / 100; - - get_battery_level(); - - if (st.battery_level == 0) - base_color = RED; - else if (st.battery_percent > st.p.tap_pct_green) - base_color = GREEN; - else - base_color = YELLOW; - - ci = st.p.tap_idx[base_color]; - max_led = st.battery_percent / CUT; - - start = get_time(); - while (1) { - /* Enable the segments gradually */ - gi = elapsed_time / st.p.tap_gate_delay; - gr = elapsed_time % st.p.tap_gate_delay; - if (gi < NUM_LEDS) - gate[gi] = FP_SCALE * gr / st.p.tap_gate_delay; - if (gi && gi <= NUM_LEDS) - gate[gi - 1] = FP_SCALE; - - for (i = 0; i < NUM_LEDS; i++) { - -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT - if (max_led > i) { - f_mult = FP_SCALE; - } else if (max_led < i) { - f_mult = 0; - } else { - switch (base_color) { - case RED: - f_power = range(st.battery_percent, 0, - st.p.battery_threshold[0] - 1); - break; - case YELLOW: - f_power = range(st.battery_percent, - i * CUT, CUT - 1); - break; - case GREEN: - /* green is always full on */ - f_power = FP_SCALE; - } - f_mult = f_min + f_power * f_delta / FP_SCALE; - } -#else - if (max_led >= i) - f_mult = FP_SCALE; - else if (max_led < i) - f_mult = 0; -#endif - - f_mult = f_mult * gate[i] / FP_SCALE; - - /* Pulse when charging and not yet full */ - if (st.battery_is_charging && - st.battery_percent <= st.p.tap_pct_green) { - int scale = (FP_SCALE - - f_osc * cycle_010(w++) / FP_SCALE); - f_mult = f_mult * scale / FP_SCALE; - } - - l = dir ? i : NUM_LEDS - 1 - i; - lb_set_rgb(l, f_mult * st.p.color[ci].r / FP_SCALE, - f_mult * st.p.color[ci].g / FP_SCALE, - f_mult * st.p.color[ci].b / FP_SCALE); - } - - WAIT_OR_RET(st.p.tap_tick_delay); - - /* Return after some time has elapsed */ - now = get_time(); - elapsed_time = now.le.lo - start.le.lo; - if (elapsed_time > st.p.tap_display_time) - break; - } - return 0; -} - -/* Override the tap direction for testing. -1 means ask the PD MCU. */ -static int force_dir = -1; - -/* Return 0 (left or none) or 1 (right) */ -static int get_tap_direction(void) -{ - static int last_dir; - int dir = 0; - - if (force_dir >= 0) - dir = force_dir; -#ifdef HAS_TASK_PDCMD - else - dir = pd_get_active_charge_port(); -#endif - if (dir < 0) - dir = last_dir; - else if (dir != 1) - dir = 0; - - CPRINTS("LB tap direction %d", dir); - last_dir = dir; - return dir; -} - -static uint32_t sequence_TAP(void) -{ - int i; - uint32_t r; - uint8_t br, save[NUM_LEDS][3]; - int dir; - - /* - * There's a lot of unavoidable glitchiness on the AC_PRESENT interrupt - * each time the EC boots, resulting in fights between the TAP sequence - * and the S5S3->S3->S3S0->S0 sequences. This delay prevents the lights - * from flickering without reducing the responsiveness to manual taps. - */ - WAIT_OR_RET(100 * MSEC); - - /* Which direction should the power meter go? */ - dir = get_tap_direction(); - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Request that the lightbar power rails be turned on. */ - if (lb_power(1)) { - lb_set_rgb(NUM_LEDS, 0, 0, 0); - } -#endif - /* First clear all segments */ - lb_set_rgb(NUM_LEDS, 0, 0, 0); - - lb_on(); - - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &save[i][0], &save[i][1], &save[i][2]); - br = lb_get_brightness(); - lb_set_brightness(255); - - r = sequence_TAP_inner(dir); - - lb_set_brightness(br); - for (i = 0; i < NUM_LEDS; i++) - lb_set_rgb(i, save[i][0], save[i][1], save[i][2]); - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Suggest that the lightbar power rails can be shut down again. */ - lb_power(0); -#endif - return r; -} - -/****************************************************************************/ -/* Lightbar bytecode interpreter: Lightbyte. */ -/****************************************************************************/ - -/* When a program halts, return this. */ -#define PROGRAM_FINISHED 2 - -static struct lightbar_program cur_prog; -static struct lightbar_program next_prog; -static uint8_t pc; - -static uint8_t led_desc[NUM_LEDS][LB_CONT_MAX][3]; -static uint32_t lb_wait_delay; -static uint32_t lb_ramp_delay; -/* Get one byte of data pointed to by the pc and advance - * the pc forward. - */ -static inline uint32_t decode_8(uint8_t *dest) -{ - if (pc >= cur_prog.size) { - CPRINTS("pc 0x%02x out of bounds", pc); - return EC_RES_INVALID_PARAM; - } - *dest = cur_prog.data[pc++]; - return EC_SUCCESS; -} - -/* Get four bytes of data pointed to by the pc and advance - * the pc forward that amount. - */ -static inline uint32_t decode_32(uint32_t *dest) -{ - if (pc >= cur_prog.size - 3) { - CPRINTS("pc 0x%02x near or out of bounds", pc); - return EC_RES_INVALID_PARAM; - } - *dest = cur_prog.data[pc++] << 24; - *dest |= cur_prog.data[pc++] << 16; - *dest |= cur_prog.data[pc++] << 8; - *dest |= cur_prog.data[pc++]; - return EC_SUCCESS; -} - -/* ON - turn on lightbar */ -static uint32_t lightbyte_ON(void) -{ - lb_on(); - return EC_SUCCESS; -} - -/* OFF - turn off lightbar */ -static uint32_t lightbyte_OFF(void) -{ - lb_off(); - return EC_SUCCESS; -} - -/* JUMP xx - jump to immediate location - * Changes the pc to the one-byte immediate argument. - */ -static uint32_t lightbyte_JUMP(void) -{ - return decode_8(&pc); -} - -/* JUMP_BATTERY aa bb - switch on battery level - * If the battery is low, changes pc to aa. - * If the battery is high, changes pc to bb. - * Otherwise, continues execution as normal. - */ -static uint32_t lightbyte_JUMP_BATTERY(void) -{ - uint8_t low_pc, high_pc; - if (decode_8(&low_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&high_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - get_battery_level(); - if (st.battery_level == 0) - pc = low_pc; - else if (st.battery_level == 3) - pc = high_pc; - - return EC_SUCCESS; -} - -/* JUMP_IF_CHARGING xx - conditional jump to location - * Changes the pc to xx if the device is charging. - */ -static uint32_t lightbyte_JUMP_IF_CHARGING(void) -{ - uint8_t charge_pc; - if (decode_8(&charge_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - if (st.battery_is_charging) - pc = charge_pc; - - return EC_SUCCESS; -} - -/* SET_WAIT_DELAY xx xx xx xx - set up to yield processor - * Sets the wait delay to the given four-byte immediate, in - * microseconds. Future WAIT instructions will wait for this - * much time. - */ -static uint32_t lightbyte_SET_WAIT_DELAY(void) -{ - return decode_32(&lb_wait_delay); -} - -/* SET_RAMP_DELAY xx xx xx xx - change ramp speed - * This sets the length of time between ramp/cycle steps to - * the four-byte immediate argument, which represents a duration - * in milliseconds. - */ -static uint32_t lightbyte_SET_RAMP_DELAY(void) -{ - return decode_32(&lb_ramp_delay); -} - -/* WAIT - yield processor for some time - * Yields the processor for some amount of time set by the most - * recent SET_WAIT_DELAY instruction. - */ -static uint32_t lightbyte_WAIT(void) -{ - if (lb_wait_delay != 0) - WAIT_OR_RET(lb_wait_delay); - - return EC_SUCCESS; -} - -/* SET_BRIGHTNESS xx - * Sets the current brightness to the given one-byte - * immediate argument. - */ -static uint32_t lightbyte_SET_BRIGHTNESS(void) -{ - uint8_t val; - if (decode_8(&val) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - lb_set_brightness(val); - return EC_SUCCESS; -} - -/* SET_COLOR_SINGLE cc xx - * SET_COLOR_RGB cc rr gg bb - * Stores a color value in the led_desc structure. - * cc is a bit-packed location to perform the action on. - * - * The high four bits are a bitset for which LEDs to operate on. - * LED 0 is the lowest of the four bits. - * - * The next two bits are the control bits. This should be a value - * in lb_control that is not LB_CONT_MAX, and the corresponding - * color will be the one the action is performed on. - * - * The last two bits are the color bits if this instruction is - * SET_COLOR_SINGLE. They correspond to a LB_COL value for the - * channel to set the color for using the next immediate byte. - * In SET_COLOR_RGB, these bits are don't-cares, as there should - * always be three bytes that follow, which correspond to a - * complete RGB specification. - */ -static uint32_t lightbyte_SET_COLOR_SINGLE(void) -{ - - uint8_t packed_loc, led, control, color, value; - int i; - if (decode_8(&packed_loc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&value) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - led = packed_loc >> 4; - control = (packed_loc >> 2) & 0x3; - color = packed_loc & 0x3; - - if (control >= LB_CONT_MAX) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < NUM_LEDS; i++) - if (led & BIT(i)) - led_desc[i][control][color] = value; - - return EC_SUCCESS; -} - -static uint32_t lightbyte_SET_COLOR_RGB(void) -{ - uint8_t packed_loc, r, g, b, led, control; - int i; - - /* gross */ - if (decode_8(&packed_loc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&r) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&g) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&b) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - led = packed_loc >> 4; - control = (packed_loc >> 2) & 0x3; - - if (control >= LB_CONT_MAX) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < NUM_LEDS; i++) - if (led & BIT(i)) { - led_desc[i][control][LB_COL_RED] = r; - led_desc[i][control][LB_COL_GREEN] = g; - led_desc[i][control][LB_COL_BLUE] = b; - } - - return EC_SUCCESS; -} - -/* GET_COLORS - take current colors and push them to the state - * Gets the current state of the LEDs and puts them in COLOR0. - * Good for the beginning of a program if you need to fade in. - */ -static uint32_t lightbyte_GET_COLORS(void) -{ - int i; - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &led_desc[i][LB_CONT_COLOR0][LB_COL_RED], - &led_desc[i][LB_CONT_COLOR0][LB_COL_GREEN], - &led_desc[i][LB_CONT_COLOR0][LB_COL_BLUE]); - - return EC_SUCCESS; -} - -/* SWAP_COLORS - swaps beginning and end colors in state - * Exchanges COLOR0 and COLOR1 on all LEDs. - */ -static uint32_t lightbyte_SWAP_COLORS(void) -{ - int i, j, tmp; - for (i = 0; i < NUM_LEDS; i++) - for (j = 0; j < 3; j++) { - tmp = led_desc[i][LB_CONT_COLOR0][j]; - led_desc[i][LB_CONT_COLOR0][j] = - led_desc[i][LB_CONT_COLOR1][j]; - led_desc[i][LB_CONT_COLOR1][j] = tmp; - } - - return EC_SUCCESS; -} - -static inline int get_interp_value(int led, int color, int interp) -{ - int base = led_desc[led][LB_CONT_COLOR0][color]; - int delta = led_desc[led][LB_CONT_COLOR1][color] - base; - return base + (delta * interp / FP_SCALE); -} - -static void set_all_leds(int color) -{ - int i, r, g, b; - for (i = 0; i < NUM_LEDS; i++) { - r = led_desc[i][color][LB_COL_RED]; - g = led_desc[i][color][LB_COL_GREEN]; - b = led_desc[i][color][LB_COL_BLUE]; - lb_set_rgb(i, r, g, b); - } -} - -static uint32_t ramp_all_leds(int stop_at) -{ - int w, i, r, g, b, f; - for (w = 0; w < stop_at; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = get_interp_value(i, LB_COL_RED, f); - g = get_interp_value(i, LB_COL_GREEN, f); - b = get_interp_value(i, LB_COL_BLUE, f); - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(lb_ramp_delay); - } - return EC_SUCCESS; -} - -/* RAMP_ONCE - simple gradient or color set - * If the ramp delay is set to zero, then this sets the color of - * all LEDs to their respective COLOR1. - * If the ramp delay is nonzero, then this sets their color to - * their respective COLOR0, and takes them via interpolation to - * COLOR1, with the delay time passing in between each step. - */ -static uint32_t lightbyte_RAMP_ONCE(void) -{ - /* special case for instantaneous set */ - if (lb_ramp_delay == 0) { - set_all_leds(LB_CONT_COLOR1); - return EC_SUCCESS; - } - - return ramp_all_leds(128); -} - -/* CYCLE_ONCE - simple cycle or color set - * If the ramp delay is zero, then this sets the color of all LEDs - * to their respective COLOR0. - * If the ramp delay is nonzero, this sets the color of all LEDs - * to COLOR0, then performs a ramp (as in RAMP_ONCE) to COLOR1, - * and finally back to COLOR0. - */ -static uint32_t lightbyte_CYCLE_ONCE(void) -{ - /* special case for instantaneous set */ - if (lb_ramp_delay == 0) { - set_all_leds(LB_CONT_COLOR0); - return EC_SUCCESS; - } - - return ramp_all_leds(256); -} - -/* CYCLE - repeating cycle - * Indefinitely ramps from COLOR0 to COLOR1, taking into - * account the PHASE of each component of each color when - * interpolating. (Different LEDs and different color channels - * on a single LED can start at different places in the cycle, - * though they will advance at the same rate.) - * - * If the ramp delay is zero, this instruction will error out. - */ -static uint32_t lightbyte_CYCLE(void) -{ - int w, i, r, g, b; - - /* what does it mean to cycle indefinitely with 0 delay? */ - if (lb_ramp_delay == 0) - return EC_RES_INVALID_PARAM; - - for (w = 0;; w++) { - for (i = 0; i < NUM_LEDS; i++) { - r = get_interp_value(i, LB_COL_RED, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_RED])); - g = get_interp_value(i, LB_COL_GREEN, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_GREEN])); - b = get_interp_value(i, LB_COL_BLUE, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_BLUE])); - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(lb_ramp_delay); - } - return EC_SUCCESS; -} - -/* HALT - return with success - * Show's over. Go back to what you were doing before. - */ -static uint32_t lightbyte_HALT(void) -{ - return PROGRAM_FINISHED; -} - -#undef GET_INTERP_VALUE - -#define OP(NAME, BYTES, MNEMONIC) NAME, -#include "lightbar_opcode_list.h" -enum lightbyte_opcode { - LIGHTBAR_OPCODE_TABLE - MAX_OPCODE -}; -#undef OP - -#define OP(NAME, BYTES, MNEMONIC) lightbyte_ ## NAME, -#include "lightbar_opcode_list.h" -static uint32_t (*lightbyte_dispatch[])(void) = { - LIGHTBAR_OPCODE_TABLE -}; -#undef OP - -#define OP(NAME, BYTES, MNEMONIC) MNEMONIC, -#include "lightbar_opcode_list.h" -static const char * const lightbyte_names[] = { - LIGHTBAR_OPCODE_TABLE -}; -#undef OP - -static uint32_t sequence_PROGRAM(void) -{ - uint8_t saved_brightness; - uint8_t next_inst; - uint32_t rc; - uint8_t old_pc; - - /* load next program */ - memcpy(&cur_prog, &next_prog, sizeof(struct lightbar_program)); - - /* reset program state */ - saved_brightness = lb_get_brightness(); - pc = 0; - memset(led_desc, 0, sizeof(led_desc)); - lb_wait_delay = 0; - lb_ramp_delay = 0; - - lb_on(); - lb_set_brightness(255); - - /* decode-execute loop */ - for (;;) { - old_pc = pc; - if (decode_8(&next_inst) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - if (next_inst >= MAX_OPCODE) { - CPRINTS("LB PROGRAM pc: 0x%02x, " - "found invalid opcode 0x%02x", - old_pc, next_inst); - lb_set_brightness(saved_brightness); - return EC_RES_INVALID_PARAM; - } else { - CPRINTS("LB PROGRAM pc: 0x%02x, opcode 0x%02x -> %s", - old_pc, next_inst, lightbyte_names[next_inst]); - rc = lightbyte_dispatch[next_inst](); - if (rc) { - lb_set_brightness(saved_brightness); - return rc; - } - } - - /* yield processor in case we are stuck in a tight loop */ - WAIT_OR_RET(100); - } -} - -/****************************************************************************/ -/* The main lightbar task. It just cycles between various pretty patterns. */ -/****************************************************************************/ - -/* Distinguish "normal" sequences from one-shot sequences */ -static inline int is_normal_sequence(enum lightbar_sequence seq) -{ - return (seq >= LIGHTBAR_S5 && seq <= LIGHTBAR_S3S5); -} - -/* Link each sequence with a command to invoke it. */ -struct lightbar_cmd_t { - const char * const string; - uint32_t (*sequence)(void); -}; - -#define LBMSG(state) { #state, sequence_##state } -#include "lightbar_msg_list.h" -static struct lightbar_cmd_t lightbar_cmds[] = { - LIGHTBAR_MSG_LIST -}; -#undef LBMSG - -void lightbar_task(void) -{ - uint32_t next_seq; - - CPRINTS("LB task starting"); - - lightbar_restore_state(); - - while (1) { - CPRINTS("LB running cur_seq %d %s. prev_seq %d %s", - st.cur_seq, lightbar_cmds[st.cur_seq].string, - st.prev_seq, lightbar_cmds[st.prev_seq].string); - next_seq = lightbar_cmds[st.cur_seq].sequence(); - if (next_seq) { - CPRINTS("LB cur_seq %d %s returned pending msg %d %s", - st.cur_seq, lightbar_cmds[st.cur_seq].string, - next_seq, lightbar_cmds[next_seq].string); - if (st.cur_seq != next_seq) { - if (is_normal_sequence(st.cur_seq)) - st.prev_seq = st.cur_seq; - st.cur_seq = next_seq; - } - } else { - CPRINTS("LB cur_seq %d %s returned value 0", - st.cur_seq, lightbar_cmds[st.cur_seq].string); - switch (st.cur_seq) { - case LIGHTBAR_S5S3: - st.cur_seq = LIGHTBAR_S3; - break; - case LIGHTBAR_S3S0: - st.cur_seq = LIGHTBAR_S0; - break; - case LIGHTBAR_S0S3: - st.cur_seq = LIGHTBAR_S3; - break; - case LIGHTBAR_S3S5: - st.cur_seq = LIGHTBAR_S5; - break; - case LIGHTBAR_STOP: - case LIGHTBAR_RUN: - case LIGHTBAR_ERROR: - case LIGHTBAR_KONAMI: - case LIGHTBAR_TAP: - case LIGHTBAR_PROGRAM: - st.cur_seq = st.prev_seq; - default: - break; - } - } - } -} - -/* Function to request a preset sequence from the lightbar task. */ -void lightbar_sequence_f(enum lightbar_sequence num, const char *f) -{ - if (num > 0 && num < LIGHTBAR_NUM_SEQUENCES) { - CPRINTS("LB %s() requests %d %s", f, num, - lightbar_cmds[num].string); - pending_msg = num; - task_set_event(TASK_ID_LIGHTBAR, PENDING_MSG); - } else - CPRINTS("LB %s() requests %d - ignored", f, num); -} - -/****************************************************************************/ -/* Get notifications from other parts of the system */ - -static uint8_t manual_suspend_control; - -static void lightbar_startup(void) -{ - manual_suspend_control = 0; - lightbar_sequence(LIGHTBAR_S5S3); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, lightbar_startup, HOOK_PRIO_DEFAULT); - -static void lightbar_resume(void) -{ - if (!manual_suspend_control) - lightbar_sequence(LIGHTBAR_S3S0); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, lightbar_resume, HOOK_PRIO_DEFAULT); - -static void lightbar_suspend(void) -{ - if (!manual_suspend_control) - lightbar_sequence(LIGHTBAR_S0S3); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, lightbar_suspend, HOOK_PRIO_DEFAULT); - -static void lightbar_shutdown(void) -{ - lightbar_sequence(LIGHTBAR_S3S5); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, lightbar_shutdown, HOOK_PRIO_DEFAULT); - -/****************************************************************************/ -/* Host commands via LPC bus */ -/****************************************************************************/ - -static enum ec_status lpc_cmd_lightbar(struct host_cmd_handler_args *args) -{ - const struct ec_params_lightbar *in = args->params; - struct ec_response_lightbar *out = args->response; - int rv; - - switch (in->cmd) { - case LIGHTBAR_CMD_DUMP: - lb_hc_cmd_dump(out); - args->response_size = sizeof(out->dump); - break; - case LIGHTBAR_CMD_OFF: - lb_off(); - break; - case LIGHTBAR_CMD_ON: - lb_on(); - break; - case LIGHTBAR_CMD_INIT: - lb_init(1); - break; - case LIGHTBAR_CMD_SET_BRIGHTNESS: - lb_set_brightness(in->set_brightness.num); - break; - case LIGHTBAR_CMD_GET_BRIGHTNESS: - out->get_brightness.num = lb_get_brightness(); - args->response_size = sizeof(out->get_brightness); - break; - case LIGHTBAR_CMD_SEQ: - lightbar_sequence(in->seq.num); - break; - case LIGHTBAR_CMD_REG: - lb_hc_cmd_reg(in); - break; - case LIGHTBAR_CMD_SET_RGB: - lb_set_rgb(in->set_rgb.led, - in->set_rgb.red, - in->set_rgb.green, - in->set_rgb.blue); - break; - case LIGHTBAR_CMD_GET_RGB: - rv = lb_get_rgb(in->get_rgb.led, - &out->get_rgb.red, - &out->get_rgb.green, - &out->get_rgb.blue); - if (rv == EC_RES_SUCCESS) - args->response_size = sizeof(out->get_rgb); - return rv; - case LIGHTBAR_CMD_GET_SEQ: - out->get_seq.num = st.cur_seq; - args->response_size = sizeof(out->get_seq); - break; - case LIGHTBAR_CMD_DEMO: - demo_mode = in->demo.num ? 1 : 0; - CPRINTS("LB_demo %d", demo_mode); - break; - case LIGHTBAR_CMD_GET_DEMO: - out->get_demo.num = demo_mode; - args->response_size = sizeof(out->get_demo); - break; - case LIGHTBAR_CMD_GET_PARAMS_V0: - CPRINTS("LB_get_params_v0 not supported"); - return EC_RES_INVALID_VERSION; - break; - case LIGHTBAR_CMD_SET_PARAMS_V0: - CPRINTS("LB_set_params_v0 not supported"); - return EC_RES_INVALID_VERSION; - break; - case LIGHTBAR_CMD_GET_PARAMS_V1: - CPRINTS("LB_get_params_v1"); - memcpy(&out->get_params_v1, &st.p, sizeof(st.p)); - args->response_size = sizeof(out->get_params_v1); - break; - case LIGHTBAR_CMD_SET_PARAMS_V1: - CPRINTS("LB_set_params_v1"); - memcpy(&st.p, &in->set_params_v1, sizeof(st.p)); - break; - case LIGHTBAR_CMD_SET_PROGRAM: - CPRINTS("LB_set_program"); - memcpy(&next_prog, - &in->set_program, - sizeof(struct lightbar_program)); - break; - case LIGHTBAR_CMD_VERSION: - CPRINTS("LB_version"); - out->version.num = LIGHTBAR_IMPLEMENTATION_VERSION; - out->version.flags = LIGHTBAR_IMPLEMENTATION_FLAGS; - args->response_size = sizeof(out->version); - break; - case LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL: - CPRINTS("LB_manual_suspend_ctrl"); - manual_suspend_control = in->manual_suspend_ctrl.enable; - break; - case LIGHTBAR_CMD_SUSPEND: - CPRINTS("LB_suspend"); - lightbar_sequence(LIGHTBAR_S0S3); - break; - case LIGHTBAR_CMD_RESUME: - CPRINTS("LB_resume"); - lightbar_sequence(LIGHTBAR_S3S0); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_TIMING: - CPRINTS("LB_get_params_v2_timing"); - memcpy(&out->get_params_v2_timing, - &st.p_v2.timing, - sizeof(st.p_v2.timing)); - args->response_size = sizeof(out->get_params_v2_timing); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_TIMING: - CPRINTS("LB_set_params_v2_timing"); - memcpy(&st.p_v2.timing, - &in->set_v2par_timing, - sizeof(struct lightbar_params_v2_timing)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_TAP: - CPRINTS("LB_get_params_v2_tap"); - memcpy(&out->get_params_v2_tap, - &st.p_v2.tap, - sizeof(struct lightbar_params_v2_tap)); - args->response_size = sizeof(out->get_params_v2_tap); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_TAP: - CPRINTS("LB_set_params_v2_tap"); - memcpy(&st.p_v2.tap, - &in->set_v2par_tap, - sizeof(struct lightbar_params_v2_tap)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_OSCILLATION: - CPRINTS("LB_get_params_v2_oscillation"); - memcpy(&out->get_params_v2_osc, &st.p_v2.osc, - sizeof(struct lightbar_params_v2_oscillation)); - args->response_size = sizeof(out->get_params_v2_osc); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_OSCILLATION: - CPRINTS("LB_set_params_v2_oscillation"); - memcpy(&st.p_v2.osc, - &in->set_v2par_osc, - sizeof(struct lightbar_params_v2_oscillation)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_BRIGHTNESS: - CPRINTS("LB_get_params_v2_brightness"); - memcpy(&out->get_params_v2_bright, - &st.p_v2.bright, - sizeof(struct lightbar_params_v2_brightness)); - args->response_size = sizeof(out->get_params_v2_bright); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_BRIGHTNESS: - CPRINTS("LB_set_params_v2_brightness"); - memcpy(&st.p_v2.bright, - &in->set_v2par_bright, - sizeof(struct lightbar_params_v2_brightness)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_THRESHOLDS: - CPRINTS("LB_get_params_v2_thlds"); - memcpy(&out->get_params_v2_thlds, - &st.p_v2.thlds, - sizeof(struct lightbar_params_v2_thresholds)); - args->response_size = sizeof(out->get_params_v2_thlds); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_THRESHOLDS: - CPRINTS("LB_set_params_v2_thlds"); - memcpy(&st.p_v2.thlds, - &in->set_v2par_thlds, - sizeof(struct lightbar_params_v2_thresholds)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_COLORS: - CPRINTS("LB_get_params_v2_colors"); - memcpy(&out->get_params_v2_colors, - &st.p_v2.colors, - sizeof(struct lightbar_params_v2_colors)); - args->response_size = sizeof(out->get_params_v2_colors); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_COLORS: - CPRINTS("LB_set_params_v2_colors"); - memcpy(&st.p_v2.colors, - &in->set_v2par_colors, - sizeof(struct lightbar_params_v2_colors)); - break; - default: - CPRINTS("LB bad cmd 0x%x", in->cmd); - return EC_RES_INVALID_PARAM; - } - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_LIGHTBAR_CMD, - lpc_cmd_lightbar, - EC_VER_MASK(0)); - -/****************************************************************************/ -/* EC console commands */ -/****************************************************************************/ - -#ifdef CONFIG_CONSOLE_CMDHELP -static int help(const char *cmd) -{ - ccprintf("Usage:\n"); - ccprintf(" %s - dump all regs\n", cmd); - ccprintf(" %s off - enter standby\n", cmd); - ccprintf(" %s on - leave standby\n", cmd); - ccprintf(" %s init - load default vals\n", cmd); - ccprintf(" %s brightness [NUM] - set intensity (0-ff)\n", cmd); - ccprintf(" %s seq [NUM|SEQUENCE] - run given pattern" - " (no arg for list)\n", cmd); - ccprintf(" %s CTRL REG VAL - set LED controller regs\n", cmd); - ccprintf(" %s LED RED GREEN BLUE - set color manually" - " (LED=%d for all)\n", cmd, NUM_LEDS); - ccprintf(" %s LED - get current LED color\n", cmd); - ccprintf(" %s demo [0|1] - turn demo mode on & off\n", cmd); -#ifdef LIGHTBAR_SIMULATION - ccprintf(" %s program filename - load lightbyte program\n", cmd); -#endif - ccprintf(" %s version - show current version\n", cmd); - return EC_SUCCESS; -} -#endif - -static uint8_t find_msg_by_name(const char *str) -{ - uint8_t i; - for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) - if (!strcasecmp(str, lightbar_cmds[i].string)) - return i; - - return LIGHTBAR_NUM_SEQUENCES; -} - -static void show_msg_names(void) -{ - int i; - ccprintf("Sequences:"); - for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) - ccprintf(" %s", lightbar_cmds[i].string); - ccprintf("\nCurrent = 0x%x %s\n", st.cur_seq, - lightbar_cmds[st.cur_seq].string); -} - -static int command_lightbar(int argc, char **argv) -{ - int i; - uint8_t num, led, r = 0, g = 0, b = 0; - struct ec_response_lightbar out; - char *e; - - if (argc == 1) { /* no args = dump 'em all */ - lb_hc_cmd_dump(&out); - for (i = 0; i < ARRAY_SIZE(out.dump.vals); i++) - ccprintf(" %02x %02x %02x\n", - out.dump.vals[i].reg, - out.dump.vals[i].ic0, - out.dump.vals[i].ic1); - - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "init")) { - lb_init(1); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "off")) { - lb_off(); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "on")) { - lb_on(); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "version")) { - ccprintf("version %d flags 0x%x\n", - LIGHTBAR_IMPLEMENTATION_VERSION, - LIGHTBAR_IMPLEMENTATION_FLAGS); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "brightness")) { - if (argc > 2) { - num = 0xff & strtoi(argv[2], &e, 16); - lb_set_brightness(num); - } - ccprintf("brightness is %02x\n", lb_get_brightness()); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "demo")) { - if (argc > 2) { - if (!strcasecmp(argv[2], "on") || - argv[2][0] == '1') - demo_mode = 1; - else if (!strcasecmp(argv[2], "off") || - argv[2][0] == '0') - demo_mode = 0; - else - return EC_ERROR_PARAM1; - } - ccprintf("demo mode is %s\n", demo_mode ? "on" : "off"); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "seq")) { - if (argc == 2) { - show_msg_names(); - return 0; - } - num = 0xff & strtoi(argv[2], &e, 16); - if (*e) - num = find_msg_by_name(argv[2]); - if (num >= LIGHTBAR_NUM_SEQUENCES) - return EC_ERROR_PARAM2; - if (argc > 3) /* for testing TAP direction */ - force_dir = strtoi(argv[3], 0, 0); - lightbar_sequence(num); - return EC_SUCCESS; - } - -#ifdef LIGHTBAR_SIMULATION - /* Load a program. */ - if (argc >= 3 && !strcasecmp(argv[1], "program")) { - return lb_load_program(argv[2], &next_prog); - } -#endif - - if (argc == 4) { - struct ec_params_lightbar in; - in.reg.ctrl = strtoi(argv[1], &e, 16); - in.reg.reg = strtoi(argv[2], &e, 16); - in.reg.value = strtoi(argv[3], &e, 16); - lb_hc_cmd_reg(&in); - return EC_SUCCESS; - } - - if (argc == 5) { - led = strtoi(argv[1], &e, 16); - r = strtoi(argv[2], &e, 16); - g = strtoi(argv[3], &e, 16); - b = strtoi(argv[4], &e, 16); - lb_set_rgb(led, r, g, b); - return EC_SUCCESS; - } - - /* Only thing left is to try to read an LED value */ - num = strtoi(argv[1], &e, 16); - if (!(e && *e)) { - if (num >= NUM_LEDS) { - for (i = 0; i < NUM_LEDS; i++) { - lb_get_rgb(i, &r, &g, &b); - ccprintf("%x: %02x %02x %02x\n", i, r, g, b); - } - } else { - lb_get_rgb(num, &r, &g, &b); - ccprintf("%02x %02x %02x\n", r, g, b); - } - return EC_SUCCESS; - } - - -#ifdef CONFIG_CONSOLE_CMDHELP - help(argv[0]); -#endif - - return EC_ERROR_INVAL; -} -DECLARE_CONSOLE_COMMAND(lightbar, command_lightbar, - "[help | COMMAND [ARGS]]", - "Get/set lightbar state"); |