mirror of
https://github.com/hathach/tinyusb.git
synced 2025-04-16 05:42:56 +00:00
Merge branch 'master' into group-boards-into-family
This commit is contained in:
commit
0cf2b02791
10
.github/ISSUE_TEMPLATE/question.md
vendored
10
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Question for this project
|
||||
title: ''
|
||||
labels: Q&A
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe what the question is**
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
@ -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)**
|
||||
|
@ -36,8 +36,9 @@ The stack supports the following MCUs:
|
||||
- **NordicSemi:** nRF52833, nRF52840
|
||||
- **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505
|
||||
- **NXP:**
|
||||
- LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
|
||||
- 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
|
||||
|
@ -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
|
||||
|
||||
|
0
examples/device/cdc_msc_freertos/.skip.MCU_MKL25ZXX
Normal file
0
examples/device/cdc_msc_freertos/.skip.MCU_MKL25ZXX
Normal file
0
examples/device/msc_dual_lun/.skip.MCU_MKL25ZXX
Normal file
0
examples/device/msc_dual_lun/.skip.MCU_MKL25ZXX
Normal file
59
examples/host/cdc_msc_hid/src/keyboard_helper.h
Normal file
59
examples/host/cdc_msc_hid/src/keyboard_helper.h
Normal 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
|
@ -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 --------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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) \
|
||||
|
234
hw/bsp/double_m33_express/LPC55S69_cm33_core0_uf2.ld
Normal file
234
hw/bsp/double_m33_express/LPC55S69_cm33_core0_uf2.ld
Normal 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")
|
||||
}
|
||||
|
55
hw/bsp/double_m33_express/board.mk
Normal file
55
hw/bsp/double_m33_express/board.mk
Normal 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 $<
|
275
hw/bsp/double_m33_express/double_m33_express.c
Normal file
275
hw/bsp/double_m33_express/double_m33_express.c
Normal 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
|
46
hw/bsp/frdm_kl25z/board.mk
Normal file
46
hw/bsp/frdm_kl25z/board.mk
Normal 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
|
148
hw/bsp/frdm_kl25z/frdm_kl25z.c
Normal file
148
hw/bsp/frdm_kl25z/frdm_kl25z.c
Normal 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
|
@ -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.
|
||||
|
99
hw/bsp/raspberry_pi_pico/board_raspberry_pi_pico.c
Normal file
99
hw/bsp/raspberry_pi_pico/board_raspberry_pi_pico.c
Normal 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
1
lib/sct_neopixel
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e73e04ca63495672d955f9268e003cffe168fcd8
|
@ -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
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
192
src/osal/osal_pico.h
Normal 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_ */
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
477
src/portable/nxp/khci/dcd_khci.c
Normal file
477
src/portable/nxp/khci/dcd_khci.c
Normal 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
|
@ -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)
|
||||
|
@ -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)
|
||||
|
482
src/portable/raspberrypi/rp2040/dcd_rp2040.c
Normal file
482
src/portable/raspberrypi/rp2040/dcd_rp2040.c
Normal 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
|
549
src/portable/raspberrypi/rp2040/hcd_rp2040.c
Normal file
549
src/portable/raspberrypi/rp2040/hcd_rp2040.c
Normal 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
|
279
src/portable/raspberrypi/rp2040/rp2040_usb.c
Normal file
279
src/portable/raspberrypi/rp2040/rp2040_usb.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
124
src/portable/raspberrypi/rp2040/rp2040_usb.h
Normal file
124
src/portable/raspberrypi/rp2040/rp2040_usb.h
Normal 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
|
@ -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?
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 )
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
15
tools/top.mk
15
tools/top.mk
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user