From a4bc6075cef1685ecc896730e704400b35e2392a Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:17:50 +0900 Subject: [PATCH 1/7] Add HCD functions for KL25Z --- hw/bsp/frdm_kl25z/frdm_kl25z.c | 5 + src/portable/nxp/khci/hcd_khci.c | 475 +++++++++++++++++++++++++++++++ 2 files changed, 480 insertions(+) create mode 100644 src/portable/nxp/khci/hcd_khci.c diff --git a/hw/bsp/frdm_kl25z/frdm_kl25z.c b/hw/bsp/frdm_kl25z/frdm_kl25z.c index 72982ed8..2930ed56 100644 --- a/hw/bsp/frdm_kl25z/frdm_kl25z.c +++ b/hw/bsp/frdm_kl25z/frdm_kl25z.c @@ -39,7 +39,12 @@ //--------------------------------------------------------------------+ void USB0_IRQHandler(void) { +#if TUSB_OPT_HOST_ENABLED + tuh_int_handler(0); +#endif +#if TUSB_OPT_DEVICE_ENABLED tud_int_handler(0); +#endif } //--------------------------------------------------------------------+ diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c new file mode 100644 index 00000000..877c4813 --- /dev/null +++ b/src/portable/nxp/khci/hcd_khci.c @@ -0,0 +1,475 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 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_HOST_ENABLED && ( \ + ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \ + ) + +#include "fsl_device_registers.h" +#define KHCI USB0 + +#include "host/hcd.h" + +//--------------------------------------------------------------------+ +// 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" ); + +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 TU_ATTR_PACKED +{ + uint8_t dev_addr; + uint8_t ep_addr; + uint16_t max_packet_size; + union { + uint8_t flags; + struct { + uint8_t data : 1; + uint8_t xfer : 2; + uint8_t : 0; + }; + }; +} pipe_state_t; + + +typedef struct +{ + union { + /* [#EP][OUT,IN][EVEN,ODD] */ + buffer_descriptor_t bdt[16][2][2]; + uint16_t bda[512]; + }; + endpoint_state_t endpoint[2]; + pipe_state_t pipe[HCD_MAX_XFER]; +} hcd_data_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +// BDT(Buffer Descriptor Table) must be 256-byte aligned +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd; + +TU_VERIFY_STATIC( sizeof(_hcd.bdt) == 512, "size is not correct" ); + +static pipe_state_t *find_pipe(uint8_t dev_addr, uint8_t ep_addr) +{ + /* Find the target pipe */ + pipe_state_t *p = _hcd.pipe; + pipe_state_t *end = p + HCD_MAX_XFER; + for (;p != end; ++p) { + if ((p->dev_addr == dev_addr) && (p->ep_addr != ep_addr)) + return p; + } + return NULL; +} + +static int prepare_packets(uint8_t rhport, uint_fast8_t dir_in, uint8_t* buffer, uint_fast16_t total_bytes) +{ + (void)rhport; + const unsigned dir_tx = dir_in ? 0 : 1; + endpoint_state_t *ep = &_hcd.endpoint[dir_tx]; + buffer_descriptor_t *bd = &_hcd.bdt[0][dir_tx][ep->odd]; + TU_ASSERT(0 == bd->own, 0); + + ep->length = total_bytes; + ep->remaining = total_bytes; + + int num_pkts = 0; /* The number of prepared packets */ + 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; + ++num_pkts; + } + bd->bc = total_bytes >= mps ? mps: total_bytes; + bd->addr = buffer; + __DSB(); + bd->own = 1; /* This bit must be set last */ + ++num_pkts; + return num_pkts; +} + +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 */ + + uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT); + TU_ASSERT(0 == epnum,); + 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; + + buffer_descriptor_t *bd = (buffer_descriptor_t *)&_hcd.bda[s]; + endpoint_state_t *ep = &_hcd.endpoint[s >> 3]; + + /* fetch status 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; + + const unsigned bc = bd->bc; + const unsigned remaining = ep->remaining - bc; + if ((TOK_PID_DATA0 == pid) || (TOK_PID_DATA1 == pid) || (TOK_PID_ACK == pid)) { + /* Go on the next packet transfer */ + if (remaining && bc == ep->max_packet_size) { + 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; /* This bit must be set last */ + while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ; + KHCI->TOKEN = KHCI->TOKEN; /* Queue the same token as the last */ + } + return; + } + } + const unsigned length = ep->length; + xfer_result_t result; + switch (pid) { + default: + result = XFER_RESULT_SUCCESS; + break; + case TOK_PID_STALL: + result = XFER_RESULT_STALLED; + break; + case TOK_PID_NAK: + case TOK_PID_ERR: + case TOK_PID_BUSTO: + result = XFER_RESULT_FAILED; + break; + } + hcd_event_xfer_complete(KHCI->ADDR & USB_ADDR_ADDR_MASK, + tu_edpt_addr(KHCI->TOKEN & USB_TOKEN_TOKENENDPT_MASK, dir_in), + length - remaining, result, true); +} + +static void process_attach(uint8_t rhport) +{ + unsigned ctl = KHCI->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; + } + hcd_event_device_attach(rhport, true); +} + +/*------------------------------------------------------------------*/ +/* Host API + *------------------------------------------------------------------*/ +bool hcd_init(uint8_t rhport) +{ + (void)rhport; + + KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK; + while (KHCI->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); + + KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; + KHCI->CTL |= USB_CTL_ODDRST_MASK; + for (unsigned i = 0; i < 16; ++i) { + KHCI->ENDPOINT[i].ENDPT = 0; + } + const endpoint_state_t ep0 = { + .max_packet_size = CFG_TUD_ENDPOINT0_SIZE, + .odd = 0, + .length = 0, + .remaining = 0, + }; + _hcd.endpoint[0] = ep0; + _hcd.endpoint[1] = ep0; + KHCI->CTL &= ~USB_CTL_ODDRST_MASK; + + KHCI->SOFTHLD = 74; /* for 64-byte packets */ + KHCI->CTL = USB_CTL_HOSTMODEEN_MASK | USB_CTL_SE0_MASK; + KHCI->USBCTRL = USB_USBCTRL_PDE_MASK; + + NVIC_ClearPendingIRQ(USB0_IRQn); + KHCI->INTEN = USB_INTEN_ATTACHEN_MASK; + return true; +} + +void hcd_int_enable(uint8_t rhport) +{ + (void)rhport; + NVIC_EnableIRQ(USB0_IRQn); +} + +void hcd_int_disable(uint8_t rhport) +{ + (void)rhport; + NVIC_DisableIRQ(USB0_IRQn); +} + +uint32_t hcd_frame_number(uint8_t rhport) +{ + (void)rhport; + uint32_t frmnum = KHCI->FRMNUML; + frmnum |= KHCI->FRMNUMH << 8u; + return frmnum; +} + +/*--------------------------------------------------------------------+ + * Port API + *--------------------------------------------------------------------+ */ +bool hcd_port_connect_status(uint8_t rhport) +{ + (void)rhport; + return false; +} + +void hcd_port_reset(uint8_t rhport) +{ + (void)rhport; + KHCI->CTL &= ~USB_CTL_USBENSOFEN_MASK; + KHCI->CTL |= USB_CTL_RESET_MASK; + unsigned cnt = SystemCoreClock / 100; + while (cnt--) __NOP(); + KHCI->CTL &= ~USB_CTL_RESET_MASK; + KHCI->CTL |= USB_CTL_USBENSOFEN_MASK; +} + +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) + speed = TUSB_SPEED_LOW; + if (ie) NVIC_EnableIRQ(USB0_IRQn); + return speed; +} + +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) +{ + (void)rhport; + pipe_state_t *p = _hcd.pipe; + pipe_state_t *end = p + HCD_MAX_XFER; + for (;p != end; ++p) { + if (p->dev_addr == dev_addr) + tu_memclr(p, sizeof(*p)); + } +} + +//--------------------------------------------------------------------+ +// Endpoints API +//--------------------------------------------------------------------+ +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) +{ + (void)rhport; + const unsigned rx_odd = _hcd.endpoint[0].odd; + const unsigned tx_odd = _hcd.endpoint[1].odd; + TU_ASSERT(0 == _hcd.bdt[0][0][tx_odd].own); + + const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); + NVIC_DisableIRQ(USB0_IRQn); + + _hcd.bdt[0][0][rx_odd ].data = 1; + _hcd.bdt[0][0][rx_odd ^ 1].data = 0; + _hcd.bdt[0][1][tx_odd ].data = 0; + _hcd.bdt[0][1][tx_odd ^ 1].data = 1; + + unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; + KHCI->ENDPOINT[0].ENDPT = hostwohub | + USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; + bool ret = false; + if (prepare_packets(rhport, TUSB_DIR_OUT, (void*)(uintptr_t)setup_packet, 8)) { + 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); + ret = true; + } + if (ie) NVIC_EnableIRQ(USB0_IRQn); + return ret; +} + +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) +{ + (void)rhport; + /* Find a free pipe */ + pipe_state_t *p = _hcd.pipe; + pipe_state_t *end = p + HCD_MAX_XFER; + for (;p < end && p->dev_addr; ++p) ; + if (p == end) return false; + p->dev_addr = dev_addr; + p->ep_addr = ep_desc->bEndpointAddress; + p->max_packet_size = ep_desc->wMaxPacketSize; + p->xfer = ep_desc->bmAttributes.xfer; + p->data = 0; + return true; +} + +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) +{ + (void)rhport; + const unsigned dir_in = tu_edpt_dir(ep_addr); + const unsigned odd = _hcd.endpoint[dir_in ^ 1].odd; + buffer_descriptor_t *bd = _hcd.bdt[0][dir_in ^ 1]; + TU_ASSERT(0 == bd[odd].own); + + unsigned flags = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; + if (tu_edpt_number(ep_addr)) { + pipe_state_t *p = find_pipe(dev_addr, ep_addr); + if (!p) return false; + bd[odd ].data = p->data; + bd[odd ^ 1].data = p->data ^ 1; + bd[odd ^ 1].own = 0; + flags |= USB_ENDPT_EPCTLDIS_MASK; + /* Disable retry for a interrupt transfer. */ + if (TUSB_XFER_INTERRUPT == p->xfer) + flags |= USB_ENDPT_RETRYDIS_MASK; + } + unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; + KHCI->ENDPOINT[0].ENDPT = hostwohub | flags; + int num_pkts = prepare_packets(rhport, dir_in, buffer, buflen); + if (!num_pkts) return false; + KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | dev_addr; + const unsigned token = tu_edpt_number(ep_addr) | + ((dir_in ? TOK_PID_IN: TOK_PID_OUT) << USB_TOKEN_TOKENPID_SHIFT); + const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); + NVIC_DisableIRQ(USB0_IRQn); + do { + while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ; + KHCI->TOKEN = token; + } while (--num_pkts); + if (ie) NVIC_EnableIRQ(USB0_IRQn); + return true; +} + +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) +{ + if (!tu_edpt_number(ep_addr)) return true; + pipe_state_t *p = find_pipe(dev_addr, ep_addr); + if (!p) return false; + p->data = 0; /* Reset data toggle */ + return true; +} + +/*--------------------------------------------------------------------+ + * ISR + *--------------------------------------------------------------------+*/ +void hcd_int_handler(uint8_t rhport) +{ + uint32_t is = KHCI->ISTAT; + uint32_t msk = KHCI->INTEN; + + /* clear disabled interrupts */ + KHCI->ISTAT = is & ~msk; + is &= msk; + + if (is & USB_ISTAT_ATTACH_MASK) { + process_attach(rhport); + } + if (is & USB_ISTAT_STALL_MASK) { + KHCI->ISTAT = USB_ISTAT_STALL_MASK; + } + if (is & USB_ISTAT_TOKDNE_MASK) { + process_tokdne(rhport); + } +} + +#endif From cdab869472e18d393d43769c8a087ff1e69deb02 Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Wed, 29 Dec 2021 21:45:32 +0900 Subject: [PATCH 2/7] Fix handling control transfer --- src/portable/nxp/khci/hcd_khci.c | 135 ++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 45 deletions(-) diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c index 877c4813..937d69f9 100644 --- a/src/portable/nxp/khci/hcd_khci.c +++ b/src/portable/nxp/khci/hcd_khci.c @@ -88,10 +88,9 @@ 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; + uint32_t pipenum:16; + uint32_t odd : 1; + uint32_t : 0; }; }; uint16_t length; @@ -125,6 +124,7 @@ typedef struct }; endpoint_state_t endpoint[2]; pipe_state_t pipe[HCD_MAX_XFER]; + bool need_reset; /* The device has not been reset after connection. */ } hcd_data_t; //--------------------------------------------------------------------+ @@ -135,31 +135,34 @@ CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd; TU_VERIFY_STATIC( sizeof(_hcd.bdt) == 512, "size is not correct" ); -static pipe_state_t *find_pipe(uint8_t dev_addr, uint8_t ep_addr) +int find_pipe(uint8_t dev_addr, uint8_t ep_addr) { /* Find the target pipe */ - pipe_state_t *p = _hcd.pipe; - pipe_state_t *end = p + HCD_MAX_XFER; - for (;p != end; ++p) { - if ((p->dev_addr == dev_addr) && (p->ep_addr != ep_addr)) - return p; + int num; + if (0 == tu_edpt_number(ep_addr)) ep_addr = 0; + for (num = 0; num < HCD_MAX_XFER; ++num) { + pipe_state_t *p = &_hcd.pipe[num]; + if ((p->dev_addr == dev_addr) && (p->ep_addr == ep_addr)) + return num; } - return NULL; + return -1; } -static int prepare_packets(uint8_t rhport, uint_fast8_t dir_in, uint8_t* buffer, uint_fast16_t total_bytes) +static int prepare_packets(int pipenum, + uint_fast8_t dir_in, uint8_t* buffer, + uint_fast16_t total_bytes) { - (void)rhport; const unsigned dir_tx = dir_in ? 0 : 1; endpoint_state_t *ep = &_hcd.endpoint[dir_tx]; buffer_descriptor_t *bd = &_hcd.bdt[0][dir_tx][ep->odd]; TU_ASSERT(0 == bd->own, 0); + ep->pipenum = pipenum; ep->length = total_bytes; ep->remaining = total_bytes; int num_pkts = 0; /* The number of prepared packets */ - const unsigned mps = ep->max_packet_size; + const unsigned mps = _hcd.pipe[pipenum].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, @@ -174,6 +177,7 @@ static int prepare_packets(uint8_t rhport, uint_fast8_t dir_in, uint8_t* buffer, __DSB(); bd->own = 1; /* This bit must be set last */ ++num_pkts; + // TU_LOG1("BD pipe=%d %d %lx %x\n", pipenum, num_pkts, bd->head, (uintptr_t)bd->addr); return num_pkts; } @@ -182,12 +186,13 @@ 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 */ - uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT); TU_ASSERT(0 == epnum,); 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; + // TU_LOG1("TOKDNE %x\n", s); + buffer_descriptor_t *bd = (buffer_descriptor_t *)&_hcd.bda[s]; endpoint_state_t *ep = &_hcd.endpoint[s >> 3]; @@ -204,19 +209,24 @@ static void process_tokdne(uint8_t rhport) const unsigned bc = bd->bc; const unsigned remaining = ep->remaining - bc; + pipe_state_t *pipe = &_hcd.pipe[ep->pipenum]; + + // TU_LOG1(" pid %x bc %x remaining %d\n", pid, bc, remaining); if ((TOK_PID_DATA0 == pid) || (TOK_PID_DATA1 == pid) || (TOK_PID_ACK == pid)) { /* Go on the next packet transfer */ - if (remaining && bc == ep->max_packet_size) { + if (remaining && bc == pipe->max_packet_size) { ep->remaining = remaining; - const int next_remaining = remaining - ep->max_packet_size; + const int next_remaining = remaining - pipe->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; + bd->addr += pipe->max_packet_size * 2; + bd->bc = next_remaining > pipe->max_packet_size ? pipe->max_packet_size: next_remaining; __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 */ + } else if (dir_in) { + KHCI->TOKEN = KHCI->TOKEN; } return; } @@ -236,7 +246,7 @@ static void process_tokdne(uint8_t rhport) result = XFER_RESULT_FAILED; break; } - hcd_event_xfer_complete(KHCI->ADDR & USB_ADDR_ADDR_MASK, + hcd_event_xfer_complete(pipe->dev_addr, tu_edpt_addr(KHCI->TOKEN & USB_TOKEN_TOKENENDPT_MASK, dir_in), length - remaining, result, true); } @@ -252,6 +262,21 @@ static void process_attach(uint8_t rhport) hcd_event_device_attach(rhport, true); } +static void process_bus_reset(uint8_t rhport) +{ + KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; + KHCI->CTL &= ~USB_CTL_USBENSOFEN_MASK; + KHCI->ADDR = 0; + KHCI->ENDPOINT[0].ENDPT = 0; + + hcd_event_device_remove(rhport, true); + + buffer_descriptor_t *bd = _hcd.bdt[0][0]; + for (unsigned i = 0; i < 2; ++i, ++bd) { + bd->head = 0; + } +} + /*------------------------------------------------------------------*/ /* Host API *------------------------------------------------------------------*/ @@ -273,14 +298,6 @@ bool hcd_init(uint8_t rhport) for (unsigned i = 0; i < 16; ++i) { KHCI->ENDPOINT[i].ENDPT = 0; } - const endpoint_state_t ep0 = { - .max_packet_size = CFG_TUD_ENDPOINT0_SIZE, - .odd = 0, - .length = 0, - .remaining = 0, - }; - _hcd.endpoint[0] = ep0; - _hcd.endpoint[1] = ep0; KHCI->CTL &= ~USB_CTL_ODDRST_MASK; KHCI->SOFTHLD = 74; /* for 64-byte packets */ @@ -288,7 +305,8 @@ bool hcd_init(uint8_t rhport) KHCI->USBCTRL = USB_USBCTRL_PDE_MASK; NVIC_ClearPendingIRQ(USB0_IRQn); - KHCI->INTEN = USB_INTEN_ATTACHEN_MASK; + KHCI->INTEN = USB_INTEN_ATTACHEN_MASK | USB_INTEN_TOKDNEEN_MASK | + USB_INTEN_USBRSTEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK; return true; } @@ -307,9 +325,12 @@ void hcd_int_disable(uint8_t rhport) uint32_t hcd_frame_number(uint8_t rhport) { (void)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; - return frmnum; + return frmnum; } /*--------------------------------------------------------------------+ @@ -318,6 +339,8 @@ 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) + return true; return false; } @@ -330,6 +353,7 @@ void hcd_port_reset(uint8_t rhport) while (cnt--) __NOP(); KHCI->CTL &= ~USB_CTL_RESET_MASK; KHCI->CTL |= USB_CTL_USBENSOFEN_MASK; + _hcd.need_reset = false; } tusb_speed_t hcd_port_speed_get(uint8_t rhport) @@ -347,8 +371,8 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)rhport; - pipe_state_t *p = _hcd.pipe; - pipe_state_t *end = p + HCD_MAX_XFER; + pipe_state_t *p = &_hcd.pipe[0]; + pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER]; for (;p != end; ++p) { if (p->dev_addr == dev_addr) tu_memclr(p, sizeof(*p)); @@ -361,10 +385,14 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { (void)rhport; + // TU_LOG1("SETUP %u\n", dev_addr); const unsigned rx_odd = _hcd.endpoint[0].odd; const unsigned tx_odd = _hcd.endpoint[1].odd; TU_ASSERT(0 == _hcd.bdt[0][0][tx_odd].own); + int num = find_pipe(dev_addr, 0); + if (num < 0) return false; + const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); NVIC_DisableIRQ(USB0_IRQn); @@ -377,26 +405,31 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet KHCI->ENDPOINT[0].ENDPT = hostwohub | USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; bool ret = false; - if (prepare_packets(rhport, TUSB_DIR_OUT, (void*)(uintptr_t)setup_packet, 8)) { + if (prepare_packets(num, TUSB_DIR_OUT, (void*)(uintptr_t)setup_packet, 8)) { 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); ret = true; } if (ie) NVIC_EnableIRQ(USB0_IRQn); + return ret; } bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { (void)rhport; + uint8_t const ep_addr = ep_desc->bEndpointAddress; + // TU_LOG1("O %u %x\n", dev_addr, ep_addr); /* Find a free pipe */ - pipe_state_t *p = _hcd.pipe; - pipe_state_t *end = p + HCD_MAX_XFER; - for (;p < end && p->dev_addr; ++p) ; - if (p == end) return false; + pipe_state_t *p = &_hcd.pipe[0]; + if (dev_addr || ep_addr) { + pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER]; + for (++p; p < end && (p->dev_addr || p->ep_addr); ++p) ; + if (p == end) return false; + } p->dev_addr = dev_addr; - p->ep_addr = ep_desc->bEndpointAddress; + p->ep_addr = ep_addr; p->max_packet_size = ep_desc->wMaxPacketSize; p->xfer = ep_desc->bmAttributes.xfer; p->data = 0; @@ -406,15 +439,18 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { (void)rhport; + // TU_LOG1("X %u %x %d\n", dev_addr, ep_addr, buflen); const unsigned dir_in = tu_edpt_dir(ep_addr); const unsigned odd = _hcd.endpoint[dir_in ^ 1].odd; buffer_descriptor_t *bd = _hcd.bdt[0][dir_in ^ 1]; TU_ASSERT(0 == bd[odd].own); + int num = find_pipe(dev_addr, ep_addr); + if (num < 0) return false; + unsigned flags = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; if (tu_edpt_number(ep_addr)) { - pipe_state_t *p = find_pipe(dev_addr, ep_addr); - if (!p) return false; + pipe_state_t *p = &_hcd.pipe[num]; bd[odd ].data = p->data; bd[odd ^ 1].data = p->data ^ 1; bd[odd ^ 1].own = 0; @@ -425,7 +461,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * } unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; KHCI->ENDPOINT[0].ENDPT = hostwohub | flags; - int num_pkts = prepare_packets(rhport, dir_in, buffer, buflen); + int num_pkts = prepare_packets(num, dir_in, buffer, buflen); if (!num_pkts) return false; KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | dev_addr; const unsigned token = tu_edpt_number(ep_addr) | @@ -435,7 +471,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * do { while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ; KHCI->TOKEN = token; - } while (--num_pkts); + } while (--num_pkts && !dir_in); if (ie) NVIC_EnableIRQ(USB0_IRQn); return true; } @@ -443,8 +479,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) { if (!tu_edpt_number(ep_addr)) return true; - pipe_state_t *p = find_pipe(dev_addr, ep_addr); - if (!p) return false; + int num = find_pipe(dev_addr, ep_addr); + if (num < 0) return false; + pipe_state_t *p = &_hcd.pipe[num]; p->data = 0; /* Reset data toggle */ return true; } @@ -457,13 +494,21 @@ void hcd_int_handler(uint8_t rhport) uint32_t is = KHCI->ISTAT; uint32_t msk = KHCI->INTEN; + // TU_LOG1("S %lx\n", is); + /* clear disabled interrupts */ - KHCI->ISTAT = is & ~msk; + KHCI->ISTAT = is & ~msk & ~USB_ISTAT_TOKDNE_MASK; is &= msk; if (is & USB_ISTAT_ATTACH_MASK) { + KHCI->INTEN = (msk & ~USB_INTEN_ATTACHEN_MASK) | USB_INTEN_USBRSTEN_MASK; + _hcd.need_reset = true; process_attach(rhport); } + if (is & USB_ISTAT_USBRST_MASK) { + KHCI->INTEN = (msk & ~USB_INTEN_USBRSTEN_MASK) | USB_INTEN_ATTACHEN_MASK; + process_bus_reset(rhport); + } if (is & USB_ISTAT_STALL_MASK) { KHCI->ISTAT = USB_ISTAT_STALL_MASK; } From cc06a3585e9f499bd2412f4f88e5d785e8e1a23f Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Mon, 10 Jan 2022 23:50:32 +0900 Subject: [PATCH 3/7] Add handling for NAK response --- src/portable/nxp/khci/hcd_khci.c | 357 ++++++++++++++++++++----------- 1 file changed, 230 insertions(+), 127 deletions(-) diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c index 937d69f9..ddc6e999 100644 --- a/src/portable/nxp/khci/hcd_khci.c +++ b/src/portable/nxp/khci/hcd_khci.c @@ -93,12 +93,11 @@ typedef struct TU_ATTR_PACKED uint32_t : 0; }; }; + uint8_t *buffer; uint16_t length; uint16_t remaining; } endpoint_state_t; -TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" ); - typedef struct TU_ATTR_PACKED { uint8_t dev_addr; @@ -112,19 +111,24 @@ typedef struct TU_ATTR_PACKED uint8_t : 0; }; }; + uint8_t *buffer; + uint16_t length; + uint16_t remaining; } pipe_state_t; typedef struct { union { - /* [#EP][OUT,IN][EVEN,ODD] */ - buffer_descriptor_t bdt[16][2][2]; - uint16_t bda[512]; + /* [OUT,IN][EVEN,ODD] */ + buffer_descriptor_t bdt[2][2]; + uint16_t bda[2*2]; }; endpoint_state_t endpoint[2]; - pipe_state_t pipe[HCD_MAX_XFER]; - bool need_reset; /* The device has not been reset after connection. */ + pipe_state_t pipe[HCD_MAX_XFER * 2]; + uint32_t in_progress; /* Bitmap. Each bit indicates that a transfer of the corresponding pipe is in progress */ + uint32_t pending; /* Bitmap. Each bit indicates that a transfer of the corresponding pipe will be resume the next frame */ + bool need_reset; /* The device has not been reset after connection. */ } hcd_data_t; //--------------------------------------------------------------------+ @@ -132,15 +136,13 @@ typedef struct //--------------------------------------------------------------------+ // BDT(Buffer Descriptor Table) must be 256-byte aligned CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd; - -TU_VERIFY_STATIC( sizeof(_hcd.bdt) == 512, "size is not correct" ); +//CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024]; int find_pipe(uint8_t dev_addr, uint8_t ep_addr) { /* Find the target pipe */ int num; - if (0 == tu_edpt_number(ep_addr)) ep_addr = 0; - for (num = 0; num < HCD_MAX_XFER; ++num) { + for (num = 0; num < HCD_MAX_XFER * 2; ++num) { pipe_state_t *p = &_hcd.pipe[num]; if ((p->dev_addr == dev_addr) && (p->ep_addr == ep_addr)) return num; @@ -148,37 +150,129 @@ int find_pipe(uint8_t dev_addr, uint8_t ep_addr) return -1; } -static int prepare_packets(int pipenum, - uint_fast8_t dir_in, uint8_t* buffer, - uint_fast16_t total_bytes) +static int prepare_packets(int pipenum) { - const unsigned dir_tx = dir_in ? 0 : 1; - endpoint_state_t *ep = &_hcd.endpoint[dir_tx]; - buffer_descriptor_t *bd = &_hcd.bdt[0][dir_tx][ep->odd]; - TU_ASSERT(0 == bd->own, 0); + pipe_state_t *pipe = &_hcd.pipe[pipenum]; + unsigned const dir_tx = tu_edpt_dir(pipe->ep_addr) ? 0 : 1; + 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); - ep->pipenum = pipenum; - ep->length = total_bytes; - ep->remaining = total_bytes; + // TU_LOG1(" %p dir %d odd %d data %d\n", &bd[odd], dir_tx, odd, pipe->data); - int num_pkts = 0; /* The number of prepared packets */ - const unsigned mps = _hcd.pipe[pipenum].max_packet_size; - if (total_bytes > mps) { - buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1; + ep->pipenum = pipenum; + + bd[odd ].data = pipe->data; + bd[odd ^ 1].data = pipe->data ^ 1; + bd[odd ^ 1].own = 0; + /* reset values for a next transfer */ + + int num_tokens = 0; /* The number of prepared packets */ + unsigned const mps = pipe->max_packet_size; + unsigned const rem = pipe->remaining; + if (rem > mps) { /* 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; - ++num_pkts; + bd[odd ^ 1].bc = rem >= 2 * mps ? mps: rem - mps; + bd[odd ^ 1].addr = pipe->buffer + mps; + bd[odd ^ 1].own = 1; + if (dir_tx) ++num_tokens; } - bd->bc = total_bytes >= mps ? mps: total_bytes; - bd->addr = buffer; + bd[odd].bc = rem >= mps ? mps: rem; + bd[odd].addr = pipe->buffer; __DSB(); - bd->own = 1; /* This bit must be set last */ - ++num_pkts; - // TU_LOG1("BD pipe=%d %d %lx %x\n", pipenum, num_pkts, bd->head, (uintptr_t)bd->addr); - return num_pkts; + bd[odd].own = 1; /* This bit must be set last */ + ++num_tokens; + return num_tokens; +} + +static int select_next_pipenum(int pipenum) +{ + unsigned wip = _hcd.in_progress & ~_hcd.pending; + if (!wip) return -1; + unsigned msk = TU_GENMASK(31, pipenum); + int next = __builtin_ctz(wip & msk); + if (next) return next; + msk = TU_GENMASK(pipenum, 0); + next = __builtin_ctz(wip & msk); + return next; +} + +/* When transfer is completed, return true. */ +static bool continue_transfer(int pipenum, buffer_descriptor_t *bd) +{ + pipe_state_t *pipe = &_hcd.pipe[pipenum]; + unsigned const bc = bd->bc; + unsigned const rem = pipe->remaining - bc; + + pipe->remaining = rem; + if (rem && bc == pipe->max_packet_size) { + int const next_rem = rem - pipe->max_packet_size; + if (next_rem > 0) { + /* Prepare to the after next transfer */ + bd->addr += pipe->max_packet_size * 2; + 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 */ + } else if (TUSB_DIR_IN == tu_edpt_dir(pipe->ep_addr)) { /* IN */ + while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ; + KHCI->TOKEN = KHCI->TOKEN; + } + return true; + } + pipe->data = bd->data ^ 1; + return false; +} + +static bool resume_transfer(int pipenum) +{ + int num_tokens = prepare_packets(pipenum); + TU_ASSERT(0 <= num_tokens); + + const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); + NVIC_DisableIRQ(USB0_IRQn); + pipe_state_t *pipe = &_hcd.pipe[pipenum]; + + unsigned flags = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; + flags |= USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; + switch (pipe->xfer) { + case TUSB_XFER_CONTROL: + flags |= USB_ENDPT_EPHSHK_MASK; + break; + case TUSB_XFER_ISOCHRONOUS: + flags |= USB_ENDPT_EPCTLDIS_MASK | USB_ENDPT_RETRYDIS_MASK; + break; + default: + flags |= USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPCTLDIS_MASK | USB_ENDPT_RETRYDIS_MASK; + break; + } + // TU_LOG1(" resume pipenum %d flags %x\n", pipenum, flags); + + KHCI->ENDPOINT[0].ENDPT = flags; + KHCI->ADDR = (KHCI->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 (--num_tokens); + if (ie) NVIC_EnableIRQ(USB0_IRQn); + return true; +} + +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; + if (TUSB_XFER_INTERRUPT == pipe->xfer) { + _hcd.pending |= TU_BIT(pipenum); + KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK; + } } static void process_tokdne(uint8_t rhport) @@ -186,13 +280,9 @@ 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 */ - uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT); - TU_ASSERT(0 == epnum,); 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; - // TU_LOG1("TOKDNE %x\n", s); - buffer_descriptor_t *bd = (buffer_descriptor_t *)&_hcd.bda[s]; endpoint_state_t *ep = &_hcd.endpoint[s >> 3]; @@ -207,48 +297,40 @@ static void process_tokdne(uint8_t rhport) /* Update the odd variable to prepare for the next transfer */ ep->odd = odd ^ 1; - const unsigned bc = bd->bc; - const unsigned remaining = ep->remaining - bc; - pipe_state_t *pipe = &_hcd.pipe[ep->pipenum]; + int pipenum = ep->pipenum; + int next_pipenum; + // TU_LOG1("TOKDNE %x PID %x pipe %d\n", s, pid, pipenum); - // TU_LOG1(" pid %x bc %x remaining %d\n", pid, bc, remaining); - if ((TOK_PID_DATA0 == pid) || (TOK_PID_DATA1 == pid) || (TOK_PID_ACK == pid)) { - /* Go on the next packet transfer */ - if (remaining && bc == pipe->max_packet_size) { - ep->remaining = remaining; - const int next_remaining = remaining - pipe->max_packet_size; - if (next_remaining > 0) { - /* Prepare to the after next transfer */ - bd->addr += pipe->max_packet_size * 2; - bd->bc = next_remaining > pipe->max_packet_size ? pipe->max_packet_size: next_remaining; - __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 */ - } else if (dir_in) { - KHCI->TOKEN = KHCI->TOKEN; - } - return; - } - } - const unsigned length = ep->length; xfer_result_t result; switch (pid) { default: + if (continue_transfer(pipenum, bd)) + return; result = XFER_RESULT_SUCCESS; break; - case TOK_PID_STALL: - result = XFER_RESULT_STALLED; - break; case TOK_PID_NAK: case TOK_PID_ERR: + suspend_transfer(pipenum, bd); + next_pipenum = select_next_pipenum(pipenum); + if (0 <= next_pipenum) + resume_transfer(next_pipenum); + return; + case TOK_PID_STALL: + result = XFER_RESULT_STALLED; + break; case TOK_PID_BUSTO: result = XFER_RESULT_FAILED; break; } + _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), - length - remaining, result, true); + pipe->length - pipe->remaining, + result, true); + next_pipenum = select_next_pipenum(pipenum); + if (0 <= next_pipenum) + resume_transfer(next_pipenum); } static void process_attach(uint8_t rhport) @@ -264,6 +346,7 @@ static void process_attach(uint8_t rhport) 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; @@ -271,7 +354,9 @@ static void process_bus_reset(uint8_t rhport) hcd_event_device_remove(rhport, true); - buffer_descriptor_t *bd = _hcd.bdt[0][0]; + _hcd.in_progress = 0; + _hcd.pending = 0; + buffer_descriptor_t *bd = &_hcd.bdt[0][0]; for (unsigned i = 0; i < 2; ++i, ++bd) { bd->head = 0; } @@ -301,12 +386,15 @@ bool hcd_init(uint8_t rhport) KHCI->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; NVIC_ClearPendingIRQ(USB0_IRQn); KHCI->INTEN = USB_INTEN_ATTACHEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_USBRSTEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK; + KHCI->ERREN = 0xff; + return true; } @@ -371,12 +459,15 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)rhport; + const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); + NVIC_DisableIRQ(USB0_IRQn); pipe_state_t *p = &_hcd.pipe[0]; - pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER]; + pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER * 2]; for (;p != end; ++p) { if (p->dev_addr == dev_addr) tu_memclr(p, sizeof(*p)); } + if (ie) NVIC_EnableIRQ(USB0_IRQn); } //--------------------------------------------------------------------+ @@ -386,34 +477,30 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet { (void)rhport; // TU_LOG1("SETUP %u\n", dev_addr); - const unsigned rx_odd = _hcd.endpoint[0].odd; - const unsigned tx_odd = _hcd.endpoint[1].odd; - TU_ASSERT(0 == _hcd.bdt[0][0][tx_odd].own); + TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(0))); - int num = find_pipe(dev_addr, 0); - if (num < 0) return false; + int pipenum = find_pipe(dev_addr, 0); + if (pipenum < 0) return false; - const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); - NVIC_DisableIRQ(USB0_IRQn); + pipe_state_t *pipe = &_hcd.pipe[pipenum]; + pipe[0].data = 0; + pipe[0].buffer = (uint8_t*)(uintptr_t)setup_packet; + pipe[0].length = 8; + pipe[0].remaining = 8; + pipe[1].data = 1; - _hcd.bdt[0][0][rx_odd ].data = 1; - _hcd.bdt[0][0][rx_odd ^ 1].data = 0; - _hcd.bdt[0][1][tx_odd ].data = 0; - _hcd.bdt[0][1][tx_odd ^ 1].data = 1; + if (1 != prepare_packets(pipenum)) + return false; + + _hcd.in_progress |= TU_BIT(pipenum); unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; KHCI->ENDPOINT[0].ENDPT = hostwohub | USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; - bool ret = false; - if (prepare_packets(num, TUSB_DIR_OUT, (void*)(uintptr_t)setup_packet, 8)) { - 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); - ret = true; - } - if (ie) NVIC_EnableIRQ(USB0_IRQn); - - return ret; + 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); + return true; } bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) @@ -423,9 +510,10 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const // TU_LOG1("O %u %x\n", dev_addr, ep_addr); /* Find a free pipe */ pipe_state_t *p = &_hcd.pipe[0]; + pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER * 2]; if (dev_addr || ep_addr) { - pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER]; - for (++p; p < end && (p->dev_addr || p->ep_addr); ++p) ; + p += 2; + for (; p < end && (p->dev_addr || p->ep_addr); ++p) ; if (p == end) return false; } p->dev_addr = dev_addr; @@ -433,45 +521,39 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const p->max_packet_size = ep_desc->wMaxPacketSize; p->xfer = ep_desc->bmAttributes.xfer; p->data = 0; + if (!ep_addr) { + /* Open one more pipe for Control IN transfer */ + TU_ASSERT(TUSB_XFER_CONTROL == p->xfer); + pipe_state_t *q = p + 1; + TU_ASSERT(!q->dev_addr && !q->ep_addr); + q->dev_addr = dev_addr; + q->ep_addr = tu_edpt_addr(0, TUSB_DIR_IN); + q->max_packet_size = ep_desc->wMaxPacketSize; + q->xfer = ep_desc->bmAttributes.xfer; + q->data = 1; + } return true; } bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { (void)rhport; - // TU_LOG1("X %u %x %d\n", dev_addr, ep_addr, buflen); - const unsigned dir_in = tu_edpt_dir(ep_addr); - const unsigned odd = _hcd.endpoint[dir_in ^ 1].odd; - buffer_descriptor_t *bd = _hcd.bdt[0][dir_in ^ 1]; - TU_ASSERT(0 == bd[odd].own); + // TU_LOG1("X %u %x %x %d\n", dev_addr, ep_addr, (uintptr_t)buffer, buflen); - int num = find_pipe(dev_addr, ep_addr); - if (num < 0) return false; + int pipenum = find_pipe(dev_addr, ep_addr); + TU_ASSERT(0 <= pipenum); - unsigned flags = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; - if (tu_edpt_number(ep_addr)) { - pipe_state_t *p = &_hcd.pipe[num]; - bd[odd ].data = p->data; - bd[odd ^ 1].data = p->data ^ 1; - bd[odd ^ 1].own = 0; - flags |= USB_ENDPT_EPCTLDIS_MASK; - /* Disable retry for a interrupt transfer. */ - if (TUSB_XFER_INTERRUPT == p->xfer) - flags |= USB_ENDPT_RETRYDIS_MASK; - } - unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK; - KHCI->ENDPOINT[0].ENDPT = hostwohub | flags; - int num_pkts = prepare_packets(num, dir_in, buffer, buflen); - if (!num_pkts) return false; - KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | dev_addr; - const unsigned token = tu_edpt_number(ep_addr) | - ((dir_in ? TOK_PID_IN: TOK_PID_OUT) << USB_TOKEN_TOKENPID_SHIFT); - const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn); + TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(pipenum))); + unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn); + unsigned const wip = _hcd.in_progress; NVIC_DisableIRQ(USB0_IRQn); - do { - while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ; - KHCI->TOKEN = token; - } while (--num_pkts && !dir_in); + pipe_state_t *pipe = &_hcd.pipe[pipenum]; + pipe->buffer = buffer; + pipe->length = buflen; + pipe->remaining = buflen; + _hcd.in_progress = wip | TU_BIT(pipenum); + if (0 == (wip & ~_hcd.pending)) + resume_transfer(pipenum); if (ie) NVIC_EnableIRQ(USB0_IRQn); return true; } @@ -497,21 +579,42 @@ void hcd_int_handler(uint8_t rhport) // TU_LOG1("S %lx\n", is); /* clear disabled interrupts */ - KHCI->ISTAT = is & ~msk & ~USB_ISTAT_TOKDNE_MASK; + KHCI->ISTAT = (is & ~msk & ~USB_ISTAT_TOKDNE_MASK) | USB_ISTAT_SOFTOK_MASK; is &= msk; + if (is & USB_ISTAT_ERROR_MASK) { + unsigned err = KHCI->ERRSTAT; + if (err) { + TU_LOG1(" ERR %x\n", err); + KHCI->ERRSTAT = err; + } else { + KHCI->INTEN &= ~USB_ISTAT_ERROR_MASK; + } + } + + if (is & USB_ISTAT_USBRST_MASK) { + KHCI->INTEN = (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; _hcd.need_reset = true; process_attach(rhport); - } - if (is & USB_ISTAT_USBRST_MASK) { - KHCI->INTEN = (msk & ~USB_INTEN_USBRSTEN_MASK) | USB_INTEN_ATTACHEN_MASK; - process_bus_reset(rhport); + return; } if (is & USB_ISTAT_STALL_MASK) { KHCI->ISTAT = USB_ISTAT_STALL_MASK; } + if (is & USB_ISTAT_SOFTOK_MASK) { + msk &= ~USB_ISTAT_SOFTOK_MASK; + KHCI->INTEN = msk; + if (_hcd.pending) { + int pipenum = __builtin_ctz(_hcd.pending); + _hcd.pending = 0; + resume_transfer(pipenum); + } + } if (is & USB_ISTAT_TOKDNE_MASK) { process_tokdne(rhport); } From 60a0be82da93b653d55b9a62b0d80cd58f47a869 Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Thu, 27 Jan 2022 22:25:36 +0900 Subject: [PATCH 4/7] Change hcd_edpt_xfer to send at the next SOF --- src/portable/nxp/khci/hcd_khci.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c index ddc6e999..133d65a6 100644 --- a/src/portable/nxp/khci/hcd_khci.c +++ b/src/portable/nxp/khci/hcd_khci.c @@ -268,7 +268,7 @@ 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; + pipe->data = bd->data ^ 1; if (TUSB_XFER_INTERRUPT == pipe->xfer) { _hcd.pending |= TU_BIT(pipenum); KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK; @@ -309,7 +309,6 @@ static void process_tokdne(uint8_t rhport) result = XFER_RESULT_SUCCESS; break; case TOK_PID_NAK: - case TOK_PID_ERR: suspend_transfer(pipenum, bd); next_pipenum = select_next_pipenum(pipenum); if (0 <= next_pipenum) @@ -318,6 +317,7 @@ static void process_tokdne(uint8_t rhport) case TOK_PID_STALL: result = XFER_RESULT_STALLED; break; + case TOK_PID_ERR: /* mismatch toggle bit */ case TOK_PID_BUSTO: result = XFER_RESULT_FAILED; break; @@ -535,6 +535,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const return true; } +/* The address of buffer must be aligned to 4 byte boundary. And it must be at least 4 bytes long. + * DMA writes data in 4 byte unit */ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { (void)rhport; @@ -545,15 +547,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(pipenum))); unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn); - unsigned const wip = _hcd.in_progress; NVIC_DisableIRQ(USB0_IRQn); pipe_state_t *pipe = &_hcd.pipe[pipenum]; pipe->buffer = buffer; pipe->length = buflen; pipe->remaining = buflen; - _hcd.in_progress = wip | TU_BIT(pipenum); - if (0 == (wip & ~_hcd.pending)) - resume_transfer(pipenum); + _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); return true; } From d7cbfaaa0a83570c5094ae84262ee9ce83856aae Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Thu, 27 Jan 2022 22:26:51 +0900 Subject: [PATCH 5/7] Add hcd into source list --- hw/bsp/frdm_kl25z/board.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/bsp/frdm_kl25z/board.mk b/hw/bsp/frdm_kl25z/board.mk index 15bd50ed..3585b8b0 100644 --- a/hw/bsp/frdm_kl25z/board.mk +++ b/hw/bsp/frdm_kl25z/board.mk @@ -23,6 +23,7 @@ LD_FILE = $(MCU_DIR)/gcc/MKL25Z128xxx4_flash.ld SRC_C += \ src/portable/nxp/khci/dcd_khci.c \ + src/portable/nxp/khci/hcd_khci.c \ $(MCU_DIR)/system_MKL25Z4.c \ $(MCU_DIR)/project_template/clock_config.c \ $(MCU_DIR)/drivers/fsl_clock.c \ From 03d2e32dc15adf9ee28772dfaecc52fac940f0d9 Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Thu, 27 Jan 2022 23:38:19 +0900 Subject: [PATCH 6/7] Fix handling for pending transfers --- src/portable/nxp/khci/hcd_khci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c index 133d65a6..92ec04d4 100644 --- a/src/portable/nxp/khci/hcd_khci.c +++ b/src/portable/nxp/khci/hcd_khci.c @@ -269,7 +269,8 @@ 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; - if (TUSB_XFER_INTERRUPT == pipe->xfer) { + if ((TUSB_XFER_INTERRUPT == pipe->xfer) || + (TUSB_XFER_BULK == pipe->xfer)) { _hcd.pending |= TU_BIT(pipenum); KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK; } @@ -613,7 +614,8 @@ void hcd_int_handler(uint8_t rhport) if (_hcd.pending) { int pipenum = __builtin_ctz(_hcd.pending); _hcd.pending = 0; - resume_transfer(pipenum); + if (!(is & USB_ISTAT_TOKDNE_MASK)) + resume_transfer(pipenum); } } if (is & USB_ISTAT_TOKDNE_MASK) { From ce7a8fed36dc61ef488f1c75a9c810681c7dcde9 Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Fri, 28 Jan 2022 01:18:44 +0900 Subject: [PATCH 7/7] Add a partial support mark to KL25 --- docs/reference/supported.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/supported.rst b/docs/reference/supported.rst index 44564373..7c5402d9 100644 --- a/docs/reference/supported.rst +++ b/docs/reference/supported.rst @@ -42,7 +42,7 @@ Supported MCUs +--------------+---------+-------------+--------+------+-----------+-------------------+--------------+ | NXP | iMXRT | RT10xx | ✔ | ✔ | ✔ | ci_hs | | | +---------+-------------+--------+------+-----------+-------------------+--------------+ -| | Kinetis | KL25 | ✔ | | ✖ | | | +| | Kinetis | KL25 | ✔ | ⚠ | ✖ | | | | | +-------------+--------+------+-----------+-------------------+--------------+ | | | K32L2 | ✔ | | ✖ | | | | +---------+-------------+--------+------+-----------+-------------------+--------------+