µ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 Liviu Ionescu.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_
29#define CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_
30
31#if defined(__cplusplus)
32
33// ----------------------------------------------------------------------------
34
35#include <cmsis-plus/rtos/os.h>
36
37#include <cmsis-plus/posix-io/device-char.h>
40
41// ----------------------------------------------------------------------------
42
43// TODO: (multiline)
44// - add flow control on both send & receive
45// - cancel pending reads/writes at close (partly done)
46// - add error processing
47
48namespace os
49{
50 namespace posix
51 {
52 // ------------------------------------------------------------------------
53
54#pragma GCC diagnostic push
55#pragma GCC diagnostic ignored "-Wpadded"
56
62 template<typename CS>
63 class device_serial_buffered : public os::posix::device_char
64 {
65 using critical_section = CS;
66
67 // ----------------------------------------------------------------------
68
74 public:
75
76 device_serial_buffered (const char* device_name,
77 os::driver::Serial* driver,
80
85 // The rule of five.
89 operator= (const device_serial_buffered&) = delete;
91 operator= (device_serial_buffered&&) = delete;
92
97 virtual
99
104 // --------------------------------------------------------------------
110 public:
111
112 // Static function called by the CMSIS driver in an
113 // interrupt context.
114
115 static void
116 signal_event (device_serial_buffered* object, uint32_t event);
117
122 // --------------------------------------------------------------------
128 protected:
129
130 virtual int
131 do_vopen (const char* path, int oflag, std::va_list args) override;
132
133 virtual int
134 do_close (void) override;
135
136 virtual ssize_t
137 do_read (void* buf, std::size_t nbyte) override;
138
139 virtual ssize_t
140 do_write (const void* buf, std::size_t nbyte) override;
141
142#if 0
143 virtual ssize_t
144 do_writev (const struct iovec* iov, int iovcnt) override;
145
146 virtual int
147 do_vioctl (int request, std::va_list args) override;
148
149 virtual int
150 do_vfcntl (int cmd, va_list args) override;
151#endif
152
153 virtual bool
154 do_is_opened (void) override;
155
156 virtual bool
157 do_is_connected (void) override;
158
163 // --------------------------------------------------------------------
164 private:
165
170 // Pointer to actual CMSIS-like serial driver (usart or usb cdc acm)
171 os::driver::Serial* driver_ = nullptr;
172
174 { "open", 0 };
176 { "rx", 0 };
178 { "tx", 0 };
179
180 os::posix::circular_buffer_bytes* rx_buf_ = nullptr;
181 os::posix::circular_buffer_bytes* tx_buf_ = nullptr;
182
183 std::size_t rx_count_ = 0; //
184 bool volatile tx_busy_ = false;
185 bool volatile is_connected_ = false;
186 bool volatile is_opened_ = false;
187 // Padding!
188
193 };
194
195#pragma GCC diagnostic pop
196
197 } /* namespace posix */
198} /* namespace os */
199
200// ===== Inline & template implementations ====================================
201
202namespace os
203{
204 namespace posix
205 {
206 // ------------------------------------------------------------------------
207
208 template<typename CS>
210 const char* device_name, //
213 //
214 device_char (device_name), // Construct parent.
215 driver_ (driver), //
216 rx_buf_ (rx_buf), //
217 tx_buf_ (tx_buf) //
218 {
219 trace::printf ("%s(\"%s\",%p,%p,%p) %p\n", __func__, device_name,
220 driver, rx_buf, tx_buf, this);
221
222 assert (rx_buf != nullptr);
223
224 // Do not check the same for tx_buf, it may be null.
225
226 driver_->register_callback (
227 reinterpret_cast<os::driver::signal_event_t> (signal_event), this);
228 }
229
230 template<typename CS>
232 {
233 trace::printf ("%s() %p\n", __func__, this);
234
235 driver_ = nullptr;
236 is_connected_ = false;
237 is_opened_ = false;
238 }
239
240 // ------------------------------------------------------------------------
241
242#pragma GCC diagnostic push
243#pragma GCC diagnostic ignored "-Wunused-parameter"
244
245 template<typename CS>
246 int
247 device_serial_buffered<CS>::do_vopen (const char* path, int oflag,
248 std::va_list args)
249 {
250 if (is_opened_)
251 {
252 errno = EEXIST; // Already opened
253 return -1;
254 }
255
256 int32_t result;
257
258 do
259 {
260 // Reset semaphores, in case we come here after close.
261 open_sem_.reset ();
262 rx_sem_.reset ();
263 tx_sem_.reset ();
264
265 is_opened_ = true;
266
267 // Clear buffers.
268 rx_buf_->clear ();
269 rx_count_ = 0;
270
271 if (tx_buf_ != nullptr)
272 {
273 tx_buf_->clear ();
274 }
275
276 // Default configuration: 8 bits, no parity, 1 stop bit,
277 // no flow control, 115200 bps.
278 result = driver_->configure (
284 115200);
285 // assert(result == os::driver::RETURN_OK);
286 if (result != os::driver::RETURN_OK)
287 break;
288
289 // Enable TX output.
290 result = driver_->control (os::driver::serial::Control::enable_tx);
291 if (result != os::driver::RETURN_OK)
292 break;
293
294 // Enable RX input.
295 result = driver_->control (os::driver::serial::Control::enable_rx);
296 if (result != os::driver::RETURN_OK)
297 break;
298
299 }
300 while (false); // Actually NOT a loop, just a sequence of ifs!
301
302 if (result != os::driver::RETURN_OK)
303 {
304 errno = EIO;
305 return -1;
306 }
307
309 capa = driver_->get_capabilities ();
310 if (capa.dcd)
311 {
313 for (;;)
314 {
315 {
316 // ----- Enter critical section ---------------------------
318
319 status = driver_->get_modem_status ();
320 // ----- Exit critical section ----------------------------
321 }
322 if (status.is_dcd_active ())
323 {
324 break;
325 }
326 open_sem_.wait ();
327 }
328 }
329
330 uint8_t* pbuf;
331 std::size_t nbyte = rx_buf_->back_contiguous_buffer (&pbuf);
332
333 result = driver_->receive (pbuf, nbyte);
334 if (result != os::driver::RETURN_OK)
335 {
336 errno = EIO;
337 return -1;
338 }
339
340 is_connected_ = true;
341
342 // Return POSIX idea of OK.
343 return 0;
344 }
345
346 template<typename CS>
347 bool
349 {
350 return is_opened_;
351 }
352
353 template<typename CS>
354 bool
356 {
357 return is_connected_;
358 }
359
360 template<typename CS>
361 int
363 {
364
365 if (is_connected_)
366 {
367 // Wait for write to complete.
368 // TODO: what if flow control prevents this?
369 if (tx_buf_ != nullptr)
370 {
371 for (;;)
372 {
373 if (tx_buf_->empty ())
374 {
375 break;
376 }
377 tx_sem_.wait ();
378 }
379 }
380 }
381
382 // Abort pending reads.
384 ret = driver_->control (os::driver::serial::Control::abort_receive);
385 assert (ret == os::driver::RETURN_OK);
386
387 // Abort pending writes.
388 ret = driver_->control (os::driver::serial::Control::abort_send);
389 assert (ret == os::driver::RETURN_OK);
390
391 // Disable transmitter and receiver.
392 ret = driver_->control (os::driver::serial::Control::disable_tx);
393 assert (ret == os::driver::RETURN_OK);
394
395 ret = driver_->control (os::driver::serial::Control::disable_rx);
396 assert (ret == os::driver::RETURN_OK);
397 ret = driver_->control (os::driver::serial::Control::disable_break);
398 assert (ret == os::driver::RETURN_OK);
399
400 is_opened_ = false;
401 is_connected_ = false;
402
403 // Return POSIX idea of OK.
404 return 0;
405 }
406
407 template<typename CS>
408 ssize_t
409 device_serial_buffered<CS>::do_read (void* buf, std::size_t nbyte)
410 {
411 // TODO: implement cases when 0 must be returned
412 // (disconnects, timeouts).
413 while (true)
414 {
415 std::size_t count;
416 {
417 // ----- Enter critical section -------------------------------
419
420 count = rx_buf_->pop_front (static_cast<uint8_t*> (buf), nbyte);
421 // ----- Exit critical section --------------------------------
422 }
423 if (count > 0)
424 {
425 // Actual number of chars received in buffer.
426 return count;
427 }
428 if (!is_connected_)
429 {
430 errno = EIO;
431 return -1;
432 }
433 // Block and wait for bytes to arrive.
434 rx_sem_.wait ();
435 }
436 }
437
438 template<typename CS>
439 ssize_t
440 device_serial_buffered<CS>::do_write (const void* buf, std::size_t nbyte)
441 {
442 std::size_t count;
443
444 if (tx_buf_ != nullptr)
445 {
446 count = 0;
447 {
448 // ----- Enter critical section -------------------------------
450
451 if (tx_buf_->below_high_water_mark ())
452 {
453 // If there is more space in the buffer, try to fill it.
454 count = tx_buf_->push_back (
455 static_cast<const uint8_t*> (buf), nbyte);
456 }
457 // ----- Exit critical section --------------------------------
458 }
459 while (true)
460 {
462 {
463 // ----- Enter critical section ---------------------------
465
466#pragma GCC diagnostic push
467#pragma GCC diagnostic ignored "-Waggregate-return"
468 status = driver_->get_status ();
469#pragma GCC diagnostic pop
470
471 // ----- Exit critical section ----------------------------
472 }
473 if (!status.tx_busy)
474 {
475 uint8_t* pbuf;
476 std::size_t nb;
477 {
478 // ----- Enter critical section -----------------------
479 critical_section cs; // -----
480
481 nb = tx_buf_->front_contiguous_buffer (&pbuf);
482 // ----- Exit critical section ------------------------
483 }
484 if (nb > 0)
485 {
486 if (driver_->send (pbuf, nb) != os::driver::RETURN_OK)
487 {
488 errno = EIO;
489 return -1;
490 }
491 }
492 }
493
494// bool isBelowHWM;
495// {
496// critical_section cs; // -----
497//
498// isBelowHWM = tx_buf_->below_high_water_mark ();
499// }
500 if (count == nbyte)
501 {
502 return nbyte;
503 }
504
505 if (!is_connected_)
506 {
507 if (count > 0)
508 {
509 return count;
510 }
511
512 errno = EIO;
513 return -1;
514 }
515
516 // Block and wait for buffer to be freed.
517 tx_sem_.wait ();
518
519 if (count < nbyte)
520 {
521 // ----- Enter critical section ---------------------------
523
524 std::size_t n;
525 // If there is more space in the buffer, try to fill it.
526 n = tx_buf_->push_back (
527 static_cast<const uint8_t*> (buf) + count,
528 nbyte - count);
529 count += n;
530 // ----- Exit critical section ----------------------------
531 }
532 }
533 }
534 else
535 {
536 // Do not use a transmit buffer, send directly from the user buffer.
537 // Wait while transmitting.
539 for (;;)
540 {
541 if (!is_connected_)
542 {
543 errno = EIO;
544 return -1;
545 }
546
547 status = driver_->get_status ();
548 if (!status.is_tx_busy ())
549 {
550 break;
551 }
552 tx_sem_.wait ();
553 }
554
555 if ((driver_->send (buf, nbyte)) == os::driver::RETURN_OK)
556 {
557 for (;;)
558 {
559 if (!is_connected_)
560 {
561 errno = EIO;
562 return -1;
563 }
564
565 status = driver_->get_status ();
566 if (!status.is_tx_busy ())
567 {
568 break;
569 }
570 tx_sem_.wait ();
571 }
572 count = driver_->get_tx_count ();
573 }
574 else
575 {
576 count = -1;
577 errno = EIO;
578 }
579 }
580
581 // Actual number of bytes transmitted from buffer.
582 return count;
583 }
584
585#if 0
586 template<typename CS>
587 ssize_t
588 device_serial_buffered<CS>::do_writev (const struct iovec* iov, int iovcnt)
589 {
590 errno = ENOSYS; // Not implemented
591 return -1;
592 }
593
594 template<typename CS>
595 int
596 device_serial_buffered<CS>::do_vioctl (int request, std::va_list args)
597 {
598 errno = ENOSYS; // Not implemented
599 return -1;
600 }
601
602 template<typename CS>
603 int
604 device_serial_buffered<CS>::do_vfcntl (int cmd, std::va_list args)
605 {
606 errno = ENOSYS; // Not implemented
607 return -1;
608 }
609#endif
610
611 // ------------------------------------------------------------------------
612
613 template<typename CS>
614 void
616 uint32_t event)
617 {
618 if (!object->is_opened_)
619 {
620 // After close(), ignore interrupts.
621 return;
622 }
623 if ((event
627 {
628 // TODO: process errors and timeouts
629 std::size_t tmpCount = object->driver_->get_rx_count ();
630 std::size_t count = tmpCount - object->rx_count_;
631 object->rx_count_ = tmpCount;
632 std::size_t adjust = object->rx_buf_->advance_back (count);
633 assert (count == adjust);
634
636 {
637 uint8_t* pbuf;
638 std::size_t nbyte = object->rx_buf_->back_contiguous_buffer (
639 &pbuf);
640 if (nbyte == 0)
641 {
642 // Overwrite the last byte, but keep the driver in
643 // receive mode continuously.
644 object->rx_buf_->retreat_back ();
645 nbyte = object->rx_buf_->back_contiguous_buffer (&pbuf);
646 }
647 assert (nbyte > 0);
648
649 // Read as much as we can.
650 int32_t status;
651 status = object->driver_->receive (pbuf, nbyte);
652 // TODO: implement error processing.
653 assert (status == os::driver::RETURN_OK);
654
655 object->rx_count_ = 0;
656 }
657 if (count > 0)
658 {
659 // Immediately wake up, do not wait to reach any water mark.
660 object->rx_sem_.post ();
661 }
662 }
664 {
665 if (object->tx_buf_ != nullptr)
666 {
667 std::size_t count = object->driver_->get_tx_count ();
668 std::size_t adjust = object->tx_buf_->advance_front (count);
669 assert (count == adjust);
670
671 uint8_t* pbuf;
672 std::size_t nbyte = object->tx_buf_->front_contiguous_buffer (
673 &pbuf);
674 if (nbyte > 0)
675 {
676 int32_t status;
677 status = object->driver_->send (pbuf, nbyte);
678 // TODO: implement error processing
679 assert (status == os::driver::RETURN_OK);
680 }
681 else
682 {
683 object->tx_busy_ = false;
684 }
685 if (object->tx_buf_->below_low_water_mark ())
686 {
687 // Wake up thread, to come and send more bytes.
688 object->tx_sem_.post ();
689 }
690 }
691 else
692 {
693 // No buffer, wake up the thread to return from write().
694 object->tx_sem_.post ();
695 }
696 }
698 {
700 status = object->driver_->get_modem_status ();
701
702 bool is_dcd_active = status.is_dcd_active ();
703 object->is_connected_ = is_dcd_active;
704 if (is_dcd_active)
705 {
706 // Connected, wake-up open().
707 object->open_sem_.post ();
708 }
709 else
710 {
711 // Disconnected, cancel read.
712 object->rx_sem_.post ();
713
714 // Cancel write.
715 object->tx_sem_.post ();
716 }
717 }
719 {
720 // TODO: add flow control
721 }
723 {
724 // TODO: add flow control
725 }
726 }
727
728#pragma GCC diagnostic pop
729
730 } /* namespace posix */
731} /* namespace os */
732
733// ----------------------------------------------------------------------------
734
735#endif /* __cplusplus */
736
737// ----------------------------------------------------------------------------
738
739#endif /* CMSIS_PLUS_POSIX_DRIVER_DEVICE_SERIAL_BUFFERED_H_ */
Serial device driver capabilities.
Definition serial.h:458
bool dcd
RI Line: false=not available, true=available.
Definition serial.h:510
Serial modem status
Definition serial.h:359
Serial port status
Definition serial.h:269
bool tx_busy
< Transmitter busy flag
Definition serial.h:301
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:74
constexpr config_t PARITY_NONE
Even Parity.
Definition serial.h:120
constexpr config_t DATA_BITS_8
9 Data bits
Definition serial.h:110
constexpr config_t STOP_BITS_1
2 Stop bits
Definition serial.h:133
constexpr config_t MODE_ASYNCHRONOUS
< UART = (Asynchronous);; arg = Baudrate
Definition serial.h:79
constexpr config_t FLOW_CONTROL_NONE
RTS Flow Control.
Definition serial.h:149
@ abort_receive
Abort Serial::transfer()
Definition serial.h:217
@ abort_send
Abort Serial::receive()
Definition serial.h:214
@ disable_tx
Disable Receiver.
Definition serial.h:223
@ enable_tx
Enable Transmitter.
Definition serial.h:205
@ enable_rx
Enable Continuous Break transmission.
Definition serial.h:208
@ disable_rx
Disable Continuous Break transmission;.
Definition serial.h:226
@ cts
DSR state changed (optional)
Definition serial.h:437
@ rx_framing_error
Parity error detected on receive.
Definition serial.h:431
@ dsr
DCD state changed (optional)
Definition serial.h:440
@ dcd
RI state changed (optional)
Definition serial.h:443
@ rx_timeout
Break detected on receive.
Definition serial.h:425
@ receive_complete
Transfer completed.
Definition serial.h:410
@ tx_complete
Transmit data not available (Synchronous Slave)
Definition serial.h:416
constexpr return_t RETURN_OK
< Operation succeeded
Definition common.h:69
int32_t return_t
Definition common.h:63
void(* signal_event_t)(const void *object, event_t event)
Definition common.h:85
System namespace.
Single file µOS++ RTOS definitions.
Definition uio.h:56