From 50f3ab7102b6fa3b79172885ff01d6fde2fbbc92 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Sat, 25 Jan 2025 19:12:34 +0800 Subject: [PATCH 1/2] feat(cherryusb): add cherryusb library Signed-off-by: sakumisu <1203593632@qq.com> --- .gitmodules | 3 ++ lib/cherryusb | 1 + src/cmake/rp2_common.cmake | 1 + src/rp2_common/cherryusb/CMakeLists.txt | 46 +++++++++++++++++++ .../pico_crt0/rp2040/memmap_blocked_ram.ld | 6 +++ .../pico_crt0/rp2040/memmap_copy_to_ram.ld | 6 +++ .../pico_crt0/rp2040/memmap_default.ld | 6 +++ .../pico_crt0/rp2040/memmap_no_flash.ld | 6 +++ .../pico_crt0/rp2350/memmap_copy_to_ram.ld | 6 +++ .../pico_crt0/rp2350/memmap_default.ld | 6 +++ .../pico_crt0/rp2350/memmap_no_flash.ld | 6 +++ 11 files changed, 93 insertions(+) create mode 160000 lib/cherryusb create mode 100644 src/rp2_common/cherryusb/CMakeLists.txt diff --git a/.gitmodules b/.gitmodules index 7c382da67..d65780c18 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/btstack"] path = lib/btstack url = https://github.com/bluekitchen/btstack.git +[submodule "lib/cherryusb"] + path = lib/cherryusb + url = https://github.com/cherry-embedded/CherryUSB.git diff --git a/lib/cherryusb b/lib/cherryusb new file mode 160000 index 000000000..40122200d --- /dev/null +++ b/lib/cherryusb @@ -0,0 +1 @@ +Subproject commit 40122200d153e0eb9fe7150839560b6357659c55 diff --git a/src/cmake/rp2_common.cmake b/src/cmake/rp2_common.cmake index 726bab933..82c1772b4 100644 --- a/src/cmake/rp2_common.cmake +++ b/src/cmake/rp2_common.cmake @@ -119,6 +119,7 @@ if (NOT PICO_BARE_METAL) pico_add_subdirectory(rp2_common/cmsis) endif() pico_add_subdirectory(rp2_common/tinyusb) + pico_add_subdirectory(rp2_common/cherryusb) pico_add_subdirectory(rp2_common/pico_stdio_usb) pico_add_subdirectory(rp2_common/pico_i2c_slave) diff --git a/src/rp2_common/cherryusb/CMakeLists.txt b/src/rp2_common/cherryusb/CMakeLists.txt new file mode 100644 index 000000000..9f5aa5ec1 --- /dev/null +++ b/src/rp2_common/cherryusb/CMakeLists.txt @@ -0,0 +1,46 @@ +if (DEFINED ENV{PICO_CHERRYUSB_PATH} AND (NOT PICO_CHERRYUSB_PATH)) + set(PICO_CHERRYUSB_PATH $ENV{PICO_CHERRYUSB_PATH}) + message("Using PICO_CHERRYUSB_PATH from environment ('${PICO_CHERRYUSB_PATH}')") +endif () + +set(CHERRYUSB_TEST_PATH "port/rp2040") +if (NOT PICO_CHERRYUSB_PATH) + set(PICO_CHERRYUSB_PATH ${PROJECT_SOURCE_DIR}/lib/cherryusb) + if (NOT EXISTS ${PICO_CHERRYUSB_PATH}/${CHERRYUSB_TEST_PATH}) + message(WARNING "CherryUSB submodule has not been initialized; USB support will be unavailable +hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).") + endif() +elseif (NOT EXISTS ${PICO_CHERRYUSB_PATH}/${CHERRYUSB_TEST_PATH}) + message(WARNING "PICO_CHERRYUSB_PATH specified but content not present.") +endif() + +if (EXISTS ${PICO_CHERRYUSB_PATH}/${CHERRYUSB_TEST_PATH}) + message("CherryUSB available at ${PICO_CHERRYUSB_PATH}; enabling build support for USB.") + pico_register_common_scope_var(PICO_CHERRYUSB_PATH) + + set(CONFIG_CHERRYUSB_DEVICE 1) + set(CONFIG_CHERRYUSB_DEVICE_CDC_ACM 1) + set(CONFIG_CHERRYUSB_DEVICE_HID 1) + set(CONFIG_CHERRYUSB_DEVICE_MSC 1) + set(CONFIG_CHERRYUSB_DEVICE_AUDIO 1) + set(CONFIG_CHERRYUSB_DEVICE_VIDEO 1) + set(CONFIG_CHERRYUSB_DEVICE_DCD "rp2040") + include(${PICO_CHERRYUSB_PATH}/cherryusb.cmake) + pico_add_library(cherryusb_device NOFLAG) + target_include_directories(cherryusb_device_headers SYSTEM INTERFACE ${cherryusb_incs}) + target_sources(cherryusb_device INTERFACE ${cherryusb_srcs}) + + set(CONFIG_CHERRYUSB_HOST 1) + set(CONFIG_CHERRYUSB_HOST_CDC_ACM 1) + set(CONFIG_CHERRYUSB_HOST_HID 1) + set(CONFIG_CHERRYUSB_HOST_MSC 1) + set(CONFIG_CHERRYUSB_OSAL "freertos") + set(CONFIG_CHERRYUSB_HOST_HCD "rp2040") + + include(${PICO_CHERRYUSB_PATH}/cherryusb.cmake) + pico_add_library(cherryusb_host NOFLAG) + target_include_directories(cherryusb_host_headers SYSTEM INTERFACE ${cherryusb_incs}) + target_sources(cherryusb_host INTERFACE ${cherryusb_srcs}) + + pico_promote_common_scope_vars() +endif() diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld b/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld index 6f5000566..e9813734a 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld @@ -106,6 +106,12 @@ SECTIONS *(.fini_array) PROVIDE_HIDDEN (__fini_array_end = .); + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) . = ALIGN(4); } > FLASH diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld b/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld index 842ebfd3c..66a3d78d8 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld @@ -125,6 +125,12 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) . = ALIGN(4); __ram_text_end__ = .; diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_default.ld b/src/rp2_common/pico_crt0/rp2040/memmap_default.ld index 51254012d..427270710 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_default.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_default.ld @@ -106,6 +106,12 @@ SECTIONS *(.fini_array) PROVIDE_HIDDEN (__fini_array_end = .); + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) . = ALIGN(4); } > FLASH diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld b/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld index dbf006a8c..ae13487c8 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld @@ -68,6 +68,12 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) } > RAM diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld b/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld index 89d63a9f0..200c5ae89 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld @@ -141,6 +141,12 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) . = ALIGN(4); __ram_text_end__ = .; diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_default.ld b/src/rp2_common/pico_crt0/rp2350/memmap_default.ld index bce316d14..a22dc50f0 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_default.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_default.ld @@ -96,6 +96,12 @@ SECTIONS *(.fini_array) PROVIDE_HIDDEN (__fini_array_end = .); + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) . = ALIGN(4); } > FLASH diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld b/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld index 98088cdfc..075478160 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld @@ -69,6 +69,12 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + /* section information for usbh class */ + . = ALIGN(4); + __usbh_class_info_start__ = .; + KEEP(*(.usbh_class_info)) + __usbh_class_info_end__ = .; + *(.eh_frame*) } > RAM From 504bdc0f4e1cee072dd55bea0bfe5842c8d400d6 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Tue, 11 Feb 2025 20:42:59 +0800 Subject: [PATCH 2/2] feat(cherryusb): support pico_stdio_usb Signed-off-by: sakumisu <1203593632@qq.com> --- lib/cherryusb | 2 +- src/cmake/rp2_common.cmake | 1 + .../pico_stdio_cherryusb/CMakeLists.txt | 25 + .../include/pico/stdio_usb.h | 168 +++++ .../include/pico/stdio_usb/reset_interface.h | 13 + .../pico_stdio_cherryusb/include/usb_config.h | 296 ++++++++ .../pico_stdio_cherryusb/stdio_usb.c | 647 ++++++++++++++++++ 7 files changed, 1151 insertions(+), 1 deletion(-) create mode 100644 src/rp2_common/pico_stdio_cherryusb/CMakeLists.txt create mode 100644 src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb.h create mode 100644 src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb/reset_interface.h create mode 100644 src/rp2_common/pico_stdio_cherryusb/include/usb_config.h create mode 100644 src/rp2_common/pico_stdio_cherryusb/stdio_usb.c diff --git a/lib/cherryusb b/lib/cherryusb index 40122200d..43e6b5b1b 160000 --- a/lib/cherryusb +++ b/lib/cherryusb @@ -1 +1 @@ -Subproject commit 40122200d153e0eb9fe7150839560b6357659c55 +Subproject commit 43e6b5b1b1f6af62497e740db8faee4499724627 diff --git a/src/cmake/rp2_common.cmake b/src/cmake/rp2_common.cmake index 82c1772b4..9e4777066 100644 --- a/src/cmake/rp2_common.cmake +++ b/src/cmake/rp2_common.cmake @@ -120,6 +120,7 @@ if (NOT PICO_BARE_METAL) endif() pico_add_subdirectory(rp2_common/tinyusb) pico_add_subdirectory(rp2_common/cherryusb) + pico_add_subdirectory(rp2_common/pico_stdio_cherryusb) pico_add_subdirectory(rp2_common/pico_stdio_usb) pico_add_subdirectory(rp2_common/pico_i2c_slave) diff --git a/src/rp2_common/pico_stdio_cherryusb/CMakeLists.txt b/src/rp2_common/pico_stdio_cherryusb/CMakeLists.txt new file mode 100644 index 000000000..8f0428b7c --- /dev/null +++ b/src/rp2_common/pico_stdio_cherryusb/CMakeLists.txt @@ -0,0 +1,25 @@ +if (TARGET cherryusb_device) + pico_add_library(pico_stdio_usb) + + target_include_directories(pico_stdio_usb_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) + + target_sources(pico_stdio_usb INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/stdio_usb.c + ) + + pico_mirrored_target_link_libraries(pico_stdio_usb INTERFACE + pico_stdio + pico_time + pico_unique_id + pico_usb_reset_interface + ) + target_link_libraries(pico_stdio_usb INTERFACE + cherryusb_device + ) + # PICO_CMAKE_CONFIG: PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, Maximum number of milliseconds to wait during initialization for a CDC connection from the host (negative means indefinite) during initialization, type=int, default=0, group=pico_stdio_usb + if (PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS) + target_compile_definitions(pico_stdio_usb INTERFACE + PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=${PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS} + ) + endif() +endif() diff --git a/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb.h b/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb.h new file mode 100644 index 000000000..b572998d8 --- /dev/null +++ b/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb.h @@ -0,0 +1,168 @@ +/* + * 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 ENABLED)` in the CMake (which + * achieves the same thing) will add USB CDC to the drivers used for standard input/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. + * + * This library also includes (by default) functionality to enable the RP-series microcontroller to be reset over the USB interface. + */ + +// 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 frequent? +// 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, Explicit User IRQ number to claim for tud_task() background execution instead of letting the implementation pick a free one dynamically (deprecated), advanced=true, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_LOW_PRIORITY_IRQ +// this variable is no longer set by default (one is claimed dynamically), but will be respected if specified +#endif + +// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1 if application is not using TinyUSB directly, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE +#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE) +#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 1 +#endif +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE, Baud rate that if selected causes a reset into BOOTSEL mode (if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE is set), default=1200, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE +#define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, Maximum number of milliseconds to wait during initialization for a CDC connection from the host (negative means indefinite) during initialization, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS +#define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 0 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS, Number of extra milliseconds to wait when using PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS after a host CDC connection is detected (some host terminals seem to sometimes lose transmissions sent right after connection), default=50, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS +#define PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS 50 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_DEINIT_DELAY_MS, Number of milliseconds to wait before deinitializing stdio_usb, default=110, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_DEINIT_DELAY_MS +#define PICO_STDIO_USB_DEINIT_DELAY_MS 110 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=47 on RP2350B, 29 otherwise, group=pico_stdio_usb + +// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW, Whether pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE) is active low, type=bool, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED_ACTIVE_LOW +#define PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED_ACTIVE_LOW 0 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED +#define PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED 0 +#endif + +// Any modes disabled here can't be re-enabled by picotool via VENDOR_INTERFACE. +// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=3, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK +#define PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK 0u +#endif + +// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE, Enable/disable resetting into BOOTSEL mode via an additional VENDOR USB interface - enables picotool based reset, type=bool, default=1 if application is not using TinyUSB directly, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#if !defined(LIB_TINYUSB_HOST) && !defined(LIB_TINYUSB_DEVICE) +#define PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE 1 +#endif +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL, If vendor reset interface is included allow rebooting to BOOTSEL mode, type=bool, default=1, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL +#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL 1 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT, If vendor reset interface is included allow rebooting with regular flash boot, type=bool, default=1, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT +#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT 1 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR, If vendor reset interface is included add support for Microsoft OS 2.0 Descriptor, type=bool, default=1, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR +#define PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR 1 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS, Delay in ms before rebooting via regular flash boot, default=100, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS +#define PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS 100 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_CONNECTION_WITHOUT_DTR, Disable use of DTR for connection checking meaning connection is assumed to be valid, type=bool, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_CONNECTION_WITHOUT_DTR +#define PICO_STDIO_USB_CONNECTION_WITHOUT_DTR 0 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_DEVICE_SELF_POWERED, Set USB device as self powered device, type=bool, default=0, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_DEVICE_SELF_POWERED +#define PICO_STDIO_USB_DEVICE_SELF_POWERED 0 +#endif + +// PICO_CONFIG: PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK, Enable USB STDIO support for stdio_set_chars_available_callback. Can be disabled to make use of USB CDC RX callback elsewhere, type=bool, default=1, group=pico_stdio_usb +#ifndef PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK +#define PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK 1 +#endif + +#ifdef __cplusplus +extern "C" { +#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_usb + * + * \ref PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS can be set to cause this method to wait for a CDC connection + * from the host before returning, which is useful if you don't want any initial stdout output to be discarded + * before the connection is established. + * + * \return true if the USB CDC was initialized, false if an error occurred + */ +bool stdio_usb_init(void); + +/*! \brief Explicitly deinitialize USB stdio and remove it from the current set of stdin drivers + * \ingroup pico_stdio_usb + * + * \return true if the USB CDC was deinitialized, false if an error occurred + */ +bool stdio_usb_deinit(void); + +/*! \brief Check if there is an active stdio CDC connection to a host + * \ingroup pico_stdio_usb + * + * \return true if stdio is connected over CDC + */ +bool stdio_usb_connected(void); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb/reset_interface.h b/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb/reset_interface.h new file mode 100644 index 000000000..e0cca103c --- /dev/null +++ b/src/rp2_common/pico_stdio_cherryusb/include/pico/stdio_usb/reset_interface.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_STDIO_USB_RESET_INTERFACE_H +#define _PICO_STDIO_USB_RESET_INTERFACE_H + +// definitions have been moved here +#include "pico/usb_reset_interface.h" + +#endif \ No newline at end of file diff --git a/src/rp2_common/pico_stdio_cherryusb/include/usb_config.h b/src/rp2_common/pico_stdio_cherryusb/include/usb_config.h new file mode 100644 index 000000000..517751b75 --- /dev/null +++ b/src/rp2_common/pico_stdio_cherryusb/include/usb_config.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef CHERRYUSB_CONFIG_EXAMPLES_COMMON_H +#define CHERRYUSB_CONFIG_EXAMPLES_COMMON_H + +/* ================ USB common Configuration ================ */ + +#define CONFIG_USB_PRINTF(...) + +#ifndef CONFIG_USB_DBG_LEVEL +#define CONFIG_USB_DBG_LEVEL USB_DBG_INFO +#endif + +/* Enable print with color */ +#define CONFIG_USB_PRINTF_COLOR_ENABLE + +/* data align size when use dma or use dcache */ +#ifndef CONFIG_USB_ALIGN_SIZE +#define CONFIG_USB_ALIGN_SIZE 4 +#endif + +//#define CONFIG_USB_DCACHE_ENABLE + +/* attribute data into no cache ram */ +#define USB_NOCACHE_RAM_SECTION + +/* ================= USB Device Stack Configuration ================ */ + +/* Ep0 in and out transfer buffer */ +#ifndef CONFIG_USBDEV_REQUEST_BUFFER_LEN +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 512 +#endif + +/* Setup packet log for debug */ +// #define CONFIG_USBDEV_SETUP_LOG_PRINT + +/* Send ep0 in data from user buffer instead of copying into ep0 reqdata + * Please note that user buffer must be aligned with CONFIG_USB_ALIGN_SIZE +*/ +// #define CONFIG_USBDEV_EP0_INDATA_NO_COPY + +/* Check if the input descriptor is correct */ +// #define CONFIG_USBDEV_DESC_CHECK + +/* Enable test mode */ +// #define CONFIG_USBDEV_TEST_MODE + +#ifndef CONFIG_USBDEV_MSC_MAX_LUN +#define CONFIG_USBDEV_MSC_MAX_LUN 1 +#endif + +#ifndef CONFIG_USBDEV_MSC_MAX_BUFSIZE +#define CONFIG_USBDEV_MSC_MAX_BUFSIZE 512 +#endif + +#ifndef CONFIG_USBDEV_MSC_MANUFACTURER_STRING +#define CONFIG_USBDEV_MSC_MANUFACTURER_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_PRODUCT_STRING +#define CONFIG_USBDEV_MSC_PRODUCT_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_VERSION_STRING +#define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" +#endif + +/* move msc read & write from isr to while(1), you should call usbd_msc_polling in while(1) */ +// #define CONFIG_USBDEV_MSC_POLLING + +/* move msc read & write from isr to thread */ +// #define CONFIG_USBDEV_MSC_THREAD + +#ifndef CONFIG_USBDEV_MSC_PRIO +#define CONFIG_USBDEV_MSC_PRIO 4 +#endif + +#ifndef CONFIG_USBDEV_MSC_STACKSIZE +#define CONFIG_USBDEV_MSC_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE +#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156 +#endif + +/* rndis transfer buffer size, must be a multiple of (1536 + 44)*/ +#ifndef CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE +#define CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE 1580 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_ID +#define CONFIG_USBDEV_RNDIS_VENDOR_ID 0x0000ffff +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_DESC +#define CONFIG_USBDEV_RNDIS_VENDOR_DESC "CherryUSB" +#endif + +#define CONFIG_USBDEV_RNDIS_USING_LWIP + +/* ================ USB HOST Stack Configuration ================== */ + +#define CONFIG_USBHOST_MAX_RHPORTS 1 +#define CONFIG_USBHOST_MAX_EXTHUBS 1 +#define CONFIG_USBHOST_MAX_EHPORTS 4 +#define CONFIG_USBHOST_MAX_INTERFACES 8 +#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 8 +#define CONFIG_USBHOST_MAX_ENDPOINTS 4 + +#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4 +#define CONFIG_USBHOST_MAX_HID_CLASS 4 +#define CONFIG_USBHOST_MAX_MSC_CLASS 2 +#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 +#define CONFIG_USBHOST_MAX_VIDEO_CLASS 1 + +#define CONFIG_USBHOST_DEV_NAMELEN 16 + +#ifndef CONFIG_USBHOST_PSC_PRIO +#define CONFIG_USBHOST_PSC_PRIO 0 +#endif +#ifndef CONFIG_USBHOST_PSC_STACKSIZE +#define CONFIG_USBHOST_PSC_STACKSIZE 2048 +#endif + +//#define CONFIG_USBHOST_GET_STRING_DESC + +// #define CONFIG_USBHOST_MSOS_ENABLE +#ifndef CONFIG_USBHOST_MSOS_VENDOR_CODE +#define CONFIG_USBHOST_MSOS_VENDOR_CODE 0x00 +#endif + +/* Ep0 max transfer buffer */ +#ifndef CONFIG_USBHOST_REQUEST_BUFFER_LEN +#define CONFIG_USBHOST_REQUEST_BUFFER_LEN 512 +#endif + +#ifndef CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT +#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500 +#endif + +#ifndef CONFIG_USBHOST_MSC_TIMEOUT +#define CONFIG_USBHOST_MSC_TIMEOUT 5000 +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE (2048) +#endif + +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE (2048) +#endif + +#define CONFIG_USBHOST_BLUETOOTH_HCI_H4 +// #define CONFIG_USBHOST_BLUETOOTH_HCI_LOG + +#ifndef CONFIG_USBHOST_BLUETOOTH_TX_SIZE +#define CONFIG_USBHOST_BLUETOOTH_TX_SIZE 2048 +#endif +#ifndef CONFIG_USBHOST_BLUETOOTH_RX_SIZE +#define CONFIG_USBHOST_BLUETOOTH_RX_SIZE 2048 +#endif + +/* ================ USB Device Port Configuration ================*/ + +#ifndef CONFIG_USBDEV_MAX_BUS +#define CONFIG_USBDEV_MAX_BUS 1 // for now, bus num must be 1 except hpm ip +#endif + +#ifndef CONFIG_USBDEV_EP_NUM +#define CONFIG_USBDEV_EP_NUM 16 +#endif + +/* When your chip hardware supports high-speed and wants to initialize it in high-speed mode, the relevant IP will configure the internal or external high-speed PHY according to CONFIG_USB_HS. */ +// #define CONFIG_USB_HS + +/* ---------------- FSDEV Configuration ---------------- */ +//#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 // maybe 1 or 2, many chips may have a difference + +/* ---------------- DWC2 Configuration ---------------- */ +/* (5 * number of control endpoints + 8) + ((largest USB packet used / 4) + 1 for + * status information) + (2 * number of OUT endpoints) + 1 for Global NAK + */ +// #define CONFIG_USB_DWC2_RXALL_FIFO_SIZE (1024 / 4) +/* IN Endpoints Max packet Size / 4 */ +// #define CONFIG_USB_DWC2_TX0_FIFO_SIZE (64 / 4) +// #define CONFIG_USB_DWC2_TX1_FIFO_SIZE (1024 / 4) +// #define CONFIG_USB_DWC2_TX2_FIFO_SIZE (64 / 4) +// #define CONFIG_USB_DWC2_TX3_FIFO_SIZE (64 / 4) +// #define CONFIG_USB_DWC2_TX4_FIFO_SIZE (0 / 4) +// #define CONFIG_USB_DWC2_TX5_FIFO_SIZE (0 / 4) +// #define CONFIG_USB_DWC2_TX6_FIFO_SIZE (0 / 4) +// #define CONFIG_USB_DWC2_TX7_FIFO_SIZE (0 / 4) +// #define CONFIG_USB_DWC2_TX8_FIFO_SIZE (0 / 4) + +// #define CONFIG_USB_DWC2_DMA_ENABLE + +/* ---------------- MUSB Configuration ---------------- */ +// #define CONFIG_USB_MUSB_SUNXI + +/* ================ USB Host Port Configuration ==================*/ +#ifndef CONFIG_USBHOST_MAX_BUS +#define CONFIG_USBHOST_MAX_BUS 1 +#endif + +#ifndef CONFIG_USBHOST_PIPE_NUM +#define CONFIG_USBHOST_PIPE_NUM 15 +#endif + +/* ---------------- EHCI Configuration ---------------- */ + +#define CONFIG_USB_EHCI_HCCR_OFFSET (0x0) +#define CONFIG_USB_EHCI_FRAME_LIST_SIZE 1024 +#define CONFIG_USB_EHCI_QH_NUM CONFIG_USBHOST_PIPE_NUM +#define CONFIG_USB_EHCI_QTD_NUM 3 +#define CONFIG_USB_EHCI_ITD_NUM 20 +// #define CONFIG_USB_EHCI_HCOR_RESERVED_DISABLE +// #define CONFIG_USB_EHCI_CONFIGFLAG +// #define CONFIG_USB_EHCI_ISO +// #define CONFIG_USB_EHCI_WITH_OHCI + +/* ---------------- OHCI Configuration ---------------- */ +#define CONFIG_USB_OHCI_HCOR_OFFSET (0x0) + +/* ---------------- XHCI Configuration ---------------- */ +#define CONFIG_USB_XHCI_HCCR_OFFSET (0x0) + +/* ---------------- DWC2 Configuration ---------------- */ +/* largest non-periodic USB packet used / 4 */ +// #define CONFIG_USB_DWC2_NPTX_FIFO_SIZE (512 / 4) +/* largest periodic USB packet used / 4 */ +// #define CONFIG_USB_DWC2_PTX_FIFO_SIZE (1024 / 4) +/* + * (largest USB packet used / 4) + 1 for status information + 1 transfer complete + + * 1 location each for Bulk/Control endpoint for handling NAK/NYET scenario + */ +// #define CONFIG_USB_DWC2_RX_FIFO_SIZE ((1012 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE)) + +/* ---------------- MUSB Configuration ---------------- */ +// #define CONFIG_USB_MUSB_SUNXI + +/* ================ USB Dcache Configuration ==================*/ + +#ifdef CONFIG_USB_DCACHE_ENABLE +/* style 1*/ +// void usb_dcache_clean(uintptr_t addr, uint32_t size); +// void usb_dcache_invalidate(uintptr_t addr, uint32_t size); +// void usb_dcache_flush(uintptr_t addr, uint32_t size); + +/* style 2*/ +// #define usb_dcache_clean(addr, size) +// #define usb_dcache_invalidate(addr, size) +// #define usb_dcache_flush(addr, size) +#endif + +#define CONFIG_USBDEV_ADVANCE_DESC +#endif diff --git a/src/rp2_common/pico_stdio_cherryusb/stdio_usb.c b/src/rp2_common/pico_stdio_cherryusb/stdio_usb.c new file mode 100644 index 000000000..c820a6620 --- /dev/null +++ b/src/rp2_common/pico_stdio_cherryusb/stdio_usb.c @@ -0,0 +1,647 @@ +#ifndef LIB_CHERRYUSB_HOST + +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && !(PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL || PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT) +#warning PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE has been selected but neither PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL nor PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT have been selected. +#endif + +#include "usbd_core.h" +#include "usbd_cdc_acm.h" +#include "pico/stdio_usb.h" + +#include "pico/binary_info.h" +#include "pico/time.h" +#include "pico/stdio/driver.h" +#include "pico/mutex.h" +#include "hardware/irq.h" + +#include "pico/unique_id.h" + +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#include "pico/stdio_usb/reset_interface.h" +#include "hardware/watchdog.h" +#include "pico/bootrom.h" +#endif + +/*!< endpoint address */ +#define CDC_IN_EP 0x81 +#define CDC_OUT_EP 0x02 +#define CDC_INT_EP 0x83 + +#ifndef USBD_VID +#define USBD_VID (0x2E8A) // Raspberry Pi +#endif + +#ifndef USBD_PID +#if PICO_RP2040 +#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC for RP2040 +#else +#define USBD_PID (0x0009) // Raspberry Pi Pico SDK CDC +#endif +#endif + +#ifndef USBD_MANUFACTURER +#define USBD_MANUFACTURER "Raspberry Pi" +#endif + +#ifndef USBD_PRODUCT +#define USBD_PRODUCT "Pico" +#endif + +#define TUD_RPI_RESET_DESC_LEN 9 +#if !PICO_STDIO_USB_DEVICE_SELF_POWERED +#define USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE USB_CONFIG_BUS_POWERED +#define USBD_MAX_POWER_MA (250) +#else +#define USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE USB_CONFIG_SELF_POWERED +#define USBD_MAX_POWER_MA (1) +#endif + +#define USBD_ITF_CDC (0) // needs 2 interfaces +#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +#define USBD_ITF_MAX (2) +#else +#define USBD_ITF_RPI_RESET (2) +#define USBD_ITF_MAX (3) +#endif + +#define USBD_LANGID_STRING 1033 + +#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE +/*!< config descriptor size */ +#define USB_CONFIG_SIZE (9 + CDC_ACM_DESCRIPTOR_LEN) +#else +#define USB_CONFIG_SIZE (9 + CDC_ACM_DESCRIPTOR_LEN + TUD_RPI_RESET_DESC_LEN) +#endif + +#ifdef CONFIG_USB_HS +#define CDC_MAX_MPS 512 +#else +#define CDC_MAX_MPS 64 +#endif + +#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) +#define USBD_STR_RPI_RESET (0x05) + +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR +#define USBD_WINUSB_VENDOR_CODE 0x01 + +#define USBD_WEBUSB_ENABLE 0 +#define USBD_BULK_ENABLE 1 +#define USBD_WINUSB_ENABLE 1 + +/* WinUSB Microsoft OS 2.0 descriptor sizes */ +#define WINUSB_DESCRIPTOR_SET_HEADER_SIZE 10 +#define WINUSB_FUNCTION_SUBSET_HEADER_SIZE 8 +#define WINUSB_FEATURE_COMPATIBLE_ID_SIZE 20 + +#define FUNCTION_SUBSET_LEN 160 +#define DEVICE_INTERFACE_GUIDS_FEATURE_LEN 132 + +#define USBD_WINUSB_DESC_SET_LEN (WINUSB_DESCRIPTOR_SET_HEADER_SIZE + USBD_WEBUSB_ENABLE * FUNCTION_SUBSET_LEN + USBD_BULK_ENABLE * FUNCTION_SUBSET_LEN) + +__ALIGN_BEGIN const uint8_t USBD_WinUSBDescriptorSetDescriptor[] = { + WBVAL(WINUSB_DESCRIPTOR_SET_HEADER_SIZE), /* wLength */ + WBVAL(WINUSB_SET_HEADER_DESCRIPTOR_TYPE), /* wDescriptorType */ + 0x00, 0x00, 0x03, 0x06, /* >= Win 8.1 */ /* dwWindowsVersion*/ + WBVAL(USBD_WINUSB_DESC_SET_LEN), /* wDescriptorSetTotalLength */ +#if (USBD_WEBUSB_ENABLE) + WBVAL(WINUSB_FUNCTION_SUBSET_HEADER_SIZE), // wLength + WBVAL(WINUSB_SUBSET_HEADER_FUNCTION_TYPE), // wDescriptorType + 0, // bFirstInterface USBD_WINUSB_IF_NUM + 0, // bReserved + WBVAL(FUNCTION_SUBSET_LEN), // wSubsetLength + WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_SIZE), // wLength + WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_TYPE), // wDescriptorType + 'W', 'I', 'N', 'U', 'S', 'B', 0, 0, // CompatibleId + 0, 0, 0, 0, 0, 0, 0, 0, // SubCompatibleId + WBVAL(DEVICE_INTERFACE_GUIDS_FEATURE_LEN), // wLength + WBVAL(WINUSB_FEATURE_REG_PROPERTY_TYPE), // wDescriptorType + WBVAL(WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ), // wPropertyDataType + WBVAL(42), // wPropertyNameLength + 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, + 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0, + 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0, + WBVAL(80), // wPropertyDataLength + '{', 0, + '9', 0, '2', 0, 'C', 0, 'E', 0, '6', 0, '4', 0, '6', 0, '2', 0, '-', 0, + '9', 0, 'C', 0, '7', 0, '7', 0, '-', 0, + '4', 0, '6', 0, 'F', 0, 'E', 0, '-', 0, + '9', 0, '3', 0, '3', 0, 'B', 0, '-', + 0, '3', 0, '1', 0, 'C', 0, 'B', 0, '9', 0, 'C', 0, '5', 0, 'A', 0, 'A', 0, '3', 0, 'B', 0, '9', 0, + '}', 0, 0, 0, 0, 0 +#endif +#if USBD_BULK_ENABLE + WBVAL(WINUSB_FUNCTION_SUBSET_HEADER_SIZE), /* wLength */ + WBVAL(WINUSB_SUBSET_HEADER_FUNCTION_TYPE), /* wDescriptorType */ + USBD_ITF_RPI_RESET, /* bFirstInterface USBD_ITF_RPI_RESET*/ + 0, /* bReserved */ + WBVAL(FUNCTION_SUBSET_LEN), /* wSubsetLength */ + WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_SIZE), /* wLength */ + WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_TYPE), /* wDescriptorType */ + 'W', 'I', 'N', 'U', 'S', 'B', 0, 0, /* CompatibleId*/ + 0, 0, 0, 0, 0, 0, 0, 0, /* SubCompatibleId*/ + WBVAL(DEVICE_INTERFACE_GUIDS_FEATURE_LEN), /* wLength */ + WBVAL(WINUSB_FEATURE_REG_PROPERTY_TYPE), /* wDescriptorType */ + WBVAL(WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ), /* wPropertyDataType */ + WBVAL(42), /* wPropertyNameLength */ + 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, + 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0, + 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0, + WBVAL(80), /* wPropertyDataLength */ + '{', 0, + 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, 'A', 0, 'D', 0, '-', 0, + '2', 0, '9', 0, '3', 0, 'B', 0, '-', 0, + '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, + 'A', 0, 'A', 0, '3', 0, '6', 0, '-', + 0, '1', 0, 'A', 0, 'A', 0, 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, '7', 0, '6', 0, + '}', 0, 0, 0, 0, 0 +#endif +}; + +#define USBD_NUM_DEV_CAPABILITIES (USBD_WEBUSB_ENABLE + USBD_WINUSB_ENABLE) + +#define USBD_WEBUSB_DESC_LEN 24 +#define USBD_WINUSB_DESC_LEN 28 + +#define USBD_BOS_WTOTALLENGTH (0x05 + \ + USBD_WEBUSB_DESC_LEN * USBD_WEBUSB_ENABLE + \ + USBD_WINUSB_DESC_LEN * USBD_WINUSB_ENABLE) + +__ALIGN_BEGIN const uint8_t USBD_BinaryObjectStoreDescriptor[] = { + 0x05, /* bLength */ + 0x0f, /* bDescriptorType */ + WBVAL(USBD_BOS_WTOTALLENGTH), /* wTotalLength */ + USBD_NUM_DEV_CAPABILITIES, /* bNumDeviceCaps */ +#if (USBD_WEBUSB_ENABLE) + USBD_WEBUSB_DESC_LEN, /* bLength */ + 0x10, /* bDescriptorType */ + USB_DEVICE_CAPABILITY_PLATFORM, /* bDevCapabilityType */ + 0x00, /* bReserved */ + 0x38, 0xB6, 0x08, 0x34, /* PlatformCapabilityUUID */ + 0xA9, 0x09, 0xA0, 0x47, + 0x8B, 0xFD, 0xA0, 0x76, + 0x88, 0x15, 0xB6, 0x65, + WBVAL(0x0100), /* 1.00 */ /* bcdVersion */ + USBD_WINUSB_VENDOR_CODE, /* bVendorCode */ + 0, /* iLandingPage */ +#endif +#if (USBD_WINUSB_ENABLE) + USBD_WINUSB_DESC_LEN, /* bLength */ + 0x10, /* bDescriptorType */ + USB_DEVICE_CAPABILITY_PLATFORM, /* bDevCapabilityType */ + 0x00, /* bReserved */ + 0xDF, 0x60, 0xDD, 0xD8, /* PlatformCapabilityUUID */ + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + 0x00, 0x00, 0x03, 0x06, /* >= Win 8.1 */ /* dwWindowsVersion*/ + WBVAL(USBD_WINUSB_DESC_SET_LEN), /* wDescriptorSetTotalLength */ + USBD_WINUSB_VENDOR_CODE, /* bVendorCode */ + 0, /* bAltEnumCode */ +#endif +}; + +struct usb_msosv2_descriptor msosv2_desc = { + .vendor_code = USBD_WINUSB_VENDOR_CODE, + .compat_id = USBD_WinUSBDescriptorSetDescriptor, + .compat_id_len = USBD_WINUSB_DESC_SET_LEN, +}; + +struct usb_bos_descriptor bos_desc = { + .string = USBD_BinaryObjectStoreDescriptor, + .string_len = USBD_BOS_WTOTALLENGTH +}; + +static int rp_vendor_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { + if (setup->wIndex == USBD_ITF_RPI_RESET) { +#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL + if (setup->bRequest == RESET_REQUEST_BOOTSEL) { +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED + int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; + bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; +#else + int gpio = -1; + bool active_low = false; +#endif +#if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED + if (setup->wValue & 0x100) { + gpio = setup->wValue >> 9u; + } + active_low = setup->wValue & 0x200; +#endif + rom_reset_usb_boot_extra(gpio, (setup->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); + // does not return, otherwise we'd return true + } +#endif + +#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT + if (setup->bRequest == RESET_REQUEST_FLASH) { + watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS); + return 0; + } +#endif + } else { + return -1; + } +} +#endif + +#ifdef CONFIG_USBDEV_ADVANCE_DESC +static const uint8_t device_descriptor[] = { +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR + USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01), +#else + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01), +#endif +}; + +static const uint8_t config_descriptor[] = { + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, USBD_ITF_MAX, 0x01, USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE, USBD_MAX_POWER_MA), + CDC_ACM_DESCRIPTOR_INIT(USBD_ITF_CDC, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, USBD_STR_CDC), +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE + USB_INTERFACE_DESCRIPTOR_INIT(USBD_ITF_RPI_RESET, 0x00, 0x00, 0xFF, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, USBD_STR_RPI_RESET), +#endif +}; + +static const uint8_t device_quality_descriptor[] = { + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, +}; + +static char usbd_serial_str[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; + +static const char *string_descriptors[] = { + [USBD_STR_0] = (const char[]){ 0x09, 0x04 }, + [USBD_STR_MANUF] = USBD_MANUFACTURER, + [USBD_STR_PRODUCT] = USBD_PRODUCT, + [USBD_STR_SERIAL] = usbd_serial_str, + [USBD_STR_CDC] = "Board CDC", +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE + [USBD_STR_RPI_RESET] = "Reset", +#endif +}; + +static const uint8_t *device_descriptor_callback(uint8_t speed) { + return device_descriptor; +} + +static const uint8_t *config_descriptor_callback(uint8_t speed) { + return config_descriptor; +} + +static const uint8_t *device_quality_descriptor_callback(uint8_t speed) { + return device_quality_descriptor; +} + +static const char *string_descriptor_callback(uint8_t speed, uint8_t index) { + if (index > 5) { + return NULL; + } + // Assign the SN using the unique flash id + if (!usbd_serial_str[0]) { + pico_get_unique_board_id_string(usbd_serial_str, sizeof(usbd_serial_str)); + } + + return string_descriptors[index]; +} + +const struct usb_descriptor cdc_descriptor = { + .device_descriptor_callback = device_descriptor_callback, + .config_descriptor_callback = config_descriptor_callback, + .device_quality_descriptor_callback = device_quality_descriptor_callback, + .string_descriptor_callback = string_descriptor_callback, +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR + .msosv2_descriptor = &msosv2_desc, + .bos_descriptor = &bos_desc +#endif +}; +#else +/*!< global descriptor */ +static const uint8_t cdc_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01), + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + CDC_ACM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, 0x02), + /////////////////////////////////////// + /// string0 descriptor + /////////////////////////////////////// + USB_LANGID_INIT(USBD_LANGID_STRING), + /////////////////////////////////////// + /// string1 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string2 descriptor + /////////////////////////////////////// + 0x26, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + ' ', 0x00, /* wcChar9 */ + 'C', 0x00, /* wcChar10 */ + 'D', 0x00, /* wcChar11 */ + 'C', 0x00, /* wcChar12 */ + ' ', 0x00, /* wcChar13 */ + 'D', 0x00, /* wcChar14 */ + 'E', 0x00, /* wcChar15 */ + 'M', 0x00, /* wcChar16 */ + 'O', 0x00, /* wcChar17 */ + /////////////////////////////////////// + /// string3 descriptor + /////////////////////////////////////// + 0x16, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + '2', 0x00, /* wcChar0 */ + '0', 0x00, /* wcChar1 */ + '2', 0x00, /* wcChar2 */ + '2', 0x00, /* wcChar3 */ + '1', 0x00, /* wcChar4 */ + '2', 0x00, /* wcChar5 */ + '3', 0x00, /* wcChar6 */ + '4', 0x00, /* wcChar7 */ + '5', 0x00, /* wcChar8 */ + '6', 0x00, /* wcChar9 */ +#ifdef CONFIG_USB_HS + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, +#endif + 0x00 +}; +#endif + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usb_read_buffer[CDC_MAX_MPS]; + +volatile bool g_usb_tx_busy_flag = false; +volatile uint32_t g_usb_rx_count = 0; +volatile uint32_t g_usb_rx_offset = 0; + +static void usbd_event_handler(uint8_t busid, uint8_t event) { + switch (event) { + case USBD_EVENT_RESET: + g_usb_tx_busy_flag = false; + g_usb_rx_offset = 0; + g_usb_rx_count = 0; + break; + case USBD_EVENT_CONNECTED: + break; + case USBD_EVENT_DISCONNECTED: + break; + case USBD_EVENT_RESUME: + break; + case USBD_EVENT_SUSPEND: + break; + case USBD_EVENT_CONFIGURED: + g_usb_tx_busy_flag = false; + /* setup first out ep read transfer */ + usbd_ep_start_read(busid, CDC_OUT_EP, g_usb_read_buffer, CDC_MAX_MPS); + break; + case USBD_EVENT_SET_REMOTE_WAKEUP: + break; + case USBD_EVENT_CLR_REMOTE_WAKEUP: + break; + + default: + break; + } +} + +void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { + g_usb_rx_count = nbytes; + g_usb_rx_offset = 0; +} + +void usbd_cdc_acm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { + if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) { + /* send zlp */ + usbd_ep_start_write(busid, CDC_IN_EP, NULL, 0); + } else { + g_usb_tx_busy_flag = false; + } +} + +/*!< endpoint call back */ +struct usbd_endpoint cdc_out_ep = { + .ep_addr = CDC_OUT_EP, + .ep_cb = usbd_cdc_acm_bulk_out +}; + +struct usbd_endpoint cdc_in_ep = { + .ep_addr = CDC_IN_EP, + .ep_cb = usbd_cdc_acm_bulk_in +}; + +static struct usbd_interface intf0; +static struct usbd_interface intf1; +static struct usbd_interface intf2; + +#if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE +// Support for default BOOTSEL reset by changing baud rate +void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding) { + (void)busid; + (void)intf; + if (line_coding->dwDTERate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) { +#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED + int gpio = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED; + bool active_low = PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED_ACTIVE_LOW; +#else + int gpio = -1; + bool active_low = false; +#endif + rom_reset_usb_boot_extra(gpio, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, active_low); + } +} +#endif + +static void cdc_acm_init(uint8_t busid, uintptr_t reg_base) { +#ifdef CONFIG_USBDEV_ADVANCE_DESC + usbd_desc_register(busid, &cdc_descriptor); +#else + usbd_desc_register(busid, cdc_descriptor); +#endif +#ifndef CONFIG_USBDEV_ADVANCE_DESC +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR + usbd_bos_desc_register(busid, &bos_desc); + usbd_msosv2_desc_register(busid, &msosv2_desc); +#endif +#endif + usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &intf0)); + usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &intf1)); +#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR + intf2.vendor_handler = rp_vendor_request_handler; + usbd_add_interface(busid, &intf2); +#endif + usbd_add_endpoint(busid, &cdc_out_ep); + usbd_add_endpoint(busid, &cdc_in_ep); + usbd_initialize(busid, reg_base, usbd_event_handler); +} + +#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK +static void (*chars_available_callback)(void *); +static void *chars_available_param; +#endif + +static mutex_t stdio_usb_mutex; + +static void stdio_usb_out_chars(const char *buf, int length) { + uint64_t last_avail_time; + if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) { + return; + } + + last_avail_time = time_us_64(); + if (usb_device_is_configured(0)) { + g_usb_tx_busy_flag = true; + usbd_ep_start_write(0, CDC_IN_EP, buf, length); + while (g_usb_tx_busy_flag) { + if ((time_us_64() - last_avail_time) > PICO_STDIO_USB_STDOUT_TIMEOUT_US) { + break; + } + } + } else { + } + mutex_exit(&stdio_usb_mutex); +} + +static void stdio_usb_out_flush(void) { +} + +static int stdio_usb_in_chars(char *buf, int length) { + int rc = PICO_ERROR_NO_DATA; + uint32_t len; + + if (g_usb_rx_count > 0) { + len = MIN(g_usb_rx_count - g_usb_rx_offset, length); + memcpy(buf, &g_usb_read_buffer[g_usb_rx_offset], len); + g_usb_rx_offset += len; + if (len > 0) { + return len; + } else { + g_usb_rx_count = 0; + /* setup first out ep read transfer */ + usbd_ep_start_read(0, CDC_OUT_EP, g_usb_read_buffer, CDC_MAX_MPS); + return rc; + } + } else { + return rc; + } +} + +#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK +void stdio_usb_set_chars_available_callback(void (*fn)(void *), void *param) { + chars_available_callback = fn; + chars_available_param = param; +} +#endif + +stdio_driver_t stdio_usb = { + .out_chars = stdio_usb_out_chars, + .out_flush = stdio_usb_out_flush, + .in_chars = stdio_usb_in_chars, +#if PICO_STDIO_USB_SUPPORT_CHARS_AVAILABLE_CALLBACK + .set_chars_available_callback = stdio_usb_set_chars_available_callback, +#endif +#if PICO_STDIO_ENABLE_CRLF_SUPPORT + .crlf_enabled = PICO_STDIO_USB_DEFAULT_CRLF +#endif +}; + +bool stdio_usb_init(void) { + if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) { + // included an assertion here rather than just returning false, as this is likely + // a coding bug, rather than anything else. + assert(false); + return false; + } +#if !PICO_NO_BI_STDIO_USB + bi_decl_if_func_used(bi_program_feature("USB stdin / stdout")); +#endif + + cdc_acm_init(0, 0); + + if (!mutex_is_initialized(&stdio_usb_mutex)) + mutex_init(&stdio_usb_mutex); + bool rc = true; + if (rc) { + stdio_set_driver_enabled(&stdio_usb, true); +#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS +#if PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS > 0 + absolute_time_t until = make_timeout_time_ms(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS); +#else + absolute_time_t until = at_the_end_of_time; +#endif + do { + if (usb_device_is_configured(0)) { +#if PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS != 0 + sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS); +#endif + break; + } + sleep_ms(10); + } while (!time_reached(until)); +#endif + } + return rc; +} + +bool stdio_usb_deinit(void) { + if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) { + // included an assertion here rather than just returning false, as this is likely + // a coding bug, rather than anything else. + assert(false); + return false; + } + + usbd_deinitialize(0); + + bool rc = true; + + stdio_set_driver_enabled(&stdio_usb, false); + +#if PICO_STDIO_USB_DEINIT_DELAY_MS != 0 + sleep_ms(PICO_STDIO_USB_DEINIT_DELAY_MS); +#endif + return rc; +} +#endif \ No newline at end of file