diff options
Diffstat (limited to 'os/hal/src/hal_buffers.c')
| -rw-r--r-- | os/hal/src/hal_buffers.c | 349 | 
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();
 | 
