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