Merge branch 'master' into group-boards-into-family

This commit is contained in:
hathach 2021-01-22 11:17:10 +07:00
commit 0cf2b02791
58 changed files with 3479 additions and 328 deletions

View File

@ -1,10 +0,0 @@
---
name: Question
about: Question for this project
title: ''
labels: Q&A
assignees: ''
---
**Describe what the question is**

3
.gitmodules vendored
View File

@ -109,3 +109,6 @@
[submodule "lib/CMSIS_5"]
path = lib/CMSIS_5
url = https://github.com/ARM-software/CMSIS_5.git
[submodule "lib/sct_neopixel"]
path = lib/sct_neopixel
url = https://github.com/gsteiert/sct_neopixel

View File

@ -31,6 +31,9 @@
- **[Kay Sievers](https://github.com/kaysievers)**
- Improve MIDI driver with packet API
- **[Koji KITAYAMA](https://github.com/kkitayam)**
- Add new DCD port for **NXP Kinetis KL25**
- **[Nathan Conrad](https://github.com/pigrew)**
- Add new DCD port for **STM32 fsdev** Fullspeed device for STM32 L0, F0, F1, F3 etc ...
- Add new class driver for **USB Test and Measurement Class (USBTMC)**

View File

@ -35,9 +35,10 @@ The stack supports the following MCUs:
- **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55
- **NordicSemi:** nRF52833, nRF52840
- **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505
- **NXP:**
- LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
- **NXP:**
- iMX RT Series: RT1011, RT1015, RT1021, RT1052, RT1062, RT1064
- Kinetis: KL25
- LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
- **Sony:** CXD56
- **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 both FullSpeed and HighSpeed
- **TI:** MSP430

View File

@ -68,6 +68,10 @@ This code base already had supported for a handful of following boards (sorted a
- [MIMX RT1064 Evaluation Kit](https://www.nxp.com/design/development-boards/i.mx-evaluation-and-development-boards/mimxrt1064-evk-i.mx-rt1064-evaluation-kit:MIMXRT1064-EVK)
- [Teensy 4.0 Development Board](https://www.pjrc.com/store/teensy40.html)
### NXP Kinetis
- [FRDM-KL25Z](https://www.nxp.com/design/development-boards/freedom-development-boards/mcu-boards/freedom-development-platform-for-kinetis-kl14-kl15-kl24-kl25-mcus:FRDM-KL25Z)
### NXP LPC
- [ARM mbed LPC1768](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/arm-mbed-lpc1768-board:OM11043)
@ -82,6 +86,7 @@ This code base already had supported for a handful of following boards (sorted a
- [LPCXpresso 54114](https://www.nxp.com/design/microcontrollers-developer-resources/lpcxpresso-boards/lpcxpresso54114-board:OM13089)
- [LPCXpresso 55s69 EVK](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc5500-cortex-m33/lpcxpresso55s69-development-board:LPC55S69-EVK)
- [NGX LPC4330-Xplorer](https://www.nxp.com/design/designs/lpc4330-xplorer-board:OM13027)
- [Double M33 Express](https://www.crowdsupply.com/steiert-solutions/double-m33-express)
### Sony

View File

@ -0,0 +1,59 @@
#ifndef KEYBOARD_HELPER_H
#define KEYBAORD_HELPER_H
#include <stdbool.h>
#include <stdint.h>
#include "tusb.h"
// look up new key in previous keys
inline bool find_key_in_report(hid_keyboard_report_t const *p_report, uint8_t keycode)
{
for(uint8_t i = 0; i < 6; i++)
{
if (p_report->keycode[i] == keycode) return true;
}
return false;
}
inline uint8_t keycode_to_ascii(uint8_t modifier, uint8_t keycode)
{
return keycode > 128 ? 0 :
hid_keycode_to_ascii_tbl [keycode][modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT) ? 1 : 0];
}
void print_kbd_report(hid_keyboard_report_t *prev_report, hid_keyboard_report_t const *new_report)
{
printf("Report: ");
uint8_t c;
// I assume it's possible to have up to 6 keypress events per report?
for (uint8_t i = 0; i < 6; i++)
{
// Check for key presses
if (new_report->keycode[i])
{
// If not in prev report then it is newly pressed
if ( !find_key_in_report(prev_report, new_report->keycode[i]) )
c = keycode_to_ascii(new_report->modifier, new_report->keycode[i]);
printf("press %c ", c);
}
// Check for key depresses (i.e. was present in prev report but not here)
if (prev_report->keycode[i])
{
// If not present in the current report then depressed
if (!find_key_in_report(new_report, prev_report->keycode[i]))
{
c = keycode_to_ascii(prev_report->modifier, prev_report->keycode[i]);
printf("depress %c ", c);
}
}
}
printf("\n");
}
#endif

View File

@ -54,9 +54,14 @@ CXX = $(CROSS_COMPILE)g++
OBJCOPY = $(CROSS_COMPILE)objcopy
SIZE = $(CROSS_COMPILE)size
MKDIR = mkdir
ifeq ($(CMDEXE),1)
CP = copy
RM = del
else
SED = sed
CP = cp
RM = rm
endif
#-------------- Source files and compiler flags --------------

View File

@ -95,7 +95,11 @@ uf2: $(BUILD)/$(BOARD)-firmware.uf2
OBJ_DIRS = $(sort $(dir $(OBJ)))
$(OBJ): | $(OBJ_DIRS)
$(OBJ_DIRS):
ifeq ($(CMDEXE),1)
@$(MKDIR) $(subst /,\,$@)
else
@$(MKDIR) -p $@
endif
$(BUILD)/$(BOARD)-firmware.elf: $(OBJ)
@echo LINK $@
@ -121,13 +125,6 @@ vpath %.c . $(TOP)
$(BUILD)/obj/%.o: %.c
@echo CC $(notdir $@)
@$(CC) $(CFLAGS) -c -MD -o $@ $<
@# The following fixes the dependency file.
@# See http://make.paulandlesley.org/autodep.html for details.
@# Regex adjusted from the above to play better with Windows paths, etc.
@$(CP) $(@:.o=.d) $(@:.o=.P); \
$(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
$(RM) $(@:.o=.d)
# ASM sources lower case .s
vpath %.s . $(TOP)
@ -148,7 +145,11 @@ size: $(BUILD)/$(BOARD)-firmware.elf
.PHONY: clean
clean:
ifeq ($(CMDEXE),1)
rd /S /Q $(subst /,\,$(BUILD))
else
$(RM) -rf $(BUILD)
endif
# Print out the value of a make variable.
# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile

View File

@ -80,6 +80,13 @@ int board_uart_write(void const * buf, int len);
return os_time_ticks_to_ms32( os_time_get() );
}
#elif CFG_TUSB_OS == OPT_OS_PICO
#include "pico/time.h"
static inline uint32_t board_millis(void)
{
return to_ms_since_boot(get_absolute_time());
}
#else
#error "board_millis() is not implemented for this OS"
#endif

View File

@ -46,7 +46,7 @@
#include "chip.h"
#elif CFG_TUSB_MCU == OPT_MCU_LPC51UXX || CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
CFG_TUSB_MCU == OPT_MCU_LPC55XX
CFG_TUSB_MCU == OPT_MCU_LPC55XX || CFG_TUSB_MCU == OPT_MCU_MKL25ZXX
#include "fsl_device_registers.h"
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
@ -117,6 +117,9 @@
#elif CFG_TUSB_MCU == OPT_MCU_DA1469X
#include "DA1469xAB.h"
#elif CFG_TUSB_MCU == OPT_MCU_RP2040
#include "pico.h"
#else
#error "Missing MCU header"
#endif

View File

@ -21,7 +21,7 @@ SRC_C += \
$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
$(MCU_FAMILY_DIR)/src/hal_gpio.c \
SRC_S += $(TOP)/hw/bsp/$(BOARD)/gcc_startup_da1469x.S
SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
INC += \
$(TOP)/hw/bsp/$(BOARD) \

View File

@ -21,7 +21,7 @@ SRC_C += \
$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
$(MCU_FAMILY_DIR)/src/hal_gpio.c \
SRC_S += $(TOP)/hw/bsp/$(BOARD)/gcc_startup_da1469x.S
SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
INC += \
$(TOP)/hw/bsp/$(BOARD) \

View File

@ -0,0 +1,234 @@
/*
** ###################################################################
** Processors: LPC55S69JBD100_cm33_core0
** LPC55S69JBD64_cm33_core0
** LPC55S69JEV98_cm33_core0
**
** Compiler: GNU C Compiler
** Reference manual: LPC55S6x/LPC55S2x/LPC552x User manual(UM11126) Rev.1.3 16 May 2019
** Version: rev. 1.1, 2019-05-16
** Build: b191008
**
** Abstract:
** Linker file for the GNU C Compiler
**
** Copyright 2016 Freescale Semiconductor, Inc.
** Copyright 2016-2019 NXP
** All rights reserved.
**
** SPDX-License-Identifier: BSD-3-Clause
**
** http: www.nxp.com
** mail: support@nxp.com
**
** ###################################################################
*/
/* Entry Point */
ENTRY(Reset_Handler)
HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0800;
RPMSG_SHMEM_SIZE = DEFINED(__use_shmem__) ? 0x1800 : 0;
/* Specify the memory areas */
MEMORY
{
m_interrupts (RX) : ORIGIN = 0x00010000, LENGTH = 0x00000200
m_text (RX) : ORIGIN = 0x00010200, LENGTH = 0x0007FE00
m_core1_image (RX) : ORIGIN = 0x00090000, LENGTH = 0x00008000
m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00033000 - RPMSG_SHMEM_SIZE
rpmsg_sh_mem (RW) : ORIGIN = 0x20033000 - RPMSG_SHMEM_SIZE, LENGTH = RPMSG_SHMEM_SIZE
m_usb_sram (RW) : ORIGIN = 0x40100000, LENGTH = 0x00004000
}
/* Define output sections */
SECTIONS
{
/* section for storing the secondary core image */
.m0code :
{
. = ALIGN(4) ;
KEEP (*(.m0code))
*(.m0code*)
. = ALIGN(4) ;
} > m_core1_image
/* NOINIT section for rpmsg_sh_mem */
.noinit_rpmsg_sh_mem (NOLOAD) : ALIGN(4)
{
__RPMSG_SH_MEM_START__ = .;
*(.noinit.$rpmsg_sh_mem*)
. = ALIGN(4) ;
__RPMSG_SH_MEM_END__ = .;
} > rpmsg_sh_mem
/* The startup code goes first into internal flash */
.interrupts :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} > m_interrupts
/* The program code and other data goes into internal flash */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
} > m_text
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > m_text
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > m_text
.ctors :
{
__CTOR_LIST__ = .;
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
} > m_text
.dtors :
{
__DTOR_LIST__ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
} > m_text
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} > m_text
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} > m_text
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} > m_text
__etext = .; /* define a global symbol at end of code */
__DATA_ROM = .; /* Symbol is used by startup for data initialization */
.data : AT(__DATA_ROM)
{
. = ALIGN(4);
__DATA_RAM = .;
__data_start__ = .; /* create a global symbol at data start */
*(.ramfunc*) /* for functions in ram */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
KEEP(*(.jcr*))
. = ALIGN(4);
__data_end__ = .; /* define a global symbol at data end */
} > m_data
__DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
text_end = ORIGIN(m_text) + LENGTH(m_text);
ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")
/* Uninitialized data section */
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
. = ALIGN(4);
__START_BSS = .;
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
__END_BSS = .;
} > m_data
.heap :
{
. = ALIGN(8);
__end__ = .;
PROVIDE(end = .);
__HeapBase = .;
. += HEAP_SIZE;
__HeapLimit = .;
__heap_limit = .; /* Add for _sbrk */
} > m_data
.stack :
{
. = ALIGN(8);
. += STACK_SIZE;
} > m_data
m_usb_bdt (NOLOAD) :
{
. = ALIGN(512);
*(m_usb_bdt)
} > m_usb_sram
m_usb_global (NOLOAD) :
{
*(m_usb_global)
} > m_usb_sram
/* Initializes stack on the end of block */
__StackTop = ORIGIN(m_data) + LENGTH(m_data);
__StackLimit = __StackTop - STACK_SIZE;
PROVIDE(__stack = __StackTop);
.ARM.attributes 0 : { *(.ARM.attributes) }
ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap")
}

View File

@ -0,0 +1,55 @@
CFLAGS += \
-flto \
-mthumb \
-mabi=aapcs \
-mcpu=cortex-m33 \
-mfloat-abi=hard \
-mfpu=fpv5-sp-d16 \
-DCPU_LPC55S69JBD100_cm33_core0 \
-DCFG_TUSB_MCU=OPT_MCU_LPC55XX \
-DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \
-DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))'
# mcu driver cause following warnings
CFLAGS += -Wno-error=unused-parameter -Wno-error=float-equal
MCU_DIR = hw/mcu/nxp/sdk/devices/LPC55S69
# All source paths should be relative to the top level.
LD_FILE = hw/bsp/$(BOARD)/LPC55S69_cm33_core0_uf2.ld
SRC_C += \
$(MCU_DIR)/system_LPC55S69_cm33_core0.c \
$(MCU_DIR)/drivers/fsl_clock.c \
$(MCU_DIR)/drivers/fsl_gpio.c \
$(MCU_DIR)/drivers/fsl_power.c \
$(MCU_DIR)/drivers/fsl_reset.c \
$(MCU_DIR)/drivers/fsl_usart.c \
$(MCU_DIR)/drivers/fsl_flexcomm.c \
lib/sct_neopixel/sct_neopixel.c
INC += \
$(TOP)/hw/bsp/ \
$(TOP)/hw/bsp/$(BOARD) \
$(TOP)/lib/sct_neopixel \
$(TOP)/$(MCU_DIR)/../../CMSIS/Include \
$(TOP)/$(MCU_DIR) \
$(TOP)/$(MCU_DIR)/drivers
SRC_S += $(MCU_DIR)/gcc/startup_LPC55S69_cm33_core0.S
LIBS += $(TOP)/$(MCU_DIR)/gcc/libpower_hardabi.a
# For TinyUSB port source
VENDOR = nxp
CHIP_FAMILY = lpc_ip3511
# For freeRTOS port source
FREERTOS_PORT = ARM_CM33_NTZ/non_secure
# For flash-jlink target
JLINK_DEVICE = LPC55S69
# flash using pyocd
flash: $(BUILD)/$(BOARD)-firmware.hex
pyocd flash -t LPC55S69 $<

View File

@ -0,0 +1,275 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
*
* 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 file is part of the TinyUSB stack.
*/
#include "../board.h"
#include "fsl_device_registers.h"
#include "fsl_gpio.h"
#include "fsl_power.h"
#include "fsl_iocon.h"
#include "fsl_usart.h"
#include "fsl_sctimer.h"
#include "sct_neopixel.h"
//--------------------------------------------------------------------+
// Forward USB interrupt events to TinyUSB IRQ Handler
//--------------------------------------------------------------------+
void USB0_IRQHandler(void)
{
tud_int_handler(0);
}
void USB1_IRQHandler(void)
{
tud_int_handler(1);
}
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
#define LED_PORT 0
#define LED_PIN 1
#define LED_STATE_ON 1
// WAKE button
#define BUTTON_PORT 0
#define BUTTON_PIN 5
#define BUTTON_STATE_ACTIVE 0
// Number of neopixels
#define NEOPIXEL_NUMBER 2
#define NEOPIXEL_PORT 0
#define NEOPIXEL_PIN 27
#define NEOPIXEL_CH 6
#define NEOPIXEL_TYPE 0
// UART
#define UART_DEV USART0
// IOCON pin mux
#define IOCON_PIO_DIGITAL_EN 0x0100u /*!<@brief Enables digital function */
#define IOCON_PIO_FUNC0 0x00u /*!<@brief Selects pin function 0 */
#define IOCON_PIO_FUNC1 0x01u /*!<@brief Selects pin function 1 */
#define IOCON_PIO_FUNC4 0x04u /*!<@brief Selects pin function 4 */
#define IOCON_PIO_FUNC7 0x07u /*!<@brief Selects pin function 7 */
#define IOCON_PIO_INV_DI 0x00u /*!<@brief Input function is not inverted */
#define IOCON_PIO_MODE_INACT 0x00u /*!<@brief No addition pin function */
#define IOCON_PIO_OPENDRAIN_DI 0x00u /*!<@brief Open drain is disabled */
#define IOCON_PIO_SLEW_STANDARD 0x00u /*!<@brief Standard mode, output slew rate control is enabled */
#define IOCON_PIO_DIG_FUNC0_EN (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC0) /*!<@brief Digital pin function 0 enabled */
#define IOCON_PIO_DIG_FUNC1_EN (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC1) /*!<@brief Digital pin function 1 enabled */
#define IOCON_PIO_DIG_FUNC4_EN (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC4) /*!<@brief Digital pin function 2 enabled */
#define IOCON_PIO_DIG_FUNC7_EN (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC7) /*!<@brief Digital pin function 2 enabled */
// Global Variables
uint32_t pixelData[NEOPIXEL_NUMBER];
/****************************************************************
name: BOARD_BootClockFROHF96M
outputs:
- {id: SYSTICK_clock.outFreq, value: 96 MHz}
- {id: System_clock.outFreq, value: 96 MHz}
settings:
- {id: SYSCON.MAINCLKSELA.sel, value: SYSCON.fro_hf}
sources:
- {id: SYSCON.fro_hf.outFreq, value: 96 MHz}
******************************************************************/
void BootClockFROHF96M(void)
{
/*!< Set up the clock sources */
/*!< Set up FRO */
POWER_DisablePD(kPDRUNCFG_PD_FRO192M); /*!< Ensure FRO is on */
CLOCK_SetupFROClocking(12000000U); /*!< Set up FRO to the 12 MHz, just for sure */
CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12MHz first to ensure we can change voltage without
accidentally being below the voltage for current speed */
CLOCK_SetupFROClocking(96000000U); /*!< Set up high frequency FRO output to selected frequency */
POWER_SetVoltageForFreq(96000000U); /*!< Set voltage for the one of the fastest clock outputs: System clock output */
CLOCK_SetFLASHAccessCyclesForFreq(96000000U); /*!< Set FLASH wait states for core */
/*!< Set up dividers */
CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false); /*!< Set AHBCLKDIV divider to value 1 */
/*!< Set up clock selectors - Attach clocks to the peripheries */
CLOCK_AttachClk(kFRO_HF_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO_HF */
/*!< Set SystemCoreClock variable. */
SystemCoreClock = 96000000U;
}
void board_init(void)
{
// Enable IOCON clock
CLOCK_EnableClock(kCLOCK_Iocon);
// Init 96 MHz clock
BootClockFROHF96M();
#if CFG_TUSB_OS == OPT_OS_NONE
// 1ms tick timer
SysTick_Config(SystemCoreClock / 1000);
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
#endif
GPIO_PortInit(GPIO, 0);
GPIO_PortInit(GPIO, 1);
// LED
/* PORT0 PIN1 configured as PIO0_1 */
IOCON_PinMuxSet(IOCON, 0U, 1U, IOCON_PIO_DIG_FUNC0_EN);
gpio_pin_config_t const led_config = { kGPIO_DigitalOutput, 1};
GPIO_PinInit(GPIO, LED_PORT, LED_PIN, &led_config);
// Neopixel
/* PORT0 PIN27 configured as SCT0_OUT6 */
IOCON_PinMuxSet(IOCON, NEOPIXEL_PORT, NEOPIXEL_PIN, IOCON_PIO_DIG_FUNC4_EN);
sctpix_init(NEOPIXEL_TYPE);
sctpix_addCh(NEOPIXEL_CH, pixelData, NEOPIXEL_NUMBER);
sctpix_setPixel(NEOPIXEL_CH, 0, 0x100010);
sctpix_setPixel(NEOPIXEL_CH, 1, 0x100010);
sctpix_show();
// Button
/* PORT0 PIN5 configured as PIO0_5 */
IOCON_PinMuxSet(IOCON, BUTTON_PORT, BUTTON_PIN, IOCON_PIO_DIG_FUNC0_EN);
gpio_pin_config_t const button_config = { kGPIO_DigitalInput, 0};
GPIO_PinInit(GPIO, BUTTON_PORT, BUTTON_PIN, &button_config);
// UART
/* PORT0 PIN29 (coords: 92) is configured as FC0_RXD_SDA_MOSI_DATA */
IOCON_PinMuxSet(IOCON, 0U, 29U, IOCON_PIO_DIG_FUNC1_EN);
/* PORT0 PIN30 (coords: 94) is configured as FC0_TXD_SCL_MISO_WS */
IOCON_PinMuxSet(IOCON, 0U, 30U, IOCON_PIO_DIG_FUNC1_EN);
#if defined(UART_DEV) && CFG_TUSB_DEBUG
// Enable UART when debug log is on
CLOCK_AttachClk(kFRO12M_to_FLEXCOMM0);
usart_config_t uart_config;
USART_GetDefaultConfig(&uart_config);
uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
uart_config.enableTx = true;
uart_config.enableRx = true;
USART_Init(UART_DEV, &uart_config, 12000000);
#endif
// USB VBUS
/* PORT0 PIN22 configured as USB0_VBUS */
IOCON_PinMuxSet(IOCON, 0U, 22U, IOCON_PIO_DIG_FUNC7_EN);
// USB Controller
POWER_DisablePD(kPDRUNCFG_PD_USB0_PHY); /*Turn on USB0 Phy */
POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /*< Turn on USB1 Phy */
/* reset the IP to make sure it's in reset state. */
RESET_PeripheralReset(kUSB0D_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB0HSL_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB0HMR_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB1H_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB1D_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB1_RST_SHIFT_RSTn);
RESET_PeripheralReset(kUSB1RAM_RST_SHIFT_RSTn);
#if (defined CFG_TUSB_RHPORT1_MODE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
CLOCK_EnableClock(kCLOCK_Usbh1);
/* Put PHY powerdown under software control */
USBHSH->PORTMODE = USBHSH_PORTMODE_SW_PDCOM_MASK;
/* According to reference mannual, device mode setting has to be set by access usb host register */
USBHSH->PORTMODE |= USBHSH_PORTMODE_DEV_ENABLE_MASK;
/* enable usb1 host clock */
CLOCK_DisableClock(kCLOCK_Usbh1);
#endif
#if (defined CFG_TUSB_RHPORT0_MODE) && (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
// Enable USB Clock Adjustments to trim the FRO for the full speed controller
ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_USBCLKADJ_MASK;
CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1, false);
CLOCK_AttachClk(kFRO_HF_to_USB0_CLK);
/* enable usb0 host clock */
CLOCK_EnableClock(kCLOCK_Usbhsl0);
/*According to reference mannual, device mode setting has to be set by access usb host register */
USBFSH->PORTMODE |= USBFSH_PORTMODE_DEV_ENABLE_MASK;
/* disable usb0 host clock */
CLOCK_DisableClock(kCLOCK_Usbhsl0);
CLOCK_EnableUsbfs0DeviceClock(kCLOCK_UsbfsSrcFro, CLOCK_GetFreq(kCLOCK_FroHf)); /* enable USB Device clock */
#endif
}
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
void board_led_write(bool state)
{
GPIO_PinWrite(GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
if (state) {
sctpix_setPixel(NEOPIXEL_CH, 0, 0x100000);
sctpix_setPixel(NEOPIXEL_CH, 1, 0x101010);
} else {
sctpix_setPixel(NEOPIXEL_CH, 0, 0x001000);
sctpix_setPixel(NEOPIXEL_CH, 1, 0x000010);
}
sctpix_show();
}
uint32_t board_button_read(void)
{
// active low
return BUTTON_STATE_ACTIVE == GPIO_PinRead(GPIO, BUTTON_PORT, BUTTON_PIN);
}
int board_uart_read(uint8_t* buf, int len)
{
(void) buf; (void) len;
return 0;
}
int board_uart_write(void const * buf, int len)
{
(void) buf; (void) len;
return 0;
}
#if CFG_TUSB_OS == OPT_OS_NONE
volatile uint32_t system_ticks = 0;
void SysTick_Handler(void)
{
system_ticks++;
}
uint32_t board_millis(void)
{
return system_ticks;
}
#endif

View File

@ -0,0 +1,46 @@
CFLAGS += \
-mthumb \
-mabi=aapcs \
-mcpu=cortex-m0plus \
-DCPU_MKL25Z128VLK4 \
-DCFG_TUSB_MCU=OPT_MCU_MKL25ZXX
# mcu driver cause following warnings
CFLAGS += -Wno-error=unused-parameter
MCU_DIR = hw/mcu/nxp/sdk/devices/MKL25Z4
# All source paths should be relative to the top level.
LD_FILE = $(MCU_DIR)/gcc/MKL25Z128xxx4_flash.ld
SRC_C += \
$(MCU_DIR)/system_MKL25Z4.c \
$(MCU_DIR)/project_template/clock_config.c \
$(MCU_DIR)/drivers/fsl_clock.c \
$(MCU_DIR)/drivers/fsl_gpio.c \
$(MCU_DIR)/drivers/fsl_lpsci.c
INC += \
$(TOP)/hw/bsp/$(BOARD) \
$(TOP)/$(MCU_DIR)/../../CMSIS/Include \
$(TOP)/$(MCU_DIR) \
$(TOP)/$(MCU_DIR)/drivers \
$(TOP)/$(MCU_DIR)/project_template \
SRC_S += $(MCU_DIR)/gcc/startup_MKL25Z4.S
# For TinyUSB port source
VENDOR = nxp
CHIP_FAMILY = khci
# For freeRTOS port source
FREERTOS_PORT = ARM_CM0
# For flash-jlink target
JLINK_DEVICE = MKL25Z128xxx4
# For flash-pyocd target
PYOCD_TARGET = mkl25zl128
# flash using pyocd
flash: flash-pyocd

View File

@ -0,0 +1,148 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
* Copyright (c) 2020, Koji Kitayama
*
* 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 file is part of the TinyUSB stack.
*/
#include "../board.h"
#include "fsl_device_registers.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "fsl_clock.h"
#include "fsl_lpsci.h"
#include "clock_config.h"
//--------------------------------------------------------------------+
// Forward USB interrupt events to TinyUSB IRQ Handler
//--------------------------------------------------------------------+
void USB0_IRQHandler(void)
{
tud_int_handler(0);
}
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// LED
#define LED_PINMUX IOMUXC_GPIO_AD_B0_09_GPIO1_IO09
#define LED_PORT GPIOB
#define LED_PIN_CLOCK kCLOCK_PortB
#define LED_PIN_PORT PORTB
#define LED_PIN 19U
#define LED_PIN_FUNCTION kPORT_MuxAsGpio
#define LED_STATE_ON 0
// UART
#define UART_PORT UART0
#define UART_PIN_CLOCK kCLOCK_PortA
#define UART_PIN_PORT PORTA
#define UART_PIN_RX 1u
#define UART_PIN_TX 2u
#define UART_PIN_FUNCTION kPORT_MuxAlt2
#define SOPT5_UART0RXSRC_UART_RX 0x00u /*!< UART0 receive data source select: UART0_RX pin */
#define SOPT5_UART0TXSRC_UART_TX 0x00u /*!< UART0 transmit data source select: UART0_TX pin */
const uint8_t dcd_data[] = { 0x00 };
void board_init(void)
{
BOARD_BootClockRUN();
SystemCoreClockUpdate();
#if CFG_TUSB_OS == OPT_OS_NONE
// 1ms tick timer
SysTick_Config(SystemCoreClock / 1000);
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
#endif
// LED
CLOCK_EnableClock(LED_PIN_CLOCK);
PORT_SetPinMux(LED_PIN_PORT, LED_PIN, LED_PIN_FUNCTION);
gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0 };
GPIO_PinInit(LED_PORT, LED_PIN, &led_config);
board_led_write(true);
// UART
CLOCK_EnableClock(UART_PIN_CLOCK);
PORT_SetPinMux(UART_PIN_PORT, UART_PIN_RX, UART_PIN_FUNCTION);
PORT_SetPinMux(UART_PIN_PORT, UART_PIN_TX, UART_PIN_FUNCTION);
SIM->SOPT5 = ((SIM->SOPT5 &
(~(SIM_SOPT5_UART0TXSRC_MASK | SIM_SOPT5_UART0RXSRC_MASK)))
| SIM_SOPT5_UART0TXSRC(SOPT5_UART0TXSRC_UART_TX)
| SIM_SOPT5_UART0RXSRC(SOPT5_UART0RXSRC_UART_RX)
);
lpsci_config_t uart_config;
CLOCK_SetLpsci0Clock(1);
LPSCI_GetDefaultConfig(&uart_config);
uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
uart_config.enableTx = true;
uart_config.enableRx = true;
LPSCI_Init(UART_PORT, &uart_config, CLOCK_GetPllFllSelClkFreq());
// USB
CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcPll0, CLOCK_GetFreq(kCLOCK_PllFllSelClk));
}
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
void board_led_write(bool state)
{
GPIO_WritePinOutput(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
}
uint32_t board_button_read(void)
{
return 0;
}
int board_uart_read(uint8_t* buf, int len)
{
LPSCI_ReadBlocking(UART_PORT, buf, len);
return len;
}
int board_uart_write(void const * buf, int len)
{
LPSCI_WriteBlocking(UART_PORT, (uint8_t*)buf, len);
return len;
}
#if CFG_TUSB_OS == OPT_OS_NONE
volatile uint32_t system_ticks = 0;
void SysTick_Handler(void)
{
system_ticks++;
}
uint32_t board_millis(void)
{
return system_ticks;
}
#endif

View File

@ -15,8 +15,14 @@ CFLAGS += -Wno-error=undef -Wno-error=unused-parameter -Wno-error=cast-align
# due to tusb_hal_nrf_power_event
GCCVERSION = $(firstword $(subst ., ,$(shell arm-none-eabi-gcc -dumpversion)))
ifeq ($(shell expr $(GCCVERSION) \>= 8), 1)
CFLAGS += -Wno-error=cast-function-type
ifeq ($(CMDEXE),1)
ifeq ($(shell if $(GCCVERSION) geq 8 echo 1), 1)
CFLAGS += -Wno-error=cast-function-type
endif
else
ifeq ($(shell expr $(GCCVERSION) \>= 8), 1)
CFLAGS += -Wno-error=cast-function-type
endif
endif
# All source paths should be relative to the top level.

View File

@ -0,0 +1,99 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*
* This file is part of the TinyUSB stack.
*/
#include "pico/stdlib.h"
#include "../board.h"
#ifndef LED_PIN
#define LED_PIN PICO_DEFAULT_LED_PIN
#endif
void board_init(void)
{
setup_default_uart();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, 1);
// Button
// todo probably set up device mode?
#if TUSB_OPT_HOST_ENABLED
// set portfunc to host !!!
#endif
}
////--------------------------------------------------------------------+
//// USB Interrupt Handler
////--------------------------------------------------------------------+
//void USB_IRQHandler(void)
//{
//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
// tuh_isr(0);
//#endif
//
//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
// tud_int_handler(0);
//#endif
//}
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
void board_led_write(bool state)
{
gpio_put(LED_PIN, state);
}
uint32_t board_button_read(void)
{
return 0;
}
int board_uart_read(uint8_t* buf, int len)
{
for(int i=0;i<len;i++) {
buf[i] = uart_getc(uart_default);
}
return 0;
}
int board_uart_write(void const * buf, int len)
{
// UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
for(int i=0;i<len;i++) {
uart_putc(uart_default, ((char *)buf)[i]);
}
return 0;
}
#if CFG_TUSB_OS == OPT_OS_NONE
uint32_t board_millis(void)
{
return to_ms_since_boot(get_absolute_time());
}
#endif

@ -1 +1 @@
Subproject commit b618cb1d521cc9e133bdcd0fca154dee2d925dfe
Subproject commit 587c65766538a5e1cfb6188ac611ded61f2eb859

1
lib/sct_neopixel Submodule

@ -0,0 +1 @@
Subproject commit e73e04ca63495672d955f9268e003cffe168fcd8

View File

@ -46,7 +46,7 @@
#endif
#ifndef CFG_TUD_HID_EP_BUFSIZE
#define CFG_TUD_HID_EP_BUFSIZE 16
#define CFG_TUD_HID_EP_BUFSIZE 64
#endif
//--------------------------------------------------------------------+

View File

@ -39,6 +39,7 @@ typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
bool valid;
uint16_t report_size;
}hidh_interface_t;
@ -53,6 +54,7 @@ static inline bool hidh_interface_open(uint8_t rhport, uint8_t dev_addr, uint8_t
p_hid->ep_in = p_endpoint_desc->bEndpointAddress;
p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
p_hid->itf_num = interface_number;
p_hid->valid = true;
return true;
}
@ -246,14 +248,14 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
usbh_driver_set_config_complete(dev_addr, itf_num);
#if CFG_TUH_HID_KEYBOARD
if ( keyboardh_data[dev_addr-1].itf_num == itf_num)
if (( keyboardh_data[dev_addr-1].itf_num == itf_num) && keyboardh_data[dev_addr-1].valid)
{
tuh_hid_keyboard_mounted_cb(dev_addr);
}
#endif
#if CFG_TUH_HID_MOUSE
if ( mouseh_data[dev_addr-1].ep_in == itf_num )
if (( mouseh_data[dev_addr-1].ep_in == itf_num ) && mouseh_data[dev_addr-1].valid)
{
tuh_hid_mouse_mounted_cb(dev_addr);
}

View File

@ -255,7 +255,7 @@ typedef struct TU_ATTR_PACKED
uint8_t : 3;
uint8_t disable_block_descriptor : 1;
uint8_t : 0;
uint8_t : 4;
uint8_t page_code : 6;
uint8_t page_control : 2;

View File

@ -71,6 +71,7 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
@ -223,179 +224,6 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
return true;
}
// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
// In case of a failed status, sense key must be set for reason of failure
int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
{
(void) bufsize; // TODO refractor later
int32_t resplen;
switch ( scsi_cmd[0] )
{
case SCSI_CMD_TEST_UNIT_READY:
resplen = 0;
if ( !tud_msc_test_unit_ready_cb(lun) )
{
// Failed status response
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
break;
case SCSI_CMD_START_STOP_UNIT:
resplen = 0;
if (tud_msc_start_stop_cb)
{
scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
{
// Failed status response
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
}
break;
case SCSI_CMD_READ_CAPACITY_10:
{
uint32_t block_count;
uint32_t block_size;
uint16_t block_size_u16;
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
block_size = (uint32_t) block_size_u16;
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
if (block_count == 0 || block_size == 0)
{
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
scsi_read_capacity10_resp_t read_capa10;
read_capa10.last_lba = tu_htonl(block_count-1);
read_capa10.block_size = tu_htonl(block_size);
resplen = sizeof(read_capa10);
memcpy(buffer, &read_capa10, resplen);
}
}
break;
case SCSI_CMD_READ_FORMAT_CAPACITY:
{
scsi_read_format_capacity_data_t read_fmt_capa =
{
.list_length = 8,
.block_num = 0,
.descriptor_type = 2, // formatted media
.block_size_u16 = 0
};
uint32_t block_count;
uint16_t block_size;
tud_msc_capacity_cb(lun, &block_count, &block_size);
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
if (block_count == 0 || block_size == 0)
{
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
read_fmt_capa.block_num = tu_htonl(block_count);
read_fmt_capa.block_size_u16 = tu_htons(block_size);
resplen = sizeof(read_fmt_capa);
memcpy(buffer, &read_fmt_capa, resplen);
}
}
break;
case SCSI_CMD_INQUIRY:
{
scsi_inquiry_resp_t inquiry_rsp =
{
.is_removable = 1,
.version = 2,
.response_data_format = 2,
};
// vendor_id, product_id, product_rev is space padded string
memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
resplen = sizeof(inquiry_rsp);
memcpy(buffer, &inquiry_rsp, resplen);
}
break;
case SCSI_CMD_MODE_SENSE_6:
{
scsi_mode_sense6_resp_t mode_resp =
{
.data_len = 3,
.medium_type = 0,
.write_protected = false,
.reserved = 0,
.block_descriptor_len = 0 // no block descriptor are included
};
bool writable = true;
if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
resplen = sizeof(mode_resp);
memcpy(buffer, &mode_resp, resplen);
}
break;
case SCSI_CMD_REQUEST_SENSE:
{
scsi_sense_fixed_resp_t sense_rsp =
{
.response_code = 0x70,
.valid = 1
};
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
sense_rsp.sense_key = _mscd_itf.sense_key;
sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
resplen = sizeof(sense_rsp);
memcpy(buffer, &sense_rsp, resplen);
// Clear sense data after copy
tud_msc_set_sense(lun, 0, 0, 0);
}
break;
default: resplen = -1; break;
}
return resplen;
}
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
@ -640,6 +468,180 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
/*------------------------------------------------------------------*/
/* SCSI Command Process
*------------------------------------------------------------------*/
// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
// In case of a failed status, sense key must be set for reason of failure
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
{
(void) bufsize; // TODO refractor later
int32_t resplen;
switch ( scsi_cmd[0] )
{
case SCSI_CMD_TEST_UNIT_READY:
resplen = 0;
if ( !tud_msc_test_unit_ready_cb(lun) )
{
// Failed status response
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
break;
case SCSI_CMD_START_STOP_UNIT:
resplen = 0;
if (tud_msc_start_stop_cb)
{
scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
{
// Failed status response
resplen = - 1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
}
break;
case SCSI_CMD_READ_CAPACITY_10:
{
uint32_t block_count;
uint32_t block_size;
uint16_t block_size_u16;
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
block_size = (uint32_t) block_size_u16;
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
if (block_count == 0 || block_size == 0)
{
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
scsi_read_capacity10_resp_t read_capa10;
read_capa10.last_lba = tu_htonl(block_count-1);
read_capa10.block_size = tu_htonl(block_size);
resplen = sizeof(read_capa10);
memcpy(buffer, &read_capa10, resplen);
}
}
break;
case SCSI_CMD_READ_FORMAT_CAPACITY:
{
scsi_read_format_capacity_data_t read_fmt_capa =
{
.list_length = 8,
.block_num = 0,
.descriptor_type = 2, // formatted media
.block_size_u16 = 0
};
uint32_t block_count;
uint16_t block_size;
tud_msc_capacity_cb(lun, &block_count, &block_size);
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
if (block_count == 0 || block_size == 0)
{
resplen = -1;
// If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}else
{
read_fmt_capa.block_num = tu_htonl(block_count);
read_fmt_capa.block_size_u16 = tu_htons(block_size);
resplen = sizeof(read_fmt_capa);
memcpy(buffer, &read_fmt_capa, resplen);
}
}
break;
case SCSI_CMD_INQUIRY:
{
scsi_inquiry_resp_t inquiry_rsp =
{
.is_removable = 1,
.version = 2,
.response_data_format = 2,
};
// vendor_id, product_id, product_rev is space padded string
memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
resplen = sizeof(inquiry_rsp);
memcpy(buffer, &inquiry_rsp, resplen);
}
break;
case SCSI_CMD_MODE_SENSE_6:
{
scsi_mode_sense6_resp_t mode_resp =
{
.data_len = 3,
.medium_type = 0,
.write_protected = false,
.reserved = 0,
.block_descriptor_len = 0 // no block descriptor are included
};
bool writable = true;
if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
resplen = sizeof(mode_resp);
memcpy(buffer, &mode_resp, resplen);
}
break;
case SCSI_CMD_REQUEST_SENSE:
{
scsi_sense_fixed_resp_t sense_rsp =
{
.response_code = 0x70,
.valid = 1
};
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
sense_rsp.sense_key = _mscd_itf.sense_key;
sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
resplen = sizeof(sense_rsp);
memcpy(buffer, &sense_rsp, resplen);
// Clear sense data after copy
tud_msc_set_sense(lun, 0, 0, 0);
}
break;
default: resplen = -1; break;
}
return resplen;
}
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;

View File

@ -102,6 +102,7 @@
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
#elif defined(__ICCARM__)
#include <intrinsics.h>
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))

View File

@ -30,6 +30,12 @@
#include "osal/osal.h"
#include "tusb_fifo.h"
// Supress IAR warning
// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
#if defined(__ICCARM__)
#pragma diag_suppress = Pa082
#endif
// implement mutex lock and unlock
#if CFG_FIFO_MUTEX
@ -106,7 +112,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRe
memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
// Write data wrapped around
memcpy(f->buffer, data + nLin*f->item_size, (n - nLin) * f->item_size);
memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
}
}
@ -131,7 +137,7 @@ static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
// Read data wrapped part
memcpy(p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
}
}

View File

@ -517,7 +517,7 @@ void tud_task (void)
TU_ASSERT(driver, );
TU_LOG2(" %s xfer callback\r\n", driver->name);
driver->xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
}
}
break;
@ -933,6 +933,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
break;
case TUSB_DESC_STRING:
{
TU_LOG2(" String[%u]\r\n", desc_index);
// String Descriptor always uses the desc set from user
@ -941,6 +942,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
// first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]);
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
@ -1101,6 +1103,24 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
{
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, desc_ep->wMaxPacketSize.size);
if (TUSB_XFER_ISOCHRONOUS == desc_ep->bmAttributes.xfer)
{
TU_ASSERT(desc_ep->wMaxPacketSize.size <= (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 1023));
}
else
{
uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 512 : 64);
if (TUSB_XFER_BULK == desc_ep->bmAttributes.xfer)
{
// Bulk must be EXACTLY 512/64 bytes
TU_ASSERT(desc_ep->wMaxPacketSize.size == max_epsize);
}else
{
TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize);
}
}
return dcd_edpt_open(rhport, desc_ep);
}

View File

@ -140,6 +140,14 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
// USBD API
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// Prototypes
//--------------------------------------------------------------------+
void usbd_control_reset(void);
void usbd_control_set_request(tusb_control_request_t const *request);
void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void usbd_control_reset(void)
{
tu_varclr(&_ctrl_xfer);

View File

@ -151,6 +151,12 @@ tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr)
return (tusb_device_state_t) _usbh_devices[dev_addr].state;
}
tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
{
TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG);
return _usbh_devices[dev_addr].speed;
}
void osal_task_delay(uint32_t msec)
{
(void) msec;

View File

@ -86,6 +86,7 @@ extern void hcd_int_handler(uint8_t rhport);
#define tuh_int_handler hcd_int_handler
tusb_device_state_t tuh_device_get_state (uint8_t dev_addr);
tusb_speed_t tuh_device_get_speed (uint8_t dev_addr);
static inline bool tuh_device_is_configured(uint8_t dev_addr)
{
return tuh_device_get_state(dev_addr) == TUSB_DEVICE_STATE_CONFIGURED;

View File

@ -53,6 +53,8 @@ typedef void (*osal_task_func_t)( void * );
#include "osal_freertos.h"
#elif CFG_TUSB_OS == OPT_OS_MYNEWT
#include "osal_mynewt.h"
#elif CFG_TUSB_OS == OPT_OS_PICO
#include "osal_pico.h"
#elif CFG_TUSB_OS == OPT_OS_CUSTOM
#include "tusb_os_custom.h" // implemented by application
#else

192
src/osal/osal_pico.h Normal file
View File

@ -0,0 +1,192 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_OSAL_PICO_H_
#define _TUSB_OSAL_PICO_H_
#include "pico/time.h"
#include "pico/sem.h"
#include "pico/mutex.h"
#include "pico/critical_section.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
#ifndef RP2040_USB_HOST_MODE
static inline void osal_task_delay(uint32_t msec)
{
sleep_ms(msec);
}
#endif
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+
typedef struct semaphore osal_semaphore_def_t, *osal_semaphore_t;
static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
{
sem_init(semdef, 0, 255);
return semdef;
}
static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
{
sem_release(sem_hdl);
return true;
}
static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
{
return sem_acquire_timeout_ms(sem_hdl, msec);
}
static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
{
sem_reset(sem_hdl, 0);
}
//--------------------------------------------------------------------+
// MUTEX API
// Within tinyusb, mutex is never used in ISR context
//--------------------------------------------------------------------+
typedef struct mutex osal_mutex_def_t, *osal_mutex_t;
static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
{
mutex_init(mdef);
return mdef;
}
static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
{
return mutex_enter_timeout_ms(mutex_hdl, msec);
}
static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
{
mutex_exit(mutex_hdl);
return true;
}
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
#include "common/tusb_fifo.h"
#if TUSB_OPT_HOST_ENABLED
extern void hcd_int_disable(uint8_t rhport);
extern void hcd_int_enable(uint8_t rhport);
#endif
typedef struct
{
tu_fifo_t ff;
struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
} osal_queue_def_t;
typedef osal_queue_def_t* osal_queue_t;
// role device/host is used by OS NONE for mutex (disable usb isr) only
#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
osal_queue_def_t _name = { \
.ff = { \
.buffer = _name##_buf, \
.depth = _depth, \
.item_size = sizeof(_type), \
.overwritable = false, \
}\
}
// lock queue by disable USB interrupt
static inline void _osal_q_lock(osal_queue_t qhdl)
{
critical_section_enter_blocking(&qhdl->critsec);
}
// unlock queue
static inline void _osal_q_unlock(osal_queue_t qhdl)
{
critical_section_exit(&qhdl->critsec);
}
static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
{
critical_section_init(&qdef->critsec);
tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
{
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
_osal_q_unlock(qhdl);
return success;
}
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
{
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_write(&qhdl->ff, data);
_osal_q_unlock(qhdl);
TU_ASSERT(success);
return success;
}
static inline bool osal_queue_empty(osal_queue_t qhdl)
{
// TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
// volatile read.
// Skip queue lock/unlock since this function is primarily called
// with interrupt disabled before going into low power mode
return tu_fifo_empty(&qhdl->ff);
}
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_OSAL_PICO_H_ */

View File

@ -664,7 +664,7 @@ static void handle_bus_reset(void)
(void)USB->USB_ALTEV_REG;
_dcd.in_reset = true;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
USB->USB_DMA_CTRL_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
@ -821,14 +821,13 @@ void dcd_disconnect(uint8_t rhport)
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
uint8_t iso_mask = 0;
(void)rhport;
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 1023);
TU_ASSERT(epnum < EP_MAX);
xfer->max_packet_size = desc_edpt->wMaxPacketSize.size;

View File

@ -252,7 +252,6 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 64);
TU_ASSERT(epnum < EP_MAX);
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);

View File

@ -37,14 +37,23 @@
/* MACRO TYPEDEF CONSTANT ENUM
*------------------------------------------------------------------*/
static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
// Setup packet is only 8 bytes in length. However under certain scenario,
// USB DMA controller may decide to overwrite/overflow the buffer with
// 2 extra bytes of CRC. From datasheet's "Management of SETUP Transactions" section
// If the number of received data bytes is the maximum data payload specified by
// PCKSIZE.SIZE minus one, only the first CRC data is written to the data buffer.
// If the number of received data is equal or less than the data payload specified
// by PCKSIZE.SIZE minus two, both CRC data bytes are written to the data buffer.
// Therefore we will need to increase it to 10 bytes here.
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8+2];
// ready for receiving SETUP packet
static inline void prepare_setup(void)
{
// Only make sure the EP0 OUT buffer is ready
sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet;
sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(_setup_packet);
sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(tusb_control_request_t);
sram_registers[0][0].PCKSIZE.bit.BYTE_COUNT = 0;
}
@ -378,7 +387,7 @@ void dcd_int_handler (uint8_t rhport)
USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
// Handle SETUP packet

View File

@ -338,7 +338,7 @@ void dcd_int_handler(uint8_t rhport)
if (intr_status & UDP_ISR_ENDBUSRES_Msk)
{
bus_reset();
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
// SOF

View File

@ -533,7 +533,7 @@ void dcd_int_handler(uint8_t rhport)
if ( int_status & USBD_INTEN_USBRESET_Msk )
{
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
// ISOIN: Data was moved to endpoint buffer, client will be notified in SOF

View File

@ -329,8 +329,7 @@ void dcd_int_handler(uint8_t rhport)
USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
if(state & USBD_STATE_SUSPEND)

View File

@ -340,7 +340,7 @@ void dcd_int_handler(uint8_t rhport)
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
if(state & USBD_ATTR_SUSPEND_Msk)

View File

@ -0,0 +1,477 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Koji Kitayama
*
* 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 file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX )
#include "fsl_device_registers.h"
#define KHCI USB0
#include "device/dcd.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
enum {
TOK_PID_OUT = 0x1u,
TOK_PID_IN = 0x9u,
TOK_PID_SETUP = 0xDu,
};
typedef struct TU_ATTR_PACKED
{
union {
uint32_t head;
struct {
union {
struct {
uint16_t : 2;
uint16_t tok_pid : 4;
uint16_t data : 1;
uint16_t own : 1;
uint16_t : 8;
};
struct {
uint16_t : 2;
uint16_t bdt_stall: 1;
uint16_t dts : 1;
uint16_t ninc : 1;
uint16_t keep : 1;
uint16_t : 10;
};
};
uint16_t bc : 10;
uint16_t : 6;
};
};
uint8_t *addr;
}buffer_descriptor_t;
TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
typedef struct TU_ATTR_PACKED
{
union {
uint32_t state;
struct {
uint32_t max_packet_size :11;
uint32_t : 5;
uint32_t odd : 1;
uint32_t :15;
};
};
uint16_t length;
uint16_t remaining;
}endpoint_state_t;
TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
typedef struct
{
union {
/* [#EP][OUT,IN][EVEN,ODD] */
buffer_descriptor_t bdt[16][2][2];
uint16_t bda[512];
};
TU_ATTR_ALIGNED(4) union {
endpoint_state_t endpoint[16][2];
endpoint_state_t endpoint_unified[16 * 2];
};
uint8_t setup_packet[8];
uint8_t addr;
}dcd_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// BDT(Buffer Descriptor Table) must be 256-byte aligned
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
static void prepare_next_setup_packet(uint8_t rhport)
{
const unsigned out_odd = _dcd.endpoint[0][0].odd;
const unsigned in_odd = _dcd.endpoint[0][1].odd;
if (_dcd.bdt[0][0][out_odd].own) {
TU_LOG1("DCD fail to prepare the next SETUP %d %d\r\n", out_odd, in_odd);
return;
}
_dcd.bdt[0][0][out_odd].data = 0;
_dcd.bdt[0][0][out_odd ^ 1].data = 1;
_dcd.bdt[0][1][in_odd].data = 1;
_dcd.bdt[0][1][in_odd ^ 1].data = 0;
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
_dcd.setup_packet, sizeof(_dcd.setup_packet));
}
static void process_stall(uint8_t rhport)
{
if (KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK) {
/* clear stall condition of the control pipe */
prepare_next_setup_packet(rhport);
KHCI->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
}
}
static void process_tokdne(uint8_t rhport)
{
const unsigned s = KHCI->STAT;
KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
/* fetch pid before discarded by the next steps */
const unsigned pid = bd->tok_pid;
/* reset values for a next transfer */
bd->bdt_stall = 0;
bd->dts = 1;
bd->ninc = 0;
bd->keep = 0;
/* update the odd variable to prepare for the next transfer */
ep->odd = odd ^ 1;
if (pid == TOK_PID_SETUP) {
dcd_event_setup_received(rhport, bd->addr, true);
KHCI->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
return;
}
if (s >> 4) {
TU_LOG1("TKDNE %x\r\n", s);
}
const unsigned bc = bd->bc;
const unsigned remaining = ep->remaining - bc;
if (remaining && bc == ep->max_packet_size) {
/* continue the transferring consecutive data */
ep->remaining = remaining;
const int next_remaining = remaining - ep->max_packet_size;
if (next_remaining > 0) {
/* prepare to the after next transfer */
bd->addr += ep->max_packet_size * 2;
bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
__DSB();
bd->own = 1; /* the own bit must set after addr */
}
return;
}
const unsigned length = ep->length;
dcd_event_xfer_complete(rhport,
((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT),
length - remaining, XFER_RESULT_SUCCESS, true);
if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) {
/* After completion a ZLP of control transfer,
* it prepares for the next steup transfer. */
if (_dcd.addr) {
/* When the transfer was the SetAddress,
* the device address should be updated here. */
KHCI->ADDR = _dcd.addr;
_dcd.addr = 0;
}
prepare_next_setup_packet(rhport);
}
}
static void process_bus_reset(uint8_t rhport)
{
KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
KHCI->CTL |= USB_CTL_ODDRST_MASK;
KHCI->ADDR = 0;
KHCI->INTEN = (KHCI->INTEN & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
for (unsigned i = 1; i < 16; ++i) {
KHCI->ENDPOINT[i].ENDPT = 0;
}
buffer_descriptor_t *bd = _dcd.bdt[0][0];
for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
bd->head = 0;
}
const endpoint_state_t ep0 = {
.max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
.odd = 0,
.length = 0,
.remaining = 0,
};
_dcd.endpoint[0][0] = ep0;
_dcd.endpoint[0][1] = ep0;
tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
_dcd.addr = 0;
prepare_next_setup_packet(rhport);
KHCI->CTL &= ~USB_CTL_ODDRST_MASK;
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
static void process_bus_inactive(uint8_t rhport)
{
(void) rhport;
const unsigned inten = KHCI->INTEN;
KHCI->INTEN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
KHCI->USBCTRL |= USB_USBCTRL_SUSP_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
static void process_bus_active(uint8_t rhport)
{
(void) rhport;
KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
const unsigned inten = KHCI->INTEN;
KHCI->INTEN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
void dcd_init(uint8_t rhport)
{
(void) rhport;
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
tu_memclr(&_dcd, sizeof(_dcd));
KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB0_IRQn);
}
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
KHCI->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK |
USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
NVIC_EnableIRQ(USB0_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB0_IRQn);
KHCI->INTEN = 0;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
_dcd.addr = dev_addr & 0x7F;
/* Response with status first before changing device address */
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
unsigned cnt = SystemCoreClock / 100;
KHCI->CTL |= USB_CTL_RESUME_MASK;
while (cnt--) __NOP();
KHCI->CTL &= ~USB_CTL_RESUME_MASK;
}
void dcd_connect(uint8_t rhport)
{
(void) rhport;
KHCI->USBCTRL = 0;
KHCI->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
KHCI->CTL |= USB_CTL_USBENSOFEN_MASK;
}
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
KHCI->CTL = 0;
KHCI->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
const unsigned ep_addr = ep_desc->bEndpointAddress;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
const unsigned xfer = ep_desc->bmAttributes.xfer;
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
const unsigned odd = ep->odd;
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
/* No support for control transfer */
TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
ep->max_packet_size = ep_desc->wMaxPacketSize.size;
unsigned val = USB_ENDPT_EPCTLDIS_MASK;
val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
KHCI->ENDPOINT[epn].ENDPT |= val;
if (xfer != TUSB_XFER_ISOCHRONOUS) {
bd[odd].dts = 1;
bd[odd].data = 0;
bd[odd ^ 1].dts = 1;
bd[odd ^ 1].data = 1;
}
return true;
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
KHCI->ENDPOINT[epn].ENDPT &= ~msk;
ep->max_packet_size = 0;
ep->length = 0;
ep->remaining = 0;
bd->head = 0;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void) rhport;
NVIC_DisableIRQ(USB0_IRQn);
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
if (bd->own) {
TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head);
return false; /* The last transfer has not completed */
}
ep->length = total_bytes;
ep->remaining = total_bytes;
const unsigned mps = ep->max_packet_size;
if (total_bytes > mps) {
buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
/* When total_bytes is greater than the max packet size,
* it prepares to the next transfer to avoid NAK in advance. */
next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps;
next->addr = buffer + mps;
next->own = 1;
}
bd->bc = total_bytes >= mps ? mps: total_bytes;
bd->addr = buffer;
__DSB();
bd->own = 1; /* the own bit must set after addr */
NVIC_EnableIRQ(USB0_IRQn);
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
if (0 == epn) {
KHCI->ENDPOINT[epn].ENDPT |= USB_ENDPT_EPSTALL_MASK;
} else {
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
bd[0].bdt_stall = 1;
bd[1].bdt_stall = 1;
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
const unsigned epn = ep_addr & 0xFu;
const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
const unsigned odd = _dcd.endpoint[epn][dir].odd;
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
bd[odd ^ 1].own = 0;
bd[odd ^ 1].data = 1;
bd[odd ^ 1].bdt_stall = 0;
bd[odd].own = 0;
bd[odd].data = 0;
bd[odd].bdt_stall = 0;
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
uint32_t is = KHCI->ISTAT;
uint32_t msk = KHCI->INTEN;
KHCI->ISTAT = is & ~msk;
is &= msk;
if (is & USB_ISTAT_ERROR_MASK) {
/* TODO: */
uint32_t es = KHCI->ERRSTAT;
KHCI->ERRSTAT = es;
KHCI->ISTAT = is; /* discard any pending events */
return;
}
if (is & USB_ISTAT_USBRST_MASK) {
KHCI->ISTAT = is; /* discard any pending events */
process_bus_reset(rhport);
return;
}
if (is & USB_ISTAT_SLEEP_MASK) {
KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
process_bus_inactive(rhport);
return;
}
if (is & USB_ISTAT_RESUME_MASK) {
KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
process_bus_active(rhport);
return;
}
if (is & USB_ISTAT_SOFTOK_MASK) {
KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
return;
}
if (is & USB_ISTAT_STALL_MASK) {
KHCI->ISTAT = USB_ISTAT_STALL_MASK;
process_stall(rhport);
return;
}
if (is & USB_ISTAT_TOKDNE_MASK) {
process_tokdne(rhport);
return;
}
}
#endif

View File

@ -471,7 +471,7 @@ static void bus_event_isr(uint8_t rhport)
if (dev_status & SIE_DEV_STATUS_RESET_MASK)
{
bus_reset();
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)

View File

@ -243,7 +243,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
(void) rhport;
// TODO not support ISO yet
if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return false;
TU_VERIFY(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
//------------- Prepare Queue Head -------------//
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
@ -357,7 +357,7 @@ void dcd_int_handler(uint8_t rhport)
if ( cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
{
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
if (cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)

View File

@ -0,0 +1,482 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
#include "pico.h"
#include "rp2040_usb.h"
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
#include "pico/fix/rp2040_usb_device_enumeration.h"
#endif
#include "device/dcd.h"
#include "pico/stdlib.h"
/*------------------------------------------------------------------*/
/* Low level controller
*------------------------------------------------------------------*/
#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_clear hw_clear_alias(usb_hw)
// Init these in dcd_init
static uint8_t assigned_address;
static uint8_t *next_buffer_ptr;
// Endpoints 0-15, direction 0 for out and 1 for in.
static struct hw_endpoint hw_endpoints[16][2] = {0};
static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, uint8_t in)
{
return &hw_endpoints[num][in];
}
static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
{
uint8_t num = tu_edpt_number(ep_addr);
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
return hw_endpoint_get_by_num(num, in);
}
static void _hw_endpoint_alloc(struct hw_endpoint *ep)
{
uint size = 64;
if (ep->wMaxPacketSize > 64)
{
size = ep->wMaxPacketSize;
}
// Assumes single buffered for now
ep->hw_data_buf = next_buffer_ptr;
next_buffer_ptr += size;
// Bits 0-5 are ignored by the controller so make sure these are 0
if ((uintptr_t)next_buffer_ptr & 0b111111u)
{
// Round up to the next 64
uint32_t fixptr = (uintptr_t)next_buffer_ptr;
fixptr &= ~0b111111u;
fixptr += 64;
pico_info("Rounding non 64 byte boundary buffer up from %x to %x\n", (uintptr_t)next_buffer_ptr, fixptr);
next_buffer_ptr = (uint8_t*)fixptr;
}
assert(((uintptr_t)next_buffer_ptr & 0b111111u) == 0);
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n",
size,
dpram_offset,
ep->hw_data_buf,
ep->num,
ep_dir_string[ep->in]);
// Fill in endpoint control register with buffer offset
uint32_t reg = EP_CTRL_ENABLE_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
| dpram_offset;
*ep->endpoint_control = reg;
}
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type)
{
uint8_t num = tu_edpt_number(ep_addr);
bool in = ep_addr & TUSB_DIR_IN_MASK;
ep->ep_addr = ep_addr;
ep->in = in;
// For device, IN is a tx transfer and OUT is an rx transfer
ep->rx = in == false;
ep->num = num;
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = num == 0 ? 1u : 0u;
// Add some checks around the max packet size
if (transfer_type == TUSB_XFER_ISOCHRONOUS)
{
if (wMaxPacketSize > USB_MAX_ISO_PACKET_SIZE)
{
panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
}
}
else
{
if (wMaxPacketSize > USB_MAX_PACKET_SIZE)
{
panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
}
}
ep->wMaxPacketSize = wMaxPacketSize;
ep->transfer_type = transfer_type;
// Every endpoint has a buffer control register in dpram
if (ep->in)
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
}
else
{
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
}
// Clear existing buffer control state
*ep->buffer_control = 0;
if (ep->num == 0)
{
// EP0 has no endpoint control register because
// the buffer offsets are fixed
ep->endpoint_control = NULL;
// Buffer offset is fixed
ep->hw_data_buf = (uint8_t*)&usb_dpram->ep0_buf_a[0];
}
else
{
// Set the endpoint control register (starts at EP1, hence num-1)
if (in)
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].in;
}
else
{
ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].out;
}
// Now alloc a buffer and fill in endpoint control register
_hw_endpoint_alloc(ep);
}
ep->configured = true;
}
#if 0 // todo unused
static void _hw_endpoint_close(struct hw_endpoint *ep)
{
// Clear hardware registers and then zero the struct
// Clears endpoint enable
*ep->endpoint_control = 0;
// Clears buffer available, etc
*ep->buffer_control = 0;
// Clear any endpoint state
memset(ep, 0, sizeof(struct hw_endpoint));
}
static void hw_endpoint_close(uint8_t ep_addr)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_close(ep);
}
#endif
static void hw_endpoint_init(uint8_t ep_addr, uint wMaxPacketSize, uint8_t bmAttributes)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes);
}
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool start)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_xfer(ep, buffer, total_bytes, start);
}
static void hw_handle_buff_status(void)
{
uint32_t remaining_buffers = usb_hw->buf_status;
pico_trace("buf_status 0x%08x\n", remaining_buffers);
uint bit = 1u;
for (uint i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
{
if (remaining_buffers & bit)
{
uint __unused which = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
// Should be single buffered
assert(which == 0);
// clear this in advance
usb_hw_clear->buf_status = bit;
// IN transfer for even i, OUT transfer for odd i
struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u));
// Continue xfer
bool done = _hw_endpoint_xfer_continue(ep);
if (done)
{
// Notify
dcd_event_xfer_complete(0, ep->ep_addr, ep->len, XFER_RESULT_SUCCESS, true);
hw_endpoint_reset_transfer(ep);
}
remaining_buffers &= ~bit;
}
bit <<= 1u;
}
}
static void reset_ep0(void)
{
// If we have finished this transfer on EP0 set pid back to 1 for next
// setup transfer. Also clear a stall in case
uint8_t addrs[] = {0x0, 0x80};
for (uint i = 0 ; i < count_of(addrs); i++)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
ep->next_pid = 1u;
ep->stalled = 0;
}
}
static void ep0_0len_status(void)
{
// Send 0len complete response on EP0 IN
reset_ep0();
hw_endpoint_xfer(0x80, NULL, 0, true);
}
static void _hw_endpoint_stall(struct hw_endpoint *ep)
{
assert(!ep->stalled);
if (ep->num == 0)
{
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
usb_hw_set->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_set_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = true;
}
static void hw_endpoint_stall(uint8_t ep_addr)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_stall(ep);
}
static void _hw_endpoint_clear_stall(struct hw_endpoint *ep)
{
if (ep->num == 0)
{
// Probably already been cleared but no harm
usb_hw_clear->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
ep->stalled = false;
}
static void hw_endpoint_clear_stall(uint8_t ep_addr)
{
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
_hw_endpoint_clear_stall(ep);
}
static void dcd_rp2040_irq(void)
{
uint32_t status = usb_hw->ints;
uint32_t handled = 0;
if (status & USB_INTS_SETUP_REQ_BITS)
{
handled |= USB_INTS_SETUP_REQ_BITS;
uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
// Clear stall bits and reset pid
reset_ep0();
// Pass setup packet to tiny usb
dcd_event_setup_received(0, setup, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
}
if (status & USB_INTS_BUFF_STATUS_BITS)
{
handled |= USB_INTS_BUFF_STATUS_BITS;
hw_handle_buff_status();
}
if (status & USB_INTS_BUS_RESET_BITS)
{
pico_trace("BUS RESET (addr %d -> %d)\n", assigned_address, 0);
assigned_address = 0;
usb_hw->dev_addr_ctrl = assigned_address;
handled |= USB_INTS_BUS_RESET_BITS;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
rp2040_usb_device_enumeration_fix();
#endif
}
if (status ^ handled)
{
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
}
}
#define USB_INTS_ERROR_BITS ( \
USB_INTS_ERROR_DATA_SEQ_BITS | \
USB_INTS_ERROR_BIT_STUFF_BITS | \
USB_INTS_ERROR_CRC_BITS | \
USB_INTS_ERROR_RX_OVERFLOW_BITS | \
USB_INTS_ERROR_RX_TIMEOUT_BITS)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
void dcd_init (uint8_t rhport)
{
pico_trace("dcd_init %d\n", rhport);
assert(rhport == 0);
// Reset hardware to default state
rp2040_usb_init();
irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
memset(hw_endpoints, 0, sizeof(hw_endpoints));
assigned_address = 0;
next_buffer_ptr = &usb_dpram->epx_data[0];
// EP0 always exists so init it now
// EP0 OUT
hw_endpoint_init(0x0, 64, 0);
// EP0 IN
hw_endpoint_init(0x80, 64, 0);
// Initializes the USB peripheral for device mode and enables it.
// Don't need to enable the pull up here. Force VBUS
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
// Enable individual controller IRQS here. Processor interrupt enable will be used
// for the global interrupt enable...
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS;
dcd_connect(rhport);
}
void dcd_int_enable(uint8_t rhport)
{
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, true);
}
void dcd_int_disable(uint8_t rhport)
{
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, false);
}
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
{
pico_trace("dcd_set_address %d %d\n", rhport, dev_addr);
assert(rhport == 0);
// Can't set device address in hardware until status xfer has complete
assigned_address = dev_addr;
ep0_0len_status();
}
void dcd_remote_wakeup(uint8_t rhport)
{
panic("dcd_remote_wakeup %d\n", rhport);
assert(rhport == 0);
}
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
pico_info("dcd_disconnect %d\n", rhport);
assert(rhport == 0);
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
pico_info("dcd_connect %d\n", rhport);
assert(rhport == 0);
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
{
pico_trace("dcd_edpt0_status_complete %d\n", rhport);
assert(rhport == 0);
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS)
{
pico_trace("Set HW address %d\n", assigned_address);
usb_hw->dev_addr_ctrl = assigned_address;
}
reset_ep0();
}
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
pico_info("dcd_edpt_open %d %02x\n", rhport, desc_edpt->bEndpointAddress);
assert(rhport == 0);
hw_endpoint_init(desc_edpt->bEndpointAddress, desc_edpt->wMaxPacketSize.size, desc_edpt->bmAttributes.xfer);
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
assert(rhport == 0);
// True means start new xfer
hw_endpoint_xfer(ep_addr, buffer, total_bytes, true);
return true;
}
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
pico_trace("dcd_edpt_stall %d %02x\n", rhport, ep_addr);
assert(rhport == 0);
hw_endpoint_stall(ep_addr);
}
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
pico_trace("dcd_edpt_clear_stall %d %02x\n", rhport, ep_addr);
assert(rhport == 0);
hw_endpoint_clear_stall(ep_addr);
}
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
{
// usbd.c says: In progress transfers on this EP may be delivered after this call
pico_trace("dcd_edpt_close %d %02x\n", rhport, ep_addr);
}
#endif

View File

@ -0,0 +1,549 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_HOST_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
#include "pico.h"
#include "rp2040_usb.h"
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "osal/osal.h"
#include "host/hcd.h"
#include "host/usbh.h"
#include "host/usbh_hcd.h"
#define ROOT_PORT 0
//--------------------------------------------------------------------+
// Low level rp2040 controller functions
//--------------------------------------------------------------------+
#ifndef PICO_USB_HOST_INTERRUPT_ENDPOINTS
#define PICO_USB_HOST_INTERRUPT_ENDPOINTS (USB_MAX_ENDPOINTS - 1)
#endif
static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
// Host mode uses one shared endpoint register for non-interrupt endpoint
struct hw_endpoint eps[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
#define epx (eps[0])
#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_clear hw_clear_alias(usb_hw)
// Used for hcd pipe busy.
// todo still a bit wasteful
// top bit set if valid
uint8_t dev_ep_map[CFG_TUSB_HOST_DEVICE_MAX][1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS][2];
// Flags we set by default in sie_ctrl (we add other bits on top)
static uint32_t sie_ctrl_base = USB_SIE_CTRL_SOF_EN_BITS |
USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
USB_SIE_CTRL_PULLDOWN_EN_BITS |
USB_SIE_CTRL_EP0_INT_1BUF_BITS;
static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
{
uint8_t num = tu_edpt_number(ep_addr);
if (num == 0) {
return &epx;
}
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
uint mapping = dev_ep_map[dev_addr-1][num][in];
pico_trace("Get dev addr %d ep %d = %d\n", dev_addr, ep_addr, mapping);
return mapping >= 128 ? eps + (mapping & 0x7fu) : NULL;
}
static void set_dev_ep(uint8_t dev_addr, uint8_t ep_addr, struct hw_endpoint *ep)
{
uint8_t num = tu_edpt_number(ep_addr);
uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
uint32_t index = ep - eps;
hard_assert(index < count_of(eps));
// todo revisit why dev_addr can be 0 here
if (dev_addr) {
dev_ep_map[dev_addr-1][num][in] = 128u | index;
}
pico_trace("Set dev addr %d ep %d = %d\n", dev_addr, ep_addr, index);
}
static inline uint8_t dev_speed(void)
{
return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
}
static bool need_pre(uint8_t dev_addr)
{
// If this device is different to the speed of the root device
// (i.e. is a low speed device on a full speed hub) then need pre
return hcd_port_speed_get(0) != tuh_device_get_speed(dev_addr);
}
static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result)
{
// Mark transfer as done before we tell the tinyusb stack
uint8_t dev_addr = ep->dev_addr;
uint8_t ep_addr = ep->ep_addr;
uint total_len = ep->total_len;
hw_endpoint_reset_transfer(ep);
hcd_event_xfer_complete(dev_addr, ep_addr, total_len, xfer_result, true);
}
static void _handle_buff_status_bit(uint bit, struct hw_endpoint *ep)
{
usb_hw_clear->buf_status = bit;
bool done = _hw_endpoint_xfer_continue(ep);
if (done)
{
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
}
}
static void hw_handle_buff_status(void)
{
uint32_t remaining_buffers = usb_hw->buf_status;
pico_trace("buf_status 0x%08x\n", remaining_buffers);
// Check EPX first
uint bit = 0b1;
if (remaining_buffers & bit)
{
remaining_buffers &= ~bit;
struct hw_endpoint *ep = &epx;
_handle_buff_status_bit(bit, ep);
}
// Check interrupt endpoints
for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++)
{
// EPX is bit 0
// IEP1 is bit 2
// IEP2 is bit 4
// IEP3 is bit 6
// etc
bit = 1 << (i*2);
if (remaining_buffers & bit)
{
remaining_buffers &= ~bit;
_handle_buff_status_bit(bit, &eps[i]);
}
}
if (remaining_buffers)
{
panic("Unhandled buffer %d\n", remaining_buffers);
}
}
static void hw_trans_complete(void)
{
struct hw_endpoint *ep = &epx;
assert(ep->active);
if (ep->sent_setup)
{
pico_trace("Sent setup packet\n");
hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
}
else
{
// Don't care. Will handle this in buff status
return;
}
}
static void hcd_rp2040_irq(void)
{
uint32_t status = usb_hw->ints;
uint32_t handled = 0;
if (status & USB_INTS_HOST_CONN_DIS_BITS)
{
handled |= USB_INTS_HOST_CONN_DIS_BITS;
if (dev_speed())
{
hcd_event_device_attach(ROOT_PORT, true);
}
else
{
hcd_event_device_remove(ROOT_PORT, true);
}
// Clear speed change interrupt
usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
}
if (status & USB_INTS_TRANS_COMPLETE_BITS)
{
handled |= USB_INTS_TRANS_COMPLETE_BITS;
usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
hw_trans_complete();
}
if (status & USB_INTS_BUFF_STATUS_BITS)
{
handled |= USB_INTS_BUFF_STATUS_BITS;
hw_handle_buff_status();
}
if (status & USB_INTS_STALL_BITS)
{
// We have rx'd a stall from the device
pico_trace("Stall REC\n");
handled |= USB_INTS_STALL_BITS;
usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
hw_xfer_complete(&epx, XFER_RESULT_STALLED);
}
if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS)
{
handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
}
if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
{
usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
panic("Data Seq Error \n");
}
if (status ^ handled)
{
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
}
}
static struct hw_endpoint *_next_free_interrupt_ep(void)
{
struct hw_endpoint *ep = NULL;
for (uint i = 1; i < count_of(eps); i++)
{
ep = &eps[i];
if (!ep->configured)
{
// Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
ep->interrupt_num = i - 1;
return ep;
}
}
return ep;
}
static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
{
struct hw_endpoint *ep = NULL;
if (transfer_type == TUSB_XFER_INTERRUPT)
{
ep = _next_free_interrupt_ep();
pico_info("Allocate interrupt ep %d\n", ep->interrupt_num);
assert(ep);
ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
// 0x180 for epx
// 0x1c0 for intep0
// 0x200 for intep1
// etc
ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 1)];
}
else
{
ep = &epx;
ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
ep->endpoint_control = &usbh_dpram->epx_ctrl;
ep->hw_data_buf = &usbh_dpram->epx_data[0];
}
return ep;
}
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
{
// Already has data buffer, endpoint control, and buffer control allocated at this point
assert(ep->endpoint_control);
assert(ep->buffer_control);
assert(ep->hw_data_buf);
uint8_t num = tu_edpt_number(ep_addr);
bool in = ep_addr & TUSB_DIR_IN_MASK;
ep->ep_addr = ep_addr;
ep->dev_addr = dev_addr;
ep->in = in;
// For host, IN to host == RX, anything else rx == false
ep->rx = in == true;
ep->num = num;
// Response to a setup packet on EP0 starts with pid of 1
ep->next_pid = num == 0 ? 1u : 0u;
ep->wMaxPacketSize = wMaxPacketSize;
ep->transfer_type = transfer_type;
pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->transfer_type);
pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->hw_data_buf);
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
// Bits 0-5 should be 0
assert(!(dpram_offset & 0b111111));
// Fill in endpoint control register with buffer offset
uint32_t ep_reg = EP_CTRL_ENABLE_BITS
| EP_CTRL_INTERRUPT_PER_BUFFER
| (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
| dpram_offset;
ep_reg |= bmInterval ? (bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB : 0;
*ep->endpoint_control = ep_reg;
pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
ep->configured = true;
if (bmInterval)
{
// This is an interrupt endpoint
// so need to set up interrupt endpoint address control register with:
// device address
// endpoint number / direction
// preamble
uint32_t reg = dev_addr | (ep->num << USB_ADDR_ENDP1_ENDPOINT_LSB);
// Assert the interrupt endpoint is IN_TO_HOST
assert(ep->in);
if (need_pre(dev_addr))
{
reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
}
usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
// Finally, enable interrupt that endpoint
usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
// If it's an interrupt endpoint we need to set up the buffer control
// register
}
}
static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc)
{
// Allocated differently based on if it's an interrupt endpoint or not
struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
_hw_endpoint_init(ep,
dev_addr,
ep_desc->bEndpointAddress,
ep_desc->wMaxPacketSize.size,
ep_desc->bmAttributes.xfer,
ep_desc->bInterval);
// Map this struct to ep@device address
set_dev_ep(dev_addr, ep_desc->bEndpointAddress, ep);
}
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
bool hcd_init(void)
{
pico_trace("hcd_init\n");
// Reset any previous state
rp2040_usb_init();
irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq);
// clear epx and interrupt eps
memset(&eps, 0, sizeof(eps));
// Enable in host mode with SOF / Keep alive on
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
usb_hw->sie_ctrl = sie_ctrl_base;
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS |
USB_INTE_HOST_CONN_DIS_BITS |
USB_INTE_HOST_RESUME_BITS |
USB_INTE_STALL_BITS |
USB_INTE_TRANS_COMPLETE_BITS |
USB_INTE_ERROR_RX_TIMEOUT_BITS |
USB_INTE_ERROR_DATA_SEQ_BITS ;
return true;
}
void hcd_port_reset(uint8_t rhport)
{
pico_trace("hcd_port_reset\n");
assert(rhport == 0);
// TODO: Nothing to do here yet. Perhaps need to reset some state?
}
bool hcd_port_connect_status(uint8_t rhport)
{
pico_trace("hcd_port_connect_status\n");
assert(rhport == 0);
return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS;
}
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
pico_trace("hcd_port_speed_get\n");
assert(rhport == 0);
// TODO: Should enumval this register
switch (dev_speed())
{
case 1:
return TUSB_SPEED_LOW;
case 2:
return TUSB_SPEED_FULL;
default:
panic("Invalid speed\n");
}
}
// Close all opened endpoint belong to this device
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
{
pico_trace("hcd_device_close %d\n", dev_addr);
}
void hcd_int_enable(uint8_t rhport)
{
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, true);
}
void hcd_int_disable(uint8_t rhport)
{
// todo we should check this is disabling from the correct core; note currently this is never called
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, false);
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
pico_info("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
// Get appropriate ep. Either EPX or interrupt endpoint
struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
if (ep_addr != ep->ep_addr)
{
// Direction has flipped so re init it but with same properties
_hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
}
// True indicates this is the start of the transfer
_hw_endpoint_xfer(ep, buffer, buflen, true);
// If a normal transfer (non-interrupt) then initiate using
// sie ctrl registers. Otherwise interrupt ep registers should
// already be configured
if (ep == &epx) {
// That has set up buffer control, endpoint control etc
// for host we have to initiate the transfer
usb_hw->dev_addr_ctrl = dev_addr | ep->num << USB_ADDR_ENDP_ENDPOINT_LSB;
uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base;
flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS;
// Set pre if we are a low speed device on full speed hub
flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
usb_hw->sie_ctrl = flags;
}
return true;
}
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
pico_info("hcd_setup_send dev_addr %d\n", dev_addr);
// Copy data into setup packet buffer
memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8);
// Configure EP0 struct with setup info for the trans complete
struct hw_endpoint *ep = _hw_endpoint_allocate(0);
// EP0 out
_hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
assert(ep->configured);
assert(ep->num == 0 && !ep->in);
ep->total_len = 8;
ep->transfer_size = 8;
ep->active = true;
ep->sent_setup = true;
// Set device address
usb_hw->dev_addr_ctrl = dev_addr;
// Set pre if we are a low speed device on full speed hub
uint32_t flags = sie_ctrl_base | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS;
flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
usb_hw->sie_ctrl = flags;
return true;
}
uint32_t hcd_uframe_number(uint8_t rhport)
{
// Microframe number is (125us) but we are max full speed so return miliseconds * 8
return usb_hw->sof_rd * 8;
}
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
hw_endpoint_init(dev_addr, ep_desc);
return true;
}
bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
{
// EPX is shared, so multiple device addresses and endpoint addresses share that
// so if any transfer is active on epx, we are busy. Interrupt endpoints have their own
// EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint
// on that device
pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr);
struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
assert(ep);
bool busy = ep->active;
pico_trace("busy == %d\n", busy);
return busy;
}
bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
{
panic("hcd_pipe_stalled");
}
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
panic("hcd_clear_stall");
return true;
}
bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
{
pico_trace("hcd_pipe_xfer dev_addr %d, ep_addr 0x%x, total_bytes %d, int_on_complete %d\n",
dev_addr, ep_addr, total_bytes, int_on_complete);
// Same logic as hcd_edpt_xfer as far as I am concerned
hcd_edpt_xfer(0, dev_addr, ep_addr, buffer, total_bytes);
return true;
}
#endif

View File

@ -0,0 +1,279 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 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.
*
* This file is part of the TinyUSB stack.
*/
#include <stdlib.h>
#include "rp2040_usb.h"
#include "hardware/clocks.h"
#include "tusb_option.h"
// Direction strings for debug
const char *ep_dir_string[] = {
"out",
"in",
};
static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) {
// todo add critsec as necessary to prevent issues between worker and IRQ...
// note that this is perhaps as simple as disabling IRQs because it would make
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
}
static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep)
{
ep->last_buf = ep->len + ep->transfer_size == ep->total_len;
}
void rp2040_usb_init(void)
{
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
// Clear any previous state just in case
memset(usb_hw, 0, sizeof(*usb_hw));
memset(usb_dpram, 0, sizeof(*usb_dpram));
// Mux to phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
}
void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
{
ep->stalled = false;
ep->active = false;
ep->sent_setup = false;
ep->total_len = 0;
ep->len = 0;
ep->transfer_size = 0;
ep->user_buf = 0;
}
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
uint32_t value = 0;
if (and_mask) {
value = *ep->buffer_control & and_mask;
}
if (or_mask) {
value |= or_mask;
if (or_mask & USB_BUF_CTRL_AVAIL) {
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
panic("ep %d %s was already available", ep->num, ep_dir_string[ep->in]);
}
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
// Don't need delay in host mode as host is in charge
#ifndef RP2040_USB_HOST_MODE
__asm volatile (
"b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1:\n"
: : : "memory");
#endif
}
}
*ep->buffer_control = value;
}
void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
{
// Prepare buffer control register value
uint32_t val = ep->transfer_size | USB_BUF_CTRL_AVAIL;
if (!ep->rx)
{
// Copy data from user buffer to hw buffer
memcpy(ep->hw_data_buf, &ep->user_buf[ep->len], ep->transfer_size);
// Mark as full
val |= USB_BUF_CTRL_FULL;
}
// PID
val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
ep->next_pid ^= 1u;
// Is this the last buffer? Only really matters for host mode. Will trigger
// the trans complete irq but also stop it polling. We only really care about
// trans complete for setup packets being sent
if (ep->last_buf)
{
pico_trace("Last buf (%d bytes left)\n", ep->transfer_size);
val |= USB_BUF_CTRL_LAST;
}
// Finally, write to buffer_control which will trigger the transfer
// the next time the controller polls this dpram address
_hw_endpoint_buffer_control_set_value32(ep, val);
pico_trace("buffer control (0x%p) <- 0x%x\n", ep->buffer_control, val);
}
void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
{
_hw_endpoint_lock_update(ep, 1);
pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, ep->num, ep_dir_string[ep->in]);
if (ep->active)
{
// TODO: Is this acceptable for interrupt packets?
pico_warn("WARN: starting new transfer on already active ep %d %s\n", ep->num, ep_dir_string[ep->in]);
hw_endpoint_reset_transfer(ep);
}
// Fill in info now that we're kicking off the hw
ep->total_len = total_len;
ep->len = 0;
// FIXME: What if low speed
ep->transfer_size = total_len > 64 ? 64 : total_len;
ep->active = true;
ep->user_buf = buffer;
// Recalculate if this is the last buffer
_hw_endpoint_update_last_buf(ep);
ep->buf_sel = 0;
_hw_endpoint_start_next_buffer(ep);
_hw_endpoint_lock_update(ep, -1);
}
void _hw_endpoint_xfer_sync(struct hw_endpoint *ep)
{
// Update hw endpoint struct with info from hardware
// after a buff status interrupt
// Get the buffer state and amount of bytes we have
// transferred
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
uint transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
#ifdef RP2040_USB_HOST_MODE
// tag::host_buf_sel_fix[]
if (ep->buf_sel == 1)
{
// Host can erroneously write status to top half of buf_ctrl register
buf_ctrl = buf_ctrl >> 16;
}
// Flip buf sel for host
ep->buf_sel ^= 1u;
// end::host_buf_sel_fix[]
#endif
// We are continuing a transfer here. If we are TX, we have successfullly
// sent some data can increase the length we have sent
if (!ep->rx)
{
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
pico_trace("tx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
ep->len += transferred_bytes;
}
else
{
// If we are OUT we have recieved some data, so can increase the length
// we have recieved AFTER we have copied it to the user buffer at the appropriate
// offset
pico_trace("rx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
assert(buf_ctrl & USB_BUF_CTRL_FULL);
memcpy(&ep->user_buf[ep->len], ep->hw_data_buf, transferred_bytes);
ep->len += transferred_bytes;
}
// Sometimes the host will send less data than we expect...
// If this is a short out transfer update the total length of the transfer
// to be the current length
if ((ep->rx) && (transferred_bytes < ep->transfer_size))
{
pico_trace("Short rx transfer\n");
// Reduce total length as this is last packet
ep->total_len = ep->len;
}
}
// Returns true if transfer is complete
bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
{
_hw_endpoint_lock_update(ep, 1);
// Part way through a transfer
if (!ep->active)
{
panic("Can't continue xfer on inactive ep %d %s", ep->num, ep_dir_string);
}
// Update EP struct from hardware state
_hw_endpoint_xfer_sync(ep);
// Now we have synced our state with the hardware. Is there more data to transfer?
uint remaining_bytes = ep->total_len - ep->len;
ep->transfer_size = remaining_bytes > 64 ? 64 : remaining_bytes;
_hw_endpoint_update_last_buf(ep);
// Can happen because of programmer error so check for it
if (ep->len > ep->total_len)
{
panic("Transferred more data than expected");
}
// If we are done then notify tinyusb
if (ep->len == ep->total_len)
{
pico_trace("Completed transfer of %d bytes on ep %d %s\n",
ep->len, ep->num, ep_dir_string[ep->in]);
// Notify caller we are done so it can notify the tinyusb
// stack
_hw_endpoint_lock_update(ep, -1);
return true;
}
else
{
_hw_endpoint_start_next_buffer(ep);
}
_hw_endpoint_lock_update(ep, -1);
// More work to do
return false;
}
void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start)
{
// Trace
pico_trace("hw_endpoint_xfer ep %d %s", ep->num, ep_dir_string[ep->in]);
pico_trace(" total_len %d, start=%d\n", total_len, start);
assert(ep->configured);
if (start)
{
_hw_endpoint_xfer_start(ep, buffer, total_len);
}
else
{
_hw_endpoint_xfer_continue(ep);
}
}

View File

@ -0,0 +1,124 @@
#ifndef RP2040_COMMON_H_
#define RP2040_COMMON_H_
#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
#error TinyUSB device and host mode not supported at the same time
#endif
#include "common/tusb_common.h"
#include "pico.h"
#include "hardware/structs/usb.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
#endif
// For memset
#include <string.h>
#if false && !defined(NDEBUG)
#define pico_trace(format,args...) printf(format, ## args)
#else
#define pico_trace(format,...) ((void)0)
#endif
#if false && !defined(NDEBUG)
#define pico_info(format,args...) printf(format, ## args)
#else
#define pico_info(format,...) ((void)0)
#endif
#if false && !defined(NDEBUG)
#define pico_warn(format,args...) printf(format, ## args)
#else
#define pico_warn(format,...) ((void)0)
#endif
// Hardware information per endpoint
struct hw_endpoint
{
// Is this a valid struct
bool configured;
// EP direction
bool in;
// EP num (not including direction)
uint8_t num;
// Transfer direction (i.e. IN is rx for host but tx for device)
// allows us to common up transfer functions
bool rx;
uint8_t ep_addr;
uint8_t next_pid;
// Endpoint control register
io_rw_32 *endpoint_control;
// Buffer control register
io_rw_32 *buffer_control;
// Buffer pointer in usb dpram
uint8_t *hw_data_buf;
// Have we been stalled
bool stalled;
// Current transfer information
bool active;
uint total_len;
uint len;
// Amount of data with the hardware
uint transfer_size;
// Only needed for host mode
bool last_buf;
// HOST BUG. Host will incorrect write status to top half of buffer
// control register when doing transfers > 1 packet
uint8_t buf_sel;
// User buffer in main memory
uint8_t *user_buf;
// Data needed from EP descriptor
uint wMaxPacketSize;
// Interrupt, bulk, etc
uint8_t transfer_type;
// Only needed for host
uint8_t dev_addr;
bool sent_setup;
// If interrupt endpoint
uint8_t interrupt_num;
};
void rp2040_usb_init(void);
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start);
void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep);
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) {
return *ep->buffer_control;
}
static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) {
return _hw_endpoint_buffer_control_update32(ep, 0, value);
}
static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) {
return _hw_endpoint_buffer_control_update32(ep, ~value, value);
}
static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) {
return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
}
static inline uintptr_t hw_data_offset(uint8_t *buf)
{
// Remove usb base from buffer pointer
return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
}
extern const char *ep_dir_string[];
#endif

View File

@ -547,7 +547,7 @@ void dcd_int_handler(uint8_t rhport) {
// USBRST is start of reset.
clear_istr_bits(USB_ISTR_RESET);
dcd_handle_bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
}

View File

@ -45,13 +45,13 @@
#define STM32L4_SYNOPSYS
#endif
#if TUSB_OPT_DEVICE_ENABLED && \
#if TUSB_OPT_DEVICE_ENABLED && \
( (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
(CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
)
// EP_MAX : Max number of bi-directional endpoints including EP0
@ -115,6 +115,7 @@
#define EP_FIFO_SIZE EP_FIFO_SIZE_HS
#define RHPORT_REGS_BASE USB_OTG_HS_PERIPH_BASE
#define RHPORT_IRQn OTG_HS_IRQn
#endif
#define GLOBAL_BASE(_port) ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE)
@ -139,25 +140,40 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
// EP size and transfer type report
typedef struct TU_ATTR_PACKED {
// The following format may look complicated but it is the most elegant way of addressing the required fields: EP number, EP direction, and EP transfer type.
// The codes assigned to those fields, according to the USB specification, can be neatly used as indices.
uint16_t ep_size[EP_MAX][2]; ///< dim 1: EP number, dim 2: EP direction denoted by TUSB_DIR_OUT (= 0) and TUSB_DIR_IN (= 1)
bool ep_transfer_type[EP_MAX][2][4]; ///< dim 1: EP number, dim 2: EP direction, dim 3: transfer type, where 0 = Control, 1 = Isochronous, 2 = Bulk, and 3 = Interrupt
///< I know very well that EP0 can only be used as control EP and we waste space here but for the sake of simplicity we accept that. It is used in a non-persistent way anyway!
} ep_sz_tt_report_t;
typedef volatile uint32_t * usb_fifo_t;
xfer_ctl_t xfer_status[EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
// EP0 transfers are limited to 1 packet - larger sizes has to be split
static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
// FIFO RAM allocation so far in words
static uint16_t _allocated_fifo_words;
// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from usb_otg->GRXFSIZ
static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
// Calculate the RX FIFO size according to recommendations from reference manual
static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
{
return 15 + 2*(ep_size/4) + 2*EP_MAX;
}
static void update_grxfsiz(uint8_t rhport)
{
(void) rhport;
USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
// Determine largest EP size for RX FIFO
uint16_t max_epsize = 0;
for (uint8_t epnum = 0; epnum < EP_MAX; epnum++)
{
max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
}
// Update size of RX FIFO
usb_otg->GRXFSIZ = calc_rx_ff_size(max_epsize);
}
// Setup the control endpoint 0.
static void bus_reset(uint8_t rhport)
@ -170,6 +186,7 @@ static void bus_reset(uint8_t rhport)
USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
tu_memclr(xfer_status, sizeof(xfer_status));
_out_ep_closed = false;
for(uint8_t n = 0; n < EP_MAX; n++) {
out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
@ -182,16 +199,28 @@ static void bus_reset(uint8_t rhport)
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
//
// The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start.
// We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located
// between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard
// configuration done below.
//
// Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed.
// All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a
// configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually
// opened when the host sends an additional command: setInterface. At this point in time
// the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size
// an additional memory
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO 0 |
// --------------- (320 or 1024) - 16
// | IN FIFO 1 |
// --------------- (320 or 1024) - 16 - x
// | . . . . |
// --------------- (320 or 1024) - 16 - x - y - ... - z
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// | FREE |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
@ -213,24 +242,16 @@ static void bus_reset(uint8_t rhport)
// NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
// of the overall picture yet. We will use the worst scenario: largest possible + EP_MAX
//
// FIXME: for Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
// For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
// are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to
// overwrite this.
#if TUD_OPT_HIGH_SPEED
_allocated_fifo_words = 271 + 2*EP_MAX;
#else
_allocated_fifo_words = 47 + 2*EP_MAX;
#endif
usb_otg->GRXFSIZ = calc_rx_ff_size(TUD_OPT_HIGH_SPEED ? 512 : 64);
usb_otg->GRXFSIZ = _allocated_fifo_words;
_allocated_fifo_words_tx = 16;
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | _allocated_fifo_words;
_allocated_fifo_words += 16;
// TU_LOG2_INT(_allocated_fifo_words);
usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
// Fixed control EP0 size to 64 bytes
in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos);
@ -534,6 +555,8 @@ void dcd_disconnect(uint8_t rhport)
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
@ -544,21 +567,26 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
TU_ASSERT(epnum < EP_MAX);
if (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
{
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= (get_speed(rhport) == TUSB_SPEED_HIGH ? 1024 : 1023));
}
else
{
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= (get_speed(rhport) == TUSB_SPEED_HIGH ? 512 : 64));
}
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = desc_edpt->wMaxPacketSize.size;
xfer->interval = desc_edpt->bInterval;
uint16_t const fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4; // Round up to next full word
if(dir == TUSB_DIR_OUT)
{
// Calculate required size of RX FIFO
uint16_t const sz = calc_rx_ff_size(4*fifo_size);
// If size_rx needs to be extended check if possible and if so enlarge it
if (usb_otg->GRXFSIZ < sz)
{
TU_ASSERT(sz + _allocated_fifo_words_tx <= EP_FIFO_SIZE/4);
// Enlarge RX FIFO
usb_otg->GRXFSIZ = sz;
}
out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
(desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
(desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
@ -571,15 +599,15 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// Peripheral FIFO architecture
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO 0 |
// --------------- (320 or 1024) - 16
// | IN FIFO 1 |
// --------------- (320 or 1024) - 16 - x
// | . . . . |
// --------------- (320 or 1024) - 16 - x - y - ... - z
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// | FREE |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
@ -587,34 +615,15 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
//
// In FIFO is allocated by following rules:
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
// - Offset: allocated so far
// - Size
// - Interrupt is EPSize
// - Bulk/ISO is max(EPSize, remaining-fifo / non-opened-EPIN)
uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words;
uint16_t fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4; // +3 for rounding up to next full word
// Check if free space is available
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + usb_otg->GRXFSIZ <= EP_FIFO_SIZE/4);
if ( desc_edpt->bmAttributes.xfer != TUSB_XFER_INTERRUPT )
{
uint8_t opened = 0;
for(uint8_t i = 0; i < EP_MAX; i++)
{
if ( (i != epnum) && (xfer_status[i][TUSB_DIR_IN].max_size > 0) ) opened++;
}
// EP Size or equally divided of remaining whichever is larger
fifo_size = tu_max16(fifo_size, fifo_remaining / (EP_MAX - opened));
}
// FIFO overflows, we probably need a better allocating scheme
TU_ASSERT(fifo_size <= fifo_remaining);
_allocated_fifo_words_tx += fifo_size;
// DIEPTXF starts at FIFO #1.
// Both TXFD and TXSA are in unit of 32-bit words.
usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | _allocated_fifo_words;
_allocated_fifo_words += fifo_size;
usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
(epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
@ -724,13 +733,21 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_edpt_disable(rhport, ep_addr, false);
// Update max_size
xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
if (dir == TUSB_DIR_IN)
{
uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos;
uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos;
// For now only endpoint that has FIFO at the end of FIFO memory can be closed without fuss.
TU_ASSERT(fifo_start + fifo_size == _allocated_fifo_words,);
_allocated_fifo_words -= fifo_size;
// For now only the last opened endpoint can be closed without fuss.
TU_ASSERT(fifo_start == EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,);
_allocated_fifo_words_tx -= fifo_size;
}
else
{
_out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
}
}
@ -986,13 +1003,15 @@ void dcd_int_handler(uint8_t rhport)
uint32_t int_status = usb_otg->GINTSTS;
if(int_status & USB_OTG_GINTSTS_USBRST) {
if(int_status & USB_OTG_GINTSTS_USBRST)
{
// USBRST is start of reset.
usb_otg->GINTSTS = USB_OTG_GINTSTS_USBRST;
bus_reset(rhport);
}
if(int_status & USB_OTG_GINTSTS_ENUMDNE) {
if(int_status & USB_OTG_GINTSTS_ENUMDNE)
{
// ENUMDNE is the end of reset where speed of the link is detected
usb_otg->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
@ -1029,45 +1048,59 @@ void dcd_int_handler(uint8_t rhport)
}
#if USE_SOF
if(int_status & USB_OTG_GINTSTS_SOF) {
if(int_status & USB_OTG_GINTSTS_SOF)
{
usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
#endif
// RxFIFO non-empty interrupt handling.
if(int_status & USB_OTG_GINTSTS_RXFLVL) {
if(int_status & USB_OTG_GINTSTS_RXFLVL)
{
// RXFLVL bit is read-only
// Mask out RXFLVL while reading data from FIFO
usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
// Loop until all available packets were handled
do {
do
{
handle_rxflvl_ints(rhport, out_ep);
int_status = usb_otg->GINTSTS;
} while(int_status & USB_OTG_GINTSTS_RXFLVL);
// Manage RX FIFO size
if (_out_ep_closed)
{
update_grxfsiz(rhport);
// Disable flag
_out_ep_closed = false;
}
usb_otg->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
}
// OUT endpoint interrupt handling.
if(int_status & USB_OTG_GINTSTS_OEPINT) {
if(int_status & USB_OTG_GINTSTS_OEPINT)
{
// OEPINT is read-only
handle_epout_ints(rhport, dev, out_ep);
}
// IN endpoint interrupt handling.
if(int_status & USB_OTG_GINTSTS_IEPINT) {
if(int_status & USB_OTG_GINTSTS_IEPINT)
{
// IEPINT bit read-only
handle_epin_ints(rhport, dev, in_ep);
}
// // Check for Incomplete isochronous IN transfer
// if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
// printf(" IISOIXFR!\r\n");
//// TU_LOG2(" IISOIXFR!\r\n");
// }
// // Check for Incomplete isochronous IN transfer
// if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
// printf(" IISOIXFR!\r\n");
//// TU_LOG2(" IISOIXFR!\r\n");
// }
}
#endif

View File

@ -232,9 +232,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
// Unsupported endpoint numbers/size or type (Iso not supported. Control
// Unsupported endpoint numbers or type (Iso not supported. Control
// not supported on nonzero endpoints).
if((desc_edpt->wMaxPacketSize.size > 64) || (epnum > 7) || \
if( (epnum > 7) || \
(desc_edpt->bmAttributes.xfer == 0) || \
(desc_edpt->bmAttributes.xfer == 1)) {
return false;
@ -572,7 +572,7 @@ void dcd_int_handler(uint8_t rhport)
{
case USBVECINT_RSTR:
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
break;
// Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet

View File

@ -332,7 +332,7 @@ static void dcd_reset(void)
usb_out_ev_enable_write(1);
usb_setup_ev_enable_write(3);
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
// Initializes the USB peripheral for device mode and enables it.

View File

@ -97,6 +97,12 @@
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
// Raspberry Pi
#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
// NXP Kinetis
#define OPT_MCU_MKL25ZXX 1200 ///< NXP MKL25Zxx
/** @} */
/** \defgroup group_supported_os Supported RTOS
@ -106,6 +112,7 @@
#define OPT_OS_FREERTOS 2 ///< FreeRTOS
#define OPT_OS_MYNEWT 3 ///< Mynewt OS
#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application
#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
/** @} */
@ -147,22 +154,22 @@
#define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE
#endif
#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST ) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST )) || \
((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE))
#if (((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST ) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST )) || \
(((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE))
#error "TinyUSB currently does not support same modes on more than 1 roothub port"
#endif
// Which roothub port is configured as host
#define TUH_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 1 : -1) )
#define TUH_OPT_RHPORT ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST) ? 1 : -1) )
#define TUSB_OPT_HOST_ENABLED ( TUH_OPT_RHPORT >= 0 )
// Which roothub port is configured as device
#define TUD_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) ? 1 : -1) )
#define TUD_OPT_RHPORT ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE) ? 1 : -1) )
#if TUD_OPT_RHPORT == 0
#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED )
#define TUD_OPT_HIGH_SPEED ( (CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HIGH_SPEED )
#else
#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED )
#define TUD_OPT_HIGH_SPEED ( (CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HIGH_SPEED )
#endif
#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 )

View File

@ -202,7 +202,7 @@ void setUp(void)
tusb_init();
}
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, false);
dcd_event_bus_reset(rhport, TUSB_SPEED_HIGH, false);
tud_task();
}

View File

@ -86,8 +86,8 @@
//------------- CDC -------------//
// FIFO size of CDC TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 64
#define CFG_TUD_CDC_TX_BUFSIZE 64
#define CFG_TUD_CDC_RX_BUFSIZE 512
#define CFG_TUD_CDC_TX_BUFSIZE 512
//------------- MSC -------------//
@ -97,7 +97,7 @@
//------------- HID -------------//
// Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 16
#define CFG_TUD_HID_EP_BUFSIZE 64
#ifdef __cplusplus
}

View File

@ -2,6 +2,12 @@ ifneq ($(lastword a b),b)
$(error This Makefile require make 3.81 or newer)
endif
# Detect whether shell style is windows or not
# https://stackoverflow.com/questions/714100/os-detecting-makefile/52062069#52062069
ifeq '$(findstring ;,$(PATH))' ';'
CMDEXE := 1
endif
# Set TOP to be the path to get from the current directory (where make was
# invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns
# the name of this makefile relative to where make was invoked.
@ -9,9 +15,16 @@ endif
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
TOP := $(patsubst %/tools/top.mk,%,$(THIS_MAKEFILE))
ifeq ($(CMDEXE),1)
TOP := $(subst \,/,$(shell for %%i in ( $(TOP) ) do echo %%~fi))
else
TOP := $(shell realpath $(TOP))
endif
#$(info Top directory is $(TOP))
ifeq ($(CMDEXE),1)
CURRENT_PATH := $(subst $(TOP)/,,$(subst \,/,$(shell echo %CD%)))
else
CURRENT_PATH := $(shell realpath --relative-to=$(TOP) `pwd`)
endif
#$(info Path from top is $(CURRENT_PATH))