summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2026-07-18 23:36:49 +0700
committerGitHub <[email protected]>2026-07-18 23:36:49 +0700
commit3b4544b974054ae95394401adc663a6bd17c7d33 (patch)
treeb7c3a206f2c92318b69e442e4e45b330531cb518
parentaa410008e8e74b0727f8c30a1ec109ff2c37efc6 (diff)
parent4782770e7f5f4a726f9739d940c110196807f15c (diff)
Merge pull request #3763 from hathach/claude/migrate-khci-to-ci_fs
Migrate NXP Kinetis khci to chipidea ci_fs (device + host) + fixes
-rw-r--r--README.rst4
-rw-r--r--examples/host/cdc_msc_hid/only.txt1
-rw-r--r--examples/host/device_info/only.txt1
-rw-r--r--hw/bsp/kinetis_k/family.c40
-rw-r--r--hw/bsp/kinetis_k/family.cmake2
-rw-r--r--hw/bsp/kinetis_k/family.mk4
-rw-r--r--hw/bsp/kinetis_k32l/family.cmake4
-rw-r--r--hw/bsp/kinetis_k32l/family.mk4
-rw-r--r--hw/bsp/kinetis_kl/family.cmake2
-rw-r--r--hw/bsp/kinetis_kl/family.mk4
-rw-r--r--src/portable/chipidea/ci_fs/ci_fs_type.h54
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c49
-rw-r--r--src/portable/chipidea/ci_fs/hcd_ci_fs.c (renamed from src/portable/nxp/khci/hcd_khci.c)265
-rw-r--r--src/portable/nxp/khci/dcd_khci.c560
-rw-r--r--test/hil/tinyusb.json30
15 files changed, 279 insertions, 745 deletions
diff --git a/README.rst b/README.rst
index e5806bafc..205f3f544 100644
--- a/README.rst
+++ b/README.rst
@@ -239,9 +239,7 @@ Supported CPUs
+--------------+---------+-------------------+--------+------+-----------+------------------------+--------------------+
| NXP | iMXRT | RT 10xx, 11xx | ✅ | ✅ | ✅ | ci_hs, ehci | |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| | Kinetis | KL | ✅ | 🟡 | ❌ | ci_fs, khci | |
-| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | K32L2 | ✅ | | ❌ | khci | ci_fs variant |
+| | Kinetis | KL, K32L | ✅ | 🟡 | ❌ | ci_fs | |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
| | LPC | 11u, 13, 15 | ✅ | ❌ | ❌ | lpc_ip3511 | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
diff --git a/examples/host/cdc_msc_hid/only.txt b/examples/host/cdc_msc_hid/only.txt
index a2ff93be5..a2f4f273a 100644
--- a/examples/host/cdc_msc_hid/only.txt
+++ b/examples/host/cdc_msc_hid/only.txt
@@ -3,6 +3,7 @@ family:samd21
family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
+mcu:KINETIS_K
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt
index 4c2cb0f35..7f30218df 100644
--- a/examples/host/device_info/only.txt
+++ b/examples/host/device_info/only.txt
@@ -4,6 +4,7 @@ family:samd21
family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
+mcu:KINETIS_K
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
diff --git a/hw/bsp/kinetis_k/family.c b/hw/bsp/kinetis_k/family.c
index a5af83931..2df3313b8 100644
--- a/hw/bsp/kinetis_k/family.c
+++ b/hw/bsp/kinetis_k/family.c
@@ -35,10 +35,25 @@
#include "fsl_clock.h"
#include "fsl_uart.h"
#include "fsl_sysmpu.h"
+#include "common/tusb_fifo.h"
#include "board/clock_config.h"
#include "board/pin_mux.h"
+#ifdef UART_DEV
+// RX ring buffer filled by the RDRF interrupt so board_uart_read() is non-blocking
+// and does not drop bytes to UART overrun (see stm32 family for reference).
+static uint8_t uart_rx_ff_buf[32];
+static tu_fifo_t uart_rx_ff;
+
+void UART0_RX_TX_IRQHandler(void) {
+ if (UART_DEV->S1 & UART_S1_RDRF_MASK) {
+ uint8_t byte = UART_DEV->D; // reading S1 then D clears RDRF (and any overrun)
+ tu_fifo_write(&uart_rx_ff, &byte);
+ }
+}
+#endif
+
//--------------------------------------------------------------------+
// Forward USB interrupt events to TinyUSB IRQ Handler
//--------------------------------------------------------------------+
@@ -89,6 +104,9 @@ void board_init(void) {
.enableRx = true
};
UART_Init(UART_DEV, &uart_config, UART_CLOCK);
+ tu_fifo_config(&uart_rx_ff, uart_rx_ff_buf, sizeof(uart_rx_ff_buf), false);
+ UART_DEV->C2 |= UART_C2_RIE_MASK; // enable RX data register full interrupt
+ NVIC_EnableIRQ(UART0_RX_TX_IRQn);
#endif
// USB
@@ -112,14 +130,11 @@ uint32_t board_button_read(void) {
}
int board_uart_read(uint8_t *buf, int len) {
- (void) buf;
- (void) len;
#ifdef UART_DEV
- // Read blocking will block until there is data
-// UART_ReadBlocking(UART_DEV, buf, len);
-// return len;
- return 0;
+ return (int) tu_fifo_read_n(&uart_rx_ff, buf, (uint16_t) len);
#else
+ (void) buf;
+ (void) len;
return 0;
#endif
}
@@ -144,6 +159,19 @@ int board_uart_write(void const *buf, int len) {
#endif
}
+size_t board_get_unique_id(uint8_t id[], size_t max_len) {
+ (void) max_len;
+ // Kinetis 128-bit Unique Identification Register (SIM->UIDH/UIDMH/UIDML/UIDL)
+ uint32_t* id32 = (uint32_t*) (uintptr_t) id;
+
+ id32[0] = SIM->UIDH;
+ id32[1] = SIM->UIDMH;
+ id32[2] = SIM->UIDML;
+ id32[3] = SIM->UIDL;
+
+ return 16;
+}
+
#if CFG_TUSB_OS == OPT_OS_NONE
volatile uint32_t system_ticks = 0;
diff --git a/hw/bsp/kinetis_k/family.cmake b/hw/bsp/kinetis_k/family.cmake
index e1b5c221e..e408c0f4a 100644
--- a/hw/bsp/kinetis_k/family.cmake
+++ b/hw/bsp/kinetis_k/family.cmake
@@ -63,7 +63,7 @@ function(family_configure_example TARGET RTOS)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
${TOP}/src/portable/chipidea/ci_fs/dcd_ci_fs.c
- ${TOP}/src/portable/nxp/khci/hcd_khci.c
+ ${TOP}/src/portable/chipidea/ci_fs/hcd_ci_fs.c
${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
)
target_include_directories(${TARGET} PUBLIC
diff --git a/hw/bsp/kinetis_k/family.mk b/hw/bsp/kinetis_k/family.mk
index b1e1fb3aa..5d0e4a702 100644
--- a/hw/bsp/kinetis_k/family.mk
+++ b/hw/bsp/kinetis_k/family.mk
@@ -17,8 +17,8 @@ LDFLAGS += \
--specs=nosys.specs --specs=nano.specs \
SRC_C += \
- src/portable/nxp/khci/dcd_khci.c \
- src/portable/nxp/khci/hcd_khci.c \
+ src/portable/chipidea/ci_fs/dcd_ci_fs.c \
+ src/portable/chipidea/ci_fs/hcd_ci_fs.c \
$(MCU_DIR)/system_${MCU_VARIANT}.c \
$(MCU_DIR)/drivers/fsl_clock.c \
$(SDK_DIR)/drivers/gpio/fsl_gpio.c \
diff --git a/hw/bsp/kinetis_k32l/family.cmake b/hw/bsp/kinetis_k32l/family.cmake
index 020695589..950682363 100644
--- a/hw/bsp/kinetis_k32l/family.cmake
+++ b/hw/bsp/kinetis_k32l/family.cmake
@@ -64,8 +64,8 @@ function(family_configure_example TARGET RTOS)
target_sources(${TARGET} PUBLIC
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
- ${TOP}/src/portable/nxp/khci/dcd_khci.c
- ${TOP}/src/portable/nxp/khci/hcd_khci.c
+ ${TOP}/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+ ${TOP}/src/portable/chipidea/ci_fs/hcd_ci_fs.c
${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
)
target_include_directories(${TARGET} PUBLIC
diff --git a/hw/bsp/kinetis_k32l/family.mk b/hw/bsp/kinetis_k32l/family.mk
index a99fb5dbe..357128aa5 100644
--- a/hw/bsp/kinetis_k32l/family.mk
+++ b/hw/bsp/kinetis_k32l/family.mk
@@ -13,8 +13,8 @@ LDFLAGS += \
-specs=nosys.specs -specs=nano.specs
SRC_C += \
- src/portable/nxp/khci/dcd_khci.c \
- src/portable/nxp/khci/hcd_khci.c \
+ src/portable/chipidea/ci_fs/dcd_ci_fs.c \
+ src/portable/chipidea/ci_fs/hcd_ci_fs.c \
$(MCUX_DEVICES)/K32L/$(MCU_VARIANT)/system_$(MCU_VARIANT).c \
$(MCUX_DEVICES)/K32L/$(MCU_VARIANT)/drivers/fsl_clock.c \
$(MCUX_CORE)/drivers/gpio/fsl_gpio.c \
diff --git a/hw/bsp/kinetis_kl/family.cmake b/hw/bsp/kinetis_kl/family.cmake
index 230a3057d..b74f4f8b9 100644
--- a/hw/bsp/kinetis_kl/family.cmake
+++ b/hw/bsp/kinetis_kl/family.cmake
@@ -62,7 +62,7 @@ function(family_configure_example TARGET RTOS)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
${TOP}/src/portable/chipidea/ci_fs/dcd_ci_fs.c
- ${TOP}/src/portable/nxp/khci/hcd_khci.c
+ ${TOP}/src/portable/chipidea/ci_fs/hcd_ci_fs.c
${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
)
target_include_directories(${TARGET} PUBLIC
diff --git a/hw/bsp/kinetis_kl/family.mk b/hw/bsp/kinetis_kl/family.mk
index 201ab99dc..9c780a868 100644
--- a/hw/bsp/kinetis_kl/family.mk
+++ b/hw/bsp/kinetis_kl/family.mk
@@ -17,8 +17,8 @@ LDFLAGS += \
-specs=nosys.specs -specs=nano.specs \
SRC_C += \
- src/portable/nxp/khci/dcd_khci.c \
- src/portable/nxp/khci/hcd_khci.c \
+ src/portable/chipidea/ci_fs/dcd_ci_fs.c \
+ src/portable/chipidea/ci_fs/hcd_ci_fs.c \
$(MCU_DIR)/system_$(MCU).c \
$(MCU_DIR)/drivers/fsl_clock.c \
$(SDK_DIR)/drivers/gpio/fsl_gpio.c \
diff --git a/src/portable/chipidea/ci_fs/ci_fs_type.h b/src/portable/chipidea/ci_fs/ci_fs_type.h
index a525c96ca..857a75253 100644
--- a/src/portable/chipidea/ci_fs/ci_fs_type.h
+++ b/src/portable/chipidea/ci_fs/ci_fs_type.h
@@ -27,6 +27,60 @@ extern "C" {
// align 4 is used to get rid of reserved fields
#define _va32 volatile TU_ATTR_ALIGNED(4)
+//--------------------------------------------------------------------+
+// Buffer Descriptor Table (BDT) - shared by the device (dcd) and host (hcd) drivers
+// since both target the same ChipIdea-FS silicon. Keep the layout in one place so a
+// fix cannot silently drift between the two drivers.
+//--------------------------------------------------------------------+
+
+// Token PID values reported in the BDT tok_pid field / written to the TOKEN register.
+// The device driver only uses OUT/IN/SETUP; the rest are host-only.
+enum {
+ TOK_PID_OUT = 0x1u,
+ TOK_PID_IN = 0x9u,
+ TOK_PID_SETUP = 0xDu,
+ TOK_PID_DATA0 = 0x3u,
+ TOK_PID_DATA1 = 0xbu,
+ TOK_PID_ACK = 0x2u,
+ TOK_PID_STALL = 0xeu,
+ TOK_PID_NAK = 0xau,
+ TOK_PID_BUSTO = 0x0u,
+ TOK_PID_ERR = 0xfu,
+};
+
+// Note: this header is included before the CMSIS device header, so use plain `volatile`
+// rather than CMSIS `__IO`.
+typedef struct TU_ATTR_PACKED
+{
+ union {
+ uint32_t head;
+ struct {
+ union {
+ struct {
+ uint16_t : 2;
+ volatile uint16_t tok_pid : 4;
+ uint16_t data : 1;
+ volatile 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;
+ };
+ };
+ volatile 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 {
_va32 uint8_t PER_ID; // [00] Peripheral ID register
_va32 uint8_t ID_COMP; // [04] Peripheral ID complement register
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
index 0f3675349..295f2e578 100644
--- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -24,43 +24,7 @@
//--------------------------------------------------------------------+
// 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;
- __IO uint16_t tok_pid : 4;
- uint16_t data : 1;
- __IO 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;
- };
- };
- __IO 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" );
+// TOK_PID_* and buffer_descriptor_t are shared with the host driver in ci_fs_type.h
typedef struct TU_ATTR_PACKED
{
@@ -175,6 +139,17 @@ static void process_tokdne(uint8_t rhport)
return;
}
const unsigned length = ep->length;
+
+ /* Transfer is complete. For OUT, a multi-packet transfer speculatively arms the
+ * sibling (even/odd) BDT to avoid NAK. When the transfer ends early - e.g. the host
+ * sends a short packet before filling both buffers - that sibling is left armed
+ * (own=1). A leftover armed BDT desyncs the even/odd ping-pong so the next OUT
+ * packet lands in the wrong buffer half (buffer + max_packet_size instead of
+ * buffer), making the stack read stale data. Disarm it here. */
+ if (dir == TUSB_DIR_OUT) {
+ _dcd.bdt[epnum][dir][odd ^ 1].own = 0;
+ }
+
dcd_event_xfer_complete(rhport,
tu_edpt_addr(epnum, dir),
length - remaining, XFER_RESULT_SUCCESS, true);
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/chipidea/ci_fs/hcd_ci_fs.c
index 209940656..a6f5405b5 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/chipidea/ci_fs/hcd_ci_fs.c
@@ -10,63 +10,28 @@
#if CFG_TUH_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
-#ifdef TUP_USBIP_CHIPIDEA_FS_KINETIS
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "ci_fs_type.h"
+
+// Host is currently only available on NXP Kinetis. The ChipIdea-FS host controller
+// interface is register-compatible via ci_fs_regs_t. Unlike the device driver, the host
+// driver does not include the ci_fs_<family>.h header because those define the device
+// dcd_int_enable()/dcd_int_disable() functions, which would collide in a dual-role build.
+#if defined(TUP_USBIP_CHIPIDEA_FS_KINETIS)
#include "fsl_device_registers.h"
- #define KHCI USB0
+ #define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
+ #define CI_FS_IRQN USB0_IRQn
#else
#error "MCU is not supported"
#endif
-#include "host/hcd.h"
-#include "host/usbh.h"
+#define CI_REG CI_FS_REG(0)
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
-
-enum {
- TOK_PID_OUT = 0x1u,
- TOK_PID_IN = 0x9u,
- TOK_PID_SETUP = 0xDu,
- TOK_PID_DATA0 = 0x3u,
- TOK_PID_DATA1 = 0xbu,
- TOK_PID_ACK = 0x2u,
- TOK_PID_STALL = 0xeu,
- TOK_PID_NAK = 0xau,
- TOK_PID_BUSTO = 0x0u,
- TOK_PID_ERR = 0xfu,
-};
-
-typedef struct TU_ATTR_PACKED
-{
- union {
- uint32_t head;
- struct {
- union {
- struct {
- uint16_t : 2;
- __IO uint16_t tok_pid : 4;
- uint16_t data : 1;
- __IO 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;
- };
- };
- __IO 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" );
+// TOK_PID_* and buffer_descriptor_t are shared with the device driver in ci_fs_type.h
typedef struct TU_ATTR_PACKED
{
@@ -107,7 +72,10 @@ typedef struct
union {
/* [OUT,IN][EVEN,ODD] */
buffer_descriptor_t bdt[2][2];
- uint16_t bda[2*2];
+ /* bda aliases bdt for STAT-register indexing: STAT gives the byte-offset/2 of the
+ * completed BD, so it indexes bda[] in uint16_t units. Each buffer_descriptor_t is
+ * 4 uint16_t, hence 2*2*4 elements to span the whole table (must equal sizeof bdt). */
+ uint16_t bda[2*2*4];
};
endpoint_state_t endpoint[2];
pipe_state_t pipe[CFG_TUH_ENDPOINT_MAX * 2];
@@ -142,7 +110,9 @@ static int prepare_packets(int pipenum)
endpoint_state_t *ep = &_hcd.endpoint[dir_tx];
unsigned const odd = ep->odd;
buffer_descriptor_t *bd = _hcd.bdt[dir_tx];
- TU_ASSERT(0 == bd[odd].own, -1);
+ // The host shares a single BDT set across all pipes. If it is still owned by an
+ // in-flight transfer on another pipe, report busy so the caller can defer & retry.
+ if (bd[odd].own) return -1;
// TU_LOG1(" %p dir %d odd %d data %d\r\n", &bd[odd], dir_tx, odd, pipe->data);
@@ -200,11 +170,11 @@ static bool continue_transfer(int pipenum, buffer_descriptor_t *bd)
bd->bc = next_rem > pipe->max_packet_size ? pipe->max_packet_size: next_rem;
__DSB();
bd->own = 1; /* This bit must be set last */
- while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
- KHCI->TOKEN = KHCI->TOKEN; /* Queue the same token as the last */
+ while (CI_REG->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
+ CI_REG->TOKEN = CI_REG->TOKEN; /* Queue the same token as the last */
} else if (TUSB_DIR_IN == tu_edpt_dir(pipe->ep_addr)) { /* IN */
- while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
- KHCI->TOKEN = KHCI->TOKEN;
+ while (CI_REG->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
+ CI_REG->TOKEN = CI_REG->TOKEN;
}
return true;
}
@@ -215,13 +185,21 @@ static bool continue_transfer(int pipenum, buffer_descriptor_t *bd)
static bool resume_transfer(int pipenum)
{
int num_tokens = prepare_packets(pipenum);
- TU_ASSERT(0 <= num_tokens);
+ if (num_tokens < 0) {
+ // Shared BDT still owned by an in-flight transfer on another pipe. Defer this
+ // pipe and retry on the next SOF once the BDT is free (avoids dropping the
+ // transfer, which stalls e.g. a 2nd device enumerating behind a hub while the
+ // app issues concurrent control transfers).
+ _hcd.pending |= TU_BIT(pipenum);
+ CI_REG->INT_EN |= USB_ISTAT_SOFTOK_MASK;
+ return true;
+ }
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ const unsigned ie = NVIC_GetEnableIRQ(CI_FS_IRQN);
+ NVIC_DisableIRQ(CI_FS_IRQN);
pipe_state_t *pipe = &_hcd.pipe[pipenum];
- unsigned flags = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK;
+ unsigned flags = CI_REG->EP[0].CTL & USB_ENDPT_HOSTWOHUB_MASK;
flags |= USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
switch (pipe->xfer) {
case TUSB_XFER_CONTROL:
@@ -236,16 +214,16 @@ static bool resume_transfer(int pipenum)
}
// TU_LOG1(" resume pipenum %d flags %x\r\n", pipenum, flags);
- KHCI->ENDPOINT[0].ENDPT = flags;
- KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | pipe->dev_addr;
+ CI_REG->EP[0].CTL = flags;
+ CI_REG->ADDR = (CI_REG->ADDR & USB_ADDR_LSEN_MASK) | pipe->dev_addr;
unsigned const token = tu_edpt_number(pipe->ep_addr) |
((tu_edpt_dir(pipe->ep_addr) ? TOK_PID_IN: TOK_PID_OUT) << USB_TOKEN_TOKENPID_SHIFT);
do {
- while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
- KHCI->TOKEN = token;
+ while (CI_REG->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
+ CI_REG->TOKEN = token;
} while (--num_tokens);
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ if (ie) NVIC_EnableIRQ(CI_FS_IRQN);
return true;
}
@@ -253,19 +231,38 @@ static void suspend_transfer(int pipenum, buffer_descriptor_t *bd)
{
pipe_state_t *pipe = &_hcd.pipe[pipenum];
pipe->buffer = bd->addr;
- pipe->data = bd->data ^ 1;
+ // A NAK transfers no data, so the data toggle must be preserved for the retry.
+ // (Do NOT flip pipe->data here: flipping it makes the retried packet use the wrong
+ // DATA0/DATA1, which the device silently discards - breaking any bulk/interrupt
+ // transfer that is NAKed, e.g. the MSC CBW/CSW when the device is momentarily busy.)
if ((TUSB_XFER_INTERRUPT == pipe->xfer) ||
(TUSB_XFER_BULK == pipe->xfer)) {
_hcd.pending |= TU_BIT(pipenum);
- KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK;
+ CI_REG->INT_EN |= USB_ISTAT_SOFTOK_MASK;
+ }
+}
+
+// Release the speculatively-armed sibling BDT of a multi-packet transfer.
+// prepare_packets arms the sibling (odd^1) BDT (own=1) so a multi-packet transfer can
+// ping-pong without NAKs. When the transfer ends - completes early on a short IN packet,
+// stalls/errors, or (for IN) is NAKed before the sibling's token is issued - that sibling
+// is left owned by the SIE. Because the host shares ONE BDT set across all pipes, a
+// leftover armed sibling blocks every other pipe forever (e.g. a 2nd device stuck
+// enumerating behind a hub). Release it - but ONLY for a multi-packet transfer: a
+// single-packet transfer never armed a sibling, so that BDT slot may legitimately belong
+// to another pipe's in-flight transfer.
+static inline void release_sibling_bd(unsigned s, const pipe_state_t *pipe)
+{
+ if (pipe->length > pipe->max_packet_size) {
+ ((buffer_descriptor_t *)&_hcd.bda[s ^ USB_STAT_ODD_MASK])->own = 0;
}
}
static void process_tokdne(uint8_t rhport)
{
(void)rhport;
- const unsigned s = KHCI->STAT;
- KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
+ const unsigned s = CI_REG->STAT;
+ CI_REG->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
uint8_t const dir_in = (s & USB_STAT_TX_MASK) ? TUSB_DIR_OUT: TUSB_DIR_IN;
unsigned const odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
@@ -295,6 +292,12 @@ static void process_tokdne(uint8_t rhport)
result = XFER_RESULT_SUCCESS;
break;
case TOK_PID_NAK:
+ // Release the speculatively-armed sibling so the deferred retry (and any other pipe
+ // sharing the single BDT) can claim it; otherwise it stays own=1 forever and every
+ // same-direction transfer wedges. IN only: an IN issues just one token so the sibling
+ // was never put on the wire, whereas an OUT issues both tokens and its sibling may
+ // still be in flight - touching it there would race the SIE write-back.
+ if (TUSB_DIR_IN == dir_in) release_sibling_bd(s, &_hcd.pipe[pipenum]);
suspend_transfer(pipenum, bd);
next_pipenum = select_next_pipenum(pipenum);
if (0 <= next_pipenum)
@@ -310,8 +313,9 @@ static void process_tokdne(uint8_t rhport)
}
_hcd.in_progress &= ~TU_BIT(pipenum);
pipe_state_t *pipe = &_hcd.pipe[ep->pipenum];
+ release_sibling_bd(s, pipe);
hcd_event_xfer_complete(pipe->dev_addr,
- tu_edpt_addr(KHCI->TOKEN & USB_TOKEN_TOKENENDPT_MASK, dir_in),
+ tu_edpt_addr(CI_REG->TOKEN & USB_TOKEN_TOKENENDPT_MASK, dir_in),
pipe->length - pipe->remaining,
result, true);
next_pipenum = select_next_pipenum(pipenum);
@@ -321,29 +325,32 @@ static void process_tokdne(uint8_t rhport)
static void process_attach(uint8_t rhport)
{
- unsigned ctl = KHCI->CTL;
+ unsigned ctl = CI_REG->CTL;
if (!(ctl & USB_CTL_JSTATE_MASK)) {
/* The attached device is a low speed device. */
- KHCI->ADDR = USB_ADDR_LSEN_MASK;
- KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_HOSTWOHUB_MASK;
+ CI_REG->ADDR = USB_ADDR_LSEN_MASK;
+ CI_REG->EP[0].CTL = USB_ENDPT_HOSTWOHUB_MASK;
}
hcd_event_device_attach(rhport, true);
}
static void process_bus_reset(uint8_t rhport)
{
- KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK;
- KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
- KHCI->CTL &= ~USB_CTL_USBENSOFEN_MASK;
- KHCI->ADDR = 0;
- KHCI->ENDPOINT[0].ENDPT = 0;
+ CI_REG->INT_STAT = USB_ISTAT_TOKDNE_MASK;
+ CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+ CI_REG->CTL &= ~USB_CTL_USBENSOFEN_MASK;
+ CI_REG->ADDR = 0;
+ CI_REG->EP[0].CTL = 0;
hcd_event_device_remove(rhport, true);
_hcd.in_progress = 0;
_hcd.pending = 0;
+ // Clear the ENTIRE shared BDT (both directions, both even/odd). Clearing only the IN
+ // pair left a stale OUT/SETUP descriptor (own=1) after a disconnect mid-OUT, which then
+ // blocks the first control transfer on re-enumeration.
buffer_descriptor_t *bd = &_hcd.bdt[0][0];
- for (unsigned i = 0; i < 2; ++i, ++bd) {
+ for (unsigned i = 0; i < 2 * 2; ++i, ++bd) {
bd->head = 0;
}
}
@@ -354,31 +361,31 @@ static void process_bus_reset(uint8_t rhport)
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
(void) rh_init;
- KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
- while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+ CI_REG->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+ while (CI_REG->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
tu_memclr(&_hcd, sizeof(_hcd));
- KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
- KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_hcd.bdt >> 8);
- KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_hcd.bdt >> 16);
- KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_hcd.bdt >> 24);
+ CI_REG->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
+ CI_REG->BDT_PAGE1 = (uint8_t)((uintptr_t)_hcd.bdt >> 8);
+ CI_REG->BDT_PAGE2 = (uint8_t)((uintptr_t)_hcd.bdt >> 16);
+ CI_REG->BDT_PAGE3 = (uint8_t)((uintptr_t)_hcd.bdt >> 24);
- KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
- KHCI->CTL |= USB_CTL_ODDRST_MASK;
+ CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+ CI_REG->CTL |= USB_CTL_ODDRST_MASK;
for (unsigned i = 0; i < 16; ++i) {
- KHCI->ENDPOINT[i].ENDPT = 0;
+ CI_REG->EP[i].CTL = 0;
}
- KHCI->CTL &= ~USB_CTL_ODDRST_MASK;
+ CI_REG->CTL &= ~USB_CTL_ODDRST_MASK;
- KHCI->SOFTHLD = 74; /* for 64-byte packets */
- // KHCI->SOFTHLD = 144; /* for low speed 8-byte packets */
- KHCI->CTL = USB_CTL_HOSTMODEEN_MASK | USB_CTL_SE0_MASK;
- KHCI->USBCTRL = USB_USBCTRL_PDE_MASK;
+ CI_REG->SOF_THLD = 74; /* for 64-byte packets */
+ // CI_REG->SOF_THLD = 144; /* for low speed 8-byte packets */
+ CI_REG->CTL = USB_CTL_HOSTMODEEN_MASK | USB_CTL_SE0_MASK;
+ CI_REG->USBCTRL = USB_USBCTRL_PDE_MASK;
- NVIC_ClearPendingIRQ(USB0_IRQn);
- KHCI->INTEN = USB_INTEN_ATTACHEN_MASK | USB_INTEN_TOKDNEEN_MASK |
+ NVIC_ClearPendingIRQ(CI_FS_IRQN);
+ CI_REG->INT_EN = USB_INTEN_ATTACHEN_MASK | USB_INTEN_TOKDNEEN_MASK |
USB_INTEN_USBRSTEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
- KHCI->ERREN = 0xff;
+ CI_REG->ERR_ENB = 0xff;
return true;
}
@@ -386,13 +393,13 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
void hcd_int_enable(uint8_t rhport)
{
(void)rhport;
- NVIC_EnableIRQ(USB0_IRQn);
+ NVIC_EnableIRQ(CI_FS_IRQN);
}
void hcd_int_disable(uint8_t rhport)
{
(void)rhport;
- NVIC_DisableIRQ(USB0_IRQn);
+ NVIC_DisableIRQ(CI_FS_IRQN);
}
uint32_t hcd_frame_number(uint8_t rhport)
@@ -401,8 +408,8 @@ uint32_t hcd_frame_number(uint8_t rhport)
/* The device must be reset at least once after connection
* in order to start the frame counter. */
if (_hcd.need_reset) hcd_port_reset(rhport);
- uint32_t frmnum = KHCI->FRMNUML;
- frmnum |= KHCI->FRMNUMH << 8u;
+ uint32_t frmnum = CI_REG->FRM_NUML;
+ frmnum |= CI_REG->FRM_NUMH << 8u;
return frmnum;
}
@@ -412,7 +419,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
bool hcd_port_connect_status(uint8_t rhport)
{
(void)rhport;
- if (KHCI->ISTAT & USB_ISTAT_ATTACH_MASK)
+ if (CI_REG->INT_STAT & USB_ISTAT_ATTACH_MASK)
return true;
return false;
}
@@ -420,12 +427,12 @@ bool hcd_port_connect_status(uint8_t rhport)
void hcd_port_reset(uint8_t rhport)
{
(void)rhport;
- KHCI->CTL &= ~USB_CTL_USBENSOFEN_MASK;
- KHCI->CTL |= USB_CTL_RESET_MASK;
+ CI_REG->CTL &= ~USB_CTL_USBENSOFEN_MASK;
+ CI_REG->CTL |= USB_CTL_RESET_MASK;
unsigned cnt = SystemCoreClock / 100;
while (cnt--) __NOP();
- KHCI->CTL &= ~USB_CTL_RESET_MASK;
- KHCI->CTL |= USB_CTL_USBENSOFEN_MASK;
+ CI_REG->CTL &= ~USB_CTL_RESET_MASK;
+ CI_REG->CTL |= USB_CTL_USBENSOFEN_MASK;
_hcd.need_reset = false;
}
@@ -437,26 +444,26 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
(void)rhport;
tusb_speed_t speed = TUSB_SPEED_FULL;
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- if (KHCI->ADDR & USB_ADDR_LSEN_MASK)
+ const unsigned ie = NVIC_GetEnableIRQ(CI_FS_IRQN);
+ NVIC_DisableIRQ(CI_FS_IRQN);
+ if (CI_REG->ADDR & USB_ADDR_LSEN_MASK)
speed = TUSB_SPEED_LOW;
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ if (ie) NVIC_EnableIRQ(CI_FS_IRQN);
return speed;
}
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ const unsigned ie = NVIC_GetEnableIRQ(CI_FS_IRQN);
+ NVIC_DisableIRQ(CI_FS_IRQN);
pipe_state_t *p = &_hcd.pipe[0];
pipe_state_t *end = &_hcd.pipe[CFG_TUH_ENDPOINT_MAX * 2];
for (;p != end; ++p) {
if (p->dev_addr == dev_addr)
tu_memclr(p, sizeof(*p));
}
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ if (ie) NVIC_EnableIRQ(CI_FS_IRQN);
}
//--------------------------------------------------------------------+
@@ -483,12 +490,12 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
_hcd.in_progress |= TU_BIT(pipenum);
- unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK;
- KHCI->ENDPOINT[0].ENDPT = hostwohub |
+ unsigned hostwohub = CI_REG->EP[0].CTL & USB_ENDPT_HOSTWOHUB_MASK;
+ CI_REG->EP[0].CTL = hostwohub |
USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
- KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | dev_addr;
- while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
- KHCI->TOKEN = (TOK_PID_SETUP << USB_TOKEN_TOKENPID_SHIFT);
+ CI_REG->ADDR = (CI_REG->ADDR & USB_ADDR_LSEN_MASK) | dev_addr;
+ while (CI_REG->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
+ CI_REG->TOKEN = (TOK_PID_SETUP << USB_TOKEN_TOKENPID_SHIFT);
return true;
}
@@ -540,16 +547,16 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
TU_ASSERT(0 <= pipenum);
TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(pipenum)));
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ unsigned const ie = NVIC_GetEnableIRQ(CI_FS_IRQN);
+ NVIC_DisableIRQ(CI_FS_IRQN);
pipe_state_t *pipe = &_hcd.pipe[pipenum];
pipe->buffer = buffer;
pipe->length = buflen;
pipe->remaining = buflen;
_hcd.in_progress |= TU_BIT(pipenum);
_hcd.pending |= TU_BIT(pipenum); /* Send at the next Frame */
- KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK;
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ CI_REG->INT_EN |= USB_ISTAT_SOFTOK_MASK;
+ if (ie) NVIC_EnableIRQ(CI_FS_IRQN);
return true;
}
@@ -577,42 +584,42 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
void hcd_int_handler(uint8_t rhport, bool in_isr)
{
(void) in_isr;
- uint32_t is = KHCI->ISTAT;
- uint32_t msk = KHCI->INTEN;
+ uint32_t is = CI_REG->INT_STAT;
+ uint32_t msk = CI_REG->INT_EN;
// TU_LOG1("S %lx\r\n", is);
/* clear disabled interrupts */
- KHCI->ISTAT = (is & ~msk & ~USB_ISTAT_TOKDNE_MASK) | USB_ISTAT_SOFTOK_MASK;
+ CI_REG->INT_STAT = (is & ~msk & ~USB_ISTAT_TOKDNE_MASK) | USB_ISTAT_SOFTOK_MASK;
is &= msk;
if (is & USB_ISTAT_ERROR_MASK) {
- unsigned err = KHCI->ERRSTAT;
+ unsigned err = CI_REG->ERR_STAT;
if (err) {
TU_LOG1(" ERR %x\r\n", err);
- KHCI->ERRSTAT = err;
+ CI_REG->ERR_STAT = err;
} else {
- KHCI->INTEN &= ~USB_ISTAT_ERROR_MASK;
+ CI_REG->INT_EN &= ~USB_ISTAT_ERROR_MASK;
}
}
if (is & USB_ISTAT_USBRST_MASK) {
- KHCI->INTEN = (msk & ~USB_INTEN_USBRSTEN_MASK) | USB_INTEN_ATTACHEN_MASK;
+ CI_REG->INT_EN = (msk & ~USB_INTEN_USBRSTEN_MASK) | USB_INTEN_ATTACHEN_MASK;
process_bus_reset(rhport);
return;
}
if (is & USB_ISTAT_ATTACH_MASK) {
- KHCI->INTEN = (msk & ~USB_INTEN_ATTACHEN_MASK) | USB_INTEN_USBRSTEN_MASK;
+ CI_REG->INT_EN = (msk & ~USB_INTEN_ATTACHEN_MASK) | USB_INTEN_USBRSTEN_MASK;
_hcd.need_reset = true;
process_attach(rhport);
return;
}
if (is & USB_ISTAT_STALL_MASK) {
- KHCI->ISTAT = USB_ISTAT_STALL_MASK;
+ CI_REG->INT_STAT = USB_ISTAT_STALL_MASK;
}
if (is & USB_ISTAT_SOFTOK_MASK) {
msk &= ~USB_ISTAT_SOFTOK_MASK;
- KHCI->INTEN = msk;
+ CI_REG->INT_EN = msk;
if (_hcd.pending) {
int pipenum = __builtin_ctz(_hcd.pending);
_hcd.pending = 0;
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
deleted file mode 100644
index 9f61bd236..000000000
--- a/src/portable/nxp/khci/dcd_khci.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * SPDX-FileCopyrightText: Copyright (c) 2020 Koji Kitayama
- * SPDX-FileCopyrightText: Copyright (c) 2020 Ha Thach (tinyusb.org)
- * SPDX-License-Identifier: MIT
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
-
-#ifdef TUP_USBIP_CHIPIDEA_FS_KINETIS
- #include "fsl_device_registers.h"
- #define KHCI USB0
-#else
- #error "MCU is not supported"
-#endif
-
-#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;
- __IO uint16_t tok_pid : 4;
- uint16_t data : 1;
- __IO 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;
- };
- };
- __IO 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_TUD_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;
- TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
-
- _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), false);
-}
-
-static void process_stall(uint8_t rhport)
-{
- for (int i = 0; i < 16; ++i) {
- unsigned const endpt = KHCI->ENDPOINT[i].ENDPT;
-
- if (endpt & USB_ENDPT_EPSTALL_MASK) {
- // prepare next setup if endpoint0
- if ( i == 0 ) prepare_next_setup_packet(rhport);
-
- // clear stall bit
- KHCI->ENDPOINT[i].ENDPT = 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 */
-
- uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT);
- uint8_t const dir = (s & USB_STAT_TX_MASK) >> USB_STAT_TX_SHIFT;
- unsigned const odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
-
- buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
- endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
-
- /* 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;
- }
-
- 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,
- tu_edpt_addr(epnum, dir),
- length - remaining, XFER_RESULT_SUCCESS, true);
- if (0 == epnum && 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 = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK |
- USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_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_sleep(uint8_t rhport)
-{
- // Enable resume & disable suspend interrupt
- const unsigned inten = KHCI->INTEN;
-
- KHCI->INTEN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
- KHCI->USBTRC0 |= USB_USBTRC0_USBRESMEN_MASK;
- KHCI->USBCTRL |= USB_USBCTRL_SUSP_MASK;
-
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
-}
-
-static void process_bus_resume(uint8_t rhport)
-{
- // Enable suspend & disable resume interrupt
- const unsigned inten = KHCI->INTEN;
-
- KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; // will also clear USB_USBTRC0_USB_RESUME_INT_MASK
- KHCI->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
- KHCI->INTEN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
-
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
-}
-
-/*------------------------------------------------------------------*/
-/* Device API
- *------------------------------------------------------------------*/
-bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rhport;
- (void) rh_init;
-
- // save crystal-less setting (if available)
- #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
- uint32_t clk_recover_irc_en = KHCI->CLK_RECOVER_IRC_EN;
- uint32_t clk_recover_ctrl = KHCI->CLK_RECOVER_CTRL;
- #endif
-
- KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
- while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
-
- // restore crystal-less setting (if available)
- #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
- KHCI->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
- KHCI->CLK_RECOVER_CTRL |= clk_recover_ctrl;
- #endif
-
- 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);
-
- KHCI->INTEN = USB_INTEN_USBRSTEN_MASK;
-
- dcd_connect(rhport);
- NVIC_ClearPendingIRQ(USB0_IRQn);
-
- return true;
-}
-
-void dcd_int_enable(uint8_t rhport)
-{
- (void) rhport;
- NVIC_EnableIRQ(USB0_IRQn);
-}
-
-void dcd_int_disable(uint8_t rhport)
-{
- (void) rhport;
- NVIC_DisableIRQ(USB0_IRQn);
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- _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, false);
-}
-
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void) rhport;
-
- KHCI->CTL |= USB_CTL_RESUME_MASK;
-
- unsigned cnt = SystemCoreClock / 1000;
- 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;
-}
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- // TODO implement later
-}
-
-//--------------------------------------------------------------------+
-// Endpoint API
-//--------------------------------------------------------------------+
-static bool edpt_open(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size, tusb_xfer_type_t xfer) {
- (void)rhport;
-
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir = tu_edpt_dir(ep_addr);
- endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
- const unsigned odd = ep->odd;
- buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
-
- /* No support for control transfer */
- TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
-
- ep->max_packet_size = max_packet_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;
-}
-
-bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
- return edpt_open(rhport, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer);
-}
-
-bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
- return edpt_open(rhport, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
-}
-
-bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
- const unsigned epn = tu_edpt_number(ep_desc->bEndpointAddress);
- const unsigned dir = tu_edpt_dir(ep_desc->bEndpointAddress);
- endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
-
- dcd_int_disable(rhport);
- ep->max_packet_size = tu_edpt_packet_size(ep_desc);
- dcd_int_enable(rhport);
-
- return true;
-}
-
-void dcd_edpt_close_all(uint8_t rhport)
-{
- (void) rhport;
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- for (unsigned i = 1; i < 16; ++i) {
- KHCI->ENDPOINT[i].ENDPT = 0;
- }
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
- buffer_descriptor_t *bd = _dcd.bdt[1][0];
- for (unsigned i = 2; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
- bd->head = 0;
- }
- endpoint_state_t *ep = &_dcd.endpoint[1][0];
- for (unsigned i = 2; i < sizeof(_dcd.endpoint)/sizeof(*ep); ++i, ++ep) {
- /* Clear except the odd */
- ep->max_packet_size = 0;
- ep->length = 0;
- ep->remaining = 0;
- }
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr)
-{
- (void) rhport;
- (void) is_isr;
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir = tu_edpt_dir(ep_addr);
- endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
- buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
- TU_ASSERT(0 == bd->own);
-
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
-
- 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; /* This bit must be set last */
-
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
- return true;
-}
-
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
- const unsigned epn = tu_edpt_number(ep_addr);
-
- if (0 == epn) {
- KHCI->ENDPOINT[epn].ENDPT |= USB_ENDPT_EPSTALL_MASK;
- } else {
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned odd = _dcd.endpoint[epn][dir].odd;
- buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][odd];
- TU_ASSERT(0 == bd->own,);
-
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
-
- bd->bdt_stall = 1;
- __DSB();
- bd->own = 1; /* This bit must be set last */
-
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
- }
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
- const unsigned epn = tu_edpt_number(ep_addr);
- TU_VERIFY(epn,);
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned odd = _dcd.endpoint[epn][dir].odd;
- buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
- TU_VERIFY(bd[odd].own,);
-
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
-
- bd[odd].own = 0;
- __DSB();
-
- // clear stall
- bd[odd].bdt_stall = 0;
-
- // Reset data toggle
- bd[odd ].data = 0;
- bd[odd ^ 1].data = 1;
-
- // We already cleared this in ISR, but just clear it here to be safe
- const unsigned endpt = KHCI->ENDPOINT[epn].ENDPT;
- if (endpt & USB_ENDPT_EPSTALL_MASK) {
- KHCI->ENDPOINT[epn].ENDPT = endpt & ~USB_ENDPT_EPSTALL_MASK;
- }
-
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
-}
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-void dcd_int_handler(uint8_t rhport)
-{
- uint32_t is = KHCI->ISTAT;
- uint32_t msk = KHCI->INTEN;
-
- // clear non-enabled interrupts
- 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 */
- }
-
- if (is & USB_ISTAT_USBRST_MASK) {
- KHCI->ISTAT = is; /* discard any pending events */
- process_bus_reset(rhport);
- }
-
- if (is & USB_ISTAT_SLEEP_MASK) {
- // TU_LOG3("Suspend: "); TU_LOG2_HEX(is);
-
- // Note Host usually has extra delay after bus reset (without SOF), which could falsely
- // detected as Sleep event. Though usbd has debouncing logic so we are good
- KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
- process_bus_sleep(rhport);
- }
-
-#if 0 // ISTAT_RESUME never trigger, probably for host mode ?
- if (is & USB_ISTAT_RESUME_MASK) {
- // TU_LOG2("ISTAT Resume: "); TU_LOG2_HEX(is);
- KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
- process_bus_resume(rhport);
- }
-#endif
-
- if (KHCI->USBTRC0 & USB_USBTRC0_USB_RESUME_INT_MASK) {
- // TU_LOG2("USBTRC0 Resume: "); TU_LOG2_HEX(is); TU_LOG2_HEX(KHCI->USBTRC0);
- process_bus_resume(rhport);
- }
-
- if (is & USB_ISTAT_SOFTOK_MASK) {
- KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
- dcd_event_sof(rhport, tu_u16(KHCI->FRMNUMH, KHCI->FRMNUML), true);
- }
-
- if (is & USB_ISTAT_STALL_MASK) {
- KHCI->ISTAT = USB_ISTAT_STALL_MASK;
- process_stall(rhport);
- }
-
- if (is & USB_ISTAT_TOKDNE_MASK) {
- process_tokdne(rhport);
- }
-}
-#endif
diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json
index 6871d812f..812eb7571 100644
--- a/test/hil/tinyusb.json
+++ b/test/hil/tinyusb.json
@@ -1,6 +1,36 @@
{
"boards": [
{
+ "name": "frdm_k64f",
+ "uid": "FFFF1A00FFFFFFFF1031454E0F000280",
+ "tests": {
+ "device": false,
+ "host": true,
+ "dual": false,
+ "dev_attached": [
+ {
+ "vid_pid": "1a86_55d4",
+ "serial": "52D2023934",
+ "is_cdc": true,
+ "comment": "CH9102 USB-serial (TX-RX loopback)"
+ },
+ {
+ "vid_pid": "21c4_0cc7",
+ "serial": "9000588268687E25",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 60620800,
+ "msc_inquiry": "Lexar USB Flash Drive PMAP"
+ }
+ ]
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000621000000",
+ "args": "-device MK64FN1M0xxx12"
+ }
+ },
+ {
"name": "ek_tm4c123gxl",
"uid": "010105186C60A110",
"tests": {