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