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