/* 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 . */ /** * @addtogroup IOQueues * @{ */ #include #ifdef CH_USE_QUEUES /** * Initializes an input queue. A Semaphore is internally initialized * and works as a counter of the bytes contained in the queue. * @param qp pointer to a \p Queue structure * @param buffer pointer to a memory area allocated as queue buffer * @param size size of the queue buffer * @param inotify pointer to a callback function that is invoked when * some data is read from the Queue. The value can be \p NULL. */ void chIQInit(Queue *qp, BYTE8 *buffer, t_size size, t_qnotify 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; } /** * Resets an input queue. All the data is lost and the waiting threads * resumed. * @param qp pointer to a \p Queue structure */ void chIQReset(Queue *qp) { chSysLock(); qp->q_rdptr = qp->q_wrptr = qp->q_buffer; chSemResetI(&qp->q_sem, 0); chSysUnlock(); } /** * Inserts a byte into an input queue. * @param qp pointer to a \p Queue structure * @param b the byte value to be written * @return \p Q_OK if the operation is successful or \p 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. */ t_msg chIQPutI(Queue *qp, BYTE8 b) { if (chIQIsFull(qp)) return Q_FULL; *qp->q_wrptr++ = b; if (qp->q_wrptr >= qp->q_top) qp->q_wrptr = qp->q_buffer; chSemSignalI(&qp->q_sem); return Q_OK; } /** * Gets a byte from the input queue, if the queue is empty then the * calling thread is suspended until a byte arrives in the queue. * @param qp pointer to a \p Queue structure * @return a byte from the queue or \p Q_RESET if the queue was reset */ t_msg chIQGet(Queue *qp) { BYTE8 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; } #ifdef CH_USE_QUEUES_TIMEOUT /** * Gets a byte from the input queue, if the queue is empty then the * calling thread is suspended until a byte arrives in the queue or the * specified time expires. * @param qp pointer to a \p Queue structure * @param time the number of ticks before the operation timouts * @return a byte from the queue, \p Q_TIMEOUT if the specified time expired * or \p Q_RESET if the queue was reset * @note The function is available only if the \p CH_USE_QUEUES_TIMEOUT * option is enabled in \p chconf.h. */ t_msg chIQGetTimeout(Queue *qp, t_time time) { BYTE8 b; t_msg msg; chSysLock(); if ((msg = chSemWaitTimeoutS(&qp->q_sem, time)) < RDY_OK) { chSysUnlock(); return msg; } 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; } #endif /* CH_USE_QUEUES_TIMEOUTS */ /** * Reads some data from the input queue into the specified buffer. The function * is non-blocking and can return zero if the queue is empty. * @param qp pointer to a \p Queue structure * @param buffer the data buffer * @param 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. * @note The function is not atomic, if you need atomicity it is suggested * to use a semaphore for mutual exclusion. */ t_size chIQRead(Queue *qp, BYTE8 *buffer, t_size n) { t_size r = 0; while (n--) { chSysLock(); if (chIQIsEmpty(qp)) { chSysUnlock(); break; } chSemFastWaitS(&qp->q_sem); *buffer++ = *qp->q_rdptr++; if (qp->q_rdptr >= qp->q_top) qp->q_rdptr = qp->q_buffer; chSysUnlock(); r++; } if (r && qp->q_notify) { chSysLock(); qp->q_notify(); chSysUnlock(); } return r; } /** * Initializes an output queue. A Semaphore is internally initialized * and works as a counter of the free bytes in the queue. * @param qp pointer to a \p Queue structure * @param buffer pointer to a memory area allocated as queue buffer * @param size size of the queue buffer * @param onotify pointer to a callback function that is invoked when * some data is written in the Queue. The value can be \p NULL. */ void chOQInit(Queue *qp, BYTE8 *buffer, t_size size, t_qnotify 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; } /** * Resets an Output Queue. All the data is lost and the waiting threads * resumed. * @param qp pointer to a \p Queue structure */ void chOQReset(Queue *qp) { chSysLock(); qp->q_rdptr = qp->q_wrptr = qp->q_buffer; chSemResetI(&qp->q_sem, (t_cnt)(qp->q_top - qp->q_buffer)); chSysUnlock(); } /** * Inserts a byte in the output queue, if the queue is full then the thread * is suspended until the queue has free space available. * @param qp pointer to a \p Queue structure * @param b the byte value to be written */ void chOQPut(Queue *qp, BYTE8 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(); } /** * Gets a byte from an output queue. * @param qp pointer to a \p Queue structure * @return the byte value or \p 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. */ t_msg chOQGetI(Queue *qp) { BYTE8 b; if (chOQIsEmpty(qp)) return Q_EMPTY; b = *qp->q_rdptr++; if (qp->q_rdptr >= qp->q_top) qp->q_rdptr = qp->q_buffer; chSemSignalI(&qp->q_sem); return b; } /** * Writes some data from the specified buffer into the queue. The function * is non-blocking and can return zero if the queue is full. * @param qp pointer to a \p Queue structure * @param buffer the data buffer * @param n the maximum amount of data to be written * @note This function is the upper side endpoint of the output queue. * @note The function is not atomic, if you need atomicity it is suggested * to use a semaphore for mutual exclusion. */ t_size chOQWrite(Queue *qp, BYTE8 *buffer, t_size n) { t_size w = 0; while (n--) { chSysLock(); if (chOQIsFull(qp)) { chSysUnlock(); break; } chSemFastWaitS(&qp->q_sem); *qp->q_wrptr++ = *buffer++; if (qp->q_wrptr >= qp->q_top) qp->q_wrptr = qp->q_buffer; chSysUnlock(); w++; } if (w && qp->q_notify) { chSysLock(); qp->q_notify(); chSysUnlock(); } return w; } #endif /* CH_USE_QUEUES */ #ifdef CH_USE_QUEUES_HALFDUPLEX /** * Initializes an half duplex queue. * @param qp pointer to the \p HalfDuplexQueue structure * @param buffer pointer to a memory area allocated as buffer for the queue * @param size the size of the queue buffer * @param inotify pointer to a callback function that is invoked when * some data is read from the queue. The value can be \p NULL. * @param 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, BYTE8 *buffer, t_size size, t_qnotify inotify, t_qnotify 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; } /** * Reads a byte from the receive queue, if the queue is empty or is in * transmission mode then the invoking thread is suspended. * @param qp pointer to a \p HalfDuplexQueue structure * @return the byte value or \p Q_RESET if the queue was reset */ t_msg chHDQGetReceive(HalfDuplexQueue *qp) { BYTE8 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; } #ifdef CH_USE_QUEUES_TIMEOUT /** * Reads a byte from the receive queue, if the queue is empty or is in * transmission mode then the invoking thread is suspended. * @param qp pointer to a \p HalfDuplexQueue structure * @param time the number of ticks before the operation timouts * @return the byte value or \p Q_TIMEOUT if a timeout occurs * @note The function is available only if the \p CH_USE_QUEUES_TIMEOUT * option is enabled in \p chconf.h. */ t_msg chHDQGetReceiveTimeout(HalfDuplexQueue *qp, t_time time) { BYTE8 b; t_msg 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 /** * Writes a byte into the transmit queue. If the buffer contains unread * input data then the the buffer is cleared and the queue goes in * transmission mode. * @param qp pointer to a \p HalfDuplexQueue structure * @param b the byte value to be written */ void chHDQPutTransmit(HalfDuplexQueue *qp, BYTE8 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(); } /** * Gets a byte from the transmit queue. * @param qp pointer to a \p HalfDuplexQueue structure * @return the byte value or \p 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. */ t_msg chHDQGetTransmitI(HalfDuplexQueue *qp) { BYTE8 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; } /** * Writes a byte into the receive queue. If the queue is in transmission mode * then the byte is lost. * @param qp pointer to a \p HalfDuplexQueue structure * @param b the byte value to be written * @return \p Q_OK if the operation is successful or \p 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. */ t_msg chHDQPutReceiveI(HalfDuplexQueue *qp, BYTE8 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 */ /** @} */