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