Merge pull request #1213 from kkitayam/add_hcd_for_msp_exp432e401y

Add a HCD driver for MSP-EXP432E401Y
This commit is contained in:
Ha Thach 2021-12-07 19:24:41 +07:00 committed by GitHub
commit c157837878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 939 additions and 20 deletions

View File

@ -9,7 +9,7 @@ INC += \
EXAMPLE_SOURCE += \
src/hid_app.c \
src/main.c
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
# TODO: suppress warning caused by host stack

View File

@ -138,6 +138,8 @@ void hid_app_task(void)
// therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
(void)desc_report;
(void)desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);

View File

@ -33,7 +33,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
}
//--------------------------------------------------------------------+
@ -42,6 +47,7 @@ void USB0_IRQHandler(void)
void board_init(void)
{
unsigned bits;
/* Turn off power domains that unused peripherals belong to */
SYSCTL->PCCAN = 0u;
#ifdef __MCU_HAS_LCD0__
@ -80,20 +86,23 @@ void board_init(void)
#endif
/* USR_LED1 ON1 */
SYSCTL->RCGCGPIO |= TU_BIT(CLK_LED);
while (!(SYSCTL->PRGPIO & TU_BIT(CLK_LED))) ;
bits = TU_BIT(CLK_LED);
SYSCTL->RCGCGPIO |= bits;
while (bits != (SYSCTL->RCGCGPIO & bits)) ;
GPIO_LED->DIR = TU_BIT(GPIO_LED_PIN);
GPIO_LED->DEN = TU_BIT(GPIO_LED_PIN);
/* USR_SW1 PJ0 */
SYSCTL->RCGCGPIO |= TU_BIT(CLK_BUTTON);
while (!(SYSCTL->PRGPIO & TU_BIT(CLK_BUTTON))) ;
bits = TU_BIT(CLK_BUTTON);
SYSCTL->RCGCGPIO |= bits;
while (bits != (SYSCTL->RCGCGPIO & bits)) ;
GPIO_BUTTON->PUR = TU_BIT(GPIO_BUTTON_PIN);
GPIO_BUTTON->DEN = TU_BIT(GPIO_BUTTON_PIN);
/* UART PA0,1 */
SYSCTL->RCGCGPIO |= 1u << 0;
while (!(SYSCTL->PRGPIO & (1u << 0))) ;
bits = TU_BIT(0);
SYSCTL->RCGCGPIO |= bits;
while (bits != (SYSCTL->RCGCGPIO & bits)) ;
GPIOA->AFSEL = 3u;
GPIOA->PCTL = 0x11u;
GPIOA->DEN = 3u;
@ -107,11 +116,22 @@ void board_init(void)
UART0->CC = UART_CC_CS_PIOSC; /* Set the baud clock to PIOSC */
UART0->CTL = UART_CTL_RXE | UART_CTL_TXE | UART_CTL_UARTEN;
/* USB PB1(VBUS) PL6,7(DP,DM) */
SYSCTL->RCGCGPIO |= (1u << 1) | (1u << 10);
while (((1u << 1) | (1u << 10)) != (SYSCTL->PRGPIO & ((1u << 1) | (1u << 10)))) ;
GPIOB->AMSEL = 1u << 1;
GPIOL->AMSEL = (1u << 6) | (1u << 7);
/* USB PB0(ID) PB1(VBUS) PL6,7(DP,DM) */
bits = TU_BIT(1) | TU_BIT(10);
SYSCTL->RCGCGPIO |= bits;
while (bits != (SYSCTL->RCGCGPIO & bits)) ;
GPIOB->AMSEL = TU_BIT(0) | TU_BIT(1);
GPIOL->AMSEL = TU_BIT(6) | TU_BIT(7);
#if TUSB_OPT_HOST_ENABLED
/* USB PD6(EPEN) */
bits = TU_BIT(3);
SYSCTL->RCGCGPIO |= bits;
while (bits != (SYSCTL->RCGCGPIO & bits)) ;
GPIOD->AFSEL = TU_BIT(6);
GPIOD->PCTL = 0x05000000u;
GPIOD->DEN = TU_BIT(6);
#endif
SYSCTL->RCGCUSB = 1u; /* Open the clock gate for SYSCLK */
while (!(SYSCTL->PRUSB & (1u << 0))) ;
@ -124,6 +144,13 @@ void board_init(void)
USB0->CC = USB_CC_CLKEN | (3u << USB_CC_CLKDIV_S); /* 60MHz = 240MHz / 4 */
__DMB(); /* Wait for completion of opening of the clock gate */
#if TUSB_OPT_HOST_ENABLED
USB0->GPCS = USB_GPCS_DEVMOD_OTG;
USB0->EPC = USB_EPC_EPENDE | USB_EPC_EPEN_HIGH;
#endif
#if TUSB_OPT_DEVICE_ENABLED
USB0->GPCS = USB_GPCS_DEVMOD_DEVVBUS;
#endif
}
//--------------------------------------------------------------------+

View File

@ -23,6 +23,7 @@ MCU_DIR = hw/mcu/ti/msp432e4
SRC_C += \
src/portable/mentor/musb/dcd_musb.c \
src/portable/mentor/musb/hcd_musb.c \
$(MCU_DIR)/Source/system_msp432e401y.c
INC += \

View File

@ -7,7 +7,13 @@
//--------------------------------------------------------------------+
void USB0_Handler(void)
{
#if TUSB_OPT_HOST_ENABLED
tuh_int_handler(0);
#endif
#if TUSB_OPT_DEVICE_ENABLED
tud_int_handler(0);
#endif
}
//--------------------------------------------------------------------+
@ -23,10 +29,12 @@ static void board_uart_init (void)
GPIOA->PCTL |= (1 << 0) | (1 << 4); // Configure the GPIOPCTL register to select UART0 in PA0 and PA1
GPIOA->DEN |= (1 << 0) | (1 << 1); // Enable the digital functionality in PA0 and PA1
/** BAUDRATE = 9600 bits per second, refer manual for calculation **/
// BAUDRATE = 115200, with SystemCoreClock = 50 Mhz refer manual for calculation
// - BRDI = SystemCoreClock / (16* baud)
// - BRDF = int(fraction*64 + 0.5)
UART0->CTL &= ~(1 << 0); // Disable UART0 by clearing UARTEN bit in the UARTCTL register
UART0->IBRD = 325; // Write the integer portion of the BRD to the UARTIRD register
UART0->FBRD = 33; // Write the fractional portion of the BRD to the UARTFBRD registerer
UART0->IBRD = 27; // Write the integer portion of the BRD to the UARTIRD register
UART0->FBRD = 8; // Write the fractional portion of the BRD to the UARTFBRD registerer
UART0->LCRH = (0x3 << 5); // 8-bit, no parity, 1 stop bit
UART0->CC = 0x0; // Configure the UART clock source as system clock
@ -40,8 +48,7 @@ static void initialize_board_led (GPIOA_Type *port, uint8_t PinMsk, uint8_t dirm
SYSCTL->RCGCGPIO |= (1 << 5);
/* Let the clock stabilize */
while ( !((SYSCTL->PRGPIO) & (1 << 5)) )
;
while ( !((SYSCTL->PRGPIO) & (1 << 5)) ) {}
/* Port Digital Enable */
port->DEN |= PinMsk;
@ -60,9 +67,13 @@ static void board_switch_init (void)
static void WriteGPIOPin (GPIOA_Type *port, uint8_t PinMsk, bool state)
{
if ( state )
{
port->DATA |= PinMsk;
}
else
{
port->DATA &= ~(PinMsk);
}
}
static uint32_t ReadGPIOPin (GPIOA_Type *port, uint8_t pinMsk)
@ -99,8 +110,7 @@ void board_init (void)
SYSCTL->RCGCGPIO |= (1u << 3);
/* Let the clock stabilize */
while ( !(SYSCTL->PRGPIO & (1u << 3)) )
;
while ( !(SYSCTL->PRGPIO & (1u << 3)) ) {}
/* USB IOs to Analog Mode */
GPIOD->AFSEL &= ~((1u << 4) | (1u << 5));
@ -119,6 +129,7 @@ void board_init (void)
/* Initialize board UART */
board_uart_init();
TU_LOG1_INT(SystemCoreClock);
}
void board_led_write (bool state)

View File

@ -28,6 +28,7 @@ INC += \
SRC_C += \
src/portable/mentor/musb/dcd_musb.c \
src/portable/mentor/musb/hcd_musb.c \
$(MCU_DIR)/Source/system_TM4C123.c \
$(MCU_DIR)/Source/GCC/tm4c123_startup.c

View File

@ -26,7 +26,8 @@
#include "tusb_option.h"
#if TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
#if TUSB_OPT_DEVICE_ENABLED && \
TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
#if __GNUC__ > 8 && defined(__ARM_FEATURE_UNALIGNED)
/* GCC warns that an address may be unaligned, even though

View File

@ -0,0 +1,876 @@
/*
* 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 && \
TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
#if __GNUC__ > 8 && defined(__ARM_FEATURE_UNALIGNED)
/* GCC warns that an address may be unaligned, even though
* the target CPU has the capability for unaligned memory access. */
_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
#endif
#include "host/hcd.h"
#if TU_CHECK_MCU(OPT_MCU_MSP432E4)
#include "musb_msp432e.h"
#elif TU_CHECK_MCU(OPT_MCU_TM4C123, OPT_MCU_TM4C129)
#include "musb_tm4c.h"
// HACK generalize later
#include "musb_type.h"
#define FIFO0_WORD FIFO0
#else
#error "Unsupported MCUs"
#endif
#ifndef HCD_ATTR_ENDPOINT_MAX
# define HCD_ATTR_ENDPOINT_MAX 8
#endif
/*------------------------------------------------------------------
* MACRO TYPEDEF CONSTANT ENUM DECLARATION
*------------------------------------------------------------------*/
#define REQUEST_TYPE_INVALID (0xFFu)
typedef struct {
uint_fast16_t beg; /* offset of including first element */
uint_fast16_t end; /* offset of excluding the last element */
} free_block_t;
typedef struct TU_ATTR_PACKED {
uint8_t TXFUNCADDR;
uint8_t RESERVED0;
uint8_t TXHUBADDR;
uint8_t TXHUBPORT;
uint8_t RXFUNCADDR;
uint8_t RESERVED1;
uint8_t RXHUBADDR;
uint8_t RXHUBPORT;
} hw_addr_t;
typedef struct TU_ATTR_PACKED {
uint16_t TXMAXP;
uint8_t TXCSRL;
uint8_t TXCSRH;
uint16_t RXMAXP;
uint8_t RXCSRL;
uint8_t RXCSRH;
uint16_t RXCOUNT;
uint8_t TXTYPE;
uint8_t TXINTERVAL;
uint8_t RXTYPE;
uint8_t RXINTERVAL;
uint16_t RESERVED;
} hw_endpoint_t;
typedef union {
uint8_t u8;
uint16_t u16;
uint32_t u32;
} hw_fifo_t;
typedef struct TU_ATTR_PACKED
{
void *buf; /* the start address of a transfer data buffer */
uint16_t length; /* the number of bytes in the buffer */
uint16_t remaining; /* the number of bytes remaining in the buffer */
} pipe_state_t;
typedef struct TU_ATTR_PACKED
{
uint8_t dev;
uint8_t ep;
} pipe_addr_t;
typedef struct
{
bool need_reset; /* The device has not been reset after connection. */
uint8_t bmRequestType;
uint8_t ctl_mps[7]; /* EP0 max packet size for each device */
pipe_state_t pipe0;
pipe_state_t pipe[7][2]; /* pipe[pipe number - 1][direction 0:RX 1:TX] */
pipe_addr_t addr[7][2]; /* addr[pipe number - 1][direction 0:RX 1:TX] */
} hcd_data_t;
/*------------------------------------------------------------------
* INTERNAL OBJECT & FUNCTION DECLARATION
*------------------------------------------------------------------*/
static hcd_data_t _hcd;
static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
{
free_block_t *cur = beg;
for (; cur < end && ((addr < cur->beg) || (cur->end <= addr)); ++cur) ;
return cur;
}
static inline int update_free_block_list(free_block_t *blks, unsigned num, uint_fast16_t addr, uint_fast16_t size)
{
free_block_t *p = find_containing_block(blks, blks + num, addr);
TU_ASSERT(p != blks + num, -2);
if (p->beg == addr) {
/* Shrink block */
p->beg = addr + size;
if (p->beg != p->end) return 0;
/* remove block */
free_block_t *end = blks + num;
while (p + 1 < end) {
*p = *(p + 1);
++p;
}
return -1;
} else {
/* Split into 2 blocks */
free_block_t tmp = {
.beg = addr + size,
.end = p->end
};
p->end = addr;
if (p->beg == p->end) {
if (tmp.beg != tmp.end) {
*p = tmp;
return 0;
}
/* remove block */
free_block_t *end = blks + num;
while (p + 1 < end) {
*p = *(p + 1);
++p;
}
return -1;
}
if (tmp.beg == tmp.end) return 0;
blks[num] = tmp;
return 1;
}
}
static inline unsigned free_block_size(free_block_t const *blk)
{
return blk->end - blk->beg;
}
static unsigned find_free_memory(uint_fast16_t size_in_log2_minus3)
{
free_block_t free_blocks[2 * (HCD_ATTR_ENDPOINT_MAX - 1)];
unsigned num_blocks = 1;
/* Initialize free memory block list */
free_blocks[0].beg = 64 / 8;
free_blocks[0].end = (4 << 10) / 8; /* 4KiB / 8 bytes */
for (int i = 1; i < HCD_ATTR_ENDPOINT_MAX; ++i) {
uint_fast16_t addr;
int num;
USB0->EPIDX = i;
addr = USB0->TXFIFOADD;
if (addr) {
unsigned sz = USB0->TXFIFOSZ;
unsigned sft = (sz & USB_TXFIFOSZ_SIZE_M) + ((sz & USB_TXFIFOSZ_DPB) ? 1: 0);
num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
TU_ASSERT(-2 < num, 0);
num_blocks += num;
}
addr = USB0->RXFIFOADD;
if (addr) {
unsigned sz = USB0->RXFIFOSZ;
unsigned sft = (sz & USB_RXFIFOSZ_SIZE_M) + ((sz & USB_RXFIFOSZ_DPB) ? 1: 0);
num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
TU_ASSERT(-2 < num, 0);
num_blocks += num;
}
}
/* Find the best fit memory block */
uint_fast16_t size_in_8byte_unit = 1 << size_in_log2_minus3;
free_block_t const *min = NULL;
uint_fast16_t min_sz = 0xFFFFu;
free_block_t const *end = &free_blocks[num_blocks];
for (free_block_t const *cur = &free_blocks[0]; cur < end; ++cur) {
uint_fast16_t sz = free_block_size(cur);
if (sz < size_in_8byte_unit) continue;
if (size_in_8byte_unit == sz) return cur->beg;
if (sz < min_sz) min = cur;
}
TU_ASSERT(min, 0);
return min->beg;
}
static inline volatile hw_endpoint_t* edpt_regs(unsigned epnum_minus1)
{
volatile hw_endpoint_t *regs = (volatile hw_endpoint_t*)((uintptr_t)&USB0->TXMAXP1);
return regs + epnum_minus1;
}
static unsigned find_pipe(uint_fast8_t dev_addr, uint_fast8_t ep_addr)
{
unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
pipe_addr_t const *p = &_hcd.addr[0][dir_tx];
for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i, p += 2) {
if ((dev_addr == p->dev) && (ep_addr == p->ep))
return i + 1;
}
return 0;
}
static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
{
volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
uintptr_t addr = (uintptr_t)buf;
while (len >= 4) {
reg->u32 = *(uint32_t const *)addr;
addr += 4;
len -= 4;
}
if (len >= 2) {
reg->u16 = *(uint16_t const *)addr;
addr += 2;
len -= 2;
}
if (len) {
reg->u8 = *(uint8_t const *)addr;
}
}
static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
{
volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
uintptr_t addr = (uintptr_t)buf;
while (len >= 4) {
*(uint32_t *)addr = reg->u32;
addr += 4;
len -= 4;
}
if (len >= 2) {
*(uint16_t *)addr = reg->u16;
addr += 2;
len -= 2;
}
if (len) {
*(uint8_t *)addr = reg->u8;
}
}
static bool edpt0_xfer_out(void)
{
pipe_state_t *pipe = &_hcd.pipe0;
unsigned const rem = pipe->remaining;
if (!rem) {
pipe->buf = NULL;
return true;
}
unsigned const dev_addr = USB0->TXFUNCADDR0;
unsigned const mps = _hcd.ctl_mps[dev_addr];
unsigned const len = TU_MIN(rem, mps);
void *buf = pipe->buf;
if (len) {
pipe_write_packet(buf, &USB0->FIFO0_WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
pipe->remaining = rem - len;
USB0->CSRL0 = USB_CSRL0_TXRDY;
return false;
}
static bool edpt0_xfer_in(void)
{
pipe_state_t *pipe = &_hcd.pipe0;
unsigned const rem = pipe->remaining;
unsigned const dev_addr = USB0->TXFUNCADDR0;
unsigned const mps = _hcd.ctl_mps[dev_addr];
unsigned const vld = USB0->COUNT0;
unsigned const len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
pipe_read_packet(buf, &USB0->FIFO0_WORD, len);
pipe->buf = (uint8_t*)buf + len;
}
pipe->remaining = rem - len;
if ((len < mps) || (rem == len)) {
pipe->buf = NULL;
return true;
}
USB0->CSRL0 = USB_CSRL0_REQPKT;
return false;
}
static bool edpt0_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
{
(void)rhport;
unsigned const req = _hcd.bmRequestType;
TU_ASSERT(req != REQUEST_TYPE_INVALID);
TU_ASSERT(dev_addr < sizeof(_hcd.ctl_mps));
USB0->TXFUNCADDR0 = dev_addr;
const unsigned dir_in = tu_edpt_dir(ep_addr);
if (tu_edpt_dir(req) == dir_in) { /* DATA stage */
TU_ASSERT(buffer);
_hcd.pipe0.buf = buffer;
_hcd.pipe0.length = buflen;
_hcd.pipe0.remaining = buflen;
if (dir_in)
USB0->CSRL0 = USB_CSRL0_REQPKT;
else
edpt0_xfer_out();
} else { /* STATUS stage */
_hcd.pipe0.buf = NULL;
_hcd.pipe0.length = 0;
_hcd.pipe0.remaining = 0;
USB0->CSRL0 = USB_CSRL0_STATUS | (dir_in ? USB_CSRL0_REQPKT: USB_CSRL0_TXRDY);
}
return true;
}
static bool pipe_xfer_out(uint_fast8_t pipenum)
{
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][1];
unsigned const rem = pipe->remaining;
if (!rem) {
pipe->buf = NULL;
return true;
}
hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
unsigned const mps = regs->TXMAXP;
unsigned const len = TU_MIN(rem, mps);
void *buf = pipe->buf;
if (len) {
pipe_write_packet(buf, &USB0->FIFO0_WORD + pipenum, len);
pipe->buf = (uint8_t*)buf + len;
}
pipe->remaining = rem - len;
regs->TXCSRL = USB_TXCSRL1_TXRDY;
return false;
}
static bool pipe_xfer_in(uint_fast8_t pipenum)
{
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][0];
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
TU_ASSERT(regs->RXCSRL & USB_RXCSRL1_RXRDY);
const unsigned mps = regs->RXMAXP;
const unsigned rem = pipe->remaining;
const unsigned vld = regs->RXCOUNT;
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
if (len) {
pipe_read_packet(buf, &USB0->FIFO0_WORD + pipenum, len);
pipe->buf = buf + len;
pipe->remaining = rem - len;
}
if ((len < mps) || (rem == len)) {
pipe->buf = NULL;
return NULL != buf;
}
regs->RXCSRL = USB_RXCSRL1_REQPKT;
return false;
}
static bool edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
{
(void)rhport;
unsigned const pipenum = find_pipe(dev_addr, ep_addr);
unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][dir_tx];
pipe->buf = buffer;
pipe->length = buflen;
pipe->remaining = buflen;
if (dir_tx) {
pipe_xfer_out(pipenum);
} else {
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
regs->RXCSRL = USB_RXCSRL1_REQPKT;
}
return true;
}
static void process_ep0(uint8_t rhport)
{
(void)rhport;
uint_fast8_t csrl = USB0->CSRL0;
// TU_LOG1(" EP0 CSRL = %x\n", csrl);
unsigned const dev_addr = USB0->TXFUNCADDR0;
unsigned const req = _hcd.bmRequestType;
if (csrl & (USB_CSRL0_ERROR | USB_CSRL0_NAKTO | USB_CSRL0_STALLED)) {
/* No response / NAK timed out / Stall received */
if (csrl & (USB_CSRL0_RXRDY | USB_CSRL0_TXRDY))
USB0->CSRH0 = USB_CSRH0_FLUSH;
USB0->CSRL0 = 0;
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
uint8_t result = (csrl & USB_CSRL0_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
if (REQUEST_TYPE_INVALID == req) { /* SETUP */
uint8_t const ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
hcd_event_xfer_complete(dev_addr, ep_addr,
_hcd.pipe0.length - _hcd.pipe0.remaining,
result, true);
} else if (csrl & USB_CSRL0_STATUS) { /* STATUS */
uint8_t const ep_addr = tu_edpt_dir(req) ?
tu_edpt_addr(0, TUSB_DIR_OUT): tu_edpt_addr(0, TUSB_DIR_IN);
hcd_event_xfer_complete(dev_addr, ep_addr,
_hcd.pipe0.length - _hcd.pipe0.remaining,
result, true);
} else { /* DATA */
uint8_t const ep_addr = tu_edpt_dir(req) ?
tu_edpt_addr(0, TUSB_DIR_IN): tu_edpt_addr(0, TUSB_DIR_OUT);
hcd_event_xfer_complete(dev_addr, ep_addr,
_hcd.pipe0.length - _hcd.pipe0.remaining,
result, true);
}
return;
}
if (csrl & USB_CSRL0_STATUS) {
/* STATUS IN */
TU_ASSERT(USB_CSRL0_RXRDY == (csrl & USB_CSRL0_RXRDY),);
TU_ASSERT(0 == USB0->COUNT0,);
USB0->CSRH0 = USB_CSRH0_FLUSH;
USB0->CSRL0 = 0;
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
hcd_event_xfer_complete(dev_addr, tu_edpt_addr(0, TUSB_DIR_IN),
0, XFER_RESULT_SUCCESS, true);
return;
}
if (csrl & USB_CSRL0_RXRDY) {
/* DATA IN */
TU_ASSERT(REQUEST_TYPE_INVALID != req,);
TU_ASSERT(_hcd.pipe0.buf,);
if (edpt0_xfer_in()) {
hcd_event_xfer_complete(dev_addr, tu_edpt_addr(0, TUSB_DIR_IN),
_hcd.pipe0.length - _hcd.pipe0.remaining,
XFER_RESULT_SUCCESS, true);
}
return;
}
/* When CSRL0 is zero, it means that completion of sending a any length packet. */
if (!_hcd.pipe0.buf) {
/* STATUS OUT */
TU_ASSERT(REQUEST_TYPE_INVALID != req,);
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
/* EP address is the reverse direction of DATA stage */
uint8_t const ep_addr = tu_edpt_dir(req) ?
tu_edpt_addr(0, TUSB_DIR_OUT): tu_edpt_addr(0, TUSB_DIR_IN);
hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, true);
return;
}
if (REQUEST_TYPE_INVALID == req) {
/* SETUP */
_hcd.bmRequestType = *(uint8_t*)_hcd.pipe0.buf;
_hcd.pipe0.buf = NULL;
hcd_event_xfer_complete(dev_addr, tu_edpt_addr(0, TUSB_DIR_OUT),
8, XFER_RESULT_SUCCESS, true);
return;
}
/* DATA OUT */
if (edpt0_xfer_out()) {
hcd_event_xfer_complete(dev_addr, tu_edpt_addr(0, TUSB_DIR_OUT),
_hcd.pipe0.length - _hcd.pipe0.remaining,
XFER_RESULT_SUCCESS, true);
}
}
static void process_pipe_tx(uint8_t rhport, uint_fast8_t pipenum)
{
(void)rhport;
bool completed;
uint8_t result;
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
unsigned const csrl = regs->TXCSRL;
// TU_LOG1(" TXCSRL%d = %x\n", pipenum, csrl);
if (csrl & (USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) {
if (csrl & USB_TXCSRL1_TXRDY)
regs->TXCSRL = (csrl & ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) | USB_TXCSRL1_FLUSH;
else
regs->TXCSRL = csrl & ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR);
completed = true;
result = (csrl & USB_TXCSRL1_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
} else {
completed = pipe_xfer_out(pipenum);
result = XFER_RESULT_SUCCESS;
}
if (completed) {
pipe_addr_t *addr = &_hcd.addr[pipenum - 1][1];
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][1];
hcd_event_xfer_complete(addr->dev, addr->ep,
pipe->length - pipe->remaining,
result, true);
}
}
static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
{
(void)rhport;
bool completed;
uint8_t result;
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
unsigned const csrl = regs->RXCSRL;
// TU_LOG1(" RXCSRL%d = %x\n", pipenum, csrl);
if (csrl & (USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) {
if (csrl & USB_RXCSRL1_RXRDY)
regs->RXCSRL = (csrl & ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) | USB_RXCSRL1_FLUSH;
else
regs->RXCSRL = csrl & ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR);
completed = true;
result = (csrl & USB_RXCSRL1_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
} else {
completed = pipe_xfer_in(pipenum);
result = XFER_RESULT_SUCCESS;
}
if (completed) {
pipe_addr_t *addr = &_hcd.addr[pipenum - 1][0];
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][0];
hcd_event_xfer_complete(addr->dev, addr->ep,
pipe->length - pipe->remaining,
result, true);
}
}
/*------------------------------------------------------------------
* Host API
*------------------------------------------------------------------*/
bool hcd_init(uint8_t rhport)
{
(void)rhport;
NVIC_ClearPendingIRQ(USB0_IRQn);
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
USB0->DEVCTL |= USB_DEVCTL_SESSION;
USB0->IE = USB_IE_DISCON | USB_IE_CONN | USB_IE_BABBLE | USB_IE_RESUME;
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;
/* 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);
return USB0->FRAME;
}
//--------------------------------------------------------------------+
// Port API
//--------------------------------------------------------------------+
bool hcd_port_connect_status(uint8_t rhport)
{
(void)rhport;
unsigned devctl = USB0->DEVCTL;
if (!(devctl & USB_DEVCTL_HOST)) return false;
if (devctl & (USB_DEVCTL_LSDEV | USB_DEVCTL_FSDEV)) return true;
return false;
}
void hcd_port_reset(uint8_t rhport)
{
(void)rhport;
USB0->POWER |= USB_POWER_HSENAB | USB_POWER_RESET;
unsigned cnt = SystemCoreClock / 1000 * 20;
while (cnt--) __NOP();
USB0->POWER &= ~USB_POWER_RESET;
_hcd.need_reset = false;
}
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
(void)rhport;
unsigned devctl = USB0->DEVCTL;
if (devctl & USB_DEVCTL_LSDEV) return TUSB_SPEED_LOW;
if (!(devctl & USB_DEVCTL_FSDEV)) return TUSB_SPEED_INVALID;
if (USB0->POWER & USB_POWER_HSMODE) return TUSB_SPEED_HIGH;
return TUSB_SPEED_FULL;
}
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
if (sizeof(_hcd.ctl_mps) <= dev_addr) return;
unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
NVIC_DisableIRQ(USB0_IRQn);
_hcd.ctl_mps[dev_addr] = 0;
if (!dev_addr) return;
pipe_addr_t *p = &_hcd.addr[0][0];
for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i) {
for (unsigned j = 0; j < 2; ++j, ++p) {
if (dev_addr != p->dev) continue;
hw_addr_t volatile *fadr = (hw_addr_t volatile*)&USB0->TXFUNCADDR0 + i + 1;
hw_endpoint_t volatile *regs = edpt_regs(i);
USB0->EPIDX = i + 1;
if (j) {
USB0->TXIE &= ~TU_BIT(i + 1);
if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
else
regs->TXCSRL = USB_TXCSRL1_CLRDT;
regs->TXMAXP = 0;
regs->TXTYPE = 0;
regs->TXINTERVAL = 0;
fadr->TXFUNCADDR = 0;
fadr->TXHUBADDR = 0;
fadr->TXHUBPORT = 0;
USB0->TXFIFOADD = 0;
USB0->TXFIFOSZ = 0;
} else {
USB0->RXIE &= ~TU_BIT(i + 1);
if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
else
regs->RXCSRL = USB_RXCSRL1_CLRDT;
regs->RXMAXP = 0;
regs->RXTYPE = 0;
regs->RXINTERVAL = 0;
fadr->RXFUNCADDR = 0;
fadr->RXHUBADDR = 0;
fadr->RXHUBPORT = 0;
USB0->RXFIFOADD = 0;
USB0->RXFIFOSZ = 0;
}
p->dev = 0;
p->ep = 0;
pipe_state_t *pipe = &_hcd.pipe[i][j];
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
}
}
if (ie) NVIC_EnableIRQ(USB0_IRQn);
}
//--------------------------------------------------------------------+
// Endpoints API
//--------------------------------------------------------------------+
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
(void)rhport;
pipe_write_packet((void*)(uintptr_t)setup_packet, &USB0->FIFO0_WORD, 8);
_hcd.pipe0.buf = (void*)(uintptr_t)setup_packet;
_hcd.pipe0.length = 8;
_hcd.pipe0.remaining = 0;
hcd_devtree_info_t devtree;
hcd_devtree_get_info(dev_addr, &devtree);
switch (devtree.speed) {
default: return false;
case TUSB_SPEED_LOW: USB0->TYPE0 = USB_TYPE0_SPEED_LOW; break;
case TUSB_SPEED_FULL: USB0->TYPE0 = USB_TYPE0_SPEED_FULL; break;
case TUSB_SPEED_HIGH: USB0->TYPE0 = USB_TYPE0_SPEED_HIGH; break;
}
USB0->TXHUBADDR0 = devtree.hub_addr;
USB0->TXHUBPORT0 = devtree.hub_port;
USB0->TXFUNCADDR0 = dev_addr;
USB0->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_SETUP;
return true;
}
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
(void)rhport;
if (sizeof(_hcd.ctl_mps) <= dev_addr) return false;
unsigned const ep_addr = ep_desc->bEndpointAddress;
unsigned const epn = tu_edpt_number(ep_addr);
if (0 == epn) {
_hcd.ctl_mps[dev_addr] = ep_desc->wMaxPacketSize;
return true;
}
unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
/* Find a free pipe */
unsigned pipenum = 0;
pipe_addr_t *p = &_hcd.addr[0][dir_tx];
for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i, p += 2) {
if (0 == p->ep) {
p->dev = dev_addr;
p->ep = ep_addr;
pipenum = i + 1;
break;
}
}
if (!pipenum) return false;
unsigned const xfer = ep_desc->bmAttributes.xfer;
unsigned const mps = tu_edpt_packet_size(ep_desc);
pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][dir_tx];
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
uint8_t pipe_type = 0;
hcd_devtree_info_t devtree;
hcd_devtree_get_info(dev_addr, &devtree);
switch (devtree.speed) {
default: return false;
case TUSB_SPEED_LOW: pipe_type |= USB_TXTYPE1_SPEED_LOW; break;
case TUSB_SPEED_FULL: pipe_type |= USB_TXTYPE1_SPEED_FULL; break;
case TUSB_SPEED_HIGH: pipe_type |= USB_TXTYPE1_SPEED_HIGH; break;
}
switch (xfer) {
default: return false;
case TUSB_XFER_BULK: pipe_type |= USB_TXTYPE1_PROTO_BULK; break;
case TUSB_XFER_INTERRUPT: pipe_type |= USB_TXTYPE1_PROTO_INT; break;
case TUSB_XFER_ISOCHRONOUS: pipe_type |= USB_TXTYPE1_PROTO_ISOC; break;
}
hw_addr_t volatile *fadr = (hw_addr_t volatile*)&USB0->TXFUNCADDR0 + pipenum;
hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
if (dir_tx) {
fadr->TXFUNCADDR = dev_addr;
fadr->TXHUBADDR = devtree.hub_addr;
fadr->TXHUBPORT = devtree.hub_port;
regs->TXMAXP = mps;
regs->TXTYPE = pipe_type | epn;
regs->TXINTERVAL = ep_desc->bInterval;
if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
else
regs->TXCSRL = USB_TXCSRL1_CLRDT;
USB0->TXIE |= TU_BIT(pipenum);
} else {
fadr->RXFUNCADDR = dev_addr;
fadr->RXHUBADDR = devtree.hub_addr;
fadr->RXHUBPORT = devtree.hub_port;
regs->RXMAXP = mps;
regs->RXTYPE = pipe_type | epn;
regs->RXINTERVAL = ep_desc->bInterval;
if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
else
regs->RXCSRL = USB_RXCSRL1_CLRDT;
USB0->RXIE |= TU_BIT(pipenum);
}
/* Setup FIFO */
int size_in_log2_minus3 = 28 - TU_MIN(28, __CLZ((uint32_t)mps));
if ((8u << size_in_log2_minus3) < mps) ++size_in_log2_minus3;
unsigned addr = find_free_memory(size_in_log2_minus3);
TU_ASSERT(addr);
USB0->EPIDX = pipenum;
if (dir_tx) {
USB0->TXFIFOADD = addr;
USB0->TXFIFOSZ = size_in_log2_minus3;
} else {
USB0->RXFIFOADD = addr;
USB0->RXFIFOSZ = size_in_log2_minus3;
}
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;
bool ret = false;
if (0 == tu_edpt_number(ep_addr)) {
ret = edpt0_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
} else {
ret = edpt_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
}
return ret;
}
// clear stall, data toggle is also reset to DATA0
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
unsigned const pipenum = find_pipe(dev_addr, ep_addr);
if (!pipenum) return false;
hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
if (dir_tx)
regs->TXCSRL = USB_TXCSRL1_CLRDT;
else
regs->RXCSRL = USB_RXCSRL1_CLRDT;
return true;
}
/*-------------------------------------------------------------------
* ISR
*-------------------------------------------------------------------*/
void hcd_int_handler(uint8_t rhport)
{
uint_fast8_t is, txis, rxis;
is = USB0->IS; /* read and clear interrupt status */
txis = USB0->TXIS; /* read and clear interrupt status */
rxis = USB0->RXIS; /* read and clear interrupt status */
// TU_LOG1("D%2x T%2x R%2x\n", is, txis, rxis);
is &= USB0->IE; /* Clear disabled interrupts */
if (is & USB_IS_RESUME) {
}
if (is & USB_IS_CONN) {
_hcd.need_reset = true;
hcd_event_device_attach(rhport, true);
}
if (is & USB_IS_DISCON) {
hcd_event_device_remove(rhport, true);
}
if (is & USB_IS_BABBLE) {
}
txis &= USB0->TXIE; /* Clear disabled interrupts */
if (txis & USB_TXIE_EP0) {
process_ep0(rhport);
txis &= ~TU_BIT(0);
}
while (txis) {
unsigned const num = __builtin_ctz(txis);
process_pipe_tx(rhport, num);
txis &= ~TU_BIT(num);
}
rxis &= USB0->RXIE; /* Clear disabled interrupts */
while (rxis) {
unsigned const num = __builtin_ctz(rxis);
process_pipe_rx(rhport, num);
rxis &= ~TU_BIT(num);
}
}
#endif