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
+ data.block_tx_buffer = false ;
72
+ _thread_customer_list.push_back (data);
73
+ }
74
+ }
75
+
76
+ void SerialDispatcher::end ()
77
+ {
78
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
79
+
80
+ /* Retrieve the current thread id and remove
81
+ * the thread data from the thread data list.
82
+ */
83
+ osThreadId_t const current_thread_id = rtos::ThisThread::get_id ();
84
+ std::remove_if (std::begin (_thread_customer_list),
85
+ std::end (_thread_customer_list),
86
+ [current_thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == current_thread_id); });
87
+
88
+ /* If no thread consumers are left also end
89
+ * the serial device altogether.
90
+ */
91
+ if (_thread_customer_list.size () == 0 )
92
+ {
93
+ _terminate_thread = true ;
94
+ _thread.join ();
95
+ _serial.end ();
96
+ }
97
+ }
98
+
99
+ int SerialDispatcher::available ()
100
+ {
101
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
102
+ return _serial.available ();
103
+ }
104
+
105
+ int SerialDispatcher::peek ()
106
+ {
107
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
108
+ return _serial.peek ();
109
+ }
110
+
111
+ int SerialDispatcher::read ()
112
+ {
113
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
114
+ return _serial.read ();
115
+ }
116
+
117
+ void SerialDispatcher::flush ()
118
+ {
119
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
120
+ _serial.flush ();
121
+ }
122
+
123
+ size_t SerialDispatcher::write (uint8_t const b)
124
+ {
125
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
126
+
127
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
128
+
129
+ /* If this thread hasn't registered yet
130
+ * with the SerialDispatcher via 'begin'.
131
+ */
132
+ if (iter == std::end (_thread_customer_list))
133
+ return 0 ;
134
+
135
+ if (iter->tx_buffer .availableForStore ())
136
+ iter->tx_buffer .store_char (b);
137
+
138
+ /* Inform the worker thread that new data has
139
+ * been written to a Serial transmit buffer.
140
+ */
141
+ _cond.notify_one ();
142
+
143
+ return 1 ;
144
+ }
145
+
146
+ size_t SerialDispatcher::write (const uint8_t * data, size_t len)
147
+ {
148
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
149
+
150
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
151
+
152
+ /* If this thread hasn't registered yet
153
+ * with the SerialDispatcher via 'begin'.
154
+ */
155
+ if (iter == std::end (_thread_customer_list))
156
+ return 0 ;
157
+
158
+ size_t bytes_written = 0 ;
159
+ for (; (bytes_written < len) && iter->tx_buffer .availableForStore (); bytes_written++)
160
+ iter->tx_buffer .store_char (data[bytes_written]);
161
+
162
+ /* Inform the worker thread that new data has
163
+ * been written to a Serial transmit buffer.
164
+ */
165
+ _cond.notify_one ();
166
+
167
+ return bytes_written;
168
+ }
169
+
170
+ void SerialDispatcher::block ()
171
+ {
172
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
173
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
174
+ iter->block_tx_buffer = true ;
175
+ }
176
+
177
+ void SerialDispatcher::unblock ()
178
+ {
179
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
180
+ auto iter = findThreadCustomerDataById (rtos::ThisThread::get_id ());
181
+ iter->block_tx_buffer = false ;
182
+ _cond.notify_one ();
183
+ }
184
+
185
+ /* *************************************************************************************
186
+ * PRIVATE MEMBER FUNCTIONS
187
+ **************************************************************************************/
188
+
189
+ void SerialDispatcher::threadFunc ()
190
+ {
191
+ _has_tread_started = true ;
192
+
193
+ while (!_terminate_thread)
194
+ {
195
+ /* Prevent race conditions by multi-threaded
196
+ * access to shared data.
197
+ */
198
+ mbed::ScopedLock<rtos::Mutex> lock (_mutex);
199
+ /* Wait for new data to be available */
200
+ _cond.wait ();
201
+ /* Iterate over all list entries. */
202
+ std::for_each (std::begin (_thread_customer_list),
203
+ std::end (_thread_customer_list),
204
+ [this ](ThreadCustomerData & d)
205
+ {
206
+ if (!d.block_tx_buffer )
207
+ {
208
+ while (d.tx_buffer .available ())
209
+ {
210
+ _serial.write (d.tx_buffer .read_char ());
211
+ }
212
+ }
213
+ });
214
+ }
215
+ }
216
+
217
+ std::list<SerialDispatcher::ThreadCustomerData>::iterator SerialDispatcher::findThreadCustomerDataById (osThreadId_t const thread_id)
218
+ {
219
+ return std::find_if (std::begin (_thread_customer_list),
220
+ std::end (_thread_customer_list),
221
+ [thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == thread_id); });
222
+ }
0 commit comments