|
ChibiOS/RT
2.5.1 |
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 /** @} */