1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "pico.h"
#include "pico/mutex.h"
#include "pico/printf.h"
#include "pico/stdio.h"
#include "pico/stdio/driver.h"
#include "pico/time.h"
#if PICO_STDIO_UART
#include "pico/stdio_uart.h"
#endif
#if PICO_STDIO_USB
#include "pico/stdio_usb.h"
#endif
#if PICO_STDIO_SEMIHOSTING
#include "pico/stdio_semihosting.h"
#endif
static stdio_driver_t *drivers;
static stdio_driver_t *filter;
#if PICO_STDOUT_MUTEX
auto_init_mutex(print_mutex);
bool stdout_serialize_begin() {
int core_num = get_core_num();
uint32_t owner;
if (!mutex_try_enter(&print_mutex, &owner)) {
if (owner == core_num) {
return false;
}
// other core owns the mutex, so lets wait
mutex_enter_blocking(&print_mutex);
}
return true;
}
void stdout_serialize_end() {
mutex_exit(&print_mutex);
}
#else
static bool print_serialize_begin() {
return true;
}
static void print_serialize_end() {
}
#endif
static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len) {
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
if (!driver->crlf_enabled) {
driver->out_chars(s, len);
return;
}
int first_of_chunk = 0;
static const char crlf_str[] = {'\r', '\n'};
for (int i = 0; i < len; i++) {
bool prev_char_was_cr = i > 0 ? s[i - 1] == '\r' : driver->last_ended_with_cr;
if (s[i] == '\n' && !prev_char_was_cr) {
if (i > first_of_chunk) {
driver->out_chars(&s[first_of_chunk], i - first_of_chunk);
}
driver->out_chars(crlf_str, 2);
first_of_chunk = i + 1;
}
}
if (first_of_chunk < len) {
driver->out_chars(&s[first_of_chunk], len - first_of_chunk);
}
if (len > 0) {
driver->last_ended_with_cr = s[len - 1] == '\r';
}
#else
driver->out_chars(s, len);
#endif
}
static bool stdio_put_string(const char *s, int len, bool newline) {
bool serialzed = stdout_serialize_begin();
if (!serialzed) {
#if PICO_STDIO_IGNORE_NESTED_STDOUT
return false;
#endif
}
if (len == -1) len = strlen(s);
for (stdio_driver_t *driver = drivers; driver; driver = driver->next) {
if (!driver->out_chars) continue;
if (filter && filter != driver) continue;
stdio_out_chars_crlf(driver, s, len);
if (newline) {
const char c = '\n';
stdio_out_chars_crlf(driver, &c, 1);
}
}
if (serialzed) {
stdout_serialize_end();
}
return len;
}
static int stdio_get_until(char *buf, int len, absolute_time_t until) {
do {
// todo round robin might be nice on each call, but then again hopefully
// no source will starve the others
for (stdio_driver_t *driver = drivers; driver; driver = driver->next) {
if (filter && filter != driver) continue;
if (driver->in_chars) {
int read = driver->in_chars(buf, len);
if (read > 0) {
return read;
}
}
}
// todo maybe a little sleep here?
} while (!time_reached(until));
return PICO_ERROR_TIMEOUT;
}
int WRAPPER_FUNC(putchar)(int c) {
char cc = c;
stdio_put_string(&cc, 1, false);
return c;
}
int WRAPPER_FUNC(puts)(const char *s) {
int len = strlen(s);
stdio_put_string(s, len, true);
stdio_flush();
return len;
}
int _read(int handle, char *buffer, int length) {
if (handle == 0) {
return stdio_get_until(buffer, length, at_the_end_of_time);
}
return -1;
}
int _write(int handle, char *buffer, int length) {
if (handle == 1) {
stdio_put_string(buffer, length, false);
return length;
}
return -1;
}
void stdio_set_driver_enabled(stdio_driver_t *driver, bool enable) {
stdio_driver_t *prev = drivers;
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (d == driver) {
if (!enable) {
prev->next = d->next;
driver->next = NULL;
}
return;
}
prev = d;
}
if (enable) {
if (prev) prev->next = driver;
else drivers = driver;
}
}
void stdio_flush() {
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (d->out_flush) d->out_flush();
}
}
typedef struct stdio_stack_buffer {
uint used;
char buf[PICO_STDIO_STACK_BUFFER_SIZE];
} stdio_stack_buffer_t;
static void stdio_stack_buffer_flush(stdio_stack_buffer_t *buffer) {
if (buffer->used) {
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (!d->out_chars) continue;
if (filter && filter != d) continue;
stdio_out_chars_crlf(d, buffer->buf, buffer->used);
}
buffer->used = 0;
}
}
static void stdio_buffered_printer(char c, void *arg) {
stdio_stack_buffer_t *buffer = (stdio_stack_buffer_t *)arg;
if (buffer->used == PICO_STDIO_STACK_BUFFER_SIZE) {
stdio_stack_buffer_flush(buffer);
}
buffer->buf[buffer->used++] = c;
}
int WRAPPER_FUNC(vprintf)(const char *format, va_list va) {
bool serialzed = stdout_serialize_begin();
if (!serialzed) {
#if PICO_STDIO_IGNORE_NESTED_STDOUT
return 0;
#endif
}
int ret;
#if PICO_PRINTF_PICO
struct stdio_stack_buffer buffer = {.used = 0};
ret = vfctprintf(stdio_buffered_printer, &buffer, format, va);
stdio_stack_buffer_flush(&buffer);
stdio_flush();
#elif PICO_PRINTF_NONE
extern void printf_none_assert();
printf_none_assert();
#else
extern int REAL_FUNC(vprintf)(const char *format, va_list va);
ret = REAL_FUNC(vprintf)(format, va);
#endif
if (serialzed) {
stdout_serialize_end();
}
return ret;
}
int __printflike(1, 0) WRAPPER_FUNC(printf)(const char* format, ...)
{
va_list va;
va_start(va, format);
int ret = vprintf(format, va);
va_end(va);
return ret;
}
void stdio_init_all() {
// todo add explicit custom, or registered although you can call stdio_enable_driver explicitly anyway
// These are well known ones
#if PICO_STDIO_UART
stdio_uart_init();
#endif
#if PICO_STDIO_SEMIHOSTING
stdio_semihosting_init();
#endif
#if PICO_STDIO_USB
stdio_usb_init();
#endif
}
int WRAPPER_FUNC(getchar)() {
char buf[1];
if (0 == stdio_get_until(buf, sizeof(buf), at_the_end_of_time)) {
return PICO_ERROR_TIMEOUT;
}
return (uint8_t)buf[0];
}
int getchar_timeout_us(uint32_t timeout_us) {
char buf[1];
int rc = stdio_get_until(buf, sizeof(buf), make_timeout_time_us(timeout_us));
if (rc < 0) return rc;
assert(rc);
return (uint8_t)buf[0];
}
void stdio_filter_driver(stdio_driver_t *driver) {
filter = driver;
}
void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
if (enabled && !driver->crlf_enabled) {
driver->last_ended_with_cr = false;
}
driver->crlf_enabled = enabled;
#else
panic_unsupported();
#endif
}
|