From 311abbd8896a927b7c4a4b2a226060a5ed64b044 Mon Sep 17 00:00:00 2001 From: Alec Berg Date: Fri, 14 Mar 2014 09:27:31 -0700 Subject: rambi: glimmer: Disable key scanning in suspend when lid is open Added ability to disable the keyboard to wake from suspend when the lid is outside a certain angle range. This has been added to glimmer by defining CONFIG_LID_ANGLE_KEY_SCAN in its board.h. Also modified the lid angle calculation to include a reliability flag which can be used to tell when the hinge aligns too closely with gravity and the lid angle value is unreliable. BUG=none BRANCH=rambi TEST=Tested on a glimmer: In S3, verified that when the lid is open past ~180 deg, the keyboard does not wake the machine. Also verified that if you align hinge with gravity, the keyboard enabled/disabled status remains the same (since we can't actually trust the lid angle value). Change-Id: If1a1592d259902d38941936961854b81b3a75b95 Signed-off-by: Alec Berg Reviewed-on: https://chromium-review.googlesource.com/190061 --- board/glimmer/board.h | 13 ++++++++ common/build.mk | 1 + common/console_output.c | 1 + common/keyboard_scan.c | 15 ++++----- common/lid_angle.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ common/motion_sense.c | 55 +++++++++++++++++++++++++++------ include/config.h | 11 +++++++ include/console.h | 1 + include/keyboard_scan.h | 5 +++ include/lid_angle.h | 20 ++++++++++++ include/motion_sense.h | 3 ++ test/motion_sense.c | 6 ++-- 12 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 common/lid_angle.c create mode 100644 include/lid_angle.h diff --git a/board/glimmer/board.h b/board/glimmer/board.h index 0354674518..c134b50f80 100644 --- a/board/glimmer/board.h +++ b/board/glimmer/board.h @@ -34,6 +34,7 @@ #define CONFIG_KEYBOARD_IRQ_GPIO GPIO_KBD_IRQ_L #define CONFIG_KEYBOARD_PROTOCOL_8042 #define CONFIG_LED_COMMON +#define CONFIG_LID_ANGLE_KEY_SCAN #define CONFIG_LOW_POWER_IDLE #undef CONFIG_PECI #define CONFIG_POWER_BUTTON @@ -78,6 +79,18 @@ #define WIRELESS_GPIO_WWAN GPIO_PP3300_LTE_EN #define WIRELESS_GPIO_WLAN_POWER GPIO_PP3300_WLAN_EN +/* + * Macros to be used in suspend to determine the state of key scanning + * based on measured lid angle. Note that the angle input is bound [-180, 180] + * where 0 degrees is lid closed/lid fully open. Also note that the key scan + * enable state will only change if recent readings are all within range, + * therefore it is recommended to leave a deadband where the lid angle doesn't + * fit into either range. Also note, if lid is closed, key scanning is + * automatically disabled. + */ +#define LID_IN_RANGE_TO_ACCEPT_KEYS(ang) ((ang) >= 15 || (ang) <= -173) +#define LID_IN_RANGE_TO_IGNORE_KEYS(ang) ((ang) <= 12 && (ang) >= -170) + /* GPIO signal definitions. */ enum gpio_signal { /* Inputs with interrupt handlers are first for efficiency */ diff --git a/common/build.mk b/common/build.mk index 921a07f20c..e4695804b7 100644 --- a/common/build.mk +++ b/common/build.mk @@ -45,6 +45,7 @@ common-$(CONFIG_KEYBOARD_PROTOCOL_8042)+=keyboard_8042.o common-$(CONFIG_KEYBOARD_PROTOCOL_MKBP)+=keyboard_mkbp.o common-$(CONFIG_KEYBOARD_TEST)+=keyboard_test.o common-$(CONFIG_LED_COMMON)+=led_common.o +common-$(CONFIG_LID_ANGLE_KEY_SCAN)+=lid_angle.o common-$(CONFIG_LID_SWITCH)+=lid_switch.o common-$(CONFIG_LPC)+=port80.o common-$(CONFIG_ONEWIRE)+=onewire.o diff --git a/common/console_output.c b/common/console_output.c index b9db75c7fd..2b52da66cf 100644 --- a/common/console_output.c +++ b/common/console_output.c @@ -43,6 +43,7 @@ static const char * const channel_names[] = { "i2c", "keyboard", "keyscan", + "lidangle", "lightbar", "lpc", "motionsense", diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c index 8af50de209..da918bbc8b 100644 --- a/common/keyboard_scan.c +++ b/common/keyboard_scan.c @@ -93,7 +93,7 @@ static int enable_scanning = 1; /* Must init to 1 for scanning at boot */ /* Constantly incrementing counter of the number of times we polled */ static volatile int kbd_polls; -static int is_scanning_enabled(void) +int keyboard_scan_is_enabled(void) { #ifdef CONFIG_LID_SWITCH /* Scanning is never enabled when lid is closed */ @@ -201,7 +201,7 @@ static int read_matrix(uint8_t *state) for (c = 0; c < KEYBOARD_COLS; c++) { /* * Stop if scanning becomes disabled. Check enable_cscanning - * instead of is_scanning_enabled() so that we can scan the + * instead of keyboard_scan_is_enabled() so that we can scan the * matrix at boot time before the lid switch is readable. */ if (!enable_scanning) @@ -404,7 +404,7 @@ static int check_keys_changed(uint8_t *state) #ifdef CONFIG_KEYBOARD_PROTOCOL_8042 /* Inform keyboard module if scanning is enabled */ - if (is_scanning_enabled()) + if (keyboard_scan_is_enabled()) keyboard_state_changed(i, c, new_mask ? 1 : 0); #endif } @@ -567,7 +567,7 @@ void keyboard_scan_task(void) while (1) { /* Enable all outputs */ CPRINTF("[%T KB wait]\n"); - if (is_scanning_enabled()) + if (keyboard_scan_is_enabled()) keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); keyboard_raw_enable_interrupt(1); @@ -579,9 +579,10 @@ void keyboard_scan_task(void) * user pressing a key and enable_interrupt() * starting to pay attention to edges. */ - if (!keyboard_raw_read_rows() || !is_scanning_enabled()) + if (!keyboard_raw_read_rows() || + !keyboard_scan_is_enabled()) task_wait_event(-1); - } while (!is_scanning_enabled()); + } while (!keyboard_scan_is_enabled()); /* Enter polling mode */ CPRINTF("[%T KB poll]\n"); @@ -589,7 +590,7 @@ void keyboard_scan_task(void) keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); /* Busy polling keyboard state. */ - while (is_scanning_enabled()) { + while (keyboard_scan_is_enabled()) { start = get_time(); /* Check for keys down */ diff --git a/common/lid_angle.c b/common/lid_angle.c new file mode 100644 index 0000000000..e7a3139ebb --- /dev/null +++ b/common/lid_angle.c @@ -0,0 +1,81 @@ +/* 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. + */ + +/* Lid angle module for Chrome EC */ + +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "keyboard_scan.h" +#include "lid_angle.h" +#include "lid_switch.h" +#include "motion_sense.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_LIDANGLE, outstr) +#define CPRINTF(format, args...) cprintf(CC_LIDANGLE, format, ## args) + +/* + * Define the number of previous lid angle measurements to keep for determining + * whether to enable or disable key scanning. Note, that in order to change + * the state of key scanning, all stored measurements of the lid angle buffer + * must be in the specified range. + */ +#define KEY_SCAN_LID_ANGLE_BUFFER_SIZE 4 + +void lidangle_keyscan_update(float lid_ang) +{ + static float lidangle_buffer[KEY_SCAN_LID_ANGLE_BUFFER_SIZE]; + static int index; + + int i; + int keys_accept = 1, keys_ignore = 1; + + /* Record most recent lid angle in circular buffer. */ + lidangle_buffer[index] = lid_ang; + index = (index == KEY_SCAN_LID_ANGLE_BUFFER_SIZE-1) ? 0 : index+1; + +#ifdef CONFIG_LID_SWITCH + /* + * If lid is closed, don't need to check if keyboard scanning should + * be enabled. + */ + if (!lid_is_open()) + return; +#endif + + if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { + for (i = 0; i < KEY_SCAN_LID_ANGLE_BUFFER_SIZE; i++) { + /* + * If any lid angle samples are unreliable, then + * don't change keyboard scanning state. + */ + if (lidangle_buffer[i] == LID_ANGLE_UNRELIABLE) + return; + + /* + * Force all elements of the lid angle buffer to be + * in range of one of the conditions in order to change + * to the corresponding key scanning state. + */ + if (!LID_IN_RANGE_TO_ACCEPT_KEYS(lidangle_buffer[i])) + keys_accept = 0; + if (!LID_IN_RANGE_TO_IGNORE_KEYS(lidangle_buffer[i])) + keys_ignore = 0; + } + + /* Enable or disable keyboard scanning if necessary. */ + if (keys_accept && !keyboard_scan_is_enabled()) { + CPRINTF("[%T Enabling keyboard scan, lid ang at %d]\n", + (int)lidangle_buffer[index]); + keyboard_scan_enable(1); + } else if (keys_ignore && !keys_accept && + keyboard_scan_is_enabled()) { + CPRINTF("[%T Disabling keyboard scan, lid ang at %d]\n", + (int)lidangle_buffer[index]); + keyboard_scan_enable(0); + } + } +} diff --git a/common/motion_sense.c b/common/motion_sense.c index 651215ef34..5dad6db1cf 100644 --- a/common/motion_sense.c +++ b/common/motion_sense.c @@ -10,6 +10,7 @@ #include "console.h" #include "hooks.h" #include "host_command.h" +#include "lid_angle.h" #include "math_util.h" #include "motion_sense.h" #include "timer.h" @@ -27,11 +28,20 @@ static vector_3_t acc_lid_raw, acc_lid, acc_base; static vector_3_t acc_lid_host, acc_base_host; static float lid_angle_deg; +static int lid_angle_is_reliable; /* Accelerometer polling intervals based on chipset state. */ #define ACCEL_INTERVAL_AP_ON_MS 10 #define ACCEL_INTERVAL_AP_SUSPEND_MS 100 +/* + * Angle threshold for how close the hinge aligns with gravity before + * considering the lid angle calculation unreliable. For computational + * efficiency, value is given unit-less, so if you want the threshold to be + * at 15 degrees, the value would be cos(15 deg) = 0.96593. + */ +#define HINGE_ALIGNED_WITH_GRAVITY_THRESHOLD 0.96593F + /* Sampling interval for measuring acceleration and calculating lid angle. */ static int accel_interval_ms = ACCEL_INTERVAL_AP_SUSPEND_MS; @@ -50,12 +60,20 @@ const struct accel_orientation * const p_acc_orient = &acc_orient; /** * Calculate the lid angle using two acceleration vectors, one recorded in * the base and one in the lid. + * + * @param base Base accel vector + * @param lid Lid accel vector + * @param lid_angle Pointer to location to store lid angle result + * + * @return flag representing if resulting lid angle calculation is reliable. */ -static float calculate_lid_angle(vector_3_t base, vector_3_t lid) +static int calculate_lid_angle(vector_3_t base, vector_3_t lid, + float *lid_angle) { vector_3_t v; float ang_lid_to_base, ang_lid_90, ang_lid_270; float lid_to_base, base_to_hinge; + int reliable = 1; /* * The angle between lid and base is: @@ -66,11 +84,21 @@ static float calculate_lid_angle(vector_3_t base, vector_3_t lid) */ lid_to_base = cosine_of_angle_diff(base, lid); base_to_hinge = cosine_of_angle_diff(base, p_acc_orient->hinge_axis); + + /* + * If hinge aligns too closely with gravity, then result may be + * unreliable. + */ + if (ABS(base_to_hinge) > HINGE_ALIGNED_WITH_GRAVITY_THRESHOLD) + reliable = 0; + base_to_hinge = SQ(base_to_hinge); /* Check divide by 0. */ - if (ABS(1.0F - base_to_hinge) < 0.01F) - return 0.0; + if (ABS(1.0F - base_to_hinge) < 0.01F) { + *lid_angle = 0.0; + return 0; + } ang_lid_to_base = arc_cos( (lid_to_base - base_to_hinge) / (1 - base_to_hinge)); @@ -101,12 +129,16 @@ static float calculate_lid_angle(vector_3_t base, vector_3_t lid) if (ang_lid_270 > ang_lid_90) ang_lid_to_base = -ang_lid_to_base; - return ang_lid_to_base; + *lid_angle = ang_lid_to_base; + return reliable; } int motion_get_lid_angle(void) { - return (int)lid_angle_deg; + if (lid_angle_is_reliable) + return (int)lid_angle_deg; + else + return (int)LID_ANGLE_UNRELIABLE; } #ifdef CONFIG_ACCEL_CALIBRATE @@ -178,7 +210,8 @@ void motion_sense_task(void) rotate(acc_lid_raw, &p_acc_orient->rot_align, &acc_lid); /* Calculate angle of lid. */ - lid_angle_deg = calculate_lid_angle(acc_base, acc_lid); + lid_angle_is_reliable = calculate_lid_angle(acc_base, acc_lid, + &lid_angle_deg); /* TODO(crosbug.com/p/25597): Add filter to smooth lid angle. */ @@ -201,7 +234,7 @@ void motion_sense_task(void) * Copy sensor data to shared memory. Note that this code * assumes little endian, which is what the host expects. */ - lpc_data[0] = (int)lid_angle_deg; + lpc_data[0] = motion_get_lid_angle(); lpc_data[1] = acc_base_host[X]; lpc_data[2] = acc_base_host[Y]; lpc_data[3] = acc_base_host[Z]; @@ -217,14 +250,18 @@ void motion_sense_task(void) EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; *lpc_status = EC_MEMMAP_ACC_STATUS_PRESENCE_BIT | sample_id; +#ifdef CONFIG_LID_ANGLE_KEY_SCAN + lidangle_keyscan_update(motion_get_lid_angle()); +#endif #ifdef CONFIG_CMD_LID_ANGLE if (accel_disp) { CPRINTF("[%T ACC base=%-5d, %-5d, %-5d lid=%-5d, " - "%-5d, %-5d a=%-6.1d]\n", + "%-5d, %-5d a=%-6.1d r=%d]\n", acc_base[X], acc_base[Y], acc_base[Z], acc_lid[X], acc_lid[Y], acc_lid[Z], - (int)(10*lid_angle_deg)); + (int)(10*lid_angle_deg), + lid_angle_is_reliable); } #endif diff --git a/include/config.h b/include/config.h index aed9085202..048033d793 100644 --- a/include/config.h +++ b/include/config.h @@ -533,6 +533,17 @@ #undef CONFIG_LED_DRIVER_DS2413 /* Maxim DS2413, on one-wire interface */ #undef CONFIG_LED_DRIVER_LP5562 /* LP5562, on I2C interface */ +/* + * Allows using the lid angle measurement to determine if key scanning should + * be enabled or disabled when chipset is suspended. + * + * Any board that defines this must also define two macros: + * LID_IN_RANGE_TO_ACCEPT_KEYS(angle), LID_IN_RANGE_TO_IGNORE_KEYS(angle). + * These macros should return true if the given angle argument is in range + * to accept or ignore key presses. + */ +#undef CONFIG_LID_ANGLE_KEY_SCAN + /* * Compile lid switch support. * diff --git a/include/console.h b/include/console.h index 8106e54f56..a6cce1fa8c 100644 --- a/include/console.h +++ b/include/console.h @@ -39,6 +39,7 @@ enum console_channel { CC_I2C, CC_KEYBOARD, CC_KEYSCAN, + CC_LIDANGLE, CC_LIGHTBAR, CC_LPC, CC_MOTION_SENSE, diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h index bc956cc685..8359cbd174 100644 --- a/include/keyboard_scan.h +++ b/include/keyboard_scan.h @@ -84,6 +84,11 @@ void keyboard_scan_enable(int enable); static inline void keyboard_scan_enable(int enable) { } #endif +/** + * Returns if keyboard matrix scanning is enabled/disabled. + */ +int keyboard_scan_is_enabled(void); + #ifdef CONFIG_KEYBOARD_SUPPRESS_NOISE /** * Indicate to audio codec that a key has been pressed. diff --git a/include/lid_angle.h b/include/lid_angle.h new file mode 100644 index 0000000000..bd60b90907 --- /dev/null +++ b/include/lid_angle.h @@ -0,0 +1,20 @@ +/* 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. + */ + +/* Lid angle module for Chrome EC */ + +#ifndef __CROS_EC_LID_ANGLE_H +#define __CROS_EC_LID_ANGLE_H + +/** + * Update the lid angle module with the most recent lid angle calculation. Then + * use the lid angle history to enable/disable keyboard scanning when chipset + * is suspended. + * + * @lid_ang Lid angle. + */ +void lidangle_keyscan_update(float lid_ang); + +#endif /* __CROS_EC_LID_ANGLE_H */ diff --git a/include/motion_sense.h b/include/motion_sense.h index 08aefbcc0b..d0c2774254 100644 --- a/include/motion_sense.h +++ b/include/motion_sense.h @@ -11,6 +11,9 @@ #include "gpio.h" #include "math_util.h" +/* Anything outside of lid angle range [-180, 180] should work. */ +#define LID_ANGLE_UNRELIABLE 500.0F + /** * This structure defines all of the data needed to specify the orientation * of the base and lid accelerometers in order to calculate the lid angle. diff --git a/test/motion_sense.c b/test/motion_sense.c index 1a5d808044..c332f962c5 100644 --- a/test/motion_sense.c +++ b/test/motion_sense.c @@ -70,15 +70,15 @@ static int test_lid_angle(void) TEST_ASSERT(motion_get_lid_angle() == -135); /* - * Align base with hinge and make sure it returns 0 for angle. In this - * test it doesn't matter what the lid acceleration vector is. + * Align base with hinge and make sure it returns unreliable for angle. + * In this test it doesn't matter what the lid acceleration vector is. */ mock_x_acc[ACCEL_BASE] = 0; mock_y_acc[ACCEL_BASE] = 1000; mock_z_acc[ACCEL_BASE] = 0; task_wake(TASK_ID_MOTIONSENSE); msleep(5); - TEST_ASSERT(motion_get_lid_angle() == 0); + TEST_ASSERT(motion_get_lid_angle() == LID_ANGLE_UNRELIABLE); /* * Use all three axes and set lid to negative base and make sure -- cgit v1.2.1