/* ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @file hal_buffers.c * @brief I/O Buffers code. * * @addtogroup HAL_BUFFERS * @details Buffers Queues are used when there is the need to exchange * fixed-length data buffers between ISRs and threads. * On the ISR side data can be exchanged only using buffers, * on the thread side data can be exchanged both using buffers and/or * using an emulation of regular byte queues. * There are several kind of buffers queues:
* - Input queue, unidirectional queue where the writer is the * ISR side and the reader is the thread side. * - Output queue, unidirectional queue where the writer is the * ISR side and the reader is the thread side. * - Full duplex queue, bidirectional queue. Full duplex queues * are implemented by pairing an input queue and an output queue * together. * . * @{ */ #include #include "hal.h" /*===========================================================================*/ /* Driver local definitions. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver local variables and types. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ /** * @brief Initializes an input buffers queue object. * * @param[out] ibqp pointer to the @p input_buffers_queue_t object * @param[in] suspended initial state of the queue * @param[in] bp pointer to a memory area allocated for buffers * @param[in] size buffers size * @param[in] n number of buffers * @param[in] infy callback called when a buffer is returned to the queue * @param[in] link application defined pointer * * @init */ void ibqObjectInit(input_buffers_queue_t *ibqp, bool suspended, uint8_t *bp, size_t size, size_t n, bqnotify_t infy, void *link) { osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= 2U)); osalThreadQueueObjectInit(&ibqp->waiting); ibqp->suspended = suspended; ibqp->bcounter = 0; ibqp->brdptr = bp; ibqp->bwrptr = bp; ibqp->btop = bp + ((size + sizeof (size_t)) * n); ibqp->bsize = size + sizeof (size_t); ibqp->bn = n; ibqp->buffers = bp; ibqp->ptr = NULL; ibqp->top = NULL; ibqp->notify = infy; ibqp->link = link; } /** * @brief Resets an input buffers queue. * @details All the data in the input buffers queue is erased and lost, any * waiting thread is resumed with status @p MSG_RESET. * @note A reset operation can be used by a low level driver in order to * obtain immediate attention from the high level layers. * * @param[in] ibqp pointer to the @p input_buffers_queue_t object * * @iclass */ void ibqResetI(input_buffers_queue_t *ibqp) { osalDbgCheckClassI(); ibqp->bcounter = 0; ibqp->brdptr = ibqp->buffers; ibqp->bwrptr = ibqp->buffers; ibqp->ptr = NULL; ibqp->top = NULL; osalThreadDequeueAllI(&ibqp->waiting, MSG_RESET); } /** * @brief Gets the next empty buffer from the queue. * @note The function always returns the same buffer if called repeatedly. * * @param[in] ibqp pointer to the @p input_buffers_queue_t object * @return A pointer to the next buffer to be filled. * @retval NULL if the queue is full. * * @iclass */ uint8_t *ibqGetEmptyBufferI(input_buffers_queue_t *ibqp) { osalDbgCheckClassI(); if (ibqIsFullI(ibqp)) { return NULL; } return ibqp->bwrptr + sizeof (size_t); } /** * @brief Posts a new filled buffer to the queue. * * @param[in] ibqp pointer to the @p input_buffers_queue_t object * @param[in] size used size of the buffer, cannot be zero * * @iclass */ void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size) { osalDbgCheckClassI(); osalDbgCheck((size > 0U) && (size <= (ibqp->bsize - sizeof (size_t)))); osalDbgAsser
/*
The MIT License (MIT)

Copyright (c) 2016 Fred Sundvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial_link/protocol/frame_router.h"
#include "serial_link/protocol/transport.h"
#include "serial_link/protocol/frame_validator.h"

static bool is_master;

void router_set_master(bool master) {
   is_master = master;
}

void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size){
    if (is_master) {
        if (link == DOWN_LINK) {
            transport_recv_frame(data[size-1], data, size - 1);
        }
    }
    else {
        if (link == UP_LINK) {
            if (data[size-1] & 1) {
                transport_recv_frame(0, data, size - 1);
            }
            data[size-1] >>= 1;
            validator_send_frame(DOWN_LINK, data, size);
        }
        else {
            data[size-1]++;
            validator_send_frame(UP_LINK, data, size);
        }
    }
}

void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
    if (destination == 0) {
        if (!is_master) {
            data[size] = 1;
            validator_send_frame(UP_LINK, data, size + 1);
        }
    }
    else {
        if (is_master) {
            data[size] = destination;
            validator_send_frame(DOWN_LINK, data, size + 1);
        }
    }
}
cified time expired. * @retval MSG_RESET if the queue has been reset or has been put in * suspended state. * * @api */ msg_t obqGetEmptyBufferTimeout(output_buffers_queue_t *obqp, sysinterval_t timeout) { msg_t msg; osalSysLock(); msg = obqGetEmptyBufferTimeoutS(obqp, timeout); osalSysUnlock(); return msg; } /** * @brief Gets the next empty buffer from the queue. * @note The function always acquires the same buffer if called repeatedly. * @post After calling the function the fields @p ptr and @p top are set * at beginning and end of the buffer data or @p NULL if the queue * is empty. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * @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 operation status. * @retval MSG_OK if a buffer has been acquired. * @retval MSG_TIMEOUT if the specified time expired. * @retval MSG_RESET if the queue has been reset or has been put in * suspended state. * * @sclass */ msg_t obqGetEmptyBufferTimeoutS(output_buffers_queue_t *obqp, sysinterval_t timeout) { osalDbgCheckClassS(); while (obqIsFullI(obqp)) { if (obqp->suspended) { return MSG_RESET; } msg_t msg = osalThreadEnqueueTimeoutS(&obqp->waiting, timeout); if (msg < MSG_OK) { return msg; } } osalDbgAssert(!obqIsFullI(obqp), "still full"); /* Setting up the "current" buffer and its boundary.*/ obqp->ptr = obqp->bwrptr + sizeof (size_t); obqp->top = obqp->bwrptr + obqp->bsize; return MSG_OK; } /** * @brief Posts a new filled buffer to the queue. * @note The object callback is called after releasing the buffer. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * @param[in] size used size of the buffer, cannot be zero * * @api */ void obqPostFullBuffer(output_buffers_queue_t *obqp, size_t size) { osalSysLock(); obqPostFullBufferS(obqp, size); osalSysUnlock(); } /** * @brief Posts a new filled buffer to the queue. * @note The object callback is called after releasing the buffer. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * @param[in] size used size of the buffer, cannot be zero * * @sclass */ void obqPostFullBufferS(output_buffers_queue_t *obqp, size_t size) { osalDbgCheckClassS(); osalDbgCheck((size > 0U) && (size <= (obqp->bsize - sizeof (size_t)))); osalDbgAssert(!obqIsFullI(obqp), "buffers queue full"); /* Writing size field in the buffer.*/ *((size_t *)obqp->bwrptr) = size; /* Posting the buffer in the queue.*/ obqp->bcounter--; obqp->bwrptr += obqp->bsize; if (obqp->bwrptr >= obqp->btop) { obqp->bwrptr = obqp->buffers; } /* No "current" buffer.*/ obqp->ptr = NULL; /* Notifying the buffer release.*/ if (obqp->notify != NULL) { obqp->notify(obqp); } } /** * @brief Output queue write with timeout. * @details This function writes a byte value to an output queue. If * the queue is full then the calling thread is suspended until a * new buffer is freed in the queue or a timeout occurs. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * @param[in] b byte value to be transferred * @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 A byte value from the queue. * @retval MSG_TIMEOUT if the specified time expired. * @retval MSG_RESET if the queue has been reset or has been put in * suspended state. * * @api */ msg_t obqPutTimeout(output_buffers_queue_t *obqp, uint8_t b, sysinterval_t timeout) { msg_t msg; osalSysLock(); /* This condition indicates that a new buffer must be acquired.*/ if (obqp->ptr == NULL) { msg = obqGetEmptyBufferTimeoutS(obqp, timeout); if (msg != MSG_OK) { osalSysUnlock(); return msg; } } /* Writing the byte to the buffer.*/ *obqp->ptr = b; obqp->ptr++; /* If the current buffer has been fully written then it is posted as full in the queue.*/ if (obqp->ptr >= obqp->top) { obqPostFullBufferS(obqp, obqp->bsize - sizeof (size_t)); } osalSysUnlock(); return MSG_OK; } /** * @brief Output queue write with timeout. * @details The function writes data from a buffer to an output queue. The * operation completes when the specified amount of data has been * transferred or after the specified timeout or if the queue has * been reset. * * @param[in] obqp pointer to the @p output_buffers_queue_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 * @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. * @retval 0 if a timeout occurred. * * @api */ size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp, size_t n, sysinterval_t timeout) { size_t w = 0; systime_t deadline; osalDbgCheck(n > 0U); osalSysLock(); /* Time window for the whole operation.*/ deadline = osalTimeAddX(osalOsGetSystemTimeX(), timeout); while (true) { size_t size; /* This condition indicates that a new buffer must be acquired.*/ if (obqp->ptr == NULL) { msg_t msg; /* TIME_INFINITE and TIME_IMMEDIATE are handled differently, no deadline.*/ if ((timeout == TIME_INFINITE) || (timeout == TIME_IMMEDIATE)) { msg = obqGetEmptyBufferTimeoutS(obqp, timeout); } else { sysinterval_t next_timeout = osalTimeDiffX(osalOsGetSystemTimeX(), deadline); /* Handling the case where the system time went past the deadline, in this case next becomes a very high number because the system time is an unsigned type.*/ if (next_timeout > timeout) { osalSysUnlock(); return w; } msg = obqGetEmptyBufferTimeoutS(obqp, next_timeout); } /* Anything except MSG_OK interrupts the operation.*/ if (msg != MSG_OK) { osalSysUnlock(); return w; } } /* Size of the space available in the current buffer.*/ size = (size_t)obqp->top - (size_t)obqp->ptr; if (size > (n - w)) { size = n - w; } /* Smaller chunks in order to not make the critical zone too long, this impacts throughput however.*/ if (size > 64U) { /* Giving the compiler a chance to optimize for a fixed size move.*/ memcpy(obqp->ptr, bp, 64U); bp += 64U; obqp->ptr += 64U; w += 64U; } else { memcpy(obqp->ptr, bp, size); bp += size; obqp->ptr += size; w += size; } /* Has the current data buffer been finished? if so then release it.*/ if (obqp->ptr >= obqp->top) { obqPostFullBufferS(obqp, obqp->bsize - sizeof (size_t)); } /* Giving a preemption chance.*/ osalSysUnlock(); if (w >= n) { return w; } osalSysLock(); } } /** * @brief Flushes the current, partially filled, buffer to the queue. * @note The notification callback is not invoked because the function * is meant to be called from ISR context. An operation status is * returned instead. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * @return The operation status. * @retval false if no new filled buffer has been posted to the queue. * @retval true if a new filled buffer has been posted to the queue. * * @iclass */ bool obqTryFlushI(output_buffers_queue_t *obqp) { osalDbgCheckClassI(); /* If queue is empty and there is a buffer partially filled and it is not being written.*/ if (obqIsEmptyI(obqp) && (obqp->ptr != NULL)) { size_t size = (size_t)obqp->ptr - ((size_t)obqp->bwrptr + sizeof (size_t)); if (size > 0U) { /* Writing size field in the buffer.*/ *((size_t *)obqp->bwrptr) = size; /* Posting the buffer in the queue.*/ obqp->bcounter--; obqp->bwrptr += obqp->bsize; if (obqp->bwrptr >= obqp->btop) { obqp->bwrptr = obqp->buffers; } /* No "current" buffer.*/ obqp->ptr = NULL; return true; } } return false; } /** * @brief Flushes the current, partially filled, buffer to the queue. * * @param[in] obqp pointer to the @p output_buffers_queue_t object * * @api */ void obqFlush(output_buffers_queue_t *obqp) { osalSysLock(); /* If there is a buffer partially filled and not being written.*/ if (obqp->ptr != NULL) { size_t size = ((size_t)obqp->ptr - (size_t)obqp->bwrptr) - sizeof (size_t); if (size > 0U) { obqPostFullBufferS(obqp, size); } } osalSysUnlock(); } /** @} */