ChibiOS  0.0.0
hal_buffers.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 /**
18  * @file hal_buffers.c
19  * @brief I/O Buffers code.
20  *
21  * @addtogroup HAL_BUFFERS
22  * @details Buffers Queues are used when there is the need to exchange
23  * fixed-length data buffers between ISRs and threads.
24  * On the ISR side data can be exchanged only using buffers,
25  * on the thread side data can be exchanged both using buffers and/or
26  * using an emulation of regular byte queues.
27  * There are several kind of buffers queues:<br>
28  * - <b>Input queue</b>, unidirectional queue where the writer is the
29  * ISR side and the reader is the thread side.
30  * - <b>Output queue</b>, unidirectional queue where the writer is the
31  * ISR side and the reader is the thread side.
32  * - <b>Full duplex queue</b>, bidirectional queue. Full duplex queues
33  * are implemented by pairing an input queue and an output queue
34  * together.
35  * .
36  * @{
37  */
38 
39 #include <string.h>
40 
41 #include "hal.h"
42 
43 /*===========================================================================*/
44 /* Driver local definitions. */
45 /*===========================================================================*/
46 
47 /*===========================================================================*/
48 /* Driver exported variables. */
49 /*===========================================================================*/
50 
51 /*===========================================================================*/
52 /* Driver local variables and types. */
53 /*===========================================================================*/
54 
55 /*===========================================================================*/
56 /* Driver local functions. */
57 /*===========================================================================*/
58 
59 /*===========================================================================*/
60 /* Driver exported functions. */
61 /*===========================================================================*/
62 
63 /**
64  * @brief Initializes an input buffers queue object.
65  *
66  * @param[out] ibqp pointer to the @p input_buffers_queue_t object
67  * @param[in] suspended initial state of the queue
68  * @param[in] bp pointer to a memory area allocated for buffers
69  * @param[in] size buffers size
70  * @param[in] n number of buffers
71  * @param[in] infy callback called when a buffer is returned to the queue
72  * @param[in] link application defined pointer
73  *
74  * @init
75  */
76 void ibqObjectInit(input_buffers_queue_t *ibqp, bool suspended, uint8_t *bp,
77  size_t size, size_t n, bqnotify_t infy, void *link) {
78 
79  osalDbgCheck((ibqp != NULL) && (bp != NULL) && (size >= 2U));
80 
82  ibqp->suspended = suspended;
83  ibqp->bcounter = 0;
84  ibqp->brdptr = bp;
85  ibqp->bwrptr = bp;
86  ibqp->btop = bp + ((size + sizeof (size_t)) * n);
87  ibqp->bsize = size + sizeof (size_t);
88  ibqp->bn = n;
89  ibqp->buffers = bp;
90  ibqp->ptr = NULL;
91  ibqp->top = NULL;
92  ibqp->notify = infy;
93  ibqp->link = link;
94 }
95 
96 /**
97  * @brief Resets an input buffers queue.
98  * @details All the data in the input buffers queue is erased and lost, any
99  * waiting thread is resumed with status @p MSG_RESET.
100  * @note A reset operation can be used by a low level driver in order to
101  * obtain immediate attention from the high level layers.
102  *
103  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
104  *
105  * @iclass
106  */
108 
110 
111  ibqp->bcounter = 0;
112  ibqp->brdptr = ibqp->buffers;
113  ibqp->bwrptr = ibqp->buffers;
114  ibqp->ptr = NULL;
115  ibqp->top = NULL;
117 }
118 
119 /**
120  * @brief Gets the next empty buffer from the queue.
121  * @note The function always returns the same buffer if called repeatedly.
122  *
123  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
124  * @return A pointer to the next buffer to be filled.
125  * @retval NULL if the queue is full.
126  *
127  * @iclass
128  */
130 
132 
133  if (ibqIsFullI(ibqp)) {
134  return NULL;
135  }
136 
137  return ibqp->bwrptr + sizeof (size_t);
138 }
139 
140 /**
141  * @brief Posts a new filled buffer to the queue.
142  *
143  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
144  * @param[in] size used size of the buffer, cannot be zero
145  *
146  * @iclass
147  */
148 void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size) {
149 
151 
152  osalDbgCheck((size > 0U) && (size <= (ibqp->bsize - sizeof (size_t))));
153  osalDbgAssert(!ibqIsFullI(ibqp), "buffers queue full");
154 
155  /* Writing size field in the buffer.*/
156  *((size_t *)ibqp->bwrptr) = size;
157 
158  /* Posting the buffer in the queue.*/
159  ibqp->bcounter++;
160  ibqp->bwrptr += ibqp->bsize;
161  if (ibqp->bwrptr >= ibqp->btop) {
162  ibqp->bwrptr = ibqp->buffers;
163  }
164 
165  /* Waking up one waiting thread, if any.*/
167 }
168 
169 /**
170  * @brief Gets the next filled buffer from the queue.
171  * @note The function always acquires the same buffer if called repeatedly.
172  * @post After calling the function the fields @p ptr and @p top are set
173  * at beginning and end of the buffer data or @p NULL if the queue
174  * is empty.
175  *
176  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
177  * @param[in] timeout the number of ticks before the operation timeouts,
178  * the following special values are allowed:
179  * - @a TIME_IMMEDIATE immediate timeout.
180  * - @a TIME_INFINITE no timeout.
181  * .
182  * @return The operation status.
183  * @retval MSG_OK if a buffer has been acquired.
184  * @retval MSG_TIMEOUT if the specified time expired.
185  * @retval MSG_RESET if the queue has been reset or has been put in
186  * suspended state.
187  *
188  * @api
189  */
191  sysinterval_t timeout) {
192  msg_t msg;
193 
194  osalSysLock();
195  msg = ibqGetFullBufferTimeoutS(ibqp, timeout);
196  osalSysUnlock();
197 
198  return msg;
199 }
200 
201  /**
202  * @brief Gets the next filled buffer from the queue.
203  * @note The function always acquires the same buffer if called repeatedly.
204  * @post After calling the function the fields @p ptr and @p top are set
205  * at beginning and end of the buffer data or @p NULL if the queue
206  * is empty.
207  *
208  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
209  * @param[in] timeout the number of ticks before the operation timeouts,
210  * the following special values are allowed:
211  * - @a TIME_IMMEDIATE immediate timeout.
212  * - @a TIME_INFINITE no timeout.
213  * .
214  * @return The operation status.
215  * @retval MSG_OK if a buffer has been acquired.
216  * @retval MSG_TIMEOUT if the specified time expired.
217  * @retval MSG_RESET if the queue has been reset or has been put in
218  * suspended state.
219  *
220  * @sclass
221  */
223  sysinterval_t timeout) {
224 
226 
227  while (ibqIsEmptyI(ibqp)) {
228  if (ibqp->suspended) {
229  return MSG_RESET;
230  }
231  msg_t msg = osalThreadEnqueueTimeoutS(&ibqp->waiting, timeout);
232  if (msg < MSG_OK) {
233  return msg;
234  }
235  }
236 
237  osalDbgAssert(!ibqIsEmptyI(ibqp), "still empty");
238 
239  /* Setting up the "current" buffer and its boundary.*/
240  ibqp->ptr = ibqp->brdptr + sizeof (size_t);
241  ibqp->top = ibqp->ptr + *((size_t *)ibqp->brdptr);
242 
243  return MSG_OK;
244 }
245 
246 /**
247  * @brief Releases the buffer back in the queue.
248  * @note The object callback is called after releasing the buffer.
249  *
250  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
251  *
252  * @api
253  */
255 
256  osalSysLock();
258  osalSysUnlock();
259 }
260 
261  /**
262  * @brief Releases the buffer back in the queue.
263  * @note The object callback is called after releasing the buffer.
264  *
265  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
266  *
267  * @sclass
268  */
270 
272  osalDbgAssert(!ibqIsEmptyI(ibqp), "buffers queue empty");
273 
274  /* Freeing a buffer slot in the queue.*/
275  ibqp->bcounter--;
276  ibqp->brdptr += ibqp->bsize;
277  if (ibqp->brdptr >= ibqp->btop) {
278  ibqp->brdptr = ibqp->buffers;
279  }
280 
281  /* No "current" buffer.*/
282  ibqp->ptr = NULL;
283 
284  /* Notifying the buffer release.*/
285  if (ibqp->notify != NULL) {
286  ibqp->notify(ibqp);
287  }
288 }
289 
290 /**
291  * @brief Input queue read with timeout.
292  * @details This function reads a byte value from an input queue. If
293  * the queue is empty then the calling thread is suspended until a
294  * new buffer arrives in the queue or a timeout occurs.
295  *
296  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
297  * @param[in] timeout the number of ticks before the operation timeouts,
298  * the following special values are allowed:
299  * - @a TIME_IMMEDIATE immediate timeout.
300  * - @a TIME_INFINITE no timeout.
301  * .
302  * @return A byte value from the queue.
303  * @retval MSG_TIMEOUT if the specified time expired.
304  * @retval MSG_RESET if the queue has been reset or has been put in
305  * suspended state.
306  *
307  * @api
308  */
310  msg_t msg;
311 
312  osalSysLock();
313 
314  /* This condition indicates that a new buffer must be acquired.*/
315  if (ibqp->ptr == NULL) {
316  msg = ibqGetFullBufferTimeoutS(ibqp, timeout);
317  if (msg != MSG_OK) {
318  osalSysUnlock();
319  return msg;
320  }
321  }
322 
323  /* Next byte from the buffer.*/
324  msg = (msg_t)*ibqp->ptr;
325  ibqp->ptr++;
326 
327  /* If the current buffer has been fully read then it is returned as
328  empty in the queue.*/
329  if (ibqp->ptr >= ibqp->top) {
331  }
332 
333  osalSysUnlock();
334  return msg;
335 }
336 
337 /**
338  * @brief Input queue read with timeout.
339  * @details The function reads data from an input queue into a buffer.
340  * The operation completes when the specified amount of data has been
341  * transferred or after the specified timeout or if the queue has
342  * been reset.
343  *
344  * @param[in] ibqp pointer to the @p input_buffers_queue_t object
345  * @param[out] bp pointer to the data buffer
346  * @param[in] n the maximum amount of data to be transferred, the
347  * value 0 is reserved
348  * @param[in] timeout the number of ticks before the operation timeouts,
349  * the following special values are allowed:
350  * - @a TIME_IMMEDIATE immediate timeout.
351  * - @a TIME_INFINITE no timeout.
352  * .
353  * @return The number of bytes effectively transferred.
354  * @retval 0 if a timeout occurred.
355  *
356  * @api
357  */
358 size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp,
359  size_t n, sysinterval_t timeout) {
360  size_t r = 0;
361  systime_t deadline;
362 
363  osalDbgCheck(n > 0U);
364 
365  osalSysLock();
366 
367  /* Time window for the whole operation.*/
368  deadline = osalTimeAddX(osalOsGetSystemTimeX(), timeout);
369 
370  while (true) {
371  size_t size;
372 
373  /* This condition indicates that a new buffer must be acquired.*/
374  if (ibqp->ptr == NULL) {
375  msg_t msg;
376 
377  /* TIME_INFINITE and TIME_IMMEDIATE are handled differently, no
378  deadline.*/
379  if ((timeout == TIME_INFINITE) || (timeout == TIME_IMMEDIATE)) {
380  msg = ibqGetFullBufferTimeoutS(ibqp, timeout);
381  }
382  else {
384  deadline);
385 
386  /* Handling the case where the system time went past the deadline,
387  in this case next becomes a very high number because the system
388  time is an unsigned type.*/
389  if (next_timeout > timeout) {
390  osalSysUnlock();
391  return r;
392  }
393  msg = ibqGetFullBufferTimeoutS(ibqp, next_timeout);
394  }
395 
396  /* Anything except MSG_OK interrupts the operation.*/
397  if (msg != MSG_OK) {
398  osalSysUnlock();
399  return r;
400  }
401  }
402 
403  /* Size of the data chunk present in the current buffer.*/
404  size = (size_t)ibqp->top - (size_t)ibqp->ptr;
405  if (size > (n - r)) {
406  size = n - r;
407  }
408 
409  /* Smaller chunks in order to not make the critical zone too long,
410  this impacts throughput however.*/
411  if (size > 64U) {
412  /* Giving the compiler a chance to optimize for a fixed size move.*/
413  memcpy(bp, ibqp->ptr, 64U);
414  bp += 64U;
415  ibqp->ptr += 64U;
416  r += 64U;
417  }
418  else {
419  memcpy(bp, ibqp->ptr, size);
420  bp += size;
421  ibqp->ptr += size;
422  r += size;
423  }
424 
425  /* Has the current data buffer been finished? if so then release it.*/
426  if (ibqp->ptr >= ibqp->top) {
428  }
429 
430  /* Giving a preemption chance.*/
431  osalSysUnlock();
432  if (r >= n) {
433  return r;
434  }
435  osalSysLock();
436  }
437 }
438 
439 /**
440  * @brief Initializes an output buffers queue object.
441  *
442  * @param[out] obqp pointer to the @p output_buffers_queue_t object
443  * @param[in] suspended initial state of the queue
444  * @param[in] bp pointer to a memory area allocated for buffers
445  * @param[in] size buffers size
446  * @param[in] n number of buffers
447  * @param[in] onfy callback called when a buffer is posted in the queue
448  * @param[in] link application defined pointer
449  *
450  * @init
451  */
452 void obqObjectInit(output_buffers_queue_t *obqp, bool suspended, uint8_t *bp,
453  size_t size, size_t n, bqnotify_t onfy, void *link) {
454 
455  osalDbgCheck((obqp != NULL) && (bp != NULL) && (size >= 2U));
456 
458  obqp->suspended = suspended;
459  obqp->bcounter = n;
460  obqp->brdptr = bp;
461  obqp->bwrptr = bp;
462  obqp->btop = bp + ((size + sizeof (size_t)) * n);
463  obqp->bsize = size + sizeof (size_t);
464  obqp->bn = n;
465  obqp->buffers = bp;
466  obqp->ptr = NULL;
467  obqp->top = NULL;
468  obqp->notify = onfy;
469  obqp->link = link;
470 }
471 
472 /**
473  * @brief Resets an output buffers queue.
474  * @details All the data in the output buffers queue is erased and lost, any
475  * waiting thread is resumed with status @p MSG_RESET.
476  * @note A reset operation can be used by a low level driver in order to
477  * obtain immediate attention from the high level layers.
478  *
479  * @param[in] obqp pointer to the @p output_buffers_queue_t object
480  *
481  * @iclass
482  */
484 
486 
487  obqp->bcounter = bqSizeX(obqp);
488  obqp->brdptr = obqp->buffers;
489  obqp->bwrptr = obqp->buffers;
490  obqp->ptr = NULL;
491  obqp->top = NULL;
493 }
494 
495 /**
496  * @brief Gets the next filled buffer from the queue.
497  * @note The function always returns the same buffer if called repeatedly.
498  *
499  * @param[in] obqp pointer to the @p output_buffers_queue_t object
500  * @param[out] sizep pointer to the filled buffer size
501  * @return A pointer to the filled buffer.
502  * @retval NULL if the queue is empty.
503  *
504  * @iclass
505  */
507  size_t *sizep) {
508 
510 
511  if (obqIsEmptyI(obqp)) {
512  return NULL;
513  }
514 
515  /* Buffer size.*/
516  *sizep = *((size_t *)obqp->brdptr);
517 
518  return obqp->brdptr + sizeof (size_t);
519 }
520 
521 /**
522  * @brief Releases the next filled buffer back in the queue.
523  *
524  * @param[in] obqp pointer to the @p output_buffers_queue_t object
525  *
526  * @iclass
527  */
529 
531  osalDbgAssert(!obqIsEmptyI(obqp), "buffers queue empty");
532 
533  /* Freeing a buffer slot in the queue.*/
534  obqp->bcounter++;
535  obqp->brdptr += obqp->bsize;
536  if (obqp->brdptr >= obqp->btop) {
537  obqp->brdptr = obqp->buffers;
538  }
539 
540  /* Waking up one waiting thread, if any.*/
542 }
543 
544 /**
545  * @brief Gets the next empty buffer from the queue.
546  * @note The function always acquires the same buffer if called repeatedly.
547  * @post After calling the function the fields @p ptr and @p top are set
548  * at beginning and end of the buffer data or @p NULL if the queue
549  * is empty.
550  *
551  * @param[in] obqp pointer to the @p output_buffers_queue_t object
552  * @param[in] timeout the number of ticks before the operation timeouts,
553  * the following special values are allowed:
554  * - @a TIME_IMMEDIATE immediate timeout.
555  * - @a TIME_INFINITE no timeout.
556  * .
557  * @return The operation status.
558  * @retval MSG_OK if a buffer has been acquired.
559  * @retval MSG_TIMEOUT if the specified time expired.
560  * @retval MSG_RESET if the queue has been reset or has been put in
561  * suspended state.
562  *
563  * @api
564  */
566  sysinterval_t timeout) {
567  msg_t msg;
568 
569  osalSysLock();
570  msg = obqGetEmptyBufferTimeoutS(obqp, timeout);
571  osalSysUnlock();
572 
573  return msg;
574 }
575 
576 /**
577  * @brief Gets the next empty buffer from the queue.
578  * @note The function always acquires the same buffer if called repeatedly.
579  * @post After calling the function the fields @p ptr and @p top are set
580  * at beginning and end of the buffer data or @p NULL if the queue
581  * is empty.
582  *
583  * @param[in] obqp pointer to the @p output_buffers_queue_t object
584  * @param[in] timeout the number of ticks before the operation timeouts,
585  * the following special values are allowed:
586  * - @a TIME_IMMEDIATE immediate timeout.
587  * - @a TIME_INFINITE no timeout.
588  * .
589  * @return The operation status.
590  * @retval MSG_OK if a buffer has been acquired.
591  * @retval MSG_TIMEOUT if the specified time expired.
592  * @retval MSG_RESET if the queue has been reset or has been put in
593  * suspended state.
594  *
595  * @sclass
596  */
598  sysinterval_t timeout) {
599 
601 
602  while (obqIsFullI(obqp)) {
603  if (obqp->suspended) {
604  return MSG_RESET;
605  }
606  msg_t msg = osalThreadEnqueueTimeoutS(&obqp->waiting, timeout);
607  if (msg < MSG_OK) {
608  return msg;
609  }
610  }
611 
612  osalDbgAssert(!obqIsFullI(obqp), "still full");
613 
614  /* Setting up the "current" buffer and its boundary.*/
615  obqp->ptr = obqp->bwrptr + sizeof (size_t);
616  obqp->top = obqp->bwrptr + obqp->bsize;
617 
618  return MSG_OK;
619 }
620 
621 /**
622  * @brief Posts a new filled buffer to the queue.
623  * @note The object callback is called after releasing the buffer.
624  *
625  * @param[in] obqp pointer to the @p output_buffers_queue_t object
626  * @param[in] size used size of the buffer, cannot be zero
627  *
628  * @api
629  */
630 void obqPostFullBuffer(output_buffers_queue_t *obqp, size_t size) {
631 
632  osalSysLock();
633  obqPostFullBufferS(obqp, size);
634  osalSysUnlock();
635 }
636 
637 /**
638  * @brief Posts a new filled buffer to the queue.
639  * @note The object callback is called after releasing the buffer.
640  *
641  * @param[in] obqp pointer to the @p output_buffers_queue_t object
642  * @param[in] size used size of the buffer, cannot be zero
643  *
644  * @sclass
645  */
646 void obqPostFullBufferS(output_buffers_queue_t *obqp, size_t size) {
647 
649  osalDbgCheck((size > 0U) && (size <= (obqp->bsize - sizeof (size_t))));
650  osalDbgAssert(!obqIsFullI(obqp), "buffers queue full");
651 
652  /* Writing size field in the buffer.*/
653  *((size_t *)obqp->bwrptr) = size;
654 
655  /* Posting the buffer in the queue.*/
656  obqp->bcounter--;
657  obqp->bwrptr += obqp->bsize;
658  if (obqp->bwrptr >= obqp->btop) {
659  obqp->bwrptr = obqp->buffers;
660  }
661 
662  /* No "current" buffer.*/
663  obqp->ptr = NULL;
664 
665  /* Notifying the buffer release.*/
666  if (obqp->notify != NULL) {
667  obqp->notify(obqp);
668  }
669 }
670 
671 /**
672  * @brief Output queue write with timeout.
673  * @details This function writes a byte value to an output queue. If
674  * the queue is full then the calling thread is suspended until a
675  * new buffer is freed in the queue or a timeout occurs.
676  *
677  * @param[in] obqp pointer to the @p output_buffers_queue_t object
678  * @param[in] b byte value to be transferred
679  * @param[in] timeout the number of ticks before the operation timeouts,
680  * the following special values are allowed:
681  * - @a TIME_IMMEDIATE immediate timeout.
682  * - @a TIME_INFINITE no timeout.
683  * .
684  * @return A byte value from the queue.
685  * @retval MSG_TIMEOUT if the specified time expired.
686  * @retval MSG_RESET if the queue has been reset or has been put in
687  * suspended state.
688  *
689  * @api
690  */
692  sysinterval_t timeout) {
693  msg_t msg;
694 
695  osalSysLock();
696 
697  /* This condition indicates that a new buffer must be acquired.*/
698  if (obqp->ptr == NULL) {
699  msg = obqGetEmptyBufferTimeoutS(obqp, timeout);
700  if (msg != MSG_OK) {
701  osalSysUnlock();
702  return msg;
703  }
704  }
705 
706  /* Writing the byte to the buffer.*/
707  *obqp->ptr = b;
708  obqp->ptr++;
709 
710  /* If the current buffer has been fully written then it is posted as
711  full in the queue.*/
712  if (obqp->ptr >= obqp->top) {
713  obqPostFullBufferS(obqp, obqp->bsize - sizeof (size_t));
714  }
715 
716  osalSysUnlock();
717  return MSG_OK;
718 }
719 
720 /**
721  * @brief Output queue write with timeout.
722  * @details The function writes data from a buffer to an output queue. The
723  * operation completes when the specified amount of data has been
724  * transferred or after the specified timeout or if the queue has
725  * been reset.
726  *
727  * @param[in] obqp pointer to the @p output_buffers_queue_t object
728  * @param[in] bp pointer to the data buffer
729  * @param[in] n the maximum amount of data to be transferred, the
730  * value 0 is reserved
731  * @param[in] timeout the number of ticks before the operation timeouts,
732  * the following special values are allowed:
733  * - @a TIME_IMMEDIATE immediate timeout.
734  * - @a TIME_INFINITE no timeout.
735  * .
736  * @return The number of bytes effectively transferred.
737  * @retval 0 if a timeout occurred.
738  *
739  * @api
740  */
741 size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp,
742  size_t n, sysinterval_t timeout) {
743  size_t w = 0;
744  systime_t deadline;
745 
746  osalDbgCheck(n > 0U);
747 
748  osalSysLock();
749 
750  /* Time window for the whole operation.*/
751  deadline = osalTimeAddX(osalOsGetSystemTimeX(), timeout);
752 
753  while (true) {
754  size_t size;
755 
756  /* This condition indicates that a new buffer must be acquired.*/
757  if (obqp->ptr == NULL) {
758  msg_t msg;
759 
760  /* TIME_INFINITE and TIME_IMMEDIATE are handled differently, no
761  deadline.*/
762  if ((timeout == TIME_INFINITE) || (timeout == TIME_IMMEDIATE)) {
763  msg = obqGetEmptyBufferTimeoutS(obqp, timeout);
764  }
765  else {
767  deadline);
768 
769  /* Handling the case where the system time went past the deadline,
770  in this case next becomes a very high number because the system
771  time is an unsigned type.*/
772  if (next_timeout > timeout) {
773  osalSysUnlock();
774  return w;
775  }
776  msg = obqGetEmptyBufferTimeoutS(obqp, next_timeout);
777  }
778 
779  /* Anything except MSG_OK interrupts the operation.*/
780  if (msg != MSG_OK) {
781  osalSysUnlock();
782  return w;
783  }
784  }
785 
786  /* Size of the space available in the current buffer.*/
787  size = (size_t)obqp->top - (size_t)obqp->ptr;
788  if (size > (n - w)) {
789  size = n - w;
790  }
791 
792  /* Smaller chunks in order to not make the critical zone too long,
793  this impacts throughput however.*/
794  if (size > 64U) {
795  /* Giving the compiler a chance to optimize for a fixed size move.*/
796  memcpy(obqp->ptr, bp, 64U);
797  bp += 64U;
798  obqp->ptr += 64U;
799  w += 64U;
800  }
801  else {
802  memcpy(obqp->ptr, bp, size);
803  bp += size;
804  obqp->ptr += size;
805  w += size;
806  }
807 
808  /* Has the current data buffer been finished? if so then release it.*/
809  if (obqp->ptr >= obqp->top) {
810  obqPostFullBufferS(obqp, obqp->bsize - sizeof (size_t));
811  }
812 
813  /* Giving a preemption chance.*/
814  osalSysUnlock();
815  if (w >= n) {
816  return w;
817  }
818  osalSysLock();
819  }
820 }
821 
822 /**
823  * @brief Flushes the current, partially filled, buffer to the queue.
824  * @note The notification callback is not invoked because the function
825  * is meant to be called from ISR context. An operation status is
826  * returned instead.
827  *
828  * @param[in] obqp pointer to the @p output_buffers_queue_t object
829  * @return The operation status.
830  * @retval false if no new filled buffer has been posted to the queue.
831  * @retval true if a new filled buffer has been posted to the queue.
832  *
833  * @iclass
834  */
836 
838 
839  /* If queue is empty and there is a buffer partially filled and
840  it is not being written.*/
841  if (obqIsEmptyI(obqp) && (obqp->ptr != NULL)) {
842  size_t size = (size_t)obqp->ptr - ((size_t)obqp->bwrptr + sizeof (size_t));
843 
844  if (size > 0U) {
845 
846  /* Writing size field in the buffer.*/
847  *((size_t *)obqp->bwrptr) = size;
848 
849  /* Posting the buffer in the queue.*/
850  obqp->bcounter--;
851  obqp->bwrptr += obqp->bsize;
852  if (obqp->bwrptr >= obqp->btop) {
853  obqp->bwrptr = obqp->buffers;
854  }
855 
856  /* No "current" buffer.*/
857  obqp->ptr = NULL;
858 
859  return true;
860  }
861  }
862  return false;
863 }
864 
865 /**
866  * @brief Flushes the current, partially filled, buffer to the queue.
867  *
868  * @param[in] obqp pointer to the @p output_buffers_queue_t object
869  *
870  * @api
871  */
873 
874  osalSysLock();
875 
876  /* If there is a buffer partially filled and not being written.*/
877  if (obqp->ptr != NULL) {
878  size_t size = ((size_t)obqp->ptr - (size_t)obqp->bwrptr) - sizeof (size_t);
879 
880  if (size > 0U) {
881  obqPostFullBufferS(obqp, size);
882  }
883  }
884 
885  osalSysUnlock();
886 }
887 /** @} */
#define ibqIsFullI(ibqp)
Evaluates to true if the specified input buffers queue is full.
Definition: hal_buffers.h:241
void obqFlush(output_buffers_queue_t *obqp)
Flushes the current, partially filled, buffer to the queue.
Definition: hal_buffers.c:872
uint8_t * ibqGetEmptyBufferI(input_buffers_queue_t *ibqp)
Gets the next empty buffer from the queue.
Definition: hal_buffers.c:129
uint8_t * obqGetFullBufferI(output_buffers_queue_t *obqp, size_t *sizep)
Gets the next filled buffer from the queue.
Definition: hal_buffers.c:506
msg_t obqGetEmptyBufferTimeoutS(output_buffers_queue_t *obqp, sysinterval_t timeout)
Gets the next empty buffer from the queue.
Definition: hal_buffers.c:597
void obqReleaseEmptyBufferI(output_buffers_queue_t *obqp)
Releases the next filled buffer back in the queue.
Definition: hal_buffers.c:528
void osalThreadDequeueAllI(threads_queue_t *tqp, msg_t msg)
Dequeues and wakes up all threads from the queue.
Definition: osal.c:309
uint64_t systime_t
Type of system time.
Definition: chtime.h:107
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
static sysinterval_t osalTimeDiffX(systime_t start, systime_t end)
Subtracts two system times returning an interval.
Definition: osal.h:620
volatile size_t bcounter
Active buffers counter.
Definition: hal_buffers.h:71
#define ibqIsEmptyI(ibqp)
Evaluates to true if the specified input buffers queue is empty.
Definition: hal_buffers.h:229
msg_t ibqGetFullBufferTimeout(input_buffers_queue_t *ibqp, sysinterval_t timeout)
Gets the next filled buffer from the queue.
Definition: hal_buffers.c:190
HAL subsystem header.
#define obqIsEmptyI(obqp)
Evaluates to true if the specified output buffers queue is empty.
Definition: hal_buffers.h:256
#define osalDbgCheckClassI()
I-Class state check.
Definition: osal.h:292
#define obqIsFullI(obqp)
Evaluates to true if the specified output buffers queue is full.
Definition: hal_buffers.h:271
uint8_t * btop
Pointer to the buffers boundary.
Definition: hal_buffers.h:83
void ibqResetI(input_buffers_queue_t *ibqp)
Resets an input buffers queue.
Definition: hal_buffers.c:107
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:540
void * link
Application defined field.
Definition: hal_buffers.h:115
bqnotify_t notify
Data notification callback.
Definition: hal_buffers.h:111
void obqPostFullBufferS(output_buffers_queue_t *obqp, size_t size)
Posts a new filled buffer to the queue.
Definition: hal_buffers.c:646
uint8_t * ptr
Pointer for R/W sequential access.
Definition: hal_buffers.h:103
size_t ibqReadTimeout(input_buffers_queue_t *ibqp, uint8_t *bp, size_t n, sysinterval_t timeout)
Input queue read with timeout.
Definition: hal_buffers.c:358
uint8_t * bwrptr
Buffer write pointer.
Definition: hal_buffers.h:75
void obqPostFullBuffer(output_buffers_queue_t *obqp, size_t size)
Posts a new filled buffer to the queue.
Definition: hal_buffers.c:630
size_t bn
Number of buffers.
Definition: hal_buffers.h:94
void ibqPostFullBufferI(input_buffers_queue_t *ibqp, size_t size)
Posts a new filled buffer to the queue.
Definition: hal_buffers.c:148
msg_t ibqGetFullBufferTimeoutS(input_buffers_queue_t *ibqp, sysinterval_t timeout)
Gets the next filled buffer from the queue.
Definition: hal_buffers.c:222
size_t obqWriteTimeout(output_buffers_queue_t *obqp, const uint8_t *bp, size_t n, sysinterval_t timeout)
Output queue write with timeout.
Definition: hal_buffers.c:741
bool obqTryFlushI(output_buffers_queue_t *obqp)
Flushes the current, partially filled, buffer to the queue.
Definition: hal_buffers.c:835
void(* bqnotify_t)(io_buffers_queue_t *bqp)
Double buffer notification callback type.
Definition: hal_buffers.h:54
msg_t obqGetEmptyBufferTimeout(output_buffers_queue_t *obqp, sysinterval_t timeout)
Gets the next empty buffer from the queue.
Definition: hal_buffers.c:565
void obqObjectInit(output_buffers_queue_t *obqp, bool suspended, uint8_t *bp, size_t size, size_t n, bqnotify_t onfy, void *link)
Initializes an output buffers queue object.
Definition: hal_buffers.c:452
static systime_t osalTimeAddX(systime_t systime, sysinterval_t interval)
Adds an interval to a system time returning a system time.
Definition: osal.h:605
#define TIME_INFINITE
Infinite interval specification for all functions with a timeout specification.
Definition: chtime.h:55
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:278
uint8_t * buffers
Queue of buffer objects.
Definition: hal_buffers.h:98
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
systime_t osalOsGetSystemTimeX(void)
Current system time.
Definition: osal.c:136
void osalThreadDequeueNextI(threads_queue_t *tqp, msg_t msg)
Dequeues and wakes up one thread from the queue, if any.
Definition: osal.c:294
msg_t obqPutTimeout(output_buffers_queue_t *obqp, uint8_t b, sysinterval_t timeout)
Output queue write with timeout.
Definition: hal_buffers.c:691
msg_t osalThreadEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout)
Enqueues the caller thread.
Definition: osal.c:277
size_t bsize
Size of buffers.
Definition: hal_buffers.h:90
Structure of a generic buffers queue.
Definition: hal_buffers.h:59
threads_queue_t waiting
Queue of waiting threads.
Definition: hal_buffers.h:63
#define bqSizeX(bqp)
Returns the queue&#39;s number of buffers.
Definition: hal_buffers.h:153
bool suspended
Queue suspended state flag.
Definition: hal_buffers.h:67
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:530
msg_t ibqGetTimeout(input_buffers_queue_t *ibqp, sysinterval_t timeout)
Input queue read with timeout.
Definition: hal_buffers.c:309
void obqResetI(output_buffers_queue_t *obqp)
Resets an output buffers queue.
Definition: hal_buffers.c:483
#define TIME_IMMEDIATE
Zero interval specification for some functions with a timeout specification.
Definition: chtime.h:47
#define osalDbgCheckClassS()
S-Class state check.
Definition: osal.h:298
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:258
uint8_t * top
Boundary for R/W sequential access.
Definition: hal_buffers.h:107
void ibqReleaseEmptyBufferS(input_buffers_queue_t *ibqp)
Releases the buffer back in the queue.
Definition: hal_buffers.c:269
void ibqObjectInit(input_buffers_queue_t *ibqp, bool suspended, uint8_t *bp, size_t size, size_t n, bqnotify_t infy, void *link)
Initializes an input buffers queue object.
Definition: hal_buffers.c:76
static void osalThreadQueueObjectInit(threads_queue_t *tqp)
Initializes a threads queue object.
Definition: osal.h:653
uint8_t * brdptr
Buffer read pointer.
Definition: hal_buffers.h:79
int32_t msg_t
Definition: chtypes.h:51
#define MSG_RESET
Wakeup caused by a reset condition.
Definition: chschd.h:43
void ibqReleaseEmptyBuffer(input_buffers_queue_t *ibqp)
Releases the buffer back in the queue.
Definition: hal_buffers.c:254