From 46e56d73491d9141b7ed3f79f2a26d3d9addc8ef Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 1 May 2009 12:58:05 +0000 Subject: [PATCH] I/O queues improvements, removed half duplex queues. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@926 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- readme.txt | 7 + src/chqueues.c | 536 +++++++++++++++---------------------------- src/chserial.c | 92 -------- src/include/queues.h | 164 ++++++------- src/include/serial.h | 56 +---- test/testbmk.c | 2 +- 6 files changed, 267 insertions(+), 590 deletions(-) diff --git a/readme.txt b/readme.txt index 49ae0b009..e172388c7 100644 --- a/readme.txt +++ b/readme.txt @@ -82,6 +82,13 @@ Win32-MinGW - ChibiOS/RT simulator and demo into a WIN32 process, real context switch time, previous benchmarks introduced too much overhead to the measurement. The STM32 performs the context switch in under 1.48uS. - NEW: Added architecture name strings to the port code. +- NEW: The I/O queues code was improved, now there are 2 separate structures: + InputQueue and Output queues. There are some changes int the queue APIs + in order to make them more symmetrical and functional. Improved the queues + documentation. +- CHANGE: Removed the half duplex queues and half duplex serial drivers because + it was never extensively tested. The code is still available but not as part + of the kernel. - CHANGE: Removed the chMsgSendWithEvent() function. It is rarely used and the functionality can be re-created with a compound atomic operation. Also removed the CH_USE_MESSAGES_EVENT configuration option. diff --git a/src/chqueues.c b/src/chqueues.c index b7e14e69d..b1711814d 100644 --- a/src/chqueues.c +++ b/src/chqueues.c @@ -33,250 +33,259 @@ * @details A Semaphore is internally initialized and works as a counter of * the bytes contained in the queue. * - * @param[out] qp pointer to a @p Queue structure + * @param[out] iqp pointer to an @p InputQueue structure * @param[in] buffer pointer to a memory area allocated as queue buffer * @param[in] size size of the queue buffer * @param[in] inotify pointer to a callback function that is invoked when - * some data is read from the Queue. The value can be -* @p NULL. + * some data is read from the queue. The value can be + * @p NULL. + * + * @note The callback is invoked from within the S-Locked system state, + * see @ref system_states. */ -void chIQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t inotify) { +void chIQInit(InputQueue *iqp, uint8_t *buffer, + size_t size, qnotify_t inotify) { - qp->q_buffer = qp->q_rdptr = qp->q_wrptr = buffer; - qp->q_top = buffer + size; - chSemInit(&qp->q_sem, 0); - qp->q_notify = inotify; + iqp->q_buffer = iqp->q_rdptr = iqp->q_wrptr = buffer; + iqp->q_top = buffer + size; + chSemInit(&iqp->q_sem, 0); + iqp->q_notify = inotify; } /** * @brief Resets an input queue. - * @details All the data is lost and the waiting threads resumed. + * @details All the data in the input queue is erased and lost, any waiting + * thread is resumed with status @p Q_RESET. * - * @param[in] qp pointer to a @p Queue structure + * @param[in] iqp pointer to an @p InputQueue structure + * + * @note A reset operation can be used by a low level driver in order to obtain + * immediate attention from the high level layers. */ -void chIQReset(Queue *qp) { +void chIQResetI(InputQueue *iqp) { - chSysLock(); - - qp->q_rdptr = qp->q_wrptr = qp->q_buffer; - chSemResetI(&qp->q_sem, 0); - - chSysUnlock(); + iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer; + chSemResetI(&iqp->q_sem, 0); } /** - * @brief Inserts a byte into an input queue. + * @brief Input queue write. + * @details A byte value is written into the low end of an input queue. * - * @param[in] qp pointer to a @p Queue structure - * @param[in] b the byte value to be written - * @retval Q_OK if the operation is successful. - * @retval Q_FULL if the queue is full. - * @note This function is the lower side endpoint of the Input Queue. - * @note This function must be called with interrupts disabled or from an - * interrupt handler. + * @param[in] iqp pointer to an @p InputQueue structure + * @param[in] b the byte value to be written in the queue + * @return The operation status, it can be one of: + * @retval Q_OK if the operation has been completed with success. + * @retval Q_FULL if the queue is full and the operation cannot be completed. */ -msg_t chIQPutI(Queue *qp, uint8_t b) { +msg_t chIQPutI(InputQueue *iqp, uint8_t b) { - if (chIQIsFull(qp)) + if (chIQIsFull(iqp)) return Q_FULL; - *qp->q_wrptr++ = b; - if (qp->q_wrptr >= qp->q_top) - qp->q_wrptr = qp->q_buffer; - chSemSignalI(&qp->q_sem); + *iqp->q_wrptr++ = b; + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + chSemSignalI(&iqp->q_sem); return Q_OK; } /** - * @brief Gets a byte from the input queue. - * @details If the queue is empty then the calling thread is suspended until - * a byte arrives in the queue. + * @brief Input queue read. + * @details This function reads a byte value from an input queue. If the queue + * is empty then the calling thread is suspended until a byte arrives + * in the queue or a timeout occurs. * - * @param[in] qp pointer to a @p Queue structure - * @return A byte value from the queue. - * @retval Q_RESET if the queue was reset. - */ -msg_t chIQGet(Queue *qp) { - uint8_t b; - - chSysLock(); - - if (chSemWaitS(&qp->q_sem) < RDY_OK) { - - chSysUnlock(); - return Q_RESET; - } - b = *qp->q_rdptr++; - if (qp->q_rdptr >= qp->q_top) - qp->q_rdptr = qp->q_buffer; - - if (qp->q_notify) - qp->q_notify(); - - chSysUnlock(); - return b; -} - -#if CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT -/** - * @brief Gets a byte from the input queue. - * @details If the queue is empty then the calling thread is suspended until - * a byte arrives in the queue or the specified time expires. - * - * @param[in] qp pointer to a @p Queue structure - * @param[in] time the number of ticks before the operation timeouts - * @return A byte value from the queue. + * @param[in] iqp pointer to an @p InputQueue structure + * @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 or: * @retval Q_TIMEOUT if the specified time expired. * @retval Q_RESET if the queue was reset. - * @note The function is available only if the @p CH_USE_QUEUES_TIMEOUT and - * @p CH_USE_SEMAPHORES_TIMEOUT options are enabled in @p chconf.h. + * + * @note The @p time parameter is only meaningful if the + * @p CH_USE_SEMAPHORES_TIMEOUT kernel option is activated, + * otherwise only the @p TIME_INFINITE value is accepted. */ -msg_t chIQGetTimeout(Queue *qp, systime_t time) { +msg_t chIQGetTimeout(InputQueue *iqp, systime_t timeout) { uint8_t b; msg_t msg; +#if CH_USE_SEMAPHORES_TIMEOUT chSysLock(); - - if ((msg = chSemWaitTimeoutS(&qp->q_sem, time)) < RDY_OK) { - + if ((msg = chSemWaitTimeoutS(&iqp->q_sem, timeout)) < RDY_OK) { chSysUnlock(); return msg; } - b = *qp->q_rdptr++; - if (qp->q_rdptr >= qp->q_top) - qp->q_rdptr = qp->q_buffer; +#else + chDbgCheck(timeout == TIME_INFINITE, "chIQGetTimeout"); - if (qp->q_notify) - qp->q_notify(); + chSysLock(); + if ((msg = chSemWaitS(&iqp->q_sem)) < RDY_OK) { + chSysUnlock(); + return msg; + } +#endif + b = *iqp->q_rdptr++; + if (iqp->q_rdptr >= iqp->q_top) + iqp->q_rdptr = iqp->q_buffer; + + if (iqp->q_notify) + iqp->q_notify(); chSysUnlock(); return b; } -#endif /* (CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT */ /** - * @brief Reads some data from the input queue into the specified buffer. - * @details The function is non-blocking and can return zero if the queue is + * @brief Non-blocking read. + * @details The function reads data from an input queue into a buffer. The + * transfer is non-blocking and can return zero if the queue is * empty. * - * @param[in] qp pointer to a @p Queue structure - * @param[out] buffer the data buffer - * @param[in] n the maximum amount of data to be read - * @return The number of bytes read. - * @note This function is the upper side endpoint of the input queue. + * @param[in] iqp pointer to an @p InputQueue structure + * @param[out] buffer pointer to the buffer where the input data is copied + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + * * @note The function is not atomic, if you need atomicity it is suggested - * to use a semaphore for mutual exclusion. + * to use a semaphore or a mutex for mutual exclusion. */ -size_t chIQRead(Queue *qp, uint8_t *buffer, size_t n) { - +size_t chIQRead(InputQueue *iqp, uint8_t *buffer, size_t n) { size_t r = 0; + while (n--) { chSysLock(); - - if (chIQIsEmpty(qp)) { - + if (chIQIsEmpty(iqp)) { chSysUnlock(); break; } - chSemFastWaitI(&qp->q_sem); - *buffer++ = *qp->q_rdptr++; - if (qp->q_rdptr >= qp->q_top) - qp->q_rdptr = qp->q_buffer; - + chSemFastWaitI(&iqp->q_sem); + *buffer++ = *iqp->q_rdptr++; + if (iqp->q_rdptr >= iqp->q_top) + iqp->q_rdptr = iqp->q_buffer; chSysUnlock(); r++; } - - if (r && qp->q_notify) { + if (r && iqp->q_notify) { chSysLock(); - - qp->q_notify(); - + iqp->q_notify(); chSysUnlock(); } - return r; } /** * @brief Initializes an output queue. - * @details A Semaphore is internally initialized and works as a counter of the - * free bytes in the queue. + * @details A Semaphore is internally initialized and works as a counter of + * the free bytes in the queue. * - * @param[out] qp pointer to a @p Queue structure + * @param[out] oqp pointer to an @p OutputQueue structure * @param[in] buffer pointer to a memory area allocated as queue buffer * @param[in] size size of the queue buffer * @param[in] onotify pointer to a callback function that is invoked when - * some data is written in the Queue. The value can be + * some data is written to the queue. The value can be * @p NULL. + * + * @note The callback is invoked from within the S-Locked system state, + * see @ref system_states. */ -void chOQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t onotify) { +void chOQInit(OutputQueue *oqp, uint8_t *buffer, + size_t size, qnotify_t onotify) { - qp->q_buffer = qp->q_rdptr = qp->q_wrptr = buffer; - qp->q_top = buffer + size; - chSemInit(&qp->q_sem, size); - qp->q_notify = onotify; + oqp->q_buffer = oqp->q_rdptr = oqp->q_wrptr = buffer; + oqp->q_top = buffer + size; + chSemInit(&oqp->q_sem, size); + oqp->q_notify = onotify; } /** - * @brief Resets an Output Queue. - * @details All the data is lost and the waiting threads resumed. + * @brief Resets an output queue. + * @details All the data in the output queue is erased and lost, any waiting + * thread is resumed with status @p Q_RESET. * - * @param[in] qp pointer to a @p Queue structure + * @param[in] oqp pointer to an @p OutputQueue structure + * + * @note A reset operation can be used by a low level driver in order to obtain + * immediate attention from the high level layers. */ -void chOQReset(Queue *qp) { +void chOQResetI(OutputQueue *oqp) { + + oqp->q_rdptr = oqp->q_wrptr = oqp->q_buffer; + chSemResetI(&oqp->q_sem, (cnt_t)(oqp->q_top - oqp->q_buffer)); +} + +/** + * @brief Output queue write. + * @details This function writes a byte value to an output queue. If the queue + * is full then the calling thread is suspended until there is space + * in the queue or a timeout occurs. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @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 Q_OK if the operation succeeded. + * @retval Q_TIMEOUT if the specified time expired. + * @retval Q_RESET if the queue was reset. + * + * @note The @p time parameter is only meaningful if the + * @p CH_USE_SEMAPHORES_TIMEOUT kernel option is activated, + * otherwise only the @p TIME_INFINITE value is accepted. + */ +msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t timeout) { + msg_t msg; + +#if CH_USE_SEMAPHORES_TIMEOUT + chSysLock(); + if ((msg = chSemWaitTimeoutS(&oqp->q_sem, timeout)) < RDY_OK) { + chSysUnlock(); + return msg; + } +#else + chDbgCheck(timeout == TIME_INFINITE, "chOQPutTimeout"); chSysLock(); + if ((msg = chSemWaitS(&oqp->q_sem)) < RDY_OK) { + chSysUnlock(); + return msg; + } +#endif + *oqp->q_wrptr++ = b; + if (oqp->q_wrptr >= oqp->q_top) + oqp->q_wrptr = oqp->q_buffer; - qp->q_rdptr = qp->q_wrptr = qp->q_buffer; - chSemResetI(&qp->q_sem, (cnt_t)(qp->q_top - qp->q_buffer)); + if (oqp->q_notify) + oqp->q_notify(); chSysUnlock(); + return Q_OK; } /** - * @brief Inserts a byte in the output queue. - * @details If the queue is full then the thread is suspended until the queue - * has free space available. + * @brief Output queue read. + * @details A byte value is read from the low end of an output queue. * - * @param[in] qp pointer to a @p Queue structure - * @param[in] b the byte value to be written - */ -void chOQPut(Queue *qp, uint8_t b) { - - chSysLock(); - - chSemWaitS(&qp->q_sem); - *qp->q_wrptr++ = b; - if (qp->q_wrptr >= qp->q_top) - qp->q_wrptr = qp->q_buffer; - - if (qp->q_notify) - qp->q_notify(); - - chSysUnlock(); -} - -/** - * @brief Gets a byte from an output queue. - * - * @param[in] qp pointer to a @p Queue structure - * @return The byte value from the queue. + * @param[in] oqp pointer to an @p OutputQueue structure + * @return The byte value from the queue or: * @retval Q_EMPTY if the queue is empty. - * @note This function is the lower side endpoint of the output queue. - * @note This function must be called with interrupts disabled or from an - * interrupt handler. */ -msg_t chOQGetI(Queue *qp) { +msg_t chOQGetI(OutputQueue *oqp) { uint8_t b; - if (chOQIsEmpty(qp)) + if (chOQIsEmpty(oqp)) return Q_EMPTY; - b = *qp->q_rdptr++; - if (qp->q_rdptr >= qp->q_top) - qp->q_rdptr = qp->q_buffer; - chSemSignalI(&qp->q_sem); + b = *oqp->q_rdptr++; + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + chSemSignalI(&oqp->q_sem); return b; } @@ -293,218 +302,43 @@ msg_t chOQGetI(Queue *qp) { * @note The function is not atomic, if you need atomicity it is suggested * to use a semaphore for mutual exclusion. */ -size_t chOQWrite(Queue *qp, uint8_t *buffer, size_t n) { +/** + * @brief Non-blocking write. + * @details The function writes data from a buffer to an output queue. The + * transfer is non-blocking and can return zero if the queue is + * already full. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @param[out] buffer pointer to the buffer where the output data is stored + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + * + * @note The function is not atomic, if you need atomicity it is suggested + * to use a semaphore or a mutex for mutual exclusion. + */ +size_t chOQWrite(OutputQueue *oqp, uint8_t *buffer, size_t n) { size_t w = 0; while (n--) { chSysLock(); - - if (chOQIsFull(qp)) { - + if (chOQIsFull(oqp)) { chSysUnlock(); break; } - chSemFastWaitI(&qp->q_sem); - *qp->q_wrptr++ = *buffer++; - if (qp->q_wrptr >= qp->q_top) - qp->q_wrptr = qp->q_buffer; - + chSemFastWaitI(&oqp->q_sem); + *oqp->q_wrptr++ = *buffer++; + if (oqp->q_wrptr >= oqp->q_top) + oqp->q_wrptr = oqp->q_buffer; chSysUnlock(); w++; } - - if (w && qp->q_notify) { + if (w && oqp->q_notify) { chSysLock(); - - qp->q_notify(); - + oqp->q_notify(); chSysUnlock(); } - return w; } #endif /* CH_USE_QUEUES */ -#if CH_USE_QUEUES_HALFDUPLEX - /** - * @brief Initializes an half duplex queue. - * - * @param[out] qp pointer to the @p HalfDuplexQueue structure - * @param[in] buffer pointer to a memory area allocated as buffer for the queue - * @param[in] size the size of the queue buffer - * @param[in] inotify pointer to a callback function that is invoked when - * some data is read from the queue. The value can be - * @p NULL. - * @param[in] onotify pointer to a callback function that is invoked when - * some data is written to the queue. The value can be - * @p NULL. - */ -void chHDQInit(HalfDuplexQueue *qp, uint8_t *buffer, size_t size, - qnotify_t inotify, qnotify_t onotify) { - - qp->hdq_buffer = qp->hdq_rdptr = qp->hdq_wrptr = buffer; - qp->hdq_top = buffer + size; - chSemInit(&qp->hdq_isem, 0); - chSemInit(&qp->hdq_osem, size); - qp->hdq_inotify = inotify; - qp->hdq_onotify = onotify; -} - -/** - * @brief Reads a byte from the receive queue. - * @details If the queue is empty or is in transmission mode then the invoking - * thread is suspended. - * - * @param[in] qp pointer to a @p HalfDuplexQueue structure - * @return The byte value. - * @retval Q_RESET if the queue was reset. - */ -msg_t chHDQGetReceive(HalfDuplexQueue *qp) { - uint8_t b; - - chSysLock(); - - if (chSemWaitS(&qp->hdq_isem) < RDY_OK) { - - chSysUnlock(); - return Q_RESET; - } - /* - * NOTE: The semaphore can be signaled only if the queue is in - * receive mode. - */ - b = *qp->hdq_rdptr++; - if (qp->hdq_rdptr >= qp->hdq_top) - qp->hdq_rdptr = qp->hdq_buffer; - - if (qp->hdq_inotify) - qp->hdq_inotify(); - - chSysUnlock(); - return b; -} - -#if CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT -/** - * @brief Reads a byte from the receive queue. - * @details If the queue is empty or is in transmission mode then the invoking - * thread is suspended. - * - * @param[in] qp pointer to a @p HalfDuplexQueue structure - * @param[in] time the number of ticks before the operation timouts - * @return The byte value. - * @retval Q_TIMEOUT if a timeout occurs. - * @note The function is available only if the @p CH_USE_QUEUES_TIMEOUT and - * @p CH_USE_SEMAPHORES_TIMEOUT options are enabled in @p chconf.h. - */ -msg_t chHDQGetReceiveTimeout(HalfDuplexQueue *qp, systime_t time) { - uint8_t b; - msg_t msg; - - chSysLock(); - - if ((msg = chSemWaitTimeoutS(&qp->hdq_isem, time)) < RDY_OK) { - - chSysUnlock(); - return msg; - } - /* - * NOTE: The semaphore can be signaled only if the queue is in - * receive mode. - */ - b = *qp->hdq_rdptr++; - if (qp->hdq_rdptr >= qp->hdq_top) - qp->hdq_rdptr = qp->hdq_buffer; - - if (qp->hdq_inotify) - qp->hdq_inotify(); - - chSysUnlock(); - return b; -} -#endif /* CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT */ - -/** - * @brief Writes a byte into the transmit queue. - * @details If the buffer contains unread input data then the the buffer is - * cleared and the queue goes in transmission mode. - * - * @param[in] qp pointer to a @p HalfDuplexQueue structure - * @param[in] b the byte value to be written - */ -void chHDQPutTransmit(HalfDuplexQueue *qp, uint8_t b) { - - chSysLock(); - - /* - * Transmission mode requires that all the unread data must be destroyed. - */ - if (qp->hdq_isem.s_cnt > 0) { - qp->hdq_isem.s_cnt = 0; - qp->hdq_rdptr = qp->hdq_wrptr = qp->hdq_buffer; - } - - /* - * Goes in transmission mode. - */ - chSemWaitS(&qp->hdq_osem); - *qp->hdq_wrptr++ = b; - if (qp->hdq_wrptr >= qp->hdq_top) - qp->hdq_wrptr = qp->hdq_buffer; - - if (qp->hdq_onotify) - qp->hdq_onotify(); - - chSysUnlock(); -} - -/** - * @brief Gets a byte from the transmit queue. - * - * @param[in] qp pointer to a @p HalfDuplexQueue structure - * @return The byte value. - * @retval Q_EMPTY if the transmit queue is empty (not in transmission mode). - * @note This function must be called with interrupts disabled or from an - * interrupt handler. - */ -msg_t chHDQGetTransmitI(HalfDuplexQueue *qp) { - uint8_t b; - - if (!chHDQIsTransmitting(qp)) - return Q_EMPTY; - - b = *qp->hdq_rdptr++; - if (qp->hdq_rdptr >= qp->hdq_top) - qp->hdq_rdptr = qp->hdq_buffer; - chSemSignalI(&qp->hdq_osem); - return b; -} - -/** - * @brief Writes a byte into the receive queue. - * @details If the queue is in transmission mode then the byte is lost. - * - * @param[in] qp pointer to a @p HalfDuplexQueue structure - * @param[in] b the byte value to be written - * @retval Q_OK if the operation is successful. - * @retval Q_FULL if the driver is in transmit mode or the receive queue is full. - * @note This function must be called with interrupts disabled or from an - * interrupt handler. - */ -msg_t chHDQPutReceiveI(HalfDuplexQueue *qp, uint8_t b) { - - if (chHDQIsTransmitting(qp)) - return Q_FULL; - - if (chHDQIsFullReceive(qp)) - return Q_FULL; - - *qp->hdq_wrptr++ = b; - if (qp->hdq_wrptr >= qp->hdq_top) - qp->hdq_wrptr = qp->hdq_buffer; - chSemSignalI(&qp->hdq_isem); - return Q_OK; -} -#endif /* CH_USE_QUEUES_HALFDUPLEX */ - /** @} */ diff --git a/src/chserial.c b/src/chserial.c index 725df207d..bb2d7b7d7 100644 --- a/src/chserial.c +++ b/src/chserial.c @@ -123,96 +123,4 @@ dflags_t chFDDGetAndClearFlags(FullDuplexDriver *sd) { } #endif /* CH_USE_SERIAL_FULLDUPLEX */ -#if CH_USE_SERIAL_HALFDUPLEX -/** - * @brief Initializes a generic half duplex driver. - * @details The HW dependent part of the initialization has to be performed - * outside, usually in the hardware initialization code. - * - * @param[out] sd pointer to a @p HalfDuplexDriver structure - * @param[in] b pointer to a memory area allocated for the queue buffer - * @param[in] size the buffer size - * @param[in] inotify pointer to a callback function that is invoked when - * some data is read from the queue. The value can be - * @p NULL. - * @param[in] onotify pointer to a callback function that is invoked when - * some data is written in the queue. The value can be - * @p NULL. - */ -void chHDDInit(HalfDuplexDriver *sd, uint8_t *b, size_t size, - qnotify_t inotify, qnotify_t onotify) { - - chDbgCheck((sd != NULL) && (b != NULL) && (size > 0), "chHDDInit"); - - chHDQInit(&sd->sd_queue, b, size, inotify, onotify); - chEvtInit(&sd->sd_ievent); - chEvtInit(&sd->sd_oevent); - chEvtInit(&sd->sd_sevent); - sd->sd_flags = SD_NO_ERROR; -} - -/** - * @brief Handles incoming data. - * @details This function must be called from the input interrupt service - * routine in order to enqueue incoming data and generate the - * related events. - * @param[in] sd pointer to a @p FullDuplexDriver structure - * @param[in] b the byte to be written in the driver's input queue - */ -void chHDDIncomingDataI(HalfDuplexDriver *sd, uint8_t b) { - - if (chHDQPutReceiveI(&sd->sd_queue, b) < Q_OK) - chHDDAddFlagsI(sd, SD_OVERRUN_ERROR); - else - chEvtBroadcastI(&sd->sd_ievent); -} - -/** - * @brief Handles outgoing data. - * @details Must be called from the output interrupt service routine in order - * to get the next byte to be transmitted. - * - * @param[in] sd pointer to a @p HalfDuplexDriver structure - * @return The byte value read from the driver's output queue. - * @retval Q_EMPTY if the queue is empty (the lower driver usually disables - * the interrupt source when this happens). - */ -msg_t chHDDRequestDataI(HalfDuplexDriver *sd) { - - msg_t b = chHDQGetTransmitI(&sd->sd_queue); - if (b < Q_OK) - chEvtBroadcastI(&sd->sd_oevent); - return b; -} - -/** - * @brief Handles communication events/errors. - * @details Must be called from the I/O interrupt service routine in order to - * notify I/O conditions as errors, signals change etc. - * - * @param[in] sd pointer to a @p HalfDuplexDriver structure - * @param[in] mask condition flags to be added to the mask - */ -void chHDDAddFlagsI(HalfDuplexDriver *sd, dflags_t mask) { - - sd->sd_flags |= mask; - chEvtBroadcastI(&sd->sd_sevent); -} - -/** - * @brief Returns and clears the errors mask associated to the driver. - * - * @param[in] sd pointer to a @p HalfDuplexDriver structure - * @return The condition flags modified since last time this function was - * invoked. - */ -dflags_t chHDDGetAndClearFlags(HalfDuplexDriver *sd) { - dflags_t mask; - - mask = sd->sd_flags; - sd->sd_flags = SD_NO_ERROR; - return mask; -} -#endif /* CH_USE_SERIAL_HALFDUPLEX */ - /** @} */ diff --git a/src/include/queues.h b/src/include/queues.h index eea80e804..ca587d388 100644 --- a/src/include/queues.h +++ b/src/include/queues.h @@ -43,9 +43,13 @@ typedef void (*qnotify_t)(void); #if CH_USE_QUEUES /** - * @brief I/O queue structure. - * @details This structure is used by both Input and Output Queues, - * the difference is on how the semaphore is initialized. + * @brief Generic I/O queue structure. + * @details This structure represents a generic Input or Output asymmetrical + * queue. The queue is asymmetrical because one end is meant to be + * accessed from a thread context, and thus can be blocking, the other + * end is accessible from interrupt handlers or from within a kernel + * lock zone (see I-Locked and S-Locked states in + * @ref system_states) and is non-blocking. */ typedef struct { uint8_t *q_buffer; /**< Pointer to the queue buffer.*/ @@ -55,7 +59,7 @@ typedef struct { uint8_t *q_rdptr; /**< Read pointer.*/ Semaphore q_sem; /**< Counter @p Semaphore.*/ qnotify_t q_notify; /**< Data notification callback.*/ -} Queue; +} GenericQueue; /** Returns the queue's buffer size. */ #define chQSize(q) ((q)->q_top - (q)->q_buffer) @@ -68,110 +72,86 @@ typedef struct { */ #define chQSpace(q) chSemGetCounterI(&(q)->q_sem) -/** Evaluates to TRUE if the specified Input Queue is empty. */ +/** + * @brief Input queue structure. + * @details This structure represents a generic asymmetrical input queue. + * Writing in the queue is non-blocking and can be performed from + * interrupt handlers or from within a kernel lock zone (see + * I-Locked and S-Locked states in @ref system_states). + * Reading the queue can be a blocking operation and is supposed to + * be performed by a system thread. + * @extends GenericQueue + */ +typedef GenericQueue InputQueue; + +/** Evaluates to @p TRUE if the specified Input Queue is empty. */ #define chIQIsEmpty(q) (chQSpace(q) <= 0) -/** Evaluates to TRUE if the specified Input Queue is full. */ +/** Evaluates to @p TRUE if the specified Input Queue is full. */ #define chIQIsFull(q) (chQSpace(q) >= chQSize(q)) -/** Evaluates to TRUE if the specified Output Queue is empty. */ +/** + * @brief Input queue read. + * @details This function reads a byte value from an input queue. If the queue + * is empty then the calling thread is suspended until a byte arrives + * in the queue. + * + * @param[in] iqp pointer to an @p InputQueue structure + * @return A byte value from the queue or: + * @retval Q_RESET if the queue was reset. + */ +#define chIQGet(iqp) chIQGetTimeout(iqp, TIME_INFINITE) + +/** + * @brief Output queue structure. + * @details This structure represents a generic asymmetrical output queue. + * Reading from the queue is non-blocking and can be performed from + * interrupt handlers or from within a kernel lock zone (see + * I-Locked and S-Locked states in @ref system_states). + * Writing the queue can be a blocking operation and is supposed to + * be performed by a system thread. + * @extends GenericQueue + */ +typedef GenericQueue OutputQueue; + +/** Evaluates to @p TRUE if the specified Output Queue is empty. */ #define chOQIsEmpty(q) (chQSpace(q) >= chQSize(q)) -/** Evaluates to TRUE if the specified Output Queue is full. */ +/** Evaluates to @p TRUE if the specified Output Queue is full. */ #define chOQIsFull(q) (chQSpace(q) <= 0) +/** + * @brief Output queue write. + * @details This function writes a byte value to an output queue. If the queue + * is full then the calling thread is suspended until there is space + * in the queue. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @param[in] b the byte value to be written in the queue + * @return The operation status: + * @retval Q_OK if the operation succeeded. + * @retval Q_RESET if the queue was reset. + */ +#define chOQPut(oqp, b) chOQPutTimeout(oqp, b, TIME_INFINITE) + #ifdef __cplusplus extern "C" { #endif - /* - * Input Queues functions. An Input Queue is usually written into by an - * interrupt handler and read from a thread. - */ - void chIQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t inotify); - void chIQReset(Queue *qp); - msg_t chIQPutI(Queue *qp, uint8_t b); - msg_t chIQGet(Queue *qp); - size_t chIQRead(Queue *qp, uint8_t *buffer, size_t n); -#if CH_USE_QUEUES_TIMEOUT - msg_t chIQGetTimeout(Queue *qp, systime_t time); -#endif - - /* - * Output Queues functions. An Output Queue is usually written into by a - * thread and read from an interrupt handler. - */ - void chOQInit(Queue *queue, uint8_t *buffer, size_t size, qnotify_t onotify); - void chOQReset(Queue *queue); - void chOQPut(Queue *queue, uint8_t b); - msg_t chOQGetI(Queue *queue); - size_t chOQWrite(Queue *queue, uint8_t *buffer, size_t n); + void chIQInit(InputQueue *qp, uint8_t *buffer, size_t size, qnotify_t inotify); + void chIQResetI(InputQueue *qp); + msg_t chIQPutI(InputQueue *qp, uint8_t b); + msg_t chIQGetTimeout(InputQueue *qp, systime_t timeout); + size_t chIQRead(InputQueue *qp, uint8_t *buffer, size_t n); + void chOQInit(OutputQueue *queue, uint8_t *buffer, size_t size, qnotify_t onotify); + void chOQResetI(OutputQueue *queue); + msg_t chOQPutTimeout(OutputQueue *queue, uint8_t b, systime_t timeout); + msg_t chOQGetI(OutputQueue *queue); + size_t chOQWrite(OutputQueue *queue, uint8_t *buffer, size_t n); #ifdef __cplusplus } #endif #endif /* CH_USE_QUEUES */ -#if CH_USE_QUEUES_HALFDUPLEX -/** - * @brief Half duplex queue structure. - */ -typedef struct { - uint8_t *hdq_buffer; /**< Pointer to the queue buffer.*/ - uint8_t *hdq_top; /**< Pointer to the first location - after the buffer. */ - uint8_t *hdq_wrptr; /**< Write pointer.*/ - uint8_t *hdq_rdptr; /**< Read pointer.*/ - Semaphore hdq_isem; /**< Input counter @p Semaphore.*/ - Semaphore hdq_osem; /**< Output counter @p Semaphore.*/ - qnotify_t hdq_inotify; /**< Input data notification - callback.*/ - qnotify_t hdq_onotify; /**< Output data notification - callback.*/ -} HalfDuplexQueue; - -/** Returns the queue's buffer size. */ -#define chHDQSize(q) ((q)->hdq_top - (q)->hdq_buffer) - -/** - * Returns the queue space when in transmission mode. - * @note The returned value can be less than zero when there are waiting - * threads on the internal semaphore. - */ -#define chHDQEmptySpace(q) chSemGetCounterI(&(q)->hdq_osem) - -/** - * Returns the number of the bytes in the queue when in receive mode. - * @note The returned value can be less than zero when there are waiting - * threads on the internal semaphore. - */ -#define chHDQFilledSpace(q) chSemGetCounterI(&(q)->hdq_isem) - -/** Evaluates to TRUE if the queue is in transmit mode. */ -#define chHDQIsTransmitting(q) (chHDQEmptySpace(q) < chHDQSize(q)) - -/** Evaluates to TRUE if the queue is in receive mode. */ -#define chHDQIsReceiving(q) (chHDQEmptySpaceQ(q) >= chHDQSize(q)) - -/** Evaluates to TRUE if the receive queue is full. */ -#define chHDQIsFullReceive(q) (chHDQFilledSpace(q) >= chHDQSize(q)) - -#ifdef __cplusplus -extern "C" { -#endif - void chHDQInit(HalfDuplexQueue *qp, uint8_t *buffer, size_t size, - qnotify_t inotify, qnotify_t onotify); - msg_t chHDQGetReceive(HalfDuplexQueue *qp); - void chHDQPutTransmit(HalfDuplexQueue *qp, uint8_t b); - msg_t chHDQGetTransmitI(HalfDuplexQueue *qp); - msg_t chHDQPutReceiveI(HalfDuplexQueue *qp, uint8_t b); -#if CH_USE_QUEUES_TIMEOUT - msg_t chHDQGetReceiveTimeout(HalfDuplexQueue *qp, systime_t time); -#endif -#ifdef __cplusplus -} -#endif - -#endif /* CH_USE_QUEUES_HALFDUPLEX */ - #endif /* _QUEUES_H_ */ /** @} */ diff --git a/src/include/serial.h b/src/include/serial.h index ebb3392bb..6530fae0d 100644 --- a/src/include/serial.h +++ b/src/include/serial.h @@ -54,14 +54,14 @@ typedef struct { /** Input queue. Incoming data can be read from this queue by using the * queues APIs.*/ - Queue sd_iqueue; + InputQueue sd_iqueue; /** Data Available @p EventSource. This event is generated when some incoming * data is inserted in the Input @p Queue.*/ EventSource sd_ievent; /** Output queue. Outgoing data can be written to this Output @p Queue by * using the queues APIs.*/ - Queue sd_oqueue; + OutputQueue sd_oqueue; /** Data Transmitted @p EventSource. This event is generated when the * Output @p Queue is empty.*/ EventSource sd_oevent; @@ -110,58 +110,6 @@ extern "C" { #endif /* CH_USE_SERIAL_FULLDUPLEX */ -#if CH_USE_SERIAL_HALFDUPLEX - -/** - * @brief Full Duplex Serial Driver main structure. - */ -typedef struct { - - /** Data queue. Transmit/receive @p HalfDuplexQueue.*/ - HalfDuplexQueue sd_queue; - /** Data Available @p EventSource. This event is generated when some - * incoming data is inserted in the receive queue.*/ - EventSource sd_ievent; - /** Data Transmitted @p EventSource. This event is generated when the - * transmission queue is empty and the driver can either transmit more - * data or enter receive mode.*/ - EventSource sd_oevent; - /** I/O driver status flags. This field should not be read directly but - * the @p chHDDGetAndClearFlags() funtion should be used - * instead.*/ - dflags_t sd_flags; - /** Status Change Event Source. This event is generated when a condition - * flag was changed.*/ - EventSource sd_sevent; -} HalfDuplexDriver; - -#ifdef __cplusplus -extern "C" { -#endif - void chHDDInit(HalfDuplexDriver *sd, uint8_t *b, size_t size, - qnotify_t inotify, qnotify_t onotify); - void chHDDIncomingDataI(HalfDuplexDriver *sd, uint8_t b); - msg_t chHDDRequestDataI(HalfDuplexDriver *sd); - void chHDDAddFlagsI(HalfDuplexDriver *sd, dflags_t mask); - dflags_t chHDDGetAndClearFlags(HalfDuplexDriver *sd); -#ifdef __cplusplus -} -#endif - -/** @see chHDQGetReceive()*/ -#define chHDDGetReceive(sd) \ - chHDQGetReceive(&(sd)->sd_queue) - -/** @see chHDQGetReceiveTimeout()*/ -#define chHDDGetReceiveTimeout(sd, t) \ - chHDQGetReceiveTimeout(&(sd)->sd_queue, t) - -/** @see chHDQPutTransmit()*/ -#define chHDDPutTransmit(sd, b) \ - chHDQPutTransmit(&(sd)->sd_queue, b) - -#endif /* CH_USE_SERIAL_HALFDUPLEX */ - #endif /* _SERIAL_H_ */ /** @} */ diff --git a/test/testbmk.c b/test/testbmk.c index ad7f528e4..258608c9f 100644 --- a/test/testbmk.c +++ b/test/testbmk.c @@ -318,7 +318,7 @@ static char *bmk8_gettest(void) { static void bmk8_execute(void) { static uint8_t ib[16]; - static Queue iq; + static InputQueue iq; chIQInit(&iq, ib, sizeof(ib), NULL); uint32_t n = 0;