Initial Release
This commit is contained in:
20
src/rp2_common/pico_stdio_usb/CMakeLists.txt
Normal file
20
src/rp2_common/pico_stdio_usb/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
if (TARGET tinyusb_device_unmarked)
|
||||
add_library(pico_stdio_usb INTERFACE)
|
||||
|
||||
target_include_directories(pico_stdio_usb INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
target_sources(pico_stdio_usb INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/stdio_usb.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/stdio_usb_descriptors.c
|
||||
)
|
||||
|
||||
target_link_libraries(pico_stdio_usb INTERFACE
|
||||
tinyusb_device_unmarked
|
||||
pico_stdio
|
||||
pico_time
|
||||
)
|
||||
|
||||
target_compile_definitions(pico_stdio_usb INTERFACE
|
||||
PICO_STDIO_USB=1
|
||||
)
|
||||
endif()
|
52
src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h
Normal file
52
src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_STDIO_USB_H
|
||||
#define _PICO_STDIO_USB_H
|
||||
|
||||
#include "pico/stdio.h"
|
||||
|
||||
/** \brief Support for stdin/stdout over USB serial (CDC)
|
||||
* \defgroup pico_stdio_usb pico_stdio_usb
|
||||
* \ingroup pico_stdio
|
||||
*
|
||||
* Linking this library or calling `pico_enable_stdio_usb(TARGET)` in the CMake (which
|
||||
* achieves the same thing) will add USB CDC to the drivers used for standard output
|
||||
*
|
||||
* Note this library is a developer convenience. It is not applicable in all cases; for one it takes full control of the USB device precluding your
|
||||
* use of the USB in device or host mode. For this reason, this library will automatically disengage if you try to using it alongside \ref tinyusb_device or
|
||||
* \ref tinyusb_host. It also takes control of a lower level IRQ and sets up a periodic background task.
|
||||
*/
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_DEFAULT_CRLF, Default state of CR/LF translation for USB output, type=bool, default=PICO_STDIO_DEFAULT_CRLF, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_DEFAULT_CRLF
|
||||
#define PICO_STDIO_USB_DEFAULT_CRLF PICO_STDIO_DEFAULT_CRLF
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_STDOUT_TIMEOUT_US, Number of microseconds to be blocked trying to write USB output before assuming the host has disappeared and discarding data, default=500000, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_STDOUT_TIMEOUT_US
|
||||
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
|
||||
#endif
|
||||
|
||||
// todo perhaps unnecessarily high?
|
||||
// PICO_CONFIG: PICO_STDIO_USB_TASK_INTERVAL_US, Period of microseconds between calling tud_task in the background, default=1000, advanced=true, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_TASK_INTERVAL_US
|
||||
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_LOW_PRIORITY_IRQ, low priority (non hardware) IRQ number to claim for tud_task() background execution, default=31, advanced=true, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_LOW_PRIORITY_IRQ
|
||||
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
|
||||
#endif
|
||||
|
||||
extern stdio_driver_t stdio_usb;
|
||||
|
||||
/*! \brief Explicitly initialize USB stdio and add it to the current set of stdin drivers
|
||||
* \ingroup pico_stdio_uart
|
||||
*/
|
||||
bool stdio_usb_init();
|
||||
|
||||
#endif
|
36
src/rp2_common/pico_stdio_usb/include/tusb_config.h
Normal file
36
src/rp2_common/pico_stdio_usb/include/tusb_config.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2020 Damien P. George
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PICO_STDIO_USB_TUSB_CONFIG_H
|
||||
#define _PICO_STDIO_USB_TUSB_CONFIG_H
|
||||
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
|
||||
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||
|
||||
#endif
|
115
src/rp2_common/pico_stdio_usb/stdio_usb.c
Normal file
115
src/rp2_common/pico_stdio_usb/stdio_usb.c
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if !defined(TINYUSB_HOST_LINKED) && !defined(TINYUSB_DEVICE_LINKED)
|
||||
#include "tusb.h"
|
||||
|
||||
#include "pico/time.h"
|
||||
#include "pico/stdio_usb.h"
|
||||
#include "pico/stdio/driver.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ > RTC_IRQ, ""); // note RTC_IRQ is currently the last one
|
||||
static mutex_t stdio_usb_mutex;
|
||||
|
||||
static void low_priority_worker_irq() {
|
||||
// if the mutex is already owned, then we are in user code
|
||||
// in this file which will do a tud_task itself, so we'll just do nothing
|
||||
// until the next tick; we won't starve
|
||||
if (mutex_try_enter(&stdio_usb_mutex, NULL)) {
|
||||
tud_task();
|
||||
mutex_exit(&stdio_usb_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
||||
irq_set_pending(PICO_STDIO_USB_LOW_PRIORITY_IRQ);
|
||||
return PICO_STDIO_USB_TASK_INTERVAL_US;
|
||||
}
|
||||
|
||||
static void stdio_usb_out_chars(const char *buf, int length) {
|
||||
static uint64_t last_avail_time;
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
||||
if (owner == get_core_num()) return; // would deadlock otherwise
|
||||
mutex_enter_blocking(&stdio_usb_mutex);
|
||||
}
|
||||
if (tud_cdc_connected()) {
|
||||
for (int i = 0; i < length;) {
|
||||
int n = length - i;
|
||||
int avail = tud_cdc_write_available();
|
||||
if (n > avail) n = avail;
|
||||
if (n) {
|
||||
int n2 = tud_cdc_write(buf + i, n);
|
||||
tud_task();
|
||||
tud_cdc_write_flush();
|
||||
i += n2;
|
||||
last_avail_time = time_us_64();
|
||||
} else {
|
||||
tud_task();
|
||||
tud_cdc_write_flush();
|
||||
if (!tud_cdc_connected() ||
|
||||
(!tud_cdc_write_available() && time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// reset our timeout
|
||||
last_avail_time = 0;
|
||||
}
|
||||
mutex_exit(&stdio_usb_mutex);
|
||||
}
|
||||
|
||||
int stdio_usb_in_chars(char *buf, int length) {
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
||||
if (owner == get_core_num()) return PICO_ERROR_NO_DATA; // would deadlock otherwise
|
||||
mutex_enter_blocking(&stdio_usb_mutex);
|
||||
}
|
||||
int rc = PICO_ERROR_NO_DATA;
|
||||
if (tud_cdc_connected() && tud_cdc_available()) {
|
||||
int count = tud_cdc_read(buf, length);
|
||||
rc = count ? count : PICO_ERROR_NO_DATA;
|
||||
}
|
||||
mutex_exit(&stdio_usb_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
stdio_driver_t stdio_usb = {
|
||||
.out_chars = stdio_usb_out_chars,
|
||||
.in_chars = stdio_usb_in_chars,
|
||||
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
||||
.crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF
|
||||
#endif
|
||||
};
|
||||
|
||||
bool stdio_usb_init(void) {
|
||||
#if !PICO_NO_BI_STDIO_USB
|
||||
bi_decl_if_func_used(bi_program_feature("USB stdin / stdout"));
|
||||
#endif
|
||||
|
||||
// initialize TinyUSB
|
||||
tusb_init();
|
||||
|
||||
irq_set_exclusive_handler(PICO_STDIO_USB_LOW_PRIORITY_IRQ, low_priority_worker_irq);
|
||||
irq_set_enabled(PICO_STDIO_USB_LOW_PRIORITY_IRQ, true);
|
||||
|
||||
mutex_init(&stdio_usb_mutex);
|
||||
bool rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
|
||||
if (rc) {
|
||||
stdio_set_driver_enabled(&stdio_usb, true);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
#include "pico/stdio_usb.h"
|
||||
#warning stdio USB was configured, but is being disabled as TinyUSB is explicitly linked
|
||||
bool stdio_usb_init(void) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
121
src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c
Normal file
121
src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is based on a file originally part of the
|
||||
* MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(TINYUSB_HOST_LINKED) && !defined(TINYUSB_DEVICE_LINKED)
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#define USBD_VID (0x2E8A) // Raspberry Pi
|
||||
#define USBD_PID (0x000a) // Pico SDK CDC
|
||||
|
||||
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
|
||||
#define USBD_MAX_POWER_MA (250)
|
||||
|
||||
#define USBD_ITF_CDC (0) // needs 2 interfaces
|
||||
#define USBD_ITF_MAX (2)
|
||||
|
||||
#define USBD_CDC_EP_CMD (0x81)
|
||||
#define USBD_CDC_EP_OUT (0x02)
|
||||
#define USBD_CDC_EP_IN (0x82)
|
||||
#define USBD_CDC_CMD_MAX_SIZE (8)
|
||||
#define USBD_CDC_IN_OUT_MAX_SIZE (64)
|
||||
|
||||
#define USBD_STR_0 (0x00)
|
||||
#define USBD_STR_MANUF (0x01)
|
||||
#define USBD_STR_PRODUCT (0x02)
|
||||
#define USBD_STR_SERIAL (0x03)
|
||||
#define USBD_STR_CDC (0x04)
|
||||
|
||||
// Note: descriptors returned from callbacks must exist long enough for transfer to complete
|
||||
|
||||
static const tusb_desc_device_t usbd_desc_device = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.idVendor = USBD_VID,
|
||||
.idProduct = USBD_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = USBD_STR_MANUF,
|
||||
.iProduct = USBD_STR_PRODUCT,
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
|
||||
|
||||
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD,
|
||||
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE),
|
||||
};
|
||||
|
||||
static const char *const usbd_desc_str[] = {
|
||||
[USBD_STR_MANUF] = "Raspberry Pi",
|
||||
[USBD_STR_PRODUCT] = "Pico",
|
||||
[USBD_STR_SERIAL] = "000000000000", // TODO
|
||||
[USBD_STR_CDC] = "Board CDC",
|
||||
};
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
|
||||
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void)index;
|
||||
return usbd_desc_cfg;
|
||||
}
|
||||
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
#define DESC_STR_MAX (20)
|
||||
static uint16_t desc_str[DESC_STR_MAX];
|
||||
|
||||
uint8_t len;
|
||||
if (index == 0) {
|
||||
desc_str[1] = 0x0409; // supported language is English
|
||||
len = 1;
|
||||
} else {
|
||||
if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) {
|
||||
return NULL;
|
||||
}
|
||||
const char *str = usbd_desc_str[index];
|
||||
for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) {
|
||||
desc_str[1 + len] = str[len];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2);
|
||||
|
||||
return desc_str;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user