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