From b89d81fdba697f993328caeff71e27e5c897e17c Mon Sep 17 00:00:00 2001
From: Kaspar Schleiser <kaspar@schleiser.de>
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 <kaspar@schleiser.de>
+ *
+ * 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 <kaspar@schleiser.de>
+ *
+ */
+
+#ifndef ISRPIPE_H
+#define ISRPIPE_H
+
+#include <stdint.h>
+
+#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 <kaspar@schleiser.de>
+ *
+ * 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 <kaspar@schleiser.de>
+ *
+ * @}
+ */
+
+#include <errno.h>
+
+#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;
+}
-- 
GitLab