aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/src/hal_buffers.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/src/hal_buffers.c')
-rw-r--r--os/hal/src/hal_buffers.c349
1 files changed, 329 insertions, 20 deletions
diff --git a/os/hal/src/hal_buffers.c b/os/hal/src/hal_buffers.c
index 85a26383b..8a468d9fe 100644
--- a/os/hal/src/hal_buffers.c
+++ b/os/hal/src/hal_buffers.c
@@ -49,10 +49,9 @@
/**
* @brief Initializes an input buffers queue object.
*
- * @param[out] ibqp pointer to the @p io_buffers_queue_t object
+ * @param[out] ibqp pointer to the @p input_buffers_queue_t object
* @param[in] bp pointer to a memory area allocated for buffers
- * @param[in] size buffer size, including the size field which is the
- * size of a @p size_t
+ * @param[in] size buffers size
* @param[in] n number of buffers
* @param[in] infy callback called when a buffer is returned to the queue
* @param[in] link application defined pointer
@@ -61,16 +60,16 @@
*/
void ibqObjectInit(input_buffers_queue_t *ibqp, uint8_t *bp,
size_t size, size_t n,
- dbnotify_t infy, void *link) {
+ bqnotify_t infy, void *link) {
- osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= sizeof(size_t) + 2));
+ osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= 2));
osalThreadQueueObjectInit(&ibqp->waiting);
ibqp->bcounter = 0;
ibqp->brdptr = bp;
ibqp->bwrptr = bp;
- ibqp->btop = bp + (size * n); /* Pre-calculated for speed. */
- ibqp->bsize = size;
+ ibqp->btop = bp + ((size + sizeof (size_t)) * n);
+ ibqp->bsize = size + sizeof (size_t);
ibqp->bn = n;
ibqp->buffers = bp;
ibqp->ptr = NULL;
@@ -124,14 +123,14 @@ uint8_t *ibqGetEmptyBufferI(input_buffers_queue_t *ibqp) {
}
/**
- * @brief Posts a new filled buffer in the queue.
+ * @brief Posts a new filled buffer to the queue.
*
* @param[in] ibqp pointer to the @p input_buffers_queue_t object
* @param[in] size used size of the buffer
*
* @iclass
*/
-void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) {
+void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size) {
osalDbgCheckClassI();
osalDbgAssert(!ibqIsFullI(ibqp), "buffers queue full");
@@ -151,7 +150,7 @@ void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) {
}
/**
- * @brief Gets the next data-filled buffer from the queue.
+ * @brief Gets the next filled buffer from the queue.
* @note The function always acquires the same buffer if called repeatedly.
* @post After calling the function the fields @p ptr and @p top are set
* at beginning and end of the buffer data or @NULL if the queue
@@ -168,11 +167,12 @@ void ibqPostBufferI(input_buffers_queue_t *ibqp, size_t size) {
* @retval MSG_TIMEOUT if the specified time expired.
* @retval MSG_RESET if the queue has been reset.
*
- * @iclass
+ * @sclass
*/
-msg_t ibqGetDataTimeoutI(input_buffers_queue_t *ibqp, systime_t timeout) {
+msg_t ibqGetFullBufferTimeoutS(input_buffers_queue_t *ibqp,
+ systime_t timeout) {
- osalDbgCheckClassI();
+ osalDbgCheckClassS();
while (ibqIsEmptyI(ibqp)) {
msg_t msg = osalThreadEnqueueTimeoutS(&ibqp->waiting, timeout);
@@ -189,14 +189,14 @@ msg_t ibqGetDataTimeoutI(input_buffers_queue_t *ibqp, systime_t timeout) {
}
/**
- * @brief Releases the data buffer back in the queue.
+ * @brief Releases the buffer back in the queue.
* @note The object callback is called after releasing the buffer.
*
- * @param[out] ibqp pointer to the @p input_buffers_queue_t object
+ * @param[in] ibqp pointer to the @p input_buffers_queue_t object
*
* @iclass
*/
-void ibqReleaseDataI(input_buffers_queue_t *ibqp) {
+void ibqReleaseEmptyBufferI(input_buffers_queue_t *ibqp) {
osalDbgCheckClassI();
osalDbgAssert(!ibqIsEmptyI(ibqp), "buffers queue empty");
@@ -238,10 +238,13 @@ void ibqReleaseDataI(input_buffers_queue_t *ibqp) {
msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, systime_t timeout) {
msg_t msg;
+ chSysLock();
+
/* This condition indicates that a new buffer must be acquired.*/
if (ibqp->ptr == NULL) {
- msg = ibqGetDataTimeoutI(ibqp, timeout);
+ msg = ibqGetFullBufferTimeoutS(ibqp, timeout);
if (msg != MSG_OK) {
+ chSysUnlock();
return msg;
}
}
@@ -253,9 +256,11 @@ msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, systime_t timeout) {
/* If the current buffer has been fully read then it is returned as
empty in the queue.*/
if (ibqp->ptr >= ibqp->top) {
- ibqReleaseDataI(ibqp);
+ ibqReleaseEmptyBufferI(ibqp);
}
+ chSysUnlock();
+
return msg;
}
@@ -302,11 +307,13 @@ size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp,
in this case next becomes a very high number because the system
time is an unsigned type.*/
if (next_timeout > timeout) {
+ osalSysUnlock();
return MSG_TIMEOUT;
}
- msg_t msg = ibqGetDataTimeoutI(ibqp, next_timeout);
+ msg_t msg = ibqGetFullBufferTimeoutS(ibqp, next_timeout);
if (msg != MSG_OK) {
+ osalSysUnlock();
return msg;
}
}
@@ -330,7 +337,309 @@ size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp,
/* Has the current data buffer been finished? if so then release it.*/
if (ibqp->ptr >= ibqp->top) {
- ibqReleaseDataI(ibqp);
+ ibqReleaseEmptyBufferI(ibqp);
+ }
+ }
+ osalSysUnlock();
+
+ return r;
+}
+
+/**
+ * @brief Initializes an output buffers queue object.
+ *
+ * @param[out] obqp pointer to the @p output_buffers_queue_t object
+ * @param[in] bp pointer to a memory area allocated for buffers
+ * @param[in] size buffers size
+ * @param[in] n number of buffers
+ * @param[in] onfy callback called when a buffer is posted in the queue
+ * @param[in] link application defined pointer
+ *
+ * @init
+ */
+void obqObjectInit(output_buffers_queue_t *obqp, uint8_t *bp,
+ size_t size, size_t n,
+ bqnotify_t onfy, void *link) {
+
+ osalDbgCheck((obqp != NULL) && (bp != NULL) && (size >= 2));
+
+ osalThreadQueueObjectInit(&obqp->waiting);
+ obqp->bcounter = n;
+ obqp->brdptr = bp;
+ obqp->bwrptr = bp;
+ obqp->btop = bp + ((size + sizeof (size_t)) * n);
+ obqp->bsize = size + sizeof (size_t);
+ obqp->bn = n;
+ obqp->buffers = bp;
+ obqp->ptr = NULL;
+ obqp->top = NULL;
+ obqp->notify = onfy;
+ obqp->link = link;
+}
+
+/**
+ * @brief Resets an output buffers queue.
+ * @details All the data in the output buffers queue is erased and lost, any
+ * waiting thread is resumed with status @p MSG_RESET.
+ * @note A reset operation can be used by a low level driver in order to
+ * obtain immediate attention from the high level layers.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ *
+ * @iclass
+ */
+void obqResetI(output_buffers_queue_t *obqp) {
+
+ osalDbgCheckClassI();
+
+ obqp->bcounter = bqSizeX(obqp);
+ obqp->brdptr = obqp->buffers;
+ obqp->bwrptr = obqp->buffers;
+ obqp->ptr = NULL;
+ obqp->top = NULL;
+ osalThreadDequeueAllI(&obqp->waiting, MSG_RESET);
+}
+
+/**
+ * @brief Gets the next filled buffer from the queue.
+ * @note The function always returns the same buffer if called repeatedly.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ * @param[out] sizep pointer to the filled buffer size
+ * @return A pointer to the filled buffer.
+ * @retval NULL if the queue is empty.
+ *
+ * @iclass
+ */
+uint8_t *obqGetFullBufferI(output_buffers_queue_t *obqp,
+ size_t *sizep) {
+
+ osalDbgCheckClassI();
+
+ if (obqIsEmptyI(obqp)) {
+ return NULL;
+ }
+
+ /* Buffer size.*/
+ *sizep = *((size_t *)obqp->brdptr);
+
+ return obqp->brdptr + sizeof (size_t);
+}
+
+/**
+ * @brief Releases the next filled buffer back in the queue.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ *
+ * @iclass
+ */
+void obqReleaseEmptyBufferI(output_buffers_queue_t *obqp) {
+
+ osalDbgCheckClassI();
+ osalDbgAssert(!obqIsEmptyI(obqp), "buffers queue empty");
+
+ /* Freeing a buffer slot in the queue.*/
+ obqp->bcounter++;
+ obqp->brdptr += obqp->bsize;
+ if (obqp->brdptr >= obqp->btop) {
+ obqp->brdptr = obqp->buffers;
+ }
+}
+
+/**
+ * @brief Gets the next empty buffer from the queue.
+ * @note The function always acquires the same buffer if called repeatedly.
+ * @post After calling the function the fields @p ptr and @p top are set
+ * at beginning and end of the buffer data or @NULL if the queue
+ * is empty.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ * @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 MSG_OK if a buffer has been acquired.
+ * @retval MSG_TIMEOUT if the specified time expired.
+ * @retval MSG_RESET if the queue has been reset.
+ *
+ * @sclass
+ */
+msg_t obqGetEmptyBufferTimeoutS(output_buffers_queue_t *obqp,
+ systime_t timeout) {
+
+ osalDbgCheckClassS();
+
+ while (obqIsEmptyI(obqp)) {
+ msg_t msg = osalThreadEnqueueTimeoutS(&obqp->waiting, timeout);
+ if (msg < MSG_OK) {
+ return msg;
+ }
+ }
+
+ /* Setting up the "current" buffer and its boundary.*/
+ obqp->ptr = obqp->brdptr + sizeof (size_t);
+ obqp->top = obqp->ptr + *((size_t *)obqp->brdptr);
+
+ return MSG_OK;
+}
+
+/**
+ * @brief Posts a new filled buffer to the queue.
+ * @note The object callback is called after releasing the buffer.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ * @param[in] size used size of the buffer
+ *
+ * @iclass
+ */
+void obqPostFullBufferI(output_buffers_queue_t *obqp, size_t size) {
+
+ osalDbgCheckClassI();
+ osalDbgAssert(!obqIsFullI(obqp), "buffers queue full");
+
+ /* Writing size field in the buffer.*/
+ *((size_t *)obqp->bwrptr) = size;
+
+ /* Posting the buffer in the queue.*/
+ obqp->bcounter--;
+ obqp->bwrptr += obqp->bsize;
+ if (obqp->bwrptr >= obqp->btop) {
+ obqp->bwrptr = obqp->buffers;
+ }
+
+ /* No "current" buffer.*/
+ obqp->ptr = NULL;
+
+ /* Notifying the buffer release.*/
+ if (obqp->notify != NULL) {
+ obqp->notify(obqp);
+ }
+}
+
+/**
+ * @brief Output queue write with timeout.
+ * @details This function writes a byte value to an output queue. If
+ * the queue is full then the calling thread is suspended until a
+ * new buffer is freed in the queue or a timeout occurs.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ * @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 A byte value from the queue.
+ * @retval MSG_TIMEOUT if the specified time expired.
+ * @retval MSG_RESET if the queue has been reset.
+ *
+ * @api
+ */
+msg_t obqPutTimeout(output_buffers_queue_t *obqp, uint8_t b,
+ systime_t timeout) {
+ msg_t msg;
+
+ osalSysLock();
+
+ /* This condition indicates that a new buffer must be acquired.*/
+ if (obqp->ptr == NULL) {
+ msg = obqGetEmptyBufferTimeoutS(obqp, timeout);
+ if (msg != MSG_OK) {
+ chSysUnlock();
+ return msg;
+ }
+ }
+
+ /* Writing the byte to the buffer.*/
+ obqp->bcounter--;
+ *obqp->bwrptr++ = b;
+
+ /* If the current buffer has been fully written then it is posted as
+ full in the queue.*/
+ if (obqp->ptr >= obqp->top) {
+ obqPostFullBufferI(obqp, obqp->bsize);
+ }
+
+ osalSysUnlock();
+
+ return MSG_OK;
+}
+
+/**
+ * @brief Output queue write with timeout.
+ * @details The function writes data from a buffer to an output queue. The
+ * operation completes when the specified amount of data has been
+ * transferred or after the specified timeout or if the queue has
+ * been reset.
+ * @note The function is not atomic, if you need atomicity it is suggested
+ * to use a semaphore or a mutex for mutual exclusion.
+ *
+ * @param[in] obqp pointer to the @p output_buffers_queue_t object
+ * @param[in] bp pointer to the data buffer
+ * @param[in] n the maximum amount of data to be transferred, the
+ * value 0 is reserved
+ * @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 number of bytes effectively transferred.
+ *
+ * @api
+ */
+size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp,
+ size_t n, systime_t timeout) {
+ size_t r = 0;
+ systime_t deadline;
+
+ osalSysLock();
+
+ /* Time window for the whole operation.*/
+ deadline = osalOsGetSystemTimeX() + timeout;
+
+ while (r < n) {
+ size_t size;
+
+ /* This condition indicates that a new buffer must be acquired.*/
+ if (obqp->ptr == NULL) {
+ systime_t next_timeout = deadline - osalOsGetSystemTimeX();
+
+ /* Handling the case where the system time went past the deadline,
+ in this case next becomes a very high number because the system
+ time is an unsigned type.*/
+ if (next_timeout > timeout) {
+ osalSysUnlock();
+ return MSG_TIMEOUT;
+ }
+
+ msg_t msg = obqGetEmptyBufferTimeoutS(obqp, next_timeout);
+ if (msg != MSG_OK) {
+ osalSysUnlock();
+ return msg;
+ }
+ }
+
+ /* Size of the space available in the current buffer.*/
+ size = obqp->top - obqp->ptr;
+ if (size > n - r) {
+ size = n - r;
+ }
+
+ /* Copying the chunk into the read buffer, the operation is performed
+ outside the critical zone.*/
+ osalSysUnlock();
+ memcpy(obqp->ptr, bp, size);
+ osalSysLock();
+
+ /* Updating the pointers and the counter.*/
+ r += size;
+ bp += size;
+ obqp->ptr += size;
+
+ /* Has the current data buffer been finished? if so then release it.*/
+ if (obqp->ptr >= obqp->top) {
+ obqPostFullBufferI(obqp, obqp->bsize);
}
}
osalSysUnlock();