diff --git a/os/hal/include/hal_buffers.h b/os/hal/include/hal_buffers.h index a256f02e2..e5d5fa7b9 100644 --- a/os/hal/include/hal_buffers.h +++ b/os/hal/include/hal_buffers.h @@ -51,7 +51,7 @@ typedef struct io_buffers_queue io_buffers_queue_t; * * @param[in] iodbp the buffers queue pointer */ -typedef void (*dbnotify_t)(io_buffers_queue_t *bqp); +typedef void (*bqnotify_t)(io_buffers_queue_t *bqp); /** * @brief Structure of a generic buffers queue. @@ -104,7 +104,7 @@ struct io_buffers_queue { /** * @brief Data notification callback. */ - dbnotify_t notify; + bqnotify_t notify; /** * @brief Application defined field. */ @@ -171,7 +171,7 @@ typedef io_buffers_queue_t output_buffers_queue_t; #define bqGetLinkX(bqp) ((bqp)->link) /** - * @brief Evaluates to @p TRUE if the specified input buffered queue is empty. + * @brief Evaluates to @p TRUE if the specified input buffers queue is empty. * * @param[in] ibqp pointer to an @p input_buffers_queue_t structure * @return The queue status. @@ -183,7 +183,7 @@ typedef io_buffers_queue_t output_buffers_queue_t; #define ibqIsEmptyI(ibqp) ((bool)(bqSpaceI(ibqp) == 0U)) /** - * @brief Evaluates to @p TRUE if the specified input buffered queue is full. + * @brief Evaluates to @p TRUE if the specified input buffers queue is full. * * @param[in] ibqp pointer to an @p input_buffers_queue_t structure * @return The queue status. @@ -194,6 +194,31 @@ typedef io_buffers_queue_t output_buffers_queue_t; */ #define ibqIsFullI(ibqp) ((bool)(((ibqp)->bwrptr == (ibqp)->brdptr) && \ ((ibqp)->bcounter != 0U))) + +/** + * @brief Evaluates to @p true if the specified output buffers queue is empty. + * + * @param[in] obqp pointer to an @p output_buffers_queue_t structure + * @return The queue status. + * @retval false if the queue is not empty. + * @retval true if the queue is empty. + * + * @iclass + */ +#define obqIsEmptyI(oqp) ((bool)(((obqp)->bwrptr == (obqp)->brdptr) && \ + ((obqp)->bcounter != 0U))) + +/** + * @brief Evaluates to @p true if the specified output buffers queue is full. + * + * @param[in] obqp pointer to an @p output_buffers_queue_t structure + * @return The queue status. + * @retval false if the queue is not full. + * @retval true if the queue is full. + * + * @iclass + */ +#define obqIsFullI(obqp) ((bool)(bqSpaceI(obqp) == 0U)) /** @} */ /*===========================================================================*/ @@ -205,15 +230,30 @@ extern "C" { #endif void ibqObjectInit(io_buffers_queue_t *ibqp, uint8_t *bp, size_t size, size_t n, - dbnotify_t infy, void *link); + bqnotify_t infy, void *link); void ibqResetI(input_buffers_queue_t *ibqp); uint8_t *ibqGetEmptyBufferI(input_buffers_queue_t *ibqp); - void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size); - msg_t ibqGetDataTimeoutI(input_buffers_queue_t *ibqp, systime_t timeout); - void ibqReleaseDataI(input_buffers_queue_t *ibqp); + void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size); + msg_t ibqGetFullBufferTimeoutS(input_buffers_queue_t *ibqp, + systime_t timeout); + void ibqReleaseEmptyBufferI(input_buffers_queue_t *ibqp); msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, systime_t timeout); size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp, size_t n, systime_t timeout); + void obqObjectInit(output_buffers_queue_t *obqp, uint8_t *bp, + size_t size, size_t n, + bqnotify_t onfy, void *link); + void obqResetI(output_buffers_queue_t *obqp); + uint8_t *obqGetFullBufferI(output_buffers_queue_t *obqp, + size_t *sizep); + void obqReleaseEmptyBufferI(output_buffers_queue_t *obqp); + msg_t obqGetEmptyBufferTimeoutS(output_buffers_queue_t *obqp, + systime_t timeout); + void obqPostFullBufferI(output_buffers_queue_t *obqp, size_t size); + msg_t obqPutTimeout(output_buffers_queue_t *obqp, uint8_t b, + systime_t timeout); + size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp, + size_t n, systime_t timeout); #ifdef __cplusplus } #endif diff --git a/os/hal/include/serial_usb.h b/os/hal/include/serial_usb.h index 22e873ea1..fca8d56ea 100644 --- a/os/hal/include/serial_usb.h +++ b/os/hal/include/serial_usb.h @@ -121,6 +121,14 @@ #if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) #define SERIAL_USB_BUFFERS_SIZE 256 #endif + +/** + * @brief Serial over USB number of buffers. + * @note The default is 2 buffers. + */ +#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__) +#define SERIAL_USB_BUFFERS_NUMBER 2 +#endif /** @} */ /*===========================================================================*/ @@ -192,14 +200,16 @@ typedef struct { _base_asynchronous_channel_data \ /* Driver state.*/ \ sdustate_t state; \ - /* Input queue.*/ \ - input_queue_t iqueue; \ + /* Input buffers queue.*/ \ + input_buffers_queue_t ibqueue; \ /* Output queue.*/ \ - output_queue_t oqueue; \ + output_buffers_queue_t obqueue; \ /* Input buffer.*/ \ - uint8_t ib[SERIAL_USB_BUFFERS_SIZE]; \ + uint8_t ib[BQ_BUFFER_SIZE(SERIAL_USB_BUFFERS_NUMBER, \ + SERIAL_USB_BUFFERS_SIZE)]; \ /* Output buffer.*/ \ - uint8_t ob[SERIAL_USB_BUFFERS_SIZE]; \ + uint8_t ob[BQ_BUFFER_SIZE(SERIAL_USB_BUFFERS_NUMBER, \ + SERIAL_USB_BUFFERS_SIZE)]; \ /* End of the mandatory fields.*/ \ /* Current configuration data.*/ \ const SerialUSBConfig *config; diff --git a/os/hal/src/hal_buffers.c b/os/hal/src/hal_buffers.c index 85a26383b..8a468d9fe 100644 --- a/os/hal/src/hal_buffers.c +++ b/os/hal/src/hal_buffers.c @@ -49,10 +49,9 @@ /** * @brief Initializes an input buffers queue object. * - * @param[out] ibqp pointer to the @p io_buffers_queue_t object + * @param[out] ibqp pointer to the @p input_buffers_queue_t object * @param[in] bp pointer to a memory area allocated for buffers - * @param[in] size buffer size, including the size field which is the - * size of a @p size_t + * @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 @@ -61,16 +60,16 @@ */ void ibqObjectInit(input_buffers_queue_t *ibqp, uint8_t *bp, size_t size, size_t n, - dbnotify_t infy, void *link) { + bqnotify_t infy, void *link) { - osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= sizeof(size_t) + 2)); + osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= 2)); osalThreadQueueObjectInit(&ibqp->waiting); ibqp->bcounter = 0; ibqp->brdptr = bp; ibqp->bwrptr = bp; - ibqp->btop = bp + (size * n); /* Pre-calculated for speed. */ - ibqp->bsize = size; + ibqp->btop = bp + ((size + sizeof (size_t)) * n); + ibqp->bsize = size + sizeof (size_t); ibqp->bn = n; ibqp->buffers = bp; ibqp->ptr = NULL; @@ -124,14 +123,14 @@ uint8_t *ibqGetEmptyBufferI(input_buffers_queue_t *ibqp) { } /** - * @brief Posts a new filled buffer in the queue. + * @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 * * @iclass */ -void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) { +void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size) { osalDbgCheckClassI(); osalDbgAssert(!ibqIsFullI(ibqp), "buffers queue full"); @@ -151,7 +150,7 @@ void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) { } /** - * @brief Gets the next data-filled buffer from the queue. + * @brief Gets the next filled 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 @NULL if the queue @@ -168,11 +167,12 @@ void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) { * @retval MSG_TIMEOUT if the specified time expired. * @retval MSG_RESET if the queue has been reset. * - * @iclass + * @sclass */ -msg_t ibqGetDataTimeoutI(input_buffers_queue_t *ibqp, systime_t timeout) { +msg_t ibqGetFullBufferTimeoutS(input_buffers_queue_t *ibqp, + systime_t timeout) { - osalDbgCheckClassI(); + osalDbgCheckClassS(); while (ibqIsEmptyI(ibqp)) { msg_t msg = osalThreadEnqueueTimeoutS(&ibqp->waiting, timeout); @@ -189,14 +189,14 @@ msg_t ibqGetDataTimeoutI(input_buffers_queue_t *ibqp, systime_t timeout) { } /** - * @brief Releases the data buffer back in the queue. + * @brief Releases the buffer back in the queue. * @note The object callback is called after releasing the buffer. * - * @param[out] ibqp pointer to the @p input_buffers_queue_t object + * @param[in] ibqp pointer to the @p input_buffers_queue_t object * * @iclass */ -void ibqReleaseDataI(input_buffers_queue_t *ibqp) { +void ibqReleaseEmptyBufferI(input_buffers_queue_t *ibqp) { osalDbgCheckClassI(); osalDbgAssert(!ibqIsEmptyI(ibqp), "buffers queue empty"); @@ -238,10 +238,13 @@ void ibqReleaseDataI(input_buffers_queue_t *ibqp) { msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, systime_t timeout) { msg_t msg; + chSysLock(); + /* This condition indicates that a new buffer must be acquired.*/ if (ibqp->ptr == NULL) { - msg = ibqGetDataTimeoutI(ibqp, timeout); + msg = ibqGetFullBufferTimeoutS(ibqp, timeout); if (msg != MSG_OK) { + chSysUnlock(); return msg; } } @@ -253,9 +256,11 @@ msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, systime_t timeout) { /* If the current buffer has been fully read then it is returned as empty in the queue.*/ if (ibqp->ptr >= ibqp->top) { - ibqReleaseDataI(ibqp); + ibqReleaseEmptyBufferI(ibqp); } + chSysUnlock(); + return msg; } @@ -302,11 +307,13 @@ size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp, in this case next becomes a very high number because the system time is an unsigned type.*/ if (next_timeout > timeout) { + osalSysUnlock(); return MSG_TIMEOUT; } - msg_t msg = ibqGetDataTimeoutI(ibqp, next_timeout); + msg_t msg = ibqGetFullBufferTimeoutS(ibqp, next_timeout); if (msg != MSG_OK) { + osalSysUnlock(); return msg; } } @@ -330,7 +337,309 @@ size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp, /* Has the current data buffer been finished? if so then release it.*/ if (ibqp->ptr >= ibqp->top) { - ibqReleaseDataI(ibqp); + ibqReleaseEmptyBufferI(ibqp); + } + } + osalSysUnlock(); + + return r; +} + +/** + * @brief Initializes an output buffers queue object. + * + * @param[out] obqp pointer to the @p output_buffers_queue_t object + * @param[in] bp pointer to a memory area allocated for buffers + * @param[in] size buffers size + * @param[in] n number of buffers + * @param[in] onfy callback called when a buffer is posted in the queue + * @param[in] link application defined pointer + * + * @init + */ +void obqObjectInit(output_buffers_queue_t *obqp, uint8_t *bp, + size_t size, size_t n, + bqnotify_t onfy, void *link) { + + osalDbgCheck((obqp != NULL) && (bp != NULL) && (size >= 2)); + + osalThreadQueueObjectInit(&obqp->waiting); + obqp->bcounter = n; + obqp->brdptr = bp; + obqp->bwrptr = bp; + obqp->btop = bp + ((size + sizeof (size_t)) * n); + obqp->bsize = size + sizeof (size_t); + obqp->bn = n; + obqp->buffers = bp; + obqp->ptr = NULL; + obqp->top = NULL; + obqp->notify = onfy; + obqp->link = link; +} + +/** + * @brief Resets an output buffers queue. + * @details All the data in the output 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] obqp pointer to the @p output_buffers_queue_t object + * + * @iclass + */ +void obqResetI(output_buffers_queue_t *obqp) { + + osalDbgCheckClassI(); + + obqp->bcounter = bqSizeX(obqp); + obqp->brdptr = obqp->buffers; + obqp->bwrptr = obqp->buffers; + obqp->ptr = NULL; + obqp->top = NULL; + osalThreadDequeueAllI(&obqp->waiting, MSG_RESET); +} + +/** + * @brief Gets the next filled buffer from the queue. + * @note The function always returns the same buffer if called repeatedly. + * + * @param[in] obqp pointer to the @p output_buffers_queue_t object + * @param[out] sizep pointer to the filled buffer size + * @return A pointer to the filled buffer. + * @retval NULL if the queue is empty. + * + * @iclass + */ +uint8_t *obqGetFullBufferI(output_buffers_queue_t *obqp, + size_t *sizep) { + + osalDbgCheckClassI(); + + if (obqIsEmptyI(obqp)) { + return NULL; + } + + /* Buffer size.*/ + *sizep = *((size_t *)obqp->brdptr); + + return obqp->brdptr + sizeof (size_t); +} + +/** + * @brief Releases the next filled buffer back in the queue. + * + * @param[in] obqp pointer to the @p output_buffers_queue_t object + * + * @iclass + */ +void obqReleaseEmptyBufferI(output_buffers_queue_t *obqp) { + + osalDbgCheckClassI(); + osalDbgAssert(!obqIsEmptyI(obqp), "buffers queue empty"); + + /* Freeing a buffer slot in the queue.*/ + obqp->bcounter++; + obqp->brdptr += obqp->bsize; + if (obqp->brdptr >= obqp->btop) { + obqp->brdptr = obqp->buffers; + } +} + +/** + * @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 @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. + * + * @sclass + */ +msg_t obqGetEmptyBufferTimeoutS(output_buffers_queue_t *obqp, + systime_t timeout) { + + osalDbgCheckClassS(); + + while (obqIsEmptyI(obqp)) { + msg_t msg = osalThreadEnqueueTimeoutS(&obqp->waiting, timeout); + if (msg < MSG_OK) { + return msg; + } + } + + /* Setting up the "current" buffer and its boundary.*/ + obqp->ptr = obqp->brdptr + sizeof (size_t); + obqp->top = obqp->ptr + *((size_t *)obqp->brdptr); + + 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 + * + * @iclass + */ +void obqPostFullBufferI(output_buffers_queue_t *obqp, size_t size) { + + osalDbgCheckClassI(); + 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] 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. + * + * @api + */ +msg_t obqPutTimeout(output_buffers_queue_t *obqp, uint8_t b, + systime_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) { + chSysUnlock(); + return msg; + } + } + + /* Writing the byte to the buffer.*/ + obqp->bcounter--; + *obqp->bwrptr++ = b; + + /* If the current buffer has been fully written then it is posted as + full in the queue.*/ + if (obqp->ptr >= obqp->top) { + obqPostFullBufferI(obqp, obqp->bsize); + } + + 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. + * @note The function is not atomic, if you need atomicity it is suggested + * to use a semaphore or a mutex for mutual exclusion. + * + * @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. + * + * @api + */ +size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp, + size_t n, systime_t timeout) { + size_t r = 0; + systime_t deadline; + + osalSysLock(); + + /* Time window for the whole operation.*/ + deadline = osalOsGetSystemTimeX() + timeout; + + while (r < n) { + size_t size; + + /* This condition indicates that a new buffer must be acquired.*/ + if (obqp->ptr == NULL) { + systime_t next_timeout = deadline - osalOsGetSystemTimeX(); + + /* 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 MSG_TIMEOUT; + } + + msg_t msg = obqGetEmptyBufferTimeoutS(obqp, next_timeout); + if (msg != MSG_OK) { + osalSysUnlock(); + return msg; + } + } + + /* Size of the space available in the current buffer.*/ + size = obqp->top - obqp->ptr; + if (size > n - r) { + size = n - r; + } + + /* Copying the chunk into the read buffer, the operation is performed + outside the critical zone.*/ + osalSysUnlock(); + memcpy(obqp->ptr, bp, size); + osalSysLock(); + + /* Updating the pointers and the counter.*/ + r += size; + bp += size; + obqp->ptr += size; + + /* Has the current data buffer been finished? if so then release it.*/ + if (obqp->ptr >= obqp->top) { + obqPostFullBufferI(obqp, obqp->bsize); } } osalSysUnlock(); diff --git a/os/hal/src/serial_usb.c b/os/hal/src/serial_usb.c index c32a17376..b22599be2 100644 --- a/os/hal/src/serial_usb.c +++ b/os/hal/src/serial_usb.c @@ -59,8 +59,8 @@ static size_t write(void *ip, const uint8_t *bp, size_t n) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return 0; - return oqWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp, - n, TIME_INFINITE); + return obqWriteTimeout(&((SerialUSBDriver *)ip)->obqueue, bp, + n, TIME_INFINITE); } static size_t read(void *ip, uint8_t *bp, size_t n) { @@ -68,8 +68,8 @@ static size_t read(void *ip, uint8_t *bp, size_t n) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return 0; - return iqReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp, - n, TIME_INFINITE); + return ibqReadTimeout(&((SerialUSBDriver *)ip)->ibqueue, bp, + n, TIME_INFINITE); } static msg_t put(void *ip, uint8_t b) { @@ -77,7 +77,7 @@ static msg_t put(void *ip, uint8_t b) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return MSG_RESET; - return oqPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, TIME_INFINITE); + return obqPutTimeout(&((SerialUSBDriver *)ip)->obqueue, b, TIME_INFINITE); } static msg_t get(void *ip) { @@ -85,7 +85,7 @@ static msg_t get(void *ip) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return MSG_RESET; - return iqGetTimeout(&((SerialUSBDriver *)ip)->iqueue, TIME_INFINITE); + return ibqGetTimeout(&((SerialUSBDriver *)ip)->ibqueue, TIME_INFINITE); } static msg_t putt(void *ip, uint8_t b, systime_t timeout) { @@ -93,7 +93,7 @@ static msg_t putt(void *ip, uint8_t b, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return MSG_RESET; - return oqPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, timeout); + return obqPutTimeout(&((SerialUSBDriver *)ip)->obqueue, b, timeout); } static msg_t gett(void *ip, systime_t timeout) { @@ -101,7 +101,7 @@ static msg_t gett(void *ip, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return MSG_RESET; - return iqGetTimeout(&((SerialUSBDriver *)ip)->iqueue, timeout); + return ibqGetTimeout(&((SerialUSBDriver *)ip)->ibqueue, timeout); } static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t timeout) { @@ -109,7 +109,7 @@ static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return 0; - return oqWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp, n, timeout); + return obqWriteTimeout(&((SerialUSBDriver *)ip)->obqueue, bp, n, timeout); } static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t timeout) { @@ -117,7 +117,7 @@ static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) return 0; - return iqReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp, n, timeout); + return ibqReadTimeout(&((SerialUSBDriver *)ip)->ibqueue, bp, n, timeout); } static const struct SerialUSBDriverVMT vmt = { @@ -126,13 +126,12 @@ static const struct SerialUSBDriverVMT vmt = { }; /** - * @brief Notification of data removed from the input queue. + * @brief Notification of empty buffer released into the input buffers queue. * - * @param[in] qp the queue pointer. + * @param[in] bqp the buffers queue pointer. */ -static void inotify(io_queue_t *qp) { - size_t n, maxsize; - SerialUSBDriver *sdup = qGetLink(qp); +static void ibnotify(io_buffers_queue_t *bqp) { + SerialUSBDriver *sdup = bqGetLinkX(bqp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ @@ -141,18 +140,16 @@ static void inotify(io_queue_t *qp) { return; } - /* If there is in the queue enough space to hold at least one packet and - a transaction is not yet started then a new transaction is started for - the available space.*/ - maxsize = sdup->config->usbp->epc[sdup->config->bulk_out]->out_maxsize; + /* Checking if there is already a transaction ongoing on the endpoint.*/ if (!usbGetReceiveStatusI(sdup->config->usbp, sdup->config->bulk_out)) { - if ((n = iqGetEmptyI(&sdup->iqueue)) >= maxsize) { + /* Trying to get a free buffer.*/ + uint8_t *buf = ibqGetEmptyBufferI(&sdup->ibqueue); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ osalSysUnlock(); - n = (n / maxsize) * maxsize; - usbPrepareQueuedReceive(sdup->config->usbp, - sdup->config->bulk_out, - &sdup->iqueue, n); + usbPrepareReceive(sdup->config->usbp, sdup->config->bulk_out, + buf, SERIAL_USB_BUFFERS_SIZE); osalSysLock(); (void) usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out); @@ -161,13 +158,13 @@ static void inotify(io_queue_t *qp) { } /** - * @brief Notification of data inserted into the output queue. + * @brief Notification of filled buffer inserted into the output buffers queue. * - * @param[in] qp the queue pointer. + * @param[in] bqp the buffers queue pointer. */ -static void onotify(io_queue_t *qp) { +static void obnotify(io_buffers_queue_t *bqp) { size_t n; - SerialUSBDriver *sdup = qGetLink(qp); + SerialUSBDriver *sdup = bqGetLinkX(bqp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ @@ -176,15 +173,15 @@ static void onotify(io_queue_t *qp) { return; } - /* If there is not an ongoing transaction and the output queue contains - data then a new transaction is started.*/ + /* Checking if there is already a transaction ongoing on the endpoint.*/ if (!usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in)) { - if ((n = oqGetFullI(&sdup->oqueue)) > 0U) { + /* Trying to get a full buffer.*/ + uint8_t *buf = obqGetFullBufferI(&sdup->obqueue, &n); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ osalSysUnlock(); - usbPrepareQueuedTransmit(sdup->config->usbp, - sdup->config->bulk_in, - &sdup->oqueue, n); + usbPrepareTransmit(sdup->config->usbp, sdup->config->bulk_in, buf, n); osalSysLock(); (void) usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in); @@ -220,8 +217,12 @@ void sduObjectInit(SerialUSBDriver *sdup) { sdup->vmt = &vmt; osalEventObjectInit(&sdup->event); sdup->state = SDU_STOP; - iqObjectInit(&sdup->iqueue, sdup->ib, SERIAL_USB_BUFFERS_SIZE, inotify, sdup); - oqObjectInit(&sdup->oqueue, sdup->ob, SERIAL_USB_BUFFERS_SIZE, onotify, sdup); + ibqObjectInit(&sdup->ibqueue, sdup->ib, + SERIAL_USB_BUFFERS_SIZE, SERIAL_USB_BUFFERS_NUMBER, + ibnotify, sdup); + obqObjectInit(&sdup->obqueue, sdup->ob, + SERIAL_USB_BUFFERS_SIZE, SERIAL_USB_BUFFERS_NUMBER, + obnotify, sdup); } /** @@ -295,8 +296,8 @@ void sduDisconnectI(SerialUSBDriver *sdup) { /* Queues reset in order to signal the driver stop to the application.*/ chnAddFlagsI(sdup, CHN_DISCONNECTED); - iqResetI(&sdup->iqueue); - iqResetI(&sdup->oqueue); + ibqResetI(&sdup->ibqueue); + obqResetI(&sdup->obqueue); } /** @@ -307,16 +308,22 @@ void sduDisconnectI(SerialUSBDriver *sdup) { * @iclass */ void sduConfigureHookI(SerialUSBDriver *sdup) { - USBDriver *usbp = sdup->config->usbp; + uint8_t *buf; - iqResetI(&sdup->iqueue); - oqResetI(&sdup->oqueue); + ibqResetI(&sdup->ibqueue); + obqResetI(&sdup->obqueue); chnAddFlagsI(sdup, CHN_CONNECTED); /* Starts the first OUT transaction immediately.*/ - usbPrepareQueuedReceive(usbp, sdup->config->bulk_out, &sdup->iqueue, - usbp->epc[sdup->config->bulk_out]->out_maxsize); - (void) usbStartReceiveI(usbp, sdup->config->bulk_out); + buf = ibqGetEmptyBufferI(&sdup->ibqueue); + + osalDbgAssert(buf != NULL, "no free buffer"); + + usbPrepareReceive(sdup->config->usbp, sdup->config->bulk_out, + buf, SERIAL_USB_BUFFERS_SIZE); +// usbPrepareQueuedReceive(usbp, sdup->config->bulk_out, &sdup->iqueue, +// usbp->epc[sdup->config->bulk_out]->out_maxsize); + (void) usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out); } /** @@ -364,6 +371,7 @@ bool sduRequestsHook(USBDriver *usbp) { * @param[in] ep endpoint number */ void sduDataTransmitted(USBDriver *usbp, usbep_t ep) { + uint8_t *buf; size_t n; SerialUSBDriver *sdup = usbp->in_params[ep - 1U]; @@ -372,8 +380,33 @@ void sduDataTransmitted(USBDriver *usbp, usbep_t ep) { } osalSysLockFromISR(); + + /* Signaling that space is available in the output queue.*/ chnAddFlagsI(sdup, CHN_OUTPUT_EMPTY); + /* Freeing the buffer just transmitted.*/ + obqReleaseEmptyBufferI(&sdup->obqueue); + + /* Checking if there is a buffer ready for transmission.*/ + buf = obqGetFullBufferI(&sdup->obqueue, &n); + if (buf != NULL) { + /* The endpoint cannot be busy, we are in the context of the callback, + so it is safe to transmit without a check.*/ + osalSysUnlockFromISR(); + + usbPrepareTransmit(usbp, ep, buf, n); + + osalSysLockFromISR(); + (void) usbStartTransmitI(usbp, ep); + osalSysUnlockFromISR(); + return; + } + + /* There could be a partial buffer being filled, special case.*/ + /* TODO */ +#error "SERIAL USB UNDERGOING CHANGES, NOT FINISHED YET" + +#if 0 /*lint -save -e9013 [15.7] There is no else because it is not needed.*/ if ((n = oqGetFullI(&sdup->oqueue)) > 0U) { /* The endpoint cannot be busy, we are in the context of the callback, @@ -402,6 +435,7 @@ void sduDataTransmitted(USBDriver *usbp, usbep_t ep) { /*lint -restore*/ osalSysUnlockFromISR(); +#endif } /** @@ -413,7 +447,7 @@ void sduDataTransmitted(USBDriver *usbp, usbep_t ep) { * @param[in] ep endpoint number */ void sduDataReceived(USBDriver *usbp, usbep_t ep) { - size_t n, maxsize; + uint8_t *buf; SerialUSBDriver *sdup = usbp->out_params[ep - 1U]; if (sdup == NULL) { @@ -421,8 +455,32 @@ void sduDataReceived(USBDriver *usbp, usbep_t ep) { } osalSysLockFromISR(); + + /* Signaling that data is available in the input queue.*/ chnAddFlagsI(sdup, CHN_INPUT_AVAILABLE); + /* Posting the filled buffer in the queue.*/ + ibqPostFullBufferI(&sdup->ibqueue, + usbGetReceiveTransactionSizeI(sdup->config->usbp, + sdup->config->bulk_out)); + + /* The endpoint cannot be busy, we are in the context of the callback, + so a packet is in the buffer for sure. Trying to get a free buffer + for the next transaction.*/ + buf = ibqGetEmptyBufferI(&sdup->ibqueue); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + osalSysUnlockFromISR(); + + usbPrepareReceive(sdup->config->usbp, sdup->config->bulk_out, + buf, SERIAL_USB_BUFFERS_SIZE); + + osalSysLockFromISR(); + (void) usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out); + } + osalSysUnlockFromISR(); + +#if 0 /* Writes to the input queue can only happen when there is enough space to hold at least one packet.*/ maxsize = usbp->epc[ep]->out_maxsize; @@ -438,6 +496,7 @@ void sduDataReceived(USBDriver *usbp, usbep_t ep) { (void) usbStartReceiveI(usbp, ep); } osalSysUnlockFromISR(); +#endif } /**