ChibiOS  21.6.0
hal_sio.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_sio.c
19  * @brief SIO Driver code.
20  *
21  * @addtogroup SIO
22  * @{
23  */
24 
25 #include "hal.h"
26 
27 #if (HAL_USE_SIO == TRUE) || defined(__DOXYGEN__)
28 
29 /*===========================================================================*/
30 /* Driver local definitions. */
31 /*===========================================================================*/
32 
33 /*===========================================================================*/
34 /* Driver exported variables. */
35 /*===========================================================================*/
36 
37 /*===========================================================================*/
38 /* Driver local variables and types. */
39 /*===========================================================================*/
40 
41 static const SIOOperation default_operation = {
42  .rx_cb = NULL,
43  .rx_idle_cb = NULL,
44  .tx_cb = NULL,
45  .tx_end_cb = NULL,
46  .rx_evt_cb = NULL
47 };
48 
49 /*===========================================================================*/
50 /* Driver local functions. */
51 /*===========================================================================*/
52 
53 #if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
54 static size_t sync_write(void *ip, const uint8_t *bp, size_t n,
55  sysinterval_t timeout) {
56  SIODriver *siop = (SIODriver *)ip;
57  size_t i;
58 
59  i = 0U;
60  while (i < n) {
61  size_t written;
62  msg_t msg;
63 
64  msg = sioSynchronizeTX(siop, timeout);
65  if (msg < MSG_OK) {
66  break;
67  }
68 
69  written = sioAsyncWrite(siop, bp, n - i);
70  i += written;
71  bp += written;
72  }
73  return n;
74 }
75 
76 static size_t sync_read(void *ip, uint8_t *bp, size_t n,
77  sysinterval_t timeout) {
78  SIODriver *siop = (SIODriver *)ip;
79  size_t i;
80 
81  i = 0U;
82  while (i < n) {
83  size_t read;
84  msg_t msg;
85 
86  msg = sioSynchronizeRX(siop, timeout);
87  if (msg < MSG_OK) {
88  break;
89  }
90 
91  read = sioAsyncRead(siop, bp, n - i);
92  i += read;
93  bp += read;
94  }
95  return n;
96 }
97 
98 /*
99  * Interface implementation, the following functions just invoke the equivalent
100  * queue-level function or macro.
101  */
102 
103 static size_t __write(void *ip, const uint8_t *bp, size_t n) {
104 
105  return sync_write(ip, bp, n, TIME_INFINITE);
106 }
107 
108 static size_t __read(void *ip, uint8_t *bp, size_t n) {
109 
110  return sync_read(ip, bp, n, TIME_INFINITE);
111 }
112 
113 static msg_t __put(void *ip, uint8_t b) {
114  SIODriver *siop = (SIODriver *)ip;
115  msg_t msg;
116 
117  msg = sioSynchronizeTX(siop, TIME_INFINITE);
118  if (msg != MSG_OK) {
119  return msg;
120  }
121 
122  sioPutX(siop, b);
123  return MSG_OK;
124 }
125 
126 static msg_t __get(void *ip) {
127  SIODriver *siop = (SIODriver *)ip;
128  msg_t msg;
129 
130  msg = sioSynchronizeRX(siop, TIME_INFINITE);
131  if (msg != MSG_OK) {
132  return msg;
133  }
134 
135  return sioGetX(siop);
136 }
137 
138 static msg_t __putt(void *ip, uint8_t b, sysinterval_t timeout) {
139  SIODriver *siop = (SIODriver *)ip;
140  msg_t msg;
141 
142  msg = sioSynchronizeTX(siop, timeout);
143  if (msg != MSG_OK) {
144  return MSG_RESET;
145  }
146 
147  sioPutX(siop, b);
148  return MSG_OK;
149 }
150 
151 static msg_t __gett(void *ip, sysinterval_t timeout) {
152  SIODriver *siop = (SIODriver *)ip;
153  msg_t msg;
154 
155  msg = sioSynchronizeRX(siop, timeout);
156  if (msg != MSG_OK) {
157  return MSG_RESET;
158  }
159 
160  return sioGetX(siop);
161 }
162 
163 static size_t __writet(void *ip, const uint8_t *bp, size_t n,
164  sysinterval_t timeout) {
165 
166  return sync_write(ip, bp, n, timeout);
167 }
168 
169 static size_t __readt(void *ip, uint8_t *bp, size_t n,
170  sysinterval_t timeout) {
171 
172  return sync_read(ip, bp, n, timeout);
173 }
174 
175 static msg_t __ctl(void *ip, unsigned int operation, void *arg) {
176  SIODriver *siop = (SIODriver *)ip;
177 
178  osalDbgCheck(siop != NULL);
179 
180  switch (operation) {
181  case CHN_CTL_NOP:
182  osalDbgCheck(arg == NULL);
183  break;
184  case CHN_CTL_INVALID:
185  osalDbgAssert(false, "invalid CTL operation");
186  break;
187  default:
188  /* Delegating to the LLD if supported.*/
189  return sio_lld_control(siop, operation, arg);
190  }
191  return MSG_OK;
192 }
193 
194 static const struct sio_driver_vmt vmt = {
195  (size_t)0,
196  __write, __read, __put, __get,
197  __putt, __gett, __writet, __readt,
198  __ctl
199 };
200 #endif
201 
202 /*===========================================================================*/
203 /* Driver exported functions. */
204 /*===========================================================================*/
205 
206 /**
207  * @brief SIO Driver initialization.
208  * @note This function is implicitly invoked by @p halInit(), there is
209  * no need to explicitly initialize the driver.
210  *
211  * @init
212  */
213 void sioInit(void) {
214 
215  sio_lld_init();
216 }
217 
218 /**
219  * @brief Initializes the standard part of a @p SIODriver structure.
220  *
221  * @param[out] siop pointer to the @p SIODriver object
222  *
223  * @init
224  */
226 
227 #if SIO_USE_SYNCHRONIZATION == TRUE
228  siop->vmt = &vmt;
229 #endif
230  siop->state = SIO_STOP;
231  siop->config = NULL;
232 
233  /* Optional, user-defined initializer.*/
234 #if defined(SIO_DRIVER_EXT_INIT_HOOK)
235  SIO_DRIVER_EXT_INIT_HOOK(siop);
236 #endif
237 }
238 
239 /**
240  * @brief Configures and activates the SIO peripheral.
241  *
242  * @param[in] siop pointer to the @p SIODriver object
243  * @param[in] config pointer to the @p SIOConfig object, can be @p NULL
244  * if the default configuration is desired
245  * @return The operation status.
246  * @retval false if the driver has been correctly started.
247  * @retval true if an error occurred.
248  *
249  * @api
250  */
251 bool sioStart(SIODriver *siop, const SIOConfig *config) {
252  bool result;
253 
254  osalDbgCheck(siop != NULL);
255 
256  osalSysLock();
257 
258  osalDbgAssert((siop->state == SIO_STOP) || (siop->state == SIO_READY),
259  "invalid state");
260 
261  siop->config = config;
262  result = sio_lld_start(siop);
263  siop->state = SIO_READY;
264 
265  osalSysUnlock();
266 
267  return result;
268 }
269 
270 /**
271  * @brief Deactivates the SIO peripheral.
272  *
273  * @param[in] siop pointer to the @p SIODriver object
274  *
275  * @api
276  */
277 void sioStop(SIODriver *siop) {
278 
279  osalDbgCheck(siop != NULL);
280 
281  osalSysLock();
282 
283  osalDbgAssert((siop->state == SIO_STOP) || (siop->state == SIO_READY),
284  "invalid state");
285 
286  sio_lld_stop(siop);
287  siop->config = NULL;
288  siop->state = SIO_STOP;
289 
290  osalSysUnlock();
291 }
292 
293 /**
294  * @brief Starts a SIO operation.
295  *
296  * @param[in] siop pointer to an @p SIODriver structure
297  * @param[in] operation pointer to an @p SIOOperation structure, can
298  * be @p NULL if callbacks are not required
299  * encoding the operation to be performed
300  *
301  * @api
302  */
303 void sioStartOperation(SIODriver *siop, const SIOOperation *operation) {
304 
305  osalDbgCheck(siop != NULL);
306 
307  osalSysLock();
308 
309  osalDbgAssert(siop->state == SIO_READY, "invalid state");
310 
311  /* The application can pass NULL if it is not interested in callbacks,
312  attaching a default operation structure.*/
313  if (operation != NULL) {
314  siop->operation = operation;
315  }
316  else {
317  siop->operation = &default_operation;
318  }
319  siop->state = SIO_ACTIVE;
320 
322 
323  osalSysUnlock();
324 }
325 
326 /**
327  * @brief Stops an ongoing SIO operation, if any.
328  *
329  * @param[in] siop pointer to an @p SIODriver structure
330  *
331  * @api
332  */
334 
335  osalDbgCheck(siop != NULL);
336 
337  osalSysLock();
338 
339  osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
340 
341 #if SIO_USE_SYNCHRONIZATION == TRUE
342  /* Informing waiting threads, if any.*/
346 #endif
347 
349 
350  siop->operation = NULL;
351  siop->state = SIO_READY;
352 
353  osalSysUnlock();
354 }
355 
356 /**
357  * @brief Return the pending SIO events flags.
358  *
359  * @param[in] siop pointer to the @p SIODriver object
360  * @return The pending event flags.
361  *
362  * @api
363  */
365  sio_events_mask_t evtmask;
366 
367  osalDbgCheck(siop != NULL);
368 
369  osalSysLock();
370 
371  evtmask = sioGetAndClearEventsI(siop);
372 
373  osalSysUnlock();
374 
375  return evtmask;
376 }
377 
378 /**
379  * @brief Reads data from the RX FIFO.
380  * @details This function is non-blocking, data is read if present and the
381  * effective amount is returned.
382  * @note This function can be called from any context but it is meant to
383  * be called from the @p rxne_cb callback handler.
384  *
385  * @param[in] siop pointer to the @p SIODriver object
386  * @param[in] buffer buffer for the received data
387  * @param[in] n maximum number of frames to read
388  * @return The number of received frames.
389  *
390  * @api
391  */
392 size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n) {
393 
394  osalDbgCheck((siop != NULL) && (buffer != NULL));
395 
396  osalSysLock();
397 
398  n = sioAsyncReadI(siop, buffer, n);
399 
400  osalSysUnlock();
401 
402  return n;
403 }
404 
405 /**
406  * @brief Writes data into the TX FIFO.
407  * @details This function is non-blocking, data is written if there is space
408  * in the FIFO and the effective amount is returned.
409  * @note This function can be called from any context but it is meant to
410  * be called from the @p txnf_cb callback handler.
411  *
412  * @param[in] siop pointer to the @p SIODriver object
413  * @param[out] buffer buffer containing the data to be transmitted
414  * @param[in] n maximum number of frames to read
415  * @return The number of transmitted frames.
416  *
417  * @api
418  */
419 size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) {
420 
421  osalDbgCheck((siop != NULL) && (buffer != NULL));
422 
423  osalSysLock();
424 
425  n = sioAsyncWriteI(siop, buffer, n);
426 
427  osalSysUnlock();
428 
429  return n;
430 }
431 
432 #if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
433 /**
434  * @brief Synchronizes with RX FIFO data availability.
435  * @note The exact behavior depends on low level FIFO settings such
436  * as thresholds, etc.
437  * @note This function can only be called by a single thread at time.
438  *
439  * @param[in] siop pointer to an @p SIODriver structure
440  * @param[in] timeout synchronization timeout
441  * @return The synchronization result.
442  * @retval MSG_OK if there is data in the RX FIFO.
443  * @retval MSG_TIMEOUT if synchronization timed out.
444  * @retval MSG_RESET operation has been stopped while waiting.
445  * @retval SIO_MSG_IDLE if RX line went idle.
446  * @retval SIO_MSG_ERRORS if RX errors occurred during wait.
447  */
449  msg_t msg = MSG_OK;
450 
451  osalDbgCheck(siop != NULL);
452 
453  osalSysLock();
454 
455  osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
456 
457  /*lint -save -e506 -e681 [2.1] Silencing this error because it is
458  tested with a template implementation of sio_lld_is_rx_empty() which
459  is constant.*/
460  while (sio_lld_is_rx_empty(siop)) {
461  /*lint -restore*/
462  msg = osalThreadSuspendTimeoutS(&siop->sync_rx, timeout);
463  }
464 
465  osalSysUnlock();
466 
467  return msg;
468 }
469 
470 /**
471  * @brief Synchronizes with TX FIFO space availability.
472  * @note The exact behavior depends on low level FIFO settings such
473  * as thresholds, etc.
474  * @note This function can only be called by a single thread at time.
475  *
476  * @param[in] siop pointer to an @p SIODriver structure
477  * @param[in] timeout synchronization timeout
478  * @return The synchronization result.
479  * @retval MSG_OK if there is space in the TX FIFO.
480  * @retval MSG_TIMEOUT if synchronization timed out.
481  * @retval MSG_RESET operation has been stopped while waiting.
482  */
484  msg_t msg = MSG_OK;
485 
486  osalDbgCheck(siop != NULL);
487 
488  osalSysLock();
489 
490  osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
491 
492  /*lint -save -e506 -e681 [2.1] Silencing this error because it is
493  tested with a template implementation of sio_lld_is_tx_full() which
494  is constant.*/
495  while (sio_lld_is_tx_full(siop)) {
496  /*lint -restore*/
497  msg = osalThreadSuspendTimeoutS(&siop->sync_tx, timeout);
498  }
499 
500  osalSysUnlock();
501 
502  return msg;
503 }
504 
505 /**
506  * @brief Synchronizes with TX completion.
507  * @note This function can only be called by a single thread at time.
508  *
509  * @param[in] siop pointer to an @p SIODriver structure
510  * @param[in] timeout synchronization timeout
511  * @return The synchronization result.
512  * @retval MSG_OK if TX operation finished.
513  * @retval MSG_TIMEOUT if synchronization timed out.
514  */
516  msg_t msg;
517 
518  osalDbgCheck(siop != NULL);
519 
520  osalSysLock();
521 
522  osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
523 
524  /*lint -save -e506 -e774 [2.1, 14.3] Silencing this error because
525  it is tested with a template implementation of sio_lld_is_tx_ongoing()
526  which is constant.*/
527  if (sio_lld_is_tx_ongoing(siop)) {
528  /*lint -restore*/
529  msg = osalThreadSuspendTimeoutS(&siop->sync_txend, timeout);
530  }
531  else {
532  msg = MSG_OK;
533  }
534 
535  osalSysUnlock();
536 
537  return msg;
538 }
539 #endif /* SIO_USE_SYNCHRONIZATION == TRUE */
540 
541 #endif /* HAL_USE_SIO == TRUE */
542 
543 /** @} */
hal_sio_driver::sync_tx
thread_reference_t sync_tx
Synchronization point for TX.
Definition: hal_sio.h:183
sioGetX
#define sioGetX(siop)
Returns one frame from the RX FIFO.
Definition: hal_sio.h:279
CHN_CTL_NOP
#define CHN_CTL_NOP
Does nothing.
Definition: hal_channels.h:44
CHN_CTL_INVALID
#define CHN_CTL_INVALID
Invalid operation code.
Definition: hal_channels.h:43
sioAsyncReadI
#define sioAsyncReadI(siop, size, buffer)
Reads data from the RX FIFO.
Definition: hal_sio.h:306
MSG_RESET
#define MSG_RESET
Wakeup caused by a reset condition.
Definition: chschd.h:42
sioPutX
#define sioPutX(siop, data)
Pushes one frame into the TX FIFO.
Definition: hal_sio.h:290
hal.h
HAL subsystem header.
sioStop
void sioStop(SIODriver *siop)
Deactivates the SIO peripheral.
Definition: hal_sio.c:277
sio_lld_init
void sio_lld_init(void)
Low level SIO driver initialization.
Definition: hal_sio_lld.c:65
sio_lld_is_tx_ongoing
#define sio_lld_is_tx_ongoing(siop)
Determines the transmission state.
Definition: hal_sio_lld.h:115
sioAsyncRead
size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n)
Reads data from the RX FIFO.
Definition: hal_sio.c:392
sioStartOperation
void sioStartOperation(SIODriver *siop, const SIOOperation *operation)
Starts a SIO operation.
Definition: hal_sio.c:303
SIO_READY
@ SIO_READY
Definition: hal_sio.h:120
osalSysUnlock
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:611
msg_t
int32_t msg_t
Definition: chearly.h:88
SIO_ACTIVE
@ SIO_ACTIVE
Definition: hal_sio.h:121
hal_sio_driver::operation
const SIOOperation * operation
Current configuration data.
Definition: hal_sio.h:174
sioStart
bool sioStart(SIODriver *siop, const SIOConfig *config)
Configures and activates the SIO peripheral.
Definition: hal_sio.c:251
sioSynchronizeRX
msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout)
Synchronizes with RX FIFO data availability.
Definition: hal_sio.c:448
hal_sio_driver::config
const SIOConfig * config
Current configuration data.
Definition: hal_sio.h:170
sio_lld_control
msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg)
Control operation on a serial port.
Definition: hal_sio_lld.c:243
hal_sio_operation
Structure representing a SIO operation.
Definition: hal_sio.h:199
sioAsyncWriteI
#define sioAsyncWriteI(siop, size, buffer)
Writes data into the TX FIFO.
Definition: hal_sio.h:322
hal_sio_driver::sync_txend
thread_reference_t sync_txend
Synchronization point for TX-end.
Definition: hal_sio.h:187
sioObjectInit
void sioObjectInit(SIODriver *siop)
Initializes the standard part of a SIODriver structure.
Definition: hal_sio.c:225
hal_sio_config
Driver configuration structure.
Definition: hal_sio.h:131
sioSynchronizeTX
msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout)
Synchronizes with TX FIFO space availability.
Definition: hal_sio.c:483
TIME_INFINITE
#define TIME_INFINITE
Infinite interval specification for all functions with a timeout specification.
Definition: chtime.h:55
sio_lld_stop
void sio_lld_stop(SIODriver *siop)
Deactivates the SIO peripheral.
Definition: hal_sio_lld.c:105
sioGetAndClearEventsI
#define sioGetAndClearEventsI(siop)
Return the pending SIO events flags.
Definition: hal_sio.h:268
MSG_OK
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
osalDbgCheck
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:284
osalThreadSuspendTimeoutS
msg_t osalThreadSuspendTimeoutS(thread_reference_t *trp, sysinterval_t timeout)
Sends the current thread sleeping and sets a reference variable.
Definition: osal.c:211
hal_sio_driver::state
siostate_t state
Driver state.
Definition: hal_sio.h:166
SIO_STOP
@ SIO_STOP
Definition: hal_sio.h:119
sysinterval_t
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
osalSysLock
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:601
sioSynchronizeTXEnd
msg_t sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout)
Synchronizes with TX completion.
Definition: hal_sio.c:515
sioStopOperation
void sioStopOperation(SIODriver *siop)
Stops an ongoing SIO operation, if any.
Definition: hal_sio.c:333
sio_lld_start_operation
void sio_lld_start_operation(SIODriver *siop)
Starts a SIO operation.
Definition: hal_sio_lld.c:126
sio_lld_is_tx_full
#define sio_lld_is_tx_full(siop)
Determines the state of the TX FIFO.
Definition: hal_sio_lld.h:103
hal_sio_driver::vmt
const struct sio_driver_vmt * vmt
Virtual Methods Table.
Definition: hal_sio.h:161
sioAsyncWrite
size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n)
Writes data into the TX FIFO.
Definition: hal_sio.c:419
osalDbgAssert
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:264
osalThreadResumeI
void osalThreadResumeI(thread_reference_t *trp, msg_t msg)
Wakes up a thread waiting on a thread reference object.
Definition: osal.c:230
sioInit
void sioInit(void)
SIO Driver initialization.
Definition: hal_sio.c:213
hal_sio_operation::rx_cb
siocb_t rx_cb
Receive non-empty callback.
Definition: hal_sio.h:204
hal_sio_driver
Structure representing a SIO driver.
Definition: hal_sio.h:156
sio_driver_vmt
SIODriver virtual methods table.
Definition: hal_sio.h:147
sio_lld_start
bool sio_lld_start(SIODriver *siop)
Configures and activates the SIO peripheral.
Definition: hal_sio_lld.c:83
sio_lld_stop_operation
void sio_lld_stop_operation(SIODriver *siop)
Stops an ongoing SIO operation, if any.
Definition: hal_sio_lld.c:138
hal_sio_driver::sync_rx
thread_reference_t sync_rx
Synchronization point for RX.
Definition: hal_sio.h:179
sioGetAndClearEvents
sio_events_mask_t sioGetAndClearEvents(SIODriver *siop)
Return the pending SIO events flags.
Definition: hal_sio.c:364
sio_lld_is_rx_empty
#define sio_lld_is_rx_empty(siop)
Determines the state of the RX FIFO.
Definition: hal_sio_lld.h:91
sio_events_mask_t
uint32_t sio_events_mask_t
Type of a SIO events mask.
Definition: hal_sio_lld.h:63