/* ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. This file is part of ChibiOS. ChibiOS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. ChibiOS 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file chpipes.c * @brief Pipes code. * @details Byte pipes. *

Operation mode

* A pipe is an asynchronous communication mechanism.
* Operations defined for mailboxes: * - Write: Writes a buffer of data in the pipe in FIFO order. * - Read: A buffer of data is read from the read and removed. * - Reset: The pipe is emptied and all the stored data * is lost. * . * @pre In order to use the pipes APIs the @p CH_CFG_USE_PIPES * option must be enabled in @p chconf.h. * @note Compatible with RT and NIL. * * @addtogroup oslib_pipes * @{ */ #include #include "ch.h" #if (CH_CFG_USE_PIPES == TRUE) || defined(__DOXYGEN__) /*===========================================================================*/ /* Module local definitions. */ /*===========================================================================*/ /* * Defaults on the best synchronization mechanism available. */ #if (CH_CFG_USE_MUTEXES == TRUE) || defined(__DOXYGEN__) #define PC_INIT(p) chMtxObjectInit(&(p)->cmtx) #define PC_LOCK(p) chMtxLock(&(p)->cmtx) #define PC_UNLOCK(p) chMtxUnlock(&(p)->cmtx) #define PW_INIT(p) chMtxObjectInit(&(p)->wmtx) #define PW_LOCK(p) chMtxLock(&(p)->wmtx) #define PW_UNLOCK(p) chMtxUnlock(&(p)->wmtx) #define PR_INIT(p) chMtxObjectInit(&(p)->rmtx) #define PR_LOCK(p) chMtxLock(&(p)->rmtx) #define PR_UNLOCK(p) chMtxUnlock(&(p)->rmtx) #else #define PC_INIT(p) chSemObjectInit(&(p)->csem, (cnt_t)1) #define PC_LOCK(p) (void) chSemWait(&(p)->csem) #define PC_UNLOCK(p) chSemSignal(&(p)->csem) #define PW_INIT(p) chSemObjectInit(&(p)->wsem, (cnt_t)1) #define PW_LOCK(p) (void) chSemWait(&(p)->wsem) #define PW_UNLOCK(p) chSemSignal(&(p)->wsem) #define PR_INIT(p) chSemObjectInit(&(p)->rsem, (cnt_t)1) #define PR_LOCK(p) (void) chSemWait(&(p)->rsem) #define PR_UNLOCK(p) chSemSignal(&(p)->rsem) #endif /*===========================================================================*/ /* Module exported variables. */ /*===========================================================================*/ /*===========================================================================*/ /* Module local types. */ /*===========================================================================*/ /*===========================================================================*/ /* Module local variables. */ /*===========================================================================*/ /*===========================================================================*/ /* Module local functions. */ /*===========================================================================*/ /** * @brief Non-blocking pipe write. * @details The function writes data from a buffer to a pipe. The * operation completes when the specified amount of data has been * transferred or when the pipe buffer has been filled. * * @param[in] pp the pointer to an initialized @p pipe_t object * @param[in] bp pointer to the data buffer * @param[in] n the maximum amount of data to be transferred, the * value 0 is reserved * @return The number of bytes effectively transferred. * * @notapi */ static size_t pipe_write(pipe_t *pp, const uint8_t *bp, size_t n) { size_t s1, s2; PC_LOCK(pp); /* Number of bytes that can be written in a single atomic operation.*/ if (n > chPipeGetFreeCount(pp)) { n = chPipeGetFreeCount(pp); } pp->cnt += n; /* Number of bytes before buffer limit.*/ /*lint -save -e9033 [10.8] Checked to be safe.*/ s1 = (size_t)(pp->top - pp->wrptr); /*lint -restore*/ if (n < s1) { memcpy((void *)pp->wrptr, (const void *)bp, n); pp->wrptr += n; } else if (n > s1) { memcpy((void *)pp->wrptr, (const void *)bp, s1); bp += s1; s2 = n - s1; memcpy((void *)pp->buffer, (const void *)bp, s2); pp->wrptr = pp->buffer + s2; } else { /* n == s1 */ memcpy((void *)pp->wrptr, (const void *)bp, n); pp->wrptr = pp->buffer; } PC_UNLOCK(pp); return n; } /** * @brief Non-blocking pipe read. * @details The function reads data from a pipe into a buffer. The * operation completes when the specified amount of data has been * transferred or when the pipe buffer has been emptied. * * @param[in] pp the pointer to an initialized @p pipe_t object * @param[out] bp pointer to the data buffer * @param[in] n the maximum amount of data to be transferred, the * value 0 is reserved * @return The number of bytes effectively transferred. * * @notapi */ static size_t pipe_read(pipe_t *pp, uint8_t *bp, size_t n) { size_t s1, s2; PC_LOCK(pp); /* Number of bytes that can be read in a single atomic operation.*/ if (n > chPipeGetUsedCount(pp)) { n = chPipeGetUsedCount(pp); } pp->cnt -= n; /* Number of bytes before buffer limit.*/ /*lint -save -e9033 [10.8] Checked to be safe.*/ s1 = (size_t)(pp->top - pp->rdptr); /*lint -restore*/ if (n < s1) { memcpy((void *)bp, (void *)pp->rdptr, n); pp->rdptr += n; } else if (n > s1) { memcpy((void *)bp, (void *)pp->rdptr, s1); bp += s1; s2 = n - s1; memcpy((void *)bp, (void *)pp->buffer, s2); pp->rdptr = pp->buffer + s2; } else { /* n == s1 */ memcpy((void *)bp, (void *)pp->rdptr, n); pp->rdptr = pp->buffer; } PC_UNLOCK(pp); return n; } /*===========================================================================*/ /* Module exported functions. */ /*===========================================================================*/ /** * @brief Initializes a @p mailbox_t object. * * @param[out] pp the pointer to the @p pipe_t structure to be * initialized * @param[in] buf pointer to the pipe buffer as an array of @p uint8_t * @param[in] n number of elements in the buffer array * * @init */ void chPipeObjectInit(pipe_t *pp, uint8_t *buf, size_t n) { chDbgCheck((pp != NULL) && (buf != NULL) && (n > (size_t)0)); pp->buffer = buf; pp->rdptr = buf; pp->wrptr = buf; pp->top = &buf[n]; pp->cnt = (size_t)0; pp->reset = false; pp->wtr = NULL; pp->rtr = NULL; PC_INIT(pp); PW_INIT(pp); PR_INIT(pp); } /** * @brief Resets a @p pipe_t object. * @details All the waiting threads are resumed with status @p MSG_RESET and * the queued data is lost. * @post The pipe is in reset state, all operations will fail and * return @p MSG_RESET until the mailbox is enabled again using * @p chPipeResumeX(). * * @param[in] pp the pointer to an initialized @p pipe_t object * * @api */ void chPipeReset(pipe_t *pp) { chDbgCheck(pp != NULL); PC_LOCK(pp); pp->wrptr = pp->buffer; pp->rdptr = pp->buffer; pp->cnt = (size_t)0; pp->reset = true; chSysLock(); chThdResumeI(&pp->wtr, MSG_RESET); chThdResumeI(&pp->rtr, MSG_RESET); chSchRescheduleS(); chSysUnlock(); PC_UNLOCK(pp); } /** * @brief Pipe write with timeout. * @details The function writes data from a buffer to a pipe. The * operation completes when the specified amount of data has been * transferred or after the specified timeout or if the pipe has * been reset. * * @param[in] pp the pointer to an initialized @p pipe_t object * @param[in] bp pointer to the data buffer * @param[in] n the number of bytes to be written, the value 0 is * reserved * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_IMMEDIATE immediate timeout. * - @a TIME_INFINITE no timeout. * . * @return The number of bytes effectively transferred. A number * lower than @p n means that a timeout occurred or the * pipe went in reset state. * * @api */ size_t chPipeWriteTimeout(pipe_t *pp, const uint8_t *bp, size_t n, sysinterval_t timeout) { size_t max = n; chDbgCheck(n > 0U); /* If the pipe is in reset state then returns immediately.*/ if (pp->reset) { return (size_t)0; } PW_LOCK(pp); while (n > 0U) { size_t done; done = pipe_write(pp, bp, n); if (done == (size_t)0) { msg_t msg; chSysLock(); msg = chThdSuspendTimeoutS(&pp->wtr, timeout); chSysUnlock(); /* Anything except MSG_OK causes the operation to stop.*/ if (msg != MSG_OK) { break; } } else { n -= done; bp += done; /* Resuming the reader, if present.*/ chThdResume(&pp->rtr, MSG_OK); } } PW_UNLOCK(pp); return max - n; } /** * @brief Pipe read with timeout. * @details The function reads data from a pipe into a buffer. The * operation completes when the specified amount of data has been * transferred or after the specified timeout or if the pipe has * been reset. * * @param[in] pp the pointer to an initialized @p pipe_t object * @param[out] bp pointer to the data buffer * @param[in] n the number of bytes to be read, the value 0 is * reserved * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_IMMEDIATE immediate timeout. * - @a TIME_INFINITE no timeout. * . * @return The number of bytes effectively transferred. A number * lower than @p n means that a timeout occurred or the * pipe went in reset state. * * @api */ size_t chPipeReadTimeout(pipe_t *pp, uint8_t *bp, size_t n, sysinterval_t timeout) { size_t max = n; chDbgCheck(n > 0U); /* If the pipe is in reset state then returns immediately.*/ if (pp->reset) { return (size_t)0; } PR_LOCK(pp); while (n > 0U) { size_t done; done = pipe_read(pp, bp, n); if (done == (size_t)0) { msg_t msg; chSysLock(); msg = chThdSuspendTimeoutS(&pp->rtr, timeout); chSysUnlock(); /* Anything except MSG_OK causes the operation to stop.*/ if (msg != MSG_OK) { break; } } else { n -= done; bp += done; /* Resuming the writer, if present.*/ chThdResume(&pp->wtr, MSG_OK); } } PR_UNLOCK(pp); return max - n; } #endif /* CH_CFG_USE_MAILBOXES == TRUE */ /** @} */