dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_com.c
Go to the documentation of this file.
1 
17 /*
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful, but
24  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26  * for more details.
27  *
28  * You should have received a copy of the GNU General Public License along
29  * with this program; if not, see <http://www.gnu.org/licenses/>
30  *
31  * Additional note on redistribution: The copyright and license notices above
32  * must be maintained in each individual source file that is a derivative work
33  * of this source file; otherwise redistribution is prohibited.
34  */
35 
36 /* Project Includes */
37 #include "pios.h"
38 
39 #if defined(PIOS_INCLUDE_COM)
40 
41 #include <circqueue.h>
42 #include <pios_com_priv.h>
43 #include "pios_delay.h" /* PIOS_DELAY_WaitmS */
44 
45 #include "pios_semaphore.h"
46 #include "pios_mutex.h"
47 
48 enum pios_com_dev_magic {
49  PIOS_COM_DEV_MAGIC = 0xaa55aa55,
50 };
51 
52 struct pios_com_dev {
53  enum pios_com_dev_magic magic;
54  uintptr_t lower_id;
55  const struct pios_com_driver * driver;
56 
57  struct pios_semaphore *tx_sem;
58  struct pios_semaphore *rx_sem;
59 #if defined(PIOS_INCLUDE_RTOS)
60  struct pios_mutex *sendbuffer_mtx;
61 #endif
62 
63  circ_queue_t rx;
64  circ_queue_t tx;
65 };
66 
67 static bool PIOS_COM_validate(struct pios_com_dev *com_dev)
68 {
69  return (com_dev && (com_dev->magic == PIOS_COM_DEV_MAGIC));
70 }
71 
72 static struct pios_com_dev *PIOS_COM_alloc(void)
73 {
74  struct pios_com_dev *com_dev;
75 
76  com_dev = (struct pios_com_dev *)PIOS_malloc(sizeof(*com_dev));
77  if (!com_dev) return (NULL);
78 
79  memset(com_dev, 0, sizeof(*com_dev));
80  com_dev->magic = PIOS_COM_DEV_MAGIC;
81  return(com_dev);
82 }
83 
84 static uint16_t PIOS_COM_TxOutCallback(uintptr_t context, uint8_t * buf, uint16_t buf_len, uint16_t * headroom, bool * need_yield);
85 static uint16_t PIOS_COM_RxInCallback(uintptr_t context, uint8_t * buf, uint16_t buf_len, uint16_t * headroom, bool * need_yield);
86 static void PIOS_COM_UnblockRx(struct pios_com_dev *com_dev, bool * need_yield);
87 static void PIOS_COM_UnblockTx(struct pios_com_dev *com_dev, bool * need_yield);
88 
96 int32_t PIOS_COM_Init(uintptr_t * com_id, const struct pios_com_driver * driver, uintptr_t lower_id, uint16_t rx_buffer_len, uint16_t tx_buffer_len)
97 {
98  PIOS_Assert(com_id);
99  PIOS_Assert(driver);
100 
101  PIOS_Assert(rx_buffer_len || tx_buffer_len);
102  PIOS_Assert(driver->bind_tx_cb || !tx_buffer_len);
103  PIOS_Assert(driver->bind_rx_cb || !rx_buffer_len);
104 
105  struct pios_com_dev *com_dev;
106 
107  com_dev = (struct pios_com_dev *) PIOS_COM_alloc();
108  if (!com_dev) goto out_fail;
109 
110  com_dev->driver = driver;
111  com_dev->lower_id = lower_id;
112  com_dev->rx = NULL;
113  com_dev->tx = NULL;
114 
115  if (rx_buffer_len) {
116  com_dev->rx = circ_queue_new(1, rx_buffer_len);
117 
118  if (!com_dev->rx) goto out_fail;
119 #if defined(PIOS_INCLUDE_RTOS)
120  com_dev->rx_sem = PIOS_Semaphore_Create();
121 #endif /* PIOS_INCLUDE_RTOS */
122  (com_dev->driver->bind_rx_cb)(lower_id, PIOS_COM_RxInCallback, (uintptr_t)com_dev);
123  if (com_dev->driver->rx_start) {
124  /* Start the receiver */
125  (com_dev->driver->rx_start)(com_dev->lower_id,
126  rx_buffer_len - 1);
127  }
128  }
129 
130  if (tx_buffer_len) {
131  com_dev->tx = circ_queue_new(1, tx_buffer_len);
132  if (!com_dev->tx) goto out_fail;
133 #if defined(PIOS_INCLUDE_RTOS)
134  com_dev->tx_sem = PIOS_Semaphore_Create();
135 #endif /* PIOS_INCLUDE_RTOS */
136  (com_dev->driver->bind_tx_cb)(lower_id, PIOS_COM_TxOutCallback, (uintptr_t)com_dev);
137  }
138 #if defined(PIOS_INCLUDE_RTOS)
139  com_dev->sendbuffer_mtx = PIOS_Mutex_Create();
140 #endif /* PIOS_INCLUDE_RTOS */
141 
142  *com_id = (uintptr_t)com_dev;
143  return(0);
144 
145 out_fail:
146  return(-1);
147 }
148 
149 static void PIOS_COM_UnblockRx(struct pios_com_dev *com_dev, bool * need_yield)
150 {
151 #if defined(PIOS_INCLUDE_RTOS)
152  if (PIOS_IRQ_InISR() == true)
153  PIOS_Semaphore_Give_FromISR(com_dev->rx_sem, need_yield);
154  else
155  PIOS_Semaphore_Give(com_dev->rx_sem);
156 #endif
157 }
158 
159 static void PIOS_COM_UnblockTx(struct pios_com_dev *com_dev, bool * need_yield)
160 {
161 #if defined(PIOS_INCLUDE_RTOS)
162  if (PIOS_IRQ_InISR() == true)
163  PIOS_Semaphore_Give_FromISR(com_dev->tx_sem, need_yield);
164  else
165  PIOS_Semaphore_Give(com_dev->tx_sem);
166 #endif
167 }
168 
169 static uint16_t PIOS_COM_RxInCallback(uintptr_t context, uint8_t * buf, uint16_t buf_len, uint16_t * headroom, bool * need_yield)
170 {
171  struct pios_com_dev *com_dev = (struct pios_com_dev *)context;
172 
173  bool valid = PIOS_COM_validate(com_dev);
174  PIOS_Assert(valid);
175  PIOS_Assert(com_dev->rx);
176 
177  uint16_t bytes_into_fifo = circ_queue_write_data(com_dev->rx,
178  buf, buf_len);
179 
180  if (bytes_into_fifo > 0) {
181  /* Data has been added to the buffer */
182  PIOS_COM_UnblockRx(com_dev, need_yield);
183  }
184 
185  if (headroom) {
186  circ_queue_write_pos(com_dev->rx, NULL, headroom);
187  }
188 
189  return (bytes_into_fifo);
190 }
191 
192 static uint16_t PIOS_COM_TxOutCallback(uintptr_t context, uint8_t * buf, uint16_t buf_len, uint16_t * headroom, bool * need_yield)
193 {
194  struct pios_com_dev *com_dev = (struct pios_com_dev *)context;
195 
196  bool valid = PIOS_COM_validate(com_dev);
197  PIOS_Assert(valid);
198  PIOS_Assert(buf);
199  PIOS_Assert(buf_len);
200  PIOS_Assert(com_dev->tx);
201 
202  uint16_t bytes_from_fifo = circ_queue_read_data(com_dev->tx,
203  buf, buf_len);
204 
205  if (bytes_from_fifo > 0) {
206  /* More space has been made in the buffer */
207  PIOS_COM_UnblockTx(com_dev, need_yield);
208  }
209 
210  if (headroom) {
211  circ_queue_read_pos(com_dev->tx, NULL, headroom);
212  }
213 
214  return (bytes_from_fifo);
215 }
216 
224 int32_t PIOS_COM_ChangeBaud(uintptr_t com_id, uint32_t baud)
225 {
226  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
227 
228  if (!PIOS_COM_validate(com_dev)) {
229  /* Undefined COM port for this board (see pios_board.c) */
230  return -1;
231  }
232 
233  /* Invoke the driver function if it exists */
234  if (com_dev->driver->set_baud) {
235  com_dev->driver->set_baud(com_dev->lower_id, baud);
236  }
237 
238  return 0;
239 }
240 
241 static int32_t SendBufferNonBlockingImpl(uintptr_t com_id, const uint8_t *buffer, uint16_t len, bool all_or_nothing)
242 {
243  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
244 
245  if (!PIOS_COM_validate(com_dev)) {
246  /* Undefined COM port for this board (see pios_board.c) */
247  return -1;
248  }
249 
250  PIOS_Assert(com_dev->tx);
251 
252 #if defined(PIOS_INCLUDE_RTOS)
253  if (PIOS_Mutex_Lock(com_dev->sendbuffer_mtx, 0) != true) {
254  return -3;
255  }
256 #endif /* defined(PIOS_INCLUDE_RTOS) */
257  if (com_dev->driver->available && !com_dev->driver->available(com_dev->lower_id)) {
258  /*
259  * Underlying device is down/unconnected.
260  * Dump our fifo contents and act like an infinite data sink.
261  * Failure to do this results in stale data in the fifo as well as
262  * possibly having the caller block trying to send to a device that's
263  * no longer accepting data.
264  */
265  /* This call uses queue "reader" state, so it is required that
266  * no one actually be reading the tx queue at the time or
267  * undefined behavior may result */
268  circ_queue_clear(com_dev->tx);
269 #if defined(PIOS_INCLUDE_RTOS)
270  PIOS_Mutex_Unlock(com_dev->sendbuffer_mtx);
271 #endif /* PIOS_INCLUDE_RTOS */
272 
273  return len;
274  }
275 
276  if (all_or_nothing) {
277  // atomic-check
278  uint16_t tot_avail;
279 
280  circ_queue_write_pos(com_dev->tx, NULL, &tot_avail);
281  if (len > tot_avail) {
282 #if defined(PIOS_INCLUDE_RTOS)
283  PIOS_Mutex_Unlock(com_dev->sendbuffer_mtx);
284 #endif /* PIOS_INCLUDE_RTOS */
285  /* Buffer cannot accept all requested bytes (retry) */
286  return -2;
287  }
288  }
289 
290  uint16_t bytes_into_fifo = circ_queue_write_data(com_dev->tx,
291  buffer, len);
292 
293  /* Make sure the tx is actually started */
294  if (com_dev->driver->tx_start) {
295  uint16_t tx_avail;
296 
297  circ_queue_read_pos(com_dev->tx, NULL, &tx_avail);
298  com_dev->driver->tx_start(com_dev->lower_id,
299  tx_avail);
300  }
301 
302 #if defined(PIOS_INCLUDE_RTOS)
303  PIOS_Mutex_Unlock(com_dev->sendbuffer_mtx);
304 #endif /* PIOS_INCLUDE_RTOS */
305  return (bytes_into_fifo);
306 }
307 
320 int32_t PIOS_COM_SendBufferNonBlocking(uintptr_t com_id, const uint8_t *buffer, uint16_t len)
321 {
322  return SendBufferNonBlockingImpl(com_id, buffer, len, true);
323 }
324 
335 int32_t PIOS_COM_SendBufferStallTimeout(uintptr_t com_id, const uint8_t *buffer, uint16_t len, uint32_t max_ms)
336 {
337  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
338 
339  if (!PIOS_COM_validate(com_dev)) {
340  /* Undefined COM port for this board (see pios_board.c) */
341  return -1;
342  }
343 
344  PIOS_Assert(com_dev->tx);
345 
346  uint16_t sent = 0;
347  while (sent < len) {
348  int32_t rc = SendBufferNonBlockingImpl(com_id, buffer,
349  len - sent, false);
350  if (rc > 0) {
351  buffer += rc;
352  sent += rc;
353  } else if (rc == 0) {
354  /* Block... for 5 seconds? */
355  if (PIOS_Semaphore_Take(com_dev->tx_sem, max_ms) != true) {
356  return -3;
357  }
358  } else {
359  // If we succeeded some, report that back.
360  if (sent) break;
361 
362  switch (rc) {
363  case -1:
364  /* Device is invalid, this will never work */
365  return -1;
366  case -2:
367  PIOS_Assert(0);
368  continue;
369  default:
370  /* Unhandled return code */
371  return rc;
372  }
373  }
374  }
375 
376  return sent;
377 }
378 
388 int32_t PIOS_COM_SendBuffer(uintptr_t com_id, const uint8_t *buffer, uint16_t len)
389 {
390  /* Allow 5s with no progress by default */
391  return PIOS_COM_SendBufferStallTimeout(com_id, buffer, len, 5000);
392 }
393 
403 int32_t PIOS_COM_SendCharNonBlocking(uintptr_t com_id, char c)
404 {
405  return PIOS_COM_SendBufferNonBlocking(com_id, (uint8_t *)&c, 1);
406 }
407 
416 int32_t PIOS_COM_SendChar(uintptr_t com_id, char c)
417 {
418  return PIOS_COM_SendBuffer(com_id, (uint8_t *)&c, 1);
419 }
420 
430 int32_t PIOS_COM_SendStringNonBlocking(uintptr_t com_id, const char *str)
431 {
432  return PIOS_COM_SendBufferNonBlocking(com_id, (uint8_t *)str, (uint16_t)strlen(str));
433 }
434 
443 int32_t PIOS_COM_SendString(uintptr_t com_id, const char *str)
444 {
445  return PIOS_COM_SendBuffer(com_id, (uint8_t *)str, strlen(str));
446 }
447 
458 int32_t PIOS_COM_SendFormattedStringNonBlocking(uintptr_t com_id, const char *format, ...)
459 {
460  uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later!
461 
462  va_list args;
463 
464  va_start(args, format);
465  vsprintf((char *)buffer, format, args);
466  va_end(args);
467 
468  return PIOS_COM_SendBufferNonBlocking(com_id, buffer, (uint16_t)strlen((char *)buffer));
469 }
470 
480 int32_t PIOS_COM_SendFormattedString(uintptr_t com_id, const char *format, ...)
481 {
482  uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later!
483  va_list args;
484 
485  va_start(args, format);
486  vsprintf((char *)buffer, format, args);
487  va_end(args);
488 
489  return PIOS_COM_SendBuffer(com_id, buffer, (uint16_t)strlen((char *)buffer));
490 }
491 
497 uint16_t PIOS_COM_GetNumReceiveBytesPending(uintptr_t com_id) {
498  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
499 
500  if (!PIOS_COM_validate(com_dev)) {
501  /* Undefined COM port for this board (see pios_board.c) */
502  PIOS_Assert(0);
503  }
504 
505  PIOS_Assert(com_dev->rx);
506 
507  uint16_t rx_pending;
508 
509  circ_queue_read_pos(com_dev->rx, NULL, &rx_pending);
510 
511  if (rx_pending == 0) {
512  /* No more bytes in receive buffer */
513  /* Make sure the receiver is running */
514  if (com_dev->driver->rx_start) {
515  uint16_t bytes_available = 0;
516 
517  /* Find out how much room in rx buffer */
518  circ_queue_write_pos(com_dev->rx, NULL,
519  &bytes_available);
520  /* Notify the lower layer that there is now room in the rx buffer */
521  (com_dev->driver->rx_start)(com_dev->lower_id,
522  bytes_available);
523  }
524 
525  /* Recheck, just in case something happened */
526  circ_queue_read_pos(com_dev->rx, NULL, &rx_pending);
527  }
528 
529  return rx_pending;
530 }
531 
537 uint16_t PIOS_COM_ReceiveBuffer(uintptr_t com_id, uint8_t * buf, uint16_t buf_len, uint32_t timeout_ms)
538 {
539  PIOS_Assert(buf);
540  PIOS_Assert(buf_len);
541  uint16_t bytes_from_fifo;
542 
543  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
544 
545  if (!PIOS_COM_validate(com_dev)) {
546  /* Undefined COM port for this board (see pios_board.c) */
547  PIOS_Assert(0);
548  }
549  PIOS_Assert(com_dev->rx);
550 
551  /* Clear any pending RX wakeup */
552  if (com_dev->rx_sem) {
553  PIOS_Semaphore_Take(com_dev->rx_sem, 0);
554  }
555 
556 check_again:
557  bytes_from_fifo = circ_queue_read_data(com_dev->rx, buf, buf_len);
558 
559  if (bytes_from_fifo == 0) {
560  /* No more bytes in receive buffer */
561  /* Make sure the receiver is running while we wait */
562  if (com_dev->driver->rx_start) {
563  /* Notify the lower layer that there is now room in the rx buffer */
564  uint16_t rx_space_avail;
565 
566  circ_queue_write_pos(com_dev->rx, NULL,
567  &rx_space_avail);
568  (com_dev->driver->rx_start)(com_dev->lower_id,
569  rx_space_avail);
570  }
571  if (timeout_ms > 0) {
572 #if defined(PIOS_INCLUDE_RTOS)
573  if (PIOS_Semaphore_Take(com_dev->rx_sem, timeout_ms) == true) {
574  /* Make sure we don't come back here again */
575  timeout_ms = 0;
576  goto check_again;
577  }
578 #else
580  timeout_ms--;
581  goto check_again;
582 #endif
583  }
584  }
585 
586  /* Return received byte */
587  return (bytes_from_fifo);
588 }
589 
595 bool PIOS_COM_Available(uintptr_t com_id)
596 {
597  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
598 
599  if (!PIOS_COM_validate(com_dev)) {
600  return false;
601  }
602 
603  // If a driver does not provide a query method assume always
604  // available if valid
605  if (com_dev->driver->available == NULL)
606  return true;
607 
608  return (com_dev->driver->available)(com_dev->lower_id);
609 }
610 
611 uintptr_t PIOS_COM_GetDriverCtx(uintptr_t com_id) {
612  struct pios_com_dev *com_dev = (struct pios_com_dev *)com_id;
613 
614  if (!PIOS_COM_validate(com_dev)) {
615  return false;
616  }
617 
618  return com_dev->lower_id;
619 }
620 
621 #endif
622 
int32_t PIOS_COM_SendString(uintptr_t com_id, const char *str)
uint16_t circ_queue_read_data(circ_queue_t q, void *buf, uint16_t num)
Definition: circqueue.c:331
int32_t PIOS_COM_SendBufferNonBlocking(uintptr_t com_id, const uint8_t *buffer, uint16_t len)
Main PiOS header to include all the compiled in PiOS options.
int32_t PIOS_COM_SendFormattedStringNonBlocking(uintptr_t com_id, const char *format,...)
void(* bind_tx_cb)(uintptr_t id, pios_com_callback tx_out_cb, uintptr_t context)
Definition: pios_com.h:48
COM private definitions.
uint16_t circ_queue_write_data(circ_queue_t q, const void *buf, uint16_t num)
Definition: circqueue.c:302
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
bool PIOS_Semaphore_Give_FromISR(struct pios_semaphore *sema, bool *woken)
void * circ_queue_write_pos(circ_queue_t q, uint16_t *contig, uint16_t *avail)
Definition: circqueue.c:84
bool PIOS_Mutex_Unlock(struct pios_mutex *mtx)
Definition: pios_mutex.c:104
struct pios_mutex * PIOS_Mutex_Create(void)
Definition: pios_mutex.c:43
circ_queue_t circ_queue_new(uint16_t elem_size, uint16_t num_elem)
Definition: circqueue.c:48
int32_t PIOS_COM_SendStringNonBlocking(uintptr_t com_id, const char *str)
int32_t PIOS_COM_SendChar(uintptr_t com_id, char c)
bool PIOS_IRQ_InISR(void)
Definition: pios_irq.c:61
uint16_t PIOS_COM_ReceiveBuffer(uintptr_t com_id, uint8_t *buf, uint16_t buf_len, uint32_t timeout_ms)
struct pios_semaphore * PIOS_Semaphore_Create(void)
Creates a binary semaphore.
uintptr_t PIOS_COM_GetDriverCtx(uintptr_t com_id)
bool PIOS_Semaphore_Give(struct pios_semaphore *sema)
Gives binary semaphore.
int32_t PIOS_COM_SendFormattedString(uintptr_t com_id, const char *format,...)
Public header for 1 reader, 1 writer circular queue.
uint16_t PIOS_COM_GetNumReceiveBytesPending(uintptr_t com_id)
uint32_t magic
PIOS_COM_SendBuffer(shub_global->frsky_port, shub_global->serial_buf, msg_length)
int32_t PIOS_COM_SendCharNonBlocking(uintptr_t com_id, char c)
int32_t PIOS_COM_Init(uintptr_t *com_id, const struct pios_com_driver *driver, uintptr_t lower_id, uint16_t rx_buffer_len, uint16_t tx_buffer_len)
bool PIOS_Semaphore_Take(struct pios_semaphore *sema, uint32_t timeout_ms)
Takes binary semaphore.
int vsprintf(char *out, const char *format, va_list args)
void circ_queue_clear(circ_queue_t q)
Definition: circqueue.c:246
int32_t PIOS_COM_SendBufferStallTimeout(uintptr_t com_id, const uint8_t *buffer, uint16_t len, uint32_t max_ms)
void(* bind_rx_cb)(uintptr_t id, pios_com_callback rx_in_cb, uintptr_t context)
Definition: pios_com.h:47
tuple args
Definition: px_mkfw.py:77
#define PIOS_Assert(test)
Definition: pios_debug.h:52
void * circ_queue_read_pos(circ_queue_t q, uint16_t *contig, uint16_t *avail)
Definition: circqueue.c:208
int32_t PIOS_DELAY_WaitmS(uint32_t mS)
Definition: pios_delay.c:140
int32_t PIOS_COM_ChangeBaud(uintptr_t com_id, uint32_t baud)
bool PIOS_COM_Available(uintptr_t com_id)
bool PIOS_Mutex_Lock(struct pios_mutex *mtx, uint32_t timeout_ms)
Definition: pios_mutex.c:66