µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
device-serial-buffered.h
Go to the documentation of this file.
1/*
2 * This file is part of the µOS++ distribution.
3 * (https://github.com/micro-os-plus)
4 * Copyright (c) 2015-2023 Liviu Ionescu. All rights reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software
7 * for any purpose is hereby granted, under the terms of the MIT license.
8 *
9 * If a copy of the license was not distributed with this file, it can
10 * be obtained from https://opensource.org/licenses/mit/.
11 */
12
13#ifndef CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_
14#define CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_
15
16#if defined(__cplusplus)
17
18// ----------------------------------------------------------------------------
19
20#include <cmsis-plus/rtos/os.h>
21
22#include <cmsis-plus/posix-io/device-char.h>
25
26// ----------------------------------------------------------------------------
27
28// TODO: (multiline)
29// - add flow control on both send & receive
30// - cancel pending reads/writes at close (partly done)
31// - add error processing
32
33namespace os
34{
35 namespace posix
36 {
37 // ------------------------------------------------------------------------
38
39#pragma GCC diagnostic push
40#if defined(__clang__)
41#pragma clang diagnostic ignored "-Wpadded"
42#elif defined(__GNUC__)
43#pragma GCC diagnostic ignored "-Wpadded"
44#endif
45
51 template<typename CS>
52 class device_serial_buffered : public os::posix::device_char
53 {
54 using critical_section = CS;
55
56 // ----------------------------------------------------------------------
57
63 public:
64
65 device_serial_buffered (const char* device_name,
66 os::driver::Serial* driver,
69
74 // The rule of five.
78 operator= (const device_serial_buffered&) = delete;
80 operator= (device_serial_buffered&&) = delete;
81
86 virtual
88
93 // --------------------------------------------------------------------
99 public:
100
101 // Static function called by the CMSIS driver in an
102 // interrupt context.
103
104 static void
105 signal_event (device_serial_buffered* object, uint32_t event);
106
111 // --------------------------------------------------------------------
117 protected:
118
119 virtual int
120 do_vopen (const char* path, int oflag, std::va_list args) override;
121
122 virtual int
123 do_close (void) override;
124
125 virtual ssize_t
126 do_read (void* buf, std::size_t nbyte) override;
127
128 virtual ssize_t
129 do_write (const void* buf, std::size_t nbyte) override;
130
131#if 0
132 virtual ssize_t
133 do_writev (const struct iovec* iov, int iovcnt) override;
134
135 virtual int
136 do_vioctl (int request, std::va_list args) override;
137
138 virtual int
139 do_vfcntl (int cmd, va_list args) override;
140#endif
141
142 virtual bool
143 do_is_opened (void) override;
144
145 virtual bool
146 do_is_connected (void) override;
147
152 // --------------------------------------------------------------------
153 private:
154
159 // Pointer to actual CMSIS-like serial driver (usart or usb cdc acm)
160 os::driver::Serial* driver_ = nullptr;
161
163 { "open", 0 };
165 { "rx", 0 };
167 { "tx", 0 };
168
169 os::posix::circular_buffer_bytes* rx_buf_ = nullptr;
170 os::posix::circular_buffer_bytes* tx_buf_ = nullptr;
171
172 std::size_t rx_count_ = 0; //
173 bool volatile tx_busy_ = false;
174 bool volatile is_connected_ = false;
175 bool volatile is_opened_ = false;
176 // Padding!
177
182 };
183
184#pragma GCC diagnostic pop
185
186 } /* namespace posix */
187} /* namespace os */
188
189// ===== Inline & template implementations ====================================
190
191namespace os
192{
193 namespace posix
194 {
195 // ------------------------------------------------------------------------
196
197 template<typename CS>
199 const char* device_name, //
202 //
203 device_char (device_name), // Construct parent.
204 driver_ (driver), //
205 rx_buf_ (rx_buf), //
206 tx_buf_ (tx_buf) //
207 {
208 trace::printf ("%s(\"%s\",%p,%p,%p) %p\n", __func__, device_name,
209 driver, rx_buf, tx_buf, this);
210
211 assert (rx_buf != nullptr);
212
213 // Do not check the same for tx_buf, it may be null.
214
215 driver_->register_callback (
216 reinterpret_cast<os::driver::signal_event_t> (signal_event), this);
217 }
218
219 template<typename CS>
221 {
222 trace::printf ("%s() %p\n", __func__, this);
223
224 driver_ = nullptr;
225 is_connected_ = false;
226 is_opened_ = false;
227 }
228
229 // ------------------------------------------------------------------------
230
231#pragma GCC diagnostic push
232#if defined(__clang__)
233#elif defined(__GNUC__)
234#pragma GCC diagnostic ignored "-Wunused-parameter"
235#endif
236
237 template<typename CS>
238 int
239 device_serial_buffered<CS>::do_vopen (const char* path, int oflag,
240 std::va_list args)
241 {
242 if (is_opened_)
243 {
244 errno = EEXIST; // Already opened
245 return -1;
246 }
247
248 int32_t result;
249
250 do
251 {
252 // Reset semaphores, in case we come here after close.
253 open_sem_.reset ();
254 rx_sem_.reset ();
255 tx_sem_.reset ();
256
257 is_opened_ = true;
258
259 // Clear buffers.
260 rx_buf_->clear ();
261 rx_count_ = 0;
262
263 if (tx_buf_ != nullptr)
264 {
265 tx_buf_->clear ();
266 }
267
268 // Default configuration: 8 bits, no parity, 1 stop bit,
269 // no flow control, 115200 bps.
270 result = driver_->configure (
276 115200);
277 // assert(result == os::driver::RETURN_OK);
278 if (result != os::driver::RETURN_OK)
279 break;
280
281 // Enable TX output.
282 result = driver_->control (os::driver::serial::Control::enable_tx);
283 if (result != os::driver::RETURN_OK)
284 break;
285
286 // Enable RX input.
287 result = driver_->control (os::driver::serial::Control::enable_rx);
288 if (result != os::driver::RETURN_OK)
289 break;
290
291 }
292 while (false); // Actually NOT a loop, just a sequence of ifs!
293
294 if (result != os::driver::RETURN_OK)
295 {
296 errno = EIO;
297 return -1;
298 }
299
301 capa = driver_->get_capabilities ();
302 if (capa.dcd)
303 {
305 for (;;)
306 {
307 {
308 // ----- Enter critical section ---------------------------
310
311 status = driver_->get_modem_status ();
312 // ----- Exit critical section ----------------------------
313 }
314 if (status.is_dcd_active ())
315 {
316 break;
317 }
318 open_sem_.wait ();
319 }
320 }
321
322 uint8_t* pbuf;
323 std::size_t nbyte = rx_buf_->back_contiguous_buffer (&pbuf);
324
325 result = driver_->receive (pbuf, nbyte);
326 if (result != os::driver::RETURN_OK)
327 {
328 errno = EIO;
329 return -1;
330 }
331
332 is_connected_ = true;
333
334 // Return POSIX idea of OK.
335 return 0;
336 }
337
338 template<typename CS>
339 bool
341 {
342 return is_opened_;
343 }
344
345 template<typename CS>
346 bool
348 {
349 return is_connected_;
350 }
351
352 template<typename CS>
353 int
355 {
356
357 if (is_connected_)
358 {
359 // Wait for write to complete.
360 // TODO: what if flow control prevents this?
361 if (tx_buf_ != nullptr)
362 {
363 for (;;)
364 {
365 if (tx_buf_->empty ())
366 {
367 break;
368 }
369 tx_sem_.wait ();
370 }
371 }
372 }
373
374 // Abort pending reads.
376 ret = driver_->control (os::driver::serial::Control::abort_receive);
377 assert (ret == os::driver::RETURN_OK);
378
379 // Abort pending writes.
380 ret = driver_->control (os::driver::serial::Control::abort_send);
381 assert (ret == os::driver::RETURN_OK);
382
383 // Disable transmitter and receiver.
384 ret = driver_->control (os::driver::serial::Control::disable_tx);
385 assert (ret == os::driver::RETURN_OK);
386
387 ret = driver_->control (os::driver::serial::Control::disable_rx);
388 assert (ret == os::driver::RETURN_OK);
389 ret = driver_->control (os::driver::serial::Control::disable_break);
390 assert (ret == os::driver::RETURN_OK);
391
392 is_opened_ = false;
393 is_connected_ = false;
394
395 // Return POSIX idea of OK.
396 return 0;
397 }
398
399 template<typename CS>
400 ssize_t
401 device_serial_buffered<CS>::do_read (void* buf, std::size_t nbyte)
402 {
403 // TODO: implement cases when 0 must be returned
404 // (disconnects, timeouts).
405 while (true)
406 {
407 std::size_t count;
408 {
409 // ----- Enter critical section -------------------------------
411
412 count = rx_buf_->pop_front (static_cast<uint8_t*> (buf), nbyte);
413 // ----- Exit critical section --------------------------------
414 }
415 if (count > 0)
416 {
417 // Actual number of chars received in buffer.
418 return count;
419 }
420 if (!is_connected_)
421 {
422 errno = EIO;
423 return -1;
424 }
425 // Block and wait for bytes to arrive.
426 rx_sem_.wait ();
427 }
428 }
429
430 template<typename CS>
431 ssize_t
432 device_serial_buffered<CS>::do_write (const void* buf, std::size_t nbyte)
433 {
434 std::size_t count;
435
436 if (tx_buf_ != nullptr)
437 {
438 count = 0;
439 {
440 // ----- Enter critical section -------------------------------
442
443 if (tx_buf_->below_high_water_mark ())
444 {
445 // If there is more space in the buffer, try to fill it.
446 count = tx_buf_->push_back (
447 static_cast<const uint8_t*> (buf), nbyte);
448 }
449 // ----- Exit critical section --------------------------------
450 }
451 while (true)
452 {
454 {
455 // ----- Enter critical section ---------------------------
457
458#pragma GCC diagnostic push
459#if defined(__clang__)
460#elif defined(__GNUC__)
461#pragma GCC diagnostic ignored "-Waggregate-return"
462#endif
463 status = driver_->get_status ();
464#pragma GCC diagnostic pop
465
466 // ----- Exit critical section ----------------------------
467 }
468 if (!status.tx_busy)
469 {
470 uint8_t* pbuf;
471 std::size_t nb;
472 {
473 // ----- Enter critical section -----------------------
474 critical_section cs; // -----
475
476 nb = tx_buf_->front_contiguous_buffer (&pbuf);
477 // ----- Exit critical section ------------------------
478 }
479 if (nb > 0)
480 {
481 if (driver_->send (pbuf, nb) != os::driver::RETURN_OK)
482 {
483 errno = EIO;
484 return -1;
485 }
486 }
487 }
488
489// bool isBelowHWM;
490// {
491// critical_section cs; // -----
492//
493// isBelowHWM = tx_buf_->below_high_water_mark ();
494// }
495 if (count == nbyte)
496 {
497 return nbyte;
498 }
499
500 if (!is_connected_)
501 {
502 if (count > 0)
503 {
504 return count;
505 }
506
507 errno = EIO;
508 return -1;
509 }
510
511 // Block and wait for buffer to be freed.
512 tx_sem_.wait ();
513
514 if (count < nbyte)
515 {
516 // ----- Enter critical section ---------------------------
518
519 std::size_t n;
520 // If there is more space in the buffer, try to fill it.
521 n = tx_buf_->push_back (
522 static_cast<const uint8_t*> (buf) + count,
523 nbyte - count);
524 count += n;
525 // ----- Exit critical section ----------------------------
526 }
527 }
528 }
529 else
530 {
531 // Do not use a transmit buffer, send directly from the user buffer.
532 // Wait while transmitting.
534 for (;;)
535 {
536 if (!is_connected_)
537 {
538 errno = EIO;
539 return -1;
540 }
541
542 status = driver_->get_status ();
543 if (!status.is_tx_busy ())
544 {
545 break;
546 }
547 tx_sem_.wait ();
548 }
549
550 if ((driver_->send (buf, nbyte)) == os::driver::RETURN_OK)
551 {
552 for (;;)
553 {
554 if (!is_connected_)
555 {
556 errno = EIO;
557 return -1;
558 }
559
560 status = driver_->get_status ();
561 if (!status.is_tx_busy ())
562 {
563 break;
564 }
565 tx_sem_.wait ();
566 }
567 count = driver_->get_tx_count ();
568 }
569 else
570 {
571 count = -1;
572 errno = EIO;
573 }
574 }
575
576 // Actual number of bytes transmitted from buffer.
577 return count;
578 }
579
580#if 0
581 template<typename CS>
582 ssize_t
583 device_serial_buffered<CS>::do_writev (const struct iovec* iov, int iovcnt)
584 {
585 errno = ENOSYS; // Not implemented
586 return -1;
587 }
588
589 template<typename CS>
590 int
591 device_serial_buffered<CS>::do_vioctl (int request, std::va_list args)
592 {
593 errno = ENOSYS; // Not implemented
594 return -1;
595 }
596
597 template<typename CS>
598 int
599 device_serial_buffered<CS>::do_vfcntl (int cmd, std::va_list args)
600 {
601 errno = ENOSYS; // Not implemented
602 return -1;
603 }
604#endif
605
606 // ------------------------------------------------------------------------
607
608 template<typename CS>
609 void
611 uint32_t event)
612 {
613 if (!object->is_opened_)
614 {
615 // After close(), ignore interrupts.
616 return;
617 }
618 if ((event
622 {
623 // TODO: process errors and timeouts
624 std::size_t tmpCount = object->driver_->get_rx_count ();
625 std::size_t count = tmpCount - object->rx_count_;
626 object->rx_count_ = tmpCount;
627 std::size_t adjust = object->rx_buf_->advance_back (count);
628 assert (count == adjust);
629
631 {
632 uint8_t* pbuf;
633 std::size_t nbyte = object->rx_buf_->back_contiguous_buffer (
634 &pbuf);
635 if (nbyte == 0)
636 {
637 // Overwrite the last byte, but keep the driver in
638 // receive mode continuously.
639 object->rx_buf_->retreat_back ();
640 nbyte = object->rx_buf_->back_contiguous_buffer (&pbuf);
641 }
642 assert (nbyte > 0);
643
644 // Read as much as we can.
645 int32_t status;
646 status = object->driver_->receive (pbuf, nbyte);
647 // TODO: implement error processing.
648 assert (status == os::driver::RETURN_OK);
649
650 object->rx_count_ = 0;
651 }
652 if (count > 0)
653 {
654 // Immediately wake up, do not wait to reach any water mark.
655 object->rx_sem_.post ();
656 }
657 }
659 {
660 if (object->tx_buf_ != nullptr)
661 {
662 std::size_t count = object->driver_->get_tx_count ();
663 std::size_t adjust = object->tx_buf_->advance_front (count);
664 assert (count == adjust);
665
666 uint8_t* pbuf;
667 std::size_t nbyte = object->tx_buf_->front_contiguous_buffer (
668 &pbuf);
669 if (nbyte > 0)
670 {
671 int32_t status;
672 status = object->driver_->send (pbuf, nbyte);
673 // TODO: implement error processing
674 assert (status == os::driver::RETURN_OK);
675 }
676 else
677 {
678 object->tx_busy_ = false;
679 }
680 if (object->tx_buf_->below_low_water_mark ())
681 {
682 // Wake up thread, to come and send more bytes.
683 object->tx_sem_.post ();
684 }
685 }
686 else
687 {
688 // No buffer, wake up the thread to return from write().
689 object->tx_sem_.post ();
690 }
691 }
693 {
695 status = object->driver_->get_modem_status ();
696
697 bool is_dcd_active = status.is_dcd_active ();
698 object->is_connected_ = is_dcd_active;
699 if (is_dcd_active)
700 {
701 // Connected, wake-up open().
702 object->open_sem_.post ();
703 }
704 else
705 {
706 // Disconnected, cancel read.
707 object->rx_sem_.post ();
708
709 // Cancel write.
710 object->tx_sem_.post ();
711 }
712 }
714 {
715 // TODO: add flow control
716 }
718 {
719 // TODO: add flow control
720 }
721 }
722
723#pragma GCC diagnostic pop
724
725 } /* namespace posix */
726} /* namespace os */
727
728// ----------------------------------------------------------------------------
729
730#endif /* __cplusplus */
731
732// ----------------------------------------------------------------------------
733
734#endif /* CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_ */
Serial device driver capabilities.
Definition serial.h:454
bool dcd
RI Line: false=not available, true=available.
Definition serial.h:506
Serial modem status
Definition serial.h:351
Serial port status
Definition serial.h:257
bool tx_busy
< Transmitter busy flag
Definition serial.h:289
Circular buffer class template.
Buffered serial driver class template.
static void signal_event(device_serial_buffered *object, uint32_t event)
virtual ssize_t do_read(void *buf, std::size_t nbyte) override
virtual bool do_is_connected(void) override
device_serial_buffered(const char *device_name, os::driver::Serial *driver, os::posix::circular_buffer_bytes *rx_buf, os::posix::circular_buffer_bytes *tx_buf)
virtual bool do_is_opened(void) override
virtual int do_vopen(const char *path, int oflag, std::va_list args) override
virtual ssize_t do_write(const void *buf, std::size_t nbyte) override
POSIX compliant binary semaphore.
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:60
constexpr config_t PARITY_NONE
Even Parity.
Definition serial.h:104
constexpr config_t DATA_BITS_8
9 Data bits
Definition serial.h:94
constexpr config_t STOP_BITS_1
2 Stop bits
Definition serial.h:117
constexpr config_t MODE_ASYNCHRONOUS
< UART = (Asynchronous);; arg = Baudrate
Definition serial.h:63
constexpr config_t FLOW_CONTROL_NONE
RTS Flow Control.
Definition serial.h:133
@ abort_receive
Abort Serial::transfer()
Definition serial.h:201
@ abort_send
Abort Serial::receive()
Definition serial.h:198
@ disable_tx
Disable Receiver.
Definition serial.h:207
@ enable_tx
Enable Transmitter.
Definition serial.h:189
@ enable_rx
Enable Continuous Break transmission.
Definition serial.h:192
@ disable_rx
Disable Continuous Break transmission;.
Definition serial.h:210
@ cts
DSR state changed (optional)
Definition serial.h:429
@ rx_framing_error
Parity error detected on receive.
Definition serial.h:423
@ dsr
DCD state changed (optional)
Definition serial.h:432
@ dcd
RI state changed (optional)
Definition serial.h:435
@ rx_timeout
Break detected on receive.
Definition serial.h:417
@ receive_complete
Transfer completed.
Definition serial.h:402
@ tx_complete
Transmit data not available (Synchronous Slave)
Definition serial.h:408
constexpr return_t RETURN_OK
< Operation succeeded
Definition common.h:53
int32_t return_t
Definition common.h:47
void(* signal_event_t)(const void *object, event_t event)
Definition common.h:69
System namespace.
Single file µOS++ RTOS definitions.
Definition uio.h:41