summaryrefslogtreecommitdiff
path: root/common/motion_sense.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/motion_sense.c')
-rw-r--r--common/motion_sense.c340
1 files changed, 305 insertions, 35 deletions
diff --git a/common/motion_sense.c b/common/motion_sense.c
index c2df8f080e..3e9bd4b550 100644
--- a/common/motion_sense.c
+++ b/common/motion_sense.c
@@ -84,6 +84,12 @@ static void print_spoof_mode_status(int id);
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;
@@ -95,24 +101,138 @@ 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;
+ uint8_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++;
+
+ /* Only continue if we removed from staged. */
+ 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_add_data instead to get a proper timestamp too.
+ * motion_sense_fifo_stage_data instead to get a proper timestamp too.
*/
-static void motion_sense_fifo_add_unit(
- struct ec_response_motion_sensor_data *data,
- struct motion_sensor_t *sensor,
- int valid_data)
+static void motion_sense_fifo_stage_unit(
+ struct ec_response_motion_sensor_data *data,
+ struct motion_sensor_t *sensor,
+ int valid_data)
{
- struct ec_response_motion_sensor_data vector;
+ struct queue_chunk chunk;
int i;
mutex_lock(&g_sensor_mutex);
- if (queue_space(&motion_sense_fifo) == 0) {
- queue_remove_unit(&motion_sense_fifo, &vector);
- motion_sense_fifo_lost++;
- motion_sensors[vector.sensor_num].lost++;
- }
+
for (i = 0; i < valid_data; i++)
sensor->xyz[i] = data->data[i];
@@ -131,17 +251,47 @@ static void motion_sense_fifo_add_unit(
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) {
+
+ 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
- mutex_lock(&g_sensor_mutex);
- queue_add_unit(&motion_sense_fifo, data);
- mutex_unlock(&g_sensor_mutex);
+
+ /*
+ * 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);
+
+ /*
+ * 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 {
@@ -155,30 +305,144 @@ 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_add_unit(&vector, sensor, 0);
+ motion_sense_fifo_stage_unit(&vector, sensor, 0);
+ motion_sense_fifo_commit_data();
}
-static void motion_sense_insert_timestamp(uint32_t timestamp)
+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_add_unit(&vector, NULL, 0);
+ motion_sense_fifo_stage_unit(&vector, NULL, 0);
}
-void motion_sense_fifo_add_data(struct ec_response_motion_sensor_data *data,
- struct motion_sensor_t *sensor,
- int valid_data,
- uint32_t time) {
-#ifdef CONFIG_SENSOR_TIGHT_TIMESTAMPS
- motion_sense_insert_timestamp(time);
-#endif
- motion_sense_fifo_add_unit(data, sensor, valid_data);
+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(
@@ -762,8 +1026,10 @@ static int motion_sense_process(struct motion_sensor_t *sensor,
vector.data[X] = v[X];
vector.data[Y] = v[Y];
vector.data[Z] = v[Z];
- motion_sense_fifo_add_data(&vector, sensor, 3,
- __hw_clock_source_read());
+ motion_sense_fifo_stage_data(
+ &vector, sensor, 3,
+ __hw_clock_source_read());
+ motion_sense_fifo_commit_data();
}
increment_sensor_collection(sensor, ts);
} else {
@@ -854,8 +1120,9 @@ static void check_and_queue_gestures(uint32_t *event)
vector.activity = MOTIONSENSE_ACTIVITY_DOUBLE_TAP;
vector.state = 1; /* triggered */
vector.sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID;
- motion_sense_fifo_add_data(&vector, NULL, 0,
- __hw_clock_source_read());
+ 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();
@@ -873,8 +1140,9 @@ static void check_and_queue_gestures(uint32_t *event)
vector.activity = MOTIONSENSE_ACTIVITY_SIG_MOTION;
vector.state = 1; /* triggered */
vector.sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID;
- motion_sense_fifo_add_data(&vector, NULL, 0,
- __hw_clock_source_read());
+ 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];
@@ -1019,9 +1287,11 @@ void motion_sense_task(void *u)
(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_insert_timestamp(
+ 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