I/O queues improvements, removed half duplex queues.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@926 35acf78f-673a-0410-8e92-d51de3d6d3f4
master
gdisirio 2009-05-01 12:58:05 +00:00
parent e0073441fe
commit 46e56d7349
6 changed files with 267 additions and 590 deletions

View File

@ -82,6 +82,13 @@ Win32-MinGW - ChibiOS/RT simulator and demo into a WIN32 process,
real context switch time, previous benchmarks introduced too much overhead real context switch time, previous benchmarks introduced too much overhead
to the measurement. The STM32 performs the context switch in under 1.48uS. to the measurement. The STM32 performs the context switch in under 1.48uS.
- NEW: Added architecture name strings to the port code. - NEW: Added architecture name strings to the port code.
- NEW: The I/O queues code was improved, now there are 2 separate structures:
InputQueue and Output queues. There are some changes int the queue APIs
in order to make them more symmetrical and functional. Improved the queues
documentation.
- CHANGE: Removed the half duplex queues and half duplex serial drivers because
it was never extensively tested. The code is still available but not as part
of the kernel.
- CHANGE: Removed the chMsgSendWithEvent() function. It is rarely used and - CHANGE: Removed the chMsgSendWithEvent() function. It is rarely used and
the functionality can be re-created with a compound atomic operation. Also the functionality can be re-created with a compound atomic operation. Also
removed the CH_USE_MESSAGES_EVENT configuration option. removed the CH_USE_MESSAGES_EVENT configuration option.

View File

@ -33,250 +33,259 @@
* @details A Semaphore is internally initialized and works as a counter of * @details A Semaphore is internally initialized and works as a counter of
* the bytes contained in the queue. * the bytes contained in the queue.
* *
* @param[out] qp pointer to a @p Queue structure * @param[out] iqp pointer to an @p InputQueue structure
* @param[in] buffer pointer to a memory area allocated as queue buffer * @param[in] buffer pointer to a memory area allocated as queue buffer
* @param[in] size size of the queue buffer * @param[in] size size of the queue buffer
* @param[in] inotify pointer to a callback function that is invoked when * @param[in] inotify pointer to a callback function that is invoked when
* some data is read from the Queue. The value can be * some data is read from the queue. The value can be
* @p NULL. * @p NULL.
*
* @note The callback is invoked from within the S-Locked system state,
* see @ref system_states.
*/ */
void chIQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t inotify) { void chIQInit(InputQueue *iqp, uint8_t *buffer,
size_t size, qnotify_t inotify) {
qp->q_buffer = qp->q_rdptr = qp->q_wrptr = buffer; iqp->q_buffer = iqp->q_rdptr = iqp->q_wrptr = buffer;
qp->q_top = buffer + size; iqp->q_top = buffer + size;
chSemInit(&qp->q_sem, 0); chSemInit(&iqp->q_sem, 0);
qp->q_notify = inotify; iqp->q_notify = inotify;
} }
/** /**
* @brief Resets an input queue. * @brief Resets an input queue.
* @details All the data is lost and the waiting threads resumed. * @details All the data in the input queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
* *
* @param[in] qp pointer to a @p Queue structure * @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 chIQReset(Queue *qp) { void chIQResetI(InputQueue *iqp) {
chSysLock(); iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer;
chSemResetI(&iqp->q_sem, 0);
qp->q_rdptr = qp->q_wrptr = qp->q_buffer;
chSemResetI(&qp->q_sem, 0);
chSysUnlock();
} }
/** /**
* @brief Inserts a byte into an input queue. * @brief Input queue write.
* @details A byte value is written into the low end of an input queue.
* *
* @param[in] qp pointer to a @p Queue structure * @param[in] iqp pointer to an @p InputQueue structure
* @param[in] b the byte value to be written * @param[in] b the byte value to be written in the queue
* @retval Q_OK if the operation is successful. * @return The operation status, it can be one of:
* @retval Q_FULL if the queue is full. * @retval Q_OK if the operation has been completed with success.
* @note This function is the lower side endpoint of the Input Queue. * @retval Q_FULL if the queue is full and the operation cannot be completed.
* @note This function must be called with interrupts disabled or from an
* interrupt handler.
*/ */
msg_t chIQPutI(Queue *qp, uint8_t b) { msg_t chIQPutI(InputQueue *iqp, uint8_t b) {
if (chIQIsFull(qp)) if (chIQIsFull(iqp))
return Q_FULL; return Q_FULL;
*qp->q_wrptr++ = b; *iqp->q_wrptr++ = b;
if (qp->q_wrptr >= qp->q_top) if (iqp->q_wrptr >= iqp->q_top)
qp->q_wrptr = qp->q_buffer; iqp->q_wrptr = iqp->q_buffer;
chSemSignalI(&qp->q_sem); chSemSignalI(&iqp->q_sem);
return Q_OK; return Q_OK;
} }
/** /**
* @brief Gets a byte from the input queue. * @brief Input queue read.
* @details If the queue is empty then the calling thread is suspended until * @details This function reads a byte value from an input queue. If the queue
* a byte arrives in the queue. * is empty then the calling thread is suspended until a byte arrives
* in the queue or a timeout occurs.
* *
* @param[in] qp pointer to a @p Queue structure * @param[in] iqp pointer to an @p InputQueue structure
* @return A byte value from the queue. * @param[in] timeout the number of ticks before the operation timeouts,
* @retval Q_RESET if the queue was reset. * the following special values are allowed:
*/ * - @a TIME_IMMEDIATE immediate timeout.
msg_t chIQGet(Queue *qp) { * - @a TIME_INFINITE no timeout.
uint8_t b; * .
* @return A byte value from the queue or:
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;
}
#if CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT
/**
* @brief Gets a byte from the input queue.
* @details If the queue is empty then the calling thread is suspended until
* a byte arrives in the queue or the specified time expires.
*
* @param[in] qp pointer to a @p Queue structure
* @param[in] time the number of ticks before the operation timeouts
* @return A byte value from the queue.
* @retval Q_TIMEOUT if the specified time expired. * @retval Q_TIMEOUT if the specified time expired.
* @retval Q_RESET if the queue was reset. * @retval Q_RESET if the queue was reset.
* @note The function is available only if the @p CH_USE_QUEUES_TIMEOUT and *
* @p CH_USE_SEMAPHORES_TIMEOUT options are enabled in @p chconf.h. * @note The @p time parameter is only meaningful if the
* @p CH_USE_SEMAPHORES_TIMEOUT kernel option is activated,
* otherwise only the @p TIME_INFINITE value is accepted.
*/ */
msg_t chIQGetTimeout(Queue *qp, systime_t time) { msg_t chIQGetTimeout(InputQueue *iqp, systime_t timeout) {
uint8_t b; uint8_t b;
msg_t msg; msg_t msg;
#if CH_USE_SEMAPHORES_TIMEOUT
chSysLock(); chSysLock();
if ((msg = chSemWaitTimeoutS(&iqp->q_sem, timeout)) < RDY_OK) {
if ((msg = chSemWaitTimeoutS(&qp->q_sem, time)) < RDY_OK) {
chSysUnlock(); chSysUnlock();
return msg; return msg;
} }
b = *qp->q_rdptr++; #else
if (qp->q_rdptr >= qp->q_top) chDbgCheck(timeout == TIME_INFINITE, "chIQGetTimeout");
qp->q_rdptr = qp->q_buffer;
if (qp->q_notify) chSysLock();
qp->q_notify(); if ((msg = chSemWaitS(&iqp->q_sem)) < RDY_OK) {
chSysUnlock();
return msg;
}
#endif
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(); chSysUnlock();
return b; return b;
} }
#endif /* (CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT */
/** /**
* @brief Reads some data from the input queue into the specified buffer. * @brief Non-blocking read.
* @details The function is non-blocking and can return zero if the queue is * @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. * empty.
* *
* @param[in] qp pointer to a @p Queue structure * @param[in] iqp pointer to an @p InputQueue structure
* @param[out] buffer the data buffer * @param[out] buffer pointer to the buffer where the input data is copied
* @param[in] n the maximum amount of data to be read * @param[in] n the maximum amount of data to be transferred
* @return The number of bytes read. * @return The number of bytes transferred.
* @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 * @note The function is not atomic, if you need atomicity it is suggested
* to use a semaphore for mutual exclusion. * to use a semaphore or a mutex for mutual exclusion.
*/ */
size_t chIQRead(Queue *qp, uint8_t *buffer, size_t n) { size_t chIQRead(InputQueue *iqp, uint8_t *buffer, size_t n) {
size_t r = 0; size_t r = 0;
while (n--) { while (n--) {
chSysLock(); chSysLock();
if (chIQIsEmpty(iqp)) {
if (chIQIsEmpty(qp)) {
chSysUnlock(); chSysUnlock();
break; break;
} }
chSemFastWaitI(&qp->q_sem); chSemFastWaitI(&iqp->q_sem);
*buffer++ = *qp->q_rdptr++; *buffer++ = *iqp->q_rdptr++;
if (qp->q_rdptr >= qp->q_top) if (iqp->q_rdptr >= iqp->q_top)
qp->q_rdptr = qp->q_buffer; iqp->q_rdptr = iqp->q_buffer;
chSysUnlock(); chSysUnlock();
r++; r++;
} }
if (r && iqp->q_notify) {
if (r && qp->q_notify) {
chSysLock(); chSysLock();
iqp->q_notify();
qp->q_notify();
chSysUnlock(); chSysUnlock();
} }
return r; return r;
} }
/** /**
* @brief Initializes an output queue. * @brief Initializes an output queue.
* @details A Semaphore is internally initialized and works as a counter of the * @details A Semaphore is internally initialized and works as a counter of
* free bytes in the queue. * the free bytes in the queue.
* *
* @param[out] qp pointer to a @p Queue structure * @param[out] oqp pointer to an @p OutputQueue structure
* @param[in] buffer pointer to a memory area allocated as queue buffer * @param[in] buffer pointer to a memory area allocated as queue buffer
* @param[in] size size of the queue buffer * @param[in] size size of the queue buffer
* @param[in] onotify pointer to a callback function that is invoked when * @param[in] onotify pointer to a callback function that is invoked when
* some data is written in the Queue. The value can be * some data is written to the queue. The value can be
* @p NULL. * @p NULL.
*
* @note The callback is invoked from within the S-Locked system state,
* see @ref system_states.
*/ */
void chOQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t onotify) { void chOQInit(OutputQueue *oqp, uint8_t *buffer,
size_t size, qnotify_t onotify) {
qp->q_buffer = qp->q_rdptr = qp->q_wrptr = buffer; oqp->q_buffer = oqp->q_rdptr = oqp->q_wrptr = buffer;
qp->q_top = buffer + size; oqp->q_top = buffer + size;
chSemInit(&qp->q_sem, size); chSemInit(&oqp->q_sem, size);
qp->q_notify = onotify; oqp->q_notify = onotify;
} }
/** /**
* @brief Resets an Output Queue. * @brief Resets an output queue.
* @details All the data is lost and the waiting threads resumed. * @details All the data in the output queue is erased and lost, any waiting
* thread is resumed with status @p Q_RESET.
* *
* @param[in] qp pointer to a @p Queue structure * @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 chOQReset(Queue *qp) { 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.
* @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] 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.
*
* @note The @p time parameter is only meaningful if the
* @p CH_USE_SEMAPHORES_TIMEOUT kernel option is activated,
* otherwise only the @p TIME_INFINITE value is accepted.
*/
msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t timeout) {
msg_t msg;
#if CH_USE_SEMAPHORES_TIMEOUT
chSysLock();
if ((msg = chSemWaitTimeoutS(&oqp->q_sem, timeout)) < RDY_OK) {
chSysUnlock();
return msg;
}
#else
chDbgCheck(timeout == TIME_INFINITE, "chOQPutTimeout");
chSysLock(); chSysLock();
if ((msg = chSemWaitS(&oqp->q_sem)) < RDY_OK) {
chSysUnlock();
return msg;
}
#endif
*oqp->q_wrptr++ = b;
if (oqp->q_wrptr >= oqp->q_top)
oqp->q_wrptr = oqp->q_buffer;
qp->q_rdptr = qp->q_wrptr = qp->q_buffer; if (oqp->q_notify)
chSemResetI(&qp->q_sem, (cnt_t)(qp->q_top - qp->q_buffer)); oqp->q_notify();
chSysUnlock(); chSysUnlock();
return Q_OK;
} }
/** /**
* @brief Inserts a byte in the output queue. * @brief Output queue read.
* @details If the queue is full then the thread is suspended until the queue * @details A byte value is read from the low end of an output queue.
* has free space available.
* *
* @param[in] qp pointer to a @p Queue structure * @param[in] oqp pointer to an @p OutputQueue structure
* @param[in] b the byte value to be written * @return The byte value from the queue or:
*/
void chOQPut(Queue *qp, uint8_t 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();
}
/**
* @brief Gets a byte from an output queue.
*
* @param[in] qp pointer to a @p Queue structure
* @return The byte value from the queue.
* @retval Q_EMPTY if the queue is empty. * @retval 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.
*/ */
msg_t chOQGetI(Queue *qp) { msg_t chOQGetI(OutputQueue *oqp) {
uint8_t b; uint8_t b;
if (chOQIsEmpty(qp)) if (chOQIsEmpty(oqp))
return Q_EMPTY; return Q_EMPTY;
b = *qp->q_rdptr++; b = *oqp->q_rdptr++;
if (qp->q_rdptr >= qp->q_top) if (oqp->q_rdptr >= oqp->q_top)
qp->q_rdptr = qp->q_buffer; oqp->q_rdptr = oqp->q_buffer;
chSemSignalI(&qp->q_sem); chSemSignalI(&oqp->q_sem);
return b; return b;
} }
@ -293,218 +302,43 @@ msg_t chOQGetI(Queue *qp) {
* @note The function is not atomic, if you need atomicity it is suggested * @note The function is not atomic, if you need atomicity it is suggested
* to use a semaphore for mutual exclusion. * to use a semaphore for mutual exclusion.
*/ */
size_t chOQWrite(Queue *qp, uint8_t *buffer, size_t n) { /**
* @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; size_t w = 0;
while (n--) { while (n--) {
chSysLock(); chSysLock();
if (chOQIsFull(oqp)) {
if (chOQIsFull(qp)) {
chSysUnlock(); chSysUnlock();
break; break;
} }
chSemFastWaitI(&qp->q_sem); chSemFastWaitI(&oqp->q_sem);
*qp->q_wrptr++ = *buffer++; *oqp->q_wrptr++ = *buffer++;
if (qp->q_wrptr >= qp->q_top) if (oqp->q_wrptr >= oqp->q_top)
qp->q_wrptr = qp->q_buffer; oqp->q_wrptr = oqp->q_buffer;
chSysUnlock(); chSysUnlock();
w++; w++;
} }
if (w && oqp->q_notify) {
if (w && qp->q_notify) {
chSysLock(); chSysLock();
oqp->q_notify();
qp->q_notify();
chSysUnlock(); chSysUnlock();
} }
return w; return w;
} }
#endif /* CH_USE_QUEUES */ #endif /* CH_USE_QUEUES */
#if CH_USE_QUEUES_HALFDUPLEX
/**
* @brief Initializes an half duplex queue.
*
* @param[out] qp pointer to the @p HalfDuplexQueue structure
* @param[in] buffer pointer to a memory area allocated as buffer for the queue
* @param[in] size the 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.
* @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.
*/
void chHDQInit(HalfDuplexQueue *qp, uint8_t *buffer, size_t size,
qnotify_t inotify, qnotify_t 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;
}
/**
* @brief Reads a byte from the receive queue.
* @details If the queue is empty or is in transmission mode then the invoking
* thread is suspended.
*
* @param[in] qp pointer to a @p HalfDuplexQueue structure
* @return The byte value.
* @retval Q_RESET if the queue was reset.
*/
msg_t chHDQGetReceive(HalfDuplexQueue *qp) {
uint8_t 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;
}
#if CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT
/**
* @brief Reads a byte from the receive queue.
* @details If the queue is empty or is in transmission mode then the invoking
* thread is suspended.
*
* @param[in] qp pointer to a @p HalfDuplexQueue structure
* @param[in] time the number of ticks before the operation timouts
* @return The byte value.
* @retval Q_TIMEOUT if a timeout occurs.
* @note The function is available only if the @p CH_USE_QUEUES_TIMEOUT and
* @p CH_USE_SEMAPHORES_TIMEOUT options are enabled in @p chconf.h.
*/
msg_t chHDQGetReceiveTimeout(HalfDuplexQueue *qp, systime_t time) {
uint8_t b;
msg_t 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 /* CH_USE_QUEUES_TIMEOUT && CH_USE_SEMAPHORES_TIMEOUT */
/**
* @brief Writes a byte into the transmit queue.
* @details If the buffer contains unread input data then the the buffer is
* cleared and the queue goes in transmission mode.
*
* @param[in] qp pointer to a @p HalfDuplexQueue structure
* @param[in] b the byte value to be written
*/
void chHDQPutTransmit(HalfDuplexQueue *qp, uint8_t 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();
}
/**
* @brief Gets a byte from the transmit queue.
*
* @param[in] qp pointer to a @p HalfDuplexQueue structure
* @return The byte value.
* @retval 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.
*/
msg_t chHDQGetTransmitI(HalfDuplexQueue *qp) {
uint8_t 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;
}
/**
* @brief Writes a byte into the receive queue.
* @details If the queue is in transmission mode then the byte is lost.
*
* @param[in] qp pointer to a @p HalfDuplexQueue structure
* @param[in] b the byte value to be written
* @retval Q_OK if the operation is successful.
* @retval 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.
*/
msg_t chHDQPutReceiveI(HalfDuplexQueue *qp, uint8_t 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 */
/** @} */ /** @} */

View File

@ -123,96 +123,4 @@ dflags_t chFDDGetAndClearFlags(FullDuplexDriver *sd) {
} }
#endif /* CH_USE_SERIAL_FULLDUPLEX */ #endif /* CH_USE_SERIAL_FULLDUPLEX */
#if CH_USE_SERIAL_HALFDUPLEX
/**
* @brief Initializes a generic half duplex driver.
* @details The HW dependent part of the initialization has to be performed
* outside, usually in the hardware initialization code.
*
* @param[out] sd pointer to a @p HalfDuplexDriver structure
* @param[in] b pointer to a memory area allocated for the queue buffer
* @param[in] size the buffer size
* @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.
* @param[in] onotify pointer to a callback function that is invoked when
* some data is written in the queue. The value can be
* @p NULL.
*/
void chHDDInit(HalfDuplexDriver *sd, uint8_t *b, size_t size,
qnotify_t inotify, qnotify_t onotify) {
chDbgCheck((sd != NULL) && (b != NULL) && (size > 0), "chHDDInit");
chHDQInit(&sd->sd_queue, b, size, inotify, onotify);
chEvtInit(&sd->sd_ievent);
chEvtInit(&sd->sd_oevent);
chEvtInit(&sd->sd_sevent);
sd->sd_flags = SD_NO_ERROR;
}
/**
* @brief Handles incoming data.
* @details This function must be called from the input interrupt service
* routine in order to enqueue incoming data and generate the
* related events.
* @param[in] sd pointer to a @p FullDuplexDriver structure
* @param[in] b the byte to be written in the driver's input queue
*/
void chHDDIncomingDataI(HalfDuplexDriver *sd, uint8_t b) {
if (chHDQPutReceiveI(&sd->sd_queue, b) < Q_OK)
chHDDAddFlagsI(sd, SD_OVERRUN_ERROR);
else
chEvtBroadcastI(&sd->sd_ievent);
}
/**
* @brief Handles outgoing data.
* @details Must be called from the output interrupt service routine in order
* to get the next byte to be transmitted.
*
* @param[in] sd pointer to a @p HalfDuplexDriver structure
* @return The byte value read from the driver's output queue.
* @retval Q_EMPTY if the queue is empty (the lower driver usually disables
* the interrupt source when this happens).
*/
msg_t chHDDRequestDataI(HalfDuplexDriver *sd) {
msg_t b = chHDQGetTransmitI(&sd->sd_queue);
if (b < Q_OK)
chEvtBroadcastI(&sd->sd_oevent);
return b;
}
/**
* @brief Handles communication events/errors.
* @details Must be called from the I/O interrupt service routine in order to
* notify I/O conditions as errors, signals change etc.
*
* @param[in] sd pointer to a @p HalfDuplexDriver structure
* @param[in] mask condition flags to be added to the mask
*/
void chHDDAddFlagsI(HalfDuplexDriver *sd, dflags_t mask) {
sd->sd_flags |= mask;
chEvtBroadcastI(&sd->sd_sevent);
}
/**
* @brief Returns and clears the errors mask associated to the driver.
*
* @param[in] sd pointer to a @p HalfDuplexDriver structure
* @return The condition flags modified since last time this function was
* invoked.
*/
dflags_t chHDDGetAndClearFlags(HalfDuplexDriver *sd) {
dflags_t mask;
mask = sd->sd_flags;
sd->sd_flags = SD_NO_ERROR;
return mask;
}
#endif /* CH_USE_SERIAL_HALFDUPLEX */
/** @} */ /** @} */

View File

@ -43,9 +43,13 @@ typedef void (*qnotify_t)(void);
#if CH_USE_QUEUES #if CH_USE_QUEUES
/** /**
* @brief I/O queue structure. * @brief Generic I/O queue structure.
* @details This structure is used by both Input and Output Queues, * @details This structure represents a generic Input or Output asymmetrical
* the difference is on how the semaphore is initialized. * queue. The queue is asymmetrical because one end is meant to be
* accessed from a thread context, and thus can be blocking, the other
* end is accessible from interrupt handlers or from within a kernel
* lock zone (see <b>I-Locked</b> and <b>S-Locked</b> states in
* @ref system_states) and is non-blocking.
*/ */
typedef struct { typedef struct {
uint8_t *q_buffer; /**< Pointer to the queue buffer.*/ uint8_t *q_buffer; /**< Pointer to the queue buffer.*/
@ -55,7 +59,7 @@ typedef struct {
uint8_t *q_rdptr; /**< Read pointer.*/ uint8_t *q_rdptr; /**< Read pointer.*/
Semaphore q_sem; /**< Counter @p Semaphore.*/ Semaphore q_sem; /**< Counter @p Semaphore.*/
qnotify_t q_notify; /**< Data notification callback.*/ qnotify_t q_notify; /**< Data notification callback.*/
} Queue; } GenericQueue;
/** Returns the queue's buffer size. */ /** Returns the queue's buffer size. */
#define chQSize(q) ((q)->q_top - (q)->q_buffer) #define chQSize(q) ((q)->q_top - (q)->q_buffer)
@ -68,110 +72,86 @@ typedef struct {
*/ */
#define chQSpace(q) chSemGetCounterI(&(q)->q_sem) #define chQSpace(q) chSemGetCounterI(&(q)->q_sem)
/** Evaluates to TRUE if the specified Input Queue is empty. */ /**
* @brief Input queue structure.
* @details This structure represents a generic asymmetrical input queue.
* Writing in the queue is non-blocking and can be performed from
* interrupt handlers or from within a kernel lock zone (see
* <b>I-Locked</b> and <b>S-Locked</b> states in @ref system_states).
* Reading the queue can be a blocking operation and is supposed to
* be performed by a system thread.
* @extends GenericQueue
*/
typedef GenericQueue InputQueue;
/** Evaluates to @p TRUE if the specified Input Queue is empty. */
#define chIQIsEmpty(q) (chQSpace(q) <= 0) #define chIQIsEmpty(q) (chQSpace(q) <= 0)
/** Evaluates to TRUE if the specified Input Queue is full. */ /** Evaluates to @p TRUE if the specified Input Queue is full. */
#define chIQIsFull(q) (chQSpace(q) >= chQSize(q)) #define chIQIsFull(q) (chQSpace(q) >= chQSize(q))
/** Evaluates to TRUE if the specified Output Queue is empty. */ /**
* @brief Input queue read.
* @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.
*
* @param[in] iqp pointer to an @p InputQueue structure
* @return A byte value from the queue or:
* @retval Q_RESET if the queue was reset.
*/
#define chIQGet(iqp) chIQGetTimeout(iqp, TIME_INFINITE)
/**
* @brief Output queue structure.
* @details This structure represents a generic asymmetrical output queue.
* Reading from the queue is non-blocking and can be performed from
* interrupt handlers or from within a kernel lock zone (see
* <b>I-Locked</b> and <b>S-Locked</b> states in @ref system_states).
* Writing the queue can be a blocking operation and is supposed to
* be performed by a system thread.
* @extends GenericQueue
*/
typedef GenericQueue OutputQueue;
/** Evaluates to @p TRUE if the specified Output Queue is empty. */
#define chOQIsEmpty(q) (chQSpace(q) >= chQSize(q)) #define chOQIsEmpty(q) (chQSpace(q) >= chQSize(q))
/** Evaluates to TRUE if the specified Output Queue is full. */ /** Evaluates to @p TRUE if the specified Output Queue is full. */
#define chOQIsFull(q) (chQSpace(q) <= 0) #define chOQIsFull(q) (chQSpace(q) <= 0)
/**
* @brief Output queue write.
* @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.
*
* @param[in] oqp pointer to an @p OutputQueue structure
* @param[in] b the byte value to be written in the queue
* @return The operation status:
* @retval Q_OK if the operation succeeded.
* @retval Q_RESET if the queue was reset.
*/
#define chOQPut(oqp, b) chOQPutTimeout(oqp, b, TIME_INFINITE)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* void chIQInit(InputQueue *qp, uint8_t *buffer, size_t size, qnotify_t inotify);
* Input Queues functions. An Input Queue is usually written into by an void chIQResetI(InputQueue *qp);
* interrupt handler and read from a thread. msg_t chIQPutI(InputQueue *qp, uint8_t b);
*/ msg_t chIQGetTimeout(InputQueue *qp, systime_t timeout);
void chIQInit(Queue *qp, uint8_t *buffer, size_t size, qnotify_t inotify); size_t chIQRead(InputQueue *qp, uint8_t *buffer, size_t n);
void chIQReset(Queue *qp); void chOQInit(OutputQueue *queue, uint8_t *buffer, size_t size, qnotify_t onotify);
msg_t chIQPutI(Queue *qp, uint8_t b); void chOQResetI(OutputQueue *queue);
msg_t chIQGet(Queue *qp); msg_t chOQPutTimeout(OutputQueue *queue, uint8_t b, systime_t timeout);
size_t chIQRead(Queue *qp, uint8_t *buffer, size_t n); msg_t chOQGetI(OutputQueue *queue);
#if CH_USE_QUEUES_TIMEOUT size_t chOQWrite(OutputQueue *queue, uint8_t *buffer, size_t n);
msg_t chIQGetTimeout(Queue *qp, systime_t time);
#endif
/*
* Output Queues functions. An Output Queue is usually written into by a
* thread and read from an interrupt handler.
*/
void chOQInit(Queue *queue, uint8_t *buffer, size_t size, qnotify_t onotify);
void chOQReset(Queue *queue);
void chOQPut(Queue *queue, uint8_t b);
msg_t chOQGetI(Queue *queue);
size_t chOQWrite(Queue *queue, uint8_t *buffer, size_t n);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* CH_USE_QUEUES */ #endif /* CH_USE_QUEUES */
#if CH_USE_QUEUES_HALFDUPLEX
/**
* @brief Half duplex queue structure.
*/
typedef struct {
uint8_t *hdq_buffer; /**< Pointer to the queue buffer.*/
uint8_t *hdq_top; /**< Pointer to the first location
after the buffer. */
uint8_t *hdq_wrptr; /**< Write pointer.*/
uint8_t *hdq_rdptr; /**< Read pointer.*/
Semaphore hdq_isem; /**< Input counter @p Semaphore.*/
Semaphore hdq_osem; /**< Output counter @p Semaphore.*/
qnotify_t hdq_inotify; /**< Input data notification
callback.*/
qnotify_t hdq_onotify; /**< Output data notification
callback.*/
} HalfDuplexQueue;
/** Returns the queue's buffer size. */
#define chHDQSize(q) ((q)->hdq_top - (q)->hdq_buffer)
/**
* Returns the queue space when in transmission mode.
* @note The returned value can be less than zero when there are waiting
* threads on the internal semaphore.
*/
#define chHDQEmptySpace(q) chSemGetCounterI(&(q)->hdq_osem)
/**
* Returns the number of the bytes in the queue when in receive mode.
* @note The returned value can be less than zero when there are waiting
* threads on the internal semaphore.
*/
#define chHDQFilledSpace(q) chSemGetCounterI(&(q)->hdq_isem)
/** Evaluates to TRUE if the queue is in transmit mode. */
#define chHDQIsTransmitting(q) (chHDQEmptySpace(q) < chHDQSize(q))
/** Evaluates to TRUE if the queue is in receive mode. */
#define chHDQIsReceiving(q) (chHDQEmptySpaceQ(q) >= chHDQSize(q))
/** Evaluates to TRUE if the receive queue is full. */
#define chHDQIsFullReceive(q) (chHDQFilledSpace(q) >= chHDQSize(q))
#ifdef __cplusplus
extern "C" {
#endif
void chHDQInit(HalfDuplexQueue *qp, uint8_t *buffer, size_t size,
qnotify_t inotify, qnotify_t onotify);
msg_t chHDQGetReceive(HalfDuplexQueue *qp);
void chHDQPutTransmit(HalfDuplexQueue *qp, uint8_t b);
msg_t chHDQGetTransmitI(HalfDuplexQueue *qp);
msg_t chHDQPutReceiveI(HalfDuplexQueue *qp, uint8_t b);
#if CH_USE_QUEUES_TIMEOUT
msg_t chHDQGetReceiveTimeout(HalfDuplexQueue *qp, systime_t time);
#endif
#ifdef __cplusplus
}
#endif
#endif /* CH_USE_QUEUES_HALFDUPLEX */
#endif /* _QUEUES_H_ */ #endif /* _QUEUES_H_ */
/** @} */ /** @} */

View File

@ -54,14 +54,14 @@ typedef struct {
/** Input queue. Incoming data can be read from this queue by using the /** Input queue. Incoming data can be read from this queue by using the
* queues APIs.*/ * queues APIs.*/
Queue sd_iqueue; InputQueue sd_iqueue;
/** Data Available @p EventSource. This event is generated when some incoming /** Data Available @p EventSource. This event is generated when some incoming
* data is inserted in the Input @p Queue.*/ * data is inserted in the Input @p Queue.*/
EventSource sd_ievent; EventSource sd_ievent;
/** Output queue. Outgoing data can be written to this Output @p Queue by /** Output queue. Outgoing data can be written to this Output @p Queue by
* using the queues APIs.*/ * using the queues APIs.*/
Queue sd_oqueue; OutputQueue sd_oqueue;
/** Data Transmitted @p EventSource. This event is generated when the /** Data Transmitted @p EventSource. This event is generated when the
* Output @p Queue is empty.*/ * Output @p Queue is empty.*/
EventSource sd_oevent; EventSource sd_oevent;
@ -110,58 +110,6 @@ extern "C" {
#endif /* CH_USE_SERIAL_FULLDUPLEX */ #endif /* CH_USE_SERIAL_FULLDUPLEX */
#if CH_USE_SERIAL_HALFDUPLEX
/**
* @brief Full Duplex Serial Driver main structure.
*/
typedef struct {
/** Data queue. Transmit/receive @p HalfDuplexQueue.*/
HalfDuplexQueue sd_queue;
/** Data Available @p EventSource. This event is generated when some
* incoming data is inserted in the receive queue.*/
EventSource sd_ievent;
/** Data Transmitted @p EventSource. This event is generated when the
* transmission queue is empty and the driver can either transmit more
* data or enter receive mode.*/
EventSource sd_oevent;
/** I/O driver status flags. This field should not be read directly but
* the @p chHDDGetAndClearFlags() funtion should be used
* instead.*/
dflags_t sd_flags;
/** Status Change Event Source. This event is generated when a condition
* flag was changed.*/
EventSource sd_sevent;
} HalfDuplexDriver;
#ifdef __cplusplus
extern "C" {
#endif
void chHDDInit(HalfDuplexDriver *sd, uint8_t *b, size_t size,
qnotify_t inotify, qnotify_t onotify);
void chHDDIncomingDataI(HalfDuplexDriver *sd, uint8_t b);
msg_t chHDDRequestDataI(HalfDuplexDriver *sd);
void chHDDAddFlagsI(HalfDuplexDriver *sd, dflags_t mask);
dflags_t chHDDGetAndClearFlags(HalfDuplexDriver *sd);
#ifdef __cplusplus
}
#endif
/** @see chHDQGetReceive()*/
#define chHDDGetReceive(sd) \
chHDQGetReceive(&(sd)->sd_queue)
/** @see chHDQGetReceiveTimeout()*/
#define chHDDGetReceiveTimeout(sd, t) \
chHDQGetReceiveTimeout(&(sd)->sd_queue, t)
/** @see chHDQPutTransmit()*/
#define chHDDPutTransmit(sd, b) \
chHDQPutTransmit(&(sd)->sd_queue, b)
#endif /* CH_USE_SERIAL_HALFDUPLEX */
#endif /* _SERIAL_H_ */ #endif /* _SERIAL_H_ */
/** @} */ /** @} */

View File

@ -318,7 +318,7 @@ static char *bmk8_gettest(void) {
static void bmk8_execute(void) { static void bmk8_execute(void) {
static uint8_t ib[16]; static uint8_t ib[16];
static Queue iq; static InputQueue iq;
chIQInit(&iq, ib, sizeof(ib), NULL); chIQInit(&iq, ib, sizeof(ib), NULL);
uint32_t n = 0; uint32_t n = 0;