Add generic `char` pipe implementation
This commit is contained in:
parent
2c885bacaf
commit
0ab1b86e5f
|
@ -81,3 +81,7 @@ endif
|
|||
ifneq (,$(filter rgbled,$(USEMODULE)))
|
||||
USEMODULE += color
|
||||
endif
|
||||
|
||||
ifneq (,$(filter pipe,$(USEMODULE)))
|
||||
USEMODULE += lib
|
||||
endif
|
||||
|
|
|
@ -94,5 +94,8 @@ endif
|
|||
ifneq (,$(filter color,$(USEMODULE)))
|
||||
DIRS += color
|
||||
endif
|
||||
ifneq (,$(filter pipe,$(USEMODULE)))
|
||||
DIRS += pipe
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup sys
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @brief Generic pipe implementation.
|
||||
* @details This pipe implementation is a tight wrapper around a ringbuffer.
|
||||
* It sends the calling thread to sleep if the ringbuffer is full
|
||||
* or empty, respectively. It can be used in ISRs, too.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef __PIPE__H
|
||||
#define __PIPE__H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifndef PIPE_BUF
|
||||
# define PIPE_BUF (128) /**< Size of a dynamically malloc'd pipe. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A generic pipe.
|
||||
*/
|
||||
typedef struct riot_pipe
|
||||
{
|
||||
ringbuffer_t *rb; /**< Wrapped ringbuffer. */
|
||||
tcb_t *read_blocked; /**< A thread that wants to write to this full pipe. */
|
||||
tcb_t *write_blocked; /**< A thread that wants to read from this empty pipe. */
|
||||
void (*free)(void *); /**< Function to call by pipe_free(). Used like `pipe->free(pipe)`. */
|
||||
} pipe_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a pipe.
|
||||
* @param[out] pipe Datum to initialize.
|
||||
* @param rb Ringbuffer to use. Needs to be initialized!
|
||||
* @param free Function to call by pipe_free(). Used like `pipe->free(pipe)`.
|
||||
* Should be `NULL` for statically allocated pipes.
|
||||
*/
|
||||
void pipe_init(pipe_t *pipe, ringbuffer_t *rb, void (*free)(void *));
|
||||
|
||||
/**
|
||||
* @brief Read from a pipe.
|
||||
* @details Only one thread may access the pipe readingly at once.
|
||||
* If the pipe is empty, then the current thread is send sleeping.
|
||||
* It gets woken up once there is data ready in the pipe.
|
||||
* In an ISR (inISR()) 0 will returned if the pipe is empty.
|
||||
* @param[in] pipe Pipe to read from.
|
||||
* @param[out] buf Buffer to write into
|
||||
* @param n Size of buffer.
|
||||
* @returns `> 0` if data could be read.
|
||||
* `== 0` if the pipe is empty and isISR().
|
||||
*/
|
||||
ssize_t pipe_read(pipe_t *pipe, void *buf, size_t n);
|
||||
|
||||
/**
|
||||
* @brief Write to a pipe.
|
||||
* @details Only one thread may access the pipe writingly at once.
|
||||
* If the pipe is full, then the current thread is send sleeping.
|
||||
* It gets woken up once there is room again in the pipe.
|
||||
* In an ISR (inISR()) 0 will returned if the pipe is full.
|
||||
* @param[in] pipe Pipe to write to.
|
||||
* @param[out] buf Buffer to read from.
|
||||
* @param n Size of buffer.
|
||||
* @returns `> 0` if data could be written.
|
||||
* `== 0` if the pipe is full and isISR().
|
||||
*/
|
||||
ssize_t pipe_write(pipe_t *pipe, const void *buf, size_t n);
|
||||
|
||||
/**
|
||||
* @brief Dynamically allocate a pipe with room for `size` bytes.
|
||||
* @details This function uses `malloc()` and may break real-time behaviors.
|
||||
* Try not to use this function.
|
||||
* @param size Size of the underlying ringbuffer to allocate.
|
||||
* @returns Newly allocated pipe. NULL if the memory is exhausted.
|
||||
*/
|
||||
pipe_t *pipe_malloc(unsigned size);
|
||||
|
||||
/**
|
||||
* @brief Free a pipe.
|
||||
* @details On statically allocated pipes you do not have to call this function.
|
||||
* Most likely you will only need this function in junction with
|
||||
* pipe_malloc().
|
||||
* @param rp Pipe to free.
|
||||
*/
|
||||
void pipe_free(pipe_t *rp);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* @}
|
||||
*/
|
|
@ -0,0 +1,3 @@
|
|||
MODULE = pipe
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys
|
||||
* @{
|
||||
* @file
|
||||
* @brief Implementation for statically allocated pipes.
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "irq.h"
|
||||
#include "pipe.h"
|
||||
#include "sched.h"
|
||||
|
||||
typedef unsigned (*ringbuffer_op_t)(ringbuffer_t *restrict rb, char *buf, unsigned n);
|
||||
|
||||
static ssize_t pipe_rw(ringbuffer_t *rb,
|
||||
void *buf,
|
||||
size_t n,
|
||||
tcb_t **other_op_blocked,
|
||||
tcb_t **this_op_blocked,
|
||||
ringbuffer_op_t ringbuffer_op)
|
||||
{
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
unsigned old_state = disableIRQ();
|
||||
|
||||
unsigned count = ringbuffer_op(rb, buf, n);
|
||||
|
||||
if (count > 0) {
|
||||
tcb_t *other_thread = *other_op_blocked;
|
||||
int other_prio = -1;
|
||||
if (other_thread) {
|
||||
*other_op_blocked = NULL;
|
||||
other_prio = other_thread->priority;
|
||||
sched_set_status(other_thread, STATUS_PENDING);
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
|
||||
if (other_prio >= 0) {
|
||||
sched_switch(other_prio);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
else if (*this_op_blocked || inISR()) {
|
||||
restoreIRQ(old_state);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
*this_op_blocked = (tcb_t *) sched_active_thread;
|
||||
|
||||
sched_set_status((tcb_t *) sched_active_thread, STATUS_SLEEPING);
|
||||
restoreIRQ(old_state);
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t pipe_read(pipe_t *pipe, void *buf, size_t n)
|
||||
{
|
||||
return pipe_rw(pipe->rb, (char *) buf, n,
|
||||
&pipe->write_blocked, &pipe->read_blocked, ringbuffer_get);
|
||||
}
|
||||
|
||||
ssize_t pipe_write(pipe_t *pipe, const void *buf, size_t n)
|
||||
{
|
||||
return pipe_rw(pipe->rb, (char *) buf, n,
|
||||
&pipe->read_blocked, &pipe->write_blocked, (ringbuffer_op_t) ringbuffer_add);
|
||||
}
|
||||
|
||||
void pipe_init(pipe_t *pipe, ringbuffer_t *rb, void (*free)(void *))
|
||||
{
|
||||
*pipe = (pipe_t) {
|
||||
.rb = rb,
|
||||
.read_blocked = NULL,
|
||||
.write_blocked = NULL,
|
||||
.free = free,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys
|
||||
* @{
|
||||
* @file
|
||||
* @brief Implementation for dynamically allocated pipes.
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#include "pipe.h"
|
||||
|
||||
struct mallocd_pipe
|
||||
{
|
||||
pipe_t pipe;
|
||||
ringbuffer_t rb;
|
||||
char buffer[1];
|
||||
};
|
||||
|
||||
pipe_t *pipe_malloc(unsigned size)
|
||||
{
|
||||
struct mallocd_pipe *m_pipe = malloc(sizeof (*m_pipe) + size);
|
||||
if (m_pipe) {
|
||||
ringbuffer_init(&m_pipe->rb, m_pipe->buffer, size);
|
||||
pipe_init(&m_pipe->pipe, &m_pipe->rb, free);
|
||||
}
|
||||
return &m_pipe->pipe;
|
||||
}
|
||||
|
||||
void pipe_free(pipe_t *rp)
|
||||
{
|
||||
if (rp && rp->free) {
|
||||
rp->free(rp);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
APPLICATION = test_pipe
|
||||
include ../Makefile.tests_common
|
||||
|
||||
BOARD_INSUFFICIENT_RAM := stm32f0discovery
|
||||
|
||||
USEMODULE += pipe
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
* @file
|
||||
* @brief Test for module pipe.
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "flags.h"
|
||||
#include "kernel.h"
|
||||
#include "pipe.h"
|
||||
#include "pipe.h"
|
||||
|
||||
#define BYTES_TOTAL (26)
|
||||
|
||||
static char stacks[2][KERNEL_CONF_STACKSIZE_MAIN];
|
||||
|
||||
static char pipe_bufs[2][6];
|
||||
static ringbuffer_t rbs[2];
|
||||
|
||||
static pipe_t pipes[2];
|
||||
|
||||
static void *run_middle(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
unsigned read_total = 0;
|
||||
while (read_total < BYTES_TOTAL) {
|
||||
char buf[4];
|
||||
unsigned read = pipe_read(&pipes[0], buf, sizeof (buf));
|
||||
unsigned read_start = read_total;
|
||||
read_total += read;
|
||||
printf("Middle read: <%.*s> [%u:%u]\n", read, buf,
|
||||
read_start, read_total);
|
||||
|
||||
unsigned written_total = 0;
|
||||
while (written_total < read) {
|
||||
int written = pipe_write(&pipes[1], &buf[written_total],
|
||||
read - written_total);
|
||||
written_total += written;
|
||||
}
|
||||
}
|
||||
|
||||
puts("Middle done.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *run_end(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
unsigned read_total = 0;
|
||||
while (read_total < BYTES_TOTAL) {
|
||||
char buf[3];
|
||||
int read = pipe_read(&pipes[1], buf, sizeof (buf));
|
||||
unsigned read_start = read_total;
|
||||
read_total += read;
|
||||
printf("End read: <%.*s> [%u:%u]\n", read, buf,
|
||||
read_start, read_total);
|
||||
}
|
||||
|
||||
puts("End done.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned min(unsigned a, unsigned b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
puts("Start.");
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ringbuffer_init(&rbs[i], pipe_bufs[i], sizeof (pipe_bufs[i]));
|
||||
pipe_init(&pipes[i], &rbs[i], NULL);
|
||||
}
|
||||
|
||||
thread_create(stacks[0], sizeof (stacks[0]),
|
||||
PRIORITY_MAIN, CREATE_WOUT_YIELD | CREATE_STACKTEST,
|
||||
run_middle, NULL, "middle");
|
||||
thread_create(stacks[1], sizeof (stacks[1]),
|
||||
PRIORITY_MAIN, CREATE_WOUT_YIELD | CREATE_STACKTEST,
|
||||
run_end, NULL, "end");
|
||||
|
||||
unsigned total = 0;
|
||||
while (total < BYTES_TOTAL) {
|
||||
char buf[5];
|
||||
unsigned bytes_cur = min(BYTES_TOTAL - total, sizeof (buf));
|
||||
for (unsigned i = 0; i < bytes_cur; ++i) {
|
||||
buf[i] = 'A' + total;
|
||||
++total;
|
||||
}
|
||||
|
||||
unsigned written_total = 0;
|
||||
while (written_total < bytes_cur) {
|
||||
int written = pipe_write(&pipes[0], &buf[written_total],
|
||||
bytes_cur - written_total);
|
||||
written_total += written;
|
||||
}
|
||||
}
|
||||
|
||||
puts("Main done.");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue