| /* Copyright (c) 2014 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. | |
| */ | |
| /* Button module for Chrome EC */ | |
| #include "button.h" | |
| #include "common.h" | |
| #include "console.h" | |
| #include "gpio.h" | |
| #include "hooks.h" | |
| #include "keyboard_protocol.h" | |
| #include "timer.h" | |
| #include "util.h" | |
| /* Console output macro */ | |
| #define CPRINTS(format, args...) cprints(CC_SWITCH, format, ## args) | |
| struct button_state_t { | |
| uint64_t debounce_time; | |
| int debounced_pressed; | |
| }; | |
| static struct button_state_t __bss_slow state[CONFIG_BUTTON_COUNT]; | |
| static uint64_t __bss_slow next_deferred_time; | |
| /* | |
| * Whether a button is currently pressed. | |
| */ | |
| static int raw_button_pressed(const struct button_config *button) | |
| { | |
| int raw_value = gpio_get_level(button->gpio); | |
| return button->flags & BUTTON_FLAG_ACTIVE_HIGH ? | |
| raw_value : !raw_value; | |
| } | |
| /* | |
| * Button initialization. | |
| */ | |
| static void button_init(void) | |
| { | |
| int i; | |
| CPRINTS("(re)initializing buttons and interrupts"); | |
| next_deferred_time = 0; | |
| for (i = 0; i < CONFIG_BUTTON_COUNT; i++) { | |
| state[i].debounced_pressed = raw_button_pressed(&buttons[i]); | |
| state[i].debounce_time = 0; | |
| gpio_enable_interrupt(buttons[i].gpio); | |
| } | |
| } | |
| DECLARE_HOOK(HOOK_INIT, button_init, HOOK_PRIO_DEFAULT); | |
| /* | |
| * Handle debounced button changing state. | |
| */ | |
| static void button_change_deferred(void); | |
| DECLARE_DEFERRED(button_change_deferred); | |
| static void button_change_deferred(void) | |
| { | |
| int i; | |
| int new_pressed; | |
| uint64_t soonest_debounce_time = 0; | |
| uint64_t time_now = get_time().val; | |
| for (i = 0; i < CONFIG_BUTTON_COUNT; i++) { | |
| /* Skip this button if we are not waiting to debounce */ | |
| if (state[i].debounce_time == 0) | |
| continue; | |
| if (state[i].debounce_time <= time_now) { | |
| /* Check if the state has changed */ | |
| new_pressed = raw_button_pressed(&buttons[i]); | |
| if (state[i].debounced_pressed != new_pressed) { | |
| state[i].debounced_pressed = new_pressed; | |
| CPRINTS("Button '%s' was %s", | |
| buttons[i].name, new_pressed ? | |
| "pressed" : "released"); | |
| #if defined(HAS_TASK_KEYPROTO) || defined(CONFIG_KEYBOARD_PROTOCOL_MKBP) | |
| keyboard_update_button(buttons[i].type, | |
| new_pressed); | |
| #endif | |
| } | |
| /* Clear the debounce time to stop checking it */ | |
| state[i].debounce_time = 0; | |
| } else { | |
| /* | |
| * Make sure the next deferred call happens on or before | |
| * each button needs it. | |
| */ | |
| soonest_debounce_time = (soonest_debounce_time == 0) ? | |
| state[i].debounce_time : | |
| MIN(soonest_debounce_time, | |
| state[i].debounce_time); | |
| } | |
| } | |
| if (soonest_debounce_time != 0) { | |
| next_deferred_time = soonest_debounce_time; | |
| hook_call_deferred(&button_change_deferred_data, | |
| next_deferred_time - time_now); | |
| } | |
| } | |
| /* | |
| * Handle a button interrupt. | |
| */ | |
| void button_interrupt(enum gpio_signal signal) | |
| { | |
| int i; | |
| uint64_t time_now = get_time().val; | |
| for (i = 0; i < CONFIG_BUTTON_COUNT; i++) { | |
| if (buttons[i].gpio != signal) | |
| continue; | |
| state[i].debounce_time = time_now + buttons[i].debounce_us; | |
| if (next_deferred_time <= time_now || | |
| next_deferred_time > state[i].debounce_time) { | |
| next_deferred_time = state[i].debounce_time; | |
| hook_call_deferred(&button_change_deferred_data, | |
| next_deferred_time - time_now); | |
| } | |
| break; | |
| } | |
| } |