diff --git a/sys/include/tsrb.h b/sys/include/tsrb.h
new file mode 100644
index 0000000000000000000000000000000000000000..989acbbe31230dd24d077106ddc910191419cc4c
--- /dev/null
+++ b/sys/include/tsrb.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+/**
+ * @defgroup    sys_tsrb Thread safe ringbuffer
+ * @ingroup     sys
+ * @{
+ */
+
+/**
+ * @file
+ * @brief       Thread-safe ringbuffer implementation
+ *
+ * This ringbuffer implementation can be used without locking if
+ * there's only one producer and one consumer.
+ *
+ * @note Buffer size must be a power of two!
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#ifndef TSRB_H
+#define TSRB_H
+
+#include <assert.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief     thread-safe ringbuffer struct
+ */
+typedef struct tsrb {
+    char *buf;                  /**< Buffer to operate on. */
+    unsigned int size;          /**< Size of buf. */
+    volatile unsigned reads;    /**< total number of reads */
+    volatile unsigned writes;   /**< total number of writes */
+} tsrb_t;
+
+/**
+ * @brief Static initializer
+ */
+#define TSRB_INIT(BUF) { (BUF), sizeof (BUF), 0, 0 }
+
+/**
+ * @brief        Initialize a tsrb.
+ * @param[out]   rb        Datum to initialize.
+ * @param[in]    buffer    Buffer to use by tsrb.
+ * @param[in]    bufsize   `sizeof (buffer)`
+ */
+static inline void tsrb_init(tsrb_t *rb, char *buffer, unsigned bufsize)
+{
+    /* make sure bufsize is a power of two.
+     * http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/
+     */
+    assert((bufsize != 0) && ((bufsize & (~bufsize + 1)) == bufsize));
+
+    rb->buf = buffer;
+    rb->size = bufsize;
+    rb->reads = 0;
+    rb->writes = 0;
+}
+
+/**
+ * @brief       Test if the tsrb is empty.
+ * @param[in]   rb  Ringbuffer to operate on
+ * @return      0   if not empty
+ * @return      1   otherwise
+ */
+static inline int tsrb_empty(const tsrb_t *rb)
+{
+    return (rb->reads == rb->writes);
+}
+
+
+/**
+ * @brief       Get number of bytes available for reading
+ * @param[in]   rb  Ringbuffer to operate on
+ * @return      nr of available bytes
+ */
+static inline unsigned int tsrb_avail(const tsrb_t *rb)
+{
+    return (rb->writes - rb->reads);
+}
+
+/**
+ * @brief       Test if the tsrb is full
+ * @param[in]   rb  Ringbuffer to operate on
+ * @return      0   if not full
+ * @return      1   otherwise
+ */
+static inline int tsrb_full(const tsrb_t *rb)
+{
+    return (rb->writes - rb->reads) == rb->size;
+}
+
+/**
+ * @brief       Get free space in ringbuffer
+ * @param[in]   rb  Ringbuffer to operate on
+ * @return      nr of available bytes
+ */
+static inline unsigned int tsrb_free(const tsrb_t *rb)
+{
+    return (rb->size - rb->writes + rb->reads);
+}
+
+/**
+ * @brief       Get a byte from ringbuffer
+ * @param[in]   rb  Ringbuffer to operate on
+ * @return      >=0 byte that has been read
+ * @return      -1  if no byte available
+ */
+int tsrb_get_one(tsrb_t *rb);
+
+/**
+ * @brief       Get bytes from ringbuffer
+ * @param[in]   rb  Ringbuffer to operate on
+ * @param[out]  dst buffer to write to
+ * @param[in]   n   max number of bytes to write to @p dst
+ * @return      nr of bytes written to @p dst
+ */
+int tsrb_get(tsrb_t *rb, char *dst, size_t n);
+
+/**
+ * @brief       Add a byte to ringbuffer
+ * @param[in]   rb  Ringbuffer to operate on
+ * @param[in]   c   Character to add to ringbuffer
+ * @return      0   on success
+ * @return      -1  if no space available
+ */
+int tsrb_add_one(tsrb_t *rb, char c);
+
+/**
+ * @brief       Add bytes to ringbuffer
+ * @param[in]   rb  Ringbuffer to operate on
+ * @param[in]   src buffer to read from
+ * @param[in]   n   max number of bytes to read from @p src
+ * @return      nr of bytes read from @p src
+ */
+int tsrb_add(tsrb_t *rb, const char *src, size_t n);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+#endif /* TSRB_H */
diff --git a/sys/tsrb/Makefile b/sys/tsrb/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/sys/tsrb/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/tsrb/tsrb.c b/sys/tsrb/tsrb.c
new file mode 100644
index 0000000000000000000000000000000000000000..334550fa4af682ab01c6aa95f1d69c3269204e24
--- /dev/null
+++ b/sys/tsrb/tsrb.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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       thread-safe ringbuffer implementation
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * @}
+ */
+
+#include "tsrb.h"
+
+static void _push(tsrb_t *rb, char c)
+{
+    rb->buf[rb->writes++ & (rb->size - 1)] = c;
+}
+
+static char _pop(tsrb_t *rb)
+{
+    return rb->buf[rb->reads++ & (rb->size - 1)];
+}
+
+int tsrb_get_one(tsrb_t *rb)
+{
+    if (!tsrb_empty(rb)) {
+        return _pop(rb);
+    }
+    else {
+        return -1;
+    }
+}
+
+int tsrb_get(tsrb_t *rb, char *dst, size_t n)
+{
+    size_t tmp = n;
+    while (tmp && !tsrb_empty(rb)) {
+        *dst++ = _pop(rb);
+        tmp--;
+    }
+    return (n - tmp);
+}
+
+int tsrb_add_one(tsrb_t *rb, char c)
+{
+    if (!tsrb_full(rb)) {
+        _push(rb, c);
+        return 0;
+    }
+    else {
+        return -1;
+    }
+}
+
+int tsrb_add(tsrb_t *rb, const char *src, size_t n)
+{
+    size_t tmp = n;
+    while (tmp && !tsrb_full(rb)) {
+        _push(rb, *src++);
+        tmp--;
+    }
+    return (n - tmp);
+}