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