Files
tinyUSB/src/portable/nxp/lpc17xx/dcd_lpc175x_6x.c

588 lines
20 KiB
C
Raw Normal View History

2014-03-12 14:08:52 +07:00
/**************************************************************************/
/*!
@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"
2018-07-23 15:25:45 +07:00
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
2014-03-12 14:08:52 +07:00
2018-03-28 13:44:39 +07:00
#include "device/dcd.h"
2014-03-12 14:08:52 +07:00
#include "dcd_lpc175x_6x.h"
#include "LPC17xx.h"
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_QHD_MAX 32
#define DCD_QTD_MAX 32 // TODO scale with configure
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 next_valid : 1;
uint16_t int_on_complete : 1; ///< make use of reserved bit
uint16_t 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 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_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;
TU_VERIFY_STATIC( sizeof(dcd_dma_descriptor_t) == 16, "size is not correct"); // TODO not support ISO for now
typedef struct
{
// must be 128 byte aligned
volatile dcd_dma_descriptor_t* udca[DCD_QHD_MAX];
2014-03-12 14:08:52 +07:00
// each endpoints can have up to 2 DD queued at a time
// TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory)
dcd_dma_descriptor_t dd[DCD_QTD_MAX][2];
2014-03-12 14:08:52 +07:00
struct
{
2018-11-27 21:51:02 +07:00
uint8_t* out_buffer;
uint8_t out_bytes;
volatile bool out_received; // indicate if data is already received in endpoint
2018-11-27 21:51:02 +07:00
uint8_t in_bytes;
} control;
2014-03-12 14:08:52 +07:00
} dcd_data_t;
2014-03-12 14:08:52 +07:00
2018-11-27 21:53:36 +07:00
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->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 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;
}
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
// PIPE HELPER
//--------------------------------------------------------------------+
static inline uint8_t edpt_addr2phy(uint8_t ep_addr)
2014-03-12 14:08:52 +07:00
{
return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
2014-03-12 14:08:52 +07:00
}
static inline void edpt_set_max_packet_size(uint8_t ep_id, uint16_t max_packet_size)
{
// follows example in 11.10.4.2
2014-03-12 14:08:52 +07:00
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) {} // TODO can be omitted
LPC_USB->USBDevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK;
}
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
// CONTROLLER API
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
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;
2018-11-27 21:53:36 +07:00
tu_memclr(&_dcd, sizeof(dcd_data_t));
2014-03-12 14:08:52 +07:00
}
2018-03-28 13:47:58 +07:00
bool dcd_init(uint8_t rhport)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2018-03-02 22:46:36 +07:00
2014-03-12 14:08:52 +07:00
//------------- user manual 11.13 usb device controller initialization -------------// LPC_USB->USBEpInd = 0;
// step 6 : set up control endpoint
2018-04-12 13:23:52 +07:00
edpt_set_max_packet_size(0, CFG_TUD_ENDOINT0_SIZE);
edpt_set_max_packet_size(1, CFG_TUD_ENDOINT0_SIZE);
2014-03-12 14:08:52 +07:00
bus_reset();
LPC_USB->USBDevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK);
2018-11-27 21:53:36 +07:00
LPC_USB->USBUDCAH = (uint32_t) _dcd.udca;
2014-03-12 14:08:52 +07:00
LPC_USB->USBDMAIntEn = (DMA_INT_END_OF_XFER_MASK | DMA_INT_ERROR_MASK );
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1); // connect
2018-03-13 16:33:23 +07:00
NVIC_EnableIRQ(USB_IRQn);
2014-03-12 14:08:52 +07:00
return TUSB_ERROR_NONE;
}
2018-03-28 13:47:58 +07:00
void dcd_connect(uint8_t rhport)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2014-03-12 14:08:52 +07:00
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1);
}
2018-03-28 13:47:58 +07:00
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2014-03-12 14:08:52 +07:00
sie_write(SIE_CMDCODE_SET_ADDRESS, 1, 0x80 | dev_addr); // 7th bit is : device_enable
}
2018-03-28 13:47:58 +07:00
void dcd_set_config(uint8_t rhport, uint8_t config_num)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2018-03-02 22:53:26 +07:00
(void) config_num;
2014-03-12 14:08:52 +07:00
sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1);
}
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
// CONTROL HELPER
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
2018-11-27 21:51:02 +07:00
static inline uint8_t byte2dword(uint8_t bytes)
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
// length in dwords
return (bytes + 3) / 4;
2014-03-12 14:08:52 +07:00
}
2018-11-27 21:51:02 +07:00
static void control_ep_write(void const * buffer, uint8_t len)
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
uint32_t const * buf32 = (uint32_t const *) buffer;
2014-03-12 14:08:52 +07:00
LPC_USB->USBCtrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0
2018-11-27 21:51:02 +07:00
LPC_USB->USBTxPLen = (uint32_t) len;
2014-03-12 14:08:52 +07:00
2018-11-27 21:51:02 +07:00
for (uint8_t count = 0; count < byte2dword(len); count++)
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
LPC_USB->USBTxData = *buf32; // NOTE: cortex M3 have no problem with alignment
buf32++;
2014-03-12 14:08:52 +07:00
}
2018-11-27 21:51:02 +07:00
LPC_USB->USBCtrl = 0;
2014-03-12 14:08:52 +07:00
// select control IN & validate the endpoint
sie_write(SIE_CMDCODE_ENDPOINT_SELECT+1, 0, 0);
sie_write(SIE_CMDCODE_BUFFER_VALIDATE , 0, 0);
}
2018-11-27 21:51:02 +07:00
static uint8_t control_ep_read(void * buffer, uint8_t len)
2014-03-12 14:08:52 +07:00
{
LPC_USB->USBCtrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0
while ((LPC_USB->USBRxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout
2018-11-27 21:51:02 +07:00
len = tu_min8(len, (uint8_t) (LPC_USB->USBRxPLen & USBRXPLEN_PACKET_LENGTH_MASK) );
uint32_t *buf32 = (uint32_t*) buffer;
2018-11-27 21:51:02 +07:00
for (uint8_t count=0; count < byte2dword(len); count++)
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
*buf32 = LPC_USB->USBRxData;
buf32++;
2014-03-12 14:08:52 +07:00
}
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);
2018-11-27 21:51:02 +07:00
return len;
2014-03-12 14:08:52 +07:00
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
2014-03-12 14:08:52 +07:00
//--------------------------------------------------------------------+
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2014-03-12 14:08:52 +07:00
// 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
2014-03-12 14:08:52 +07:00
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);
//------------- first DD prepare -------------//
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t* const p_dd = &_dcd.dd[ep_id][0];
2018-10-23 12:19:32 +07:00
tu_memclr(p_dd, sizeof(dcd_dma_descriptor_t));
2014-03-12 14:08:52 +07:00
p_dd->isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
2014-03-12 14:08:52 +07:00
p_dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
p_dd->retired = 1; // inactive at first
2014-03-12 14:08:52 +07:00
2018-11-27 21:53:36 +07:00
_dcd.udca[ ep_id ] = p_dd; // hook to UDCA
2014-03-12 14:08:52 +07:00
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0); // clear all endpoint status
return true;
2014-03-12 14:08:52 +07:00
}
bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
uint8_t ep_id = edpt_addr2phy( ep_addr );
2018-11-27 21:53:36 +07:00
return (_dcd.udca[ep_id] != NULL && !_dcd.udca[ep_id]->retired);
2014-03-12 14:08:52 +07:00
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
2014-03-12 14:08:52 +07:00
{
(void) rhport;
2018-11-27 21:51:02 +07:00
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 = edpt_addr2phy( ep_addr );
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, SIE_SET_ENDPOINT_STALLED_MASK);
}
2014-03-12 14:08:52 +07:00
}
2018-03-28 13:47:58 +07:00
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
2014-03-12 14:08:52 +07:00
{
uint8_t ep_id = edpt_addr2phy(ep_addr);
2014-03-12 14:08:52 +07:00
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0);
}
bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
{
// TODO implement later
return false;
}
2014-03-12 14:08:52 +07:00
void dd_xfer_init(dcd_dma_descriptor_t* p_dd, void* buffer, uint16_t total_bytes)
{
p_dd->next = 0;
p_dd->next_valid = 0;
2014-03-12 14:08:52 +07:00
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;
//}
2018-11-27 21:51:02 +07:00
static bool control_xact(uint8_t rhport, uint8_t dir, uint8_t * buffer, uint8_t len)
{
(void) rhport;
if ( dir )
{
2018-11-27 21:53:36 +07:00
_dcd.control.in_bytes = len;
2018-11-27 21:51:02 +07:00
control_ep_write(buffer, len);
}else
{
2018-11-27 21:53:36 +07:00
if ( _dcd.control.out_received )
2018-11-27 21:51:02 +07:00
{
// Already received the DATA OUT packet
2018-11-27 21:53:36 +07:00
_dcd.control.out_received = false;
_dcd.control.out_buffer = NULL;
_dcd.control.out_bytes = 0;
2014-03-12 14:08:52 +07:00
2018-11-27 21:51:02 +07:00
uint8_t received = control_ep_read(buffer, len);
dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true);
}else
{
2018-11-27 21:53:36 +07:00
_dcd.control.out_buffer = buffer;
_dcd.control.out_bytes = len;
2018-11-27 21:51:02 +07:00
}
}
2014-03-12 14:08:52 +07:00
return true;
2014-03-12 14:08:52 +07:00
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
2014-03-12 14:08:52 +07:00
{
2018-11-27 21:51:02 +07:00
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
// Control transfer is not DMA support, and must be done in slave mode
if ( epnum == 0 )
{
2018-11-27 21:51:02 +07:00
return control_xact(rhport, dir, buffer, (uint8_t) total_bytes);
}
uint8_t ep_id = edpt_addr2phy(ep_addr);
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t* const p_first_dd = &_dcd.dd[ep_id][0];
2014-03-12 14:08:52 +07:00
//------------- fixed DD is already queued a xfer -------------//
if ( p_first_dd->buffer_length )
{
// setup new dd
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t* const p_dd = &_dcd.dd[ ep_id ][1];
2018-10-23 12:19:32 +07:00
tu_memclr(p_dd, sizeof(dcd_dma_descriptor_t));
2014-03-12 14:08:52 +07:00
dd_xfer_init(p_dd, buffer, total_bytes);
2018-11-27 21:51:02 +07:00
p_dd->max_packet_size = p_first_dd->max_packet_size;
p_dd->isochronous = p_first_dd->isochronous;
2018-11-27 21:51:02 +07:00
p_dd->int_on_complete = true;
2014-03-12 14:08:52 +07:00
// hook to fixed dd
2018-11-27 21:51:02 +07:00
p_first_dd->next = (uint32_t) p_dd;
p_first_dd->next_valid = 1;
2014-03-12 14:08:52 +07:00
}
//------------- fixed DD is free -------------//
else
{
dd_xfer_init(p_first_dd, buffer, total_bytes);
p_first_dd->int_on_complete = true;
2014-03-12 14:08:52 +07:00
}
p_first_dd->retired = 0; // activate xfer
2018-11-27 21:53:36 +07:00
_dcd.udca[ep_id] = p_first_dd;
LPC_USB->USBEpDMAEn = BIT_(ep_id);
2014-03-12 14:08:52 +07:00
if ( ep_id % 2 )
{
// endpoint IN need to actively raise DMA request
LPC_USB->USBDMARSet = BIT_(ep_id);
2014-03-12 14:08:52 +07:00
}
return true;
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
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) )
{
2018-11-27 21:53:36 +07:00
dcd_dma_descriptor_t* const p_first_dd = &_dcd.dd[ep_id][0];
dcd_dma_descriptor_t* const p_last_dd = _dcd.dd[ep_id] + (p_first_dd->next_valid ? 1 : 0); // Maximum is 2 QTD are queued in an endpoint
// only handle when Controller already finished the last DD
2018-11-27 21:53:36 +07:00
if ( _dcd.udca[ep_id] == p_last_dd )
{
2018-11-27 21:53:36 +07:00
_dcd.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 )
{
uint8_t result = (p_last_dd->status == DD_STATUS_NORMAL || p_last_dd->status == DD_STATUS_DATA_UNDERUN) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED;
// report only xferred bytes in the IOC qtd
uint8_t const ep_addr = (ep_id/2) | ( (ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0 );
dcd_event_xfer_complete(0, ep_addr, p_last_dd->present_count, result, true);
}
}
}
}
}
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
dcd_event_t event = { .rhport = 0 };
//------------- Setup Received-------------//
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
uint8_t setup_packet[8];
2018-11-27 21:51:02 +07:00
control_ep_read(setup_packet, 8); // TODO read before clear setup above
dcd_event_setup_received(0, setup_packet, true);
}
else if (endpoint_int_status & 0x03)
{
2018-11-27 21:51:02 +07:00
// Control out complete
if ( endpoint_int_status & BIT_(0) )
{
2018-11-27 21:53:36 +07:00
if ( _dcd.control.out_buffer )
2018-11-27 21:51:02 +07:00
{
// software queued transfer previously
2018-11-27 21:53:36 +07:00
uint8_t received = control_ep_read(_dcd.control.out_buffer, _dcd.control.out_bytes);
2018-11-27 21:51:02 +07:00
2018-11-27 21:53:36 +07:00
_dcd.control.out_buffer = NULL;
_dcd.control.out_bytes = 0;
2018-11-27 21:51:02 +07:00
dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true);
}else
{
// mark as received
2018-11-27 21:53:36 +07:00
_dcd.control.out_received = true;
2018-11-27 21:51:02 +07:00
}
}
// Control In complete
if ( endpoint_int_status & BIT_(1) )
{
2018-11-27 21:53:36 +07:00
dcd_event_xfer_complete(0, TUSB_DIR_IN_MASK, _dcd.control.in_bytes, XFER_RESULT_SUCCESS, 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_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
}
if (dev_status_reg & 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_reg & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
{
if (dev_status_reg & SIE_DEV_STATUS_SUSPEND_MASK)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
}
// else
// { // resume signal
// dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
// }
// }
}
}
//------------- 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);
}
2014-03-12 14:08:52 +07:00
}
#endif