1
+ /*
2
+ * This file is part of the Arduino_ThreadsafeIO library.
3
+ * Copyright (c) 2021 Arduino SA.
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ * This library is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ * Lesser General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU Lesser General Public
15
+ * License along with this library; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+ */
18
+
19
+ /* *************************************************************************************
20
+ * INCLUDE
21
+ **************************************************************************************/
22
+
23
+ #include " SerialDispatcher.h"
24
+
25
+ /* *************************************************************************************
26
+ * CTOR/DTOR
27
+ **************************************************************************************/
28
+
29
+ SerialDispatcher::SerialDispatcher (arduino::HardwareSerial & serial)
30
+ : _is_initialized{false }
31
+ , _mutex{}
32
+ , _cond{_mutex}
33
+ , _serial{serial}
34
+ , _thread(osPriorityRealtime, 4096 , nullptr , " SerialDispatcher" )
35
+ , _has_tread_started{false }
36
+ , _terminate_thread{false }
37
+ {
38
+
39
+ }
40
+
41
+ /* *************************************************************************************
42
+ * PUBLIC MEMBER FUNCTIONS
43
+ **************************************************************************************/
44
+
45
+ void SerialDispatcher::begin (unsigned long baudrate)
46
+ {
47
+ begin (baudrate, SERIAL_8N1);
48
+ }
49
+
50
+ void SerialDispatcher::begin (unsigned long baudrate, uint16_t config)
51
+ {
52
+ if (!_is_initialized)
53
+ {
54
+ _serial.begin (baudrate, config);
55
+ _is_initialized = true ;
56
+ _thread.start (mbed::callback (this , &SerialDispatcher::threadFunc)); /* TODO: Check return code */
57
+ while (!_has_tread_started) { }
58
+ }
59
+
60
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
61
+
62
+ /* Check if the thread calling begin is already in the list. */
63
+ osThreadId_t const current_thread_id = rtos::ThisThread::get_id ();
64
+ if (findThreadCustomerDataById (rtos::ThisThread::get_id ()) == std::end (_thread_customer_list))
65
+ {
66
+ /* Since the thread is not in the list yet we are
67
+ * going to create a new entry to the list.
68
+ */
69
+ ThreadCustomerData data;
70
+ data.thread_id = current_thread_id;
71
+ _thread_customer_list.push_back (data);
72
+ }
73
+ }
74
+
75
+ void SerialDispatcher::end ()
76
+ {
77
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
78
+
79
+ /* Retrieve the current thread id and remove
80
+ * the thread data from the thread data list.
81
+ */
82
+ osThreadId_t const current_thread_id = rtos::ThisThread::get_id ();
83
+ std::remove_if (std::begin (_thread_customer_list),
84
+ std::end (_thread_customer_list),
85
+ [current_thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == current_thread_id); });
86
+
87
+ /* If no thread consumers are left also end
88
+ * the serial device alltogether.
89
+ */
90
+ if (_thread_customer_list.size () == 0 )
91
+ {
92
+ _terminate_thread = true ;
93
+ _thread.join ();
94
+ _serial.end ();
95
+ }
96
+ }
97
+
98
+ int SerialDispatcher::available ()
99
+ {
100
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
101
+ return _serial.available ();
102
+ }
103
+
104
+ int SerialDispatcher::peek ()
105
+ {
106
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
107
+ return _serial.peek ();
108
+ }
109
+
110
+ int SerialDispatcher::read ()
111
+ {
112
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
113
+ return _serial.read ();
114
+ }
115
+
116
+ void SerialDispatcher::flush ()
117
+ {
118
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
119
+ _serial.flush ();
120
+ }
121
+
122
+ size_t SerialDispatcher::write (uint8_t const b)
123
+ {
124
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
125
+
126
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
127
+
128
+ /* If this thread hasn't registered yet
129
+ * with the SerialDispatcher via 'begin'.
130
+ */
131
+ if (iter == std::end (_thread_customer_list))
132
+ return 0 ;
133
+
134
+ if (iter->tx_buffer .availableForStore ())
135
+ iter->tx_buffer .store_char (b);
136
+
137
+ /* Inform the worker thread that new data has
138
+ * been written to a Serial transmit buffer.
139
+ */
140
+ _cond.notify_one ();
141
+
142
+ return 1 ;
143
+ }
144
+
145
+ size_t SerialDispatcher::write (const uint8_t * data, size_t len)
146
+ {
147
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
148
+
149
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
150
+
151
+ /* If this thread hasn't registered yet
152
+ * with the SerialDispatcher via 'begin'.
153
+ */
154
+ if (iter == std::end (_thread_customer_list))
155
+ return 0 ;
156
+
157
+ size_t bytes_written = 0 ;
158
+ for (; (bytes_written < len) && iter->tx_buffer .availableForStore (); bytes_written++)
159
+ iter->tx_buffer .store_char (data[bytes_written]);
160
+
161
+ /* Inform the worker thread that new data has
162
+ * been written to a Serial transmit buffer.
163
+ */
164
+ _cond.notify_one ();
165
+
166
+ return bytes_written;
167
+ }
168
+
169
+ /* *************************************************************************************
170
+ * PRIVATE MEMBER FUNCTIONS
171
+ **************************************************************************************/
172
+
173
+ void SerialDispatcher::threadFunc ()
174
+ {
175
+ _has_tread_started = true ;
176
+
177
+ while (!_terminate_thread)
178
+ {
179
+ /* Prevent race conditions by multi-threaded
180
+ * access to shared data.
181
+ */
182
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
183
+ /* Wait for new data to be available */
184
+ _cond.wait ();
185
+ /* Iterate over all list entries. */
186
+ std::for_each (std::begin (_thread_customer_list),
187
+ std::end (_thread_customer_list),
188
+ [this ](ThreadCustomerData & d)
189
+ {
190
+ while (d.tx_buffer .available ())
191
+ {
192
+ _serial.write (d.tx_buffer .read_char ());
193
+ }
194
+ });
195
+ }
196
+ }
197
+
198
+ std::list<SerialDispatcher::ThreadCustomerData>::iterator SerialDispatcher::findThreadCustomerDataById (osThreadId_t const thread_id)
199
+ {
200
+ return std::find_if (std::begin (_thread_customer_list),
201
+ std::end (_thread_customer_list),
202
+ [thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == thread_id); });
203
+ }
0 commit comments