diff options
Diffstat (limited to 'src')
22 files changed, 1763 insertions, 2361 deletions
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 4fc3dd1b5..83c9990d8 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -62,7 +62,7 @@ enum typedef struct {
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
-//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
+//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX)
// uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
//#endif
diff --git a/src/device/dcd.h b/src/device/dcd.h index db84df839..6b1356cbf 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -90,14 +90,14 @@ typedef struct ATTR_ALIGNED(4) TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
/*------------------------------------------------------------------*/
-/* Device API (Weak is optional)
+/* Device API
*------------------------------------------------------------------*/
-bool dcd_init (uint8_t rhport);
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr);
-void dcd_set_config (uint8_t rhport, uint8_t config_num);
+bool dcd_init (uint8_t rhport);
+void dcd_int_enable (uint8_t rhport);
+void dcd_int_disable(uint8_t rhport);
-void dcd_connect (uint8_t rhport) ATTR_WEAK;
-void dcd_disconnect (uint8_t rhport) ATTR_WEAK;
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
+void dcd_set_config (uint8_t rhport, uint8_t config_num);
/*------------------------------------------------------------------*/
/* Event Function
diff --git a/src/device/usbd.c b/src/device/usbd.c index 81fcfe959..ddb63cebb 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -190,23 +190,26 @@ bool tud_mounted(void) //--------------------------------------------------------------------+
tusb_error_t usbd_init (void)
{
+ // Init device queue & task
+ _usbd_q = osal_queue_create(&_usbd_qdef);
+ TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
+
+ osal_task_create(&_usbd_task_def);
+
+ // Init class drivers
+ for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) usbd_class_drivers[i].init();
+
+ // Init device controller driver
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
dcd_init(0);
+ dcd_int_enable(0);
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
dcd_init(1);
+ dcd_int_enable(1);
#endif
- //------------- Task init -------------//
- _usbd_q = osal_queue_create(&_usbd_qdef);
- TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
-
- osal_task_create(&_usbd_task_def);
-
- //------------- class init -------------//
- for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) usbd_class_drivers[i].init();
-
return TUSB_ERROR_NONE;
}
diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index dda75cee8..57c4c4d9d 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -72,6 +72,20 @@ static void bus_reset(void) { bool dcd_init (uint8_t rhport) { (void) rhport; + + // Reset to get in a clean state. + USB->DEVICE.CTRLA.bit.SWRST = true; + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} + + USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; + USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; + USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + + USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val; + USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val; + + // Configure registers USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers; USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS; USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; @@ -82,13 +96,16 @@ bool dcd_init (uint8_t rhport) return true; } -void dcd_connect (uint8_t rhport) +void dcd_int_enable(uint8_t rhport) { - + (void) rhport; + NVIC_EnableIRQ(USB_IRQn); } -void dcd_disconnect (uint8_t rhport) -{ +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USB_IRQn); } void dcd_set_address (uint8_t rhport, uint8_t dev_addr) diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index d0ad17f92..ba53d5598 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -73,6 +73,19 @@ bool dcd_init (uint8_t rhport) { (void) rhport; + // Reset to get in a clean state. + USB->DEVICE.CTRLA.bit.SWRST = true; + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} + + USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; + USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; + USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + + USB->DEVICE.QOSCTRL.bit.CQOS = 3; + USB->DEVICE.QOSCTRL.bit.DQOS = 3; + + // Configure registers USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers; USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS; USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; @@ -82,13 +95,22 @@ bool dcd_init (uint8_t rhport) return true; } -void dcd_connect (uint8_t rhport) +void dcd_int_enable(uint8_t rhport) { (void) rhport; + NVIC_EnableIRQ(USB_0_IRQn); + NVIC_EnableIRQ(USB_1_IRQn); + NVIC_EnableIRQ(USB_2_IRQn); + NVIC_EnableIRQ(USB_3_IRQn); } -void dcd_disconnect (uint8_t rhport) + +void dcd_int_disable(uint8_t rhport) { (void) rhport; + NVIC_DisableIRQ(USB_3_IRQn); + NVIC_DisableIRQ(USB_2_IRQn); + NVIC_DisableIRQ(USB_1_IRQn); + NVIC_DisableIRQ(USB_0_IRQn); } void dcd_set_address (uint8_t rhport, uint8_t dev_addr) diff --git a/src/portable/microchip/samd51/hal_samd51.c b/src/portable/microchip/samd51/hal_samd51.c deleted file mode 100644 index 49212b74c..000000000 --- a/src/portable/microchip/samd51/hal_samd51.c +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************/ -/*! - @file hal_nrf5x.c - @author hathach - - @section LICENSE - - Software License Agreement (BSD License) - - Copyright (c) 2018, hathach (tinyusb.org) - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - This file is part of the tinyusb stack. -*/ -/**************************************************************************/ - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51 - -#include "sam.h" - -#include "tusb_hal.h" - -/*------------------------------------------------------------------*/ -/* TUSB HAL - *------------------------------------------------------------------*/ -bool tusb_hal_init(void) -{ - // Reset to get in a clean state. - USB->DEVICE.CTRLA.bit.SWRST = true; - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} - - USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; - USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; - - USB->DEVICE.QOSCTRL.bit.CQOS = 3; - USB->DEVICE.QOSCTRL.bit.DQOS = 3; - - tusb_hal_int_enable(0); - return true; -} - -void tusb_hal_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USB_0_IRQn); - NVIC_EnableIRQ(USB_1_IRQn); - NVIC_EnableIRQ(USB_2_IRQn); - NVIC_EnableIRQ(USB_3_IRQn); -} - -void tusb_hal_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USB_3_IRQn); - NVIC_DisableIRQ(USB_2_IRQn); - NVIC_DisableIRQ(USB_1_IRQn); - NVIC_DisableIRQ(USB_0_IRQn); -} - -#endif diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index e1e53a83e..c7a4f413f 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -195,13 +195,16 @@ bool dcd_init (uint8_t rhport) return true; } -void dcd_connect (uint8_t rhport) +void dcd_int_enable(uint8_t rhport) { (void) rhport; + NVIC_EnableIRQ(USBD_IRQn); } -void dcd_disconnect (uint8_t rhport) + +void dcd_int_disable(uint8_t rhport) { (void) rhport; + NVIC_DisableIRQ(USBD_IRQn); } void dcd_set_address (uint8_t rhport, uint8_t dev_addr) diff --git a/src/portable/nordic/nrf5x/hal_nrf5x.c b/src/portable/nordic/nrf5x/hal_nrf5x.c index f40e904d7..4cbcd1788 100644 --- a/src/portable/nordic/nrf5x/hal_nrf5x.c +++ b/src/portable/nordic/nrf5x/hal_nrf5x.c @@ -128,27 +128,6 @@ static void hfclk_disable(void) nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP); } - -/*------------------------------------------------------------------*/ -/* TUSB HAL - *------------------------------------------------------------------*/ -bool tusb_hal_init(void) -{ - return true; -} - -void tusb_hal_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USBD_IRQn); -} - -void tusb_hal_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USBD_IRQn); -} - /*------------------------------------------------------------------*/ /* Controller Start up Sequence (USBD 51.4 specs) *------------------------------------------------------------------*/ diff --git a/src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.c b/src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.c new file mode 100644 index 000000000..69c2759de --- /dev/null +++ b/src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.c @@ -0,0 +1,392 @@ +/**************************************************************************/ +/*! + @file dcd_lpc11_13_15.c + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This file is part of the tinyusb stack. +*/ +/**************************************************************************/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX) + +#include "chip.h" +#include "device/dcd.h" +#include "dcd_lpc11_13_15.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// Number of endpoints +#define EP_COUNT 10 + +// only SRAM1 & USB RAM can be used for transfer +#define SRAM_REGION 0x20000000 + +/* Although device controller are the same. DMA of + * - LPC11u can only transfer up to nbytes = 64 + * - LPC13 can transfer nbytes = 1023 (maximum) + * - LPC15 can ??? + */ +enum { + DMA_NBYTES_MAX = (CFG_TUSB_MCU == OPT_MCU_LPC11UXX ? 64 : 1023) +}; + +enum { + INT_SOF_MASK = BIT_(30), + INT_DEVICE_STATUS_MASK = BIT_(31) +}; + +enum { + CMDSTAT_DEVICE_ADDR_MASK = BIT_(7 )-1, + CMDSTAT_DEVICE_ENABLE_MASK = BIT_(7 ), + CMDSTAT_SETUP_RECEIVED_MASK = BIT_(8 ), + CMDSTAT_DEVICE_CONNECT_MASK = BIT_(16), ///< reflect the softconnect only, does not reflect the actual attached state + CMDSTAT_DEVICE_SUSPEND_MASK = BIT_(17), + CMDSTAT_CONNECT_CHANGE_MASK = BIT_(24), + CMDSTAT_SUSPEND_CHANGE_MASK = BIT_(25), + CMDSTAT_RESET_CHANGE_MASK = BIT_(26), + CMDSTAT_VBUS_DEBOUNCED_MASK = BIT_(28), +}; + +typedef struct ATTR_PACKED +{ + // Bits 21:6 (aligned 64) used in conjunction with bit 31:22 of DATABUFSTART + volatile uint16_t buffer_offset; + + volatile uint16_t nbytes : 10 ; + uint16_t is_iso : 1 ; + uint16_t toggle_mode : 1 ; + uint16_t toggle_reset : 1 ; + uint16_t stall : 1 ; + uint16_t disable : 1 ; + volatile uint16_t active : 1 ; +}ep_cmd_sts_t; + +TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" ); + +typedef struct +{ + uint16_t total_bytes; + uint16_t xferred_bytes; + + uint16_t nbytes; +}xfer_dma_t; + +// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering. +// current_td is used to keep track of number of remaining & xferred bytes of the current request. +typedef struct +{ + // 256 byte aligned, 2 for double buffer (not used) + // Each cmd_sts can only transfer up to DMA_NBYTES_MAX bytes each + ep_cmd_sts_t ep[EP_COUNT][2]; + + xfer_dma_t dma[EP_COUNT]; + + ATTR_ALIGNED(64) uint8_t setup_packet[8]; +}dcd_data_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +// EP list must be 256-byte aligned +CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) static dcd_data_t _dcd; + +static inline uint16_t get_buf_offset(void const * buffer) +{ + uint32_t addr = (uint32_t) buffer; + TU_ASSERT( (addr & 0x3f) == 0, 0 ); + return ( (addr >> 6) & 0xFFFFUL ) ; +} + +static inline uint8_t ep_addr2id(uint8_t endpoint_addr) +{ + return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0); +} + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ +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_config(uint8_t rhport, uint8_t config_num) +{ + +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + + LPC_USB->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK; + LPC_USB->DEVCMDSTAT |= dev_addr; +} + +bool dcd_init(uint8_t rhport) +{ + (void) rhport; + + LPC_USB->EPLISTSTART = (uint32_t) _dcd.ep; + LPC_USB->DATABUFSTART = SRAM_REGION; + + LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt + LPC_USB->INTEN = INT_DEVICE_STATUS_MASK; + LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK | + CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK; + + NVIC_EnableIRQ(USB0_IRQn); + + return true; +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + if ( edpt_number(ep_addr) == 0 ) + { + // TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around + _dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 1; + } + else + { + uint8_t const ep_id = ep_addr2id(ep_addr); + _dcd.ep[ep_id][0].stall = 1; + } +} + +bool dcd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const ep_id = ep_addr2id(ep_addr); + return _dcd.ep[ep_id][0].stall; +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t edpt_addr) +{ + uint8_t const ep_id = ep_addr2id(edpt_addr); + + _dcd.ep[ep_id][0].stall = 0; + _dcd.ep[ep_id][0].toggle_reset = 1; + _dcd.ep[ep_id][0].toggle_mode = 0; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + // TODO not support ISO yet + if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return false; + + //------------- Prepare Queue Head -------------// + uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress); + + // Check if endpoint is available + TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable ); + + tu_memclr(_dcd.ep[ep_id], 2*sizeof(ep_cmd_sts_t)); + _dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS); + + // Enable EP interrupt + LPC_USB->INTEN |= BIT_(ep_id); + + return true; +} + +bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const ep_id = ep_addr2id(ep_addr); + return _dcd.ep[ep_id][0].active; +} + +static void prepare_ep_xfer(uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) +{ + uint16_t const nbytes = tu_min16(total_bytes, DMA_NBYTES_MAX); + + _dcd.dma[ep_id].nbytes = nbytes; + + _dcd.ep[ep_id][0].buffer_offset = buf_offset; + _dcd.ep[ep_id][0].nbytes = nbytes; + _dcd.ep[ep_id][0].active = 1; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) +{ + uint8_t const ep_id = ep_addr2id(ep_addr); + + tu_varclr(&_dcd.dma[ep_id]); + _dcd.dma[ep_id].total_bytes = total_bytes; + + prepare_ep_xfer(ep_id, get_buf_offset(buffer), total_bytes); + + return true; +} + +//--------------------------------------------------------------------+ +// IRQ +//--------------------------------------------------------------------+ +static void bus_reset(void) +{ + tu_memclr(&_dcd, sizeof(dcd_data_t)); + + // disable all non-control endpoints on bus reset + for(uint8_t ep_id = 2; ep_id < EP_COUNT; ep_id++) + { + _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1; + } + + _dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet); + + LPC_USB->EPINUSE = 0; + LPC_USB->EPBUFCFG = 0; + LPC_USB->EPSKIP = 0xFFFFFFFF; + + LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt + LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt + LPC_USB->INTEN = INT_DEVICE_STATUS_MASK | BIT_(0) | BIT_(1); // enable device status & control endpoints +} + +static void process_xfer_isr(uint32_t int_status) +{ + for(uint8_t ep_id = 0; ep_id < EP_COUNT; ep_id++ ) + { + if ( BIT_TEST_(int_status, ep_id) ) + { + ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0]; + xfer_dma_t* xfer_dma = &_dcd.dma[ep_id]; + + xfer_dma->xferred_bytes += xfer_dma->nbytes - ep_cs->nbytes; + + if ( (ep_cs->nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) ) + { + // There is more data to transfer + // buff_offset has been already increased by hw to correct value for next transfer + prepare_ep_xfer(ep_id, ep_cs->buffer_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes); + } + else + { + xfer_dma->total_bytes = xfer_dma->xferred_bytes; + + uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0); + + // TODO no way determine if the transfer is failed or not + dcd_event_xfer_complete(0, ep_addr, xfer_dma->xferred_bytes, XFER_RESULT_SUCCESS, true); + } + } + } +} + +void USB_IRQHandler(void) +{ + uint32_t const dev_cmd_stat = LPC_USB->DEVCMDSTAT; + + uint32_t int_status = LPC_USB->INTSTAT & LPC_USB->INTEN; + LPC_USB->INTSTAT = int_status; // Acknowledge handled interrupt + + if (int_status == 0) return; + + //------------- Device Status -------------// + if ( int_status & INT_DEVICE_STATUS_MASK ) + { + LPC_USB->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK; + if ( dev_cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset + { + bus_reset(); + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if (dev_cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK) + { + // device disconnect + if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK) + { + // debouncing as this can be set when device is powering + dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); + } + } + + // TODO support suspend & resume + if (dev_cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK) + { + if (dev_cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK) + { // suspend signal, bus idle for more than 3ms + // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. + if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK) + { + dcd_event_bus_signal(0, DCD_EVENT_SUSPENDED, true); + } + } + } +// else +// { // resume signal +// dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); +// } +// } + } + + // Setup Receive + if ( BIT_TEST_(int_status, 0) && (dev_cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) ) + { + // Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints + _dcd.ep[0][0].active = _dcd.ep[1][0].active = 0; + _dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 0; + + LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; + + dcd_event_setup_received(0, _dcd.setup_packet, true); + + // keep waiting for next setup + _dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet); + + // clear bit0 + int_status = BIT_CLR_(int_status, 0); + } + + // Endpoint transfer complete interrupt + process_xfer_isr(int_status); +} + +#endif + diff --git a/src/portable/nxp/lpc17xx/hal_mcu.h b/src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.h index be1e71671..92946e346 100644 --- a/src/portable/nxp/lpc17xx/hal_mcu.h +++ b/src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.h @@ -1,55 +1,51 @@ -/**************************************************************************/ -/*! - @file hal_lpc175x_6x.h - @author hathach (tinyusb.org) - - @section LICENSE - - Software License Agreement (BSD License) - - Copyright (c) 2013, hathach (tinyusb.org) - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - This file is part of the tinyusb stack. -*/ -/**************************************************************************/ - -#ifndef _TUSB_HAL_MCU_H_ -#define _TUSB_HAL_MCU_H_ - -#include "LPC17xx.h" -#include "lpc17xx_pinsel.h" - -#ifdef __cplusplus - extern "C" { -#endif - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_HAL_MCU_H_ */ - +/**************************************************************************/
+/*!
+ @file dcd_lpc11_13_15.h
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2013, hathach (tinyusb.org)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file is part of the tinyusb stack.
+*/
+/**************************************************************************/
+
+#ifndef _TUSB_DCD_LPC11_13_15_H_
+#define _TUSB_DCD_LPC11_13_15_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DCD_LPC11_13_15_H_ */
diff --git a/src/portable/nxp/lpc11xx_lpc13xx/dcd_lpc_11uxx_13uxx.c b/src/portable/nxp/lpc11xx_lpc13xx/dcd_lpc_11uxx_13uxx.c deleted file mode 100644 index 4e514d6e2..000000000 --- a/src/portable/nxp/lpc11xx_lpc13xx/dcd_lpc_11uxx_13uxx.c +++ /dev/null @@ -1,579 +0,0 @@ -/**************************************************************************/
-/*!
- @file dcd_lpc_11uxx_13uxx.c
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-#include "tusb_option.h"
-
-#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
-
-#define _TINY_USB_SOURCE_FILE_
-
-// NOTE: despite of being very the same to lpc13uxx controller, lpc11u's controller cannot queue transfer more than
-// endpoint's max packet size and need some soft DMA helper
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
-#include "hal/hal.h"
-#include "osal/osal.h"
-
-#include "device/dcd.h"
-#include "usbd_dcd.h"
-#include "dcd_lpc_11uxx_13uxx.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-#define DCD_11U_13U_QHD_COUNT 10
-
-enum {
- DCD_11U_13U_MAX_BYTE_PER_TD = (CFG_TUSB_MCU == OPT_MCU_LPC11UXX ? 64 : 1023)
-};
-
-#ifdef __CC_ARM
-#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
-#endif
-
-enum {
- INT_MASK_SOF = BIT_(30),
- INT_MASK_DEVICE_STATUS = BIT_(31)
-};
-
-#ifdef __CC_ARM
-#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
-#endif
-
-enum {
- CMDSTAT_DEVICE_ADDR_MASK = BIT_(7 )-1,
- CMDSTAT_DEVICE_ENABLE_MASK = BIT_(7 ),
- CMDSTAT_SETUP_RECEIVED_MASK = BIT_(8 ),
- CMDSTAT_DEVICE_CONNECT_MASK = BIT_(16), ///< reflect the softconnect only, does not reflect the actual attached state
- CMDSTAT_DEVICE_SUSPEND_MASK = BIT_(17),
- CMDSTAT_CONNECT_CHANGE_MASK = BIT_(24),
- CMDSTAT_SUSPEND_CHANGE_MASK = BIT_(25),
- CMDSTAT_RESET_CHANGE_MASK = BIT_(26),
- CMDSTAT_VBUS_DEBOUNCED_MASK = BIT_(28),
-};
-
-// buffer input must be 64 byte alignment
-typedef struct ATTR_PACKED
-{
- volatile uint16_t buff_addr_offset ; ///< The address offset is updated by hardware after each successful reception/transmission of a packet. Hardware increments the original value with the integer value when the packet size is divided by 64.
-
- volatile uint16_t nbytes : 10 ; ///< For OUT endpoints this is the number of bytes that can be received in this buffer. For IN endpoints this is the number of bytes that must be transmitted. HW decrements this value with the packet size every time when a packet is successfully transferred. Note: If a short packet is received on an OUT endpoint, the active bit will be cleared and the NBytes value indicates the remaining buffer space that is not used. Software calculates the received number of bytes by subtracting the remaining NBytes from the programmed value.
- uint16_t is_isochronous : 1 ;
- uint16_t feedback_toggle : 1 ; ///< For bulk endpoints and isochronous endpoints this bit is reserved and must be set to zero. For the control endpoint zero this bit is used as the toggle value. When the toggle reset bit is set, the data toggle is updated with the value programmed in this bit. When the endpoint is used as an interrupt endpoint, it can be set to the following values. 0: Interrupt endpoint in ‘toggle mode’ 1: Interrupt endpoint in ‘rate feedback mode’. This means that the data toggle is fixed to zero for all data packets. When the interrupt endpoint is in ‘rate feedback mode’, the TR bit must always be set to zero.
- uint16_t toggle_reset : 1 ; ///< When software sets this bit to one, the HW will set the toggle value equal to the value indicated in the “toggle value” (TV) bit. For the control endpoint zero, this is not needed to be used because the hardware resets the endpoint toggle to one for both directions when a setup token is received. For the other endpoints, the toggle can only be reset to zero when the endpoint is reset.
- uint16_t stall : 1 ; ///< 0: The selected endpoint is not stalled 1: The selected endpoint is stalled The Active bit has always higher priority than the Stall bit. This means that a Stall handshake is only sent when the active bit is zero and the stall bit is one. Software can only modify this bit when the active bit is zero.
- uint16_t disable : 1 ; ///< 0: The selected endpoint is enabled. 1: The selected endpoint is disabled. If a USB token is received for an endpoint that has the disabled bit set, hardware will ignore the token and not return any data or handshake. When a bus reset is received, software must set the disable bit of all endpoints to 1. Software can only modify this bit when the active bit is zero.
- volatile uint16_t active : 1 ; ///< The buffer is enabled. HW can use the buffer to store received OUT data or to transmit data on the IN endpoint. Software can only set this bit to ‘1’. As long as this bit is set to one, software is not allowed to update any of the values in this 32-bit word. In case software wants to deactivate the buffer, it must write a one to the corresponding “skip” bit in the USB Endpoint skip register. Hardware can only write this bit to zero. It will do this when it receives a short packet or when the NBytes field transitions to zero or when software has written a one to the “skip” bit.
-}dcd_11u_13u_qhd_t;
-
-TU_VERIFY_STATIC( sizeof(dcd_11u_13u_qhd_t) == 4, "size is not correct" );
-
-// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering.
-// If there is another dcd_edpt_xfer request, the new request will be saved and executed when the first is done.
-// next_td stored the 2nd request information
-// current_td is used to keep track of number of remaining & xferred bytes of the current request.
-// queued_bytes_in_buff keep track of number of bytes queued to each buffer (in case of short packet)
-
-typedef struct {
- dcd_11u_13u_qhd_t qhd[DCD_11U_13U_QHD_COUNT][2]; ///< must be 256 byte alignment, 2 for double buffer
-
- // start at 80, the size should not exceed 48 (for setup_request align at 128)
- struct {
- uint16_t buff_addr_offset;
- uint16_t total_bytes;
- }next_td[DCD_11U_13U_QHD_COUNT];
-
- uint32_t current_ioc; ///< interrupt on complete mask for current TD
- uint32_t next_ioc; ///< interrupt on complete mask for next TD
-
- // must start from 128
- ATTR_ALIGNED(64) tusb_control_request_t setup_request;
-
- struct {
- uint16_t remaining_bytes; ///< expected bytes of the queued transfer
- uint16_t xferred_total; ///< xferred bytes of the current transfer
-
- uint16_t queued_bytes_in_buff[2]; ///< expected bytes that are queued for each buffer
- }current_td[DCD_11U_13U_QHD_COUNT];
-
- uint8_t class_code[DCD_11U_13U_QHD_COUNT]; ///< class where the endpoints belongs to TODO no need for control endpoints
-
-}dcd_11u_13u_data_t;
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-// CFG_TUSB_MEM_SECTION must have ATTR_ALIGNED(64) for lpc11u & lpc13u
-#ifdef __ICCARM__
-ATTR_ALIGNED(256) CFG_TUSB_MEM_SECTION // for IAR the first ATTR_ALIGNED takes effect
-#else
-CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) // GCC & Keil the last ATTR_ALIGNED takes effect
-#endif
-STATIC_VAR dcd_11u_13u_data_t dcd_data;
-
-static inline uint16_t addr_offset(void const * p_buffer) ATTR_CONST ATTR_ALWAYS_INLINE;
-static inline uint16_t addr_offset(void const * p_buffer)
-{
- TU_ASSERT( (((uint32_t) p_buffer) & 0x3f) == 0, 0 );
- return (uint16_t) ( (((uint32_t) p_buffer) >> 6 ) & 0xFFFF) ;
-}
-
-static void queue_xfer_to_buffer(uint8_t ep_id, uint8_t buff_idx, uint16_t buff_addr_offset, uint16_t total_bytes);
-static void pipe_queue_xfer(uint8_t ep_id, uint16_t buff_addr_offset, uint16_t total_bytes);
-static void queue_xfer_in_next_td(uint8_t ep_id);
-
-//--------------------------------------------------------------------+
-// CONTROLLER API
-//--------------------------------------------------------------------+
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_CONNECT_MASK;
-}
-
-void dcd_set_config(uint8_t rhport, uint8_t config_num)
-{
-
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void) rhport;
-
- LPC_USB->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK;
- LPC_USB->DEVCMDSTAT |= dev_addr;
-}
-
-bool dcd_init(uint8_t rhport)
-{
- (void) rhport;
-
- LPC_USB->EPLISTSTART = (uint32_t) dcd_data.qhd;
- LPC_USB->DATABUFSTART = 0x20000000; // only SRAM1 & USB RAM can be used for transfer
-
- LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
- LPC_USB->INTEN = INT_MASK_DEVICE_STATUS;
- LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
- CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
-
- NVIC_EnableIRQ(USB_IRQn);
-
- return true;
-}
-
-static void bus_reset(void)
-{
- tu_memclr(&dcd_data, sizeof(dcd_11u_13u_data_t));
- for(uint8_t ep_id = 2; ep_id < DCD_11U_13U_QHD_COUNT; ep_id++)
- { // disable all non-control endpoints on bus reset
- dcd_data.qhd[ep_id][0].disable = dcd_data.qhd[ep_id][1].disable = 1;
- }
-
- dcd_data.qhd[0][1].buff_addr_offset = addr_offset(&dcd_data.setup_request);
-
- LPC_USB->EPINUSE = 0;
- LPC_USB->EPBUFCFG = 0; // all start with single buffer
- LPC_USB->EPSKIP = 0xFFFFFFFF;
-
- LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
- LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
- LPC_USB->INTEN = INT_MASK_DEVICE_STATUS | BIT_(0) | BIT_(1); // enable device status & control endpoints
-}
-
-static void endpoint_non_control_isr(uint32_t int_status)
-{
- for(uint8_t ep_id = 2; ep_id < DCD_11U_13U_QHD_COUNT; ep_id++ )
- {
- if ( BIT_TEST_(int_status, ep_id) )
- {
- dcd_11u_13u_qhd_t * const arr_qhd = dcd_data.qhd[ep_id];
-
- // when double buffering, the complete buffer is opposed to the current active buffer in EPINUSE
- uint8_t const buff_idx = LPC_USB->EPINUSE & BIT_(ep_id) ? 0 : 1;
- uint16_t const xferred_bytes = dcd_data.current_td[ep_id].queued_bytes_in_buff[buff_idx] - arr_qhd[buff_idx].nbytes;
-
- dcd_data.current_td[ep_id].xferred_total += xferred_bytes;
-
- // there are still data to transfer.
- if ( (arr_qhd[buff_idx].nbytes == 0) && (dcd_data.current_td[ep_id].remaining_bytes > 0) )
- { // NOTE although buff_addr_offset has been increased when xfer is completed
- // but we still need to increase it one more as we are using double buffering.
- queue_xfer_to_buffer(ep_id, buff_idx, arr_qhd[buff_idx].buff_addr_offset+1, dcd_data.current_td[ep_id].remaining_bytes);
- }
- // short packet or (no more byte and both buffers are finished)
- else if ( (arr_qhd[buff_idx].nbytes > 0) || !arr_qhd[1-buff_idx].active )
- { // current TD (request) is completed
- LPC_USB->EPSKIP = BIT_SET_(LPC_USB->EPSKIP, ep_id); // skip other endpoint in case of short-package
-
- dcd_data.current_td[ep_id].remaining_bytes = 0;
-
- if ( BIT_TEST_(dcd_data.current_ioc, ep_id) )
- {
- edpt_hdl_t edpt_hdl =
- {
- .rhport = 0,
- .index = ep_id,
- .class_code = dcd_data.class_code[ep_id]
- };
-
- dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
-
- // TODO no way determine if the transfer is failed or not
- dcd_xfer_complete(edpt_hdl, dcd_data.current_td[ep_id].xferred_total, true);
- }
-
- //------------- Next TD is available -------------//
- if ( dcd_data.next_td[ep_id].total_bytes != 0 )
- {
- queue_xfer_in_next_td(ep_id);
- }
- }else
- {
- // transfer complete, there is no more remaining bytes, but this buffer is not the last transaction (the other is)
- }
- }
- }
-}
-
-static void endpoint_control_isr(uint32_t int_status)
-{
- uint8_t const ep_id = ( int_status & BIT_(0) ) ? 0 : 1;
-
- // there are still data to transfer.
- if ( (dcd_data.qhd[ep_id][0].nbytes == 0) && (dcd_data.current_td[ep_id].remaining_bytes > 0) )
- {
- queue_xfer_to_buffer(ep_id, 0, dcd_data.qhd[ep_id][0].buff_addr_offset, dcd_data.current_td[ep_id].remaining_bytes);
- }else
- {
- dcd_data.current_td[ep_id].remaining_bytes = 0;
-
- if ( BIT_TEST_(dcd_data.current_ioc, ep_id) )
- {
- edpt_hdl_t edpt_hdl = { .rhport = 0 };
-
- dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, ep_id);
-
- // FIXME xferred_byte for control xfer is not needed now !!!
- dcd_xfer_complete(edpt_hdl, 0, true);
- }
- }
-}
-
-void hal_dcd_isr(uint8_t rhport)
-{
- (void) rhport;
-
- uint32_t const int_enable = LPC_USB->INTEN;
- uint32_t const int_status = LPC_USB->INTSTAT & int_enable;
- LPC_USB->INTSTAT = int_status; // Acknowledge handled interrupt
-
- if (int_status == 0) return;
-
- uint32_t const dev_cmd_stat = LPC_USB->DEVCMDSTAT;
-
- dcd_event_t event = { .rhport = rhport };
-
- //------------- Device Status -------------//
- if ( int_status & INT_MASK_DEVICE_STATUS )
- {
- LPC_USB->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
- if ( dev_cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
- {
- bus_reset();
-
- event.event_id = DCD_EVENT_BUS_RESET;
- dcd_event_handler(&event, true);
- }
-
- if (dev_cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)
- { // device disconnect
- if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
- { // debouncing as this can be set when device is powering
- event.event_id = DCD_EVENT_UNPLUGGED;
- dcd_event_handler(&event, true);
- }
- }
-
- // TODO support suspend & resume
- if (dev_cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
- {
- if (dev_cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK)
- { // suspend signal, bus idle for more than 3ms
- // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
- if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
- {
- event.event_id = DCD_EVENT_SUSPENDED;
- dcd_event_handler(&event, true);
- }
- }
- }
-// else
-// { // resume signal
-// event.event_id = DCD_EVENT_RESUME;
-// dcd_event_handler(&event, true);
-// }
-// }
- }
-
- //------------- Setup Received -------------//
- if ( BIT_TEST_(int_status, 0) && (dev_cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) )
- { // received control request from host
- // copy setup request & acknowledge so that the next setup can be received by hw
- event.event_id = DCD_EVENT_SETUP_RECEIVED;
- event.setup_received = dcd_data.setup_request;
-
- dcd_event_handler(&event, true);
-
- // NXP control flowchart clear Active & Stall on both Control IN/OUT endpoints
- dcd_data.qhd[0][0].stall = dcd_data.qhd[1][0].stall = 0;
-
- LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK;
- dcd_data.qhd[0][1].buff_addr_offset = addr_offset(&dcd_data.setup_request);
- }
- //------------- Control Endpoint -------------//
- else if ( int_status & 0x03 )
- {
- endpoint_control_isr(int_status);
- }
-
- //------------- Non-Control Endpoints -------------//
- if( int_status & ~(0x03UL) )
- {
- endpoint_non_control_isr(int_status);
- }
-}
-
-//--------------------------------------------------------------------+
-// CONTROL PIPE API
-//--------------------------------------------------------------------+
-void dcd_control_stall(uint8_t rhport)
-{
- (void) rhport;
- // TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
- dcd_data.qhd[0][0].stall = dcd_data.qhd[1][0].stall = 1;
-}
-
-bool dcd_control_xfer(uint8_t rhport, uint8_t dir, uint8_t * p_buffer, uint16_t length, bool int_on_complete)
-{
- (void) rhport;
-
- // determine Endpoint where Data & Status phase occurred (IN or OUT)
- uint8_t const ep_data = (dir == TUSB_DIR_IN) ? 1 : 0;
- uint8_t const ep_status = 1 - ep_data;
-
- dcd_data.current_ioc = int_on_complete ? BIT_SET_(dcd_data.current_ioc, ep_status) : BIT_CLR_(dcd_data.current_ioc, ep_status);
-
- //------------- Data Phase -------------//
- if (length)
- {
- dcd_data.current_td[ep_data].remaining_bytes = length;
- dcd_data.current_td[ep_data].xferred_total = 0;
-
- queue_xfer_to_buffer(ep_data, 0, addr_offset(p_buffer), length);
- }
-
- //------------- Status Phase -------------//
- dcd_data.current_td[ep_status].remaining_bytes = 0;
- dcd_data.current_td[ep_status].xferred_total = 0;
-
- queue_xfer_to_buffer(ep_status, 0, 0, 0);
-
- return true;
-}
-
-//--------------------------------------------------------------------+
-// PIPE HELPER
-//--------------------------------------------------------------------+
-static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr) ATTR_CONST ATTR_ALWAYS_INLINE;
-static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr)
-{
- return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
-}
-
-#if 0
-static inline uint8_t edpt_phy2log(uint8_t physical_endpoint) ATTR_CONST ATTR_ALWAYS_INLINE;
-static inline uint8_t edpt_phy2log(uint8_t physical_endpoint)
-{
- return physical_endpoint/2;
-}
-#endif
-
-//--------------------------------------------------------------------+
-// BULK/INTERRUPT/ISOCHRONOUS PIPE API
-//--------------------------------------------------------------------+
-void dcd_edpt_stall(edpt_hdl_t edpt_hdl)
-{
- dcd_data.qhd[edpt_hdl.index][0].stall = dcd_data.qhd[edpt_hdl.index][1].stall = 1;
-}
-
-bool dcd_pipe_is_stalled(edpt_hdl_t edpt_hdl)
-{
- return dcd_data.qhd[edpt_hdl.index][0].stall || dcd_data.qhd[edpt_hdl.index][1].stall;
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t edpt_addr)
-{
- uint8_t ep_id = edpt_addr2phy(edpt_addr);
-// uint8_t active_buffer = BIT_TEST_(LPC_USB->EPINUSE, ep_id) ? 1 : 0;
-
- dcd_data.qhd[ep_id][0].stall = dcd_data.qhd[ep_id][1].stall = 0;
-
- // since the next transfer always take place on buffer0 --> clear buffer0 toggle
- dcd_data.qhd[ep_id][0].toggle_reset = 1;
- dcd_data.qhd[ep_id][0].feedback_toggle = 0;
-
- //------------- clear stall must carry on any previously queued transfer -------------//
- if ( dcd_data.next_td[ep_id].total_bytes != 0 )
- {
- queue_xfer_in_next_td(ep_id);
- }
-}
-
-edpt_hdl_t dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc, uint8_t class_code)
-{
- (void) rhport;
- edpt_hdl_t const null_handle = { 0 };
-
- if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return null_handle; // TODO not support ISO yet
-
- TU_ASSERT (p_endpoint_desc->wMaxPacketSize.size <= 64, null_handle); // TODO ISO can be 1023, but ISO not supported now
-
- // TODO prevent to open if endpoint size is not 64
-
- //------------- Prepare Queue Head -------------//
- uint8_t ep_id = edpt_addr2phy(p_endpoint_desc->bEndpointAddress);
-
- TU_ASSERT( dcd_data.qhd[ep_id][0].disable && dcd_data.qhd[ep_id][1].disable, null_handle ); // endpoint must not previously opened, normally this means running out of endpoints
-
- tu_memclr(dcd_data.qhd[ep_id], 2*sizeof(dcd_11u_13u_qhd_t));
- dcd_data.qhd[ep_id][0].is_isochronous = dcd_data.qhd[ep_id][1].is_isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
- dcd_data.class_code[ep_id] = class_code;
-
- dcd_data.qhd[ep_id][0].disable = dcd_data.qhd[ep_id][1].disable = 0;
-
- LPC_USB->EPBUFCFG |= BIT_(ep_id);
- LPC_USB->INTEN |= BIT_(ep_id);
-
- return (edpt_hdl_t)
- {
- .rhport = 0,
- .index = ep_id,
- .class_code = class_code
- };
-}
-
-bool dcd_edpt_busy(edpt_hdl_t edpt_hdl)
-{
- return dcd_data.qhd[edpt_hdl.index][0].active || dcd_data.qhd[edpt_hdl.index][1].active;
-}
-
-static void queue_xfer_to_buffer(uint8_t ep_id, uint8_t buff_idx, uint16_t buff_addr_offset, uint16_t total_bytes)
-{
- uint16_t const queued_bytes = tu_min16(total_bytes, DCD_11U_13U_MAX_BYTE_PER_TD);
-
- dcd_data.current_td[ep_id].queued_bytes_in_buff[buff_idx] = queued_bytes;
- dcd_data.current_td[ep_id].remaining_bytes -= queued_bytes;
-
- dcd_data.qhd[ep_id][buff_idx].buff_addr_offset = buff_addr_offset;
- dcd_data.qhd[ep_id][buff_idx].nbytes = queued_bytes;
-
- dcd_data.qhd[ep_id][buff_idx].active = 1;
-}
-
-static void pipe_queue_xfer(uint8_t ep_id, uint16_t buff_addr_offset, uint16_t total_bytes)
-{
- dcd_data.current_td[ep_id].remaining_bytes = total_bytes;
- dcd_data.current_td[ep_id].xferred_total = 0;
- dcd_data.current_td[ep_id].queued_bytes_in_buff[0] = 0;
- dcd_data.current_td[ep_id].queued_bytes_in_buff[1] = 0;
-
- LPC_USB->EPINUSE = BIT_CLR_(LPC_USB->EPINUSE , ep_id); // force HW to use buffer0
-
- // need to queue buffer1 first, as activate buffer0 can causes controller does transferring immediately
- // while buffer1 is not ready yet
- if ( total_bytes > DCD_11U_13U_MAX_BYTE_PER_TD)
- {
- queue_xfer_to_buffer(ep_id, 1, buff_addr_offset+1, total_bytes - DCD_11U_13U_MAX_BYTE_PER_TD);
- }
-
- queue_xfer_to_buffer(ep_id, 0, buff_addr_offset, total_bytes);
-}
-
-static void queue_xfer_in_next_td(uint8_t ep_id)
-{
- dcd_data.current_ioc |= ( dcd_data.next_ioc & BIT_(ep_id) ); // copy next IOC to current IOC
-
- pipe_queue_xfer(ep_id, dcd_data.next_td[ep_id].buff_addr_offset, dcd_data.next_td[ep_id].total_bytes);
-
- dcd_data.next_td[ep_id].total_bytes = 0; // clear this field as it is used to indicate whehther next TD available
-}
-
-tusb_error_t dcd_edpt_queue_xfer(edpt_hdl_t edpt_hdl, uint8_t * buffer, uint16_t total_bytes)
-{
- TU_ASSERT( !dcd_edpt_busy(edpt_hdl), TUSB_ERROR_INTERFACE_IS_BUSY); // endpoint must not in transferring
-
- dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
-
- pipe_queue_xfer(edpt_hdl.index, addr_offset(buffer), total_bytes);
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t dcd_edpt_xfer(edpt_hdl_t edpt_hdl, uint8_t* buffer, uint16_t total_bytes, bool int_on_complete)
-{
- if( dcd_edpt_busy(edpt_hdl) || dcd_pipe_is_stalled(edpt_hdl) )
- { // save this transfer data to next td if pipe is busy or already been stalled
- dcd_data.next_td[edpt_hdl.index].buff_addr_offset = addr_offset(buffer);
- dcd_data.next_td[edpt_hdl.index].total_bytes = total_bytes;
-
- dcd_data.next_ioc = int_on_complete ? BIT_SET_(dcd_data.next_ioc, edpt_hdl.index) : BIT_CLR_(dcd_data.next_ioc, edpt_hdl.index);
- }else
- {
- dcd_data.current_ioc = int_on_complete ? BIT_SET_(dcd_data.current_ioc, edpt_hdl.index) : BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
-
- pipe_queue_xfer(edpt_hdl.index, addr_offset(buffer), total_bytes);
- }
-
- return TUSB_ERROR_NONE;
-}
-
-#endif
-
diff --git a/src/portable/nxp/lpc17xx/dcd_lpc175x_6x.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c index 517f037d0..84eb0c121 100644 --- a/src/portable/nxp/lpc17xx/dcd_lpc175x_6x.c +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c @@ -1,602 +1,596 @@ -/**************************************************************************/
-/*!
- @file dcd_lpc175x_6x.c
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-#include "tusb_option.h"
-
-#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
-
-#include "device/dcd.h"
-#include "dcd_lpc175x_6x.h"
-#include "LPC17xx.h"
-
-#include "osal/osal.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-#define DCD_ENDPOINT_MAX 32
-
-typedef struct ATTR_ALIGNED(4)
-{
- //------------- Word 0 -------------//
- uint32_t next;
-
- //------------- Word 1 -------------//
- uint16_t atle_mode : 2; // 00: normal, 01: ATLE (auto length extraction)
- uint16_t next_valid : 1;
- uint16_t : 1; ///< reserved
- uint16_t isochronous : 1; // is an iso endpoint
- uint16_t max_packet_size : 11;
-
- volatile uint16_t buflen; // bytes for non-iso, number of packets for iso endpoint
-
- //------------- Word 2 -------------//
- volatile uint32_t buffer;
-
- //------------- Word 3 -------------//
- volatile uint16_t retired : 1; // initialized to zero
- volatile uint16_t status : 4;
- volatile uint16_t iso_last_packet_valid : 1;
- volatile uint16_t atle_lsb_extracted : 1; // used in ATLE mode
- volatile uint16_t atle_msb_extracted : 1; // used in ATLE mode
- volatile uint16_t atle_mess_len_position : 6; // used in ATLE mode
- uint16_t : 2;
-
- volatile uint16_t present_count; // For non-iso : The number of bytes transferred by the DMA engine
- // For iso : number of packets
-
- //------------- Word 4 -------------//
- // uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso
-}dma_desc_t;
-
-TU_VERIFY_STATIC( sizeof(dma_desc_t) == 16, "size is not correct"); // TODO not support ISO for now
-
-typedef struct
-{
- // must be 128 byte aligned
- volatile dma_desc_t* udca[DCD_ENDPOINT_MAX];
-
- // TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory)
- dma_desc_t dd[DCD_ENDPOINT_MAX];
-
- struct
- {
- uint8_t* out_buffer;
- uint8_t out_bytes;
- volatile bool out_received; // indicate if data is already received in endpoint
-
- uint8_t in_bytes;
- } control;
-
-} dcd_data_t;
-
-ATTR_ALIGNED(128) static dcd_data_t _dcd;
-
-
-//--------------------------------------------------------------------+
-// SIE Command
-//--------------------------------------------------------------------+
-static void sie_cmd_code (sie_cmdphase_t phase, uint8_t code_data)
-{
- LPC_USB->USBDevIntClr = (DEV_INT_COMMAND_CODE_EMPTY_MASK | DEV_INT_COMMAND_DATA_FULL_MASK);
- LPC_USB->USBCmdCode = (phase << 8) | (code_data << 16);
-
- uint32_t const wait_flag = (phase == SIE_CMDPHASE_READ) ? DEV_INT_COMMAND_DATA_FULL_MASK : DEV_INT_COMMAND_CODE_EMPTY_MASK;
- while ((LPC_USB->USBDevIntSt & wait_flag) == 0) {}
-
- LPC_USB->USBDevIntClr = wait_flag;
-}
-
-static void sie_write (uint8_t cmd_code, uint8_t data_len, uint8_t data)
-{
- sie_cmd_code(SIE_CMDPHASE_COMMAND, cmd_code);
-
- if (data_len)
- {
- sie_cmd_code(SIE_CMDPHASE_WRITE, data);
- }
-}
-
-static uint32_t sie_read (uint8_t cmd_code, uint8_t data_len)
-{
- // TODO multiple read
- sie_cmd_code(SIE_CMDPHASE_COMMAND , cmd_code);
- sie_cmd_code(SIE_CMDPHASE_READ , cmd_code);
- return LPC_USB->USBCmdData;
-}
-
-//--------------------------------------------------------------------+
-// PIPE HELPER
-//--------------------------------------------------------------------+
-static inline uint8_t ep_addr2idx(uint8_t ep_addr)
-{
- return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
-}
-
-static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size)
-{
- // follows example in 11.10.4.2
- LPC_USB->USBReEp |= BIT_(ep_id);
- LPC_USB->USBEpInd = ep_id; // select index before setting packet size
- LPC_USB->USBMaxPSize = max_packet_size;
-
- while ((LPC_USB->USBDevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK) == 0) {}
- LPC_USB->USBDevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK;
-}
-
-
-//--------------------------------------------------------------------+
-// CONTROLLER API
-//--------------------------------------------------------------------+
-static void bus_reset(void)
-{
- // step 7 : slave mode set up
- LPC_USB->USBEpIntClr = 0xFFFFFFFF; // clear all pending interrupt
- LPC_USB->USBDevIntClr = 0xFFFFFFFF; // clear all pending interrupt
- LPC_USB->USBEpIntEn = 0x03UL; // control endpoint cannot use DMA, non-control all use DMA
- LPC_USB->USBEpIntPri = 0x03UL; // fast for control endpoint
-
- // step 8 : DMA set up
- LPC_USB->USBEpDMADis = 0xFFFFFFFF; // firstly disable all dma
- LPC_USB->USBDMARClr = 0xFFFFFFFF; // clear all pending interrupt
- LPC_USB->USBEoTIntClr = 0xFFFFFFFF;
- LPC_USB->USBNDDRIntClr = 0xFFFFFFFF;
- LPC_USB->USBSysErrIntClr = 0xFFFFFFFF;
-
- tu_memclr(&_dcd, sizeof(dcd_data_t));
-}
-
-bool dcd_init(uint8_t rhport)
-{
- (void) rhport;
-
- //------------- user manual 11.13 usb device controller initialization -------------// LPC_USB->USBEpInd = 0;
- // step 6 : set up control endpoint
- set_ep_size(0, CFG_TUD_ENDOINT0_SIZE);
- set_ep_size(1, CFG_TUD_ENDOINT0_SIZE);
-
- bus_reset();
-
- LPC_USB->USBDevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_FAST_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK);
- LPC_USB->USBUDCAH = (uint32_t) _dcd.udca;
- LPC_USB->USBDMAIntEn = (DMA_INT_END_OF_XFER_MASK /*| DMA_INT_NEW_DD_REQUEST_MASK*/ | DMA_INT_ERROR_MASK);
-
- sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1); // connect
-
- // USB IRQ priority should be set by application previously
- // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
-#if CFG_TUSB_OS == OPT_OS_FREERTOS
- if ( NVIC_GetPriority(USB_IRQn) < configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY )
- {
- NVIC_SetPriority(USB_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
- }
-#endif
-
- NVIC_ClearPendingIRQ(USB_IRQn);
- NVIC_EnableIRQ(USB_IRQn);
-
- return TUSB_ERROR_NONE;
-}
-
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1);
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void) rhport;
- sie_write(SIE_CMDCODE_SET_ADDRESS, 1, 0x80 | dev_addr); // 7th bit is : device_enable
-}
-
-void dcd_set_config(uint8_t rhport, uint8_t config_num)
-{
- (void) rhport;
- (void) config_num;
- sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1);
-}
-
-//--------------------------------------------------------------------+
-// CONTROL HELPER
-//--------------------------------------------------------------------+
-static inline uint8_t byte2dword(uint8_t bytes)
-{
- return (bytes + 3) / 4; // length in dwords
-}
-
-static void control_ep_write(void const * buffer, uint8_t len)
-{
- uint32_t const * buf32 = (uint32_t const *) buffer;
-
- LPC_USB->USBCtrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0
- LPC_USB->USBTxPLen = (uint32_t) len;
-
- for (uint8_t count = 0; count < byte2dword(len); count++)
- {
- LPC_USB->USBTxData = *buf32; // NOTE: cortex M3 have no problem with alignment
- buf32++;
- }
-
- LPC_USB->USBCtrl = 0;
-
- // select control IN & validate the endpoint
- sie_write(SIE_CMDCODE_ENDPOINT_SELECT+1, 0, 0);
- sie_write(SIE_CMDCODE_BUFFER_VALIDATE , 0, 0);
-}
-
-static uint8_t control_ep_read(void * buffer, uint8_t len)
-{
- LPC_USB->USBCtrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0
- while ((LPC_USB->USBRxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout
-
- len = tu_min8(len, (uint8_t) (LPC_USB->USBRxPLen & USBRXPLEN_PACKET_LENGTH_MASK) );
- uint32_t *buf32 = (uint32_t*) buffer;
-
- for (uint8_t count=0; count < byte2dword(len); count++)
- {
- *buf32 = LPC_USB->USBRxData;
- buf32++;
- }
-
- LPC_USB->USBCtrl = 0;
-
- // select control OUT & clear the endpoint
- sie_write(SIE_CMDCODE_ENDPOINT_SELECT+0, 0, 0);
- sie_write(SIE_CMDCODE_BUFFER_CLEAR , 0, 0);
-
- return len;
-}
-
-//--------------------------------------------------------------------+
-// DCD Endpoint Port
-//--------------------------------------------------------------------+
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
- (void) rhport;
-
- uint8_t const epnum = edpt_number(p_endpoint_desc->bEndpointAddress);
- uint8_t const ep_id = ep_addr2idx(p_endpoint_desc->bEndpointAddress);
-
- // Endpoint type is fixed to endpoint number
- // 1: interrupt, 2: Bulk, 3: Iso and so on
- switch ( p_endpoint_desc->bmAttributes.xfer )
- {
- case TUSB_XFER_INTERRUPT:
- TU_ASSERT((epnum % 3) == 1);
- break;
-
- case TUSB_XFER_BULK:
- TU_ASSERT((epnum % 3) == 2 || (epnum == 15));
- break;
-
- case TUSB_XFER_ISOCHRONOUS:
- TU_ASSERT((epnum % 3) == 3 && (epnum != 15));
- break;
-
- default:
- break;
- }
-
- //------------- Realize Endpoint with Max Packet Size -------------//
- set_ep_size(ep_id, p_endpoint_desc->wMaxPacketSize.size);
-
- //------------- first DD prepare -------------//
- dma_desc_t* const dd = &_dcd.dd[ep_id];
- tu_memclr(dd, sizeof(dma_desc_t));
-
- dd->isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
- dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
- dd->retired = 1; // invalid at first
-
- sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id, 1, 0); // clear all endpoint status
-
- return true;
-}
-
-bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- uint8_t ep_id = ep_addr2idx( ep_addr );
- return (_dcd.udca[ep_id] != NULL && !_dcd.udca[ep_id]->retired);
-}
-
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- if ( edpt_number(ep_addr) == 0 )
- {
- sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+0, 1, SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK);
- }else
- {
- uint8_t ep_id = ep_addr2idx( ep_addr );
- sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, SIE_SET_ENDPOINT_STALLED_MASK);
- }
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
- uint8_t ep_id = ep_addr2idx(ep_addr);
-
- sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0);
-}
-
-bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- uint32_t const ep_state = sie_read(SIE_CMDCODE_ENDPOINT_SELECT + ep_addr2idx(ep_addr), 1);
-
- return (ep_state & SIE_SELECT_ENDPOINT_STALL_MASK) ? true : false;
-}
-
-static bool control_xact(uint8_t rhport, uint8_t dir, uint8_t * buffer, uint8_t len)
-{
- (void) rhport;
-
- if ( dir )
- {
- _dcd.control.in_bytes = len;
- control_ep_write(buffer, len);
- }else
- {
- if ( _dcd.control.out_received )
- {
- // Already received the DATA OUT packet
- _dcd.control.out_received = false;
- _dcd.control.out_buffer = NULL;
- _dcd.control.out_bytes = 0;
-
- uint8_t received = control_ep_read(buffer, len);
- dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true);
- }else
- {
- _dcd.control.out_buffer = buffer;
- _dcd.control.out_bytes = len;
- }
- }
-
- return true;
-}
-
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
-{
- // Control transfer is not DMA support, and must be done in slave mode
- if ( edpt_number(ep_addr) == 0 )
- {
- return control_xact(rhport, edpt_dir(ep_addr), buffer, (uint8_t) total_bytes);
- }
- else
- {
- uint8_t ep_id = ep_addr2idx(ep_addr);
- dma_desc_t* dd = &_dcd.dd[ep_id];
-
- // Prepare DMA descriptor
- // Isochronous & max packet size must be preserved, Other fields of dd should be clear
- uint16_t const ep_size = dd->max_packet_size;
- uint8_t is_iso = dd->isochronous;
-
- tu_memclr(dd, sizeof(dma_desc_t));
- dd->isochronous = is_iso;
- dd->max_packet_size = ep_size;
- dd->buffer = buffer;
- dd->buflen = total_bytes;
-
- _dcd.udca[ep_id] = dd;
-
- if ( ep_id % 2 )
- {
- // Clear EP interrupt before Enable DMA
- LPC_USB->USBEpIntEn &= ~BIT_(ep_id);
- LPC_USB->USBEpDMAEn = BIT_(ep_id);
-
- // endpoint IN need to actively raise DMA request
- LPC_USB->USBDMARSet = BIT_(ep_id);
- }else
- {
- // Enable DMA
- LPC_USB->USBEpDMAEn = BIT_(ep_id);
- }
-
- return true;
- }
-}
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-
-// handle control xfer (slave mode)
-static void control_xfer_isr(uint8_t rhport, uint32_t ep_int_status)
-{
- // Control out complete
- if ( ep_int_status & BIT_(0) )
- {
- bool is_setup = sie_read(SIE_CMDCODE_ENDPOINT_SELECT+0, 1) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK;
-
- LPC_USB->USBEpIntClr = BIT_(0);
-
- if (is_setup)
- {
- uint8_t setup_packet[8];
- control_ep_read(setup_packet, 8); // TODO read before clear setup above
-
- dcd_event_setup_received(rhport, setup_packet, true);
- }
- else if ( _dcd.control.out_buffer )
- {
- // software queued transfer previously
- uint8_t received = control_ep_read(_dcd.control.out_buffer, _dcd.control.out_bytes);
-
- _dcd.control.out_buffer = NULL;
- _dcd.control.out_bytes = 0;
-
- dcd_event_xfer_complete(rhport, 0, received, XFER_RESULT_SUCCESS, true);
- }else
- {
- // hardware auto ack packet -> mark as received
- _dcd.control.out_received = true;
- }
- }
-
- // Control In complete
- if ( ep_int_status & BIT_(1) )
- {
- LPC_USB->USBEpIntClr = BIT_(1);
- dcd_event_xfer_complete(rhport, TUSB_DIR_IN_MASK, _dcd.control.in_bytes, XFER_RESULT_SUCCESS, true);
- }
-}
-
-// handle bus event signal
-static void bus_event_isr(uint8_t rhport)
-{
- uint8_t const dev_status = sie_read(SIE_CMDCODE_DEVICE_STATUS, 1);
- if (dev_status & SIE_DEV_STATUS_RESET_MASK)
- {
- bus_reset();
- dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
- }
-
- if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)
- {
- // device is disconnected, require using VBUS (P1_30)
- dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
- }
-
- if (dev_status & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
- {
- if (dev_status & SIE_DEV_STATUS_SUSPEND_MASK)
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
- }
- else
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
- }
- }
-}
-
-// Helper to complete a DMA descriptor for non-control transfer
-static void dd_complete_isr(uint8_t rhport, uint8_t ep_id)
-{
- dma_desc_t* const dd = &_dcd.dd[ep_id];
- uint8_t result = (dd->status == DD_STATUS_NORMAL || dd->status == DD_STATUS_DATA_UNDERUN) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED;
- uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0);
-
- dcd_event_xfer_complete(rhport, ep_addr, dd->present_count, result, true);
-}
-
-// main USB IRQ handler
-void hal_dcd_isr(uint8_t rhport)
-{
- uint32_t const dev_int_status = LPC_USB->USBDevIntSt & LPC_USB->USBDevIntEn;
- LPC_USB->USBDevIntClr = dev_int_status;// Acknowledge handled interrupt
-
- // Bus event
- if (dev_int_status & DEV_INT_DEVICE_STATUS_MASK)
- {
- bus_event_isr(rhport);
- }
-
- // Endpoint interrupt
- uint32_t const ep_int_status = LPC_USB->USBEpIntSt & LPC_USB->USBEpIntEn;
-
- // Control Endpoint are fast
- if (dev_int_status & DEV_INT_ENDPOINT_FAST_MASK)
- {
- // Note clear USBEpIntClr will also clear the setup received bit --> clear after handle setup packet
- // Only clear USBEpIntClr 1 endpoint each, and should wait for CDFULL bit set
- control_xfer_isr(rhport, ep_int_status);
- }
-
- // non-control IN are slow
- if (dev_int_status & DEV_INT_ENDPOINT_SLOW_MASK)
- {
- for ( uint8_t ep_id = 3; ep_id < DCD_ENDPOINT_MAX; ep_id += 2 )
- {
- if ( BIT_TEST_(ep_int_status, ep_id) )
- {
- LPC_USB->USBEpIntClr = BIT_(ep_id);
-
- // Clear Ep interrupt for next DMA
- LPC_USB->USBEpIntEn &= ~BIT_(ep_id);
-
- dd_complete_isr(rhport, ep_id);
- }
- }
- }
-
- // DMA transfer complete (RAM <-> EP) for Non-Control
- // OUT: USB transfer is fully complete
- // IN : UBS transfer is still on-going -> enable EpIntEn to know when it is complete
- uint32_t const dma_int_status = LPC_USB->USBDMAIntSt & LPC_USB->USBDMAIntEn;
- if (dma_int_status & DMA_INT_END_OF_XFER_MASK)
- {
- uint32_t const eot = LPC_USB->USBEoTIntSt;
- LPC_USB->USBEoTIntClr = eot; // acknowledge interrupt source
-
- for ( uint8_t ep_id = 2; ep_id < DCD_ENDPOINT_MAX; ep_id++ )
- {
- if ( BIT_TEST_(eot, ep_id) )
- {
- if ( ep_id & 0x01 )
- {
- // IN enable EpInt for end of usb transfer
- LPC_USB->USBEpIntEn |= BIT_(ep_id);
- }else
- {
- // OUT
- dd_complete_isr(rhport, ep_id);
- }
- }
- }
- }
-
- // Errors
- if ( (dev_int_status & DEV_INT_ERROR_MASK) || (dma_int_status & DMA_INT_ERROR_MASK) )
- {
- uint32_t error_status = sie_read(SIE_CMDCODE_READ_ERROR_STATUS, 1);
- (void) error_status;
- TU_BREAKPOINT();
- }
-}
-
-#endif
+/**************************************************************************/ +/*! + @file dcd_lpc175x_6x.c + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This file is part of the tinyusb stack. +*/ +/**************************************************************************/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) + +#include "device/dcd.h" +#include "dcd_lpc17_40.h" +#include "chip.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +#define DCD_ENDPOINT_MAX 32 + +typedef struct ATTR_ALIGNED(4) +{ + //------------- Word 0 -------------// + uint32_t next; + + //------------- Word 1 -------------// + uint16_t atle_mode : 2; // 00: normal, 01: ATLE (auto length extraction) + uint16_t next_valid : 1; + uint16_t : 1; ///< reserved + uint16_t isochronous : 1; // is an iso endpoint + uint16_t max_packet_size : 11; + + volatile uint16_t buflen; // bytes for non-iso, number of packets for iso endpoint + + //------------- Word 2 -------------// + volatile uint32_t buffer; + + //------------- Word 3 -------------// + volatile uint16_t retired : 1; // initialized to zero + volatile uint16_t status : 4; + volatile uint16_t iso_last_packet_valid : 1; + volatile uint16_t atle_lsb_extracted : 1; // used in ATLE mode + volatile uint16_t atle_msb_extracted : 1; // used in ATLE mode + volatile uint16_t atle_mess_len_position : 6; // used in ATLE mode + uint16_t : 2; + + volatile uint16_t present_count; // For non-iso : The number of bytes transferred by the DMA engine + // For iso : number of packets + + //------------- Word 4 -------------// + // uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso +}dma_desc_t; + +TU_VERIFY_STATIC( sizeof(dma_desc_t) == 16, "size is not correct"); // TODO not support ISO for now + +typedef struct +{ + // must be 128 byte aligned + volatile dma_desc_t* udca[DCD_ENDPOINT_MAX]; + + // TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory) + dma_desc_t dd[DCD_ENDPOINT_MAX]; + + struct + { + uint8_t* out_buffer; + uint8_t out_bytes; + volatile bool out_received; // indicate if data is already received in endpoint + + uint8_t in_bytes; + } control; + +} dcd_data_t; + +CFG_TUSB_MEM_SECTION ATTR_ALIGNED(128) static dcd_data_t _dcd; + + +//--------------------------------------------------------------------+ +// SIE Command +//--------------------------------------------------------------------+ +static void sie_cmd_code (sie_cmdphase_t phase, uint8_t code_data) +{ + LPC_USB->DevIntClr = (DEV_INT_COMMAND_CODE_EMPTY_MASK | DEV_INT_COMMAND_DATA_FULL_MASK); + LPC_USB->CmdCode = (phase << 8) | (code_data << 16); + + uint32_t const wait_flag = (phase == SIE_CMDPHASE_READ) ? DEV_INT_COMMAND_DATA_FULL_MASK : DEV_INT_COMMAND_CODE_EMPTY_MASK; + while ((LPC_USB->DevIntSt & wait_flag) == 0) {} + + LPC_USB->DevIntClr = wait_flag; +} + +static void sie_write (uint8_t cmd_code, uint8_t data_len, uint8_t data) +{ + sie_cmd_code(SIE_CMDPHASE_COMMAND, cmd_code); + + if (data_len) + { + sie_cmd_code(SIE_CMDPHASE_WRITE, data); + } +} + +static uint8_t sie_read (uint8_t cmd_code) +{ + sie_cmd_code(SIE_CMDPHASE_COMMAND , cmd_code); + sie_cmd_code(SIE_CMDPHASE_READ , cmd_code); + return (uint8_t) LPC_USB->CmdData; +} + +//--------------------------------------------------------------------+ +// PIPE HELPER +//--------------------------------------------------------------------+ +static inline uint8_t ep_addr2idx(uint8_t ep_addr) +{ + return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0); +} + +static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size) +{ + // follows example in 11.10.4.2 + LPC_USB->ReEp |= BIT_(ep_id); + LPC_USB->EpInd = ep_id; // select index before setting packet size + LPC_USB->MaxPSize = max_packet_size; + + while ((LPC_USB->DevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK) == 0) {} + LPC_USB->DevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK; +} + + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ +static void bus_reset(void) +{ + // step 7 : slave mode set up + LPC_USB->EpIntClr = 0xFFFFFFFF; // clear all pending interrupt + LPC_USB->DevIntClr = 0xFFFFFFFF; // clear all pending interrupt + LPC_USB->EpIntEn = 0x03UL; // control endpoint cannot use DMA, non-control all use DMA + LPC_USB->EpIntPri = 0x03UL; // fast for control endpoint + + // step 8 : DMA set up + LPC_USB->EpDMADis = 0xFFFFFFFF; // firstly disable all dma + LPC_USB->DMARClr = 0xFFFFFFFF; // clear all pending interrupt + LPC_USB->EoTIntClr = 0xFFFFFFFF; + LPC_USB->NDDRIntClr = 0xFFFFFFFF; + LPC_USB->SysErrIntClr = 0xFFFFFFFF; + + tu_memclr(&_dcd, sizeof(dcd_data_t)); +} + +bool dcd_init(uint8_t rhport) +{ + (void) rhport; + + //------------- user manual 11.13 usb device controller initialization -------------// + // step 6 : set up control endpoint + set_ep_size(0, CFG_TUD_ENDOINT0_SIZE); + set_ep_size(1, CFG_TUD_ENDOINT0_SIZE); + + bus_reset(); + + LPC_USB->DevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_FAST_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK); + LPC_USB->UDCAH = (uint32_t) _dcd.udca; + LPC_USB->DMAIntEn = (DMA_INT_END_OF_XFER_MASK /*| DMA_INT_NEW_DD_REQUEST_MASK*/ | DMA_INT_ERROR_MASK); + + sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1); // connect + + // USB IRQ priority should be set by application previously + NVIC_ClearPendingIRQ(USB_IRQn); + NVIC_EnableIRQ(USB_IRQn); + + return TUSB_ERROR_NONE; +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USB_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USB_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + sie_write(SIE_CMDCODE_SET_ADDRESS, 1, 0x80 | dev_addr); // 7th bit is : device_enable +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; + sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1); +} + +//--------------------------------------------------------------------+ +// CONTROL HELPER +//--------------------------------------------------------------------+ +static inline uint8_t byte2dword(uint8_t bytes) +{ + return (bytes + 3) / 4; // length in dwords +} + +static void control_ep_write(void const * buffer, uint8_t len) +{ + uint32_t const * buf32 = (uint32_t const *) buffer; + + LPC_USB->Ctrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0 + LPC_USB->TxPLen = (uint32_t) len; + + for (uint8_t count = 0; count < byte2dword(len); count++) + { + LPC_USB->TxData = *buf32; // NOTE: cortex M3 have no problem with alignment + buf32++; + } + + LPC_USB->Ctrl = 0; + + // select control IN & validate the endpoint + sie_write(SIE_CMDCODE_ENDPOINT_SELECT+1, 0, 0); + sie_write(SIE_CMDCODE_BUFFER_VALIDATE , 0, 0); +} + +static uint8_t control_ep_read(void * buffer, uint8_t len) +{ + LPC_USB->Ctrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0 + while ((LPC_USB->RxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout + + len = tu_min8(len, (uint8_t) (LPC_USB->RxPLen & USBRXPLEN_PACKET_LENGTH_MASK) ); + uint32_t *buf32 = (uint32_t*) buffer; + + for (uint8_t count=0; count < byte2dword(len); count++) + { + *buf32 = LPC_USB->RxData; + buf32++; + } + + LPC_USB->Ctrl = 0; + + // select control OUT & clear the endpoint + sie_write(SIE_CMDCODE_ENDPOINT_SELECT+0, 0, 0); + sie_write(SIE_CMDCODE_BUFFER_CLEAR , 0, 0); + + return len; +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + uint8_t const epnum = edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const ep_id = ep_addr2idx(p_endpoint_desc->bEndpointAddress); + + // Endpoint type is fixed to endpoint number + // 1: interrupt, 2: Bulk, 3: Iso and so on + switch ( p_endpoint_desc->bmAttributes.xfer ) + { + case TUSB_XFER_INTERRUPT: + TU_ASSERT((epnum % 3) == 1); + break; + + case TUSB_XFER_BULK: + TU_ASSERT((epnum % 3) == 2 || (epnum == 15)); + break; + + case TUSB_XFER_ISOCHRONOUS: + TU_ASSERT((epnum % 3) == 3 && (epnum != 15)); + break; + + default: + break; + } + + //------------- Realize Endpoint with Max Packet Size -------------// + set_ep_size(ep_id, p_endpoint_desc->wMaxPacketSize.size); + + //------------- first DD prepare -------------// + dma_desc_t* const dd = &_dcd.dd[ep_id]; + tu_memclr(dd, sizeof(dma_desc_t)); + + dd->isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0; + dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size; + dd->retired = 1; // invalid at first + + sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id, 1, 0); // clear all endpoint status + + return true; +} + +bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t ep_id = ep_addr2idx( ep_addr ); + return (_dcd.udca[ep_id] != NULL && !_dcd.udca[ep_id]->retired); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + if ( edpt_number(ep_addr) == 0 ) + { + sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+0, 1, SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK); + }else + { + uint8_t ep_id = ep_addr2idx( ep_addr ); + sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, SIE_SET_ENDPOINT_STALLED_MASK); + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + uint8_t ep_id = ep_addr2idx(ep_addr); + + sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0); +} + +bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const ep_state = sie_read(SIE_CMDCODE_ENDPOINT_SELECT + ep_addr2idx(ep_addr)); + return (ep_state & SIE_SELECT_ENDPOINT_STALL_MASK) ? true : false; +} + +static bool control_xact(uint8_t rhport, uint8_t dir, uint8_t * buffer, uint8_t len) +{ + (void) rhport; + + if ( dir ) + { + _dcd.control.in_bytes = len; + control_ep_write(buffer, len); + }else + { + if ( _dcd.control.out_received ) + { + // Already received the DATA OUT packet + _dcd.control.out_received = false; + _dcd.control.out_buffer = NULL; + _dcd.control.out_bytes = 0; + + uint8_t received = control_ep_read(buffer, len); + dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true); + }else + { + _dcd.control.out_buffer = buffer; + _dcd.control.out_bytes = len; + } + } + + return true; +} + +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) +{ + // Control transfer is not DMA support, and must be done in slave mode + if ( edpt_number(ep_addr) == 0 ) + { + return control_xact(rhport, edpt_dir(ep_addr), buffer, (uint8_t) total_bytes); + } + else + { + uint8_t ep_id = ep_addr2idx(ep_addr); + dma_desc_t* dd = &_dcd.dd[ep_id]; + + // Prepare DMA descriptor + // Isochronous & max packet size must be preserved, Other fields of dd should be clear + uint16_t const ep_size = dd->max_packet_size; + uint8_t is_iso = dd->isochronous; + + tu_memclr(dd, sizeof(dma_desc_t)); + dd->isochronous = is_iso; + dd->max_packet_size = ep_size; + dd->buffer = (uint32_t) buffer; + dd->buflen = total_bytes; + + _dcd.udca[ep_id] = dd; + + if ( ep_id % 2 ) + { + // Clear EP interrupt before Enable DMA + LPC_USB->EpIntEn &= ~BIT_(ep_id); + LPC_USB->EpDMAEn = BIT_(ep_id); + + // endpoint IN need to actively raise DMA request + LPC_USB->DMARSet = BIT_(ep_id); + }else + { + // Enable DMA + LPC_USB->EpDMAEn = BIT_(ep_id); + } + + return true; + } +} + +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ + +// handle control xfer (slave mode) +static void control_xfer_isr(uint8_t rhport, uint32_t ep_int_status) +{ + // Control out complete + if ( ep_int_status & BIT_(0) ) + { + bool is_setup = sie_read(SIE_CMDCODE_ENDPOINT_SELECT+0) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK; + + LPC_USB->EpIntClr = BIT_(0); + + if (is_setup) + { + uint8_t setup_packet[8]; + control_ep_read(setup_packet, 8); // TODO read before clear setup above + + dcd_event_setup_received(rhport, setup_packet, true); + } + else if ( _dcd.control.out_buffer ) + { + // software queued transfer previously + uint8_t received = control_ep_read(_dcd.control.out_buffer, _dcd.control.out_bytes); + + _dcd.control.out_buffer = NULL; + _dcd.control.out_bytes = 0; + + dcd_event_xfer_complete(rhport, 0, received, XFER_RESULT_SUCCESS, true); + }else + { + // hardware auto ack packet -> mark as received + _dcd.control.out_received = true; + } + } + + // Control In complete + if ( ep_int_status & BIT_(1) ) + { + LPC_USB->EpIntClr = BIT_(1); + dcd_event_xfer_complete(rhport, TUSB_DIR_IN_MASK, _dcd.control.in_bytes, XFER_RESULT_SUCCESS, true); + } +} + +// handle bus event signal +static void bus_event_isr(uint8_t rhport) +{ + uint8_t const dev_status = sie_read(SIE_CMDCODE_DEVICE_STATUS); + if (dev_status & SIE_DEV_STATUS_RESET_MASK) + { + bus_reset(); + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + } + + if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK) + { + // device is disconnected, require using VBUS (P1_30) + dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); + } + + if (dev_status & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK) + { + if (dev_status & SIE_DEV_STATUS_SUSPEND_MASK) + { + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true); + } + else + { + dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + } + } +} + +// Helper to complete a DMA descriptor for non-control transfer +static void dd_complete_isr(uint8_t rhport, uint8_t ep_id) +{ + dma_desc_t* const dd = &_dcd.dd[ep_id]; + uint8_t result = (dd->status == DD_STATUS_NORMAL || dd->status == DD_STATUS_DATA_UNDERUN) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED; + uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0); + + dcd_event_xfer_complete(rhport, ep_addr, dd->present_count, result, true); +} + +// main USB IRQ handler +void hal_dcd_isr(uint8_t rhport) +{ + uint32_t const dev_int_status = LPC_USB->DevIntSt & LPC_USB->DevIntEn; + LPC_USB->DevIntClr = dev_int_status;// Acknowledge handled interrupt + + // Bus event + if (dev_int_status & DEV_INT_DEVICE_STATUS_MASK) + { + bus_event_isr(rhport); + } + + // Endpoint interrupt + uint32_t const ep_int_status = LPC_USB->EpIntSt & LPC_USB->EpIntEn; + + // Control Endpoint are fast + if (dev_int_status & DEV_INT_ENDPOINT_FAST_MASK) + { + // Note clear USBEpIntClr will also clear the setup received bit --> clear after handle setup packet + // Only clear USBEpIntClr 1 endpoint each, and should wait for CDFULL bit set + control_xfer_isr(rhport, ep_int_status); + } + + // non-control IN are slow + if (dev_int_status & DEV_INT_ENDPOINT_SLOW_MASK) + { + for ( uint8_t ep_id = 3; ep_id < DCD_ENDPOINT_MAX; ep_id += 2 ) + { + if ( BIT_TEST_(ep_int_status, ep_id) ) + { + LPC_USB->EpIntClr = BIT_(ep_id); + + // Clear Ep interrupt for next DMA + LPC_USB->EpIntEn &= ~BIT_(ep_id); + + dd_complete_isr(rhport, ep_id); + } + } + } + + // DMA transfer complete (RAM <-> EP) for Non-Control + // OUT: USB transfer is fully complete + // IN : UBS transfer is still on-going -> enable EpIntEn to know when it is complete + uint32_t const dma_int_status = LPC_USB->DMAIntSt & LPC_USB->DMAIntEn; + if (dma_int_status & DMA_INT_END_OF_XFER_MASK) + { + uint32_t const eot = LPC_USB->EoTIntSt; + LPC_USB->EoTIntClr = eot; // acknowledge interrupt source + + for ( uint8_t ep_id = 2; ep_id < DCD_ENDPOINT_MAX; ep_id++ ) + { + if ( BIT_TEST_(eot, ep_id) ) + { + if ( ep_id & 0x01 ) + { + // IN enable EpInt for end of usb transfer + LPC_USB->EpIntEn |= BIT_(ep_id); + }else + { + // OUT + dd_complete_isr(rhport, ep_id); + } + } + } + } + + // Errors + if ( (dev_int_status & DEV_INT_ERROR_MASK) || (dma_int_status & DMA_INT_ERROR_MASK) ) + { + uint32_t error_status = sie_read(SIE_CMDCODE_READ_ERROR_STATUS); + (void) error_status; + TU_BREAKPOINT(); + } +} + +#endif diff --git a/src/portable/nxp/lpc17xx/dcd_lpc175x_6x.h b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h index d6b76fcd1..d6b76fcd1 100644 --- a/src/portable/nxp/lpc17xx/dcd_lpc175x_6x.h +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h diff --git a/src/portable/nxp/lpc11xx_lpc13xx/dcd_lpc_11uxx_13uxx.h b/src/portable/nxp/lpc17_40/hal_lpc17_40.c index 530fcd808..7509dac58 100644 --- a/src/portable/nxp/lpc11xx_lpc13xx/dcd_lpc_11uxx_13uxx.h +++ b/src/portable/nxp/lpc17_40/hal_lpc17_40.c @@ -1,57 +1,58 @@ -/**************************************************************************/
-/*!
- @file dcd_lpc13xx_12adc.h
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-/** \ingroup group_dcd
- * \defgroup group_dcd_lpc11_13u LPC11uxx LPC13uxx
- * @{ */
-
-#ifndef _TUSB_DCD_LPC13XX_12ADC_H_
-#define _TUSB_DCD_LPC13XX_12ADC_H_
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_DCD_LPC13XX_12ADC_H_ */
-
-/** @} */
+/**************************************************************************/ +/*! + @file hal_lpc175x_6x.c + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This file is part of the tinyusb stack. +*/ +/**************************************************************************/ + +#include "common/tusb_common.h" + +#if (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) + +#include "chip.h" + +void USB_IRQHandler(void) +{ + extern void hal_dcd_isr(uint8_t rhport); + + #if MODE_HOST_SUPPORTED + hal_hcd_isr(0); + #endif + + #if TUSB_OPT_DEVICE_ENABLED + hal_dcd_isr(0); + #endif +} + +#endif diff --git a/src/portable/nxp/lpc17xx/hal_lpc175x_6x.c b/src/portable/nxp/lpc17xx/hal_lpc175x_6x.c deleted file mode 100644 index 13cb178f2..000000000 --- a/src/portable/nxp/lpc17xx/hal_lpc175x_6x.c +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************/
-/*!
- @file hal_lpc175x_6x.c
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-#include "common/tusb_common.h"
-
-#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X
-
-#include "LPC17xx.h"
-#include "lpc17xx_pinsel.h"
-
-void tusb_hal_int_enable(uint8_t rhport)
-{
- (void) rhport; // discard compiler's warning
- NVIC_EnableIRQ(USB_IRQn);
-}
-
-void tusb_hal_int_disable(uint8_t rhport)
-{
- (void) rhport; // discard compiler's warning
- NVIC_DisableIRQ(USB_IRQn);
-}
-
-//--------------------------------------------------------------------+
-// IMPLEMENTATION
-//--------------------------------------------------------------------+
-bool tusb_hal_init(void)
-{
- enum {
- USBCLK_DEVCIE = 0x12, // AHB + Device
- USBCLK_HOST = 0x19, // AHB + Host + OTG (!)
-
- PCONP_PCUSB = BIT_(31)
- };
-
- LPC_SC->PCONP |= PCONP_PCUSB; // enable USB Peripherals
-
- //------------- user manual 11.13 usb device controller initialization -------------//
- PINSEL_ConfigPin( &(PINSEL_CFG_Type) { .Portnum = 0, .Pinnum = 29, .Funcnum = 1} ); // P0.29 as D+
- PINSEL_ConfigPin( &(PINSEL_CFG_Type) { .Portnum = 0, .Pinnum = 30, .Funcnum = 1} ); // P0.30 as D-
-
-#if MODE_HOST_SUPPORTED
- PINSEL_ConfigPin( &(PINSEL_CFG_Type) { .Portnum = 1, .Pinnum = 22, .Funcnum = 2} ); // P1.22 as USB_PWRD
- PINSEL_ConfigPin( &(PINSEL_CFG_Type) { .Portnum = 1, .Pinnum = 19, .Funcnum = 2} ); // P1.19 as USB_PPWR
-
- LPC_USB->USBClkCtrl = USBCLK_HOST;
- while ((LPC_USB->USBClkSt & USBCLK_HOST) != USBCLK_HOST);
- LPC_USB->OTGStCtrl = 0x3;
-#endif
-
-#if TUSB_OPT_DEVICE_ENABLED
- LPC_PINCON->PINSEL4 = bit_set_range(LPC_PINCON->PINSEL4, 18, 19, BIN8(01)); // P2_9 as USB Connect
-
- // P1_30 as VBUS, ignore if it is already in VBUS mode
- if ( !(!BIT_TEST_(LPC_PINCON->PINSEL3, 28) && BIT_TEST_(LPC_PINCON->PINSEL3, 29)) )
- {
- // some board like lpcxpresso1769 does not connect VBUS signal to pin P1_30, this allow those board to overwrite
- // by always pulling P1_30 to high
- PINSEL_ConfigPin( &(PINSEL_CFG_Type) {
- .Portnum = 1, .Pinnum = 30,
- .Funcnum = 2, .Pinmode = PINSEL_PINMODE_PULLDOWN} );
- }
-
- LPC_USB->USBClkCtrl = USBCLK_DEVCIE;
- while ((LPC_USB->USBClkSt & USBCLK_DEVCIE) != USBCLK_DEVCIE);
-#endif
-
- return true;
-}
-
-void USB_IRQHandler(void)
-{
- extern void hal_dcd_isr(uint8_t rhport);
-
- #if MODE_HOST_SUPPORTED
- hal_hcd_isr(0);
- #endif
-
- #if TUSB_OPT_DEVICE_ENABLED
- hal_dcd_isr(0);
- #endif
-}
-
-void check_failed(uint8_t *file, uint32_t line)
-{
- (void) file;
- (void) line;
-}
-
-#endif
diff --git a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 66ca04b71..7f6442f8c 100644 --- a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -1,412 +1,390 @@ -/**************************************************************************/
-/*!
- @file dcd_lpc43xx.c
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-#include "tusb_option.h"
-
-#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_LPC43XX
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
-#include "tusb_hal.h"
-#include "osal/osal.h"
-
-#include "device/dcd.h"
-#include "dcd_lpc43xx.h"
-
-#include "LPC43xx.h"
-#include "lpc43xx_cgu.h"
-
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-typedef struct {
- dcd_qhd_t qhd[DCD_QHD_MAX] ATTR_ALIGNED(64); ///< Must be at 2K alignment
- dcd_qtd_t qtd[DCD_QTD_MAX] ATTR_ALIGNED(32);
-}dcd_data_t;
-
-#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
-CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data0;
-#endif
-
-#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
-CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data1;
-#endif
-
-static LPC_USB0_Type * const LPC_USB[2] = { LPC_USB0, ((LPC_USB0_Type*) LPC_USB1_BASE) };
-
-static dcd_data_t* const dcd_data_ptr[2] =
-{
-#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
- &dcd_data0,
-#else
- NULL,
-#endif
-
-#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
- &dcd_data1
-#else
- NULL
-#endif
-};
-
-//--------------------------------------------------------------------+
-// CONTROLLER API
-//--------------------------------------------------------------------+
-void dcd_connect(uint8_t rhport)
-{
- LPC_USB[rhport]->USBCMD_D |= BIT_(0);
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- LPC_USB[rhport]->DEVICEADDR = (dev_addr << 25) | BIT_(24);
-}
-
-void dcd_set_config(uint8_t rhport, uint8_t config_num)
-{
-
-}
-
-/// follows LPC43xx User Manual 23.10.3
-static void bus_reset(uint8_t rhport)
-{
- LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
-
- // The reset value for all endpoint types is the control endpoint. If one endpoint
- // direction is enabled and the paired endpoint of opposite direction is disabled, then the
- // endpoint type of the unused direction must bechanged from the control type to any other
- // type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior
- // for the data PID tracking on the active endpoint.
- lpc_usb->ENDPTCTRL1 = lpc_usb->ENDPTCTRL2 = lpc_usb->ENDPTCTRL3 = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
-
- // USB1 only has 3 non-control endpoints
- if ( rhport == 0)
- {
- lpc_usb->ENDPTCTRL4 = lpc_usb->ENDPTCTRL5 = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
- }
-
- //------------- Clear All Registers -------------//
- lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK;
- lpc_usb->ENDPTNAKEN = 0;
- lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
- lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;
- lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE;
-
- while (lpc_usb->ENDPTPRIME);
- lpc_usb->ENDPTFLUSH = 0xFFFFFFFF;
- while (lpc_usb->ENDPTFLUSH);
-
- // read reset bit in portsc
-
- //------------- Queue Head & Queue TD -------------//
- dcd_data_t* p_dcd = dcd_data_ptr[rhport];
-
- tu_memclr(p_dcd, sizeof(dcd_data_t));
-
- //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
- p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1;
- p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDOINT0_SIZE;
- p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
-
- p_dcd->qhd[0].int_on_setup = 1; // OUT only
-
-}
-
-bool dcd_init(uint8_t rhport)
-{
- LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
- dcd_data_t* p_dcd = dcd_data_ptr[rhport];
-
- tu_memclr(p_dcd, sizeof(dcd_data_t));
-
- lpc_usb->ENDPOINTLISTADDR = (uint32_t) p_dcd->qhd; // Endpoint List Address has to be 2K alignment
- lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
- lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF;
-
- lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0
- lpc_usb->USBCMD_D |= BIT_(0); // connect
-
- // enable interrupt
- NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
-
- return true;
-}
-
-//--------------------------------------------------------------------+
-// HELPER
-//--------------------------------------------------------------------+
-// index to bit position in register
-static inline uint8_t ep_idx2bit(uint8_t ep_idx)
-{
- return ep_idx/2 + ( (ep_idx%2) ? 16 : 0);
-}
-
-static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
-{
- tu_memclr(p_qtd, sizeof(dcd_qtd_t));
-
- p_qtd->next = QTD_NEXT_INVALID;
- p_qtd->active = 1;
- p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
-
- if (data_ptr != NULL)
- {
- p_qtd->buffer[0] = (uint32_t) data_ptr;
- for(uint8_t i=1; i<5; i++)
- {
- p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
- }
- }
-}
-
-static inline volatile uint32_t * get_endpt_ctrl_reg(uint8_t rhport, uint8_t ep_idx)
-{
- return &(LPC_USB[rhport]->ENDPTCTRL0) + ep_idx/2;
-}
-
-//--------------------------------------------------------------------+
-// DCD Endpoint Port
-//--------------------------------------------------------------------+
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = edpt_number(ep_addr);
- uint8_t const dir = edpt_dir(ep_addr);
- uint8_t const ep_idx = 2*epnum + dir;
-
- volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
-
- if ( epnum == 0)
- {
- // Stall both Control IN and OUT
- (*endpt_ctrl) |= ( (ENDPTCTRL_MASK_STALL << 16) || (ENDPTCTRL_MASK_STALL << 0) );
- }else
- {
- (*endpt_ctrl) |= ENDPTCTRL_MASK_STALL << (ep_idx & 0x01 ? 16 : 0);
- }
-}
-
-// TOOD implement later
-bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
-{
- return false;
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = edpt_number(ep_addr);
- uint8_t const dir = edpt_dir(ep_addr);
- uint8_t const ep_idx = 2*epnum + dir;
-
- volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
-
- // data toggle also need to be reset
- (*endpt_ctrl) |= ENDPTCTRL_MASK_TOGGLE_RESET << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0);
- (*endpt_ctrl) &= ~(ENDPTCTRL_MASK_STALL << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0));
-}
-
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
- // TODO USB1 only has 4 non-control enpoint (USB0 has 5)
- // TODO not support ISO yet
- TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
-
- uint8_t const epnum = edpt_number(p_endpoint_desc->bEndpointAddress);
- uint8_t const dir = edpt_dir(p_endpoint_desc->bEndpointAddress);
- uint8_t const ep_idx = 2*epnum + dir;
-
- //------------- Prepare Queue Head -------------//
- dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
- tu_memclr(p_qhd, sizeof(dcd_qhd_t));
-
- p_qhd->zero_length_termination = 1;
- p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
- p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
-
- //------------- Endpoint Control Register -------------//
- volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
-
- // endpoint must not be already enabled
- TU_VERIFY( !( (*endpt_ctrl) & (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)) ) );
-
- (*endpt_ctrl) |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
-
- return true;
-}
-
-bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = edpt_number(ep_addr);
- uint8_t const dir = edpt_dir(ep_addr);
- uint8_t const ep_idx = 2*epnum + dir;
-
- dcd_qhd_t const * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
- dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
-
- return p_qtd->active;
-// return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
- uint8_t const epnum = edpt_number(ep_addr);
- uint8_t const dir = edpt_dir(ep_addr);
- uint8_t const ep_idx = 2*epnum + dir;
-
- if ( epnum == 0 )
- {
- // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
- // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
- while(LPC_USB[rhport]->ENDPTSETUPSTAT & BIT_(0)) {}
- }
-
- dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
- dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
-
- //------------- Prepare qtd -------------//
- qtd_init(p_qtd, buffer, total_bytes);
- p_qtd->int_on_complete = true;
- p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
-
- // start transfer
- LPC_USB[rhport]->ENDPTPRIME = BIT_( ep_idx2bit(ep_idx) ) ;
-
- return true;
-}
-
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-void hal_dcd_isr(uint8_t rhport)
-{
- LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
-
- uint32_t const int_enable = lpc_usb->USBINTR_D;
- uint32_t const int_status = lpc_usb->USBSTS_D & int_enable;
- lpc_usb->USBSTS_D = int_status; // Acknowledge handled interrupt
-
- if (int_status == 0) return;// disabled interrupt sources
-
-
- if (int_status & INT_MASK_RESET)
- {
- bus_reset(rhport);
-
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
- dcd_event_handler(&event, true);
- }
-
- if (int_status & INT_MASK_SUSPEND)
- {
- if (lpc_usb->PORTSC1_D & PORTSC_SUSPEND_MASK)
- { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
- if ((lpc_usb->DEVICEADDR >> 25) & 0x0f)
- {
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPENDED };
- dcd_event_handler(&event, true);
- }
- }
- }
-
- // TODO disconnection does not generate interrupt !!!!!!
-// if (int_status & INT_MASK_PORT_CHANGE)
-// {
-// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) )
-// {
-// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
-// dcd_event_handler(&event, true);
-// }
-// }
-
- if (int_status & INT_MASK_USB)
- {
- uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE;
- lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge
-
- dcd_data_t* const p_dcd = dcd_data_ptr[rhport];
-
- if (lpc_usb->ENDPTSETUPSTAT)
- {
- //------------- Set up Received -------------//
- // 23.10.10.2 Operational model for setup transfers
- lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge
-
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
- event.setup_received = p_dcd->qhd[0].setup_request;
-
- dcd_event_handler(&event, true);
- }
-
- if ( edpt_complete )
- {
- for(uint8_t ep_idx = 0; ep_idx < DCD_QHD_MAX; ep_idx++)
- {
- if ( BIT_TEST_(edpt_complete, ep_idx2bit(ep_idx)) )
- {
- // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
- dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
- dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
-
- uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
- ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
-
- uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 );
- dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
- }
- }
- }
- }
-
- if (int_status & INT_MASK_SOF)
- {
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF };
- dcd_event_handler(&event, true);
- }
-
- if (int_status & INT_MASK_NAK) {}
- if (int_status & INT_MASK_ERROR) TU_ASSERT(false, );
-}
-
-#endif
+/**************************************************************************/ +/*! + @file dcd_lpc43xx.c + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This file is part of the tinyusb stack. +*/ +/**************************************************************************/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "tusb_hal.h" + +#include "device/dcd.h" +#include "dcd_lpc18_43.h" + +#include "chip.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +#define QHD_MAX 12 +#define QTD_NEXT_INVALID 0x01 + +typedef struct { + // Must be at 2K alignment + dcd_qhd_t qhd[QHD_MAX] ATTR_ALIGNED(64); + dcd_qtd_t qtd[QHD_MAX] ATTR_ALIGNED(32); +}dcd_data_t; + +#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) +CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data0; +#endif + +#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) +CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data1; +#endif + +static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; + +static dcd_data_t* const dcd_data_ptr[2] = +{ +#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) + &dcd_data0, +#else + NULL, +#endif + +#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) + &dcd_data1 +#else + NULL +#endif +}; + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + LPC_USB[rhport]->DEVICEADDR = (dev_addr << 25) | BIT_(24); +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + // nothing to do +} + +/// follows LPC43xx User Manual 23.10.3 +static void bus_reset(uint8_t rhport) +{ + LPC_USBHS_T* lpc_usb = LPC_USB[rhport]; + + // The reset value for all endpoint types is the control endpoint. If one endpoint + // direction is enabled and the paired endpoint of opposite direction is disabled, then the + // endpoint type of the unused direction must bechanged from the control type to any other + // type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior + // for the data PID tracking on the active endpoint. + + // USB0 has 5 but USB1 only has 3 non-control endpoints + for( int i=1; i < (rhport ? 6 : 4); i++) + { + lpc_usb->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); + } + + //------------- Clear All Registers -------------// + lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK; + lpc_usb->ENDPTNAKEN = 0; + lpc_usb->USBSTS_D = lpc_usb->USBSTS_D; + lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT; + lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE; + + while (lpc_usb->ENDPTPRIME); + lpc_usb->ENDPTFLUSH = 0xFFFFFFFF; + while (lpc_usb->ENDPTFLUSH); + + // read reset bit in portsc + + //------------- Queue Head & Queue TD -------------// + dcd_data_t* p_dcd = dcd_data_ptr[rhport]; + tu_memclr(p_dcd, sizeof(dcd_data_t)); + + //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// + p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1; + p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDOINT0_SIZE; + p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; + + p_dcd->qhd[0].int_on_setup = 1; // OUT only +} + +bool dcd_init(uint8_t rhport) +{ + LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; + dcd_data_t* p_dcd = dcd_data_ptr[rhport]; + + tu_memclr(p_dcd, sizeof(dcd_data_t)); + + lpc_usb->ENDPOINTLISTADDR = (uint32_t) p_dcd->qhd; // Endpoint List Address has to be 2K alignment + lpc_usb->USBSTS_D = lpc_usb->USBSTS_D; + lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF; + + lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0 + lpc_usb->USBCMD_D |= BIT_(0); // connect + + return true; +} + +void dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn); +} + +//--------------------------------------------------------------------+ +// HELPER +//--------------------------------------------------------------------+ +// index to bit position in register +static inline uint8_t ep_idx2bit(uint8_t ep_idx) +{ + return ep_idx/2 + ( (ep_idx%2) ? 16 : 0); +} + +static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) +{ + tu_memclr(p_qtd, sizeof(dcd_qtd_t)); + + p_qtd->next = QTD_NEXT_INVALID; + p_qtd->active = 1; + p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; + + if (data_ptr != NULL) + { + p_qtd->buffer[0] = (uint32_t) data_ptr; + for(uint8_t i=1; i<5; i++) + { + p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; + } + } +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = edpt_number(ep_addr); + uint8_t const dir = edpt_dir(ep_addr); + + if ( epnum == 0) + { + // Stall both Control IN and OUT + LPC_USB[rhport]->ENDPTCTRL[epnum] |= ( (ENDPTCTRL_MASK_STALL << 16) || (ENDPTCTRL_MASK_STALL << 0) ); + }else + { + LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_STALL << (dir ? 16 : 0); + } +} + +bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = edpt_number(ep_addr); + uint8_t const dir = edpt_dir(ep_addr); + + return LPC_USB[rhport]->ENDPTCTRL[epnum] & (ENDPTCTRL_MASK_STALL << (dir ? 16 : 0)); +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = edpt_number(ep_addr); + uint8_t const dir = edpt_dir(ep_addr); + + // data toggle also need to be reset + LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_TOGGLE_RESET << ( dir ? 16 : 0 ); + LPC_USB[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_MASK_STALL << ( dir ? 16 : 0)); +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + // TODO not support ISO yet + TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); + + uint8_t const epnum = edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const dir = edpt_dir(p_endpoint_desc->bEndpointAddress); + uint8_t const ep_idx = 2*epnum + dir; + + // USB0 has 5, USB1 has 3 non-control endpoints + TU_ASSERT( epnum <= (rhport ? 3 : 5) ); + + //------------- Prepare Queue Head -------------// + dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; + tu_memclr(p_qhd, sizeof(dcd_qhd_t)); + + p_qhd->zero_length_termination = 1; + p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; + p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; + + // Enable EP Control + LPC_USB[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0); + + return true; +} + +bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = edpt_number(ep_addr); + uint8_t const dir = edpt_dir(ep_addr); + uint8_t const ep_idx = 2*epnum + dir; + + dcd_qhd_t const * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; + dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx]; + + return p_qtd->active; +// return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = edpt_number(ep_addr); + uint8_t const dir = edpt_dir(ep_addr); + uint8_t const ep_idx = 2*epnum + dir; + + if ( epnum == 0 ) + { + // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism + // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out + while(LPC_USB[rhport]->ENDPTSETUPSTAT & BIT_(0)) {} + } + + dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; + dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx]; + + //------------- Prepare qtd -------------// + qtd_init(p_qtd, buffer, total_bytes); + p_qtd->int_on_complete = true; + p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd + + // start transfer + LPC_USB[rhport]->ENDPTPRIME = BIT_( ep_idx2bit(ep_idx) ) ; + + return true; +} + +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ +void hal_dcd_isr(uint8_t rhport) +{ + LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; + + uint32_t const int_enable = lpc_usb->USBINTR_D; + uint32_t const int_status = lpc_usb->USBSTS_D & int_enable; + lpc_usb->USBSTS_D = int_status; // Acknowledge handled interrupt + + if (int_status == 0) return;// disabled interrupt sources + + + if (int_status & INT_MASK_RESET) + { + bus_reset(rhport); + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + } + + if (int_status & INT_MASK_SUSPEND) + { + if (lpc_usb->PORTSC1_D & PORTSC_SUSPEND_MASK) + { + // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. + if ((lpc_usb->DEVICEADDR >> 25) & 0x0f) + { + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true); + } + } + } + + // TODO disconnection does not generate interrupt !!!!!! +// if (int_status & INT_MASK_PORT_CHANGE) +// { +// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) ) +// { +// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; +// dcd_event_handler(&event, true); +// } +// } + + if (int_status & INT_MASK_USB) + { + uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE; + lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge + + dcd_data_t* const p_dcd = dcd_data_ptr[rhport]; + + if (lpc_usb->ENDPTSETUPSTAT) + { + //------------- Set up Received -------------// + // 23.10.10.2 Operational model for setup transfers + lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge + + dcd_event_setup_received(rhport, (uint8_t*) &p_dcd->qhd[0].setup_request, true); + } + + if ( edpt_complete ) + { + for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++) + { + if ( BIT_TEST_(edpt_complete, ep_idx2bit(ep_idx)) ) + { + // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set + dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; + dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx]; + + uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED : + ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; + + uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 ); + dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd + } + } + } + } + + if (int_status & INT_MASK_SOF) + { + dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + } + + if (int_status & INT_MASK_NAK) {} + if (int_status & INT_MASK_ERROR) TU_ASSERT(false, ); +} + +#endif diff --git a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.h b/src/portable/nxp/lpc18_43/dcd_lpc18_43.h index 85334a34d..faf0e3b0f 100644 --- a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.h +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.h @@ -1,160 +1,156 @@ -/**************************************************************************/
-/*!
- @file dcd_lpc43xx.h
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-/** \ingroup group_dcd
- * \defgroup group_dcd_lpc143xx LPC43xx
- * @{ */
-
-#ifndef _TUSB_DCD_LPC43XX_H_
-#define _TUSB_DCD_LPC43XX_H_
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-#define DCD_QHD_MAX 12
-#define DCD_QTD_MAX 12
-
-#define QTD_NEXT_INVALID 0x01
-
-/*---------- ENDPTCTRL ----------*/
-enum {
- ENDPTCTRL_MASK_STALL = BIT_(0),
- ENDPTCTRL_MASK_TOGGLE_INHIBIT = BIT_(5), ///< used for test only
- ENDPTCTRL_MASK_TOGGLE_RESET = BIT_(6),
- ENDPTCTRL_MASK_ENABLE = BIT_(7)
-};
-
-/*---------- USBCMD ----------*/
-enum {
- USBCMD_MASK_RUN_STOP = BIT_(0),
- USBCMD_MASK_RESET = BIT_(1),
- USBCMD_MASK_SETUP_TRIPWIRE = BIT_(13),
- USBCMD_MASK_ADD_QTD_TRIPWIRE = BIT_(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
-};
-// Interrupt Threshold bit 23:16
-
-/*---------- USBSTS, USBINTR ----------*/
-enum {
- INT_MASK_USB = BIT_(0),
- INT_MASK_ERROR = BIT_(1),
- INT_MASK_PORT_CHANGE = BIT_(2),
- INT_MASK_RESET = BIT_(6),
- INT_MASK_SOF = BIT_(7),
- INT_MASK_SUSPEND = BIT_(8),
- INT_MASK_NAK = BIT_(16)
-};
-
-//------------- PORTSC -------------//
-enum {
- PORTSC_CURRENT_CONNECT_STATUS_MASK = BIT_(0),
- PORTSC_FORCE_PORT_RESUME_MASK = BIT_(6),
- PORTSC_SUSPEND_MASK = BIT_(7)
-};
-
-typedef struct
-{
- // Word 0: Next QTD Pointer
- uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
-
- // Word 1: qTQ Token
- uint32_t : 3 ;
- volatile uint32_t xact_err : 1 ;
- uint32_t : 1 ;
- volatile uint32_t buffer_err : 1 ;
- volatile uint32_t halted : 1 ;
- volatile uint32_t active : 1 ;
- uint32_t : 2 ;
- uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
- uint32_t : 3 ;
- uint32_t int_on_complete : 1 ;
- volatile uint32_t total_bytes : 15 ;
- uint32_t : 0 ;
-
- // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
- uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
-
- //------------- DCD Area -------------//
- uint16_t expected_bytes;
- uint8_t reserved[2];
-} dcd_qtd_t;
-
-TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
-
-typedef struct
-{
- // Word 0: Capabilities and Characteristics
- uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
- uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
- uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
- uint32_t : 2 ;
- uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
- uint32_t iso_mult : 2 ; ///<
- uint32_t : 0 ;
-
- // Word 1: Current qTD Pointer
- volatile uint32_t qtd_addr;
-
- // Word 2-9: Transfer Overlay
- volatile dcd_qtd_t qtd_overlay;
-
- // Word 10-11: Setup request (control OUT only)
- volatile tusb_control_request_t setup_request;
-
- //--------------------------------------------------------------------+
- /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
- /// thus there are 16 bytes padding free that we can make use of.
- //--------------------------------------------------------------------+
- uint8_t reserved[16];
-} dcd_qhd_t;
-
-TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
-
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_DCD_LPC43XX_H_ */
-
-/** @} */
+/**************************************************************************/ +/*! + @file dcd_lpc43xx.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2013, hathach (tinyusb.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This file is part of the tinyusb stack. +*/ +/**************************************************************************/ + +/** \ingroup group_dcd + * \defgroup group_dcd_lpc143xx LPC43xx + * @{ */ + +#ifndef _TUSB_DCD_LPC43XX_H_ +#define _TUSB_DCD_LPC43XX_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +/*---------- ENDPTCTRL ----------*/ +enum { + ENDPTCTRL_MASK_STALL = BIT_(0), + ENDPTCTRL_MASK_TOGGLE_INHIBIT = BIT_(5), ///< used for test only + ENDPTCTRL_MASK_TOGGLE_RESET = BIT_(6), + ENDPTCTRL_MASK_ENABLE = BIT_(7) +}; + +/*---------- USBCMD ----------*/ +enum { + USBCMD_MASK_RUN_STOP = BIT_(0), + USBCMD_MASK_RESET = BIT_(1), + USBCMD_MASK_SETUP_TRIPWIRE = BIT_(13), + USBCMD_MASK_ADD_QTD_TRIPWIRE = BIT_(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD +}; +// Interrupt Threshold bit 23:16 + +/*---------- USBSTS, USBINTR ----------*/ +enum { + INT_MASK_USB = BIT_(0), + INT_MASK_ERROR = BIT_(1), + INT_MASK_PORT_CHANGE = BIT_(2), + INT_MASK_RESET = BIT_(6), + INT_MASK_SOF = BIT_(7), + INT_MASK_SUSPEND = BIT_(8), + INT_MASK_NAK = BIT_(16) +}; + +//------------- PORTSC -------------// +enum { + PORTSC_CURRENT_CONNECT_STATUS_MASK = BIT_(0), + PORTSC_FORCE_PORT_RESUME_MASK = BIT_(6), + PORTSC_SUSPEND_MASK = BIT_(7) +}; + +typedef struct +{ + // Word 0: Next QTD Pointer + uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed + + // Word 1: qTQ Token + uint32_t : 3 ; + volatile uint32_t xact_err : 1 ; + uint32_t : 1 ; + volatile uint32_t buffer_err : 1 ; + volatile uint32_t halted : 1 ; + volatile uint32_t active : 1 ; + uint32_t : 2 ; + uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. + uint32_t : 3 ; + uint32_t int_on_complete : 1 ; + volatile uint32_t total_bytes : 15 ; + uint32_t : 0 ; + + // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page + uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous + + //------------- DCD Area -------------// + uint16_t expected_bytes; + uint8_t reserved[2]; +} dcd_qtd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); + +typedef struct +{ + // Word 0: Capabilities and Characteristics + uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. + uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. + uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) + uint32_t : 2 ; + uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. + uint32_t iso_mult : 2 ; ///< + uint32_t : 0 ; + + // Word 1: Current qTD Pointer + volatile uint32_t qtd_addr; + + // Word 2-9: Transfer Overlay + volatile dcd_qtd_t qtd_overlay; + + // Word 10-11: Setup request (control OUT only) + volatile tusb_control_request_t setup_request; + + //--------------------------------------------------------------------+ + /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes + /// thus there are 16 bytes padding free that we can make use of. + //--------------------------------------------------------------------+ + uint8_t reserved[16]; +} dcd_qhd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_LPC43XX_H_ */ + +/** @} */ diff --git a/src/portable/microchip/samd21/hal_samd21.c b/src/portable/nxp/lpc18_43/hal_lpc18_43.c index 9f04630ea..2978bebf6 100644 --- a/src/portable/microchip/samd21/hal_samd21.c +++ b/src/portable/nxp/lpc18_43/hal_lpc18_43.c @@ -1,13 +1,13 @@ /**************************************************************************/ /*! - @file hal_nrf5x.c - @author hathach + @file hal_lpc43xx.c + @author hathach (tinyusb.org) @section LICENSE Software License Agreement (BSD License) - Copyright (c) 2018, hathach (tinyusb.org) + Copyright (c) 2013, hathach (tinyusb.org) All rights reserved. Redistribution and use in source and binary forms, with or without @@ -26,56 +26,48 @@ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This file is part of the tinyusb stack. */ /**************************************************************************/ -#include "tusb_option.h" +#include "tusb.h" -#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD21 +#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX) -#include "sam.h" +#include "chip.h" -#include "tusb_hal.h" +void hal_dcd_isr(uint8_t rhport); - -/*------------------------------------------------------------------*/ -/* TUSB HAL - *------------------------------------------------------------------*/ -bool tusb_hal_init(void) +#if CFG_TUSB_RHPORT0_MODE +void USB0_IRQHandler(void) { - // Reset to get in a clean state. - USB->DEVICE.CTRLA.bit.SWRST = true; - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} - - USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; - USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + #if MODE_HOST_SUPPORTED + hal_hcd_isr(0); + #endif - USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val; - USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val; - - tusb_hal_int_enable(0); - return true; + #if TUSB_OPT_DEVICE_ENABLED + hal_dcd_isr(0); + #endif } +#endif -void tusb_hal_int_enable(uint8_t rhport) +#if CFG_TUSB_RHPORT1_MODE +void USB1_IRQHandler(void) { - (void) rhport; - NVIC_EnableIRQ(USB_IRQn); -} + #if MODE_HOST_SUPPORTED + hal_hcd_isr(1); + #endif -void tusb_hal_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USB_IRQn); + #if TUSB_OPT_DEVICE_ENABLED + hal_dcd_isr(1); + #endif } +#endif #endif diff --git a/src/portable/nxp/lpc43xx_lpc18xx/hal_lpc43xx.c b/src/portable/nxp/lpc43xx_lpc18xx/hal_lpc43xx.c deleted file mode 100644 index 213b8dd99..000000000 --- a/src/portable/nxp/lpc43xx_lpc18xx/hal_lpc43xx.c +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************/
-/*!
- @file hal_lpc43xx.c
- @author hathach (tinyusb.org)
-
- @section LICENSE
-
- Software License Agreement (BSD License)
-
- Copyright (c) 2013, hathach (tinyusb.org)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holders nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This file is part of the tinyusb stack.
-*/
-/**************************************************************************/
-
-#include "tusb.h"
-
-#if CFG_TUSB_MCU == OPT_MCU_LPC43XX
-
-#include "LPC43xx.h"
-#include "lpc43xx_cgu.h"
-
-enum {
- LPC43XX_USBMODE_DEVICE = 2,
- LPC43XX_USBMODE_HOST = 3
-};
-
-enum {
- LPC43XX_USBMODE_VBUS_LOW = 0,
- LPC43XX_USBMODE_VBUS_HIGH = 1
-};
-
-void tusb_hal_int_enable(uint8_t rhport)
-{
- NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
-}
-
-void tusb_hal_int_disable(uint8_t rhport)
-{
- NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
-}
-
-
-static void hal_controller_reset(uint8_t rhport)
-{ // TODO timeout expired to prevent trap
- volatile uint32_t * p_reg_usbcmd;
-
- p_reg_usbcmd = (rhport ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
-// NXP chip powered with non-host mode --> sts bit is not correctly reflected
- (*p_reg_usbcmd) |= BIT_(1);
-
-// tu_timeout_t timeout;
-// tu_timeout_set(&timeout, 2); // should not take longer the time to stop controller
- while( ((*p_reg_usbcmd) & BIT_(1)) /*&& !tu_timeout_expired(&timeout)*/) {}
-//
-// return tu_timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE;
-}
-
-bool tusb_hal_init(void)
-{
- LPC_CREG->CREG0 &= ~(1<<5); /* Turn on the phy */
-
- //------------- USB0 -------------//
-#if CFG_TUSB_RHPORT0_MODE
- CGU_EnableEntity(CGU_CLKSRC_PLL0, DISABLE); /* Disable PLL first */
- TU_VERIFY( CGU_ERROR_SUCCESS == CGU_SetPLL0()); /* the usb core require output clock = 480MHz */
- CGU_EntityConnect(CGU_CLKSRC_XTAL_OSC, CGU_CLKSRC_PLL0);
- CGU_EnableEntity(CGU_CLKSRC_PLL0, ENABLE); /* Enable PLL after all setting is done */
-
- // reset controller & set role
- hal_controller_reset(0);
-
- #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
- LPC_USB0->USBMODE_H = LPC43XX_USBMODE_HOST | (LPC43XX_USBMODE_VBUS_HIGH << 5);
- #else // TODO OTG
- LPC_USB0->USBMODE_D = LPC43XX_USBMODE_DEVICE;
- LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/;
- #if CFG_TUD_FULLSPEED // TODO for easy testing
- LPC_USB0->PORTSC1_D |= (1<<24); // force full speed
- #endif
- #endif
-#endif
-
- //------------- USB1 -------------//
-#if CFG_TUSB_RHPORT1_MODE
- // Host require to config P2_5, TODO confirm whether device mode require P2_5 or not
- scu_pinmux(0x2, 5, MD_PLN | MD_EZI | MD_ZI, FUNC2); // USB1_VBUS monitor presence, must be high for bus reset occur
-
- /* connect CLK_USB1 to 60 MHz clock */
- CGU_EntityConnect(CGU_CLKSRC_PLL1, CGU_BASE_USB1); /* FIXME Run base BASE_USB1_CLK clock from PLL1 (assume PLL1 is 60 MHz, no division required) */
- LPC_SCU->SFSUSB = (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 0x16 : 0x12; // enable USB1 with on-chip FS PHY
-
- hal_controller_reset(1);
-
- #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST
- LPC_USB1->USBMODE_H = LPC43XX_USBMODE_HOST | (LPC43XX_USBMODE_VBUS_HIGH << 5);
- #else // TODO OTG
- LPC_USB1->USBMODE_D = LPC43XX_USBMODE_DEVICE;
- #endif
-
- LPC_USB1->PORTSC1_D |= (1<<24); // TODO abstract, force rhport to fullspeed
-#endif
-
- return true;
-}
-
-void hal_dcd_isr(uint8_t rhport);
-
-#if CFG_TUSB_RHPORT0_MODE
-void USB0_IRQHandler(void)
-{
- #if MODE_HOST_SUPPORTED
- hal_hcd_isr(0);
- #endif
-
- #if TUSB_OPT_DEVICE_ENABLED
- hal_dcd_isr(0);
- #endif
-}
-#endif
-
-#if CFG_TUSB_RHPORT1_MODE
-void USB1_IRQHandler(void)
-{
- #if MODE_HOST_SUPPORTED
- hal_hcd_isr(1);
- #endif
-
- #if TUSB_OPT_DEVICE_ENABLED
- hal_dcd_isr(1);
- #endif
-}
-#endif
-
-
-void check_failed(uint8_t *file, uint32_t line)
-{
- (void) file;
- (void) line;
-}
-
-#endif
diff --git a/src/tusb.c b/src/tusb.c index e9db184f2..81c4907fb 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -47,14 +47,11 @@ static bool _initialized = false;
-
tusb_error_t tusb_init(void)
{
// skip if already initialized
if (_initialized) return TUSB_ERROR_NONE;
- TU_VERIFY( tusb_hal_init(), TUSB_ERROR_FAILED ) ; // hardware init
-
#if MODE_HOST_SUPPORTED
TU_ASSERT_ERR( usbh_init() ); // host stack init
#endif
diff --git a/src/tusb_hal.h b/src/tusb_hal.h index 85a2a8709..301e8c2f2 100644 --- a/src/tusb_hal.h +++ b/src/tusb_hal.h @@ -51,55 +51,37 @@ extern "C" { //--------------------------------------------------------------------+
// HAL API
//--------------------------------------------------------------------+
-/** \ingroup group_mcu
- * \defgroup group_hal Hardware Abtract Layer (HAL)
- * Hardware Abstraction Layer (HAL) is an abstraction layer, between the physical hardware and the tinyusb stack.
- * Its function is to hide differences in hardware from most of MCUs, so that most of the stack code does not need to be changed to
- * run on systems with a different MCU.
- * HAL are sets of routines that emulate some platform-specific details, giving programs direct access to the hardware resources.
- * @{ */
-
-/** \brief Initialize USB controller hardware
- * \returns true if succeeded
- * \note This function is invoked by \ref tusb_init as part of the initialization.
- */
-bool tusb_hal_init(void);
-
-/** \brief Enable USB Interrupt on a specific USB Controller
- * \param[in] rhport is a zero-based index to identify USB controller's ID
- */
-void tusb_hal_int_enable(uint8_t rhport);
-
-/** \brief Disable USB Interrupt on a specific USB Controller
- * \param[in] rhport is a zero-based index to identify USB controller's ID
- */
-void tusb_hal_int_disable(uint8_t rhport);
// Only required to implement if using No RTOS (osal_none)
uint32_t tusb_hal_millis(void);
+// TODO remove
+extern void dcd_int_enable (uint8_t rhport);
+extern void dcd_int_disable(uint8_t rhport);
// Enable all ports' interrupt
+// TODO remove
static inline void tusb_hal_int_enable_all(void)
{
#ifdef CFG_TUSB_RHPORT0_MODE
- tusb_hal_int_enable(0);
+ dcd_int_enable(0);
#endif
#ifdef CFG_TUSB_RHPORT0_MODE
- tusb_hal_int_enable(1);
+ dcd_int_enable(1);
#endif
}
// Disable all ports' interrupt
+// TODO remove
static inline void tusb_hal_int_disable_all(void)
{
#ifdef CFG_TUSB_RHPORT0_MODE
- tusb_hal_int_disable(0);
+ dcd_int_disable(0);
#endif
#ifdef CFG_TUSB_RHPORT0_MODE
- tusb_hal_int_disable(1);
+ dcd_int_disable(1);
#endif
}
@@ -111,4 +93,3 @@ static inline void tusb_hal_int_disable_all(void) #endif /* _TUSB_HAL_H_ */
-/** @} */
diff --git a/src/tusb_option.h b/src/tusb_option.h index e1aec3302..c558b30ff 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -48,13 +48,14 @@ /** \defgroup group_mcu Supported MCU
* \ref CFG_TUSB_MCU must be defined to one of these
* @{ */
-#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx series
-#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx (not supported yet)
-#define OPT_MCU_LPC13UXX 3 ///< NXP LPC13xx 12 bit ADC series
-#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x series
-#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x series (not supported yet)
-#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx series (not supported yet)
-#define OPT_MCU_LPC43XX 7 ///< NXP LPC43xx series
+#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
+
+#define OPT_MCU_LPC13XX 3 ///< NXP LPC13xx
+#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
+#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
+#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
+#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
+#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
@@ -92,7 +93,7 @@ #define OPT_MODE_NONE 0x00 ///< Disabled
#define OPT_MODE_DEVICE 0x01 ///< Device Mode
#define OPT_MODE_HOST 0x02 ///< Host Mode
-#define OPT_MODE_HIGH_SPEED 0x10 ///< Highspeed
+#define OPT_MODE_HIGH_SPEED 0x10 ///< High speed
/** @} */
#ifndef CFG_TUSB_RHPORT0_MODE
@@ -152,6 +153,10 @@ #define CFG_TUSB_MEM_SECTION
#endif
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
+#endif
+
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
@@ -236,7 +241,7 @@ #endif
#if CFG_TUD_ENDOINT0_SIZE > 64
- #error Control Endpoint Max Package Size cannot larger than 64
+ #error Control Endpoint Max Packet Size cannot be larger than 64
#endif
#endif /* _TUSB_OPTION_H_ */
|
