rename subfolder source to src

This commit is contained in:
hathach
2018-06-18 14:05:24 +07:00
parent b96e8c6cef
commit 424735d440
70 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,570 @@
/**************************************************************************/
/*!
@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 MODE_DEVICE_SUPPORTED && (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 "common/timeout_timer.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;
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_ATTR_USBRAM must have ATTR_ALIGNED(64) for lpc11u & lpc13u
#ifdef __ICCARM__
ATTR_ALIGNED(256) CFG_TUSB_ATTR_USBRAM // for IAR the first ATTR_ALIGNED takes effect
#else
CFG_TUSB_ATTR_USBRAM 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)
{
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;
//------------- 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();
dcd_bus_event(0, USBD_BUS_EVENT_RESET);
}
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_bus_event(0, USBD_BUS_EVENT_UNPLUGGED);
}
}
// 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_bus_event(0, USBD_BUS_EVENT_SUSPENDED);
}
}
}
// else
// { // resume signal
// dcd_bus_event(0, USBD_BUS_EVENT_RESUME);
// }
// }
}
//------------- 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
dcd_setup_received(rhport, (uint8_t*)&dcd_data.setup_request);
// 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, tusb_dir_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
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 = min16_of(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

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/*!
@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_ */
/** @} */

View File

@@ -0,0 +1,532 @@
/**************************************************************************/
/*!
@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 MODE_DEVICE_SUPPORTED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "device/dcd.h"
#include "dcd_lpc175x_6x.h"
#include "usbd_dcd.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_QHD_MAX 32
#define DCD_QTD_MAX 32 // TODO scale with configure
typedef struct {
volatile dcd_dma_descriptor_t* udca[DCD_QHD_MAX]; // must be 128 byte aligned
dcd_dma_descriptor_t dd[DCD_QTD_MAX][2]; // each endpoints can have up to 2 DD queued at a time TODO 0-1 are not used, offset to reduce memory
uint8_t class_code[DCD_QHD_MAX];
struct {
uint8_t* p_data;
uint16_t remaining_bytes;
uint8_t int_on_complete;
}control_dma;
}dcd_data_t;
CFG_TUSB_ATTR_USBRAM ATTR_ALIGNED(128) STATIC_VAR dcd_data_t dcd_data;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static void bus_reset(void);
static tusb_error_t pipe_control_read(void * buffer, uint16_t length);
static tusb_error_t pipe_control_write(void const * buffer, uint16_t length);
static tusb_error_t pipe_control_xfer(uint8_t ep_id, uint8_t* p_buffer, uint16_t length);
//--------------------------------------------------------------------+
// 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);
}
static inline void edpt_set_max_packet_size(uint8_t ep_id, uint16_t max_packet_size) ATTR_ALWAYS_INLINE;
static inline void edpt_set_max_packet_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;
#ifndef _TEST_
while ((LPC_USB->USBDevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK) == 0) {} // TODO can be omitted
LPC_USB->USBDevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK;
#endif
}
//--------------------------------------------------------------------+
// USBD-DCD 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 = (uint32_t) BIN8(11); // control endpoint cannot use DMA, non-control all use DMA
LPC_USB->USBEpIntPri = 0; // same priority for all 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;
memclr_(&dcd_data, 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
edpt_set_max_packet_size(0, CFG_TUD_ENDOINT0_SIZE);
edpt_set_max_packet_size(1, CFG_TUD_ENDOINT0_SIZE);
bus_reset();
LPC_USB->USBDevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK);
LPC_USB->USBUDCAH = (uint32_t) dcd_data.udca;
LPC_USB->USBDMAIntEn = (DMA_INT_END_OF_XFER_MASK | DMA_INT_ERROR_MASK );
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1); // connect
NVIC_EnableIRQ(USB_IRQn);
return TUSB_ERROR_NONE;
}
static void endpoint_non_control_isr(uint32_t eot_int)
{
for(uint8_t ep_id = 2; ep_id < DCD_QHD_MAX; ep_id++ )
{
if ( BIT_TEST_(eot_int, ep_id) )
{
dcd_dma_descriptor_t* const p_first_dd = &dcd_data.dd[ep_id][0];
dcd_dma_descriptor_t* const p_last_dd = dcd_data.dd[ep_id] + (p_first_dd->is_next_valid ? 1 : 0); // Maximum is 2 QTD are queued in an endpoint
// only handle when Controller already finished the last DD
if ( dcd_data.udca[ep_id] == p_last_dd )
{
dcd_data.udca[ep_id] = p_first_dd; // UDCA currently points to the last DD, change to the fixed DD
p_first_dd->buffer_length = 0; // buffer length is used to determined if first dd is queued in pipe xfer function
if ( p_last_dd->int_on_complete )
{
edpt_hdl_t edpt_hdl =
{
.rhport = 0,
.index = ep_id,
.class_code = dcd_data.class_code[ep_id]
};
bool succeeded = (p_last_dd->status == DD_STATUS_NORMAL || p_last_dd->status == DD_STATUS_DATA_UNDERUN) ? true : false;
dcd_xfer_complete(edpt_hdl, p_last_dd->present_count, succeeded); // report only xferred bytes in the IOC qtd
}
}
}
}
}
static void endpoint_control_isr(void)
{
uint32_t const interrupt_enable = LPC_USB->USBEpIntEn;
uint32_t const endpoint_int_status = LPC_USB->USBEpIntSt & interrupt_enable;
// LPC_USB->USBEpIntClr = endpoint_int_status; // acknowledge interrupt TODO cannot immediately acknowledge setup packet
//------------- Setup Recieved-------------//
if ( (endpoint_int_status & BIT_(0)) &&
(sie_read(SIE_CMDCODE_ENDPOINT_SELECT+0, 1) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK) )
{
(void) sie_read(SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT+0, 1); // clear setup bit
tusb_control_request_t control_request;
pipe_control_read(&control_request, 8); // TODO read before clear setup above
dcd_setup_received(0, (uint8_t*) &control_request);
}
else if (endpoint_int_status & 0x03)
{
uint8_t const ep_id = ( endpoint_int_status & BIT_(0) ) ? 0 : 1;
if ( dcd_data.control_dma.remaining_bytes > 0 )
{ // there are still data to transfer
pipe_control_xfer(ep_id, dcd_data.control_dma.p_data, dcd_data.control_dma.remaining_bytes);
}
else
{
dcd_data.control_dma.remaining_bytes = 0;
if ( BIT_TEST_(dcd_data.control_dma.int_on_complete, ep_id) )
{
edpt_hdl_t edpt_hdl = { .rhport = 0, .class_code = 0 };
dcd_data.control_dma.int_on_complete = 0;
// FIXME xferred_byte for control xfer is not needed now !!!
dcd_xfer_complete(edpt_hdl, 0, true);
}
}
}
LPC_USB->USBEpIntClr = endpoint_int_status; // acknowledge interrupt TODO cannot immediately acknowledge setup packet
}
void hal_dcd_isr(uint8_t rhport)
{
(void) rhport;
uint32_t const device_int_enable = LPC_USB->USBDevIntEn;
uint32_t const device_int_status = LPC_USB->USBDevIntSt & device_int_enable;
LPC_USB->USBDevIntClr = device_int_status;// Acknowledge handled interrupt
//------------- usb bus event -------------//
if (device_int_status & DEV_INT_DEVICE_STATUS_MASK)
{
uint8_t const dev_status_reg = sie_read(SIE_CMDCODE_DEVICE_STATUS, 1);
if (dev_status_reg & SIE_DEV_STATUS_RESET_MASK)
{
bus_reset();
dcd_bus_event(0, USBD_BUS_EVENT_RESET);
}
if (dev_status_reg & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)
{ // device is disconnected, require using VBUS (P1_30)
dcd_bus_event(0, USBD_BUS_EVENT_UNPLUGGED);
}
if (dev_status_reg & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
{
if (dev_status_reg & SIE_DEV_STATUS_SUSPEND_MASK)
{
dcd_bus_event(0, USBD_BUS_EVENT_SUSPENDED);
}
// else
// {
// dcd_bus_event(0, USBD_BUS_EVENT_RESUME);
// }
}
}
//------------- Control Endpoint (Slave Mode) -------------//
if (device_int_status & DEV_INT_ENDPOINT_SLOW_MASK)
{
endpoint_control_isr();
}
//------------- Non-Control Endpoint (DMA Mode) -------------//
uint32_t const dma_int_enable = LPC_USB->USBDMAIntEn;
uint32_t const dma_int_status = LPC_USB->USBDMAIntSt & dma_int_enable;
if (dma_int_status & DMA_INT_END_OF_XFER_MASK)
{
uint32_t eot_int = LPC_USB->USBEoTIntSt;
LPC_USB->USBEoTIntClr = eot_int; // acknowledge interrupt source
endpoint_non_control_isr(eot_int);
}
if (device_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_ASSERT(false, (void) 0);
}
}
//--------------------------------------------------------------------+
// USBD API - CONTROLLER
//--------------------------------------------------------------------+
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);
}
//--------------------------------------------------------------------+
// PIPE CONTROL HELPER
//--------------------------------------------------------------------+
static inline uint16_t length_byte2dword(uint16_t length_in_bytes) ATTR_ALWAYS_INLINE ATTR_CONST;
static inline uint16_t length_byte2dword(uint16_t length_in_bytes)
{
return (length_in_bytes + 3) / 4; // length_in_dword
}
static tusb_error_t pipe_control_xfer(uint8_t ep_id, uint8_t* p_buffer, uint16_t length)
{
uint16_t const packet_len = min16_of(length, CFG_TUD_ENDOINT0_SIZE);
if (ep_id)
{
TU_ASSERT_ERR ( pipe_control_write(p_buffer, packet_len) );
}else
{
TU_ASSERT_ERR ( pipe_control_read(p_buffer, packet_len) );
}
dcd_data.control_dma.remaining_bytes -= packet_len;
dcd_data.control_dma.p_data += packet_len;
return TUSB_ERROR_NONE;
}
static tusb_error_t pipe_control_write(void const * buffer, uint16_t length)
{
uint32_t const * p_write_data = (uint32_t const *) buffer;
LPC_USB->USBCtrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0
LPC_USB->USBTxPLen = length;
for (uint16_t count = 0; count < length_byte2dword(length); count++)
{
LPC_USB->USBTxData = *p_write_data; // NOTE: cortex M3 have no problem with alignment
p_write_data++;
}
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);
return TUSB_ERROR_NONE;
}
static tusb_error_t pipe_control_read(void * buffer, uint16_t length)
{
LPC_USB->USBCtrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0
while ((LPC_USB->USBRxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout
uint16_t actual_length = min16_of(length, (uint16_t) (LPC_USB->USBRxPLen & USBRXPLEN_PACKET_LENGTH_MASK) );
uint32_t *p_read_data = (uint32_t*) buffer;
for( uint16_t count=0; count < length_byte2dword(actual_length); count++)
{
*p_read_data = LPC_USB->USBRxData;
p_read_data++; // increase by 4 ( sizeof(uint32_t) )
}
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 TUSB_ERROR_NONE;
}
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
void dcd_control_stall(uint8_t rhport)
{
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+0, 1, SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK);
}
bool dcd_control_xfer(uint8_t rhport, tusb_dir_t dir, uint8_t * p_buffer, uint16_t length, bool int_on_complete)
{
(void) rhport;
VERIFY( !(length != 0 && p_buffer == NULL) );
// 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.control_dma.int_on_complete = int_on_complete ? BIT_(ep_status) : 0;
//------------- Data Phase -------------//
if ( length )
{
dcd_data.control_dma.p_data = (uint8_t*) p_buffer;
dcd_data.control_dma.remaining_bytes = length;
// lpc17xx already received the first DATA OUT packet by now
VERIFY_ERR ( pipe_control_xfer(ep_data, p_buffer, length), false );
}
//------------- Status Phase (opposite direct to Data) -------------//
if (dir == TUSB_DIR_OUT)
{ // only write for CONTROL OUT, CONTROL IN data will be retrieved in hal_dcd_isr // TODO ????
VERIFY_ERR ( pipe_control_write(NULL, 0), false );
}
return true;
}
//--------------------------------------------------------------------+
// BULK/INTERRUPT/ISO PIPE API
//--------------------------------------------------------------------+
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 };
// TODO refractor to universal pipe open validation function
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
uint8_t ep_id = edpt_addr2phy( p_endpoint_desc->bEndpointAddress );
//------------- Realize Endpoint with Max Packet Size -------------//
edpt_set_max_packet_size(ep_id, p_endpoint_desc->wMaxPacketSize.size);
dcd_data.class_code[ep_id] = class_code;
//------------- first DD prepare -------------//
dcd_dma_descriptor_t* const p_dd = &dcd_data.dd[ep_id][0];
memclr_(p_dd, sizeof(dcd_dma_descriptor_t));
p_dd->is_isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
p_dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
p_dd->is_retired = 1; // inactive at first
dcd_data.udca[ ep_id ] = p_dd; // hook to UDCA
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0); // clear all endpoint status
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.udca[edpt_hdl.index] != NULL && !dcd_data.udca[edpt_hdl.index]->is_retired);
}
void dcd_edpt_stall(edpt_hdl_t edpt_hdl)
{
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+edpt_hdl.index, 1, SIE_SET_ENDPOINT_STALLED_MASK);
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t ep_id = ep_addr2phy(ep_addr);
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0);
}
void dd_xfer_init(dcd_dma_descriptor_t* p_dd, void* buffer, uint16_t total_bytes)
{
p_dd->next = 0;
p_dd->is_next_valid = 0;
p_dd->buffer_addr = (uint32_t) buffer;
p_dd->buffer_length = total_bytes;
p_dd->status = DD_STATUS_NOT_SERVICED;
p_dd->iso_last_packet_valid = 0;
p_dd->present_count = 0;
}
tusb_error_t dcd_edpt_queue_xfer(edpt_hdl_t edpt_hdl, uint8_t * buffer, uint16_t total_bytes)
{ // NOTE for sure the qhd has no dds
dcd_dma_descriptor_t* const p_fixed_dd = &dcd_data.dd[edpt_hdl.index][0]; // always queue with the fixed DD
dd_xfer_init(p_fixed_dd, buffer, total_bytes);
p_fixed_dd->is_retired = 1;
p_fixed_dd->int_on_complete = 0;
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)
{
dcd_dma_descriptor_t* const p_first_dd = &dcd_data.dd[edpt_hdl.index][0];
//------------- fixed DD is already queued a xfer -------------//
if ( p_first_dd->buffer_length )
{
// setup new dd
dcd_dma_descriptor_t* const p_dd = &dcd_data.dd[ edpt_hdl.index ][1];
memclr_(p_dd, sizeof(dcd_dma_descriptor_t));
dd_xfer_init(p_dd, buffer, total_bytes);
p_dd->max_packet_size = p_first_dd->max_packet_size;
p_dd->is_isochronous = p_first_dd->is_isochronous;
p_dd->int_on_complete = int_on_complete;
// hook to fixed dd
p_first_dd->next = (uint32_t) p_dd;
p_first_dd->is_next_valid = 1;
}
//------------- fixed DD is free -------------//
else
{
dd_xfer_init(p_first_dd, buffer, total_bytes);
p_first_dd->int_on_complete = int_on_complete;
}
p_first_dd->is_retired = 0; // activate xfer
dcd_data.udca[edpt_hdl.index] = p_first_dd;
LPC_USB->USBEpDMAEn = BIT_(edpt_hdl.index);
if ( edpt_hdl.index % 2 )
{ // endpoint IN need to actively raise DMA request
LPC_USB->USBDMARSet = BIT_(edpt_hdl.index);
}
return TUSB_ERROR_NONE;
}
#endif

View File

@@ -0,0 +1,240 @@
/**************************************************************************/
/*!
@file dcd_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.
*/
/**************************************************************************/
/** \ingroup group_dcd
* \defgroup group_dcd_lpc175x_6x LPC175x_6x
* @{ */
#ifndef _TUSB_DCD_LPC175X_6X_H_
#define _TUSB_DCD_LPC175X_6X_H_
#include <common/tusb_common.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ATTR_ALIGNED(4)
{
//------------- Word 0 -------------//
uint32_t next;
//------------- Word 1 -------------//
uint16_t mode : 2; // either 00 normal or 01 ATLE(auto length extraction)
uint16_t is_next_valid : 1;
uint16_t int_on_complete : 1; ///< make use of reserved bit
uint16_t is_isochronous : 1; // is an iso endpoint
uint16_t max_packet_size : 11;
volatile uint16_t buffer_length;
//------------- Word 2 -------------//
volatile uint32_t buffer_addr;
//------------- Word 3 -------------//
volatile uint16_t is_retired : 1; // initialized to zero
volatile uint16_t status : 4;
volatile uint16_t iso_last_packet_valid : 1;
volatile uint16_t atle_is_lsb_extracted : 1; // used in ATLE mode
volatile uint16_t atle_is_msb_extracted : 1; // used in ATLE mode
volatile uint16_t atle_message_length_position : 6; // used in ATLE mode
uint16_t : 2;
volatile uint16_t present_count; // The number of bytes transferred by the DMA engine. The DMA engine updates this field after completing each packet transfer.
//------------- Word 4 -------------//
// uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso
}dcd_dma_descriptor_t;
VERIFY_STATIC( sizeof(dcd_dma_descriptor_t) == 16, "size is not correct"); // TODO not support ISO for now
//--------------------------------------------------------------------+
// Register Interface
//--------------------------------------------------------------------+
//------------- USB Interrupt USBIntSt -------------//
//enum {
// DCD_USB_REQ_LOW_PRIO_MASK = BIT_(0),
// DCD_USB_REQ_HIGH_PRIO_MASK = BIT_(1),
// DCD_USB_REQ_DMA_MASK = BIT_(2),
// DCD_USB_REQ_NEED_CLOCK_MASK = BIT_(8),
// DCD_USB_REQ_ENABLE_MASK = BIT_(31)
//};
//------------- Device Interrupt USBDevInt -------------//
enum {
DEV_INT_FRAME_MASK = BIT_(0),
DEV_INT_ENDPOINT_FAST_MASK = BIT_(1),
DEV_INT_ENDPOINT_SLOW_MASK = BIT_(2),
DEV_INT_DEVICE_STATUS_MASK = BIT_(3),
DEV_INT_COMMAND_CODE_EMPTY_MASK = BIT_(4),
DEV_INT_COMMAND_DATA_FULL_MASK = BIT_(5),
DEV_INT_RX_ENDPOINT_PACKET_MASK = BIT_(6),
DEV_INT_TX_ENDPOINT_PACKET_MASK = BIT_(7),
DEV_INT_ENDPOINT_REALIZED_MASK = BIT_(8),
DEV_INT_ERROR_MASK = BIT_(9)
};
//------------- DMA Interrupt USBDMAInt-------------//
enum {
DMA_INT_END_OF_XFER_MASK = BIT_(0),
DMA_INT_NEW_DD_REQUEST_MASK = BIT_(1),
DMA_INT_ERROR_MASK = BIT_(2)
};
//------------- USBCtrl -------------//
enum {
USBCTRL_READ_ENABLE_MASK = BIT_(0),
USBCTRL_WRITE_ENABLE_MASK = BIT_(1),
};
//------------- USBRxPLen -------------//
enum {
USBRXPLEN_PACKET_LENGTH_MASK = (BIT_(10)-1),
USBRXPLEN_DATA_VALID_MASK = BIT_(10),
USBRXPLEN_PACKET_READY_MASK = BIT_(11),
};
//------------- SIE Command Code -------------//
typedef enum
{
SIE_CMDPHASE_WRITE = 1,
SIE_CMDPHASE_READ = 2,
SIE_CMDPHASE_COMMAND = 5
} sie_cmdphase_t;
enum {
// device commands
SIE_CMDCODE_SET_ADDRESS = 0xd0,
SIE_CMDCODE_CONFIGURE_DEVICE = 0xd8,
SIE_CMDCODE_SET_MODE = 0xf3,
SIE_CMDCODE_READ_FRAME_NUMBER = 0xf5,
SIE_CMDCODE_READ_TEST_REGISTER = 0xfd,
SIE_CMDCODE_DEVICE_STATUS = 0xfe,
SIE_CMDCODE_GET_ERROR = 0xff,
SIE_CMDCODE_READ_ERROR_STATUS = 0xfb,
// endpoint commands
SIE_CMDCODE_ENDPOINT_SELECT = 0x00, // + endpoint index
SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT = 0x40, // + endpoint index, should use USBEpIntClr instead
SIE_CMDCODE_ENDPOINT_SET_STATUS = 0x40, // + endpoint index
SIE_CMDCODE_BUFFER_CLEAR = 0xf2,
SIE_CMDCODE_BUFFER_VALIDATE = 0xfa
};
//------------- SIE Device Status (get/set from SIE_CMDCODE_DEVICE_STATUS) -------------//
enum {
SIE_DEV_STATUS_CONNECT_STATUS_MASK = BIT_(0),
SIE_DEV_STATUS_CONNECT_CHANGE_MASK = BIT_(1),
SIE_DEV_STATUS_SUSPEND_MASK = BIT_(2),
SIE_DEV_STATUS_SUSPEND_CHANGE_MASK = BIT_(3),
SIE_DEV_STATUS_RESET_MASK = BIT_(4)
};
//------------- SIE Select Endpoint Command -------------//
enum {
SIE_SELECT_ENDPOINT_FULL_EMPTY_MASK = BIT_(0), // 0: empty, 1 full. IN endpoint checks empty, OUT endpoint check full
SIE_SELECT_ENDPOINT_STALL_MASK = BIT_(1),
SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK = BIT_(2), // clear by SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT
SIE_SELECT_ENDPOINT_PACKET_OVERWRITTEN_MASK = BIT_(3), // previous packet is overwritten by a SETUP packet
SIE_SELECT_ENDPOINT_NAK_MASK = BIT_(4), // last packet response is NAK (auto clear by an ACK)
SIE_SELECT_ENDPOINT_BUFFER1_FULL_MASK = BIT_(5),
SIE_SELECT_ENDPOINT_BUFFER2_FULL_MASK = BIT_(6)
};
typedef enum
{
SIE_SET_ENDPOINT_STALLED_MASK = BIT_(0),
SIE_SET_ENDPOINT_DISABLED_MASK = BIT_(5),
SIE_SET_ENDPOINT_RATE_FEEDBACK_MASK = BIT_(6),
SIE_SET_ENDPOINT_CONDITION_STALLED_MASK = BIT_(7),
}sie_endpoint_set_status_mask_t;
//------------- DMA Descriptor Status -------------//
enum {
DD_STATUS_NOT_SERVICED = 0,
DD_STATUS_BEING_SERVICED,
DD_STATUS_NORMAL,
DD_STATUS_DATA_UNDERUN, // short packet
DD_STATUS_DATA_OVERRUN,
DD_STATUS_SYSTEM_ERROR
};
//--------------------------------------------------------------------+
// SIE Command
//--------------------------------------------------------------------+
static inline void sie_cmd_code (sie_cmdphase_t phase, uint8_t code_data) ATTR_ALWAYS_INLINE;
static inline 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;
#ifndef _TEST_
while ((LPC_USB->USBDevIntSt & wait_flag) == 0); // TODO blocking forever potential
#endif
LPC_USB->USBDevIntClr = wait_flag;
}
static inline void sie_write (uint8_t cmd_code, uint8_t data_len, uint8_t data) ATTR_ALWAYS_INLINE;
static inline 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 inline uint32_t sie_read (uint8_t cmd_code, uint8_t data_len) ATTR_ALWAYS_INLINE;
static inline 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;
}
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC175X_6X_H_ */
/** @} */

View File

@@ -0,0 +1,120 @@
/**************************************************************************/
/*!
@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 "hal_usb.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 MODE_DEVICE_SUPPORTED
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)
{
#if MODE_HOST_SUPPORTED
hal_hcd_isr(0);
#endif
#if MODE_DEVICE_SUPPORTED
hal_dcd_isr(0);
#endif
}
void check_failed(uint8_t *file, uint32_t line)
{
(void) file;
(void) line;
}
#endif

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/*!
@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_ */

View File

@@ -0,0 +1,501 @@
/**************************************************************************/
/*!
@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 MODE_DEVICE_SUPPORTED && CFG_TUSB_MCU == OPT_MCU_LPC43XX
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "tusb_hal.h"
#include "osal/osal.h"
#include "common/timeout_timer.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;
extern ATTR_WEAK dcd_data_t dcd_data0;
extern ATTR_WEAK dcd_data_t dcd_data1;
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
CFG_TUSB_ATTR_USBRAM ATTR_ALIGNED(2048) STATIC_VAR dcd_data_t dcd_data0;
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
CFG_TUSB_ATTR_USBRAM ATTR_ALIGNED(2048) STATIC_VAR 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] = { &dcd_data0, &dcd_data1 };
//--------------------------------------------------------------------+
// 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];
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];
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;
}
//--------------------------------------------------------------------+
// PIPE HELPER
//--------------------------------------------------------------------+
#if 0
static inline uint8_t edpt_pos2phy(uint8_t pos)
{ // 0-5 --> OUT, 16-21 IN
return (pos < DCD_QHD_MAX/2) ? (2*pos) : (2*(pos-16)+1);
}
#endif
static inline uint8_t edpt_phy2pos(uint8_t physical_endpoint)
{
return physical_endpoint/2 + ( (physical_endpoint%2) ? 16 : 0);
}
static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr)
{
return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
}
static inline uint8_t edpt_phy2addr(uint8_t ep_idx)
{
return (ep_idx/2) | ( ep_idx & 0x01 ? TUSB_DIR_IN_MASK : 0 );
}
static inline uint8_t edpt_phy2log(uint8_t physical_endpoint)
{
return physical_endpoint/2;
}
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
memclr_(p_qtd, sizeof(dcd_qtd_t));
p_qtd->used = 1;
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] |= align4k( p_qtd->buffer[i-1] ) + 4096;
}
}
}
// retval 0: invalid
static inline uint8_t qtd_find_free(uint8_t rhport)
{
// QTD0 is reserved for control transfer
for(uint8_t i=1; i<DCD_QTD_MAX; i++)
{
if ( dcd_data_ptr[rhport]->qtd[i].used == 0) return i;
}
return 0;
}
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
// control transfer does not need to use qtd find function
// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
bool dcd_control_xfer(uint8_t rhport, tusb_dir_t dir, uint8_t * p_buffer, uint16_t length)
{
LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
dcd_data_t* const p_dcd = dcd_data_ptr[rhport];
uint8_t const ep_phy = (dir == TUSB_DIR_IN) ? 1 : 0;
dcd_qhd_t* qhd = &p_dcd->qhd[ep_phy];
// wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
while(lpc_usb->ENDPTSETUPSTAT & BIT_(0)) {}
VERIFY( !qhd->qtd_overlay.active );
dcd_qtd_t* qtd = &p_dcd->qtd[0];
qtd_init(qtd, p_buffer, length);
// skip xfer complete for Status
qtd->int_on_complete = (length > 0 ? 1 : 0);
qhd->qtd_overlay.next = (uint32_t) qtd;
lpc_usb->ENDPTPRIME = BIT_(edpt_phy2pos(ep_phy));
return true;
}
//--------------------------------------------------------------------+
// BULK/INTERRUPT/ISOCHRONOUS PIPE API
//--------------------------------------------------------------------+
static inline volatile uint32_t * get_reg_control_addr(uint8_t rhport, uint8_t physical_endpoint)
{
return &(LPC_USB[rhport]->ENDPTCTRL0) + edpt_phy2log(physical_endpoint);
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t ep_idx = edpt_addr2phy(ep_addr);
volatile uint32_t * reg_control = get_reg_control_addr(rhport, ep_idx);
if ( ep_addr == 0)
{
// Stall both Control IN and OUT
(*reg_control) |= ( (ENDPTCTRL_MASK_STALL << 16) || (ENDPTCTRL_MASK_STALL << 0) );
}else
{
(*reg_control) |= ENDPTCTRL_MASK_STALL << (ep_idx & 0x01 ? 16 : 0);
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
volatile uint32_t * reg_control = get_reg_control_addr(rhport, edpt_addr2phy(ep_addr));
// data toggle also need to be reset
(*reg_control) |= ENDPTCTRL_MASK_TOGGLE_RESET << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0);
(*reg_control) &= ~(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
VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
tusb_dir_t dir = (p_endpoint_desc->bEndpointAddress & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
//------------- Prepare Queue Head -------------//
uint8_t ep_idx = edpt_addr2phy(p_endpoint_desc->bEndpointAddress);
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
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 * reg_control = get_reg_control_addr(rhport, ep_idx);
// endpoint must not be already enabled
VERIFY( !( (*reg_control) & (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)) ) );
(*reg_control) |= ((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 ep_idx = edpt_addr2phy(ep_addr);
dcd_qhd_t const * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
return p_qhd->list_qtd_idx[0] != 0; // qtd list is not empty
// return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
}
// add only, controller virtually cannot know
// TODO remove and merge to dcd_edpt_xfer
static bool pipe_add_xfer(uint8_t rhport, uint8_t ed_idx, void * buffer, uint16_t total_bytes, bool int_on_complete)
{
uint8_t qtd_idx = qtd_find_free(rhport);
TU_ASSERT(qtd_idx != 0);
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
dcd_qhd_t * p_qhd = &p_dcd->qhd[ed_idx];
dcd_qtd_t * p_qtd = &p_dcd->qtd[qtd_idx];
//------------- Find free slot in qhd's array list -------------//
uint8_t free_slot;
for(free_slot=0; free_slot < DCD_QTD_PER_QHD_MAX; free_slot++)
{
if ( p_qhd->list_qtd_idx[free_slot] == 0 ) break; // found free slot
}
TU_ASSERT(free_slot < DCD_QTD_PER_QHD_MAX);
p_qhd->list_qtd_idx[free_slot] = qtd_idx; // add new qtd to qhd's array list
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
p_qtd->int_on_complete = int_on_complete;
if ( free_slot > 0 ) p_dcd->qtd[ p_qhd->list_qtd_idx[free_slot-1] ].next = (uint32_t) p_qtd;
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t ep_idx = edpt_addr2phy(ep_addr);
VERIFY ( pipe_add_xfer(rhport, ep_idx, buffer, total_bytes, true) );
dcd_qhd_t* p_qhd = &dcd_data_ptr[rhport]->qhd[ ep_idx ];
dcd_qtd_t* p_qtd = &dcd_data_ptr[rhport]->qtd[ p_qhd->list_qtd_idx[0] ];
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // attach head QTD to QHD start transferring
LPC_USB[rhport]->ENDPTPRIME = BIT_( edpt_phy2pos(ep_idx) ) ;
return true;
}
//------------- Device Controller Driver's Interrupt Handler -------------//
void xfer_complete_isr(uint8_t rhport, uint32_t reg_complete)
{
for(uint8_t ep_idx = 2; ep_idx < DCD_QHD_MAX; ep_idx++)
{
if ( BIT_TEST_(reg_complete, edpt_phy2pos(ep_idx)) )
{ // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
// retire all QTDs in array list, up to 1st still-active QTD
while( p_qhd->list_qtd_idx[0] != 0 )
{
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ p_qhd->list_qtd_idx[0] ];
if (p_qtd->active) break; // stop immediately if found still-active QTD and shift array list
//------------- Free QTD and shift array list -------------//
p_qtd->used = 0; // free QTD
memmove( (void*) p_qhd->list_qtd_idx, (void*) (p_qhd->list_qtd_idx+1), DCD_QTD_PER_QHD_MAX-1);
p_qhd->list_qtd_idx[DCD_QTD_PER_QHD_MAX-1]=0;
if (p_qtd->int_on_complete)
{
bool succeeded = ( p_qtd->xact_err || p_qtd->halted || p_qtd->buffer_err ) ? false : true;
uint8_t ep_addr = edpt_phy2addr(ep_idx);
dcd_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, succeeded); // only number of bytes in the IOC qtd
}
}
}
}
}
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_bus_event(rhport, USBD_BUS_EVENT_RESET);
}
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_bus_event(0, USBD_BUS_EVENT_SUSPENDED);
}
}
}
// TODO disconnection does not generate interrupt !!!!!!
// if (int_status & INT_MASK_PORT_CHANGE)
// {
// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) )
// {
// dcd_bus_event(0, USBD_BUS_EVENT_UNPLUGGED);
// }
// }
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];
//------------- Set up Received -------------//
if (lpc_usb->ENDPTSETUPSTAT)
{
// 23.10.10.2 Operational model for setup transfers
lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge
dcd_setup_received(rhport, (uint8_t*) &p_dcd->qhd[0].setup_request);
}
//------------- Control Request Completed -------------//
else if ( edpt_complete & ( BIT_(0) | BIT_(16)) )
{
// determine Control OUT or IN
uint8_t ep_idx = BIT_TEST_(edpt_complete, 0) ? 0 : 1;
// TODO use the actual QTD instead of the qhd's overlay to get expected bytes for actual byte xferred
dcd_qtd_t* const p_qtd = (dcd_qtd_t*) p_dcd->qhd[ep_idx].qtd_addr;
if ( p_qtd->int_on_complete )
{
bool succeeded = ( p_qtd->xact_err || p_qtd->halted || p_qtd->buffer_err ) ? false : true;
(void) succeeded;
dcd_control_complete(rhport, p_qtd->expected_bytes - p_qtd->total_bytes);
}
}
//------------- Transfer Complete -------------//
if ( edpt_complete & ~(BIT_(0) | BIT_(16)) )
{
xfer_complete_isr(rhport, edpt_complete);
}
}
if (int_status & INT_MASK_SOF)
{
dcd_bus_event(rhport, USBD_BUS_EVENT_SOF);
}
if (int_status & INT_MASK_NAK) {}
if (int_status & INT_MASK_ERROR) TU_ASSERT(false, );
}
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
#endif

View File

@@ -0,0 +1,165 @@
/**************************************************************************/
/*!
@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 DCD_QTD_PER_QHD_MAX 2 // maximum number of qtd that are linked into one queue head at a time
#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) endpoints 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 used;
uint8_t reserved;
} dcd_qtd_t;
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.
//--------------------------------------------------------------------+
volatile uint8_t list_qtd_idx[DCD_QTD_PER_QHD_MAX];
uint8_t reserved[16-DCD_QTD_PER_QHD_MAX];
} dcd_qhd_t;
VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC43XX_H_ */
/** @} */

View File

@@ -0,0 +1,165 @@
/**************************************************************************/
/*!
@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);
// timeout_timer_t timeout;
// timeout_set(&timeout, 2); // should not take longer the time to stop controller
while( ((*p_reg_usbcmd) & BIT_(1)) /*&& !timeout_expired(&timeout)*/) {}
//
// return 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 */
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 MODE_DEVICE_SUPPORTED
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 MODE_DEVICE_SUPPORTED
hal_dcd_isr(1);
#endif
}
#endif
void check_failed(uint8_t *file, uint32_t line)
{
(void) file;
(void) line;
}
#endif