ChibiOS/RT
2.5.1
chqueues.c
Go to the documentation of this file.
00001 /*
00002     ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
00003                  2011,2012 Giovanni Di Sirio.
00004 
00005     This file is part of ChibiOS/RT.
00006 
00007     ChibiOS/RT is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 3 of the License, or
00010     (at your option) any later version.
00011 
00012     ChibiOS/RT is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 /**
00022  * @file    chqueues.c
00023  * @brief   I/O Queues code.
00024  *
00025  * @addtogroup io_queues
00026  * @details ChibiOS/RT queues are mostly used in serial-like device drivers.
00027  *          The device drivers are usually designed to have a lower side
00028  *          (lower driver, it is usually an interrupt service routine) and an
00029  *          upper side (upper driver, accessed by the application threads).<br>
00030  *          There are several kind of queues:<br>
00031  *          - <b>Input queue</b>, unidirectional queue where the writer is the
00032  *            lower side and the reader is the upper side.
00033  *          - <b>Output queue</b>, unidirectional queue where the writer is the
00034  *            upper side and the reader is the lower side.
00035  *          - <b>Full duplex queue</b>, bidirectional queue. Full duplex queues
00036  *            are implemented by pairing an input queue and an output queue
00037  *            together.
00038  *          .
00039  * @pre     In order to use the I/O queues the @p CH_USE_QUEUES option must
00040  *          be enabled in @p chconf.h.
00041  * @{
00042  */
00043 
00044 #include "ch.h"
00045 
00046 #if CH_USE_QUEUES || defined(__DOXYGEN__)
00047 
00048 /**
00049  * @brief   Puts the invoking thread into the queue's threads queue.
00050  *
00051  * @param[out] qp       pointer to an @p GenericQueue structure
00052  * @param[in] time      the number of ticks before the operation timeouts,
00053  *                      the following special values are allowed:
00054  *                      - @a TIME_IMMEDIATE immediate timeout.
00055  *                      - @a TIME_INFINITE no timeout.
00056  *                      .
00057  * @return              A message specifying how the invoking thread has been
00058  *                      released from threads queue.
00059  * @retval Q_OK         is the normal exit, thread signaled.
00060  * @retval Q_RESET      if the queue has been reset.
00061  * @retval Q_TIMEOUT    if the queue operation timed out.
00062  */
00063 static msg_t qwait(GenericQueue *qp, systime_t time) {
00064 
00065   if (TIME_IMMEDIATE == time)
00066     return Q_TIMEOUT;
00067   currp->p_u.wtobjp = qp;
00068   queue_insert(currp, &qp->q_waiting);
00069   return chSchGoSleepTimeoutS(THD_STATE_WTQUEUE, time);
00070 }
00071 
00072 /**
00073  * @brief   Initializes an input queue.
00074  * @details A Semaphore is internally initialized and works as a counter of
00075  *          the bytes contained in the queue.
00076  * @note    The callback is invoked from within the S-Locked system state,
00077  *          see @ref system_states.
00078  *
00079  * @param[out] iqp      pointer to an @p InputQueue structure
00080  * @param[in] bp        pointer to a memory area allocated as queue buffer
00081  * @param[in] size      size of the queue buffer
00082  * @param[in] infy      pointer to a callback function that is invoked when
00083  *                      data is read from the queue. The value can be @p NULL.
00084  * @param[in] link      application defined pointer
00085  *
00086  * @init
00087  */
00088 void chIQInit(InputQueue *iqp, uint8_t *bp, size_t size, qnotify_t infy,
00089               void *link) {
00090 
00091   queue_init(&iqp->q_waiting);
00092   iqp->q_counter = 0;
00093   iqp->q_buffer = iqp->q_rdptr = iqp->q_wrptr = bp;
00094   iqp->q_top = bp + size;
00095   iqp->q_notify = infy;
00096   iqp->q_link = link;
00097 }
00098 
00099 /**
00100  * @brief   Resets an input queue.
00101  * @details All the data in the input queue is erased and lost, any waiting
00102  *          thread is resumed with status @p Q_RESET.
00103  * @note    A reset operation can be used by a low level driver in order to
00104  *          obtain immediate attention from the high level layers.
00105  *
00106  * @param[in] iqp       pointer to an @p InputQueue structure
00107  *
00108  * @iclass
00109  */
00110 void chIQResetI(InputQueue *iqp) {
00111 
00112   chDbgCheckClassI();
00113 
00114   iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer;
00115   iqp->q_counter = 0;
00116   while (notempty(&iqp->q_waiting))
00117     chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_RESET;
00118 }
00119 
00120 /**
00121  * @brief   Input queue write.
00122  * @details A byte value is written into the low end of an input queue.
00123  *
00124  * @param[in] iqp       pointer to an @p InputQueue structure
00125  * @param[in] b         the byte value to be written in the queue
00126  * @return              The operation status.
00127  * @retval Q_OK         if the operation has been completed with success.
00128  * @retval Q_FULL       if the queue is full and the operation cannot be
00129  *                      completed.
00130  *
00131  * @iclass
00132  */
00133 msg_t chIQPutI(InputQueue *iqp, uint8_t b) {
00134 
00135   chDbgCheckClassI();
00136 
00137   if (chIQIsFullI(iqp))
00138     return Q_FULL;
00139 
00140   iqp->q_counter++;
00141   *iqp->q_wrptr++ = b;
00142   if (iqp->q_wrptr >= iqp->q_top)
00143     iqp->q_wrptr = iqp->q_buffer;
00144 
00145   if (notempty(&iqp->q_waiting))
00146     chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK;
00147 
00148   return Q_OK;
00149 }
00150 
00151 /**
00152  * @brief   Input queue read with timeout.
00153  * @details This function reads a byte value from an input queue. If the queue
00154  *          is empty then the calling thread is suspended until a byte arrives
00155  *          in the queue or a timeout occurs.
00156  * @note    The callback is invoked before reading the character from the
00157  *          buffer or before entering the state @p THD_STATE_WTQUEUE.
00158  *
00159  * @param[in] iqp       pointer to an @p InputQueue structure
00160  * @param[in] time      the number of ticks before the operation timeouts,
00161  *                      the following special values are allowed:
00162  *                      - @a TIME_IMMEDIATE immediate timeout.
00163  *                      - @a TIME_INFINITE no timeout.
00164  *                      .
00165  * @return              A byte value from the queue.
00166  * @retval Q_TIMEOUT    if the specified time expired.
00167  * @retval Q_RESET      if the queue has been reset.
00168  *
00169  * @api
00170  */
00171 msg_t chIQGetTimeout(InputQueue *iqp, systime_t time) {
00172   uint8_t b;
00173 
00174   chSysLock();
00175   if (iqp->q_notify)
00176     iqp->q_notify(iqp);
00177 
00178   while (chIQIsEmptyI(iqp)) {
00179     msg_t msg;
00180     if ((msg = qwait((GenericQueue *)iqp, time)) < Q_OK) {
00181       chSysUnlock();
00182       return msg;
00183     }
00184   }
00185 
00186   iqp->q_counter--;
00187   b = *iqp->q_rdptr++;
00188   if (iqp->q_rdptr >= iqp->q_top)
00189     iqp->q_rdptr = iqp->q_buffer;
00190 
00191   chSysUnlock();
00192   return b;
00193 }
00194 
00195 /**
00196  * @brief   Input queue read with timeout.
00197  * @details The function reads data from an input queue into a buffer. The
00198  *          operation completes when the specified amount of data has been
00199  *          transferred or after the specified timeout or if the queue has
00200  *          been reset.
00201  * @note    The function is not atomic, if you need atomicity it is suggested
00202  *          to use a semaphore or a mutex for mutual exclusion.
00203  * @note    The callback is invoked before reading each character from the
00204  *          buffer or before entering the state @p THD_STATE_WTQUEUE.
00205  *
00206  * @param[in] iqp       pointer to an @p InputQueue structure
00207  * @param[out] bp       pointer to the data buffer
00208  * @param[in] n         the maximum amount of data to be transferred, the
00209  *                      value 0 is reserved
00210  * @param[in] time      the number of ticks before the operation timeouts,
00211  *                      the following special values are allowed:
00212  *                      - @a TIME_IMMEDIATE immediate timeout.
00213  *                      - @a TIME_INFINITE no timeout.
00214  *                      .
00215  * @return              The number of bytes effectively transferred.
00216  *
00217  * @api
00218  */
00219 size_t chIQReadTimeout(InputQueue *iqp, uint8_t *bp,
00220                        size_t n, systime_t time) {
00221   qnotify_t nfy = iqp->q_notify;
00222   size_t r = 0;
00223 
00224   chDbgCheck(n > 0, "chIQReadTimeout");
00225 
00226   chSysLock();
00227   while (TRUE) {
00228     if (nfy)
00229       nfy(iqp);
00230 
00231     while (chIQIsEmptyI(iqp)) {
00232       if (qwait((GenericQueue *)iqp, time) != Q_OK) {
00233         chSysUnlock();
00234         return r;
00235       }
00236     }
00237 
00238     iqp->q_counter--;
00239     *bp++ = *iqp->q_rdptr++;
00240     if (iqp->q_rdptr >= iqp->q_top)
00241       iqp->q_rdptr = iqp->q_buffer;
00242 
00243     chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
00244     r++;
00245     if (--n == 0)
00246       return r;
00247 
00248     chSysLock();
00249   }
00250 }
00251 
00252 /**
00253  * @brief   Initializes an output queue.
00254  * @details A Semaphore is internally initialized and works as a counter of
00255  *          the free bytes in the queue.
00256  * @note    The callback is invoked from within the S-Locked system state,
00257  *          see @ref system_states.
00258  *
00259  * @param[out] oqp      pointer to an @p OutputQueue structure
00260  * @param[in] bp        pointer to a memory area allocated as queue buffer
00261  * @param[in] size      size of the queue buffer
00262  * @param[in] onfy      pointer to a callback function that is invoked when
00263  *                      data is written to the queue. The value can be @p NULL.
00264  * @param[in] link      application defined pointer
00265  *
00266  * @init
00267  */
00268 void chOQInit(OutputQueue *oqp, uint8_t *bp, size_t size, qnotify_t onfy,
00269               void *link) {
00270 
00271   queue_init(&oqp->q_waiting);
00272   oqp->q_counter = size;
00273   oqp->q_buffer = oqp->q_rdptr = oqp->q_wrptr = bp;
00274   oqp->q_top = bp + size;
00275   oqp->q_notify = onfy;
00276   oqp->q_link = link;
00277 }
00278 
00279 /**
00280  * @brief   Resets an output queue.
00281  * @details All the data in the output queue is erased and lost, any waiting
00282  *          thread is resumed with status @p Q_RESET.
00283  * @note    A reset operation can be used by a low level driver in order to
00284  *          obtain immediate attention from the high level layers.
00285  *
00286  * @param[in] oqp       pointer to an @p OutputQueue structure
00287  *
00288  * @iclass
00289  */
00290 void chOQResetI(OutputQueue *oqp) {
00291 
00292   chDbgCheckClassI();
00293 
00294   oqp->q_rdptr = oqp->q_wrptr = oqp->q_buffer;
00295   oqp->q_counter = chQSizeI(oqp);
00296   while (notempty(&oqp->q_waiting))
00297     chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_RESET;
00298 }
00299 
00300 /**
00301  * @brief   Output queue write with timeout.
00302  * @details This function writes a byte value to an output queue. If the queue
00303  *          is full then the calling thread is suspended until there is space
00304  *          in the queue or a timeout occurs.
00305  * @note    The callback is invoked after writing the character into the
00306  *          buffer.
00307  *
00308  * @param[in] oqp       pointer to an @p OutputQueue structure
00309  * @param[in] b         the byte value to be written in the queue
00310  * @param[in] time      the number of ticks before the operation timeouts,
00311  *                      the following special values are allowed:
00312  *                      - @a TIME_IMMEDIATE immediate timeout.
00313  *                      - @a TIME_INFINITE no timeout.
00314  *                      .
00315  * @return              The operation status.
00316  * @retval Q_OK         if the operation succeeded.
00317  * @retval Q_TIMEOUT    if the specified time expired.
00318  * @retval Q_RESET      if the queue has been reset.
00319  *
00320  * @api
00321  */
00322 msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t time) {
00323 
00324   chSysLock();
00325   while (chOQIsFullI(oqp)) {
00326     msg_t msg;
00327 
00328     if ((msg = qwait((GenericQueue *)oqp, time)) < Q_OK) {
00329       chSysUnlock();
00330       return msg;
00331     }
00332   }
00333 
00334   oqp->q_counter--;
00335   *oqp->q_wrptr++ = b;
00336   if (oqp->q_wrptr >= oqp->q_top)
00337     oqp->q_wrptr = oqp->q_buffer;
00338 
00339   if (oqp->q_notify)
00340     oqp->q_notify(oqp);
00341 
00342   chSysUnlock();
00343   return Q_OK;
00344 }
00345 
00346 /**
00347  * @brief   Output queue read.
00348  * @details A byte value is read from the low end of an output queue.
00349  *
00350  * @param[in] oqp       pointer to an @p OutputQueue structure
00351  * @return              The byte value from the queue.
00352  * @retval Q_EMPTY      if the queue is empty.
00353  *
00354  * @iclass
00355  */
00356 msg_t chOQGetI(OutputQueue *oqp) {
00357   uint8_t b;
00358 
00359   chDbgCheckClassI();
00360 
00361   if (chOQIsEmptyI(oqp))
00362     return Q_EMPTY;
00363 
00364   oqp->q_counter++;
00365   b = *oqp->q_rdptr++;
00366   if (oqp->q_rdptr >= oqp->q_top)
00367     oqp->q_rdptr = oqp->q_buffer;
00368 
00369   if (notempty(&oqp->q_waiting))
00370     chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK;
00371 
00372   return b;
00373 }
00374 
00375 /**
00376  * @brief   Output queue write with timeout.
00377  * @details The function writes data from a buffer to an output queue. The
00378  *          operation completes when the specified amount of data has been
00379  *          transferred or after the specified timeout or if the queue has
00380  *          been reset.
00381  * @note    The function is not atomic, if you need atomicity it is suggested
00382  *          to use a semaphore or a mutex for mutual exclusion.
00383  * @note    The callback is invoked after writing each character into the
00384  *          buffer.
00385  *
00386  * @param[in] oqp       pointer to an @p OutputQueue structure
00387  * @param[out] bp       pointer to the data buffer
00388  * @param[in] n         the maximum amount of data to be transferred, the
00389  *                      value 0 is reserved
00390  * @param[in] time      the number of ticks before the operation timeouts,
00391  *                      the following special values are allowed:
00392  *                      - @a TIME_IMMEDIATE immediate timeout.
00393  *                      - @a TIME_INFINITE no timeout.
00394  *                      .
00395  * @return              The number of bytes effectively transferred.
00396  *
00397  * @api
00398  */
00399 size_t chOQWriteTimeout(OutputQueue *oqp, const uint8_t *bp,
00400                         size_t n, systime_t time) {
00401   qnotify_t nfy = oqp->q_notify;
00402   size_t w = 0;
00403 
00404   chDbgCheck(n > 0, "chOQWriteTimeout");
00405 
00406   chSysLock();
00407   while (TRUE) {
00408     while (chOQIsFullI(oqp)) {
00409       if (qwait((GenericQueue *)oqp, time) != Q_OK) {
00410         chSysUnlock();
00411         return w;
00412       }
00413     }
00414     oqp->q_counter--;
00415     *oqp->q_wrptr++ = *bp++;
00416     if (oqp->q_wrptr >= oqp->q_top)
00417       oqp->q_wrptr = oqp->q_buffer;
00418 
00419     if (nfy)
00420       nfy(oqp);
00421 
00422     chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
00423     w++;
00424     if (--n == 0)
00425       return w;
00426     chSysLock();
00427   }
00428 }
00429 #endif  /* CH_USE_QUEUES */
00430 
00431 /** @} */