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