summaryrefslogtreecommitdiff
path: root/common/motion_sense_fifo.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/motion_sense_fifo.c')
-rw-r--r--common/motion_sense_fifo.c523
1 files changed, 0 insertions, 523 deletions
diff --git a/common/motion_sense_fifo.c b/common/motion_sense_fifo.c
deleted file mode 100644
index bdf39a857c..0000000000
--- a/common/motion_sense_fifo.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/* Copyright 2019 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.
- */
-
-#include "console.h"
-#include "hwtimer.h"
-#include "mkbp_event.h"
-#include "motion_sense_fifo.h"
-#include "tablet_mode.h"
-#include "task.h"
-#include "util.h"
-
-#define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ## args)
-
-/**
- * Staged metadata for the fifo queue.
- * @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;
-};
-
-/**
- * Timestamp state metadata for maintaining spreading between commits.
- * @prev: The previous timestamp that was added to the FIFO
- * @next: The predicted next timestamp that will be added to the FIFO
- */
-struct timestamp_state {
- uint32_t prev;
- uint32_t next;
-};
-
-/** Queue to hold the data to be sent to the AP. */
-static struct queue fifo = QUEUE_NULL(CONFIG_ACCEL_FIFO_SIZE,
- struct ec_response_motion_sensor_data);
-/** Count of the number of entries lost due to a small queue. */
-static int fifo_lost;
-/** Metadata for the fifo, used for staging and spreading data. */
-static struct fifo_staged fifo_staged;
-
-/**
- * Cached expected timestamp per sensor. If a sensor's timestamp pre-dates this
- * timestamp it will be fast forwarded.
- */
-static struct timestamp_state next_timestamp[SENSOR_COUNT];
-
-/**
- * Bitmap telling which sensors have valid entries in the next_timestamp array.
- */
-static uint32_t next_timestamp_initialized;
-
-/** Need to wake up the AP. */
-static int wake_up_needed;
-
-/**
- * Check whether or not a give sensor data entry is a timestamp or not.
- *
- * @param data The data entry to check.
- * @return 1 if the entry is a timestamp, 0 otherwise.
- */
-static inline int is_timestamp(
- const struct ec_response_motion_sensor_data *data)
-{
- return data->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP;
-}
-
-/**
- * Convenience function to get the head of the fifo. This function makes no
- * guarantee on whether or not the entry is valid.
- *
- * @return Pointer to the head of the fifo.
- */
-static inline struct ec_response_motion_sensor_data *get_fifo_head(void)
-{
- return ((struct ec_response_motion_sensor_data *) fifo.buffer) +
- (fifo.state->head & fifo.buffer_units_mask);
-}
-
-/**
- * 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 fifo_pop(void)
-{
- struct ec_response_motion_sensor_data *head = get_fifo_head();
- const size_t initial_count = queue_count(&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(&fifo, 1);
-
- /*
- * If we're about to pop a wakeup flag, we should remember it as though
- * it was committed.
- */
- if (head->flags & MOTIONSENSE_SENSOR_FLAG_WAKEUP)
- wake_up_needed = 1;
- /*
- * By not using queue_remove_unit we're avoiding an un-necessary memcpy.
- */
- queue_advance_head(&fifo, 1);
- 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;
- }
- }
- }
-}
-
-/**
- * Make sure that the fifo has at least 1 empty spot to stage data into.
- */
-static void fifo_ensure_space(void)
-{
- /* If we already have space just bail. */
- if (queue_space(&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 {
- fifo_pop();
- } while (IS_ENABLED(CONFIG_SENSOR_TIGHT_TIMESTAMPS) &&
- !is_timestamp(get_fifo_head()) &&
- queue_count(&fifo) + fifo_staged.count);
-}
-
-/**
- * Stage a single data unit to the motion sense fifo. Note that for the AP to
- * see this data, it must be committed.
- *
- * @param data The data to stage.
- * @param sensor The sensor that generated the data
- * @param valid_data The number of readable data entries in the data.
- */
-static void 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)
- goto stage_unit_end;
- removed = sensor->oversampling++;
- sensor->oversampling %= sensor->oversampling_ratio;
- if (removed)
- goto stage_unit_end;
- }
-
- /* Make sure we have room for the data */
- fifo_ensure_space();
-
- if (IS_ENABLED(CONFIG_TABLET_MODE))
- data->flags |= (tablet_get_mode() ?
- MOTIONSENSE_SENSOR_FLAG_TABLET_MODE : 0);
-
- /*
- * 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(&fifo, fifo_staged.count);
-
- if (!chunk.buffer) {
- /*
- * This should never happen since we already ensured there was
- * space, but if there was a bug, we don't want to write to
- * address 0. Just don't add any data to the queue instead.
- */
- CPRINTS("Failed to get write chunk for new fifo data!");
- goto stage_unit_end;
- }
-
- /*
- * 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, 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;
-
-stage_unit_end:
- mutex_unlock(&g_sensor_mutex);
-}
-
-/**
- * Stage an entry representing a single timestamp.
- *
- * @param timestamp The timestamp to add to the fifo.
- */
-static void 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;
- fifo_stage_unit(&vector, NULL, 0);
-}
-
-/**
- * Peek into the staged data at a given offset. This function performs no bound
- * checking and is purely for confinience.
- *
- * @param offset The offset into the staged data to peek into.
- * @return Pointer to the entry at the given offset.
- */
-static inline struct ec_response_motion_sensor_data *
-peek_fifo_staged(size_t offset)
-{
- return (struct ec_response_motion_sensor_data *)
- queue_get_write_chunk(&fifo, offset).buffer;
-}
-
-int motion_sense_fifo_wake_up_needed(void)
-{
- int res;
-
- mutex_lock(&g_sensor_mutex);
- res = wake_up_needed;
- mutex_unlock(&g_sensor_mutex);
- return res;
-}
-
-void motion_sense_fifo_reset_wake_up_needed(void)
-{
- mutex_lock(&g_sensor_mutex);
- wake_up_needed = 0;
- mutex_unlock(&g_sensor_mutex);
-}
-
-void motion_sense_fifo_insert_async_event(
- struct motion_sensor_t *sensor,
- enum motion_sense_async_event event)
-{
- struct ec_response_motion_sensor_data vector;
-
- vector.flags = event;
- vector.timestamp = __hw_clock_source_read();
- vector.sensor_num = sensor - motion_sensors;
-
- fifo_stage_unit(&vector, sensor, 0);
- motion_sense_fifo_commit_data();
-}
-
-inline void motion_sense_fifo_add_timestamp(uint32_t timestamp)
-{
- fifo_stage_timestamp(timestamp);
- motion_sense_fifo_commit_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();
- fifo_stage_timestamp(time);
- }
- fifo_stage_unit(data, sensor, valid_data);
-}
-
-void motion_sense_fifo_commit_data(void)
-{
- /* Cached data periods, static to store off stack. */
- static uint32_t data_periods[SENSOR_COUNT];
- struct ec_response_motion_sensor_data *data;
- int i, window, sensor_num;
-
- /* Nothing staged, no work to do. */
- if (!fifo_staged.count)
- return;
-
- mutex_lock(&g_sensor_mutex);
- /*
- * 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 commit_data_end;
-
- data = 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");
- fifo_staged.requires_spreading = 0;
- goto commit_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 count - 1).
- */
- if (window && fifo_staged.sample_count[i] > 1)
- period = MIN(
- period,
- window / (fifo_staged.sample_count[i] - 1));
- data_periods[i] = period;
- }
-
-commit_data_end:
- /*
- * Conditionally 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 = peek_fifo_staged(i);
- if (data->flags & MOTIONSENSE_SENSOR_FLAG_WAKEUP)
- wake_up_needed = 1;
-
- /* 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 = peek_fifo_staged(i - 1);
-
- /*
- * If this is the first time we're seeing a timestamp for this
- * sensor or the timestamp is after our computed next, skip
- * ahead.
- */
- if (!(next_timestamp_initialized & BIT(sensor_num)) ||
- time_after(data->timestamp,
- next_timestamp[sensor_num].prev)) {
- next_timestamp[sensor_num].next = data->timestamp;
- next_timestamp_initialized |= BIT(sensor_num);
- }
-
- /* Spread the timestamp and compute the expected next. */
- data->timestamp = next_timestamp[sensor_num].next;
- next_timestamp[sensor_num].prev =
- next_timestamp[sensor_num].next;
- next_timestamp[sensor_num].next +=
- fifo_staged.requires_spreading
- ? data_periods[sensor_num]
- : motion_sensors[sensor_num].collection_rate;
- }
-
- /* Advance the tail and clear the staged metadata. */
- queue_advance_tail(&fifo, fifo_staged.count);
-
- /* Reset metadata for next staging cycle. */
- memset(&fifo_staged, 0, sizeof(fifo_staged));
-
- mutex_unlock(&g_sensor_mutex);
-}
-
-void motion_sense_fifo_get_info(
- struct ec_response_motion_sense_fifo_info *fifo_info,
- int reset)
-{
- mutex_lock(&g_sensor_mutex);
- fifo_info->size = fifo.buffer_units;
- fifo_info->count = queue_count(&fifo);
- fifo_info->total_lost = fifo_lost;
- mutex_unlock(&g_sensor_mutex);
- fifo_info->timestamp = mkbp_last_event_time;
-
- if (reset)
- fifo_lost = 0;
-}
-
-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_fifo_get_info(&data->sensor_fifo.info, 0);
- return sizeof(data->sensor_fifo);
-}
-
-DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_SENSOR_FIFO, motion_sense_get_next_event);
-
-inline int motion_sense_fifo_over_thres(void)
-{
- int result;
-
- mutex_lock(&g_sensor_mutex);
- result = queue_space(&fifo) < CONFIG_ACCEL_FIFO_THRES;
- mutex_unlock(&g_sensor_mutex);
-
- return result;
-}
-
-int motion_sense_fifo_read(int capacity_bytes, int max_count, void *out,
- uint16_t *out_size)
-{
- int count;
-
- mutex_lock(&g_sensor_mutex);
- count = MIN(capacity_bytes / fifo.unit_bytes,
- MIN(queue_count(&fifo), max_count));
- count = queue_remove_units(&fifo, out, count);
- mutex_unlock(&g_sensor_mutex);
- *out_size = count * fifo.unit_bytes;
-
- return count;
-}
-
-void motion_sense_fifo_reset(void)
-{
- next_timestamp_initialized = 0;
- memset(&fifo_staged, 0, sizeof(fifo_staged));
- queue_init(&fifo);
-}