Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / flight / pios / posix / pios_com.c
blob8cd754055d30e034ce6af86a13ebc49b446a5343
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_COM COM layer functions
6 * @brief Hardware communication layer
7 * @{
9 * @file pios_com.c
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * Parts by Thorsten Klose (tk@midibox.org)
12 * @brief COM layer functions
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 /* Project Includes */
33 #include "pios.h"
35 #if defined(PIOS_INCLUDE_COM)
37 #include "fifo_buffer.h"
38 #include <pios_com_priv.h>
40 #if !defined(PIOS_INCLUDE_FREERTOS)
41 #include "pios_delay.h" /* PIOS_DELAY_WaitmS */
42 #endif
44 enum pios_com_dev_magic {
45 PIOS_COM_DEV_MAGIC = 0xaa55aa55,
48 struct pios_com_dev {
49 enum pios_com_dev_magic magic;
50 uint32_t lower_id;
51 const struct pios_com_driver *driver;
53 #if defined(PIOS_INCLUDE_FREERTOS)
54 xSemaphoreHandle tx_sem;
55 xSemaphoreHandle rx_sem;
56 #endif
58 bool has_rx;
59 bool has_tx;
61 t_fifo_buffer rx;
62 t_fifo_buffer tx;
65 static bool PIOS_COM_validate(struct pios_com_dev *com_dev)
67 return com_dev && (com_dev->magic == PIOS_COM_DEV_MAGIC);
70 #if defined(PIOS_INCLUDE_FREERTOS) && 0
71 // static struct pios_com_dev * PIOS_COM_alloc(void)
72 // {
73 // struct pios_com_dev * com_dev;
75 // com_dev = (struct pios_com_dev *)malloc(sizeof(*com_dev));
76 // if (!com_dev) return (NULL);
78 // com_dev->magic = PIOS_COM_DEV_MAGIC;
79 // return(com_dev);
80 // }
81 #else
82 static struct pios_com_dev pios_com_devs[PIOS_COM_MAX_DEVS];
83 static uint8_t pios_com_num_devs;
84 static uint32_t PIOS_COM_create(void)
86 struct pios_com_dev *com_dev;
88 if (pios_com_num_devs >= PIOS_COM_MAX_DEVS) {
89 return PIOS_COM_MAX_DEVS + 1;
92 com_dev = &pios_com_devs[pios_com_num_devs++];
93 com_dev->magic = PIOS_COM_DEV_MAGIC;
95 return pios_com_num_devs;
97 static struct pios_com_dev *PIOS_COM_find_dev(uint32_t com_dev_id)
99 if (!com_dev_id) {
100 return NULL;
102 if (com_dev_id > pios_com_num_devs + 1) {
103 return NULL;
105 return &pios_com_devs[com_dev_id - 1];
107 #endif /* if defined(PIOS_INCLUDE_FREERTOS) && 0 */
109 static uint16_t PIOS_COM_TxOutCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield);
110 static uint16_t PIOS_COM_RxInCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield);
111 static void PIOS_COM_UnblockRx(struct pios_com_dev *com_dev, bool *need_yield);
112 static void PIOS_COM_UnblockTx(struct pios_com_dev *com_dev, bool *need_yield);
115 * Initialises COM layer
116 * \param[out] handle
117 * \param[in] driver
118 * \param[in] id
119 * \return < 0 if initialisation failed
121 int32_t PIOS_COM_Init(uint32_t *com_id, const struct pios_com_driver *driver, uint32_t lower_id, uint8_t *rx_buffer, uint16_t rx_buffer_len, uint8_t *tx_buffer, uint16_t tx_buffer_len)
123 PIOS_Assert(com_id);
124 PIOS_Assert(driver);
126 bool has_rx = (rx_buffer && rx_buffer_len > 0);
127 bool has_tx = (tx_buffer && tx_buffer_len > 0);
128 PIOS_Assert(has_rx || has_tx);
129 PIOS_Assert(driver->bind_tx_cb || !has_tx);
130 PIOS_Assert(driver->bind_rx_cb || !has_rx);
132 uint32_t com_dev_id;
133 struct pios_com_dev *com_dev;
135 com_dev_id = PIOS_COM_create();
136 com_dev = PIOS_COM_find_dev(com_dev_id);
137 if (!com_dev) {
138 goto out_fail;
141 com_dev->driver = driver;
142 com_dev->lower_id = lower_id;
144 com_dev->has_rx = has_rx;
145 com_dev->has_tx = has_tx;
147 if (has_rx) {
148 fifoBuf_init(&com_dev->rx, rx_buffer, rx_buffer_len);
149 #if defined(PIOS_INCLUDE_FREERTOS)
150 vSemaphoreCreateBinary(com_dev->rx_sem);
151 #endif /* PIOS_INCLUDE_FREERTOS */
152 (com_dev->driver->bind_rx_cb)(lower_id, PIOS_COM_RxInCallback, com_dev_id);
153 if (com_dev->driver->rx_start) {
154 /* Start the receiver */
155 (com_dev->driver->rx_start)(com_dev->lower_id,
156 fifoBuf_getFree(&com_dev->rx));
160 if (has_tx) {
161 fifoBuf_init(&com_dev->tx, tx_buffer, tx_buffer_len);
162 #if defined(PIOS_INCLUDE_FREERTOS)
163 vSemaphoreCreateBinary(com_dev->tx_sem);
164 #endif /* PIOS_INCLUDE_FREERTOS */
165 (com_dev->driver->bind_tx_cb)(lower_id, PIOS_COM_TxOutCallback, com_dev_id);
168 *com_id = com_dev_id;
169 return 0;
171 out_fail:
172 return -1;
175 static void PIOS_COM_UnblockRx(struct pios_com_dev *com_dev, bool *need_yield)
177 #if defined(PIOS_INCLUDE_FREERTOS)
178 static signed portBASE_TYPE xHigherPriorityTaskWoken;
179 xSemaphoreGiveFromISR(com_dev->rx_sem, &xHigherPriorityTaskWoken);
181 if (xHigherPriorityTaskWoken != pdFALSE) {
182 *need_yield = true;
183 } else {
184 *need_yield = false;
186 #else
187 *need_yield = false;
188 #endif
191 static void PIOS_COM_UnblockTx(struct pios_com_dev *com_dev, bool *need_yield)
193 #if defined(PIOS_INCLUDE_FREERTOS)
194 static signed portBASE_TYPE xHigherPriorityTaskWoken;
195 xSemaphoreGiveFromISR(com_dev->tx_sem, &xHigherPriorityTaskWoken);
197 if (xHigherPriorityTaskWoken != pdFALSE) {
198 *need_yield = true;
199 } else {
200 *need_yield = false;
202 #else
203 *need_yield = false;
204 #endif
207 static uint16_t PIOS_COM_RxInCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield)
209 struct pios_com_dev *com_dev = PIOS_COM_find_dev(context);
211 bool valid = PIOS_COM_validate(com_dev);
213 PIOS_Assert(valid);
214 PIOS_Assert(com_dev->has_rx);
216 PIOS_IRQ_Disable();
217 uint16_t bytes_into_fifo = fifoBuf_putData(&com_dev->rx, buf, buf_len);
218 PIOS_IRQ_Enable();
220 if (bytes_into_fifo > 0) {
221 /* Data has been added to the buffer */
222 PIOS_COM_UnblockRx(com_dev, need_yield);
225 if (headroom) {
226 *headroom = fifoBuf_getFree(&com_dev->rx);
229 return bytes_into_fifo;
232 static uint16_t PIOS_COM_TxOutCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield)
234 struct pios_com_dev *com_dev = PIOS_COM_find_dev(context);
236 bool valid = PIOS_COM_validate(com_dev);
238 PIOS_Assert(valid);
239 PIOS_Assert(buf);
240 PIOS_Assert(buf_len);
241 PIOS_Assert(com_dev->has_tx);
243 PIOS_IRQ_Disable();
244 uint16_t bytes_from_fifo = fifoBuf_getData(&com_dev->tx, buf, buf_len);
245 PIOS_IRQ_Enable();
247 if (bytes_from_fifo > 0) {
248 /* More space has been made in the buffer */
249 PIOS_COM_UnblockTx(com_dev, need_yield);
252 if (headroom) {
253 *headroom = fifoBuf_getUsed(&com_dev->tx);
256 return bytes_from_fifo;
260 * Change the port speed without re-initializing
261 * \param[in] port COM port
262 * \param[in] baud Requested baud rate
263 * \return -1 if port not available
264 * \return 0 on success
266 int32_t PIOS_COM_ChangeBaud(uint32_t com_id, uint32_t baud)
268 struct pios_com_dev *com_dev = PIOS_COM_find_dev(com_id);
270 if (!PIOS_COM_validate(com_dev)) {
271 /* Undefined COM port for this board (see pios_board.c) */
272 return -1;
275 /* Invoke the driver function if it exists */
276 if (com_dev->driver->set_baud) {
277 com_dev->driver->set_baud(com_dev->lower_id, baud);
280 return 0;
284 * Sends a package over given port
285 * \param[in] port COM port
286 * \param[in] buffer character buffer
287 * \param[in] len buffer length
288 * \return -1 if port not available
289 * \return -2 if non-blocking mode activated: buffer is full
290 * caller should retry until buffer is free again
291 * \return 0 on success
293 int32_t PIOS_COM_SendBufferNonBlocking(uint32_t com_id, const uint8_t *buffer, uint16_t len)
295 struct pios_com_dev *com_dev = PIOS_COM_find_dev(com_id);
297 if (!PIOS_COM_validate(com_dev)) {
298 /* Undefined COM port for this board (see pios_board.c) */
299 return -1;
302 PIOS_Assert(com_dev->has_tx);
304 if (len >= fifoBuf_getFree(&com_dev->tx)) {
305 /* Buffer cannot accept all requested bytes (retry) */
306 return -2;
309 PIOS_IRQ_Disable();
310 uint16_t bytes_into_fifo = fifoBuf_putData(&com_dev->tx, buffer, len);
311 PIOS_IRQ_Enable();
313 if (bytes_into_fifo > 0) {
314 /* More data has been put in the tx buffer, make sure the tx is started */
315 if (com_dev->driver->tx_start) {
316 com_dev->driver->tx_start(com_dev->lower_id,
317 fifoBuf_getUsed(&com_dev->tx));
321 return 0;
325 * Sends a package over given port
326 * (blocking function)
327 * \param[in] port COM port
328 * \param[in] buffer character buffer
329 * \param[in] len buffer length
330 * \return -1 if port not available
331 * \return 0 on success
333 int32_t PIOS_COM_SendBuffer(uint32_t com_id, const uint8_t *buffer, uint16_t len)
335 struct pios_com_dev *com_dev = PIOS_COM_find_dev(com_id);
337 if (!PIOS_COM_validate(com_dev)) {
338 /* Undefined COM port for this board (see pios_board.c) */
339 return -1;
342 PIOS_Assert(com_dev->has_tx);
344 int32_t rc;
345 do {
346 rc = PIOS_COM_SendBufferNonBlocking(com_id, buffer, len);
348 #if defined(PIOS_INCLUDE_FREERTOS)
349 if (rc == -2) {
350 /* Make sure the transmitter is running while we wait */
351 if (com_dev->driver->tx_start) {
352 (com_dev->driver->tx_start)(com_dev->lower_id,
353 fifoBuf_getUsed(&com_dev->tx));
355 if (xSemaphoreTake(com_dev->tx_sem, portMAX_DELAY) != pdTRUE) {
356 return -3;
359 #endif
360 } while (rc == -2);
362 return rc;
366 * Sends a single character over given port
367 * \param[in] port COM port
368 * \param[in] c character
369 * \return -1 if port not available
370 * \return -2 buffer is full
371 * caller should retry until buffer is free again
372 * \return 0 on success
374 int32_t PIOS_COM_SendCharNonBlocking(uint32_t com_id, char c)
376 return PIOS_COM_SendBufferNonBlocking(com_id, (uint8_t *)&c, 1);
380 * Sends a single character over given port
381 * (blocking function)
382 * \param[in] port COM port
383 * \param[in] c character
384 * \return -1 if port not available
385 * \return 0 on success
387 int32_t PIOS_COM_SendChar(uint32_t com_id, char c)
389 return PIOS_COM_SendBuffer(com_id, (uint8_t *)&c, 1);
393 * Sends a string over given port
394 * \param[in] port COM port
395 * \param[in] str zero-terminated string
396 * \return -1 if port not available
397 * \return -2 buffer is full
398 * caller should retry until buffer is free again
399 * \return 0 on success
401 int32_t PIOS_COM_SendStringNonBlocking(uint32_t com_id, const char *str)
403 return PIOS_COM_SendBufferNonBlocking(com_id, (uint8_t *)str, (uint16_t)strlen(str));
407 * Sends a string over given port
408 * (blocking function)
409 * \param[in] port COM port
410 * \param[in] str zero-terminated string
411 * \return -1 if port not available
412 * \return 0 on success
414 int32_t PIOS_COM_SendString(uint32_t com_id, const char *str)
416 return PIOS_COM_SendBuffer(com_id, (uint8_t *)str, strlen(str));
420 * Sends a formatted string (-> printf) over given port
421 * \param[in] port COM port
422 * \param[in] *format zero-terminated format string - 128 characters supported maximum!
423 * \param[in] ... optional arguments,
424 * 128 characters supported maximum!
425 * \return -2 if non-blocking mode activated: buffer is full
426 * caller should retry until buffer is free again
427 * \return 0 on success
429 int32_t PIOS_COM_SendFormattedStringNonBlocking(uint32_t com_id, const char *format, ...)
431 uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later!
433 va_list args;
435 va_start(args, format);
436 vsprintf((char *)buffer, format, args);
437 return PIOS_COM_SendBufferNonBlocking(com_id, buffer, (uint16_t)strlen((char *)buffer));
441 * Sends a formatted string (-> printf) over given port
442 * (blocking function)
443 * \param[in] port COM port
444 * \param[in] *format zero-terminated format string - 128 characters supported maximum!
445 * \param[in] ... optional arguments,
446 * \return -1 if port not available
447 * \return 0 on success
449 int32_t PIOS_COM_SendFormattedString(uint32_t com_id, const char *format, ...)
451 uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later!
452 va_list args;
454 va_start(args, format);
455 vsprintf((char *)buffer, format, args);
456 return PIOS_COM_SendBuffer(com_id, buffer, (uint16_t)strlen((char *)buffer));
460 * Transfer bytes from port buffers into another buffer
461 * \param[in] port COM port
462 * \returns Byte from buffer
464 uint16_t PIOS_COM_ReceiveBuffer(uint32_t com_id, uint8_t *buf, uint16_t buf_len, uint32_t timeout_ms)
466 PIOS_Assert(buf);
467 PIOS_Assert(buf_len);
469 struct pios_com_dev *com_dev = PIOS_COM_find_dev(com_id);
471 if (!PIOS_COM_validate(com_dev)) {
472 /* Undefined COM port for this board (see pios_board.c) */
473 PIOS_Assert(0);
475 PIOS_Assert(com_dev->has_rx);
477 check_again:
478 PIOS_IRQ_Disable();
479 uint16_t bytes_from_fifo = fifoBuf_getData(&com_dev->rx, buf, buf_len);
480 PIOS_IRQ_Enable();
482 if (bytes_from_fifo == 0 && timeout_ms > 0) {
483 /* No more bytes in receive buffer */
484 /* Make sure the receiver is running while we wait */
485 if (com_dev->driver->rx_start) {
486 /* Notify the lower layer that there is now room in the rx buffer */
487 (com_dev->driver->rx_start)(com_dev->lower_id,
488 fifoBuf_getFree(&com_dev->rx));
490 #if defined(PIOS_INCLUDE_FREERTOS)
491 if (xSemaphoreTake(com_dev->rx_sem, timeout_ms / portTICK_RATE_MS) == pdTRUE) {
492 /* Make sure we don't come back here again */
493 timeout_ms = 0;
494 goto check_again;
496 #else
497 PIOS_DELAY_WaitmS(1);
498 timeout_ms--;
499 goto check_again;
500 #endif
503 /* Return received byte */
504 return bytes_from_fifo;
508 * Query if a com port is available for use. That can be
509 * used to check a link is established even if the device
510 * is valid.
512 uint32_t PIOS_COM_Available(uint32_t com_id)
514 struct pios_com_dev *com_dev = PIOS_COM_find_dev(com_id);
516 if (!PIOS_COM_validate(com_dev)) {
517 return COM_AVAILABLE_NONE;
520 // If a driver does not provide a query method assume always
521 // available if valid
522 if (com_dev->driver->available == NULL) {
523 if (com_dev->has_rx && com_dev->has_tx) {
524 return COM_AVAILABLE_RXTX;
525 } else if (com_dev->has_rx) {
526 return COM_AVAILABLE_RX;
527 } else if (com_dev->has_tx) {
528 return COM_AVAILABLE_TX;
531 return COM_AVAILABLE_NONE; /* can this really happen? */
534 return (com_dev->driver->available)(com_dev->lower_id);
537 #endif /* if defined(PIOS_INCLUDE_COM) */
540 * @}
541 * @}