From b89d81fdba697f993328caeff71e27e5c897e17c Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 7 Dec 2016 20:32:00 +0100 Subject: [PATCH] sys: add isrpipe --- Makefile.dep | 4 ++ sys/include/isrpipe.h | 113 ++++++++++++++++++++++++++++++++++++++++++ sys/isrpipe/Makefile | 1 + sys/isrpipe/isrpipe.c | 104 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 sys/include/isrpipe.h create mode 100644 sys/isrpipe/Makefile create mode 100644 sys/isrpipe/isrpipe.c diff --git a/Makefile.dep b/Makefile.dep index 65411f6db7..5ae32e1a37 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -376,6 +376,10 @@ ifneq (,$(filter uart_stdio,$(USEMODULE))) USEMODULE += tsrb endif +ifneq (,$(filter isrpipe,$(USEMODULE))) + USEMODULE += tsrb +endif + ifneq (,$(filter posix,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/sys/include/isrpipe.h b/sys/include/isrpipe.h new file mode 100644 index 0000000000..3d4590b07f --- /dev/null +++ b/sys/include/isrpipe.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys + * @{ + * @file + * @brief ISR -> userspace pipe interface + * + * @author Kaspar Schleiser + * + */ + +#ifndef ISRPIPE_H +#define ISRPIPE_H + +#include + +#include "mutex.h" +#include "tsrb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Context structure for isrpipe + */ +typedef struct { + mutex_t mutex; /**< isrpipe mutex */ + tsrb_t tsrb; /**< isrpipe thread safe ringbuffer */ +} isrpipe_t; + +/** + * @brief Static initializer for irspipe + */ +#define ISRPIPE_INIT(tsrb_buf) { .mutex = MUTEX_INIT, .tsrb = TSRB_INIT(tsrb_buf) } + +/** + * @brief Initialisation function for isrpipe + * + * @param[in] isrpipe isrpipe object to initialize + * @param[in] buf buffer to use as ringbuffer (must be power of two sized!) + * @param[in] bufsize size of @p buf + */ +void isrpipe_init(isrpipe_t *isrpipe, char *buf, size_t bufsize); + +/** + * @brief Put one character into the isrpipe's buffer + * + * @param[in] isrpipe isrpipe object to initialize + * @param[in] c character to add to isrpipe buffer + * + * @returns 0 if character could be added + * @returns -1 if buffer was full + */ +int isrpipe_write_one(isrpipe_t *isrpipe, char c); + +/** + * @brief Read data from isrpipe (blocking) + * + * @param[in] isrpipe isrpipe object to operate on + * @param[in] buf buffer to write to + * @param[in] count number of bytes to read + * + * @returns number of bytes read + */ +int isrpipe_read(isrpipe_t *isrpipe, char *buf, size_t count); + +/** + * @brief Read data from isrpipe (with timeout, blocking) + * + * Currently, the timeout parameter is applied on every underlying read, which + * might be *per single byte*. + * + * @note This function might return less than @p count bytes + * + * @param[in] isrpipe isrpipe object to operate on + * @param[in] buf buffer to write to + * @param[in] count number of bytes to read + * @param[in] timeout timeout in ms + * + * @returns number of bytes read + * @returns -ETIMEDOUT on timeout + */ +int isrpipe_read_timeout(isrpipe_t *isrpipe, char *buf, size_t count, uint32_t timeout); + +/** + * @brief Read data from isrpipe (with timeout, blocking, wait until all read) + * + * This function is like @ref isrpipe_read_timeout, but will only return on + * timeout or when @p count bytes have been received. + * + * @param[in] isrpipe isrpipe object to operate on + * @param[in] buf buffer to write to + * @param[in] count number of bytes to read + * @param[in] timeout timeout in ms + * + * @returns number of bytes read + * @returns -ETIMEDOUT on timeout + */ +int isrpipe_read_all_timeout(isrpipe_t *isrpipe, char *buf, size_t count, uint32_t timeout); + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* ISRPIPE_H */ diff --git a/sys/isrpipe/Makefile b/sys/isrpipe/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/isrpipe/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/isrpipe/isrpipe.c b/sys/isrpipe/isrpipe.c new file mode 100644 index 0000000000..c9f6e1926a --- /dev/null +++ b/sys/isrpipe/isrpipe.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys + * @{ + * @file + * @brief ISR -> userspace pipe implementation + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "isrpipe.h" +#include "xtimer.h" + +void isrpipe_init(isrpipe_t *isrpipe, char *buf, size_t bufsize) +{ + mutex_init(&isrpipe->mutex); + tsrb_init(&isrpipe->tsrb, buf, bufsize); +} + +int isrpipe_write_one(isrpipe_t *isrpipe, char c) +{ + int res = tsrb_add_one(&isrpipe->tsrb, c); + + /* `res` is either 0 on success or -1 when the buffer is full. Either way, + * unlocking the mutex is fine. + */ + mutex_unlock(&isrpipe->mutex); + + return res; +} + +int isrpipe_read(isrpipe_t *isrpipe, char *buffer, size_t count) +{ + int res; + + while (!(res = tsrb_get(&isrpipe->tsrb, buffer, count))) { + mutex_lock(&isrpipe->mutex); + } + return res; +} + +typedef struct { + mutex_t *mutex; + int flag; +} _isrpipe_timeout_t; + +static void _cb(void *arg) +{ + _isrpipe_timeout_t *_timeout = (_isrpipe_timeout_t *) arg; + + _timeout->flag = 1; + mutex_unlock(_timeout->mutex); +} + +int isrpipe_read_timeout(isrpipe_t *isrpipe, char *buffer, size_t count, uint32_t timeout) +{ + int res; + + _isrpipe_timeout_t _timeout = { .mutex = &isrpipe->mutex, .flag = 0 }; + + xtimer_t timer = { .callback = _cb, .arg = &_timeout }; + + xtimer_set(&timer, timeout); + while (!(res = tsrb_get(&isrpipe->tsrb, buffer, count))) { + mutex_lock(&isrpipe->mutex); + if (_timeout.flag) { + res = -ETIMEDOUT; + break; + } + } + + xtimer_remove(&timer); + return res; +} + + +int isrpipe_read_all_timeout(isrpipe_t *isrpipe, char *buffer, size_t count, uint32_t timeout) +{ + char *pos = buffer; + + while (count) { + int res = isrpipe_read_timeout(isrpipe, pos, count, timeout); + if (res >= 0) { + count -= res; + pos += res; + } + else { + return res; + } + } + + return pos - buffer; +}