mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
migrate NXP Kinetis khci to chipidea ci_fs driver (device + host)
Complete the khci -> chipidea ci_fs migration that was started for device
(commit d70403f1f "host is not yet"):
- device: switch kinetis_k/kl/k32l (Makefiles + k32l CMake) to dcd_ci_fs.c
- host: add hcd_ci_fs.c (port of hcd_khci.c onto ci_fs_regs_t) and switch all
Kinetis families to it; remove src/portable/nxp/khci entirely
- enable host examples (device_info, cdc_msc_hid) for mcu:KINETIS_K
- README: merge the KL and K32L2 rows into a single "KL, K32L" ci_fs row
hcd_ci_fs.c also fixes two pre-existing host bugs found via HIL on frdm_k64f
(present in the old hcd_khci.c too):
- data toggle was flipped on a NAK in suspend_transfer; a NAK transfers no
data so the toggle must be preserved, else the retried bulk packet is
silently discarded by the device (MSC CBW/CSW hang). See comment in file.
- prepare_packets asserted and dropped a transfer when the single shared BDT
was still owned by an in-flight transfer under concurrent activity; now it
returns busy and resume_transfer defers/retries on the next SOF.
HIL verified on frdm_k64f: device 13/13, host cdc_msc_hid (CDC mount + echo +
MSC mount, through a hub).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExGPLP5eU43LR7o6yYLpNi
This commit is contained in:
@ -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 | |
|
||||
| | +-------------------+--------+------+-----------+------------------------+--------------------+
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -10,15 +10,23 @@
|
||||
|
||||
#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
|
||||
@ -142,7 +150,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 +210,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 +225,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 +254,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 +271,22 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -311,7 +332,7 @@ static void process_tokdne(uint8_t rhport)
|
||||
_hcd.in_progress &= ~TU_BIT(pipenum);
|
||||
pipe_state_t *pipe = &_hcd.pipe[ep->pipenum];
|
||||
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,22 +342,22 @@ 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);
|
||||
|
||||
@ -354,31 +375,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 +407,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 +422,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 +433,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 +441,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 +458,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 +504,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 +561,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 +598,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;
|
||||
@ -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
|
||||
Reference in New Issue
Block a user