diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 7886cc5f0bb28fbc2ba5d818e43ef5f7879c0e50..cc0926d1078f25f3638486ba66072a9bf3d73d2b 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -1,6 +1,8 @@ #ifndef _EPICARDIUM_H #define _EPICARDIUM_H #include <stdint.h> +#include <stddef.h> +#include <errno.h> #ifndef API #define API(id, def) def @@ -12,6 +14,7 @@ #define API_LEDS_SET 0x3 #define API_VIBRA_SET 0x4 #define API_VIBRA_VIBRATE 0x5 +#define API_STREAM_READ 0x6 /* clang-format on */ /** @@ -55,6 +58,61 @@ API(API_UART_READ, char epic_uart_read_chr(void)); */ API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)); +/** + * Sensor Data Streams + * =================== + * A few of card10's sensors can do continuous measurements. To allow + * performant access to their data, the following function is made for generic + * access to streams. + */ + +/** + * Read sensor data into a buffer. ``epic_stream_read()`` will read as many + * sensor data packets as possible into ``buf`` and return as soon as possible. + * It will poke the sensor driver once to check whether new data can be fetched. + * If there is no new sensor data, ``epic_stream_read()`` will return ``0`` and + * not touch ``buf``. Otherwise it will return the number of data packets which + * were read into ``buf``. + * + * :param int sd: Sensor Descriptor. You get sensor descriptors as return + * values when activating the respective sensors. + * :param void* buf: Buffer where sensor data should be read into. + * :param size_t count: How many bytes to read at max. Note that fewer bytes + * might be read. In most cases, this should be ``sizeof(buf)``. + * :return: Number of data packets read (**not** number of bytes) or a negative + * error value. Possible errors: + * + * - ``-ENODEV``: Sensor is not currently available. + * - ``-EBADF``: The given sensor descriptor is unknown. + * - ``-EINVAL``: If ``count`` is not a multiple of the sensor data packet + * size. + * + * **Example**: + * + * .. code-block:: cpp + * + * #include "epicardium.h" + * + * struct foo_measurement sensor_data[16]; + * int foo_sd, n; + * + * foo_sd = epic_foo_sensor_enable(9001); + * + * while (1) { + * n = epic_stream_read( + * foo_sd, + * &sensor_data, + * sizeof(sensor_data) + * ); + * + * // Print out the measured sensor samples + * for (int i = 0; i < n; i++) { + * printf("Measured: %?\n", sensor_data[i]); + * } + * } + */ +API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count)); + /** * Misc * ==== diff --git a/epicardium/main.c b/epicardium/main.c index 0117add3c1290474fba9f8cb6bb88c18339057a0..51d365f2c0a754b1432dfe5ae14ddf95de620aee 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -11,6 +11,7 @@ #include "api/dispatcher.h" #include "modules/modules.h" #include "modules/log.h" +#include "modules/stream.h" #include <Heart.h> #include "GUI_Paint.h" @@ -53,6 +54,7 @@ int main(void) } fatfs_init(); + stream_init(); LOG_INFO("startup", "Initializing tasks ..."); diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index dfc27d00574c49ec1bdbeffc99321f3bb4f1758e..a038fb21d7f5ef7c650a10851503150a27c0b1eb 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -4,5 +4,6 @@ module_sources = files( 'log.c', 'pmic.c', 'serial.c', + 'stream.c', 'vibra.c', ) diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 8fe73d59755c6146bba1d364938a6305af9e1baa..0db3d11bf84ca07f8a65f499fa1497a55940968c 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -15,4 +15,3 @@ void vSerialTask(void *pvParameters); void vPmicTask(void *pvParameters); #endif /* MODULES_H */ - diff --git a/epicardium/modules/stream.c b/epicardium/modules/stream.c new file mode 100644 index 0000000000000000000000000000000000000000..63e63070e6c8c6aaf06386506edb9f6acd13582b --- /dev/null +++ b/epicardium/modules/stream.c @@ -0,0 +1,74 @@ +#include <string.h> + +#include "epicardium.h" +#include "stream.h" + +static struct stream_info *stream_table[SD_MAX]; + +int stream_init() +{ + memset(stream_table, 0x00, sizeof(stream_table)); + return 0; +} + +int stream_register(int sd, struct stream_info *stream) +{ + if (sd < 0 || sd >= SD_MAX) { + return -EINVAL; + } + + if (stream_table[sd] != NULL) { + /* Stream already registered */ + return -EACCES; + } + + stream_table[sd] = stream; + return 0; +} + +int stream_deregister(int sd, struct stream_info *stream) +{ + if (sd < 0 || sd >= SD_MAX) { + return -EINVAL; + } + + if (stream_table[sd] != stream) { + /* Stream registered by someone else */ + return -EACCES; + } + + stream_table[sd] = NULL; + return 0; +} + +int epic_stream_read(int sd, void *buf, size_t count) +{ + if (sd < 0 || sd >= SD_MAX) { + return -EBADF; + } + + struct stream_info *stream = stream_table[sd]; + if (stream == NULL) { + return -ENODEV; + } + + /* Poll the stream */ + int ret = stream->poll_stream(); + if (ret < 0) { + return ret; + } + + /* Check buffer sizing */ + if (count % stream->item_size != 0) { + return -EINVAL; + } + + size_t i; + for (i = 0; i < count; i += stream->item_size) { + if (!xQueueReceive(stream->queue, buf + i, 10)) { + break; + } + } + + return i / stream->item_size; +} diff --git a/epicardium/modules/stream.h b/epicardium/modules/stream.h new file mode 100644 index 0000000000000000000000000000000000000000..d5dd30794d2367320c2f9ef8e2112769b0add203 --- /dev/null +++ b/epicardium/modules/stream.h @@ -0,0 +1,59 @@ +#ifndef STREAM_H +#define STREAM_H + +#include <stddef.h> +#include <stdint.h> + +#include "FreeRTOS.h" +#include "queue.h" + +/** + * **Stream Descriptors**: + * + * All supported streams have to have a unique ID in this list. ``SD_MAX`` + * must be greater than or equal to the highest defined ID. Please keep IDs + * in sequential order. + */ +enum stream_descriptor { + /** Highest descriptor must always be ``SD_MAX``. */ + SD_MAX, +}; + +struct stream_info { + QueueHandle_t queue; + size_t item_size; + int (*poll_stream)(); +}; + +/** + * Register a stream so it can be read from Epicardium API. + * + * :param int sd: Stream Descriptor. Must be from the above enum. + * :param stream_info stream: Stream info. + * :returns: ``0`` on success or a negative value on error. Possible errors: + * + * - ``-EINVAL``: Out of range sensor descriptor. + * - ``-EACCES``: Stream already registered. + */ +int stream_register(int sd, struct stream_info *stream); + +/** + * Deregister a stream. + * + * :param int sd: Stream Descriptor. + * :param stream_info stream: The stream which should be registered for the + * stream ``sd``. If a different stream is registered, this function + * will return an error. + * :returns: ``0`` on success or a negative value on error. Possible errors: + * + * - ``-EINVAL``: Out of range sensor descriptor. + * - ``-EACCES``: Stream ``stream`` was not registered for ``sd``. + */ +int stream_deregister(int sd, struct stream_info *stream); + +/* + * Initialize stream interface. Called by main(). + */ +int stream_init(); + +#endif /* STREAM_H */