blob: fe2d9e86d323e643d613fd309f39c8767500dd52 [file] [log] [blame]
/* Copyright 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.
*/
/* Motion sense module to read from various motion sensors. */
#include "accelgyro.h"
#include "atomic.h"
#include "chipset.h"
#include "common.h"
#include "console.h"
#include "gesture.h"
#include "hooks.h"
#include "host_command.h"
#include "hwtimer.h"
#include "lid_angle.h"
#include "lightbar.h"
#include "math_util.h"
#include "mkbp_event.h"
#include "motion_sense.h"
#include "motion_lid.h"
#include "panic.h"
#include "power.h"
#include "queue.h"
#include "tablet_mode.h"
#include "timer.h"
#include "task.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_MOTION_SENSE, outstr)
#define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_MOTION_SENSE, format, ## args)
#ifdef CONFIG_ORIENTATION_SENSOR
/*
* Orientation mode vectors, must match sequential ordering of
* known orientations from enum motionsensor_orientation
*/
const intv3_t orientation_modes[] = {
[MOTIONSENSE_ORIENTATION_LANDSCAPE] = { 0, -1, 0 },
[MOTIONSENSE_ORIENTATION_PORTRAIT] = { 1, 0, 0 },
[MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT] = { -1, 0, 0 },
[MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE] = { 0, 1, 0 },
};
#endif
/* Delay between FIFO interruption. */
static unsigned int ap_event_interval;
/* Minimum time in between running motion sense task loop. */
unsigned int motion_min_interval = CONFIG_MOTION_MIN_SENSE_WAIT_TIME * MSEC;
#ifdef CONFIG_CMD_ACCEL_INFO
static int accel_disp;
#endif
#define SENSOR_ACTIVE(_sensor) (sensor_active & (_sensor)->active_mask)
/*
* Adjustment in us to ec rate when calculating interrupt interval:
* To be sure the EC will send an interrupt even if it finishes processing
* events slightly earlier than the previous period.
*/
#define MOTION_SENSOR_INT_ADJUSTMENT_US 10
/*
* Mutex to protect sensor values between host command task and
* motion sense task:
* When we process CMD_DUMP, we want to be sure the motion sense
* task is not updating the sensor values at the same time.
*/
static struct mutex g_sensor_mutex;
/*
* Current power level (S0, S3, S5, ...)
*/
test_export_static enum chipset_state_mask sensor_active;
#ifdef CONFIG_ACCEL_SPOOF_MODE
static void print_spoof_mode_status(int id);
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
/* Flags to control whether to send an ODR change event for a sensor */
static uint32_t odr_event_required;
#ifdef CONFIG_ACCEL_FIFO
static inline int is_timestamp(struct ec_response_motion_sensor_data *data)
{
return data->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP;
}
/* Need to wake up the AP */
static int wake_up_needed;
/* Number of element the AP should collect */
static int fifo_queue_count;
static int fifo_int_enabled;
struct queue motion_sense_fifo = QUEUE_NULL(CONFIG_ACCEL_FIFO,
struct ec_response_motion_sensor_data);
static int motion_sense_fifo_lost;
/**
* Staged metadata for the motion_sense_fifo.
* @read_ts: The timestamp at which the staged data was read. This value will
* serve as the upper bound for spreading
* @count: The total number of motion_sense_fifo entries that are currently
* staged.
* @sample_count: The total number of sensor readings per sensor that are
* currently staged.
* @requires_spreading: Flag used to shortcut the commit process. This should be
* true iff at least one of sample_count[] > 1
*/
struct fifo_staged {
uint32_t read_ts;
uint16_t count;
uint8_t sample_count[SENSOR_COUNT];
uint8_t requires_spreading;
};
static struct fifo_staged fifo_staged;
static inline struct ec_response_motion_sensor_data *
get_motion_sense_fifo_head(void)
{
return ((struct ec_response_motion_sensor_data *)
motion_sense_fifo.buffer) +
(motion_sense_fifo.state->head &
motion_sense_fifo.unit_bytes);
}
/**
* Pop one entry from the motion sense fifo. Poping will give priority to
* committed data (data residing between the head and tail of the queue). If no
* committed data is available (all the data is staged), then this function will
* remove the oldest staged data by moving both the head and tail.
*
* As a side-effect of this function, it'll updated any appropriate lost and
* count variables.
*
* WARNING: This function MUST be called from within a locked context of
* g_sensor_mutex.
*/
static void motion_sense_fifo_pop(void)
{
struct ec_response_motion_sensor_data *head =
get_motion_sense_fifo_head();
const size_t initial_count = queue_count(&motion_sense_fifo);
/* Check that we have something to pop. */
if (!initial_count && !fifo_staged.count)
return;
/*
* If all the data is staged (nothing in the committed queue), we'll
* need to move the head and the tail over to simulate poping from the
* staged data.
*/
if (!initial_count)
queue_advance_tail(&motion_sense_fifo, 1);
/*
* By not using queue_remove_unit we're avoiding an un-necessary memcpy.
*/
queue_advance_head(&motion_sense_fifo, 1);
motion_sense_fifo_lost++;
/* Increment lost counter if we have valid data. */
if (!is_timestamp(head))
motion_sensors[head->sensor_num].lost++;
/*
* We're done if the initial count was non-zero and we only advanced the
* head. Else, decrement the staged count and update staged metadata.
*/
if (initial_count)
return;
fifo_staged.count--;
/* If we removed a timestamp there's nothing else for us to do. */
if (is_timestamp(head))
return;
/*
* Decrement sample count, if the count was 2 before, we might not need
* to spread anymore. Loop through and check.
*/
if (--fifo_staged.sample_count[head->sensor_num] < 2) {
int i;
fifo_staged.requires_spreading = 0;
for (i = 0; i < SENSOR_COUNT; i++)
if (fifo_staged.sample_count[i] > 1) {
fifo_staged.requires_spreading = 1;
break;
}
}
}
static void motion_sense_fifo_ensure_space(void)
{
/* If we already have space just bail. */
if (queue_space(&motion_sense_fifo) > fifo_staged.count)
return;
/*
* Pop at least 1 spot, but if all the following conditions are met we
* will continue to pop:
* 1. We're operating with tight timestamps.
* 2. The new head isn't a timestamp.
* 3. We have data that we can possibly pop.
*
* Removing more than one entry is needed because if we are using tight
* timestamps and we pop a timestamp, then the next head is data, the AP
* would assign a bad timestamp to it.
*/
do {
motion_sense_fifo_pop();
} while (IS_ENABLED(CONFIG_SENSOR_TIGHT_TIMESTAMPS) &&
!is_timestamp(get_motion_sense_fifo_head()) &&
queue_count(&motion_sense_fifo) + fifo_staged.count);
}
/*
* Do not use this function directly if you just want to add sensor data, use
* motion_sense_fifo_stage_data instead to get a proper timestamp too.
*/
static void motion_sense_fifo_stage_unit(
struct ec_response_motion_sensor_data *data,
struct motion_sensor_t *sensor,
int valid_data)
{
struct queue_chunk chunk;
int i;
mutex_lock(&g_sensor_mutex);
for (i = 0; i < valid_data; i++)
sensor->xyz[i] = data->data[i];
/* For valid sensors, check if AP really needs this data */
if (valid_data) {
int removed;
if (sensor->oversampling_ratio == 0) {
mutex_unlock(&g_sensor_mutex);
return;
}
removed = sensor->oversampling++;
sensor->oversampling %= sensor->oversampling_ratio;
if (removed != 0) {
mutex_unlock(&g_sensor_mutex);
return;
}
}
/* Make sure we have room for the data */
motion_sense_fifo_ensure_space();
mutex_unlock(&g_sensor_mutex);
if (data->flags & MOTIONSENSE_SENSOR_FLAG_WAKEUP)
wake_up_needed = 1;
#ifdef CONFIG_TABLET_MODE
data->flags |= (tablet_get_mode() ?
MOTIONSENSE_SENSOR_FLAG_TABLET_MODE : 0);
#endif
/*
* Get the next writable block in the fifo. We don't need to lock this
* because it will always be past the tail and thus the AP will never
* read this until motion_sense_fifo_commit_data() is called.
*/
chunk = queue_get_write_chunk(
&motion_sense_fifo, fifo_staged.count);
if (!chunk.buffer)
panic("Failed to get write chunk for new fifo data");
/*
* Save the data to the writable block and increment count. This data
* will now reside AFTER the tail of the queue and will not be visible
* to the AP until the motion_sense_fifo_commit_data() function is
* called. Because count is incremented, the following staged data will
* be written to the next available block and this one will remain
* staged.
*/
memcpy(chunk.buffer, data, motion_sense_fifo.unit_bytes);
fifo_staged.count++;
/*
* If we're using tight timestamps, and the current entry isn't a
* timestamp we'll increment the sample_count for the given sensor.
* If the new per-sensor sample count is greater than 1, we'll need to
* spread.
*/
if (IS_ENABLED(CONFIG_SENSOR_TIGHT_TIMESTAMPS) &&
!is_timestamp(data) &&
++fifo_staged.sample_count[data->sensor_num] > 1) {
fifo_staged.requires_spreading = 1;
}
}
enum motion_sense_async_event {
ASYNC_EVENT_FLUSH = MOTIONSENSE_SENSOR_FLAG_FLUSH |
MOTIONSENSE_SENSOR_FLAG_TIMESTAMP,
ASYNC_EVENT_ODR = MOTIONSENSE_SENSOR_FLAG_ODR |
MOTIONSENSE_SENSOR_FLAG_TIMESTAMP,
};
static void motion_sense_insert_async_event(struct motion_sensor_t *sensor,
enum motion_sense_async_event evt)
{
struct ec_response_motion_sensor_data vector;
vector.flags = evt;
vector.timestamp = __hw_clock_source_read();
vector.sensor_num = sensor - motion_sensors;
motion_sense_fifo_stage_unit(&vector, sensor, 0);
motion_sense_fifo_commit_data();
}
static void motion_sense_fifo_stage_timestamp(uint32_t timestamp)
{
struct ec_response_motion_sensor_data vector;
vector.flags = MOTIONSENSE_SENSOR_FLAG_TIMESTAMP;
vector.timestamp = timestamp;
vector.sensor_num = 0;
motion_sense_fifo_stage_unit(&vector, NULL, 0);
}
void motion_sense_fifo_stage_data(struct ec_response_motion_sensor_data *data,
struct motion_sensor_t *sensor,
int valid_data,
uint32_t time)
{
if (IS_ENABLED(CONFIG_SENSOR_TIGHT_TIMESTAMPS)) {
/* First entry, save the time for spreading later. */
if (!fifo_staged.count)
fifo_staged.read_ts = __hw_clock_source_read();
motion_sense_fifo_stage_timestamp(time);
}
motion_sense_fifo_stage_unit(data, sensor, valid_data);
}
/**
* Peek into the staged data at a given offset. This function performs no bound
* checking and is purely for convenience.
*/
static inline struct ec_response_motion_sensor_data *
motion_sense_peek_fifo_staged(size_t offset)
{
return (struct ec_response_motion_sensor_data *)
queue_get_write_chunk(&motion_sense_fifo, offset).buffer;
}
void motion_sense_fifo_commit_data(void)
{
/*
* Static data to use off stack. Note that next_timestamp should persist
* and is only updated if the timestamp from the sensor is greater.
*/
static uint32_t data_periods[SENSOR_COUNT];
static uint32_t next_timestamp[SENSOR_COUNT];
struct ec_response_motion_sensor_data *data;
int i, window, sensor_num = 0;
/* Nothing staged, no work to do. */
if (!fifo_staged.count)
return;
/*
* If per-sensor event counts are never more than 1, no spreading is
* needed. This will also catch cases where tight timestamps aren't
* used.
*/
if (!fifo_staged.requires_spreading)
goto flush_data_end;
data = motion_sense_peek_fifo_staged(0);
/*
* Spreading only makes sense if tight timestamps are used. In such case
* entries are expected to be ordered: timestamp then data. If the first
* entry isn't a timestamp we must have gotten out of sync. Just commit
* all the data and skip the spreading.
*/
if (!is_timestamp(data)) {
CPRINTS("Spreading skipped, first entry is not a timestamp");
goto flush_data_end;
}
window = time_until(data->timestamp, fifo_staged.read_ts);
/* Update the data_periods as needed for this flush. */
for (i = 0; i < SENSOR_COUNT; i++) {
int period;
/* Skip empty sensors. */
if (!fifo_staged.sample_count[i])
continue;
period = motion_sensors[i].collection_rate;
/*
* Clamp the sample period to the MIN of collection_rate and the
* window length / sample counts.
*/
if (window)
period = MIN(period,
window / fifo_staged.sample_count[i]);
data_periods[i] = period;
}
/*
* Spread the timestamps.
*
* If we got this far that means that the tight timestamps config is
* enabled. This means that we can expect the staged entries to have 1
* or more timestamps followed by exactly 1 data entry. We'll loop
* through the timestamps until we get to data. We only need to update
* the timestamp right before it to keep things correct.
*/
for (i = 0; i < fifo_staged.count; i++) {
data = motion_sense_peek_fifo_staged(i);
/* Skip timestamp, we don't know the sensor number yet. */
if (is_timestamp(data))
continue;
/* Get the sensor number and point to the timestamp entry. */
sensor_num = data->sensor_num;
data = motion_sense_peek_fifo_staged(i - 1);
/* If the timestamp is after our computed next, skip ahead. */
if (time_after(data->timestamp, next_timestamp[sensor_num]))
next_timestamp[sensor_num] = data->timestamp;
/* Spread the timestamp and compute the expected next. */
data->timestamp = next_timestamp[sensor_num];
next_timestamp[sensor_num] += data_periods[sensor_num];
}
flush_data_end:
/* Advance the tail and clear the staged metadata. */
mutex_lock(&g_sensor_mutex);
queue_advance_tail(&motion_sense_fifo, fifo_staged.count);
mutex_unlock(&g_sensor_mutex);
/* Reset metadata for next staging cycle. */
memset(&fifo_staged, 0, sizeof(fifo_staged));
}
static void motion_sense_get_fifo_info(
struct ec_response_motion_sense_fifo_info *fifo_info)
{
fifo_info->size = motion_sense_fifo.buffer_units;
mutex_lock(&g_sensor_mutex);
fifo_info->count = fifo_queue_count;
fifo_info->total_lost = motion_sense_fifo_lost;
mutex_unlock(&g_sensor_mutex);
fifo_info->timestamp = mkbp_last_event_time;
}
#endif
static inline int motion_sensor_in_forced_mode(
const struct motion_sensor_t *sensor)
{
#ifdef CONFIG_ACCEL_FORCE_MODE_MASK
/* Sensor not in force mode, its irq_handler is getting data. */
if (!(CONFIG_ACCEL_FORCE_MODE_MASK & (1 << (sensor - motion_sensors))))
return 0;
else
return 1;
#else
return 0;
#endif
}
/* Minimal amount of time since last collection before triggering a new one */
static inline int motion_sensor_time_to_read(const timestamp_t *ts,
const struct motion_sensor_t *sensor)
{
if (sensor->collection_rate == 0)
return 0;
/*
* If the time is within the min motion interval (3 ms) go ahead and
* read from the sensor
*/
return time_after(ts->le.lo,
sensor->next_collection - motion_min_interval);
}
static enum sensor_config motion_sense_get_ec_config(void)
{
switch (sensor_active) {
case SENSOR_ACTIVE_S0:
return SENSOR_CONFIG_EC_S0;
case SENSOR_ACTIVE_S3:
return SENSOR_CONFIG_EC_S3;
case SENSOR_ACTIVE_S5:
return SENSOR_CONFIG_EC_S5;
default:
CPRINTS("get_ec_config: Invalid active state: %x",
sensor_active);
return SENSOR_CONFIG_MAX;
}
}
/* motion_sense_set_data_rate
*
* Set the sensor data rate. It is altered when the AP change the data
* rate or when the power state changes.
*/
int motion_sense_set_data_rate(struct motion_sensor_t *sensor)
{
int roundup, ap_odr_mhz = 0, ec_odr_mhz, odr, ret;
enum sensor_config config_id;
timestamp_t ts = get_time();
/* We assume the sensor is initialized */
/* Check the AP setting first. */
if (sensor_active != SENSOR_ACTIVE_S5)
ap_odr_mhz = BASE_ODR(sensor->config[SENSOR_CONFIG_AP].odr);
/* check if the EC set the sensor ODR at a higher frequency */
config_id = motion_sense_get_ec_config();
ec_odr_mhz = BASE_ODR(sensor->config[config_id].odr);
if (ec_odr_mhz > ap_odr_mhz) {
odr = ec_odr_mhz;
} else {
odr = ap_odr_mhz;
config_id = SENSOR_CONFIG_AP;
}
roundup = !!(sensor->config[config_id].odr & ROUND_UP_FLAG);
ret = sensor->drv->set_data_rate(sensor, odr, roundup);
if (ret)
return ret;
#ifdef CONFIG_CONSOLE_VERBOSE
CPRINTS("%s ODR: %d - roundup %d from config %d [AP %d]",
sensor->name, odr, roundup, config_id,
BASE_ODR(sensor->config[SENSOR_CONFIG_AP].odr));
#else
CPRINTS("%c%d ODR %d rup %d cfg %d AP %d",
sensor->name[0], sensor->type, odr, roundup, config_id,
BASE_ODR(sensor->config[SENSOR_CONFIG_AP].odr));
#endif
mutex_lock(&g_sensor_mutex);
if (ap_odr_mhz)
/*
* In case the AP want to run the sensors faster than it can,
* be sure we don't see the ratio to 0.
*/
sensor->oversampling_ratio = MAX(1,
sensor->drv->get_data_rate(sensor) / ap_odr_mhz);
else
sensor->oversampling_ratio = 0;
/*
* Reset last collection: the last collection may be so much in the past
* it may appear to be in the future.
*/
odr = sensor->drv->get_data_rate(sensor);
sensor->collection_rate = odr > 0 ? SECOND * 1000 / odr : 0;
sensor->next_collection = ts.le.lo + sensor->collection_rate;
sensor->oversampling = 0;
mutex_unlock(&g_sensor_mutex);
return 0;
}
static int motion_sense_set_ec_rate_from_ap(
const struct motion_sensor_t *sensor,
unsigned int new_rate_us)
{
int odr_mhz = sensor->drv->get_data_rate(sensor);
if (new_rate_us == 0)
return 0;
if (motion_sensor_in_forced_mode(sensor))
/*
* AP EC sampling rate does not matter: we will collect at the
* requested sensor frequency.
*/
goto end_set_ec_rate_from_ap;
if (odr_mhz == 0)
goto end_set_ec_rate_from_ap;
/*
* If the EC collection rate is close to the sensor data rate,
* given variation from the EC scheduler, it is possible that a sensor
* will not present any measurement for a given time slice, and then 2
* measurement for the next. That will create a large interval between
* 2 measurements.
* To prevent that, increase the EC period by 5% to be sure to get at
* least one measurement at every collection time.
* We will apply that correction only if the ec rate is within 10% of
* the data rate.
*/
if (SECOND * 1100 / odr_mhz > new_rate_us)
new_rate_us = new_rate_us / 100 * 105;
end_set_ec_rate_from_ap:
return MAX(new_rate_us, motion_min_interval);
}
/*
* motion_sense_select_ec_rate
*
* Calculate the ec_rate for a given sensor.
* - sensor: sensor to use
* - config_id: determine the requester (AP or EC).
* - interrupt:
* If interrupt is set: return the sampling rate requested by AP or EC.
* If interrupt is not set and the sensor is in forced mode,
* we return the rate needed to probe the sensor at the right ODR.
* otherwise return the sampling rate requested by AP or EC.
*
* return rate in us.
*/
static int motion_sense_select_ec_rate(
const struct motion_sensor_t *sensor,
enum sensor_config config_id,
int interrupt)
{
if (interrupt == 0 && motion_sensor_in_forced_mode(sensor)) {
int rate_mhz = BASE_ODR(sensor->config[config_id].odr);
/* we have to run ec at the sensor frequency rate.*/
if (rate_mhz > 0)
return SECOND * 1000 / rate_mhz;
else
return 0;
} else {
return sensor->config[config_id].ec_rate;
}
}
/* motion_sense_ec_rate
*
* Calculate the sensor ec rate. It will be use to set the motion task polling
* rate.
*
* Return the EC rate, in us.
*/
static int motion_sense_ec_rate(struct motion_sensor_t *sensor)
{
int ec_rate = 0, ec_rate_from_cfg;
/* Check the AP setting first. */
if (sensor_active != SENSOR_ACTIVE_S5)
ec_rate = motion_sense_select_ec_rate(
sensor, SENSOR_CONFIG_AP, 0);
ec_rate_from_cfg = motion_sense_select_ec_rate(
sensor, motion_sense_get_ec_config(), 0);
if (ec_rate_from_cfg != 0)
if (ec_rate == 0 || ec_rate_from_cfg < ec_rate)
ec_rate = ec_rate_from_cfg;
return ec_rate;
}
/*
* motion_sense_set_motion_intervals
*
* Set the wake up interval for the motion sense thread.
* It is set to the highest frequency one of the sensors need to be polled at.
*
* Note: Not static to be tested.
*/
static void motion_sense_set_motion_intervals(void)
{
int i, sensor_ec_rate, ec_int_rate = 0;
struct motion_sensor_t *sensor;
for (i = 0; i < motion_sensor_count; ++i) {
sensor = &motion_sensors[i];
/*
* If the sensor is sleeping, no need to check it periodically.
*/
if ((sensor->state != SENSOR_INITIALIZED) ||
(sensor->drv->get_data_rate(sensor) == 0))
continue;
sensor_ec_rate = motion_sense_select_ec_rate(
sensor, SENSOR_CONFIG_AP, 1);
if (ec_int_rate == 0 ||
(sensor_ec_rate && sensor_ec_rate < ec_int_rate))
ec_int_rate = sensor_ec_rate;
}
ap_event_interval =
MAX(0, ec_int_rate - MOTION_SENSOR_INT_ADJUSTMENT_US);
/*
* Wake up the motion sense task: we want to sensor task to take
* in account the new period right away.
*/
task_wake(TASK_ID_MOTIONSENSE);
}
static inline int motion_sense_init(struct motion_sensor_t *sensor)
{
int ret, cnt = 3;
/* Initialize accelerometers. */
do {
ret = sensor->drv->init(sensor);
} while ((ret != EC_SUCCESS) && (--cnt > 0));
if (ret != EC_SUCCESS) {
sensor->state = SENSOR_INIT_ERROR;
} else {
sensor->state = SENSOR_INITIALIZED;
motion_sense_set_data_rate(sensor);
}
return ret;
}
/*
* sensor_init_done
*
* Called by init routine of each sensors when successful.
*/
int sensor_init_done(const struct motion_sensor_t *s)
{
int ret;
ret = s->drv->set_range(s, BASE_RANGE(s->default_range),
!!(s->default_range & ROUND_UP_FLAG));
if (ret == EC_RES_SUCCESS) {
#ifdef CONFIG_CONSOLE_VERBOSE
CPRINTS("%s: MS Done Init type:0x%X range:%d",
s->name, s->type, s->drv->get_range(s));
#else
CPRINTS("%c%d InitDone r:%d", s->name[0], s->type,
s->drv->get_range(s));
#endif
}
return ret;
}
/*
* motion_sense_switch_sensor_rate
*
* Suspend all sensors that are not needed.
* Mark them as uninitialized, they will lose power and
* need to be initialized again.
*/
static void motion_sense_switch_sensor_rate(void)
{
int i, ret;
struct motion_sensor_t *sensor;
for (i = 0; i < motion_sensor_count; ++i) {
sensor = &motion_sensors[i];
if (SENSOR_ACTIVE(sensor)) {
/* Initialize or just back the odr previously set. */
if (sensor->state == SENSOR_INITIALIZED) {
motion_sense_set_data_rate(sensor);
} else {
ret = motion_sense_init(sensor);
if (ret != EC_SUCCESS) {
CPRINTS("%s: %d: init failed: %d",
sensor->name, i, ret);
#if defined(CONFIG_TABLET_MODE) && defined(CONFIG_LID_ANGLE)
/*
* No tablet mode allowed if an accel
* is not working.
*/
if (i == CONFIG_LID_ANGLE_SENSOR_BASE ||
i == CONFIG_LID_ANGLE_SENSOR_LID) {
tablet_set_mode(0);
}
#endif
}
}
} else {
/* The sensors are being powered off */
if (sensor->state == SENSOR_INITIALIZED)
sensor->state = SENSOR_NOT_INITIALIZED;
}
}
motion_sense_set_motion_intervals();
}
DECLARE_DEFERRED(motion_sense_switch_sensor_rate);
static void motion_sense_shutdown(void)
{
int i;
struct motion_sensor_t *sensor;
#ifdef CONFIG_GESTURE_DETECTION_MASK
uint32_t enabled = 0, disabled, mask;
#endif
sensor_active = SENSOR_ACTIVE_S5;
for (i = 0; i < motion_sensor_count; i++) {
sensor = &motion_sensors[i];
/* Forget about changes made by the AP */
sensor->config[SENSOR_CONFIG_AP].odr = 0;
sensor->config[SENSOR_CONFIG_AP].ec_rate = 0;
}
motion_sense_switch_sensor_rate();
/* Forget activities set by the AP */
#ifdef CONFIG_GESTURE_DETECTION_MASK
mask = CONFIG_GESTURE_DETECTION_MASK;
while (mask) {
i = get_next_bit(&mask);
sensor = &motion_sensors[i];
if (sensor->state != SENSOR_INITIALIZED)
continue;
sensor->drv->list_activities(sensor,
&enabled, &disabled);
/* exclude double tap, it is used internally. */
enabled &= ~BIT(MOTIONSENSE_ACTIVITY_DOUBLE_TAP);
while (enabled) {
int activity = get_next_bit(&enabled);
sensor->drv->manage_activity(sensor, activity, 0, NULL);
}
/* Re-enable double tap in case AP disabled it */
sensor->drv->manage_activity(sensor,
MOTIONSENSE_ACTIVITY_DOUBLE_TAP, 1, NULL);
}
#endif
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, motion_sense_shutdown,
MOTION_SENSE_HOOK_PRIO);
static void motion_sense_suspend(void)
{
/*
* If we are coming from S5, don't enter suspend:
* We will go in SO almost immediately.
*/
if (sensor_active == SENSOR_ACTIVE_S5)
return;
sensor_active = SENSOR_ACTIVE_S3;
/*
* During shutdown sequence sensor rails can be powered down
* asynchronously to the EC hence EC cannot interlock the sensor
* states with the power down states. To avoid this issue, defer
* switching the sensors rate with a configurable delay if in S3.
* By the time deferred function is serviced, if the chipset is
* in S5 we can back out from switching the sensor rate.
*
* TODO: This does not fix the issue completely. It is mitigating
* some of the accesses when we're going from S0->S5 with a very
* brief stop in S3.
*/
hook_call_deferred(&motion_sense_switch_sensor_rate_data,
CONFIG_MOTION_SENSE_SUSPEND_DELAY_US);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, motion_sense_suspend,
MOTION_SENSE_HOOK_PRIO);
static void motion_sense_resume(void)
{
sensor_active = SENSOR_ACTIVE_S0;
hook_call_deferred(&motion_sense_switch_sensor_rate_data,
CONFIG_MOTION_SENSE_RESUME_DELAY_US);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, motion_sense_resume,
MOTION_SENSE_HOOK_PRIO);
static void motion_sense_startup(void)
{
/*
* If the AP is already in S0, call the resume hook now.
* We may initialize the sensor 2 times (once in RO, another time in
* RW), but it may be necessary if the init sequence has changed.
*/
if (chipset_in_state(SENSOR_ACTIVE_S0_S3_S5))
motion_sense_shutdown();
if (chipset_in_state(SENSOR_ACTIVE_S0_S3))
motion_sense_suspend();
if (chipset_in_state(SENSOR_ACTIVE_S0))
motion_sense_resume();
}
DECLARE_HOOK(HOOK_INIT, motion_sense_startup,
MOTION_SENSE_HOOK_PRIO);
/* Write to LPC status byte to represent that accelerometers are present. */
static inline void set_present(uint8_t *lpc_status)
{
*lpc_status |= EC_MEMMAP_ACC_STATUS_PRESENCE_BIT;
}
#ifdef CONFIG_MOTION_FILL_LPC_SENSE_DATA
/* Update/Write LPC data */
static inline void update_sense_data(uint8_t *lpc_status, int *psample_id)
{
int s, d, i;
uint16_t *lpc_data = (uint16_t *)host_get_memmap(EC_MEMMAP_ACC_DATA);
#if (!defined HAS_TASK_ALS) && (defined CONFIG_ALS)
uint16_t *lpc_als = (uint16_t *)host_get_memmap(EC_MEMMAP_ALS);
#endif
struct motion_sensor_t *sensor;
/*
* Set the busy bit before writing the sensor data. Increment
* the counter and clear the busy bit after writing the sensor
* data. On the host side, the host needs to make sure the busy
* bit is not set and that the counter remains the same before
* and after reading the data.
*/
*lpc_status |= EC_MEMMAP_ACC_STATUS_BUSY_BIT;
/*
* Copy sensor data to shared memory. Note that this code
* assumes little endian, which is what the host expects. Also,
* note that we share the lid angle calculation with host only
* for debugging purposes. The EC lid angle is an approximation
* with uncalibrated accelerometers. The AP calculates a separate,
* more accurate lid angle.
*/
#ifdef CONFIG_LID_ANGLE
lpc_data[0] = motion_lid_get_angle();
#else
lpc_data[0] = LID_ANGLE_UNRELIABLE;
#endif
/*
* The first 2 entries must be accelerometers, then gyroscope.
* If there is only one accel and one gyro, the entry for the second
* accel is skipped.
*/
for (s = 0, d = 0; d < 3 && s < motion_sensor_count; s++, d++) {
sensor = &motion_sensors[s];
if (sensor->type > MOTIONSENSE_TYPE_GYRO)
break;
else if (sensor->type == MOTIONSENSE_TYPE_GYRO)
d = 2;
for (i = X; i <= Z; i++)
lpc_data[1 + i + 3 * d] = sensor->xyz[i];
}
#if (!defined HAS_TASK_ALS) && (defined CONFIG_ALS)
for (i = 0; i < EC_ALS_ENTRIES && i < ALS_COUNT; i++)
lpc_als[i] = motion_als_sensors[i]->xyz[X];
#endif
/*
* Increment sample id and clear busy bit to signal we finished
* updating data.
*/
*psample_id = (*psample_id + 1) &
EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
*lpc_status = EC_MEMMAP_ACC_STATUS_PRESENCE_BIT | *psample_id;
}
#endif
static int motion_sense_read(struct motion_sensor_t *sensor)
{
if (sensor->state != SENSOR_INITIALIZED)
return EC_ERROR_UNKNOWN;
if (sensor->drv->get_data_rate(sensor) == 0)
return EC_ERROR_NOT_POWERED;
#ifdef CONFIG_ACCEL_SPOOF_MODE
/*
* If the sensor is in spoof mode, the readings are already present in
* spoof_xyz.
*/
if (sensor->flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE)
return EC_SUCCESS;
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
/* Otherwise, read all raw X,Y,Z accelerations. */
return sensor->drv->read(sensor, sensor->raw_xyz);
}
static inline void increment_sensor_collection(struct motion_sensor_t *sensor,
const timestamp_t *ts)
{
sensor->next_collection += sensor->collection_rate;
if (time_after(ts->le.lo, sensor->next_collection)) {
/*
* If we get here it means that we completely missed a sensor
* collection time and we attempt to recover by scheduling as
* soon as possible. This should not happen and if it does it
* means that the ec cannot handle the requested data rate.
*/
int missed_events =
time_until(sensor->next_collection, ts->le.lo) /
sensor->collection_rate;
CPRINTS("%s Missed %d data collections at %u - rate: %d",
sensor->name, missed_events, sensor->next_collection,
sensor->collection_rate);
sensor->next_collection = ts->le.lo + motion_min_interval;
}
}
static int motion_sense_process(struct motion_sensor_t *sensor,
uint32_t *event,
const timestamp_t *ts)
{
int ret = EC_SUCCESS;
int is_odr_pending = 0;
if (*event & TASK_EVENT_MOTION_ODR_CHANGE) {
const int sensor_bit = 1 << (sensor - motion_sensors);
int odr_pending = atomic_read_clear(&odr_event_required);
is_odr_pending = odr_pending & sensor_bit;
odr_pending &= ~sensor_bit;
atomic_or(&odr_event_required, odr_pending);
}
#ifdef CONFIG_ACCEL_INTERRUPTS
if ((*event & TASK_EVENT_MOTION_INTERRUPT_MASK || is_odr_pending) &&
(sensor->drv->irq_handler != NULL)) {
ret = sensor->drv->irq_handler(sensor, event);
}
#endif
#ifdef CONFIG_ACCEL_FIFO
if (motion_sensor_in_forced_mode(sensor)) {
if (motion_sensor_time_to_read(ts, sensor)) {
struct ec_response_motion_sensor_data vector;
int *v = sensor->raw_xyz;
ret = motion_sense_read(sensor);
if (ret == EC_SUCCESS) {
vector.flags = 0;
vector.sensor_num = sensor - motion_sensors;
#ifdef CONFIG_ACCEL_SPOOF_MODE
if (sensor->flags &
MOTIONSENSE_FLAG_IN_SPOOF_MODE)
v = sensor->spoof_xyz;
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
vector.data[X] = v[X];
vector.data[Y] = v[Y];
vector.data[Z] = v[Z];
motion_sense_fifo_stage_data(
&vector, sensor, 3,
__hw_clock_source_read());
motion_sense_fifo_commit_data();
}
increment_sensor_collection(sensor, ts);
} else {
ret = EC_ERROR_BUSY;
}
}
if (*event & TASK_EVENT_MOTION_FLUSH_PENDING) {
int flush_pending = atomic_read_clear(&sensor->flush_pending);
for (; flush_pending > 0; flush_pending--) {
motion_sense_insert_async_event(sensor,
ASYNC_EVENT_FLUSH);
}
}
#else
if (motion_sensor_in_forced_mode(sensor)) {
if (motion_sensor_time_to_read(ts, sensor)) {
/* Get latest data for local calculation */
ret = motion_sense_read(sensor);
increment_sensor_collection(sensor, ts);
} else {
ret = EC_ERROR_BUSY;
}
if (ret == EC_SUCCESS) {
mutex_lock(&g_sensor_mutex);
memcpy(sensor->xyz, sensor->raw_xyz,
sizeof(sensor->xyz));
mutex_unlock(&g_sensor_mutex);
}
}
#endif
/* ODR change was requested. */
if (is_odr_pending) {
motion_sense_set_data_rate(sensor);
motion_sense_set_motion_intervals();
#ifdef CONFIG_ACCEL_FIFO
motion_sense_insert_async_event(sensor,
ASYNC_EVENT_ODR);
#endif
}
return ret;
}
#ifdef CONFIG_ORIENTATION_SENSOR
enum motionsensor_orientation motion_sense_remap_orientation(
const struct motion_sensor_t *s,
enum motionsensor_orientation orientation)
{
enum motionsensor_orientation rotated_orientation;
const intv3_t *orientation_v;
intv3_t rotated_orientation_v;
if (orientation == MOTIONSENSE_ORIENTATION_UNKNOWN)
return MOTIONSENSE_ORIENTATION_UNKNOWN;
orientation_v = &orientation_modes[orientation];
rotate(*orientation_v, *s->rot_standard_ref, rotated_orientation_v);
rotated_orientation = ((2 * rotated_orientation_v[1] +
rotated_orientation_v[0] + 4) % 5);
return rotated_orientation;
}
#endif
#ifdef CONFIG_GESTURE_DETECTION
static void check_and_queue_gestures(uint32_t *event)
{
#ifdef CONFIG_ORIENTATION_SENSOR
const struct motion_sensor_t *sensor;
#endif
#ifdef CONFIG_GESTURE_SW_DETECTION
/* Run gesture recognition engine */
gesture_calc(event);
#endif
#ifdef CONFIG_GESTURE_SENSOR_BATTERY_TAP
if (*event & TASK_EVENT_MOTION_ACTIVITY_INTERRUPT(
MOTIONSENSE_ACTIVITY_DOUBLE_TAP)) {
#ifdef CONFIG_GESTURE_HOST_DETECTION
struct ec_response_motion_sensor_data vector;
/*
* Send events to the FIFO
* AP is ignoring double tap event, do no wake up and no
* automatic disable.
*/
vector.flags = 0;
vector.activity = MOTIONSENSE_ACTIVITY_DOUBLE_TAP;
vector.state = 1; /* triggered */
vector.sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID;
motion_sense_fifo_stage_data(&vector, NULL, 0,
__hw_clock_source_read());
motion_sense_fifo_commit_data();
#endif
/* Call board specific function to process tap */
sensor_board_proc_double_tap();
}
#endif
#ifdef CONFIG_GESTURE_SIGMO
if (*event & TASK_EVENT_MOTION_ACTIVITY_INTERRUPT(
MOTIONSENSE_ACTIVITY_SIG_MOTION)) {
struct motion_sensor_t *activity_sensor;
#ifdef CONFIG_GESTURE_HOST_DETECTION
struct ec_response_motion_sensor_data vector;
/* Send events to the FIFO */
vector.flags = MOTIONSENSE_SENSOR_FLAG_WAKEUP;
vector.activity = MOTIONSENSE_ACTIVITY_SIG_MOTION;
vector.state = 1; /* triggered */
vector.sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID;
motion_sense_fifo_stage_data(&vector, NULL, 0,
__hw_clock_source_read());
motion_sense_fifo_commit_data();
#endif
/* Disable further detection */
activity_sensor = &motion_sensors[CONFIG_GESTURE_SIGMO];
activity_sensor->drv->manage_activity(
activity_sensor,
MOTIONSENSE_ACTIVITY_SIG_MOTION,
0, NULL);
}
#endif
#ifdef CONFIG_ORIENTATION_SENSOR
sensor = &motion_sensors[LID_ACCEL];
if (SENSOR_ACTIVE(sensor) && (sensor->state == SENSOR_INITIALIZED)) {
struct ec_response_motion_sensor_data vector = {
.flags = 0,
.activity = MOTIONSENSE_ACTIVITY_ORIENTATION,
.sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID,
};
mutex_lock(sensor->mutex);
if (ORIENTATION_CHANGED(sensor) && (GET_ORIENTATION(sensor) !=
MOTIONSENSE_ORIENTATION_UNKNOWN)) {
SET_ORIENTATION_UPDATED(sensor);
vector.state = GET_ORIENTATION(sensor);
motion_sense_fifo_add_data(&vector, NULL, 0,
__hw_clock_source_read());
#ifdef CONFIG_DEBUG_ORIENTATION
{
static const char * const mode_strs[] = {
"Landscape",
"Portrait",
"Inv_Portrait",
"Inv_Landscape",
"Unknown"
};
CPRINTS(mode_strs[GET_ORIENTATION(sensor)]);
}
#endif
}
mutex_unlock(sensor->mutex);
}
#endif
}
#endif
/*
* Motion Sense Task
* Requirement: motion_sensors[] are defined in board.c file.
* Two (minimum) Accelerometers:
* 1 in the A/B(lid, display) and 1 in the C/D(base, keyboard)
* Gyro Sensor (optional)
*/
void motion_sense_task(void *u)
{
int i, ret, wait_us;
timestamp_t ts_begin_task, ts_end_task;
int32_t time_diff;
uint32_t event = 0;
uint16_t ready_status;
struct motion_sensor_t *sensor;
#ifdef CONFIG_LID_ANGLE
const uint16_t lid_angle_sensors = (BIT(CONFIG_LID_ANGLE_SENSOR_BASE)|
BIT(CONFIG_LID_ANGLE_SENSOR_LID));
#endif
#ifdef CONFIG_ACCEL_FIFO
timestamp_t ts_last_int;
#endif
#ifdef CONFIG_MOTION_FILL_LPC_SENSE_DATA
int sample_id = 0;
uint8_t *lpc_status;
lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS);
set_present(lpc_status);
#endif
#ifdef CONFIG_ACCEL_FIFO
ts_last_int = get_time();
#endif
while (1) {
ts_begin_task = get_time();
ready_status = 0;
for (i = 0; i < motion_sensor_count; ++i) {
sensor = &motion_sensors[i];
/* if the sensor is active in the current power state */
if (SENSOR_ACTIVE(sensor)) {
if (sensor->state != SENSOR_INITIALIZED) {
continue;
}
ret = motion_sense_process(sensor, &event,
&ts_begin_task);
if (ret != EC_SUCCESS)
continue;
ready_status |= BIT(i);
}
}
#ifdef CONFIG_GESTURE_DETECTION
check_and_queue_gestures(&event);
#endif
#ifdef CONFIG_LID_ANGLE
/*
* Check to see that the sensors required for lid angle
* calculation are ready.
*/
ready_status &= lid_angle_sensors;
if (ready_status == lid_angle_sensors)
motion_lid_calc();
#endif
#ifdef CONFIG_CMD_ACCEL_INFO
if (accel_disp) {
CPRINTF("[%T event 0x%08x ", event);
for (i = 0; i < motion_sensor_count; ++i) {
sensor = &motion_sensors[i];
CPRINTF("%s=%-5d, %-5d, %-5d ", sensor->name,
sensor->xyz[X],
sensor->xyz[Y],
sensor->xyz[Z]);
}
#ifdef CONFIG_LID_ANGLE
CPRINTF("a=%-4d", motion_lid_get_angle());
#endif
CPRINTF("]\n");
}
#endif
#ifdef CONFIG_MOTION_FILL_LPC_SENSE_DATA
update_sense_data(lpc_status, &sample_id);
#endif
#ifdef CONFIG_ACCEL_FIFO
/*
* Ask the host to flush the queue if
* - a flush event has been queued.
* - the queue is almost full,
* - we haven't done it for a while.
*/
if (wake_up_needed ||
event & (TASK_EVENT_MOTION_ODR_CHANGE |
TASK_EVENT_MOTION_FLUSH_PENDING) ||
queue_space(&motion_sense_fifo) < CONFIG_ACCEL_FIFO_THRES ||
(ap_event_interval > 0 &&
time_after(ts_begin_task.le.lo,
ts_last_int.le.lo + ap_event_interval))) {
if ((event & TASK_EVENT_MOTION_FLUSH_PENDING) == 0) {
motion_sense_fifo_stage_timestamp(
__hw_clock_source_read());
motion_sense_fifo_commit_data();
}
ts_last_int = ts_begin_task;
/*
* Count the number of event the AP is allowed to
* collect.
*/
mutex_lock(&g_sensor_mutex);
fifo_queue_count = queue_count(&motion_sense_fifo);
mutex_unlock(&g_sensor_mutex);
#ifdef CONFIG_MKBP_EVENT
/*
* Send an event if we know we are in S0 and the kernel
* driver is listening, or the AP needs to be waken up.
* In the latter case, the driver pulls the event and
* will resume listening until it is suspended again.
*/
if ((fifo_int_enabled &&
sensor_active == SENSOR_ACTIVE_S0) ||
wake_up_needed) {
mkbp_send_event(EC_MKBP_EVENT_SENSOR_FIFO);
wake_up_needed = 0;
}
#endif
}
#endif
ts_end_task = get_time();
wait_us = -1;
for (i = 0; i < motion_sensor_count; i++) {
struct motion_sensor_t *sensor = &motion_sensors[i];
if (!motion_sensor_in_forced_mode(sensor) ||
sensor->collection_rate == 0)
continue;
time_diff = time_until(ts_end_task.le.lo,
sensor->next_collection);
/* We missed our collection time so wake soon */
if (time_diff <= 0) {
wait_us = 0;
break;
}
if (wait_us == -1 || wait_us > time_diff)
wait_us = time_diff;
}
if (wait_us >= 0 && wait_us < motion_min_interval) {
/*
* Guarantee some minimum delay to allow other lower
* priority tasks to run.
*/
wait_us = motion_min_interval;
}
event = task_wait_event(wait_us);
}
}
#ifdef CONFIG_ACCEL_FIFO
static int motion_sense_get_next_event(uint8_t *out)
{
union ec_response_get_next_data *data =
(union ec_response_get_next_data *)out;
/* out is not padded. It has one byte for the event type */
motion_sense_get_fifo_info(&data->sensor_fifo.info);
return sizeof(data->sensor_fifo);
}
DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_SENSOR_FIFO, motion_sense_get_next_event);
#endif
/*****************************************************************************/
/* Host commands */
/* Function to map host sensor IDs to motion sensor. */
static struct motion_sensor_t
*host_sensor_id_to_real_sensor(int host_id)
{
struct motion_sensor_t *sensor;
if (host_id >= motion_sensor_count)
return NULL;
sensor = &motion_sensors[host_id];
/* if sensor is powered and initialized, return match */
if (SENSOR_ACTIVE(sensor) && (sensor->state == SENSOR_INITIALIZED))
return sensor;
/* If no match then the EC currently doesn't support ID received. */
return NULL;
}
static struct motion_sensor_t
*host_sensor_id_to_motion_sensor(int host_id)
{
#ifdef CONFIG_GESTURE_HOST_DETECTION
if (host_id == MOTION_SENSE_ACTIVITY_SENSOR_ID)
/*
* Return the info for the first sensor that
* support some gestures.
*/
return host_sensor_id_to_real_sensor(
__builtin_ctz(CONFIG_GESTURE_DETECTION_MASK));
#endif
return host_sensor_id_to_real_sensor(host_id);
}
static int host_cmd_motion_sense(struct host_cmd_handler_args *args)
{
const struct ec_params_motion_sense *in = args->params;
struct ec_response_motion_sense *out = args->response;
struct motion_sensor_t *sensor;
int i, ret = EC_RES_INVALID_PARAM, reported;
switch (in->cmd) {
case MOTIONSENSE_CMD_DUMP:
out->dump.module_flags =
(*(host_get_memmap(EC_MEMMAP_ACC_STATUS)) &
EC_MEMMAP_ACC_STATUS_PRESENCE_BIT) ?
MOTIONSENSE_MODULE_FLAG_ACTIVE : 0;
out->dump.sensor_count = ALL_MOTION_SENSORS;
args->response_size = sizeof(out->dump);
reported = MIN(ALL_MOTION_SENSORS, in->dump.max_sensor_count);
mutex_lock(&g_sensor_mutex);
for (i = 0; i < reported; i++) {
out->dump.sensor[i].flags =
MOTIONSENSE_SENSOR_FLAG_PRESENT;
if (i < motion_sensor_count) {
sensor = &motion_sensors[i];
/* casting from int to s16 */
out->dump.sensor[i].data[X] = sensor->xyz[X];
out->dump.sensor[i].data[Y] = sensor->xyz[Y];
out->dump.sensor[i].data[Z] = sensor->xyz[Z];
} else {
memset(out->dump.sensor[i].data, 0,
3 * sizeof(int16_t));
}
}
mutex_unlock(&g_sensor_mutex);
args->response_size += reported *
sizeof(struct ec_response_motion_sensor_data);
break;
case MOTIONSENSE_CMD_DATA:
sensor = host_sensor_id_to_real_sensor(
in->sensor_odr.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
out->data.flags = 0;
mutex_lock(&g_sensor_mutex);
out->data.data[X] = sensor->xyz[X];
out->data.data[Y] = sensor->xyz[Y];
out->data.data[Z] = sensor->xyz[Z];
mutex_unlock(&g_sensor_mutex);
args->response_size = sizeof(out->data);
break;
case MOTIONSENSE_CMD_INFO:
sensor = host_sensor_id_to_motion_sensor(
in->sensor_odr.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
#ifdef CONFIG_GESTURE_HOST_DETECTION
if (in->sensor_odr.sensor_num ==
MOTION_SENSE_ACTIVITY_SENSOR_ID)
out->info.type = MOTIONSENSE_TYPE_ACTIVITY;
else
#endif
out->info.type = sensor->type;
out->info.location = sensor->location;
out->info.chip = sensor->chip;
if (args->version >= 3) {
out->info_3.min_frequency = sensor->min_frequency;
out->info_3.max_frequency = sensor->max_frequency;
out->info_3.fifo_max_event_count = MAX_FIFO_EVENT_COUNT;
args->response_size = sizeof(out->info_3);
} else {
args->response_size = sizeof(out->info);
}
break;
case MOTIONSENSE_CMD_EC_RATE:
sensor = host_sensor_id_to_real_sensor(
in->sensor_odr.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
/*
* Set new sensor sampling rate when AP is on, if the data arg
* has a value.
*/
if (in->ec_rate.data != EC_MOTION_SENSE_NO_VALUE) {
sensor->config[SENSOR_CONFIG_AP].ec_rate =
motion_sense_set_ec_rate_from_ap(
sensor, in->ec_rate.data * MSEC);
/* Bound the new sampling rate. */
motion_sense_set_motion_intervals();
/* Force a collection to purge old events. */
task_set_event(TASK_ID_MOTIONSENSE,
TASK_EVENT_MOTION_ODR_CHANGE, 0);
}
out->ec_rate.ret = motion_sense_ec_rate(sensor) / MSEC;
args->response_size = sizeof(out->ec_rate);
break;
case MOTIONSENSE_CMD_SENSOR_ODR:
/* Verify sensor number is valid. */
sensor = host_sensor_id_to_real_sensor(
in->sensor_odr.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
/* Set new data rate if the data arg has a value. */
if (in->sensor_odr.data != EC_MOTION_SENSE_NO_VALUE) {
sensor->config[SENSOR_CONFIG_AP].odr =
in->sensor_odr.data |
(in->sensor_odr.roundup ? ROUND_UP_FLAG : 0);
/*
* The new ODR may suspend sensor, leaving samples
* in the FIFO. Flush it explicitly.
*/
atomic_or(&odr_event_required,
1 << (sensor - motion_sensors));
task_set_event(TASK_ID_MOTIONSENSE,
TASK_EVENT_MOTION_ODR_CHANGE, 0);
}
out->sensor_odr.ret = sensor->drv->get_data_rate(sensor);
args->response_size = sizeof(out->sensor_odr);
break;
case MOTIONSENSE_CMD_SENSOR_RANGE:
/* Verify sensor number is valid. */
sensor = host_sensor_id_to_real_sensor(
in->sensor_range.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
/* Set new range if the data arg has a value. */
if (in->sensor_range.data != EC_MOTION_SENSE_NO_VALUE) {
if (!sensor->drv->set_range)
return EC_RES_INVALID_COMMAND;
if (sensor->drv->set_range(sensor,
in->sensor_range.data,
in->sensor_range.roundup)
!= EC_SUCCESS) {
return EC_RES_INVALID_PARAM;
}
}
if (!sensor->drv->get_range)
return EC_RES_INVALID_COMMAND;
out->sensor_range.ret = sensor->drv->get_range(sensor);
args->response_size = sizeof(out->sensor_range);
break;
case MOTIONSENSE_CMD_SENSOR_OFFSET:
/* Verify sensor number is valid. */
sensor = host_sensor_id_to_real_sensor(
in->sensor_offset.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
/* Set new range if the data arg has a value. */
if (in->sensor_offset.flags & MOTION_SENSE_SET_OFFSET) {
if (!sensor->drv->set_offset)
return EC_RES_INVALID_COMMAND;
ret = sensor->drv->set_offset(sensor,
in->sensor_offset.offset,
in->sensor_offset.temp);
if (ret != EC_SUCCESS)
return ret;
}
if (!sensor->drv->get_offset)
return EC_RES_INVALID_COMMAND;
ret = sensor->drv->get_offset(sensor, out->sensor_offset.offset,
&out->sensor_offset.temp);
if (ret != EC_SUCCESS)
return ret;
args->response_size = sizeof(out->sensor_offset);
break;
case MOTIONSENSE_CMD_SENSOR_SCALE:
/* Verify sensor number is valid. */
sensor = host_sensor_id_to_real_sensor(
in->sensor_scale.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
/* Set new range if the data arg has a value. */
if (in->sensor_scale.flags & MOTION_SENSE_SET_OFFSET) {
if (!sensor->drv->set_scale)
return EC_RES_INVALID_COMMAND;
ret = sensor->drv->set_scale(sensor,
in->sensor_scale.scale,
in->sensor_scale.temp);
if (ret != EC_SUCCESS)
return ret;
}
if (!sensor->drv->get_scale)
return EC_RES_INVALID_COMMAND;
ret = sensor->drv->get_scale(sensor, out->sensor_scale.scale,
&out->sensor_scale.temp);
if (ret != EC_SUCCESS)
return ret;
args->response_size = sizeof(out->sensor_scale);
break;
case MOTIONSENSE_CMD_PERFORM_CALIB:
/* Verify sensor number is valid. */
sensor = host_sensor_id_to_real_sensor(
in->sensor_offset.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
if (!sensor->drv->perform_calib)
return EC_RES_INVALID_COMMAND;
ret = sensor->drv->perform_calib(sensor);
if (ret != EC_SUCCESS)
return ret;
ret = sensor->drv->get_offset(sensor, out->sensor_offset.offset,
&out->sensor_offset.temp);
if (ret != EC_SUCCESS)
return ret;
args->response_size = sizeof(out->sensor_offset);
break;
#ifdef CONFIG_ACCEL_FIFO
case MOTIONSENSE_CMD_FIFO_FLUSH:
sensor = host_sensor_id_to_real_sensor(
in->sensor_odr.sensor_num);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
atomic_add(&sensor->flush_pending, 1);
task_set_event(TASK_ID_MOTIONSENSE,
TASK_EVENT_MOTION_FLUSH_PENDING, 0);
/* pass-through */
case MOTIONSENSE_CMD_FIFO_INFO:
motion_sense_get_fifo_info(&out->fifo_info);
for (i = 0; i < motion_sensor_count; i++) {
out->fifo_info.lost[i] = motion_sensors[i].lost;
motion_sensors[i].lost = 0;
}
motion_sense_fifo_lost = 0;
args->response_size = sizeof(out->fifo_info) +
sizeof(uint16_t) * motion_sensor_count;
break;
case MOTIONSENSE_CMD_FIFO_READ:
mutex_lock(&g_sensor_mutex);
reported = MIN((args->response_max - sizeof(out->fifo_read)) /
motion_sense_fifo.unit_bytes,
MIN(queue_count(&motion_sense_fifo),
in->fifo_read.max_data_vector));
reported = queue_remove_units(&motion_sense_fifo,
out->fifo_read.data, reported);
mutex_unlock(&g_sensor_mutex);
out->fifo_read.number_data = reported;
args->response_size = sizeof(out->fifo_read) + reported *
motion_sense_fifo.unit_bytes;
break;
case MOTIONSENSE_CMD_FIFO_INT_ENABLE:
switch (in->fifo_int_enable.enable) {
case 0:
case 1:
fifo_int_enabled = in->fifo_int_enable.enable;
/* fallthrough */
case EC_MOTION_SENSE_NO_VALUE:
out->fifo_int_enable.ret = fifo_int_enabled;
args->response_size = sizeof(out->fifo_int_enable);
break;
default:
return EC_RES_INVALID_PARAM;
}
break;
#else
case MOTIONSENSE_CMD_FIFO_INFO:
/* Only support the INFO command, to tell there is no FIFO. */
memset(&out->fifo_info, 0, sizeof(out->fifo_info));
args->response_size = sizeof(out->fifo_info);
break;
#endif
#ifdef CONFIG_GESTURE_HOST_DETECTION
case MOTIONSENSE_CMD_LIST_ACTIVITIES: {
uint32_t enabled, disabled, mask, i;
out->list_activities.enabled = 0;
out->list_activities.disabled = 0;
ret = EC_RES_SUCCESS;
mask = CONFIG_GESTURE_DETECTION_MASK;
while (mask && ret == EC_RES_SUCCESS) {
i = get_next_bit(&mask);
sensor = &motion_sensors[i];
ret = sensor->drv->list_activities(sensor,
&enabled, &disabled);
if (ret == EC_RES_SUCCESS) {
out->list_activities.enabled |= enabled;
out->list_activities.disabled |= disabled;
}
}
if (ret != EC_RES_SUCCESS)
return ret;
args->response_size = sizeof(out->list_activities);
break;
}
case MOTIONSENSE_CMD_SET_ACTIVITY: {
uint32_t enabled, disabled, mask, i;
mask = CONFIG_GESTURE_DETECTION_MASK;
ret = EC_RES_SUCCESS;
while (mask && ret == EC_RES_SUCCESS) {
i = get_next_bit(&mask);
sensor = &motion_sensors[i];
sensor->drv->list_activities(sensor,
&enabled, &disabled);
if ((1 << in->set_activity.activity) &
(enabled | disabled))
ret = sensor->drv->manage_activity(sensor,
in->set_activity.activity,
in->set_activity.enable,
&in->set_activity);
}
if (ret != EC_RES_SUCCESS)
return ret;
args->response_size = 0;
break;
}
#endif /* defined(CONFIG_GESTURE_HOST_DETECTION) */
#ifdef CONFIG_ACCEL_SPOOF_MODE
case MOTIONSENSE_CMD_SPOOF: {
sensor = host_sensor_id_to_real_sensor(in->spoof.sensor_id);
if (sensor == NULL)
return EC_RES_INVALID_PARAM;
switch (in->spoof.spoof_enable) {
case MOTIONSENSE_SPOOF_MODE_DISABLE:
/* Disable spoof mode. */
sensor->flags &= ~MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_CUSTOM:
/*
* Enable spoofing, but use provided component values.
*/
sensor->spoof_xyz[X] = (int)in->spoof.components[X];
sensor->spoof_xyz[Y] = (int)in->spoof.components[Y];
sensor->spoof_xyz[Z] = (int)in->spoof.components[Z];
sensor->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT:
/*
* Enable spoofing, but lock to current sensor
* values. raw_xyz already has the values we want.
*/
sensor->spoof_xyz[X] = sensor->raw_xyz[X];
sensor->spoof_xyz[Y] = sensor->raw_xyz[Y];
sensor->spoof_xyz[Z] = sensor->raw_xyz[Z];
sensor->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_QUERY:
/* Querying the spoof status of the sensor. */
out->spoof.ret = !!(sensor->flags &
MOTIONSENSE_FLAG_IN_SPOOF_MODE);
args->response_size = sizeof(out->spoof);
break;
default:
return EC_RES_INVALID_PARAM;
}
/*
* Only print the status when spoofing is enabled or disabled.
*/
if (in->spoof.spoof_enable != MOTIONSENSE_SPOOF_MODE_QUERY)
print_spoof_mode_status((int)(sensor - motion_sensors));
break;
}
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
default:
/* Call other users of the motion task */
#ifdef CONFIG_LID_ANGLE
if (ret == EC_RES_INVALID_PARAM)
ret = host_cmd_motion_lid(args);
#endif
return ret;
}
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_MOTION_SENSE_CMD,
host_cmd_motion_sense,
EC_VER_MASK(1) | EC_VER_MASK(2) | EC_VER_MASK(3));
/*****************************************************************************/
/* Console commands */
#ifdef CONFIG_CMD_ACCELS
static int command_accelrange(int argc, char **argv)
{
char *e;
int id, data, round = 1;
struct motion_sensor_t *sensor;
if (argc < 2 || argc > 4)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (*e || id < 0 || id >= motion_sensor_count)
return EC_ERROR_PARAM1;
sensor = &motion_sensors[id];
if (argc >= 3) {
/* Second argument is data to write. */
data = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
if (argc == 4) {
/* Third argument is rounding flag. */
round = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
}
/*
* Write new range, if it returns invalid arg, then return
* a parameter error.
*/
if (sensor->drv->set_range(sensor,
data,
round) == EC_ERROR_INVAL)
return EC_ERROR_PARAM2;
} else {
ccprintf("Range for sensor %d: %d\n", id,
sensor->drv->get_range(sensor));
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelrange, command_accelrange,
"id [data [roundup]]",
"Read or write accelerometer range");
static int command_accelresolution(int argc, char **argv)
{
char *e;
int id, data, round = 1;
struct motion_sensor_t *sensor;
if (argc < 2 || argc > 4)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (*e || id < 0 || id >= motion_sensor_count)
return EC_ERROR_PARAM1;
sensor = &motion_sensors[id];
if (argc >= 3) {
/* Second argument is data to write. */
data = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
if (argc == 4) {
/* Third argument is rounding flag. */
round = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
}
/*
* Write new resolution, if it returns invalid arg, then
* return a parameter error.
*/
if (sensor->drv->set_resolution &&
sensor->drv->set_resolution(sensor, data, round)
== EC_ERROR_INVAL)
return EC_ERROR_PARAM2;
} else {
ccprintf("Resolution for sensor %d: %d\n", id,
sensor->drv->get_resolution(sensor));
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelres, command_accelresolution,
"id [data [roundup]]",
"Read or write accelerometer resolution");
static int command_accel_data_rate(int argc, char **argv)
{
char *e;
int id, data, round = 1;
struct motion_sensor_t *sensor;
enum sensor_config config_id;
if (argc < 2 || argc > 4)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (*e || id < 0 || id >= motion_sensor_count)
return EC_ERROR_PARAM1;
sensor = &motion_sensors[id];
if (argc >= 3) {
/* Second argument is data to write. */
data = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
if (argc == 4) {
/* Third argument is rounding flag. */
round = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
}
/*
* Take ownership of the sensor and
* Write new data rate, if it returns invalid arg, then
* return a parameter error.
*/
config_id = motion_sense_get_ec_config();
sensor->config[SENSOR_CONFIG_AP].odr = 0;
sensor->config[config_id].odr =
data | (round ? ROUND_UP_FLAG : 0);
task_set_event(TASK_ID_MOTIONSENSE,
TASK_EVENT_MOTION_ODR_CHANGE, 0);
} else {
ccprintf("Data rate for sensor %d: %d\n", id,
sensor->drv->get_data_rate(sensor));
ccprintf("EC rate for sensor %d: %d\n", id,
motion_sense_ec_rate(sensor));
ccprintf("Current Interrupt rate: %d\n", ap_event_interval);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelrate, command_accel_data_rate,
"id [data [roundup]]",
"Read or write accelerometer ODR");
static int command_accel_read_xyz(int argc, char **argv)
{
char *e;
int id, n = 1, ret;
struct motion_sensor_t *sensor;
intv3_t v;
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (*e || id < 0 || id >= motion_sensor_count)
return EC_ERROR_PARAM1;
if (argc >= 3)
n = strtoi(argv[2], &e, 0);
sensor = &motion_sensors[id];
while ((n == -1) || (n-- > 0)) {
ret = sensor->drv->read(sensor, v);
if (ret == 0)
ccprintf("Current data %d: %-5d %-5d %-5d\n",
id, v[X], v[Y], v[Z]);
else
ccprintf("vector not ready\n");
ccprintf("Last calib. data %d: %-5d %-5d %-5d\n",
id, sensor->xyz[X], sensor->xyz[Y], sensor->xyz[Z]);
task_wait_event(motion_min_interval);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelread, command_accel_read_xyz,
"id [n]",
"Read sensor x/y/z");
static int command_accel_init(int argc, char **argv)
{
char *e;
int id, ret;
struct motion_sensor_t *sensor;
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (*e || id < 0 || id >= motion_sensor_count)
return EC_ERROR_PARAM1;
sensor = &motion_sensors[id];
ret = motion_sense_init(sensor);
ccprintf("%s: state %d - %d\n", sensor->name, sensor->state, ret);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelinit, command_accel_init,
"id",
"Init sensor");
#ifdef CONFIG_CMD_ACCEL_INFO
static int command_display_accel_info(int argc, char **argv)
{
int val, i, j;
if (argc > 3)
return EC_ERROR_PARAM_COUNT;
ccprintf("Motion sensors count = %d\n", motion_sensor_count);
/* Print motion sensor info. */
for (i = 0; i < motion_sensor_count; i++) {
ccprintf("\nsensor %d name: %s\n", i, motion_sensors[i].name);
ccprintf("active mask: %d\n", motion_sensors[i].active_mask);
ccprintf("chip: %d\n", motion_sensors[i].chip);
ccprintf("type: %d\n", motion_sensors[i].type);
ccprintf("location: %d\n", motion_sensors[i].location);
ccprintf("port: %d\n", motion_sensors[i].port);
ccprintf("addr: %d\n", motion_sensors[i].addr);
ccprintf("range: %d\n", motion_sensors[i].default_range);
ccprintf("min_freq: %d\n", motion_sensors[i].min_frequency);
ccprintf("max_freq: %d\n", motion_sensors[i].max_frequency);
ccprintf("config:\n");
for (j = 0; j < SENSOR_CONFIG_MAX; j++) {
ccprintf("%d - odr: %umHz, ec_rate: %uus\n", j,
motion_sensors[i].config[j].odr &
~ROUND_UP_FLAG,
motion_sensors[i].config[j].ec_rate);
}
}
/* First argument is on/off whether to display accel data. */
if (argc > 1) {
if (!parse_bool(argv[1], &val))
return EC_ERROR_PARAM1;
accel_disp = val;
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelinfo, command_display_accel_info,
"on/off [interval]",
"Print motion sensor info, lid angle calculations"
" and set calculation frequency.");
#endif /* CONFIG_CMD_ACCEL_INFO */
#ifdef CONFIG_CMD_ACCEL_FIFO
static int motion_sense_read_fifo(int argc, char **argv)
{
int count, i;
struct ec_response_motion_sensor_data v;
if (argc < 1)
return EC_ERROR_PARAM_COUNT;
/* Limit the amount of data to avoid saturating the UART buffer */
count = MIN(queue_count(&motion_sense_fifo), 16);
for (i = 0; i < count; i++) {
queue_peek_units(&motion_sense_fifo, &v, i, 1);
if (v.flags & (MOTIONSENSE_SENSOR_FLAG_TIMESTAMP |
MOTIONSENSE_SENSOR_FLAG_FLUSH)) {
uint64_t timestamp;
memcpy(&timestamp, v.data, sizeof(v.data));
ccprintf("Timestamp: 0x%016lx%s\n", timestamp,
(v.flags & MOTIONSENSE_SENSOR_FLAG_FLUSH ?
" - Flush" : ""));
} else {
ccprintf("%d %d: %-5d %-5d %-5d\n", i, v.sensor_num,
v.data[X], v.data[Y], v.data[Z]);
}
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(fiforead, motion_sense_read_fifo,
"id",
"Read Fifo sensor");
#endif /* defined(CONFIG_CMD_ACCEL_FIFO) */
#endif /* CONFIG_CMD_ACCELS */
#ifdef CONFIG_ACCEL_SPOOF_MODE
static void print_spoof_mode_status(int id)
{
CPRINTS("Sensor %d spoof mode is %s. <%d, %d, %d>", id,
(motion_sensors[id].flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE)
? "enabled" : "disabled",
motion_sensors[id].spoof_xyz[X],
motion_sensors[id].spoof_xyz[Y],
motion_sensors[id].spoof_xyz[Z]);
}
#ifdef CONFIG_CMD_ACCELSPOOF
static int command_accelspoof(int argc, char **argv)
{
char *e;
int id, enable, i;
struct motion_sensor_t *s;
/* There must be at least 1 parameter, the sensor id. */
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
/* First argument is sensor id. */
id = strtoi(argv[1], &e, 0);
if (id >= motion_sensor_count || id < 0)
return EC_ERROR_PARAM1;
s = &motion_sensors[id];
/* Print the sensor's current spoof status. */
if (argc == 2)
print_spoof_mode_status(id);
/* Enable/Disable spoof mode. */
if (argc >= 3) {
if (!parse_bool(argv[2], &enable))
return EC_ERROR_PARAM2;
if (enable) {
/*
* If no components are provided, we'll just use the
* current values as the spoofed values. But if the
* components are provided, use the provided ones as the
* spoofed ones.
*/
if (argc == 6) {
for (i = 0; i < 3; i++)
s->spoof_xyz[i] = strtoi(argv[3 + i],
&e, 0);
} else if (argc == 3) {
for (i = X; i <= Z; i++)
s->spoof_xyz[i] = s->raw_xyz[i];
} else {
/* It's either all or nothing. */
return EC_ERROR_PARAM_COUNT;
}
}
if (enable)
s->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
else
s->flags &= ~MOTIONSENSE_FLAG_IN_SPOOF_MODE;
print_spoof_mode_status(id);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelspoof, command_accelspoof,
"id [on/off] [X] [Y] [Z]",
"Enable/Disable spoofing of sensor readings.");
#endif /* defined(CONIFG_CMD_ACCELSPOOF) */
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */