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