/*
ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT 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/RT 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 chqueues.c
* @brief I/O Queues code.
* @addtogroup io_queues
* @{
*/
#include
#if CH_USE_QUEUES
/**
* @brief Initializes an input queue.
* @details A Semaphore is internally initialized and works as a counter of
* the bytes contained in the queue.
*
* @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.
*
* @note The callback is invoked from within the S-Locked system state,
* see @ref system_states.
*/
void chIQInit(InputQueue *iqp, uint8_t *buffer,
size_t size, qnotify_t 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 in the input queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
*
* @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 chIQResetI(InputQueue *iqp) {
iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer;
chSemResetI(&iqp->q_sem, 0);
}
/**
* @brief Input queue write.
* @details A byte value is written into the low end of an input queue.
*
* @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(InputQueue *iqp, uint8_t b) {
if (chIQIsFull(iqp))
return Q_FULL;
*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 Input queue read with timeout.
* @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] 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.
*/
msg_t chIQGetTimeout(InputQueue *iqp, systime_t timeout) {
uint8_t b;
msg_t msg;
chSysLock();
if ((msg = chSemWaitTimeoutS(&iqp->q_sem, timeout)) < RDY_OK) {
chSysUnlock();
return msg;
}
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;
}
/**
* @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] 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 or a mutex for mutual exclusion.
*/
size_t chIQRead(InputQueue *iqp, uint8_t *buffer, size_t n) {
size_t r = 0;
while (n--) {
chSysLock();
if (chIQIsEmpty(iqp)) {
chSysUnlock();
break;
}
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 && iqp->q_notify) {
chSysLock();
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.
*
* @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 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(OutputQueue *oqp, uint8_t *buffer,
size_t size, qnotify_t 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 in the output queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
*
* @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 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 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 there is space
* in the queue or a timeout occurs.
*
* @param[in] oqp pointer to an @p OutputQueue structure
* @param[in] b the byte value to be written in the queue
* @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.
*/
msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t timeout) {
msg_t msg;
chSysLock();
if ((msg = chSemWaitTimeoutS(&oqp->q_sem, timeout)) < RDY_OK) {
chSysUnlock();
return msg;
}
*oqp->q_wrptr++ = b;
if (oqp->q_wrptr >= oqp->q_top)
oqp->q_wrptr = oqp->q_buffer;
if (oqp->q_notify)
oqp->q_notify();
chSysUnlock();
return Q_OK;
}
/**
* @brief Output queue read.
* @details A byte value is read from the low end of an output 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.
*/
msg_t chOQGetI(OutputQueue *oqp) {
uint8_t b;
if (chOQIsEmpty(oqp))
return Q_EMPTY;
b = *oqp->q_rdptr++;
if (oqp->q_rdptr >= oqp->q_top)
oqp->q_rdptr = oqp->q_buffer;
chSemSignalI(&oqp->q_sem);
return b;
}
/**
* @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(oqp)) {
chSysUnlock();
break;
}
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 && oqp->q_notify) {
chSysLock();
oqp->q_notify();
chSysUnlock();
}
return w;
}
#endif /* CH_USE_QUEUES */
/** @} */