Initial Release

This commit is contained in:
graham sanderson
2021-01-20 10:44:27 -06:00
commit 26653ea81e
404 changed files with 135614 additions and 0 deletions

View File

@ -0,0 +1,18 @@
if (NOT TARGET pico_stdio)
add_library(pico_stdio INTERFACE)
target_include_directories(pico_stdio INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(pico_stdio INTERFACE
${CMAKE_CURRENT_LIST_DIR}/stdio.c
)
pico_wrap_function(pico_stdio printf)
pico_wrap_function(pico_stdio vprintf)
pico_wrap_function(pico_stdio puts)
pico_wrap_function(pico_stdio putchar)
if (TARGET pico_printf)
target_link_libraries(pico_stdio INTERFACE pico_printf)
endif()
endif()

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Marco Paland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_STDIO_H
#define _PICO_STDIO_H
/** \file stdio.h
* \defgroup pico_stdio pico_stdio
* Customized stdio support allowing for input and output from UART, USB, semi-hosting etc.
*
* Note the API for adding additional input output devices is not yet considered stable
*/
#include "pico.h"
// PICO_CONFIG: PICO_STDOUT_MUTEX, Enable/disable mutex around stdout, type=bool, default=1, group=pico_stdio
#ifndef PICO_STDOUT_MUTEX
#define PICO_STDOUT_MUTEX 1
#endif
// PICO_CONFIG: PICO_STDIO_ENABLE_CRLF_SUPPORT, Enable/disable CR/LF output conversion support, type=bool, default=1, group=pico_stdio
#ifndef PICO_STDIO_ENABLE_CRLF_SUPPORT
#define PICO_STDIO_ENABLE_CRLF_SUPPORT 1
#endif
// PICO_CONFIG: PICO_STDIO_DEFAULT_CRLF, Default for CR/LF conversion enabled on all stdio outputs, type=bool, default=1, depends=PICO_STDIO_ENABLE_CRLF_SUPPORT, group=pico_stdio
#ifndef PICO_STDIO_DEFAULT_CRLF
#define PICO_STDIO_DEFAULT_CRLF 1
#endif
// PICO_CONFIG: PICO_STDIO_STACK_BUFFER_SIZE, Define printf buffer size (on stack)... this is just a working buffer not a max output size, min=0, max=512, default=128, group=pico_stdio
#ifndef PICO_STDIO_STACK_BUFFER_SIZE
#define PICO_STDIO_STACK_BUFFER_SIZE 128
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stdio_driver stdio_driver_t;
/*! \brief Initialize all of the present standard stdio types that are linked into the binary.
* \ingroup pico_stdio
*
* Call this method once you have set up your clocks to enable the stdio support for UART, USB
* and semihosting based on the presence of the respective librariess in the binary.
*
* \see stdio_uart, stdio_usb, stdio_semihosting
*/
void stdio_init_all();
/*! \brief Initialize all of the present standard stdio types that are linked into the binary.
* \ingroup pico_stdio
*
* Call this method once you have set up your clocks to enable the stdio support for UART, USB
* and semihosting based on the presence of the respective librariess in the binary.
*
* \see stdio_uart, stdio_usb, stdio_semihosting
*/
void stdio_flush();
/*! \brief Return a character from stdin if there is one available within a timeout
* \ingroup pico_stdio
*
* \param timeout_us the timeout in microseconds, or 0 to not wait for a character if none available.
* \return the character from 0-255 or PICO_ERROR_TIMEOUT if timeout occurs
*/
int getchar_timeout_us(uint32_t timeout_us);
/*! \brief Adds or removes a driver from the list of active drivers used for input/output
* \ingroup pico_stdio
*
* \note this method should always be called on an initialized driver
* \param driver the driver
* \param enabled true to add, false to remove
*/
void stdio_set_driver_enabled(stdio_driver_t *driver, bool enabled);
/*! \brief Control limiting of output to a single driver
* \ingroup pico_stdio
*
* \note this method should always be called on an initialized driver
*
* \param driver if non-null then output only that driver will be used for input/output (assuming it is in the list of enabled drivers).
* if NULL then all enabled drivers will be used
*/
void stdio_filter_driver(stdio_driver_t *driver);
/*! \brief control conversion of line feeds to carriage return on transmissions
* \ingroup pico_stdio
*
* \note this method should always be called on an initialized driver
*
* \param driver the driver
* \param translate If true, convert line feeds to carriage return on transmissions
*/
void stdio_set_translate_crlf(stdio_driver_t *driver, bool translate);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_STDIO_DRIVER_H
#define _PICO_STDIO_DRIVER_H
#include "pico/stdio.h"
#include "pico/platform.h"
struct stdio_driver {
void (*out_chars)(const char *buf, int len);
void (*out_flush)();
int (*in_chars)(char *buf, int len);
stdio_driver_t *next;
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
bool last_ended_with_cr;
bool crlf_enabled;
#endif
};
#endif

View File

@ -0,0 +1,287 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "pico.h"
#include "pico/mutex.h"
#include "pico/printf.h"
#include "pico/stdio.h"
#include "pico/stdio/driver.h"
#include "pico/time.h"
#if PICO_STDIO_UART
#include "pico/stdio_uart.h"
#endif
#if PICO_STDIO_USB
#include "pico/stdio_usb.h"
#endif
#if PICO_STDIO_SEMIHOSTING
#include "pico/stdio_semihosting.h"
#endif
static stdio_driver_t *drivers;
static stdio_driver_t *filter;
#if PICO_STDOUT_MUTEX
auto_init_mutex(print_mutex);
bool stdout_serialize_begin() {
int core_num = get_core_num();
uint32_t owner;
if (!mutex_try_enter(&print_mutex, &owner)) {
if (owner == core_num) {
return false;
}
// other core owns the mutex, so lets wait
mutex_enter_blocking(&print_mutex);
}
return true;
}
void stdout_serialize_end() {
mutex_exit(&print_mutex);
}
#else
static bool print_serialize_begin() {
return true;
}
static void print_serialize_end() {
}
#endif
static void stdio_out_chars_crlf(stdio_driver_t *driver, const char *s, int len) {
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
if (!driver->crlf_enabled) {
driver->out_chars(s, len);
return;
}
int first_of_chunk = 0;
static const char crlf_str[] = {'\r', '\n'};
for (int i = 0; i < len; i++) {
bool prev_char_was_cr = i > 0 ? s[i - 1] == '\r' : driver->last_ended_with_cr;
if (s[i] == '\n' && !prev_char_was_cr) {
if (i > first_of_chunk) {
driver->out_chars(&s[first_of_chunk], i - first_of_chunk);
}
driver->out_chars(crlf_str, 2);
first_of_chunk = i + 1;
}
}
if (first_of_chunk < len) {
driver->out_chars(&s[first_of_chunk], len - first_of_chunk);
}
if (len > 0) {
driver->last_ended_with_cr = s[len - 1] == '\r';
}
#else
driver->out_chars(s, len);
#endif
}
static bool stdio_put_string(const char *s, int len, bool newline) {
bool serialzed = stdout_serialize_begin();
if (!serialzed) {
#if PICO_STDIO_IGNORE_NESTED_STDOUT
return false;
#endif
}
if (len == -1) len = strlen(s);
for (stdio_driver_t *driver = drivers; driver; driver = driver->next) {
if (!driver->out_chars) continue;
if (filter && filter != driver) continue;
stdio_out_chars_crlf(driver, s, len);
if (newline) {
const char c = '\n';
stdio_out_chars_crlf(driver, &c, 1);
}
}
if (serialzed) {
stdout_serialize_end();
}
return len;
}
static int stdio_get_until(char *buf, int len, absolute_time_t until) {
do {
// todo round robin might be nice on each call, but then again hopefully
// no source will starve the others
for (stdio_driver_t *driver = drivers; driver; driver = driver->next) {
if (filter && filter != driver) continue;
if (driver->in_chars) {
int read = driver->in_chars(buf, len);
if (read > 0) {
return read;
}
}
}
// todo maybe a little sleep here?
} while (!time_reached(until));
return PICO_ERROR_TIMEOUT;
}
int WRAPPER_FUNC(putchar)(int c) {
char cc = c;
stdio_put_string(&cc, 1, false);
return c;
}
int WRAPPER_FUNC(puts)(const char *s) {
int len = strlen(s);
stdio_put_string(s, len, true);
stdio_flush();
return len;
}
int _read(int handle, char *buffer, int length) {
if (handle == 0) {
return stdio_get_until(buffer, length, at_the_end_of_time);
}
return -1;
}
int _write(int handle, char *buffer, int length) {
if (handle == 1) {
stdio_put_string(buffer, length, false);
return length;
}
return -1;
}
void stdio_set_driver_enabled(stdio_driver_t *driver, bool enable) {
stdio_driver_t *prev = drivers;
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (d == driver) {
if (!enable) {
prev->next = d->next;
driver->next = NULL;
}
return;
}
prev = d;
}
if (enable) {
if (prev) prev->next = driver;
else drivers = driver;
}
}
void stdio_flush() {
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (d->out_flush) d->out_flush();
}
}
typedef struct stdio_stack_buffer {
uint used;
char buf[PICO_STDIO_STACK_BUFFER_SIZE];
} stdio_stack_buffer_t;
static void stdio_stack_buffer_flush(stdio_stack_buffer_t *buffer) {
if (buffer->used) {
for (stdio_driver_t *d = drivers; d; d = d->next) {
if (!d->out_chars) continue;
if (filter && filter != d) continue;
stdio_out_chars_crlf(d, buffer->buf, buffer->used);
}
buffer->used = 0;
}
}
static void stdio_buffered_printer(char c, void *arg) {
stdio_stack_buffer_t *buffer = (stdio_stack_buffer_t *)arg;
if (buffer->used == PICO_STDIO_STACK_BUFFER_SIZE) {
stdio_stack_buffer_flush(buffer);
}
buffer->buf[buffer->used++] = c;
}
int WRAPPER_FUNC(vprintf)(const char *format, va_list va) {
bool serialzed = stdout_serialize_begin();
if (!serialzed) {
#if PICO_STDIO_IGNORE_NESTED_STDOUT
return 0;
#endif
}
int ret;
#if PICO_PRINTF_PICO
struct stdio_stack_buffer buffer = {.used = 0};
ret = vfctprintf(stdio_buffered_printer, &buffer, format, va);
stdio_stack_buffer_flush(&buffer);
stdio_flush();
#elif PICO_PRINTF_NONE
extern void printf_none_assert();
printf_none_assert();
#else
extern int REAL_FUNC(vprintf)(const char *format, va_list va);
ret = REAL_FUNC(vprintf)(format, va);
#endif
if (serialzed) {
stdout_serialize_end();
}
return ret;
}
int __printflike(1, 0) WRAPPER_FUNC(printf)(const char* format, ...)
{
va_list va;
va_start(va, format);
int ret = vprintf(format, va);
va_end(va);
return ret;
}
void stdio_init_all() {
// todo add explicit custom, or registered although you can call stdio_enable_driver explicitly anyway
// These are well known ones
#if PICO_STDIO_UART
stdio_uart_init();
#endif
#if PICO_STDIO_SEMIHOSTING
stdio_semihosting_init();
#endif
#if PICO_STDIO_USB
stdio_usb_init();
#endif
}
int WRAPPER_FUNC(getchar)() {
char buf[1];
if (0 == stdio_get_until(buf, sizeof(buf), at_the_end_of_time)) {
return PICO_ERROR_TIMEOUT;
}
return (uint8_t)buf[0];
}
int getchar_timeout_us(uint32_t timeout_us) {
char buf[1];
int rc = stdio_get_until(buf, sizeof(buf), make_timeout_time_us(timeout_us));
if (rc < 0) return rc;
assert(rc);
return (uint8_t)buf[0];
}
void stdio_filter_driver(stdio_driver_t *driver) {
filter = driver;
}
void stdio_set_translate_crlf(stdio_driver_t *driver, bool enabled) {
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
if (enabled && !driver->crlf_enabled) {
driver->last_ended_with_cr = false;
}
driver->crlf_enabled = enabled;
#else
panic_unsupported();
#endif
}