diff --git a/CMakeLists.txt b/CMakeLists.txt index f507cf8..94a2332 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,11 +56,11 @@ endif() target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libco/libco.S -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/unique.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/modeset.c @@ -68,8 +68,15 @@ target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/tusb_plt.S ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ${CMAKE_CURRENT_SOURCE_DIR}/src/vnd_cfg.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/0def.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default2/0def.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/_default.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/cdc_serprog.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/tempsensor.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/vnd_i2ctinyusb.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/cdc_uart.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/i2c_tinyusb.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/spi_serprog.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c +# ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default2/0def.c ) if(USE_USBCDC_FOR_STDIO) target_sources(${PROJECT} PUBLIC @@ -79,13 +86,13 @@ endif() target_include_directories(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/ ${CMAKE_CURRENT_SOURCE_DIR}/libco/ -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Include/ -# ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Core/Include/ + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Include/ + ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Core/Include/ ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ ${CMAKE_CURRENT_SOURCE_DIR}/bsp/default/ ) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR} diff --git a/bsp/rp2040/DAP_config.h b/bsp/rp2040/DAP_config.h new file mode 100644 index 0000000..c3182c6 --- /dev/null +++ b/bsp/rp2040/DAP_config.h @@ -0,0 +1,627 @@ +// vim: set et: +/* + * The MIT License (MIT) + * + * 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 + * 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. + */ + +/* +This DAP_config provides a CMSIS-DAP alternative to picoprobe and raspberrypi-swd.cfg +*/ + +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bsp/board.h" +#include "cmsis_compiler.h" + +#include "info.h" +#include "util.h" +#include "m_default/bsp-feature.h" +#include "m_default/pinout.h" + +#define PINOUT_SWCLK PINOUT_JTAG_TCK +#define PINOUT_SWDIO PINOUT_JTAG_TMS + +#define PINOUT_SWCLK_MASK (1UL << PINOUT_SWCLK) +#define PINOUT_SWDIO_MASK (1UL << PINOUT_SWDIO) + +#define PINOUT_TCK_MASK (1UL << PINOUT_JTAG_TCK) +#define PINOUT_TMS_MASK (1UL << PINOUT_JTAG_TMS) +#define PINOUT_TDI_MASK (1UL << PINOUT_JTAG_TDI) +#define PINOUT_TDO_MASK (1UL << PINOUT_JTAG_TDO) +#define PINOUT_nTRST_MASK (1UL << PINOUT_JTAG_nTRST) +#define PINOUT_nRESET_MASK (1UL << PINOUT_JTAG_nRESET) + +#define PINOUT_LED_MASK (1UL << PINOUT_LED) + +/// Processor Clock of the Cortex-M MCU used in the Debug Unit. +/// This value is used to calculate the SWD/JTAG clock speed. +#define CPU_CLOCK 48000000U ///< 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 2U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0. + +/// 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 Capabilities. +#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 Capabilities. +#define DAP_JTAG 1 ///< 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 2U ///< 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 CFG_TUD_HID_EP_BUFSIZE ///< 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 1U ///< 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 Capabilities. +#define SWO_UART 0 ///< SWO UART: 1 = available, 0 = not available. + +/// 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 Capabilities. +#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. + +/// Indicate that UART Communication Port is available. +/// This information is returned by the command \ref DAP_Info as part of Capabilities. +#define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available. + +/// USART Driver instance number for the UART Communication Port. +#define DAP_UART_DRIVER 1 ///< USART Driver instance number (Driver_USART#). + +/// UART Receive Buffer Size. +#define DAP_UART_RX_BUFFER_SIZE 64U ///< Uart Receive Buffer Size in bytes (must be 2^n). + +/// UART Transmit Buffer Size. +#define DAP_UART_TX_BUFFER_SIZE 64U ///< 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 Capabilities. +#ifdef USE_USBCDC_FOR_STDIO +#define DAP_UART_USB_COM_PORT 1 ///< USB COM Port: 1 = available, 0 = not available. +#else +#define DAP_UART_USB_COM_PORT 0 +#endif + +/// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET. +#define TIMESTAMP_CLOCK 0U ///< Timestamp clock in Hz (0 = timestamps not supported). + +/// 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 and Device Name string is stored which +/// may be used by the debugger or IDE to configure device parameters. +#define TARGET_DEVICE_FIXED 0 ///< Target Device: 1 = known, 0 = unknown; + +#if TARGET_DEVICE_FIXED +#define TARGET_DEVICE_VENDOR "Raspberry Pi" ///< String indicating the Silicon Vendor +#define TARGET_DEVICE_NAME "Pico" ///< String indicating the Target Device +#endif + +#include "DAP.h" + +/** Get Vendor ID string. +\param str Pointer to buffer to store the string. +\return String length. +*/ +__STATIC_INLINE uint8_t DAP_GetVendorString(char* str) { + static const char vnd[] = INFO_MANUFACTURER; + for (size_t i = 0; i < sizeof(vnd); ++i) str[i] = vnd[i]; + return sizeof(vnd) - 1; +} + +/** Get Product ID string. +\param str Pointer to buffer to store the string. +\return String length. +*/ +__STATIC_INLINE uint8_t DAP_GetProductString(char* str) { + static const char prd[] = INFO_PRODUCT(INFO_BOARDNAME); + for (size_t i = 0; i < sizeof(prd); ++i) str[i] = prd[i]; + return sizeof(prd) - 1; +} + +/** Get Serial Number string. +\param str Pointer to buffer to store the string. +\return String length. +*/ +__STATIC_INLINE uint8_t DAP_GetSerNumString(char* str) { return get_unique_id_u8((uint8_t*)str); } + +/** 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) { + (void)str; + return 0; +} + +/** 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) { + (void)str; + return 0; +} + +/** 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) { + (void)str; + return 0; +} + +/** 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) { + (void)str; + return 0; +} + +/* TODO! */ +/** 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 0; +} + +///@} + +//************************************************************************************************** +/** +\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) { + resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS); + + /* set to default high level */ + sio_hw->gpio_oe_set = PINOUT_TCK_MASK | PINOUT_TMS_MASK | PINOUT_TDI_MASK | PINOUT_nTRST_MASK | + PINOUT_nRESET_MASK; + sio_hw->gpio_set = PINOUT_TCK_MASK | PINOUT_TMS_MASK | PINOUT_TDI_MASK | PINOUT_nTRST_MASK | + PINOUT_nRESET_MASK; + /* TDO needs to be an input */ + sio_hw->gpio_oe_clr = PINOUT_TDO_MASK; + + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TCK], + PADS_BANK0_GPIO0_IE_BITS, // bits to set: input enable + PADS_BANK0_GPIO0_IE_BITS | + PADS_BANK0_GPIO0_OD_BITS); // bits to mask out: input enable, output disable + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TMS], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDI], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDO], + PADS_BANK0_GPIO0_IE_BITS | + PADS_BANK0_GPIO0_OD_BITS, // TDO needs to have its output disabled + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nTRST], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nRESET], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + + // NOTE: hiZ: ctrl = (ctrl & ~(CTRL_OEOVER_BITS)) | (GPIO_OVERRIDE_LOW << CTRL_OEOVER_LSB); + // normal == 0, low == 2 + + // set pin modes to general IO (SIO) + iobank0_hw->io[PINOUT_JTAG_TCK].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + iobank0_hw->io[PINOUT_JTAG_TMS].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + iobank0_hw->io[PINOUT_JTAG_TDI].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + iobank0_hw->io[PINOUT_JTAG_TDO].ctrl = (GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) + /*| (GPIO_OVERRIDE_LOW << IO_BANK0_GPIO0_CTRL_OEOVER_LSB)*/; + iobank0_hw->io[PINOUT_JTAG_nTRST].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + iobank0_hw->io[PINOUT_JTAG_nRESET].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; +} + +/** 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). +*/ +__STATIC_INLINE void PORT_SWD_SETUP(void) { + resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS); + + /* set to default high level */ + sio_hw->gpio_oe_set = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK; + sio_hw->gpio_set = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK; + + hw_write_masked(&padsbank0_hw->io[PINOUT_SWCLK], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + hw_write_masked(&padsbank0_hw->io[PINOUT_SWDIO], PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + iobank0_hw->io[PINOUT_SWCLK].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + iobank0_hw->io[PINOUT_SWDIO].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; +} + +/** 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) { + sio_hw->gpio_oe_clr = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK | + PINOUT_TDI_MASK //| PINOUT_TDO_MASK + | PINOUT_nTRST_MASK | PINOUT_nRESET_MASK; +} + +// 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 (sio_hw->gpio_in & PINOUT_SWCLK_MASK) >> PINOUT_SWCLK; +} + +/** 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) { sio_hw->gpio_set = PINOUT_SWCLK_MASK; } + +/** 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) { sio_hw->gpio_clr = PINOUT_SWCLK_MASK; } + +// 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 (sio_hw->gpio_in & PINOUT_SWDIO_MASK) >> PINOUT_SWDIO; +} + +/* PIN_SWDIO_TMS_SET and PIN_SWDIO_TMS_CLR are used by SWJ_Sequence */ + +/** 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) { sio_hw->gpio_set = PINOUT_SWDIO_MASK; } + +/** 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) { sio_hw->gpio_clr = PINOUT_SWDIO_MASK; } + +/** 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 (sio_hw->gpio_in & PINOUT_SWDIO_MASK) ? 1U : 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) { + if (bit & 1) + sio_hw->gpio_set = PINOUT_SWDIO_MASK; + else + sio_hw->gpio_clr = PINOUT_SWDIO_MASK; +} + +/** 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) { sio_hw->gpio_oe_set = PINOUT_SWDIO_MASK; } + +/** 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) { sio_hw->gpio_oe_clr = PINOUT_SWDIO_MASK; } + +// 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 (sio_hw->gpio_in & PINOUT_TDI_MASK) >> PINOUT_JTAG_TDI; +} + +/** 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) { + if (bit & 1) + sio_hw->gpio_set = PINOUT_TDI_MASK; + else + sio_hw->gpio_clr = PINOUT_TDI_MASK; +} + +// 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 (sio_hw->gpio_in & PINOUT_TDO_MASK) >> PINOUT_JTAG_TDO; +} + +// 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 (sio_hw->gpio_in & PINOUT_nTRST_MASK) >> PINOUT_JTAG_nTRST; +} + +/** 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) { + if (bit & 1) + sio_hw->gpio_set = PINOUT_nTRST_MASK; + else + sio_hw->gpio_clr = PINOUT_nTRST_MASK; +} + +// 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 (sio_hw->gpio_in & PINOUT_nRESET_MASK) >> PINOUT_JTAG_nRESET; +} + +/** 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) { + if (bit & 1) + sio_hw->gpio_set = PINOUT_nRESET_MASK; + else + sio_hw->gpio_clr = PINOUT_nRESET_MASK; +} + +///@} + +//************************************************************************************************** +/** +\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) { +#if PINOUT_LED_CONNECTED + if (bit & 1) + sio_hw->gpio_set = PINOUT_LED_MASK; + else + sio_hw->gpio_clr = PINOUT_LED_MASK; +#else + (void)bit; +#endif +} + +/** 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) { +#if PINOUT_LED_RUNNING + if (bit & 1) + sio_hw->gpio_set = PINOUT_LED_MASK; + else + sio_hw->gpio_clr = PINOUT_LED_MASK; +#else + (void)bit; +#endif +} + +///@} + +//************************************************************************************************** +/** +\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) { +#if TIMESTAMP_CLOCK > 0 + return (DWT->CYCCNT); +#else + return 0; +#endif +} + +///@} + +//************************************************************************************************** +/** +\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) { + sio_hw->gpio_oe_set = PINOUT_LED_MASK; + sio_hw->gpio_clr = PINOUT_LED_MASK; + + hw_write_masked( + &padsbank0_hw->io[PINOUT_LED], 0, PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + iobank0_hw->io[PINOUT_LED].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + + bi_decl(bi_2pins_with_names(PINOUT_JTAG_TCK, "TCK / SWCLK", PINOUT_JTAG_TMS, "TMS / SWDIO")); + bi_decl(bi_4pins_with_names(PINOUT_JTAG_TDI, "TDI", PINOUT_JTAG_TDO, "TDO", PINOUT_JTAG_nTRST, + "nTRST", PINOUT_JTAG_nRESET, "nRESET")); +} + +/** 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/bsp/rp2040/bsp-info.h b/bsp/rp2040/bsp-info.h new file mode 100644 index 0000000..d2fd1a8 --- /dev/null +++ b/bsp/rp2040/bsp-info.h @@ -0,0 +1,25 @@ +// vim: set et: + +#ifndef BSP_INFO_H_ +#define BSP_INFO_H_ + +/*#define USB_VID 0x2e8a*/ /* Raspberry Pi */ +#define USB_VID 0xcafe /* TinyUSB */ +/*#define USB_VID 0x1209*/ /* Generic */ +/*#define USB_VID 0x1d50*/ /* OpenMoko */ +#define USB_PID 0x1312 + +// TODO: other RP2040 boards +#define INFO_BOARDNAME "RP2040 Pico" + +/* each CFG_TUD_xxx macro must be the max across all modes */ +// TODO: have this depend on the DBOARD_HAS_xxx macros? +#define CFG_TUD_HID 1 +#ifdef USE_USBCDC_FOR_STDIO +#define CFG_TUD_CDC 3 +#else +#define CFG_TUD_CDC 2 +#endif +#define CFG_TUD_VENDOR 1 + +#endif diff --git a/bsp/rp2040/cdc_stdio.c b/bsp/rp2040/cdc_stdio.c index d632a7f..9709ce6 100644 --- a/bsp/rp2040/cdc_stdio.c +++ b/bsp/rp2040/cdc_stdio.c @@ -7,9 +7,7 @@ #include #include -//#include "pinout.h" -//#include "protocfg.h" -#include "tusb.h" +#include #ifndef PICO_STDIO_USB_STDOUT_TIMEOUT_US #define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000 @@ -18,9 +16,13 @@ // *mostly* the same as the SDK code, *except* we have to explicitely pass the // CDC interface number to the tusb functions, making the SDK code itself very // non-reusable >__> -#define CDC_N_STDIO 0 static mutex_t stdio_usb_mutex; +static int CDC_N_STDIO = 0; + +void stdio_usb_set_itf_num(int itf) { + CDC_N_STDIO = itf; +} static void stdio_usb_out_chars(const char* buf, int length) { static uint64_t last_avail_time; diff --git a/bsp/rp2040/m_default/bsp-feature.h b/bsp/rp2040/m_default/bsp-feature.h new file mode 100644 index 0000000..7feecac --- /dev/null +++ b/bsp/rp2040/m_default/bsp-feature.h @@ -0,0 +1,38 @@ + +#ifndef BSP_FEATURE_M_DEFAULT_H_ +#define BSP_FEATURE_M_DEFAULT_H_ + +#define DBOARD_HAS_UART +#define DBOARD_HAS_CMSISDAP +#define DBOARD_HAS_SERPROG +// FIXME: this one doesn't work yet!!!!! (kernel usb device cfg fails) +// "usb 1-1: can't set config #1, error -32" +/*#define DBOARD_HAS_I2C*/ +/*#define DBOARD_HAS_TEMPSENSOR*/ + +#include "bsp-info.h" + +enum { + HID_N_CMSISDAP = 0, + + HID_N__NITF +}; +enum { + CDC_N_UART = 0, + CDC_N_SERPROG, +#ifdef USE_USBCDC_FOR_STDIO + CDC_N_STDIO, +#endif + + CDC_N__NITF +}; +enum { +#if CFG_TUD_VENDOR > 0 + VND_CFG = 0, +#endif + + VND_N__NITF +}; + +#endif + diff --git a/bsp/rp2040/m_default/cdc_uart.c b/bsp/rp2040/m_default/cdc_uart.c new file mode 100644 index 0000000..0e4ed96 --- /dev/null +++ b/bsp/rp2040/m_default/cdc_uart.c @@ -0,0 +1,77 @@ +// vim: set et: +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Raspberry Pi (Trading) 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 + * 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. + * + */ + +#include +#include + +#include + +#include "m_default/bsp-feature.h" +#include "m_default/pinout.h" +#include "m_default/cdc.h" + +static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE]; +static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE]; + +void cdc_uart_init(void) { + gpio_set_function(PINOUT_UART_TX, GPIO_FUNC_UART); + gpio_set_function(PINOUT_UART_RX, GPIO_FUNC_UART); + uart_init(PINOUT_UART_INTERFACE, PINOUT_UART_BAUDRATE); + + bi_decl(bi_2pins_with_func(PINOUT_UART_TX, PINOUT_UART_RX, GPIO_FUNC_UART)); +} +void cdc_uart_deinit(void) { + uart_deinit(PINOUT_UART_INTERFACE); + gpio_set_function(PINOUT_UART_TX, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_UART_RX, GPIO_FUNC_NULL); +} + +void cdc_uart_task(void) { + // Consume uart fifo regardless even if not connected + uint rx_len = 0; + while (uart_is_readable(PINOUT_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) { + rx_buf[rx_len++] = uart_getc(PINOUT_UART_INTERFACE); + } + + if (tud_cdc_n_connected(CDC_N_UART)) { + // Do we have anything to display on the host's terminal? + if (rx_len) { + for (uint i = 0; i < rx_len; i++) { tud_cdc_n_write_char(CDC_N_UART, rx_buf[i]); } + tud_cdc_n_write_flush(CDC_N_UART); + } + + if (tud_cdc_n_available(CDC_N_UART)) { + // Is there any data from the host for us to tx + uint tx_len = tud_cdc_n_read(CDC_N_UART, tx_buf, sizeof(tx_buf)); + uart_write_blocking(PINOUT_UART_INTERFACE, tx_buf, tx_len); + } + } +} + +void cdc_uart_set_hwflow(bool enable) { uart_set_hw_flow(PINOUT_UART_INTERFACE, enable, enable); } + +void cdc_uart_set_baudrate(uint32_t brate) { uart_init(PINOUT_UART_INTERFACE, brate); } + diff --git a/bsp/rp2040/m_default/i2c_tinyusb.c b/bsp/rp2040/m_default/i2c_tinyusb.c new file mode 100644 index 0000000..d8b78e0 --- /dev/null +++ b/bsp/rp2040/m_default/i2c_tinyusb.c @@ -0,0 +1,461 @@ +// vim: set et: + +#include + +#include +#include +#include +#include +#include +#include + +#include "m_default/bsp-feature.h" +#include "m_default/pinout.h" + +#include "m_default/i2ctinyusb.h" + +static int delay = 10, delay2 = 5; + +// I2C bitbang reimpl because ugh, synopsys +// (mostly inspired by original I2CTinyUSB AVR firmware) +__attribute__((__always_inline__)) inline static void i2cio_set_sda(bool hi) { + if (hi) { + sio_hw->gpio_oe_clr = (1 << PINOUT_I2C_SDA); // SDA is input + // => pullup configured, so it'll go high + } else { + sio_hw->gpio_oe_set = (1 << PINOUT_I2C_SDA); // SDA is output + sio_hw->gpio_clr = (1 << PINOUT_I2C_SDA); // and drive it low + } +} +__attribute__((__always_inline__)) inline static bool i2cio_get_sda(void) { + return (sio_hw->gpio_in & (1 << PINOUT_I2C_SDA)) != 0; +} +__attribute__((__always_inline__)) inline static void i2cio_set_scl(bool hi) { + busy_wait_us_32(delay2); + sio_hw->gpio_oe_set = (1 << PINOUT_I2C_SCL); // SCL is output + if (hi) + sio_hw->gpio_set = (1 << PINOUT_I2C_SCL); // SCL is high + else + sio_hw->gpio_clr = (1 << PINOUT_I2C_SCL); // SCL is low + busy_wait_us_32(delay2); +} + +__attribute__((__always_inline__)) inline static void i2cio_scl_toggle(void) { + i2cio_set_scl(true); + i2cio_set_scl(false); +} + +static void __no_inline_not_in_flash_func(i2cio_start)(void) { // start condition + i2cio_set_sda(false); + i2cio_set_scl(false); +} +static void __no_inline_not_in_flash_func(i2cio_repstart)(void) { // repstart condition + i2cio_set_sda(true); + i2cio_set_scl(true); + + i2cio_set_sda(false); + i2cio_set_scl(false); +} +static void __no_inline_not_in_flash_func(i2cio_stop)(void) { // stop condition + i2cio_set_sda(false); + i2cio_set_scl(true); + i2cio_set_sda(true); +} + +static bool __no_inline_not_in_flash_func(i2cio_write7)( + uint8_t v) { // return value: acked? // needed for 10bitaddr xfers + for (int i = 6; i >= 0; --i) { + i2cio_set_sda((v & (1 << i)) != 0); + i2cio_scl_toggle(); + } + + i2cio_set_sda(true); + i2cio_set_scl(true); + + bool ack = !i2cio_get_sda(); + i2cio_set_scl(false); + + return ack; +} +static bool __no_inline_not_in_flash_func(i2cio_write8)(uint8_t v) { // return value: acked? + for (int i = 7; i >= 0; --i) { + i2cio_set_sda((v & (1 << i)) != 0); + i2cio_scl_toggle(); + } + + i2cio_set_sda(true); + i2cio_set_scl(true); + + bool ack = !i2cio_get_sda(); + i2cio_set_scl(false); + + return ack; +} +static uint8_t __no_inline_not_in_flash_func(i2cio_read8)(bool last) { + i2cio_set_sda(true); + i2cio_set_scl(false); + + uint8_t rv = 0; + for (int i = 7; i >= 0; --i) { + i2cio_set_scl(true); + bool c = i2cio_get_sda(); + rv <<= 1; + if (c) rv |= 1; + i2cio_set_scl(false); + } + + if (last) + i2cio_set_sda(true); + else + i2cio_set_sda(false); + + i2cio_scl_toggle(); + i2cio_set_sda(true); + + return rv; +} + +// replicating/rewriting some SDK functions because they don't do what I want +// so I'm making better ones + +static int __no_inline_not_in_flash_func(i2cex_probe_address)(uint16_t addr, bool a10bit) { + // I2C pins to SIO + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_SIO); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_SIO); + + int rv; + i2cio_start(); + + if (a10bit) { + // A10 magic higher 2 addr bits r/#w bit + uint8_t addr1 = 0x70 | (((addr >> 8) & 3) << 1) | 0, addr2 = addr & 0xff; + + if (i2cio_write7(addr1)) { + if (i2cio_write8(addr2)) + rv = 0; + else + rv = PICO_ERROR_GENERIC; + } else + rv = PICO_ERROR_GENERIC; + } else { + if (i2cio_write8((addr << 1) & 0xff)) + rv = 0; // acked: ok + else + rv = PICO_ERROR_GENERIC; // nak :/ + } + i2cio_stop(); + + // I2C back to I2C + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_I2C); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_I2C); + + return rv; +} + +inline static void i2cex_abort_xfer(i2c_inst_t* i2c) { +#if 1 + // may be bugged??? so doesnt do anything for now + (void)i2c; + return; +#else + // now do the abort + i2c->hw->enable = 1 /*| (1<<2)*/ | (1 << 1); + // wait for M_TX_ABRT irq + do { + /*if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + }*/ + tight_loop_contents(); + } while (/*!timeout &&*/ !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS)); + // reset irq + // if (!timeout) + (void)i2c->hw->clr_tx_abrt; +#endif +} + +static int i2cex_write_blocking_until(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + const uint8_t* src, size_t len, bool nostop, absolute_time_t until) { + timeout_state_t ts_; + + struct timeout_state* ts = &ts_; + + check_timeout_fn timeout_check = init_single_timeout_until(&ts_, until); + + if ((int)len < 0) return PICO_ERROR_GENERIC; + if (a10bit) { // addr too high + if (addr & ~(uint16_t)((1 << 10) - 1)) return PICO_ERROR_GENERIC; + } else if (addr & 0x80) + return PICO_ERROR_GENERIC; + + if (len == 0) return i2cex_probe_address(addr, a10bit); + + bool abort = false, timeout = false; + uint32_t abort_reason = 0; + int byte_ctr; + + i2c->hw->enable = 0; + // enable 10bit mode if requested + // clang-format off + hw_write_masked(&i2c->hw->con, I2C_IC_CON_IC_10BITADDR_MASTER_BITS, + (a10bit ? I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_10BITS + : I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_7BITS) + << I2C_IC_CON_IC_10BITADDR_MASTER_LSB); + // clang-format on + i2c->hw->tar = addr; + i2c->hw->enable = 1; + + for (byte_ctr = 0; byte_ctr < (int)len; ++byte_ctr) { + bool first = byte_ctr == 0, last = byte_ctr == (int)len - 1; + + i2c->hw->data_cmd = (bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB) + | (bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB) + | *src++; + + do { + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); + } while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_EMPTY_BITS)); + + if (!timeout) { + abort_reason = i2c->hw->tx_abrt_source; + if (abort_reason) { + (void)i2c->hw->clr_tx_abrt; + abort = true; + } + + if (abort || (last && !nostop)) { + do { + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); + // clang-format off + } while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_STOP_DET_BITS)); + // clang-format on + + if (!timeout) + (void)i2c->hw->clr_stop_det; + else + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(i2c); + } + } else + i2cex_abort_xfer(i2c); + + if (abort) break; + } + + int rval; + + if (abort) { + // clang-format off + const int addr_noack = I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK_BITS + | I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK_BITS; + // clang-format on + + if (timeout) + rval = PICO_ERROR_TIMEOUT; + else if (!abort_reason || (abort_reason & addr_noack)) + rval = PICO_ERROR_GENERIC; + else if (abort_reason & I2C_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK_BITS) + rval = byte_ctr; + else + rval = PICO_ERROR_GENERIC; + } else + rval = byte_ctr; + + i2c->restart_on_next = nostop; + return rval; +} +static int i2cex_read_blocking_until(i2c_inst_t* i2c, uint16_t addr, bool a10bit, uint8_t* dst, + size_t len, bool nostop, absolute_time_t until) { + timeout_state_t ts_; + struct timeout_state* ts = &ts_; + check_timeout_fn timeout_check = init_single_timeout_until(&ts_, until); + + if ((int)len < 0) return PICO_ERROR_GENERIC; + if (a10bit) { // addr too high + if (addr & ~(uint16_t)((1 << 10) - 1)) return PICO_ERROR_GENERIC; + } else if (addr & 0x80) + return PICO_ERROR_GENERIC; + + i2c->hw->enable = 0; + // enable 10bit mode if requested + hw_write_masked(&i2c->hw->con, I2C_IC_CON_IC_10BITADDR_MASTER_BITS, + (a10bit ? I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_10BITS + : I2C_IC_CON_IC_10BITADDR_MASTER_VALUE_ADDR_7BITS) + << I2C_IC_CON_IC_10BITADDR_MASTER_LSB); + i2c->hw->tar = addr; + i2c->hw->enable = 1; + + if (len == 0) return i2cex_probe_address(addr, a10bit); + + bool abort = false, timeout = false; + uint32_t abort_reason = 0; + int byte_ctr; + + for (byte_ctr = 0; byte_ctr < (int)len; ++byte_ctr) { + bool first = byte_ctr == 0; + bool last = byte_ctr == (int)len - 1; + + while (!i2c_get_write_available(i2c) && !abort) { + tight_loop_contents(); + // ? + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + } + + if (timeout) { + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(i2c); + } + if (abort) break; + + i2c->hw->data_cmd = bool_to_bit(first && i2c->restart_on_next) + << I2C_IC_DATA_CMD_RESTART_LSB | + bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB | + I2C_IC_DATA_CMD_CMD_BITS; // -> 1 for read + + do { + abort_reason = i2c->hw->tx_abrt_source; + abort = (bool)i2c->hw->clr_tx_abrt; + + if (timeout_check) { + timeout = timeout_check(ts); + abort |= timeout; + } + tight_loop_contents(); // ? + } while (!abort && !i2c_get_read_available(i2c)); + + if (timeout) { + // if we had a timeout, send an abort request to the hardware, + // so that the bus gets released + i2cex_abort_xfer(i2c); + } + if (abort) break; + + uint8_t v = (uint8_t)i2c->hw->data_cmd; + // printf("\ngot read %02x\n", v); + *dst++ = v; + } + + int rval; + + if (abort) { + // printf("\ngot abrt: "); + const int addr_noack = I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS | + I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK_BITS | + I2C_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK_BITS; + + if (timeout) { /*printf("timeout\n");*/ + rval = PICO_ERROR_TIMEOUT; + } else if (!abort_reason || (abort_reason & addr_noack)) { // printf("disconn\n"); + rval = PICO_ERROR_GENERIC; + } else { /*printf("unk\n");*/ + rval = PICO_ERROR_GENERIC; + } + } else + rval = byte_ctr; + + i2c->restart_on_next = nostop; + return rval; +} +static inline int i2cex_write_timeout_us(i2c_inst_t* i2c, uint16_t addr, bool a10bit, + const uint8_t* src, size_t len, bool nostop, uint32_t timeout_us) { + absolute_time_t t = make_timeout_time_us(timeout_us); + return i2cex_write_blocking_until(i2c, addr, a10bit, src, len, nostop, t); +} +static inline int i2cex_read_timeout_us(i2c_inst_t* i2c, uint16_t addr, bool a10bit, uint8_t* dst, + size_t len, bool nostop, uint32_t timeout_us) { + absolute_time_t t = make_timeout_time_us(timeout_us); + return i2cex_read_blocking_until(i2c, addr, a10bit, dst, len, nostop, t); +} + +__attribute__((__const__)) enum ki2c_funcs i2ctu_get_func(void) { + // TODO: SMBUS_EMUL_ALL => I2C_M_RECV_LEN + // TODO: maybe also PROTOCOL_MANGLING, NOSTART + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +void i2ctu_dev_init(void) { + // default to 100 kHz (SDK example default so should be ok) + delay = 10; + delay2 = 5; + i2c_init(PINOUT_I2C_DEV, 100 * 1000); + + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_I2C); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_I2C); + gpio_pull_up(PINOUT_I2C_SCL); + gpio_pull_up(PINOUT_I2C_SDA); + + bi_decl(bi_2pins_with_func(PINOUT_I2C_SCL, PINOUT_I2C_SDA, GPIO_FUNC_I2C)); +} +void i2ctu_dev_deinit(void) { + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_NULL); + gpio_disable_pulls(PINOUT_I2C_SCL); + gpio_disable_pulls(PINOUT_I2C_SDA); + + // default to 100 kHz (SDK example default so should be ok) + delay = 10; + delay2 = 5; + i2c_deinit(PINOUT_I2C_DEV); +} + +uint32_t i2ctu_dev_set_freq(uint32_t freq, uint32_t us) { + delay = us; + delay2 = us >> 1; + if (!delay2) delay2 = 1; + + return i2c_set_baudrate(PINOUT_I2C_DEV, freq); +} + +enum itu_status i2ctu_dev_write(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, + const uint8_t* buf, size_t len) { + bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END); + bool bit10 = flags & I2C_M_TEN; + + /*if (len == 0) { + // do a read, that's less hazardous + uint8_t stuff = 0; + int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, bit10, &stuff, 1, + nostop, 1000*1000); + if (rv < 0) return ITU_STATUS_ADDR_NAK; + return ITU_STATUS_ADDR_ACK; + } else*/ + { + int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 1000 * 1000); + if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; + return ITU_STATUS_ADDR_ACK; + } +} +enum itu_status i2ctu_dev_read(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, + uint8_t* buf, size_t len) { + bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END); + bool bit10 = flags & I2C_M_TEN; + + /*if (len == 0) { + uint8_t stuff = 0; + int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, bit10, &stuff, 1, + nostop, 1000*1000); + if (rv < 0) return ITU_STATUS_ADDR_NAK; + return ITU_STATUS_ADDR_ACK; + } else*/ + { + int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 1000 * 1000); + // printf("p le rv=%d buf=%02x ", rv, buf[0]); + if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; + return ITU_STATUS_ADDR_ACK; + } +} + diff --git a/bsp/rp2040/m_default/pinout.h b/bsp/rp2040/m_default/pinout.h new file mode 100644 index 0000000..639f88a --- /dev/null +++ b/bsp/rp2040/m_default/pinout.h @@ -0,0 +1,51 @@ +// vim: set et: + +#ifndef PINOUT_H_ +#define PINOUT_H_ + +// UART config +#define PINOUT_UART_TX 4 +#define PINOUT_UART_RX 5 +#define PINOUT_UART_CTS 10 +#define PINOUT_UART_RTS 11 +#define PINOUT_UART_INTERFACE uart1 +#define PINOUT_UART_BAUDRATE 115200 + +// JTAG config +#define PINOUT_JTAG_TCK 2 // == SWCLK +#define PINOUT_JTAG_TMS 3 // == SWDIO +#define PINOUT_JTAG_TDI 6 +#define PINOUT_JTAG_TDO 7 +#define PINOUT_JTAG_nTRST 8 +#define PINOUT_JTAG_nRESET 9 + +// SPI config +#define PINOUT_SPI_DEV spi1 +#define PINOUT_SPI_SCLK 14 +#define PINOUT_SPI_MOSI 15 +#define PINOUT_SPI_MISO 12 +#define PINOUT_SPI_nCS 13 + +// I2C config +#define PINOUT_I2C_DEV i2c0 +#define PINOUT_I2C_SCL 21 +#define PINOUT_I2C_SDA 20 + +// LED config + +// you can change these two as you like +#define PINOUT_LED_CONNECTED 1 +#define PINOUT_LED_RUNNING 0 + +#ifndef PINOUT_LED +#ifndef PICO_DEFAULT_LED_PIN +#error "PICO_DEFAULT_LED_PIN is not defined, run PICOPROBE_LED= cmake" +#elif PICO_DEFAULT_LED_PIN == -1 +#error "PICO_DEFAULT_LED_PIN is defined as -1, run PICOPROBE_LED= cmake" +#else +#define PINOUT_LED PICO_DEFAULT_LED_PIN +#endif +#endif /* PICOPROBE_LED */ + +#endif + diff --git a/bsp/rp2040/m_default/spi_serprog.c b/bsp/rp2040/m_default/spi_serprog.c new file mode 100644 index 0000000..44147a1 --- /dev/null +++ b/bsp/rp2040/m_default/spi_serprog.c @@ -0,0 +1,86 @@ +// vim: set et: + +#include + +#include +#include +#include + +#include "m_default/bsp-feature.h" +#include "m_default/pinout.h" + +#include "m_default/serprog.h" + +static bool cs_asserted; + +void sp_spi_init(void) { + cs_asserted = false; + + spi_init(PINOUT_SPI_DEV, 512 * 1000); // default to 512 kHz + + gpio_set_function(PINOUT_SPI_MISO, GPIO_FUNC_SPI); + gpio_set_function(PINOUT_SPI_MOSI, GPIO_FUNC_SPI); + gpio_set_function(PINOUT_SPI_SCLK, GPIO_FUNC_SPI); + + // gpio_set_function(PINOUT_SPI_nCS, GPIO_FUNC_SIO); + gpio_init(PINOUT_SPI_nCS); + gpio_put(PINOUT_SPI_nCS, 1); + gpio_set_dir(PINOUT_SPI_nCS, GPIO_OUT); + + bi_decl(bi_3pins_with_func(PINOUT_SPI_MISO, PINOUT_SPI_MOSI, PINOUT_SPI_SCLK, GPIO_FUNC_SPI)); + bi_decl(bi_1pin_with_name(PINOUT_SPI_nCS, "SPI #CS")); +} +void sp_spi_deinit(void) { + cs_asserted = false; + + gpio_set_function(PINOUT_SPI_MISO, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_SPI_MOSI, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_SPI_SCLK, GPIO_FUNC_NULL); + gpio_set_function(PINOUT_SPI_nCS , GPIO_FUNC_NULL); + + gpio_set_dir(PINOUT_SPI_nCS, GPIO_IN); + gpio_disable_pulls(PINOUT_SPI_nCS); + + spi_deinit(PINOUT_SPI_DEV); +} +uint32_t __not_in_flash_func(sp_spi_set_freq)(uint32_t freq_wanted) { + return spi_set_baudrate(PINOUT_SPI_DEV, freq_wanted); +} +void __not_in_flash_func(sp_spi_cs_deselect)(void) { + asm volatile("nop\nnop\nnop"); // idk if this is needed + gpio_put(PINOUT_SPI_nCS, 1); + asm volatile("nop\nnop\nnop"); // idk if this is needed + cs_asserted = false; +} +void __not_in_flash_func(sp_spi_cs_select)(void) { + asm volatile("nop\nnop\nnop"); // idk if this is needed + gpio_put(PINOUT_SPI_nCS, 0); + asm volatile("nop\nnop\nnop"); // idk if this is needed + cs_asserted = true; +} + +void __not_in_flash_func(sp_spi_op_begin)(void) { + // sp_spi_cs_select(); + if (!cs_asserted) { + asm volatile("nop\nnop\nnop"); // idk if this is needed + gpio_put(PINOUT_SPI_nCS, 0); + asm volatile("nop\nnop\nnop"); // idk if this is needed + } +} +void __not_in_flash_func(sp_spi_op_end)(void) { + // sp_spi_cs_deselect(); + if (!cs_asserted) { // YES, this condition is the intended one! + asm volatile("nop\nnop\nnop"); // idk if this is needed + gpio_put(PINOUT_SPI_nCS, 1); + asm volatile("nop\nnop\nnop"); // idk if this is needed + } +} + +// TODO: use dma? +void __not_in_flash_func(sp_spi_op_write)(uint32_t write_len, const uint8_t* write_data) { + spi_write_blocking(PINOUT_SPI_DEV, write_data, write_len); +} +void __not_in_flash_func(sp_spi_op_read)(uint32_t read_len, uint8_t* read_data) { + spi_read_blocking(PINOUT_SPI_DEV, 0, read_data, read_len); +} + diff --git a/bsp/rp2040/m_default/tempsensor.c b/bsp/rp2040/m_default/tempsensor.c new file mode 100644 index 0000000..602cdc9 --- /dev/null +++ b/bsp/rp2040/m_default/tempsensor.c @@ -0,0 +1,59 @@ +// vim: set et: + +#include "m_default/tempsensor.h" + +#include +#include + +#define T_SLOPE (-0.001721f) +#define T_BIAS (0.706f) +#define V_MAX (3.3f) +#define D_RANGE (4096) +#define T_OFF (27) + +// convert float to x.4 fixed format +#define float2fix(x) (int)((x) * (1 << 4)) + +// convert x.4 fixed to 8.4 fixed +__attribute__((__const__)) inline static int16_t trunc_8fix4(int fix) { + // clang-format off + if (fix > 4095) fix = 4095; + if (fix < -4096) fix = -4096; + // clang-format on + return fix; +} + +void tempsense_dev_init(void) { + adc_init(); + adc_set_temp_sensor_enabled(true); +} +void tempsense_dev_deinit(void) { + adc_set_temp_sensor_enabled(false); + // call init, as it resets the ADC control register + adc_init(); + // we still need to disable it again afterwards, though + reset_block(RESETS_RESET_ADC_BITS); +} +// 8.4 +int16_t tempsense_dev_get_temp(void) { + adc_select_input(4); // select temp sensor + uint16_t result = adc_read(); + + float voltage = result * (V_MAX / D_RANGE); + + float tempf = T_OFF + (voltage - T_BIAS) / T_SLOPE; + + // FIXME: use fixed point instead! but something's wrong with the formula below + /*int temperature = float2fix(T_OFF - T_BIAS / T_SLOPE) + + (int)result * float2fix(V_MAX / (D_RANGE * T_SLOPE));*/ + + return trunc_8fix4(/*temperature*/ float2fix(tempf)); +} + +// RP2040 absolute min/max are -20/85 +// clang-format off +int16_t tempsense_dev_get_lower(void) { return trunc_8fix4(float2fix(-15)); } +int16_t tempsense_dev_get_upper(void) { return trunc_8fix4(float2fix( 75)); } +int16_t tempsense_dev_get_crit (void) { return trunc_8fix4(float2fix( 80)); } +// clang-format on + diff --git a/bsp/rp2040/protocfg.h b/bsp/rp2040/protocfg.h deleted file mode 100644 index f9f852d..0000000 --- a/bsp/rp2040/protocfg.h +++ /dev/null @@ -1,15 +0,0 @@ -// vim: set et: - -#ifndef PROTOCFG_H_ -#define PROTOCFG_H_ - -/*#define USB_VID 0x2e8a*/ /* Raspberry Pi */ -#define USB_VID 0xcafe /* TinyUSB */ -/*#define USB_VID 0x1209*/ /* Generic */ -/*#define USB_VID 0x1d50*/ /* OpenMoko */ -#define USB_PID 0x1312 - -// TODO: other RP2040 boards -#define INFO_BOARDNAME "RP2040 Pico" - -#endif diff --git a/src/info.h b/src/info.h new file mode 100644 index 0000000..29013f0 --- /dev/null +++ b/src/info.h @@ -0,0 +1,12 @@ + +#ifndef INFO_H_ +#define INFO_H_ + +#include "bsp-info.h" + +#define INFO_MANUFACTURER "BLAHAJ CTF" +#define INFO_PRODUCT_BARE "Dragnbus" +#define INFO_PRODUCT(board) "Dragnbus (" board ")" + +#endif + diff --git a/src/m_default/0def.c b/src/m_default/0def.c deleted file mode 100644 index 85db793..0000000 --- a/src/m_default/0def.c +++ /dev/null @@ -1,111 +0,0 @@ -// vim: set et: - -#include - -#include "mode.h" -#include "vnd_cfg.h" - -static void enter_cb(void) { - // TODO: init hw -} -static void leave_cb(void) { - // TODO: deinit hw -} - -static void task_cb(void) { - // TODO: do stuff -} - -static void handle_cmd_cb(uint8_t cmd) { - uint8_t resp = 0; - - switch (cmd) { - case mode_cmd_get_features: - vnd_cfg_write_resp(cfg_resp_ok, 1, &resp); - break; - default: - vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL); - break; - } -} - -enum { - STRID_LANGID = 0, - STRID_MANUFACTURER, - STRID_PRODUCT, - STRID_SERIAL, - - STRID_CONFIG, - - STRID_IF_VND_CFG, - STRID_IF_CDC_STDIO, -}; -enum { - ITF_NUM_VND_CFG, -//#ifdef USE_USBCDC_FOR_STDIO - ITF_NUM_CDC_STDIO_COM, - ITF_NUM_CDC_STDIO_DATA, -//#endif - - ITF_NUM__TOTAL -}; -enum { - CONFIG_TOTAL_LEN - = TUD_CONFIG_DESC_LEN - + TUD_VENDOR_DESC_LEN -//#ifdef USE_USBCDC_FOR_STDIO - + TUD_CDC_DESC_LEN -//#endif -}; - -#define EPNUM_CDC_STDIO_OUT 0x03 -#define EPNUM_CDC_STDIO_IN 0x83 -#define EPNUM_CDC_STDIO_NOTIF 0x84 - -// TODO: are these ok numbers? -#define EPNUM_VND_CFG_OUT 0x02 -#define EPNUM_VND_CFG_IN 0x82 - -// clang-format off -static const uint8_t desc_configuration[] = { - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN, - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT, - EPNUM_VND_CFG_IN, 64), - -//#ifdef USE_USBCDC_FOR_STDIO - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF, 64, - EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, 64), -//#endif -}; -static const char* string_desc_arr[] = { - /*[STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409) - [STRID_MANUFACTURER] = "BLAHAJ CTF", // Manufacturer - [STRID_PRODUCT] = "Dragnbus (RP2040 Pico)", // Product*/ - NULL, - - [STRID_CONFIG] = "Configuration descriptor", - // max string length check: ||||||||||||||||||||||||||||||| - [STRID_IF_VND_CFG ] = "Device cfg/ctl interface", - [STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)", -}; -// clang-format on - -extern struct mode m_01_default; -// clang-format off -struct mode m_01_default = { - .name = "Default mode with misc features", - .version = 0x0010, - .n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), - - .usb_desc = desc_configuration, - .string_desc = string_desc_arr, - - .enter = enter_cb, - .leave = leave_cb, - .task = task_cb, - .handle_cmd = handle_cmd_cb, -}; -// clang-format on - diff --git a/src/m_default/_default.c b/src/m_default/_default.c new file mode 100644 index 0000000..f6f3bd2 --- /dev/null +++ b/src/m_default/_default.c @@ -0,0 +1,326 @@ +// vim: set et: + +#include + +#include "mode.h" +#include "thread.h" +#include "vnd_cfg.h" + +#include "m_default/bsp-feature.h" + +/* CMSIS-DAP */ +#include "DAP_config.h" /* ARM code *assumes* this is included prior to DAP.h */ +#include "DAP.h" + +/* I2C-Tiny-USB */ +#include "m_default/i2ctinyusb.h" + +/* CDC UART */ +#include "m_default/cdc.h" + +/* CDC-Serprog */ +#include "m_default/serprog.h" + +#ifdef DBOARD_HAS_UART +static cothread_t uartthread; +static uint8_t uartstack[THREAD_STACK_SIZE]; + +static void uart_thread_fn(void) { + cdc_uart_init(); + thread_yield(); + while (1) { + cdc_uart_task(); + thread_yield(); + } +} +#endif + +#ifdef DBOARD_HAS_SERPROG +static cothread_t serprogthread; +static uint8_t serprogstack[THREAD_STACK_SIZE]; + +static void serprog_thread_fn(void) { + cdc_serprog_init(); + thread_yield(); + while (1) { + cdc_serprog_task(); + thread_yield(); + } +} +#endif + +void stdio_usb_set_itf_num(int itf); // TODO: move to a header! + +static void enter_cb(void) { + stdio_usb_set_itf_num(CDC_N_STDIO); + + // TODO: CMSISDAP? +#ifdef DBOARD_HAS_I2C + i2ctu_init(); +#endif +#ifdef DBOARD_HAS_UART + uartthread = co_derive(uartstack, sizeof uartstack, uart_thread_fn); + thread_enter(uartthread); // will call cdc_uart_init() on correct thread +#endif +#ifdef DBOARD_HAS_SERPROG + serprogthread = co_derive(serprogstack, sizeof serprogstack, serprog_thread_fn); + thread_enter(serprogthread); // will call cdc_serprog_init() on correct thread +#endif +} +static void leave_cb(void) { + // TODO: CMSISDAP? +#ifdef DBOARD_HAS_I2C + i2ctu_deinit(); +#endif +#ifdef DBOARD_HAS_UART + cdc_uart_deinit(); +#endif +#ifdef DBOARD_HAS_SERPROG + cdc_serprog_deinit(); +#endif +} + +static void task_cb(void) { +#ifdef DBOARD_HAS_UART + tud_task(); + thread_enter(uartthread); +#endif +#ifdef DBOARD_HAS_SERPROG + tud_task(); + thread_enter(serprogthread); +#endif +} + +static void handle_cmd_cb(uint8_t cmd) { + uint8_t resp = 0; + + // TODO: tempsensor control commands! + switch (cmd) { + case mode_cmd_get_features: + vnd_cfg_write_resp(cfg_resp_ok, 1, &resp); + break; + default: + vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL); + break; + } +} + +#define TUD_I2CTINYUSB_LEN (9) +#define TUD_I2CTINYUSB_DESCRIPTOR(_itfnum, _stridx) \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, 0, 0, 0, _stridx \ + + +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + + STRID_CONFIG, + + STRID_IF_VND_CFG, + STRID_IF_HID_CMSISDAP, + STRID_IF_VND_I2CTINYUSB, + STRID_IF_CDC_UART, + STRID_IF_CDC_SERPROG, + STRID_IF_CDC_STDIO, +}; +enum { +#if CFG_TUD_VENDOR > 0 + ITF_NUM_VND_CFG, +#endif +#ifdef DBOARD_HAS_I2C + ITF_NUM_VND_I2CTINYUSB, +#endif +#ifdef DBOARD_HAS_CMSISDAP + ITF_NUM_HID_CMSISDAP, +#endif +#ifdef DBOARD_HAS_UART + ITF_NUM_CDC_UART_COM, + ITF_NUM_CDC_UART_DATA, +#endif +#ifdef DBOARD_HAS_SERPROG + ITF_NUM_CDC_SERPROG_COM, + ITF_NUM_CDC_SERPROG_DATA, +#endif +#ifdef USE_USBCDC_FOR_STDIO + ITF_NUM_CDC_STDIO_COM, + ITF_NUM_CDC_STDIO_DATA, +#endif + + ITF_NUM__TOTAL +}; +enum { + CONFIG_TOTAL_LEN + = TUD_CONFIG_DESC_LEN +#if CFG_TUD_VENDOR > 0 + + TUD_VENDOR_DESC_LEN +#endif +#ifdef DBOARD_HAS_I2C + + TUD_I2CTINYUSB_LEN +#endif +#ifdef DBOARD_HAS_CMSISDAP + + TUD_HID_INOUT_DESC_LEN +#endif +#ifdef DBOARD_HAS_UART + + TUD_CDC_DESC_LEN +#endif +#ifdef DBOARD_HAS_SERPROG + + TUD_CDC_DESC_LEN +#endif +#ifdef USE_USBCDC_FOR_STDIO + + TUD_CDC_DESC_LEN +#endif +}; + +#define EPNUM_VND_CFG_OUT 0x01 +#define EPNUM_VND_CFG_IN 0x81 +#define EPNUM_HID_CMSISDAP 0x02 +#define EPNUM_CDC_UART_OUT 0x03 +#define EPNUM_CDC_UART_IN 0x83 +#define EPNUM_CDC_UART_NOTIF 0x84 +#define EPNUM_CDC_SERPROG_OUT 0x05 +#define EPNUM_CDC_SERPROG_IN 0x85 +#define EPNUM_CDC_SERPROG_NOTIF 0x86 +#define EPNUM_CDC_STDIO_OUT 0x07 +#define EPNUM_CDC_STDIO_IN 0x87 +#define EPNUM_CDC_STDIO_NOTIF 0x88 + +/*#define EPNUM_CDC_UART_OUT 0x02 +#define EPNUM_CDC_UART_IN 0x82 +#define EPNUM_CDC_UART_NOTIF 0x83 +#define EPNUM_HID_CMSISDAP 0x04 +#define EPNUM_CDC_SERPROG_OUT 0x05 +#define EPNUM_CDC_SERPROG_IN 0x85 +#define EPNUM_CDC_SERPROG_NOTIF 0x86 +#define EPNUM_CDC_STDIO_OUT 0x07 +#define EPNUM_CDC_STDIO_IN 0x87 +#define EPNUM_CDC_STDIO_NOTIF 0x88*/ + +// clang-format off +#if CFG_TUD_HID > 0 +static const uint8_t desc_hid_report[] = { // ugh + TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE) +}; +#endif +// TODO: replace magic 64s by actual buffer size macros +static const uint8_t desc_configuration[] = { + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN, + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + +#if CFG_TUD_VENDOR > 0 + TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT, + EPNUM_VND_CFG_IN, 64), +#endif + +#ifdef DBOARD_HAS_I2C + TUD_I2CTINYUSB_DESCRIPTOR(ITF_NUM_VND_I2CTINYUSB, STRID_IF_VND_I2CTINYUSB), +#endif + +#ifdef DBOARD_HAS_CMSISDAP + TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_CMSISDAP, STRID_IF_HID_CMSISDAP, + 0 /*HID_PROTOCOL_NONE*/, sizeof(desc_hid_report), EPNUM_HID_CMSISDAP, + 0x80 | (EPNUM_HID_CMSISDAP + 0), CFG_TUD_HID_EP_BUFSIZE, 1), +#endif + +#ifdef DBOARD_HAS_UART + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_UART_COM, STRID_IF_CDC_UART, EPNUM_CDC_UART_NOTIF, 64, + EPNUM_CDC_UART_OUT, EPNUM_CDC_UART_IN, 64), +#endif + +#ifdef DBOARD_HAS_SERPROG + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_SERPROG_COM, STRID_IF_CDC_SERPROG, EPNUM_CDC_SERPROG_NOTIF, + 64, EPNUM_CDC_SERPROG_OUT, EPNUM_CDC_SERPROG_IN, 64), +#endif + +#ifdef USE_USBCDC_FOR_STDIO + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF, 64, + EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, 64), +#endif +}; +static const char* string_desc_arr[] = { + /*[STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409) + [STRID_MANUFACTURER] = "BLAHAJ CTF", // Manufacturer + [STRID_PRODUCT] = "Dragnbus (RP2040 Pico)", // Product*/ + NULL, + + [STRID_CONFIG] = "Configuration descriptor", + // max string length check: ||||||||||||||||||||||||||||||| + [STRID_IF_VND_CFG ] = "Device cfg/ctl interface", + [STRID_IF_HID_CMSISDAP] = "CMSIS-DAP HID interface", + [STRID_IF_VND_I2CTINYUSB] = "I2C-Tiny-USB interface", + [STRID_IF_CDC_UART] = "UART CDC interface", + [STRID_IF_CDC_SERPROG] = "Serprog CDC interface", + [STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)", +}; +// clang-format on + +#ifdef DBOARD_HAS_CMSISDAP +static const uint8_t* my_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + + return desc_hid_report; +} +/*static uint16_t my_hid_get_report_cb(uint8_t instance, uint8_t report_id, + hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + // TODO not implemented + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + + return 0; +}*/ + +static void my_hid_set_report_cb(uint8_t instance, uint8_t report_id, + hid_report_type_t report_type, uint8_t const* rx_buffer, uint16_t bufsize) { + static uint8_t tx_buffer[CFG_TUD_HID_EP_BUFSIZE]; + uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize); + + (void)instance; + (void)report_id; + (void)report_type; + + DAP_ProcessCommand(rx_buffer, tx_buffer); + + tud_hid_report(0, tx_buffer, response_size); +} +#endif + +#ifdef DBOARD_HAS_I2CTINYUSB +static bool my_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, + tusb_control_request_t const* req) { + return i2ctu_ctl_req(rhport, ep_addr, req); +} +#endif + +extern struct mode m_01_default; +// clang-format off +struct mode m_01_default = { + .name = "Default mode with misc features", + .version = 0x0010, + .n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), + + .usb_desc = desc_configuration, + .string_desc = string_desc_arr, + + .enter = enter_cb, + .leave = leave_cb, + .task = task_cb, + .handle_cmd = handle_cmd_cb, + +#if defined(DBOARD_HAS_CMSISDAP) && CFG_TUD_HID > 0 +#if 0 + .tud_hid_get_report_cb = my_hid_get_report_cb, +#endif + .tud_hid_set_report_cb = my_hid_set_report_cb, + .tud_hid_descriptor_report_cb = my_hid_descriptor_report_cb, +#endif + +#if defined(DBOARD_HAS_I2CTINYUSB) + .tud_vendor_control_xfer_cb = i2ctu_ctl_req, +#endif +}; +// clang-format on + diff --git a/src/m_default/cdc.h b/src/m_default/cdc.h new file mode 100644 index 0000000..17a8d7e --- /dev/null +++ b/src/m_default/cdc.h @@ -0,0 +1,15 @@ + +#ifndef CDC_H_ +#define CDC_H_ + +#include "m_default/bsp-feature.h" + +/* BSP function prototypes for various USB-CDC interfaces */ +#ifdef DBOARD_HAS_UART +void cdc_uart_init(void); +void cdc_uart_deinit(void); +void cdc_uart_task(void); +#endif + +#endif + diff --git a/src/m_default/cdc_serprog.c b/src/m_default/cdc_serprog.c new file mode 100644 index 0000000..ccd481d --- /dev/null +++ b/src/m_default/cdc_serprog.c @@ -0,0 +1,223 @@ +// vim: set et: + +#include + +#include + +#include "m_default/bsp-feature.h" + +#ifdef DBOARD_HAS_SERPROG + +#include "info.h" +#include "util.h" +#include "thread.h" + +#include "serprog.h" + +// clang-format off +static const uint8_t serprog_cmdmap[32] = { + 0x3f, // cmd 00..05 not 0x06 (Q_CHIPSIZE) and 0x07 (Q_OPBUF), as this is a SPI-only device + 0x01, // only cmd 08 + 0x1f, // cmd 10..15 supported + 0, // rest is 0 +}; +// clang-format on +static const char serprog_pgmname[16] = INFO_PRODUCT_BARE; + +static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE]; +static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE]; + +static uint32_t rxavail, rxpos; + +void cdc_serprog_init(void) { + rxavail = 0; + rxpos = 0; + + sp_spi_init(); +} +void cdc_serprog_deinit(void) { + sp_spi_deinit(); + + rxavail = 0; + rxpos = 0; +} + +static uint8_t read_byte(void) { + while (rxavail <= 0) { + if (!tud_cdc_n_connected(CDC_N_SERPROG) || !tud_cdc_n_available(CDC_N_SERPROG)) { + thread_yield(); + continue; + } + + rxpos = 0; + rxavail = tud_cdc_n_read(CDC_N_SERPROG, rx_buf, sizeof rx_buf); + + if (rxavail == 0) thread_yield(); + } + + uint8_t rv = rx_buf[rxpos]; + ++rxpos; + --rxavail; + return rv; +} + +static void handle_cmd(void) { + uint32_t nresp = 0; + + uint8_t cmd = read_byte(); + switch (cmd) { + case S_CMD_NOP: + tx_buf[0] = S_ACK; + nresp = 1; + break; + case S_CMD_SYNCNOP: + tx_buf[0] = S_NAK; + tx_buf[1] = S_ACK; + nresp = 2; + break; + case S_CMD_Q_IFACE: + tx_buf[0] = S_ACK; + tx_buf[1] = SERPROG_IFACE_VERSION & 0xff; + tx_buf[2] = (SERPROG_IFACE_VERSION >> 8) & 0xff; + nresp = 3; + break; + case S_CMD_Q_CMDMAP: + tx_buf[0] = S_ACK; + memcpy(&tx_buf[1], serprog_cmdmap, sizeof serprog_cmdmap); + nresp = sizeof(serprog_cmdmap) + 1; + break; + case S_CMD_Q_PGMNAME: + tx_buf[0] = S_ACK; + memcpy(&tx_buf[1], serprog_pgmname, sizeof serprog_pgmname); + nresp = sizeof(serprog_pgmname) + 1; + break; + case S_CMD_Q_SERBUF: + tx_buf[0] = S_ACK; + tx_buf[1] = sizeof(rx_buf) & 0xff; + tx_buf[2] = (sizeof(rx_buf) >> 8) & 0xff; + nresp = 3; + break; + case S_CMD_Q_BUSTYPE: + tx_buf[0] = S_ACK; + tx_buf[1] = 1 << 3; // SPI only + nresp = 2; + break; + case S_CMD_Q_WRNMAXLEN: + tx_buf[0] = S_ACK; + tx_buf[1] = (sizeof(tx_buf) - 1) & 0xff; + tx_buf[2] = ((sizeof(tx_buf) - 1) >> 8) & 0xff; + tx_buf[3] = ((sizeof(tx_buf) - 1) >> 16) & 0xff; + nresp = 4; + break; + case S_CMD_Q_RDNMAXLEN: + tx_buf[0] = S_ACK; + tx_buf[1] = (sizeof(rx_buf) - 1) & 0xff; + tx_buf[2] = ((sizeof(rx_buf) - 1) >> 8) & 0xff; + tx_buf[3] = ((sizeof(rx_buf) - 1) >> 16) & 0xff; + nresp = 4; + break; + case S_CMD_S_BUSTYPE: + if (read_byte() /* bus type to set */ == (1 << 3)) { + tx_buf[0] = S_ACK; + } else { + tx_buf[0] = S_NAK; + } + nresp = 1; + break; + + case S_CMD_SPIOP: { + uint32_t slen, rlen; + + // clang-format off + slen = (uint32_t)read_byte(); + slen |= (uint32_t)read_byte() << 8; + slen |= (uint32_t)read_byte() << 16; + rlen = (uint32_t)read_byte(); + rlen |= (uint32_t)read_byte() << 8; + rlen |= (uint32_t)read_byte() << 16; + // clang-format on + + sp_spi_op_begin(); + size_t this_batch; + + // 1. write slen data bytes + // we're going to use the tx buf for all operations here + while (slen > 0) { + this_batch = sizeof(tx_buf); + if (this_batch > slen) this_batch = slen; + + for (size_t i = 0; i < this_batch; ++i) tx_buf[i] = read_byte(); + sp_spi_op_write(this_batch, tx_buf); + + slen -= this_batch; + } + + // 2. write data + // first, do a batch of 63, because we also need to send an ACK byte + this_batch = sizeof(tx_buf) - 1; + if (this_batch > rlen) this_batch = rlen; + sp_spi_op_read(this_batch, &tx_buf[1]); + tx_buf[0] = S_ACK; + tud_cdc_n_write(CDC_N_SERPROG, tx_buf, this_batch + 1); + rlen -= this_batch; + + // now do in batches of 64 + while (rlen > 0) { + this_batch = sizeof(tx_buf); + if (this_batch > rlen) this_batch = rlen; + + sp_spi_op_read(this_batch, tx_buf); + tud_cdc_n_write(CDC_N_SERPROG, tx_buf, this_batch); + + rlen -= this_batch; + } + tud_cdc_n_write_flush(CDC_N_SERPROG); + + // that's it! + sp_spi_op_end(); + nresp = 0; // we sent our own response manually + } break; + case S_CMD_S_SPI_FREQ: { + uint32_t freq; + // clang-format off + freq = (uint32_t)read_byte(); + freq |= (uint32_t)read_byte() << 8; + freq |= (uint32_t)read_byte() << 16; + freq |= (uint32_t)read_byte() << 24; + // clang-format on + + uint32_t nfreq = sp_spi_set_freq(freq); + + tx_buf[0] = S_ACK; + tx_buf[1] = nfreq & 0xff; + tx_buf[2] = (nfreq >> 8) & 0xff; + tx_buf[3] = (nfreq >> 16) & 0xff; + tx_buf[4] = (nfreq >> 24) & 0xff; + nresp = 5; + } break; + case S_CMD_S_PINSTATE: { + if (read_byte() == 0) + sp_spi_cs_deselect(); + else + sp_spi_cs_select(); + + tx_buf[0] = S_ACK; + nresp = 1; + } break; + + default: + tx_buf[0] = S_NAK; + nresp = 1; + break; + } + + if (nresp > 0) { + tud_cdc_n_write(CDC_N_SERPROG, tx_buf, nresp); + tud_cdc_n_write_flush(CDC_N_SERPROG); + } +} + +void cdc_serprog_task(void) { handle_cmd(); } + +#endif /* DBOARD_HAS_SERPROG */ + diff --git a/src/m_default/i2ctinyusb.h b/src/m_default/i2ctinyusb.h new file mode 100644 index 0000000..fe2fd53 --- /dev/null +++ b/src/m_default/i2ctinyusb.h @@ -0,0 +1,111 @@ +// vim: set et: + +#ifndef I2CTINYUSB_H_ +#define I2CTINYUSB_H_ + +#include +#include +#include + +#include + +#include "m_default/bsp-feature.h" + +enum itu_command { + ITU_CMD_ECHO = 0, + ITU_CMD_GET_FUNC = 1, + ITU_CMD_SET_DELAY = 2, + ITU_CMD_GET_STATUS = 3, + + ITU_CMD_I2C_IO_BEGIN_F = (1 << 0), + ITU_CMD_I2C_IO_END_F = (1 << 1), + ITU_CMD_I2C_IO_DIR_MASK = ITU_CMD_I2C_IO_BEGIN_F | ITU_CMD_I2C_IO_END_F, + + ITU_CMD_I2C_IO = 4, + ITU_CMD_I2C_IO_BEGIN = 4 | ITU_CMD_I2C_IO_BEGIN_F, + ITU_CMD_I2C_IO_END = 4 | ITU_CMD_I2C_IO_END_F, + ITU_CMD_I2C_IO_BEGINEND = 4 | ITU_CMD_I2C_IO_BEGIN_F | ITU_CMD_I2C_IO_END_F, +}; + +enum itu_status { ITU_STATUS_IDLE = 0, ITU_STATUS_ADDR_ACK = 1, ITU_STATUS_ADDR_NAK = 2 }; + +// these two are lifted straight from the linux kernel, lmao +enum ki2c_flags { + I2C_M_RD = 0x0001, /* guaranteed to be 0x0001! */ + I2C_M_TEN = 0x0010, /* use only if I2C_FUNC_10BIT_ADDR */ + I2C_M_DMA_SAFE = 0x0200, /* use only in kernel space */ + I2C_M_RECV_LEN = 0x0400, /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */ + I2C_M_NO_RD_ACK = 0x0800, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_IGNORE_NAK = 0x1000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_REV_DIR_ADDR = 0x2000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_NOSTART = 0x4000, /* use only if I2C_FUNC_NOSTART */ + I2C_M_STOP = 0x8000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ +}; + +enum ki2c_funcs { + I2C_FUNC_I2C = 0x00000001, + I2C_FUNC_10BIT_ADDR = 0x00000002, /* required for I2C_M_TEN */ + I2C_FUNC_PROTOCOL_MANGLING = 0x00000004, /* required for I2C_M_IGNORE_NAK etc. */ + I2C_FUNC_SMBUS_PEC = 0x00000008, + I2C_FUNC_NOSTART = 0x00000010, /* required for I2C_M_NOSTART */ + I2C_FUNC_SLAVE = 0x00000020, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL = 0x00008000, /* SMBus 2.0 or later */ + I2C_FUNC_SMBUS_QUICK = 0x00010000, + I2C_FUNC_SMBUS_READ_BYTE = 0x00020000, + I2C_FUNC_SMBUS_WRITE_BYTE = 0x00040000, + I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x00080000, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x00100000, + I2C_FUNC_SMBUS_READ_WORD_DATA = 0x00200000, + I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000, + I2C_FUNC_SMBUS_PROC_CALL = 0x00800000, + I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000, /* required for I2C_M_RECV_LEN */ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000, + I2C_FUNC_SMBUS_READ_I2C_BLOCK = 0x04000000, /* I2C-like block xfer */ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = 0x08000000, /* w/ 1-byte reg. addr. */ + I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 = 0x10000000, /* I2C-like block xfer */ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 = 0x20000000, /* w/ 2-byte reg. addr. */ + I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC = 0x40000000, /* SMBus 2.0 or later */ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC = 0x80000000, /* SMBus 2.0 or later */ + + I2C_FUNC_SMBUS_BYTE = (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE), + I2C_FUNC_SMBUS_BYTE_DATA = (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA), + I2C_FUNC_SMBUS_WORD_DATA = (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA), + I2C_FUNC_SMBUS_BLOCK_DATA = (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA), + I2C_FUNC_SMBUS_I2C_BLOCK = (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK), + + I2C_FUNC_SMBUS_EMUL = (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC), + + /* if I2C_M_RECV_LEN is also supported */ + I2C_FUNC_SMBUS_EMUL_ALL = + (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL), +}; + +__attribute__((__packed__)) struct itu_cmd { + uint16_t flags; + uint16_t addr; + uint16_t len; + uint8_t cmd; +}; + +#ifdef DBOARD_HAS_I2C +/* functions to be implemented by BSP */ +__attribute__((__const__)) enum ki2c_funcs i2ctu_dev_get_func(void); +void i2ctu_dev_init(void); +void i2ctu_dev_deinit(void); +uint32_t i2ctu_dev_set_freq(uint32_t freq, uint32_t us); // returns selected frequency, or 0 on error +enum itu_status i2ctu_dev_write(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, + const uint8_t* buf, size_t len); +enum itu_status i2ctu_dev_read(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, + uint8_t* buf, size_t len); + +/* I2C-Tiny-USB protocol handling code */ +void i2ctu_init(void); +void i2ctu_deinit(void); +bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req); +#endif + +#endif + diff --git a/src/m_default/serprog.h b/src/m_default/serprog.h new file mode 100644 index 0000000..dea36f3 --- /dev/null +++ b/src/m_default/serprog.h @@ -0,0 +1,65 @@ +// vim: set et: + +#ifndef SERPROG_H_ +#define SERPROG_H_ + +enum serprog_cmd { + S_CMD_NOP = 0x00, + S_CMD_Q_IFACE = 0x01, + S_CMD_Q_CMDMAP = 0x02, + S_CMD_Q_PGMNAME = 0x03, + S_CMD_Q_SERBUF = 0x04, + S_CMD_Q_BUSTYPE = 0x05, + S_CMD_Q_CHIPSIZE = 0x06, + S_CMD_Q_OPBUF = 0x07, + S_CMD_Q_WRNMAXLEN = 0x08, + S_CMD_R_BYTE = 0x09, + S_CMD_R_NBYTES = 0x0a, + S_CMD_O_INIT = 0x0b, + S_CMD_O_WRITEB = 0x0c, + S_CMD_O_WRITEN = 0x0d, + S_CMD_O_DELAY = 0x0e, + S_CMD_O_EXEC = 0x0f, + S_CMD_SYNCNOP = 0x10, + S_CMD_Q_RDNMAXLEN = 0x11, + S_CMD_S_BUSTYPE = 0x12, + S_CMD_SPIOP = 0x13, + S_CMD_S_SPI_FREQ = 0x14, + S_CMD_S_PINSTATE = 0x15, + + S_CMD_MAGIC_SETTINGS = 0x53 +}; + +enum serprog_response { S_ACK = 0x06, S_NAK = 0x15 }; + +#define SERPROG_IFACE_VERSION 0x0001 + +#ifdef DBOARD_HAS_SERPROG +/* functions to be implemented by the BSP */ +uint32_t /*freq_applied*/ sp_spi_set_freq(uint32_t freq_wanted); + +void sp_spi_init(void); +void sp_spi_deinit(void); +void sp_spi_cs_deselect(void); +void sp_spi_cs_select(void); +void sp_spi_op_begin(void); +void sp_spi_op_write(uint32_t write_len, const uint8_t* write_data); +void sp_spi_op_read(uint32_t read_len, uint8_t* read_data); +void sp_spi_op_end(void); + +static inline void sp_spi_op_do( + uint32_t write_len, const uint8_t* write_data, uint32_t read_len, uint8_t* read_data) { + sp_spi_op_begin(); + sp_spi_op_write(write_len, write_data); + sp_spi_op_write(read_len, read_data); + sp_spi_op_end(); +} + +/* protocol handling functions */ +void cdc_serprog_init(void); +void cdc_serprog_deinit(void); +void cdc_serprog_task(void); +#endif + +#endif + diff --git a/src/m_default/tempsensor.c b/src/m_default/tempsensor.c new file mode 100644 index 0000000..7076f2e --- /dev/null +++ b/src/m_default/tempsensor.c @@ -0,0 +1,255 @@ +// vim: set et: + +#include +#include +#include +#include + +#ifndef VERY_FAKE +#include "m_default/bsp-feature.h" +// clang-format off +#define printf(fmt, ...) do { } while (0) \ + +// clang-format on +#endif + +#ifdef DBOARD_HAS_TEMPSENSOR + +#include "tempsensor.h" + +static bool active; +static uint8_t addr; +static uint8_t reg; +static size_t index; +static bool instartstop, hasreg; + +enum regid { cap = 0, config, t_upper, t_lower, t_crit, t_a, manuf_id, dev_idrev, reso }; + +#define MANUF_ID 0x0054 +#define DEV_IDREV 0x0400 + +struct { + uint16_t config; + uint16_t t_upper, t_lower, t_crit; + uint8_t reso; +} mcp9808; + +#define float2fix(x) (int)((x) * (1 << 4)) +__attribute__((__const__)) inline static int16_t trunc_8fix4(int fix) { + // clang-format off + if (fix > 4095) fix = 4095; + if (fix < -4096) fix = -4096; + // clang-format on + return fix; +} +void tempsense_init(void) { + active = false; + addr = 0xff; + reg = 0; + index = 0; + instartstop = false; + hasreg = false; + + tempsense_dev_init(); + + // clang-format off + mcp9808.t_lower = tempsense_dev_get_lower(); + mcp9808.t_upper = tempsense_dev_get_upper(); + mcp9808.t_crit = tempsense_dev_get_crit (); + // clang-format on +} +void tempsense_deinit(void) { + tempsense_dev_deinit(); + + active = false; + addr = 0xff; + reg = 0; + index = 0; + instartstop = false; + hasreg = false; +} + +bool tempsense_get_active(void) { return active; } +void tempsense_set_active(bool act) { + active = act; + if (!act) addr = 0xff; +} +uint8_t tempsense_get_addr(void) { return addr; } +void tempsense_set_addr(uint8_t a) { + addr = a; + active = addr >= 0x8 && addr <= 0x77; + printf("set: ad=%02x ac=%c\n", addr, active ? 't' : 'f'); +} + +void tempsense_do_start(void) { + printf("ts start\n"); + // reg = 0; + index = 0; + instartstop = true; + hasreg = false; +} +void tempsense_do_stop(void) { + printf("ts stop\n"); + instartstop = false; +} + +int tempsense_do_read(int length, uint8_t* buf) { + printf("read l=%d reg=%02x ", length, reg); + + if (!instartstop || length < 0) return -1; // nak + if (length == 0) return 0; // ack + // if (!hasreg) return -1; // nak + + int i; + for (i = 0; i < length; ++i, ++index) { + switch (reg) { + // TODO: big or little endian? seems to be big + case cap: buf[index] = 0; break; + case config: + if (index == 0) + buf[0] = (mcp9808.config >> 8) & 0xff; + else if (index == 1) + buf[1] = (mcp9808.config >> 0) & 0xff; + else + return index; + break; + case t_upper: + if (index == 0) + buf[0] = (mcp9808.t_upper >> 8) & 0xff; + else if (index == 1) + buf[1] = (mcp9808.t_upper >> 0) & 0xff; + else + return index; + break; + case t_lower: + if (index == 0) + buf[0] = (mcp9808.t_lower >> 8) & 0xff; + else if (index == 1) + buf[1] = (mcp9808.t_lower >> 0) & 0xff; + else + return index; + break; + case t_crit: + if (index == 0) + buf[0] = (mcp9808.t_crit >> 8) & 0xff; + else if (index == 1) + buf[1] = (mcp9808.t_crit >> 0) & 0xff; + else + return index; + break; + case t_a: { + static uint16_t temp; + if (index == 0) { + int16_t res = tempsense_dev_get_temp(); + + // clang-format off + uint32_t tup = mcp9808.t_upper & 0x1ffc; + if (tup & 0x1000) tup |= 0xffffe000; // make negative + uint32_t tlo = mcp9808.t_lower & 0x1ffc; + if (tlo & 0x1000) tlo |= 0xffffe000; // make negative + uint32_t tcr = mcp9808.t_crit & 0x1ffc; + if (tcr & 0x1000) tcr |= 0xffffe000; // make negative + // clang-format on + + temp = res & 0x1fff; // data bits and sign bit + + if ((int32_t)tlo > res) temp |= 0x2000; + if ((int32_t)tup < res) temp |= 0x4000; + if ((int32_t)tcr < res) temp |= 0x8000; + + buf[0] = (temp >> 8) & 0xff; + } else if (index == 1) + buf[1] = (temp >> 0) & 0xff; + else + return index; + } break; + case manuf_id: + if (index == 0) + buf[0] = (MANUF_ID >> 8) & 0xff; + else if (index == 1) + buf[1] = (MANUF_ID >> 0) & 0xff; + else + return index; + break; + case dev_idrev: + if (index == 0) + buf[0] = (DEV_IDREV >> 8) & 0xff; + else if (index == 1) + buf[1] = (DEV_IDREV >> 0) & 0xff; + else + return index; + break; + case reso: + if (index == 0) + buf[0] = mcp9808.reso; + else + return index; + break; + default: return -1; + } + } + + return i; +} +int tempsense_do_write(int length, const uint8_t* buf) { + printf("write l=%d reg=%02x iss=%c ", length, reg, instartstop ? 't' : 'f'); + + if (!instartstop || length < 0) return -1; // nak + if (length == 0) return 0; // ack + + if (!hasreg) { + printf("get reg %02x ", reg); + + reg = *buf & 0xf; + ++buf; + --length; + hasreg = true; + } + + if (length == 0) return 1; // ack, probably a read following + + int i; + for (i = 0; i < length; ++i, ++index) { + switch (reg) { + case config: + if (index == 0) { + mcp9808.config = (mcp9808.config & 0x00ff) | ((uint16_t)buf[0] << 8); + } else if (index == 1) { + mcp9808.config = (mcp9808.config & 0xff00) | ((uint16_t)buf[1] << 0); + } else + return index; + break; + case t_upper: + if (index == 0) { + mcp9808.t_upper = (mcp9808.t_upper & 0x00ff) | ((uint16_t)buf[0] << 8); + } else if (index == 1) { + mcp9808.t_upper = (mcp9808.t_upper & 0xff00) | ((uint16_t)buf[1] << 0); + } else + return index; + break; + case t_lower: + if (index == 0) { + mcp9808.t_lower = (mcp9808.t_lower & 0x00ff) | ((uint16_t)buf[0] << 8); + } else if (index == 1) { + mcp9808.t_lower = (mcp9808.t_lower & 0xff00) | ((uint16_t)buf[1] << 0); + } else + return index; + break; + case t_crit: + if (index == 0) { + mcp9808.t_crit = (mcp9808.t_crit & 0x00ff) | ((uint16_t)buf[0] << 8); + } else if (index == 1) { + mcp9808.t_crit = (mcp9808.t_crit & 0xff00) | ((uint16_t)buf[1] << 0); + } else + return index; + break; + case reso: mcp9808.reso = buf[index]; break; + default: printf("unk reg\n"); return -1; + } + } + + return i; +} + +#endif + diff --git a/src/m_default/tempsensor.h b/src/m_default/tempsensor.h new file mode 100644 index 0000000..70c0bac --- /dev/null +++ b/src/m_default/tempsensor.h @@ -0,0 +1,34 @@ +// vim: set et: + +#ifndef TEMPSENSOR_H_ +#define TEMPSENSOR_H_ + +#include +#include + +void tempsense_init(void); +void tempsense_deinit(void); + +bool tempsense_get_active(void); +void tempsense_set_active(bool active); +uint8_t tempsense_get_addr(void); +void tempsense_set_addr(uint8_t addr); + +void tempsense_do_start(void); // start cond +int tempsense_do_read(int length, uint8_t* buf); +int tempsense_do_write(int length, const uint8_t* buf); +void tempsense_do_stop(void); // stop cond + +#ifdef DBOARD_HAS_TEMPSENSOR +void tempsense_dev_init(void); +void tempsense_dev_deinit(void); +// 8.4 +int16_t tempsense_dev_get_temp(void); + +int16_t tempsense_dev_get_lower(void); +int16_t tempsense_dev_get_upper(void); +int16_t tempsense_dev_get_crit(void); +#endif + +#endif + diff --git a/src/m_default/vnd_i2ctinyusb.c b/src/m_default/vnd_i2ctinyusb.c new file mode 100644 index 0000000..4d27103 --- /dev/null +++ b/src/m_default/vnd_i2ctinyusb.c @@ -0,0 +1,201 @@ + +#include "m_default/bsp-feature.h" + +#ifdef DBOARD_HAS_I2C + +// clang-format off +#include +#include +#include + +#include + +#include +#include + +#include "m_default/bsp-feature.h" + +#include "i2ctinyusb.h" + +#include "tempsensor.h" +// clang-format on + +static enum itu_status status; +static struct itu_cmd curcmd; + +static uint8_t rxbuf[128]; +static uint8_t txbuf[128]; + +void i2ctu_init(void) { + status = ITU_STATUS_IDLE; + memset(&curcmd, 0, sizeof curcmd); + + i2ctu_dev_init(); +#ifdef DBOARD_HAS_TEMPSENSOR + tempsense_init(); +#endif +} + +void i2ctu_deinit(void) { + status = ITU_STATUS_IDLE; + memset(&curcmd, 0, sizeof curcmd); + + i2ctu_dev_deinit(); +#ifdef DBOARD_HAS_TEMPSENSOR + tempsense_deinit(); +#endif +} + +bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) { + (void)rhport; + + /*static char* stages[]={"SETUP","DATA","ACK"}; + static char* types[]={"STD","CLS","VND","INV"}; + + printf("ctl req stage=%s rt=%s, wIndex=%04x, bReq=%02x, wValue=%04x wLength=%04x\n", + stages[stage], types[req->bmRequestType_bit.type], + req->wIndex, req->bRequest, req->wValue, req->wLength);*/ + + if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true; + + if (stage == CONTROL_STAGE_DATA) { + struct itu_cmd cmd = curcmd; + + if (req->bRequest >= ITU_CMD_I2C_IO && req->bRequest <= ITU_CMD_I2C_IO_BEGINEND && + cmd.cmd == req->bRequest && cmd.flags == req->wValue && cmd.addr == req->wIndex && + cmd.len == req->wLength) { + // printf("WDATA a=%04hx l=%04hx ", cmd.addr, cmd.len); + + // printf("data=%02x %02x...\n", rxbuf[0], rxbuf[1]); +#ifdef DBOARD_HAS_TEMPSENSOR + if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) { + if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start(); + // FIXME: fix status handling + int rv = tempsense_do_write(cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len, rxbuf); + if (rv < 0 || rv != cmd.len) + status = ITU_STATUS_ADDR_NAK; + else + status = ITU_STATUS_ADDR_ACK; + if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop(); + } else +#endif + { + status = i2ctu_dev_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd.addr, rxbuf, + cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len); + } + + // cancel curcmd + curcmd.cmd = 0xff; + } + return true; + } else if (stage == CONTROL_STAGE_SETUP) { + switch (req->bRequest) { + case ITU_CMD_ECHO: { // flags to be echoed back, addr unused, len=2 + + if (req->wLength != 2) return false; // bad length -> let's stall + + uint8_t rv[2]; + rv[0] = req->wValue & 0xff; + rv[1] = (req->wValue >> 8) & 0xff; + return tud_control_xfer(rhport, req, rv, sizeof rv); + } break; + case ITU_CMD_GET_FUNC: { // flags unused, addr unused, len=4 + if (req->wLength != 4) return false; + + const uint32_t func = i2ctu_dev_get_func(); + txbuf[0] = func & 0xff; + txbuf[1] = (func >> 8) & 0xff; + txbuf[2] = (func >> 16) & 0xff; + txbuf[3] = (func >> 24) & 0xff; + return tud_control_xfer(rhport, req, txbuf, 4); + } break; + case ITU_CMD_SET_DELAY: { // flags=delay, addr unused, len=0 + if (req->wLength != 0) return false; + + uint32_t us = req->wValue ? req->wValue : 1; + uint32_t freq = 1000 * 1000 / us; + + // printf("set freq us=%u freq=%u\n", us, freq); + if (i2ctu_dev_set_freq(freq, us) != 0) // returned an ok frequency + return tud_control_status(rhport, req); + else + return false; + } break; + case ITU_CMD_GET_STATUS: { // flags unused, addr unused, len=1 + if (req->wLength != 1) return false; + + uint8_t rv = status; + return tud_control_xfer(rhport, req, &rv, 1); + } break; + + case ITU_CMD_I2C_IO: // flags: ki2c_flags + case ITU_CMD_I2C_IO_BEGIN: // addr: I2C address + case ITU_CMD_I2C_IO_END: // len: transfer size + case ITU_CMD_I2C_IO_BEGINEND: { // (transfer dir is in flags) + struct itu_cmd cmd; + cmd.flags = req->wValue; + cmd.addr = req->wIndex; + cmd.len = req->wLength; + cmd.cmd = req->bRequest; + + if (cmd.flags & I2C_M_RD) { // read from I2C device + // printf("read addr=%04hx len=%04hx ", cmd.addr, cmd.len); +#ifdef DBOARD_HAS_TEMPSENSOR + if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) { + if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start(); + int rv = tempsense_do_read( + cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len, txbuf); + if (rv < 0 || rv != cmd.len) + status = ITU_STATUS_ADDR_NAK; + else + status = ITU_STATUS_ADDR_ACK; + if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop(); + } else +#endif + { + status = i2ctu_dev_read(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd.addr, + txbuf, cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len); + } + // printf("data=%02x %02x...\n", txbuf[0], txbuf[1]); + return tud_control_xfer( + rhport, req, txbuf, cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len); + } else { // write + // printf("write addr=%04hx len=%04hx ", cmd.addr, cmd.len); + if (cmd.len == 0) { // address probe -> do this here + uint8_t bleh = 0; +#ifdef DBOARD_HAS_TEMPSENSOR + if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) { + if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start(); + int rv = tempsense_do_write(0, &bleh); + if (rv < 0 || rv != cmd.len) + status = ITU_STATUS_ADDR_NAK; + else + status = ITU_STATUS_ADDR_ACK; + if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop(); + } else +#endif + { + status = i2ctu_dev_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, &bleh, 0); + } + // printf("probe -> %d\n", status); + return tud_control_status(rhport, req); + } else { + // handled in DATA stage! + curcmd = cmd; + bool rv = tud_control_xfer(rhport, req, rxbuf, + cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len); + return rv; + } + } + } break; + default: + // printf("I2C-Tiny-USB: unknown command %02x\n", req->bRequest); + return false; + } + } else + return true; // other stage... +} + +#endif /* DBOARD_HAS_I2C */ + diff --git a/src/m_default2/0def.c b/src/m_default2/0def.c deleted file mode 100644 index fdd461b..0000000 --- a/src/m_default2/0def.c +++ /dev/null @@ -1,112 +0,0 @@ -// vim: set et: - -#include - -#include "mode.h" -#include "vnd_cfg.h" - -static void enter_cb(void) { - // TODO: init hw -} -static void leave_cb(void) { - // TODO: deinit hw -} - -static void task_cb(void) { - // TODO: do stuff -} - -static void handle_cmd_cb(uint8_t cmd) { - uint8_t resp = 0; - - switch (cmd) { - case mode_cmd_get_features: - resp = 0x42; - vnd_cfg_write_resp(cfg_resp_ok, 1, &resp); - break; - default: - vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL); - break; - } -} - -enum { - STRID_LANGID = 0, - STRID_MANUFACTURER, - STRID_PRODUCT, - STRID_SERIAL, - - STRID_CONFIG, - - STRID_IF_VND_CFG, - STRID_IF_CDC_STDIO, -}; -enum { - ITF_NUM_VND_CFG, -//#ifdef USE_USBCDC_FOR_STDIO - ITF_NUM_CDC_STDIO_COM, - ITF_NUM_CDC_STDIO_DATA, -//#endif - - ITF_NUM__TOTAL -}; -enum { - CONFIG_TOTAL_LEN - = TUD_CONFIG_DESC_LEN - + TUD_VENDOR_DESC_LEN -//#ifdef USE_USBCDC_FOR_STDIO - + TUD_CDC_DESC_LEN -//#endif -}; - -#define EPNUM_CDC_STDIO_OUT 0x03 -#define EPNUM_CDC_STDIO_IN 0x83 -#define EPNUM_CDC_STDIO_NOTIF 0x84 - -// TODO: are these ok numbers? -#define EPNUM_VND_CFG_OUT 0x02 -#define EPNUM_VND_CFG_IN 0x82 - -// clang-format off -static const uint8_t desc_configuration[] = { - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN, - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT, - EPNUM_VND_CFG_IN, 64), - -//#ifdef USE_USBCDC_FOR_STDIO - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF, 64, - EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, 64), -//#endif -}; -static const char* string_desc_arr[] = { - /*[STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409) - [STRID_MANUFACTURER] = "BLAHAJ CTF", // Manufacturer - [STRID_PRODUCT] = "Dragnbus (hi mum)", // Product*/ - - NULL, - [STRID_CONFIG] = "Configuration descriptor", - // max string length check: ||||||||||||||||||||||||||||||| - [STRID_IF_VND_CFG ] = "Device cfg/ctl interface", - [STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)", -}; -// clang-format on - -extern struct mode m_02_default2; -// clang-format off -struct mode m_02_default2 = { - .name = "Default mode but betterer!", - .version = 0x0020, - .n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]), - - .usb_desc = desc_configuration, - .string_desc = string_desc_arr, - - .enter = enter_cb, - .leave = leave_cb, - .task = task_cb, - .handle_cmd = handle_cmd_cb, -}; -// clang-format on - diff --git a/src/main.c b/src/main.c index dc9e0d9..c9b57f7 100644 --- a/src/main.c +++ b/src/main.c @@ -13,8 +13,10 @@ static cothread_t vndcfg_thread; static uint8_t vndcfg_stack[THREAD_STACK_SIZE]; +// FIXME: move declaration elsewhere #ifdef USE_USBCDC_FOR_STDIO void stdio_usb_init(void); +void stdio_usb_set_itf_num(int itf); #endif static void vndcfg_thread_fn(void) { @@ -32,7 +34,7 @@ int main() { board_init(); // tinyusb hardware support function vndcfg_thread = co_derive(vndcfg_stack, sizeof vndcfg_stack, vndcfg_thread_fn); - co_switch(vndcfg_thread); + thread_enter(vndcfg_thread); modes_init(); if (mode_current) mode_current->enter(); @@ -41,6 +43,8 @@ int main() { // FIXME: put elsewhere? #ifdef USE_USBCDC_FOR_STDIO + stdio_usb_set_itf_num(0); + stdio_usb_init(); #endif @@ -49,7 +53,7 @@ int main() { if (mode_current) mode_current->task(); tud_task(); - co_switch(vndcfg_thread); + thread_enter(vndcfg_thread); // do this here instead of in a callback or in the vnd_cfg_task fn if (mode_next_id != -1) { diff --git a/src/mode.h b/src/mode.h index 073126c..8af69a4 100644 --- a/src/mode.h +++ b/src/mode.h @@ -34,10 +34,10 @@ struct mode { #if CFG_TUD_CDC > 0 void (*tud_cdc_line_coding_cb)(uint8_t itf, cdc_line_coding_t const* line_coding); #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 bool (*tud_vendor_control_xfer_cb)(uint8_t rhport, uint8_t ep_addr, tusb_control_request_t const* req); -#endif +//#endif uint8_t const* (*tud_hid_descriptor_report_cb)(uint8_t instance); uint8_t const* (*tud_descriptor_device_cb)(void); @@ -70,10 +70,10 @@ void mode_std_hid_set_report_cb(uint8_t instance, uint8_t report_id, #if CFG_TUD_CDC > 0 void mode_std_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding); #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 bool mode_std_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_control_request_t const* req); -#endif +//#endif // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor diff --git a/src/modeset.c b/src/modeset.c index b3792b4..f31f00c 100644 --- a/src/modeset.c +++ b/src/modeset.c @@ -2,15 +2,14 @@ #include "mode.h" -extern struct mode m_01_default, m_02_default2; +extern struct mode m_01_default/*, m_02_default2*/; // clang-format off const struct mode* mode_list[16] = { NULL, // dummy 0 entry &m_01_default, - &m_02_default2, + //&m_02_default2, NULL, // terminating entry - }; // clang-format on @@ -55,12 +54,12 @@ void modes_init(void) { #else tusb_got[tusbgot_cdc_line_coding ] = NULL; #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 tusb_got[tusbgot_vendor_control_xfer] = ORDEF(mode_default->tud_vendor_control_xfer_cb, mode_std_vendor_control_xfer_cb); -#else - tusb_got[tusbgot_vendor_control_xfer] = NULL; -#endif +//#else +// tusb_got[tusbgot_vendor_control_xfer] = NULL; +//#endif tusb_got[tusbgot_hid_descriptor_report ] = ORDEF(mode_default->tud_hid_descriptor_report_cb, mode_std_hid_descriptor_report_cb); @@ -107,12 +106,12 @@ void modes_switch(uint8_t newmode) { #else tusb_got[tusbgot_cdc_line_coding ] = NULL; #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 tusb_got[tusbgot_vendor_control_xfer] = ORDEF(mode_current->tud_vendor_control_xfer_cb, mode_std_vendor_control_xfer_cb); -#else - tusb_got[tusbgot_vendor_control_xfer] = NULL; -#endif +//#else +// tusb_got[tusbgot_vendor_control_xfer] = NULL; +//#endif tusb_got[tusbgot_hid_descriptor_report ] = ORDEF(mode_current->tud_hid_descriptor_report_cb, mode_std_hid_descriptor_report_cb); @@ -137,11 +136,11 @@ void modes_switch(uint8_t newmode) { #else tusb_got[tusbgot_cdc_line_coding ] = NULL; #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 tusb_got[tusbgot_vendor_control_xfer] = mode_std_vendor_control_xfer_cb; -#else - tusb_got[tusbgot_vendor_control_xfer] = NULL; -#endif +//#else +// tusb_got[tusbgot_vendor_control_xfer] = NULL; +//#endif tusb_got[tusbgot_hid_descriptor_report ] = mode_std_hid_descriptor_report_cb; tusb_got[tusbgot_descriptor_device ] = mode_std_descriptor_device_cb; diff --git a/src/thread.c b/src/thread.c index f528974..5f7550a 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,6 +1,8 @@ // vim: set et: +#include #include +#include #include "thread.h" @@ -11,6 +13,32 @@ cothread_t co_active_handle; static cothread_t mainthread; -void thread_init (void) { mainthread = co_active(); } -void thread_yield(void) { co_switch(mainthread); } +static cothread_t threadarr[16]; /* 16 nested threads should be enough... */ +static size_t threadind; + +void thread_init(void) { + memset(threadarr, 0, sizeof threadarr); + + mainthread = co_active(); + threadarr[0] = mainthread; + threadind = 0; +} + +void thread_yield(void) { + /*cothread_t newthrd = threadarr[threadind]; + if (threadind > 0) --threadind;*/ + + co_switch(mainthread/*newthrd*/); +} + +void thread_enter(cothread_t thrid) { + /*if (threadind + 1 == sizeof(threadarr) / sizeof(threadarr[0])) { + // TODO: PANIC! + } + + threadarr[threadind] = co_active(); + ++threadind;*/ + + co_switch(thrid); +} diff --git a/src/thread.h b/src/thread.h index 2664774..0b60f05 100644 --- a/src/thread.h +++ b/src/thread.h @@ -10,5 +10,10 @@ void thread_init (void); void thread_yield(void); +/* thread_enter + thread_yield can be used to "call" threads in a stack-like + * way, much like functions. this is needed because vnd_cfg might call mode + * stuff which might do stuff in its own tasks and so on. */ +void thread_enter(cothread_t thrid); + #endif diff --git a/src/tusb_config.h b/src/tusb_config.h index 99059ac..64bfd0a 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -31,7 +31,7 @@ extern "C" { #endif -//#include "protocfg.h" +#include "bsp-info.h" //-------------------------------------------------------------------- // COMMON CONFIGURATION @@ -107,14 +107,7 @@ extern "C" { #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_NET 0 -// see also: bsp//protocfg.h -#define CFG_TUD_HID 0 -#ifdef USE_USBCDC_FOR_STDIO -#define CFG_TUD_CDC 1 -#else -#define CFG_TUD_CDC 0 -#endif -#define CFG_TUD_VENDOR 1 +// see also: bsp//bsp-info.h #define CFG_TUD_HID_EP_BUFSIZE 64 diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index bcd7667..80a7528 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -3,9 +3,8 @@ #include "tusb_config.h" #include +#include "info.h" #include "mode.h" - -#include "protocfg.h" #include "util.h" #define USB_BCD_BASE 0x8000 @@ -22,22 +21,41 @@ enum { STRID_CONFIG, +#if CFG_TUD_VENDOR > 0 STRID_IF_VND_CFG, +#endif +#ifdef USE_USBCDC_FOR_STDIO + STRID_IF_CDC_STDIO, +#endif }; enum { +#if CFG_TUD_VENDOR > 0 ITF_NUM_VND_CFG, +#endif +#ifdef USE_USBCDC_FOR_STDIO + ITF_NUM_CDC_STDIO_COM, + ITF_NUM_CDC_STDIO_DATA, +#endif ITF_NUM__TOTAL }; enum { CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN +#if CFG_TUD_VENDOR > 0 + TUD_VENDOR_DESC_LEN +#endif +#ifdef USE_USBCDC_FOR_STDIO + + TUD_CDC_DESC_LEN +#endif }; -#define EPNUM_VND_CFG_OUT 0x02 -#define EPNUM_VND_CFG_IN 0x82 +#define EPNUM_VND_CFG_OUT 0x01 +#define EPNUM_VND_CFG_IN 0x81 +#define EPNUM_CDC_STDIO_OUT 0x02 +#define EPNUM_CDC_STDIO_IN 0x82 +#define EPNUM_CDC_STDIO_NOTIF 0x83 // clang-format off static const tusb_desc_device_t desc_device = { @@ -73,8 +91,15 @@ static const uint8_t desc_configuration[] = { TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), +#if CFG_TUD_VENDOR > 0 TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT, EPNUM_VND_CFG_IN, 64), +#endif + +#ifdef USE_USBCDC_FOR_STDIO + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF, 64, + EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, 64), +#endif }; static const char* string_desc_arr[] = { [STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409) @@ -83,6 +108,7 @@ static const char* string_desc_arr[] = { [STRID_CONFIG] = "Configuration descriptor", [STRID_IF_VND_CFG] = "Device cfg/ctl interface", + [STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)", }; // clang-format on @@ -106,6 +132,7 @@ void mode_std_hid_set_report_cb(uint8_t instance, uint8_t report_id, (void)instance; (void)report_id; (void)report_type; + (void)rx_data_buffer; tud_hid_report(0, tx_data_buffer, response_size); } @@ -116,7 +143,7 @@ void mode_std_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_codi (void)line_coding; } #endif -#if CFG_TUD_VENDOR > 0 +//#if CFG_TUD_VENDOR > 0 bool mode_std_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_control_request_t const* req) { (void)rhport; @@ -125,7 +152,7 @@ bool mode_std_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, return true; } -#endif +//#endif const uint8_t* mode_std_hid_descriptor_report_cb(uint8_t instance) { (void)instance; diff --git a/src/vnd_cfg.c b/src/vnd_cfg.c index c38f946..5cbb328 100644 --- a/src/vnd_cfg.c +++ b/src/vnd_cfg.c @@ -8,6 +8,7 @@ #include "thread.h" +#if CFG_TUD_VENDOR > 0 static uint8_t rx_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; static uint8_t tx_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; @@ -144,4 +145,14 @@ void vnd_cfg_task(void) { } } } +#else /* CFG_TUD_VENDOR == 0 */ +void vnd_cfg_init(void) { } +uint8_t vnd_cfg_read_byte(void) { return 0xff; } +void vnd_cfg_write_flush(void) { } +void vnd_cfg_write_byte(uint8_t v) { (void)v; } +void vnd_cfg_write_resp(enum cfg_resp stat, uint16_t len, const void* data) { + (void)stat; (void)len; (void)data; +} +void vnd_cfg_task(void) { } +#endif /* CFG_TUD_VENDOR */