Merge pull request #31 from P33M/picodebug
Add a CMSIS compatible implementation for picoprobe
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f8890cf
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "CMSIS_5"]
+ path = CMSIS_5
+ url = https://github.com/ARM-software/CMSIS_5
+[submodule "freertos"]
+ path = freertos
+ url = https://github.com/FreeRTOS/FreeRTOS-Kernel
diff --git a/CMSIS_5 b/CMSIS_5
new file mode 160000
index 0000000..a65b7c9
--- /dev/null
+++ b/CMSIS_5
@@ -0,0 +1 @@
+Subproject commit a65b7c9a3e6502127fdb80eb288d8cbdf251a6f4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c64db04..c5135ce 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,9 @@
include(pico_sdk_import.cmake)
+set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/freertos)
+include(FreeRTOS_Kernel_import.cmake)
+
project(picoprobe)
pico_sdk_init()
@@ -13,8 +16,25 @@
src/probe.c
src/cdc_uart.c
src/get_serial.c
+ src/sw_dp_pio.c
)
+target_sources(picoprobe PRIVATE
+ CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c
+ CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c
+ CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c
+ CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c
+ #CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c
+ )
+
+target_include_directories(picoprobe PRIVATE
+ CMSIS_5/CMSIS/DAP/Firmware/Include/
+ CMSIS_5/CMSIS/Core/Include/
+ include/
+ )
+
+target_compile_options(picoprobe PRIVATE -Wall)
+
if (DEFINED ENV{PICOPROBE_LED})
message("PICOPROBE_LED is defined as " $ENV{PICOPROBE_LED})
target_compile_definitions(picoprobe PRIVATE PICOPROBE_LED=$ENV{PICOPROBE_LED})
@@ -30,6 +50,17 @@
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
)
-target_link_libraries(picoprobe PRIVATE pico_stdlib pico_unique_id tinyusb_device tinyusb_board hardware_pio)
+target_link_libraries(picoprobe PRIVATE
+ pico_multicore
+ pico_stdlib
+ pico_unique_id
+ tinyusb_device
+ tinyusb_board
+ hardware_pio
+ FreeRTOS-Kernel
+ FreeRTOS-Kernel-Heap1
+)
+
+pico_set_binary_type(picoprobe copy_to_ram)
pico_add_extra_outputs(picoprobe)
diff --git a/FreeRTOS_Kernel_import.cmake b/FreeRTOS_Kernel_import.cmake
new file mode 100755
index 0000000..1f0bf11
--- /dev/null
+++ b/FreeRTOS_Kernel_import.cmake
@@ -0,0 +1,61 @@
+# This is a copy of <FREERTOS_KERNEL_PATH>/portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake
+
+# This can be dropped into an external project to help locate the FreeRTOS kernel
+# It should be include()ed prior to project(). Alternatively this file may
+# or the CMakeLists.txt in this directory may be included or added via add_subdirectory
+# respectively.
+
+if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH))
+ set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH})
+ message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')")
+endif ()
+
+set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC/RP2040")
+# undo the above
+set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..")
+
+if (NOT FREERTOS_KERNEL_PATH)
+ # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly)
+ get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH)
+ get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH)
+ if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
+ get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
+ endif()
+ if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH)
+ get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH)
+ message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake")
+ elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel")
+ set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel)
+ message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}")
+ endif()
+endif ()
+
+if (NOT FREERTOS_KERNEL_PATH)
+ foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source)
+ # check if FreeRTOS-Kernel exists under directory that included us
+ set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
+ get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH)
+ if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt)
+ get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH)
+ message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project")
+ break()
+ endif()
+ endforeach()
+endif()
+
+if (NOT FREERTOS_KERNEL_PATH)
+ message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.")
+endif()
+
+set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel")
+
+get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${FREERTOS_KERNEL_PATH})
+ message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found")
+endif()
+if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt)
+ message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain an RP2040 port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}")
+endif()
+set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE)
+
+add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL)
\ No newline at end of file
diff --git a/freertos b/freertos
new file mode 160000
index 0000000..2dfdfc4
--- /dev/null
+++ b/freertos
@@ -0,0 +1 @@
+Subproject commit 2dfdfc4ba4d8bb487c8ea6b5428d7d742ce162b8
diff --git a/include/DAP_config.h b/include/DAP_config.h
new file mode 100755
index 0000000..e05e27b
--- /dev/null
+++ b/include/DAP_config.h
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2013-2021 ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the License); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----------------------------------------------------------------------
+ *
+ * $Date: 16. June 2021
+ * $Revision: V2.1.0
+ *
+ * Project: CMSIS-DAP Configuration
+ * Title: DAP_config.h CMSIS-DAP Configuration File (Template)
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef __DAP_CONFIG_H__
+#define __DAP_CONFIG_H__
+
+
+//**************************************************************************************************
+/**
+\defgroup DAP_Config_Debug_gr CMSIS-DAP Debug Unit Information
+\ingroup DAP_ConfigIO_gr
+@{
+Provides definitions about the hardware and configuration of the Debug Unit.
+
+This information includes:
+ - Definition of Cortex-M processor parameters used in CMSIS-DAP Debug Unit.
+ - Debug Unit Identification strings (Vendor, Product, Serial Number).
+ - Debug Unit communication packet size.
+ - Debug Access Port supported modes and settings (JTAG/SWD and SWO).
+ - Optional information about a connected Target Device (for Evaluation Boards).
+*/
+#include <pico/stdlib.h>
+#include <hardware/gpio.h>
+
+#include "cmsis_compiler.h"
+#include "picoprobe_config.h"
+#include "probe.h"
+
+/// Processor Clock of the Cortex-M MCU used in the Debug Unit.
+/// This value is used to calculate the SWD/JTAG clock speed.
+/* Picoprobe actually uses kHz rather than Hz, so just lie about it here */
+#define CPU_CLOCK 125000000U ///< Specifies the CPU Clock in Hz.
+
+/// Number of processor cycles for I/O Port write operations.
+/// This value is used to calculate the SWD/JTAG clock speed that is generated with I/O
+/// Port write operations in the Debug Unit by a Cortex-M MCU. Most Cortex-M processors
+/// require 2 processor cycles for a I/O Port Write operation. If the Debug Unit uses
+/// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be
+/// required.
+#define IO_PORT_WRITE_CYCLES 1U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0.
+#define DELAY_SLOW_CYCLES 1U // We don't differentiate between fast/slow, we've got a 16-bit divisor for that
+
+/// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_SWD 1 ///< SWD Mode: 1 = available, 0 = not available.
+
+/// Indicate that JTAG communication mode is available at the Debug Port.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_JTAG 0 ///< JTAG Mode: 1 = available, 0 = not available.
+
+/// Configure maximum number of JTAG devices on the scan chain connected to the Debug Access Port.
+/// This setting impacts the RAM requirements of the Debug Unit. Valid range is 1 .. 255.
+#define DAP_JTAG_DEV_CNT 8U ///< Maximum number of JTAG devices on scan chain.
+
+/// Default communication mode on the Debug Access Port.
+/// Used for the command \ref DAP_Connect when Port Default mode is selected.
+#define DAP_DEFAULT_PORT 1U ///< Default JTAG/SWJ Port Mode: 1 = SWD, 2 = JTAG.
+
+/// Default communication speed on the Debug Access Port for SWD and JTAG mode.
+/// Used to initialize the default SWD/JTAG clock frequency.
+/// The command \ref DAP_SWJ_Clock can be used to overwrite this default setting.
+#define DAP_DEFAULT_SWJ_CLOCK 1000000U ///< Default SWD/JTAG clock frequency in Hz.
+
+/// Maximum Package Size for Command and Response data.
+/// This configuration settings is used to optimize the communication performance with the
+/// debugger and depends on the USB peripheral. Typical vales are 64 for Full-speed USB HID or WinUSB,
+/// 1024 for High-speed USB HID and 512 for High-speed USB WinUSB.
+#define DAP_PACKET_SIZE 64U ///< Specifies Packet Size in bytes.
+
+/// Maximum Package Buffers for Command and Response data.
+/// This configuration settings is used to optimize the communication performance with the
+/// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the
+/// setting can be reduced (valid range is 1 .. 255).
+#define DAP_PACKET_COUNT 2U ///< Specifies number of packets buffered.
+
+/// Indicate that UART Serial Wire Output (SWO) trace is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define SWO_UART 0 ///< SWO UART: 1 = available, 0 = not available.
+
+/// USART Driver instance number for the UART SWO.
+#define SWO_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#).
+
+/// Maximum SWO UART Baudrate.
+#define SWO_UART_MAX_BAUDRATE 10000000U ///< SWO UART Maximum Baudrate in Hz.
+
+/// Indicate that Manchester Serial Wire Output (SWO) trace is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define SWO_MANCHESTER 0 ///< SWO Manchester: 1 = available, 0 = not available.
+
+/// SWO Trace Buffer Size.
+#define SWO_BUFFER_SIZE 4096U ///< SWO Trace Buffer Size in bytes (must be 2^n).
+
+/// SWO Streaming Trace.
+#define SWO_STREAM 0 ///< SWO Streaming Trace: 1 = available, 0 = not available.
+
+/// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET.
+#define TIMESTAMP_CLOCK 1000000U ///< Timestamp clock in Hz (0 = timestamps not supported).
+
+/// Indicate that UART Communication Port is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available.
+
+/// USART Driver instance number for the UART Communication Port.
+#define DAP_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#).
+
+/// UART Receive Buffer Size.
+#define DAP_UART_RX_BUFFER_SIZE 1024U ///< Uart Receive Buffer Size in bytes (must be 2^n).
+
+/// UART Transmit Buffer Size.
+#define DAP_UART_TX_BUFFER_SIZE 1024U ///< Uart Transmit Buffer Size in bytes (must be 2^n).
+
+/// Indicate that UART Communication via USB COM Port is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_UART_USB_COM_PORT 0 ///< USB COM Port: 1 = available, 0 = not available.
+
+/// Debug Unit is connected to fixed Target Device.
+/// The Debug Unit may be part of an evaluation board and always connected to a fixed
+/// known device. In this case a Device Vendor, Device Name, Board Vendor and Board Name strings
+/// are stored and may be used by the debugger or IDE to configure device parameters.
+#define TARGET_FIXED 0 ///< Target: 1 = known, 0 = unknown;
+
+#define TARGET_DEVICE_VENDOR "Arm" ///< String indicating the Silicon Vendor
+#define TARGET_DEVICE_NAME "Cortex-M" ///< String indicating the Target Device
+#define TARGET_BOARD_VENDOR "Arm" ///< String indicating the Board Vendor
+#define TARGET_BOARD_NAME "Arm board" ///< String indicating the Board Name
+
+#if TARGET_FIXED != 0
+#include <string.h>
+static const char TargetDeviceVendor [] = TARGET_DEVICE_VENDOR;
+static const char TargetDeviceName [] = TARGET_DEVICE_NAME;
+static const char TargetBoardVendor [] = TARGET_BOARD_VENDOR;
+static const char TargetBoardName [] = TARGET_BOARD_NAME;
+#endif
+
+/** Get Vendor Name string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetVendorString (char *str) {
+ (void)str;
+ return (0U);
+}
+
+/** Get Product Name string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetProductString (char *str) {
+ (void)str;
+ return (0U);
+}
+
+/** Get Serial Number string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetSerNumString (char *str) {
+ (void)str;
+ return (0U);
+}
+
+/** Get Target Device Vendor string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetTargetDeviceVendorString (char *str) {
+#if TARGET_FIXED != 0
+ uint8_t len;
+
+ strcpy(str, TargetDeviceVendor);
+ len = (uint8_t)(strlen(TargetDeviceVendor) + 1U);
+ return (len);
+#else
+ (void)str;
+ return (0U);
+#endif
+}
+
+/** Get Target Device Name string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetTargetDeviceNameString (char *str) {
+#if TARGET_FIXED != 0
+ uint8_t len;
+
+ strcpy(str, TargetDeviceName);
+ len = (uint8_t)(strlen(TargetDeviceName) + 1U);
+ return (len);
+#else
+ (void)str;
+ return (0U);
+#endif
+}
+
+/** Get Target Board Vendor string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetTargetBoardVendorString (char *str) {
+#if TARGET_FIXED != 0
+ uint8_t len;
+
+ strcpy(str, TargetBoardVendor);
+ len = (uint8_t)(strlen(TargetBoardVendor) + 1U);
+ return (len);
+#else
+ (void)str;
+ return (0U);
+#endif
+}
+
+/** Get Target Board Name string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetTargetBoardNameString (char *str) {
+#if TARGET_FIXED != 0
+ uint8_t len;
+
+ strcpy(str, TargetBoardName);
+ len = (uint8_t)(strlen(TargetBoardName) + 1U);
+ return (len);
+#else
+ (void)str;
+ return (0U);
+#endif
+}
+
+/** Get Product Firmware Version string.
+\param str Pointer to buffer to store the string (max 60 characters).
+\return String length (including terminating NULL character) or 0 (no string).
+*/
+__STATIC_INLINE uint8_t DAP_GetProductFirmwareVersionString (char *str) {
+ (void)str;
+ return (0U);
+}
+
+///@}
+
+
+//**************************************************************************************************
+/**
+\defgroup DAP_Config_PortIO_gr CMSIS-DAP Hardware I/O Pin Access
+\ingroup DAP_ConfigIO_gr
+@{
+
+Standard I/O Pins of the CMSIS-DAP Hardware Debug Port support standard JTAG mode
+and Serial Wire Debug (SWD) mode. In SWD mode only 2 pins are required to implement the debug
+interface of a device. The following I/O Pins are provided:
+
+JTAG I/O Pin | SWD I/O Pin | CMSIS-DAP Hardware pin mode
+---------------------------- | -------------------- | ---------------------------------------------
+TCK: Test Clock | SWCLK: Clock | Output Push/Pull
+TMS: Test Mode Select | SWDIO: Data I/O | Output Push/Pull; Input (for receiving data)
+TDI: Test Data Input | | Output Push/Pull
+TDO: Test Data Output | | Input
+nTRST: Test Reset (optional) | | Output Open Drain with pull-up resistor
+nRESET: Device Reset | nRESET: Device Reset | Output Open Drain with pull-up resistor
+
+
+DAP Hardware I/O Pin Access Functions
+-------------------------------------
+The various I/O Pins are accessed by functions that implement the Read, Write, Set, or Clear to
+these I/O Pins.
+
+For the SWDIO I/O Pin there are additional functions that are called in SWD I/O mode only.
+This functions are provided to achieve faster I/O that is possible with some advanced GPIO
+peripherals that can independently write/read a single I/O pin without affecting any other pins
+of the same I/O port. The following SWDIO I/O Pin functions are provided:
+ - \ref PIN_SWDIO_OUT_ENABLE to enable the output mode from the DAP hardware.
+ - \ref PIN_SWDIO_OUT_DISABLE to enable the input mode to the DAP hardware.
+ - \ref PIN_SWDIO_IN to read from the SWDIO I/O pin with utmost possible speed.
+ - \ref PIN_SWDIO_OUT to write to the SWDIO I/O pin with utmost possible speed.
+*/
+
+
+// Configure DAP I/O pins ------------------------------
+
+/** Setup JTAG I/O pins: TCK, TMS, TDI, TDO, nTRST, and nRESET.
+Configures the DAP Hardware I/O pins for JTAG mode:
+ - TCK, TMS, TDI, nTRST, nRESET to output mode and set to high level.
+ - TDO to input mode.
+*/
+__STATIC_INLINE void PORT_JTAG_SETUP (void) {
+ ;
+}
+
+/** Setup SWD I/O pins: SWCLK, SWDIO, and nRESET.
+Configures the DAP Hardware I/O pins for Serial Wire Debug (SWD) mode:
+ - SWCLK, SWDIO, nRESET to output mode and set to default high level.
+ - TDI, nTRST to HighZ mode (pins are unused in SWD mode).
+*/
+// hack - zap our "stop doing divides everywhere" cache
+extern volatile uint32_t cached_delay;
+__STATIC_INLINE void PORT_SWD_SETUP (void) {
+ probe_init();
+ cached_delay = 0;
+}
+
+/** Disable JTAG/SWD I/O Pins.
+Disables the DAP Hardware I/O pins which configures:
+ - TCK/SWCLK, TMS/SWDIO, TDI, TDO, nTRST, nRESET to High-Z mode.
+*/
+__STATIC_INLINE void PORT_OFF (void) {
+ probe_deinit();
+}
+
+
+// SWCLK/TCK I/O pin -------------------------------------
+
+/** SWCLK/TCK I/O pin: Get Input.
+\return Current status of the SWCLK/TCK DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void) {
+ return (0U);
+}
+
+/** SWCLK/TCK I/O pin: Set Output to High.
+Set the SWCLK/TCK DAP hardware I/O pin to high level.
+*/
+__STATIC_FORCEINLINE void PIN_SWCLK_TCK_SET (void) {
+ ;
+}
+
+/** SWCLK/TCK I/O pin: Set Output to Low.
+Set the SWCLK/TCK DAP hardware I/O pin to low level.
+*/
+__STATIC_FORCEINLINE void PIN_SWCLK_TCK_CLR (void) {
+ ;
+}
+
+
+// SWDIO/TMS Pin I/O --------------------------------------
+
+/** SWDIO/TMS I/O pin: Get Input.
+\return Current status of the SWDIO/TMS DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_SWDIO_TMS_IN (void) {
+ return (0U);
+}
+
+/** SWDIO/TMS I/O pin: Set Output to High.
+Set the SWDIO/TMS DAP hardware I/O pin to high level.
+*/
+__STATIC_FORCEINLINE void PIN_SWDIO_TMS_SET (void) {
+ ;
+}
+
+/** SWDIO/TMS I/O pin: Set Output to Low.
+Set the SWDIO/TMS DAP hardware I/O pin to low level.
+*/
+__STATIC_FORCEINLINE void PIN_SWDIO_TMS_CLR (void) {
+ ;
+}
+
+/** SWDIO I/O pin: Get Input (used in SWD mode only).
+\return Current status of the SWDIO DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_SWDIO_IN (void) {
+ return (0U);
+}
+
+/** SWDIO I/O pin: Set Output (used in SWD mode only).
+\param bit Output value for the SWDIO DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE void PIN_SWDIO_OUT (uint32_t bit) {
+ ;
+}
+
+/** SWDIO I/O pin: Switch to Output mode (used in SWD mode only).
+Configure the SWDIO DAP hardware I/O pin to output mode. This function is
+called prior \ref PIN_SWDIO_OUT function calls.
+*/
+__STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE (void) {
+ probe_write_mode();
+}
+
+/** SWDIO I/O pin: Switch to Input mode (used in SWD mode only).
+Configure the SWDIO DAP hardware I/O pin to input mode. This function is
+called prior \ref PIN_SWDIO_IN function calls.
+*/
+__STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE (void) {
+ probe_read_mode();
+}
+
+
+// TDI Pin I/O ---------------------------------------------
+
+/** TDI I/O pin: Get Input.
+\return Current status of the TDI DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_TDI_IN (void) {
+ return (0U);
+}
+
+/** TDI I/O pin: Set Output.
+\param bit Output value for the TDI DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE void PIN_TDI_OUT (uint32_t bit) {
+ ;
+}
+
+
+// TDO Pin I/O ---------------------------------------------
+
+/** TDO I/O pin: Get Input.
+\return Current status of the TDO DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_TDO_IN (void) {
+ return (0U);
+}
+
+
+// nTRST Pin I/O -------------------------------------------
+
+/** nTRST I/O pin: Get Input.
+\return Current status of the nTRST DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_nTRST_IN (void) {
+ return (0U);
+}
+
+/** nTRST I/O pin: Set Output.
+\param bit JTAG TRST Test Reset pin status:
+ - 0: issue a JTAG TRST Test Reset.
+ - 1: release JTAG TRST Test Reset.
+*/
+__STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit) {
+ ;
+}
+
+// nRESET Pin I/O------------------------------------------
+
+/** nRESET I/O pin: Get Input.
+\return Current status of the nRESET DAP hardware I/O pin.
+*/
+__STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void) {
+ return (0U);
+}
+
+/** nRESET I/O pin: Set Output.
+\param bit target device hardware reset pin status:
+ - 0: issue a device hardware reset.
+ - 1: release device hardware reset.
+*/
+__STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) {
+ ;
+}
+
+///@}
+
+
+//**************************************************************************************************
+/**
+\defgroup DAP_Config_LEDs_gr CMSIS-DAP Hardware Status LEDs
+\ingroup DAP_ConfigIO_gr
+@{
+
+CMSIS-DAP Hardware may provide LEDs that indicate the status of the CMSIS-DAP Debug Unit.
+
+It is recommended to provide the following LEDs for status indication:
+ - Connect LED: is active when the DAP hardware is connected to a debugger.
+ - Running LED: is active when the debugger has put the target device into running state.
+*/
+
+/** Debug Unit: Set status of Connected LED.
+\param bit status of the Connect LED.
+ - 1: Connect LED ON: debugger is connected to CMSIS-DAP Debug Unit.
+ - 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit.
+*/
+__STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit) {}
+
+/** Debug Unit: Set status Target Running LED.
+\param bit status of the Target Running LED.
+ - 1: Target Running LED ON: program execution in target started.
+ - 0: Target Running LED OFF: program execution in target stopped.
+*/
+__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit) {}
+
+///@}
+
+
+//**************************************************************************************************
+/**
+\defgroup DAP_Config_Timestamp_gr CMSIS-DAP Timestamp
+\ingroup DAP_ConfigIO_gr
+@{
+Access function for Test Domain Timer.
+
+The value of the Test Domain Timer in the Debug Unit is returned by the function \ref TIMESTAMP_GET. By
+default, the DWT timer is used. The frequency of this timer is configured with \ref TIMESTAMP_CLOCK.
+
+*/
+
+/** Get timestamp of Test Domain Timer.
+\return Current timestamp value.
+*/
+__STATIC_INLINE uint32_t TIMESTAMP_GET (void) {
+ return time_us_32();
+}
+
+///@}
+
+
+//**************************************************************************************************
+/**
+\defgroup DAP_Config_Initialization_gr CMSIS-DAP Initialization
+\ingroup DAP_ConfigIO_gr
+@{
+
+CMSIS-DAP Hardware I/O and LED Pins are initialized with the function \ref DAP_SETUP.
+*/
+
+/** Setup of the Debug Unit I/O pins and LEDs (called when Debug Unit is initialized).
+This function performs the initialization of the CMSIS-DAP Hardware I/O Pins and the
+Status LEDs. In detail the operation of Hardware I/O and LED pins are enabled and set:
+ - I/O clock system enabled.
+ - all I/O pins: input buffer enabled, output pins are set to HighZ mode.
+ - for nTRST, nRESET a weak pull-up (if available) is enabled.
+ - LED output pins are enabled and LEDs are turned off.
+*/
+__STATIC_INLINE void DAP_SETUP (void) {
+ probe_gpio_init();
+}
+
+/** Reset Target Device with custom specific I/O pin or command sequence.
+This function allows the optional implementation of a device specific reset sequence.
+It is called when the command \ref DAP_ResetTarget and is for example required
+when a device needs a time-critical unlock sequence that enables the debug port.
+\return 0 = no device specific reset sequence is implemented.\n
+ 1 = a device specific reset sequence is implemented.
+*/
+__STATIC_INLINE uint8_t RESET_TARGET (void) {
+ return (0U); // change to '1' when a device reset sequence is implemented
+}
+
+///@}
+
+
+#endif /* __DAP_CONFIG_H__ */
diff --git a/src/FreeRTOSConfig.h b/src/FreeRTOSConfig.h
new file mode 100644
index 0000000..63ce706
--- /dev/null
+++ b/src/FreeRTOSConfig.h
@@ -0,0 +1,139 @@
+/*
+ * FreeRTOS V202107.00
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html
+ *----------------------------------------------------------*/
+
+/* Scheduler Related */
+#define configUSE_PREEMPTION 1
+#define configUSE_TICKLESS_IDLE 0
+#define configUSE_IDLE_HOOK 0
+#define configUSE_TICK_HOOK 1
+#define configTICK_RATE_HZ ( ( TickType_t ) 20000 )
+#define configMAX_PRIORITIES 32
+#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256
+#define configUSE_16_BIT_TICKS 0
+
+#define configIDLE_SHOULD_YIELD 1
+
+/* Synchronization Related */
+#define configUSE_MUTEXES 1
+#define configUSE_RECURSIVE_MUTEXES 1
+#define configUSE_APPLICATION_TASK_TAG 0
+#define configUSE_COUNTING_SEMAPHORES 1
+#define configQUEUE_REGISTRY_SIZE 8
+#define configUSE_QUEUE_SETS 1
+#define configUSE_TIME_SLICING 1
+#define configUSE_NEWLIB_REENTRANT 0
+#define configENABLE_BACKWARD_COMPATIBILITY 0
+#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
+
+/* System */
+#define configSTACK_DEPTH_TYPE uint32_t
+#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
+
+/* Memory allocation related definitions. */
+#define configSUPPORT_STATIC_ALLOCATION 0
+#define configSUPPORT_DYNAMIC_ALLOCATION 1
+#define configTOTAL_HEAP_SIZE (128*1024)
+#define configAPPLICATION_ALLOCATED_HEAP 0
+
+/* Hook function related definitions. */
+#define configCHECK_FOR_STACK_OVERFLOW 2
+#define configUSE_MALLOC_FAILED_HOOK 1
+#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS 0
+#define configUSE_TRACE_FACILITY 1
+#define configUSE_STATS_FORMATTING_FUNCTIONS 0
+
+/* Co-routine related definitions. */
+#define configUSE_CO_ROUTINES 0
+#define configMAX_CO_ROUTINE_PRIORITIES 1
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS 1
+#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
+#define configTIMER_QUEUE_LENGTH 10
+#define configTIMER_TASK_STACK_DEPTH 1024
+
+/* Interrupt nesting behaviour configuration. */
+/*
+#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor]
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application]
+#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application]
+*/
+
+/* SMP port only */
+#define configNUM_CORES 1
+#define configTICK_CORE 1
+#define configRUN_MULTIPLE_PRIORITIES 1
+
+/* RP2040 specific */
+#define configSUPPORT_PICO_SYNC_INTEROP 1
+#define configSUPPORT_PICO_TIME_INTEROP 1
+
+#include <assert.h>
+/* Define to trap errors during development. */
+#define configASSERT(x) assert(x)
+
+/* Set the following definitions to 1 to include the API function, or zero
+to exclude the API function. */
+#define INCLUDE_vTaskPrioritySet 1
+#define INCLUDE_uxTaskPriorityGet 1
+#define INCLUDE_vTaskDelete 1
+#define INCLUDE_vTaskSuspend 1
+#define INCLUDE_vTaskDelayUntil 1
+#define INCLUDE_vTaskDelay 1
+#define INCLUDE_xTaskGetSchedulerState 1
+#define INCLUDE_xTaskGetCurrentTaskHandle 1
+#define INCLUDE_uxTaskGetStackHighWaterMark 1
+#define INCLUDE_xTaskGetIdleTaskHandle 1
+#define INCLUDE_eTaskGetState 1
+#define INCLUDE_xTimerPendFunctionCall 1
+#define INCLUDE_xTaskAbortDelay 1
+#define INCLUDE_xTaskGetHandle 1
+#define INCLUDE_xTaskResumeFromISR 1
+#define INCLUDE_xQueueGetMutexHolder 1
+
+/* A header file that defines trace macro can be included here. */
+
+#endif /* FREERTOS_CONFIG_H */
+
diff --git a/src/cdc_uart.c b/src/cdc_uart.c
index 8f20fef..45942cf 100644
--- a/src/cdc_uart.c
+++ b/src/cdc_uart.c
@@ -24,46 +24,103 @@
*/
#include <pico/stdlib.h>
+#include "FreeRTOS.h"
+#include "task.h"
#include "tusb.h"
#include "picoprobe_config.h"
+TaskHandle_t uart_taskhandle;
+TickType_t last_wake, interval = 100;
+
+static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE];
+static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE];
+
void cdc_uart_init(void) {
gpio_set_function(PICOPROBE_UART_TX, GPIO_FUNC_UART);
gpio_set_function(PICOPROBE_UART_RX, GPIO_FUNC_UART);
+ gpio_set_pulls(PICOPROBE_UART_TX, 1, 0);
+ gpio_set_pulls(PICOPROBE_UART_RX, 1, 0);
uart_init(PICOPROBE_UART_INTERFACE, PICOPROBE_UART_BAUDRATE);
}
-#define MAX_UART_PKT 64
-void cdc_task(void) {
- uint8_t rx_buf[MAX_UART_PKT];
- uint8_t tx_buf[MAX_UART_PKT];
+void cdc_task(void)
+{
+ static int was_connected = 0;
+ uint rx_len = 0;
// Consume uart fifo regardless even if not connected
- uint rx_len = 0;
- while(uart_is_readable(PICOPROBE_UART_INTERFACE) && (rx_len < MAX_UART_PKT)) {
+ while(uart_is_readable(PICOPROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
rx_buf[rx_len++] = uart_getc(PICOPROBE_UART_INTERFACE);
}
if (tud_cdc_connected()) {
- // Do we have anything to display on the host's terminal?
+ was_connected = 1;
+ int written = 0;
+ /* Implicit overflow if we don't write all the bytes to the host.
+ * Also throw away bytes if we can't write... */
if (rx_len) {
- for (uint i = 0; i < rx_len; i++) {
- tud_cdc_write_char(rx_buf[i]);
- }
+ written = MIN(tud_cdc_write_available(), rx_len);
+ if (written > 0) {
+ tud_cdc_write(rx_buf, written);
tud_cdc_write_flush();
+ }
}
- if (tud_cdc_available()) {
- // Is there any data from the host for us to tx
- uint tx_len = tud_cdc_read(tx_buf, sizeof(tx_buf));
- uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len);
- }
+ /* Reading from a firehose and writing to a FIFO. */
+ size_t watermark = MIN(tud_cdc_available(), sizeof(tx_buf));
+ if (watermark > 0) {
+ size_t tx_len;
+ /* Batch up to half a FIFO of data - don't clog up on RX */
+ watermark = MIN(watermark, 16);
+ tx_len = tud_cdc_read(tx_buf, watermark);
+ uart_write_blocking(PICOPROBE_UART_INTERFACE, tx_buf, tx_len);
+ }
+ } else if (was_connected) {
+ tud_cdc_write_clear();
+ was_connected = 0;
}
}
-void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
- picoprobe_info("New baud rate %d\n", line_coding->bit_rate);
- uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate);
+void cdc_thread(void *ptr)
+{
+ BaseType_t delayed;
+ last_wake = xTaskGetTickCount();
+ /* Threaded with a polling interval that scales according to linerate */
+ while (1) {
+ cdc_task();
+ delayed = xTaskDelayUntil(&last_wake, interval);
+ if (delayed == pdFALSE)
+ last_wake = xTaskGetTickCount();
+ }
+}
+
+void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
+{
+ /* Set the tick thread interval to the amount of time it takes to
+ * fill up half a FIFO. Millis is too coarse for integer divide.
+ */
+ uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1);
+
+ /* Modifying state, so park the thread before changing it. */
+ vTaskSuspend(uart_taskhandle);
+ interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
+ picoprobe_info("New baud rate %d micros %d interval %u\n",
+ line_coding->bit_rate, micros, interval);
+ uart_deinit(PICOPROBE_UART_INTERFACE);
+ tud_cdc_write_clear();
+ tud_cdc_read_flush();
+ uart_init(PICOPROBE_UART_INTERFACE, line_coding->bit_rate);
+ vTaskResume(uart_taskhandle);
+}
+
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
+{
+ /* CDC drivers use linestate as a bodge to activate/deactivate the interface.
+ * Resume our UART polling on activate, stop on deactivate */
+ if (!dtr && !rts)
+ vTaskSuspend(uart_taskhandle);
+ else
+ vTaskResume(uart_taskhandle);
}
diff --git a/src/cdc_uart.h b/src/cdc_uart.h
index 186ad50..8782b64 100644
--- a/src/cdc_uart.h
+++ b/src/cdc_uart.h
@@ -26,7 +26,10 @@
#ifndef CDC_UART_H
#define CDC_UART_H
+void cdc_thread(void *ptr);
void cdc_uart_init(void);
void cdc_task(void);
+extern TaskHandle_t uart_taskhandle;
+
#endif
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index 21439a1..baee8e1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
+ * Copyright (c) 2021 Peter Lawrence
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +24,10 @@
*
*/
-#include <stdlib.h>
+#include "FreeRTOS.h"
+#include "task.h"
+
+#include <pico/stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -35,27 +39,163 @@
#include "cdc_uart.h"
#include "get_serial.h"
#include "led.h"
+#include "DAP.h"
// UART0 for Picoprobe debug
// UART1 for picoprobe to target device
+static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
+static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
+
+#define THREADED 1
+
+#define UART_TASK_PRIO (tskIDLE_PRIORITY + 3)
+#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
+#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
+
+static TaskHandle_t dap_taskhandle, tud_taskhandle;
+
+void usb_thread(void *ptr)
+{
+ do {
+ tud_task();
+ // Trivial delay to save power
+ vTaskDelay(1);
+ } while (1);
+}
+
+void dap_thread(void *ptr)
+{
+ uint32_t resp_len;
+ do {
+ if (tud_vendor_available()) {
+ tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
+ resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
+ tud_vendor_write(TxDataBuffer, resp_len);
+ } else {
+ // Trivial delay to save power
+ vTaskDelay(2);
+ }
+ } while (1);
+}
+
int main(void) {
+ uint32_t resp_len;
board_init();
usb_serial_init();
cdc_uart_init();
tusb_init();
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
+ probe_gpio_init();
probe_init();
+#else
+ DAP_Setup();
+#endif
led_init();
picoprobe_info("Welcome to Picoprobe!\n");
- while (1) {
- tud_task(); // tinyusb device task
+ if (THREADED) {
+ /* UART needs to preempt USB as if we don't, characters get lost */
+ xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
+ xTaskCreate(usb_thread, "TUD", configMINIMAL_STACK_SIZE, NULL, TUD_TASK_PRIO, &tud_taskhandle);
+ /* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
+ xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
+ vTaskStartScheduler();
+ }
+
+ while (!THREADED) {
+ tud_task();
cdc_task();
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
probe_task();
led_task();
+#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
+ if (tud_vendor_available()) {
+ tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
+ resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
+ tud_vendor_write(TxDataBuffer, resp_len);
+ }
+#endif
}
return 0;
-}
\ No newline at end of file
+}
+
+uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
+{
+ // TODO not Implemented
+ (void) itf;
+ (void) report_id;
+ (void) report_type;
+ (void) buffer;
+ (void) reqlen;
+
+ return 0;
+}
+
+void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* RxDataBuffer, uint16_t bufsize)
+{
+ uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize);
+
+ // This doesn't use multiple report and report ID
+ (void) itf;
+ (void) report_id;
+ (void) report_type;
+
+ DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
+
+ tud_hid_report(0, TxDataBuffer, response_size);
+}
+
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
+extern uint8_t const desc_ms_os_20[];
+
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // nothing to with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
+ switch (request->bmRequestType_bit.type)
+ {
+ case TUSB_REQ_TYPE_VENDOR:
+ switch (request->bRequest)
+ {
+ case 1:
+ if ( request->wIndex == 7 )
+ {
+ // Get Microsoft OS 2.0 compatible descriptor
+ uint16_t total_len;
+ memcpy(&total_len, desc_ms_os_20+8, 2);
+
+ return tud_control_xfer(rhport, request, (void*) desc_ms_os_20, total_len);
+ }else
+ {
+ return false;
+ }
+
+ default: break;
+ }
+ break;
+ default: break;
+ }
+
+ // stall unknown request
+ return false;
+}
+#endif
+
+void vApplicationTickHook (void)
+{
+};
+
+void vApplicationStackOverflowHook(TaskHandle_t Task, char *pcTaskName)
+{
+ panic("stack overflow (not the helpful kind) for %s\n", *pcTaskName);
+}
+
+void vApplicationMallocFailedHook(void)
+{
+ panic("Malloc Failed\n");
+};
diff --git a/src/picoprobe_config.h b/src/picoprobe_config.h
index 87be47b..1552c36 100644
--- a/src/picoprobe_config.h
+++ b/src/picoprobe_config.h
@@ -49,8 +49,8 @@
// PIO config
#define PROBE_SM 0
#define PROBE_PIN_OFFSET 2
-#define PROBE_PIN_SWCLK PROBE_PIN_OFFSET + 0 // 2
-#define PROBE_PIN_SWDIO PROBE_PIN_OFFSET + 1 // 3
+#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2
+#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) // 3
// Target reset config
#define PROBE_PIN_RESET 6
@@ -72,6 +72,15 @@
#define PICOPROBE_LED PICO_DEFAULT_LED_PIN
#endif
+#define PROTO_OPENOCD_CUSTOM 0
+#define PROTO_DAP_V1 1
+#define PROTO_DAP_V2 2
+
+// Interface config
+#ifndef PICOPROBE_DEBUG_PROTOCOL
+#define PICOPROBE_DEBUG_PROTOCOL PROTO_DAP_V2
+#endif
+
#endif
#endif
diff --git a/src/probe.c b/src/probe.c
index 3396796..719468f 100644
--- a/src/probe.c
+++ b/src/probe.c
@@ -87,20 +87,20 @@
};
void probe_set_swclk_freq(uint freq_khz) {
- picoprobe_info("Set swclk freq %dKHz\n", freq_khz);
- uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000;
- // Worked out with saleae
- uint32_t divider = clk_sys_freq_khz / freq_khz / 2;
- pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0);
+ uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000;
+ picoprobe_info("Set swclk freq %dKHz sysclk %dkHz\n", freq_khz, clk_sys_freq_khz);
+ // Worked out with saleae
+ uint32_t divider = clk_sys_freq_khz / freq_khz / 2;
+ pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0);
}
-static inline void probe_assert_reset(bool state)
+void probe_assert_reset(bool state)
{
/* Change the direction to out to drive pin to 0 or to in to emulate open drain */
gpio_set_dir(PROBE_PIN_RESET, state);
}
-static inline void probe_write_bits(uint bit_count, uint8_t data_byte) {
+void probe_write_bits(uint bit_count, uint32_t data_byte) {
DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE);
pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1);
pio_sm_put_blocking(pio0, PROBE_SM, data_byte);
@@ -112,14 +112,13 @@
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE);
}
-static inline uint8_t probe_read_bits(uint bit_count) {
+uint32_t probe_read_bits(uint bit_count) {
DEBUG_PINS_SET(probe_timing, DBG_PIN_READ);
pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1);
uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM);
- uint8_t data_shifted = data >> 24;
-
- if (bit_count < 8) {
- data_shifted = data_shifted >> 8-bit_count;
+ uint32_t data_shifted = data;
+ if (bit_count < 32) {
+ data_shifted = data >> (32 - bit_count);
}
picoprobe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted);
@@ -127,23 +126,26 @@
return data_shifted;
}
-static void probe_read_mode(void) {
+void probe_read_mode(void) {
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge));
while(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO));
}
-static void probe_write_mode(void) {
+void probe_write_mode(void) {
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge));
while(!(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO)));
}
-void probe_init() {
+void probe_gpio_init()
+{
// Funcsel pins
pio_gpio_init(pio0, PROBE_PIN_SWCLK);
pio_gpio_init(pio0, PROBE_PIN_SWDIO);
// Make sure SWDIO has a pullup on it. Idle state is high
gpio_pull_up(PROBE_PIN_SWDIO);
+}
+void probe_init() {
// Target reset pin: pull up, input to emulate open drain pin
gpio_pull_up(PROBE_PIN_RESET);
// gpio_init will leave the pin cleared and set as input
@@ -183,6 +185,13 @@
probe_write_mode();
}
+void probe_deinit(void)
+{
+ probe_read_mode();
+ pio_sm_set_enabled(pio0, PROBE_SM, 0);
+ pio_remove_program(pio0, &probe_program, probe.offset);
+}
+
void probe_handle_read(uint total_bits) {
picoprobe_debug("Read %d bits\n", total_bits);
probe_read_mode();
@@ -195,7 +204,7 @@
} else {
chunk = bits;
}
- probe.tx_buf[probe.tx_len] = probe_read_bits(chunk);
+ probe.tx_buf[probe.tx_len] = (uint8_t)probe_read_bits(chunk);
probe.tx_len++;
// Decrement remaining bits
bits -= chunk;
@@ -218,7 +227,7 @@
chunk = bits;
}
- probe_write_bits(chunk, *data++);
+ probe_write_bits(chunk, (uint32_t)*data++);
bits -= chunk;
}
}
diff --git a/src/probe.h b/src/probe.h
index 2474a38..0ad8c76 100644
--- a/src/probe.h
+++ b/src/probe.h
@@ -26,7 +26,19 @@
#ifndef PROBE_H_
#define PROBE_H_
-void probe_task(void);
-void probe_init(void);
+void probe_set_swclk_freq(uint freq_khz);
+void probe_write_bits(uint bit_count, uint32_t data_byte);
+uint32_t probe_read_bits(uint bit_count);
-#endif
\ No newline at end of file
+void probe_read_mode(void);
+void probe_write_mode(void);
+
+void probe_handle_read(uint total_bits);
+void probe_handle_write(uint8_t *data, uint total_bits);
+
+void probe_task(void);
+void probe_gpio_init(void);
+void probe_init(void);
+void probe_deinit(void);
+
+#endif
diff --git a/src/probe.pio b/src/probe.pio
index 920a3a5..4a242ba 100644
--- a/src/probe.pio
+++ b/src/probe.pio
@@ -34,7 +34,7 @@
out_negedge_bitloop:
out pins, 1 side 0x0 ; clock data out on falling edge
jmp x-- out_negedge_bitloop side 0x1 ; data is present for posedge
- set pins, 0 side 0x0 ; Drive data low
+ set pins, 1 side 0x0 ; Drive data high (idle bus state)
push ; Push to rx fifo just so processor knows when done
jmp out_negedge ; Wait for next transaction
diff --git a/src/sw_dp_pio.c b/src/sw_dp_pio.c
new file mode 100755
index 0000000..951b47f
--- /dev/null
+++ b/src/sw_dp_pio.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2013-2022 ARM Limited. All rights reserved.
+ * Copyright (c) 2022 Raspberry Pi Ltd
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the License); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This is a shim between the SW_DP functions and the PIO
+ * implementation used for Picoprobe. Instead of calling bitbash functions,
+ * hand off the bit sequences to a SM for asynchronous completion.
+ */
+
+#include <stdio.h>
+
+#include "DAP_config.h"
+#include "DAP.h"
+#include "probe.h"
+
+/* Slight hack - we're not bitbashing so we need to set baudrate off the DAP's delay cycles.
+ * Ideally we don't want calls to udiv everywhere... */
+#define MAKE_KHZ(x) (CPU_CLOCK / (2000 * ((x) + 1)))
+volatile uint32_t cached_delay = 0;
+
+// Generate SWJ Sequence
+// count: sequence bit count
+// data: pointer to sequence bit data
+// return: none
+#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
+void SWJ_Sequence (uint32_t count, const uint8_t *data) {
+ uint32_t bits;
+ uint32_t n;
+
+ if (DAP_Data.clock_delay != cached_delay) {
+ probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
+ cached_delay = DAP_Data.clock_delay;
+ }
+ picoprobe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
+ n = count;
+ while (n > 0) {
+ if (n > 8)
+ bits = 8;
+ else
+ bits = n;
+ probe_write_bits(bits, *data++);
+ n -= bits;
+ }
+}
+#endif
+
+// Generate SWD Sequence
+// info: sequence information
+// swdo: pointer to SWDIO generated data
+// swdi: pointer to SWDIO captured data
+// return: none
+#if (DAP_SWD != 0)
+void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
+ uint32_t bits;
+ uint32_t n;
+
+ if (DAP_Data.clock_delay != cached_delay) {
+ probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
+ cached_delay = DAP_Data.clock_delay;
+ }
+ picoprobe_debug("SWD sequence\n");
+ n = info & SWD_SEQUENCE_CLK;
+ if (n == 0U) {
+ n = 64U;
+ }
+ bits = n;
+ if (info & SWD_SEQUENCE_DIN) {
+ while (n > 0) {
+ if (n > 8)
+ bits = 8;
+ else
+ bits = n;
+ *swdi++ = probe_read_bits(bits);
+ n -= bits;
+ }
+ } else {
+ while (n > 0) {
+ if (n > 8)
+ bits = 8;
+ else
+ bits = n;
+ probe_write_bits(bits, *swdo++);
+ n -= bits;
+ }
+ }
+}
+#endif
+
+#if (DAP_SWD != 0)
+// SWD Transfer I/O
+// request: A[3:2] RnW APnDP
+// data: DATA[31:0]
+// return: ACK[2:0]
+uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
+ uint8_t prq = 0;
+ uint8_t ack;
+ uint8_t bit;
+ uint32_t val = 0;
+ uint32_t parity = 0;
+ uint32_t n;
+
+ if (DAP_Data.clock_delay != cached_delay) {
+ probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
+ cached_delay = DAP_Data.clock_delay;
+ }
+ picoprobe_debug("SWD_transfer\n");
+ /* Generate the request packet */
+ prq |= (1 << 0); /* Start Bit */
+ for (n = 1; n < 5; n++) {
+ bit = (request >> (n - 1)) & 0x1;
+ prq |= bit << n;
+ parity += bit;
+ }
+ prq |= (parity & 0x1) << 5; /* Parity Bit */
+ prq |= (0 << 6); /* Stop Bit */
+ prq |= (1 << 7); /* Park bit */
+ probe_write_bits(8, prq);
+
+ /* Turnaround (ignore read bits) */
+ probe_read_mode();
+
+ ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3);
+ ack >>= DAP_Data.swd_conf.turnaround;
+
+ if (ack == DAP_TRANSFER_OK) {
+ /* Data transfer phase */
+ if (request & DAP_TRANSFER_RnW) {
+ /* Read RDATA[0:31] - note probe_read shifts to LSBs */
+ val = probe_read_bits(32);
+ bit = probe_read_bits(1);
+ parity = __builtin_popcount(val);
+ if ((parity ^ bit) & 1U) {
+ /* Parity error */
+ ack = DAP_TRANSFER_ERROR;
+ }
+ if (data)
+ *data = val;
+ picoprobe_debug("Read %02x ack %02x 0x%08x parity %01x\n",
+ prq, ack, val, bit);
+ /* Turnaround for line idle */
+ probe_read_bits(DAP_Data.swd_conf.turnaround);
+ probe_write_mode();
+ } else {
+ /* Turnaround for write */
+ probe_read_bits(DAP_Data.swd_conf.turnaround);
+ probe_write_mode();
+
+ /* Write WDATA[0:31] */
+ val = *data;
+ probe_write_bits(32, val);
+ parity = __builtin_popcount(val);
+ /* Write Parity Bit */
+ probe_write_bits(1, parity & 0x1);
+ picoprobe_debug("write %02x ack %02x 0x%08x parity %01x\n",
+ prq, ack, val, parity);
+ }
+ /* Capture Timestamp */
+ if (request & DAP_TRANSFER_TIMESTAMP) {
+ DAP_Data.timestamp = time_us_32();
+ }
+
+ /* Idle cycles - drive 0 for N clocks */
+ if (DAP_Data.transfer.idle_cycles) {
+ for (n = DAP_Data.transfer.idle_cycles; n; ) {
+ if (n > 32) {
+ probe_write_bits(32, 0);
+ n -= 32;
+ } else {
+ probe_write_bits(n, 0);
+ n -= n;
+ }
+ }
+ }
+ return ((uint8_t)ack);
+ }
+
+ if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
+ if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) {
+ /* Dummy Read RDATA[0:31] + Parity */
+ probe_read_bits(33);
+ }
+ probe_read_bits(DAP_Data.swd_conf.turnaround);
+ probe_write_mode();
+ if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
+ /* Dummy Write WDATA[0:31] + Parity */
+ probe_write_bits(32, 0);
+ probe_write_bits(1, 0);
+ }
+ return ((uint8_t)ack);
+ }
+
+ /* Protocol error */
+ n = DAP_Data.swd_conf.turnaround + 32U + 1U;
+ /* Back off data phase */
+ probe_read_bits(n);
+ probe_write_mode();
+ return ((uint8_t)ack);
+}
+
+#endif /* (DAP_SWD != 0) */
diff --git a/src/tusb_config.h b/src/tusb_config.h
index b4dc45c..ad0e1fa 100644
--- a/src/tusb_config.h
+++ b/src/tusb_config.h
@@ -62,7 +62,7 @@
#endif
//------------- CLASS -------------//
-#define CFG_TUD_HID 0
+#define CFG_TUD_HID 1
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index dd6b4f0..29b8bd5 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -2,6 +2,8 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2021 Peter Lawrence
+ * Copyright (c) 2022 Raspberry Pi Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +27,7 @@
#include "tusb.h"
#include "get_serial.h"
-
+#include "picoprobe_config.h"
//--------------------------------------------------------------------+
// Device Descriptors
@@ -34,14 +36,22 @@
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0110, // // USB Specification version 1.1
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
+ .bcdUSB = 0x0210, // USB Specification version 2.1 for BOS
+#else
+ .bcdUSB = 0x0110,
+#endif
.bDeviceClass = 0x00, // Each interface specifies its own
.bDeviceSubClass = 0x00, // Each interface specifies its own
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x2E8A, // Pi
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
.idProduct = 0x0004, // Picoprobe
+#else
+ .idProduct = 0x000c, // CMSIS-DAP adapter
+#endif
.bcdDevice = 0x0100, // Version 01.00
.iManufacturer = 0x01,
.iProduct = 0x02,
@@ -62,9 +72,9 @@
enum
{
+ ITF_NUM_PROBE, // Old versions of Keil MDK only look at interface 0
ITF_NUM_CDC_COM,
ITF_NUM_CDC_DATA,
- ITF_NUM_PROBE,
ITF_NUM_TOTAL
};
@@ -74,18 +84,39 @@
#define PROBE_OUT_EP_NUM 0x04
#define PROBE_IN_EP_NUM 0x85
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
+#else
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN)
+#endif
+
+static uint8_t const desc_hid_report[] =
+{
+ TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE)
+};
+
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
+{
+ (void) itf;
+ return desc_hid_report;
+}
uint8_t const desc_configuration[] =
{
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
-
- // Interface 0 + 1
- TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 0, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
-
- // Interface 2
- TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64)
-
+ // Interface 0
+#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
+ // HID (named interface)
+ TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_PROBE, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, CFG_TUD_HID_EP_BUFSIZE, 1),
+#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
+ // Bulk (named interface)
+ TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
+#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
+ // Bulk
+ TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
+#endif
+ // Interface 1 + 2
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 6, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -106,8 +137,11 @@
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer
- "Picoprobe", // 2: Product
+ "Picoprobe CMSIS-DAP", // 2: Product
usb_serial, // 3: Serial, uses flash unique ID
+ "Picoprobe CMSIS-DAP v1", // 4: Interface descriptor for HID transport
+ "Picoprobe CMSIS-DAP v2", // 5: Interface descriptor for Bulk transport
+ "Picoprobe CDC-ACM UART", // 6: Interface descriptor for CDC
};
static uint16_t _desc_str[32];
@@ -147,3 +181,69 @@
return _desc_str;
}
+
+/* [incoherent gibbering to make Windows happy] */
+
+//--------------------------------------------------------------------+
+// BOS Descriptor
+//--------------------------------------------------------------------+
+
+/* Microsoft OS 2.0 registry property descriptor
+Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
+device should create DeviceInterfaceGUIDs. It can be done by driver and
+in case of real PnP solution device should expose MS "Microsoft OS 2.0
+registry property descriptor". Such descriptor can insert any record
+into Windows registry per device/configuration/interface. In our case it
+will insert "DeviceInterfaceGUIDs" multistring property.
+
+
+https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
+(Section Microsoft OS compatibility descriptors)
+*/
+#define MS_OS_20_DESC_LEN 0xB2
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+
+uint8_t const desc_bos[] =
+{
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)
+};
+
+uint8_t const desc_ms_os_20[] =
+{
+ // Set header: length, type, windows version, total length
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+
+ // Configuration subset header: length, type, configuration index, reserved, configuration total length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
+
+ // Function Subset header: length, type, first interface, reserved, subset length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_PROBE, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
+
+ // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
+
+ // MS OS 2.0 Registry property descriptor: length, type
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
+ 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(0x0050), // wPropertyDataLength
+ // bPropertyData "{CDB3B5AD-293B-4663-AA36-1AAE46463776}" as a UTF-16 string (b doesn't mean bytes)
+ '{', 0x00, 'C', 0x00, 'D', 0x00, 'B', 0x00, '3', 0x00, 'B', 0x00, '5', 0x00, 'A', 0x00, 'D', 0x00, '-', 0x00,
+ '2', 0x00, '9', 0x00, '3', 0x00, 'B', 0x00, '-', 0x00, '4', 0x00, '6', 0x00, '6', 0x00, '3', 0x00, '-', 0x00,
+ 'A', 0x00, 'A', 0x00, '3', 0x00, '6', 0x00, '-', 0x00, '1', 0x00, 'A', 0x00, 'A', 0x00, 'E', 0x00, '4', 0x00,
+ '6', 0x00, '4', 0x00, '6', 0x00, '3', 0x00, '7', 0x00, '7', 0x00, '6', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
+
+uint8_t const * tud_descriptor_bos_cb(void)
+{
+ return desc_bos;
+}