Merge branch 'master' of https://github.com/hathach/tinyusb into stm32f4

This commit is contained in:
William D. Jones
2019-01-10 09:58:06 -05:00
1010 changed files with 347813 additions and 112197 deletions
@@ -1,6 +1,6 @@
/**************************************************************************/
/*!
@file dcd_lpc13xx_12adc.h
@file cdc.h
@author hathach (tinyusb.org)
@section LICENSE
@@ -26,32 +26,53 @@
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_dcd
* \defgroup group_dcd_lpc11_13u LPC11uxx LPC13uxx
/** \ingroup group_class
* \defgroup ClassDriver_Audio Audio
* Currently only MIDI subclass is supported
* @{ */
#ifndef _TUSB_DCD_LPC13XX_12ADC_H_
#define _TUSB_DCD_LPC13XX_12ADC_H_
#ifndef _TUSB_CDC_H__
#define _TUSB_CDC_H__
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/// Audio Interface Subclass Codes
typedef enum
{
AUDIO_SUBCLASS_AUDIO_CONTROL = 0x01 , ///< Audio Control
AUDIO_SUBCLASS_AUDIO_STREAMING , ///< Audio Streaming
AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
} audio_subclass_type_t;
/// Audio Protocol Codes
typedef enum
{
AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0
AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0
AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0
} audio_protocol_type_t;
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC13XX_12ADC_H_ */
#endif
/** @} */
+37 -37
View File
@@ -41,9 +41,7 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "cdc_device.h"
#include "device/usbd_pvt.h"
@@ -95,7 +93,7 @@ CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
bool tud_cdc_n_connected(uint8_t itf)
{
// DTR (bit 0) active is considered as connected
return BIT_TEST_(_cdcd_itf[itf].line_state, 0);
return TU_BIT_TEST(_cdcd_itf[itf].line_state, 0);
}
uint8_t tud_cdc_n_get_line_state (uint8_t itf)
@@ -231,17 +229,14 @@ void cdcd_reset(uint8_t rhport)
}
}
tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length)
bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
{
if ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL != p_interface_desc->bInterfaceSubClass) return TUSB_ERROR_CDC_UNSUPPORTED_SUBCLASS;
// Only support ACM subclass
TU_ASSERT ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
// Only support AT commands, no protocol and vendor specific commands.
if ( !(tu_within(CDC_COMM_PROTOCOL_ATCOMMAND, p_interface_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
p_interface_desc->bInterfaceProtocol == CDC_COMM_PROTOCOL_NONE ||
p_interface_desc->bInterfaceProtocol == 0xff ) )
{
return TUSB_ERROR_CDC_UNSUPPORTED_PROTOCOL;
}
TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
itf_desc->bInterfaceProtocol == 0xff);
// Find available interface
cdcd_interface_t * p_cdc = NULL;
@@ -253,55 +248,59 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
break;
}
}
TU_ASSERT(p_cdc);
//------------- Control Interface -------------//
p_cdc->itf_num = p_interface_desc->bInterfaceNumber;
p_cdc->itf_num = itf_desc->bInterfaceNumber;
uint8_t const * p_desc = descriptor_next ( (uint8_t const *) p_interface_desc );
uint8_t const * p_desc = tu_desc_next( itf_desc );
(*p_length) = sizeof(tusb_desc_interface_t);
// Communication Functional Descriptors
while( TUSB_DESC_CLASS_SPECIFIC == p_desc[DESC_OFFSET_TYPE] )
while ( TUSB_DESC_CLASS_SPECIFIC == tu_desc_type(p_desc) )
{
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
{ // notification endpoint if any
TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), TUSB_ERROR_DCD_OPEN_PIPE_FAILED);
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
// notification endpoint if any
TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
{
// next to endpoint descritpor
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
// next to endpoint descriptor
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
// Open endpoint pair with usbd helper
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT_ERR( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
(*p_length) += 2*sizeof(tusb_desc_endpoint_t);
}
// Prepare for incoming data
TU_ASSERT( dcd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE), TUSB_ERROR_DCD_EDPT_XFER);
TU_ASSERT( dcd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE) );
return TUSB_ERROR_NONE;
return true;
}
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool cdcd_control_request_complete(uint8_t rhport, tusb_control_request_t const * request)
{
(void) rhport;
//------------- Class Specific Request -------------//
TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
@@ -347,9 +346,10 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request
// This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
p_cdc->line_state = (uint8_t) request->wValue;
// Invoke callback
if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, BIT_TEST_(request->wValue, 0), BIT_TEST_(request->wValue, 1));
usbd_control_status(rhport, request);
// Invoke callback
if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, TU_BIT_TEST(request->wValue, 0), TU_BIT_TEST(request->wValue, 1));
break;
default: return false; // stall unsupported request
@@ -358,8 +358,10 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request
return true;
}
tusb_error_t cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
// TODO Support multiple interfaces
uint8_t const itf = 0;
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
@@ -367,29 +369,27 @@ tusb_error_t cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
// receive new data
if ( ep_addr == p_cdc->ep_out )
{
char const wanted = p_cdc->wanted_char;
for(uint32_t i=0; i<xferred_bytes; i++)
{
tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]);
// Check for wanted char and invoke callback if needed
if ( tud_cdc_rx_wanted_cb && ( wanted != -1 ) && ( wanted == p_cdc->epout_buf[i] ) )
if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) )
{
tud_cdc_rx_wanted_cb(itf, wanted);
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
}
}
// invoke receive callback (if there is still data)
if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
// prepare for next
TU_ASSERT( dcd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE), TUSB_ERROR_DCD_EDPT_XFER );
// prepare for incoming data
TU_ASSERT( dcd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE) );
}
// nothing to do with in and notif endpoint
return TUSB_ERROR_NONE;
return true;
}
#endif
+4 -4
View File
@@ -112,12 +112,12 @@ ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_li
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
void cdcd_init (void);
tusb_error_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
void cdcd_init (void);
bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
bool cdcd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t cdcd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdcd_reset (uint8_t rhport);
bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void cdcd_reset (uint8_t rhport);
#endif
+85 -89
View File
@@ -38,100 +38,92 @@
#include "tusb_option.h"
#if (MODE_HOST_SUPPORTED && CFG_TUSB_HOST_CDC)
#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "cdc_host.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
uint8_t itf_num;
uint8_t itf_protocol;
uint8_t ep_notif;
uint8_t ep_in;
uint8_t ep_out;
cdc_acm_capability_t acm_capability;
} cdch_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
STATIC_VAR cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; // TODO to be static
static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX];
static inline cdc_pipeid_t get_app_pipeid(pipe_handle_t pipe_hdl)
bool tuh_cdc_mounted(uint8_t dev_addr)
{
cdch_data_t const * p_cdc = &cdch_data[pipe_hdl.dev_addr-1];
return pipehandle_is_equal( pipe_hdl, p_cdc->pipe_notification ) ? CDC_PIPE_NOTIFICATION :
pipehandle_is_equal( pipe_hdl, p_cdc->pipe_in ) ? CDC_PIPE_DATA_IN :
pipehandle_is_equal( pipe_hdl, p_cdc->pipe_out ) ? CDC_PIPE_DATA_OUT : CDC_PIPE_ERROR;
}
static inline bool tusbh_cdc_is_mounted(uint8_t dev_addr)
{
// FIXME cannot use mounted class flag as at the point _open_sublass is called, the flag is not set yet
#ifdef _TEST_
return (tusbh_device_get_mounted_class_flag(dev_addr) & BIT_(TUSB_CLASS_CDC)) != 0;
#else
return pipehandle_is_valid(cdch_data[dev_addr-1].pipe_in) &&
pipehandle_is_valid(cdch_data[dev_addr-1].pipe_out);
#endif
cdch_data_t* cdc = &cdch_data[dev_addr-1];
return cdc->ep_in && cdc->ep_out;
}
bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid)
{
if ( !tusbh_cdc_is_mounted(dev_addr) ) return false;
if ( !tuh_cdc_mounted(dev_addr) ) return false;
cdch_data_t const * p_cdc = &cdch_data[dev_addr-1];
switch (pipeid)
{
case CDC_PIPE_NOTIFICATION:
return hcd_pipe_is_busy( p_cdc->pipe_notification );
return hcd_edpt_busy(dev_addr, p_cdc->ep_notif );
case CDC_PIPE_DATA_IN:
return hcd_pipe_is_busy( p_cdc->pipe_in );
return hcd_edpt_busy(dev_addr, p_cdc->ep_in );
case CDC_PIPE_DATA_OUT:
return hcd_pipe_is_busy( p_cdc->pipe_out );
return hcd_edpt_busy(dev_addr, p_cdc->ep_out );
default:
return false;
}
}
//--------------------------------------------------------------------+
// APPLICATION API (parameter validation needed)
//--------------------------------------------------------------------+
bool tuh_cdc_serial_is_mounted(uint8_t dev_addr)
{
// TODO consider all AT Command as serial candidate
return tusbh_cdc_is_mounted(dev_addr) &&
(CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].interface_protocol) &&
(cdch_data[dev_addr-1].interface_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
return tuh_cdc_mounted(dev_addr) &&
(CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) &&
(cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
}
tusb_error_t tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
{
TU_ASSERT( tusbh_cdc_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
TU_ASSERT( p_data != NULL && length, TUSB_ERROR_INVALID_PARA);
TU_VERIFY( tuh_cdc_mounted(dev_addr) );
TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA);
pipe_handle_t pipe_out = cdch_data[dev_addr-1].pipe_out;
if ( hcd_pipe_is_busy(pipe_out) ) return TUSB_ERROR_INTERFACE_IS_BUSY;
uint8_t const ep_out = cdch_data[dev_addr-1].ep_out;
if ( hcd_edpt_busy(dev_addr, ep_out) ) return false;
return hcd_pipe_xfer( pipe_out, (void *) p_data, length, is_notify);
return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify);
}
tusb_error_t tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
{
TU_ASSERT( tusbh_cdc_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
TU_ASSERT( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA);
TU_VERIFY( tuh_cdc_mounted(dev_addr) );
TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA);
pipe_handle_t pipe_in = cdch_data[dev_addr-1].pipe_in;
if ( hcd_pipe_is_busy(pipe_in) ) return TUSB_ERROR_INTERFACE_IS_BUSY;
uint8_t const ep_in = cdch_data[dev_addr-1].ep_in;
if ( hcd_edpt_busy(dev_addr, ep_in) ) return false;
return hcd_pipe_xfer( pipe_in, p_buffer, length, is_notify);
return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify);
}
//--------------------------------------------------------------------+
@@ -142,102 +134,106 @@ void cdch_init(void)
tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
}
tusb_error_t cdch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
{
OSAL_SUBTASK_BEGIN
// TODO change following assert to subtask_assert
// Only support ACM
TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
if ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL != p_interface_desc->bInterfaceSubClass) return TUSB_ERROR_CDC_UNSUPPORTED_SUBCLASS;
if ( !(tu_within(CDC_COMM_PROTOCOL_ATCOMMAND, p_interface_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
0xff == p_interface_desc->bInterfaceProtocol) )
{
return TUSB_ERROR_CDC_UNSUPPORTED_PROTOCOL;
}
// Only support AT commands, no protocol and vendor specific commands.
TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
0xff == itf_desc->bInterfaceProtocol);
uint8_t const * p_desc;
cdch_data_t * p_cdc;
p_desc = descriptor_next ( (uint8_t const *) p_interface_desc );
p_cdc = &cdch_data[dev_addr-1]; // non-static variable cannot be used after OS service call
p_desc = tu_desc_next(itf_desc);
p_cdc = &cdch_data[dev_addr-1];
p_cdc->interface_number = p_interface_desc->bInterfaceNumber;
p_cdc->interface_protocol = p_interface_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
p_cdc->itf_num = itf_desc->bInterfaceNumber;
p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
//------------- Communication Interface -------------//
(*p_length) = sizeof(tusb_desc_interface_t);
// Communication Functional Descriptors
while( TUSB_DESC_CLASS_SPECIFIC == p_desc[DESC_OFFSET_TYPE] )
{ // Communication Functional Descriptors
{
if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
{ // save ACM bmCapabilities
{
// save ACM bmCapabilities
p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
p_desc = tu_desc_next(p_desc);
}
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
{ // notification endpoint if any
p_cdc->pipe_notification = hcd_pipe_open(dev_addr, (tusb_desc_endpoint_t const *) p_desc, TUSB_CLASS_CDC);
{
// notification endpoint
tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) );
p_cdc->ep_notif = ep_desc->bEndpointAddress;
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
TU_ASSERT(pipehandle_is_valid(p_cdc->pipe_notification), TUSB_ERROR_HCD_OPEN_PIPE_FAILED);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
{
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next(p_desc);
p_desc = tu_desc_next(p_desc);
// data endpoints expected to be in pairs
for(uint32_t i=0; i<2; i++)
{
tusb_desc_endpoint_t const *p_endpoint = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType, TUSB_ERROR_USBH_DESCRIPTOR_CORRUPTED);
TU_ASSERT(TUSB_XFER_BULK == p_endpoint->bmAttributes.xfer, TUSB_ERROR_USBH_DESCRIPTOR_CORRUPTED);
tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
&p_cdc->pipe_in : &p_cdc->pipe_out;
TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
(*p_pipe_hdl) = hcd_pipe_open(dev_addr, p_endpoint, TUSB_CLASS_CDC);
TU_ASSERT ( pipehandle_is_valid(*p_pipe_hdl), TUSB_ERROR_HCD_OPEN_PIPE_FAILED );
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
{
p_cdc->ep_in = ep_desc->bEndpointAddress;
}else
{
p_cdc->ep_out = ep_desc->bEndpointAddress;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = descriptor_next( p_desc );
p_desc = tu_desc_next( p_desc );
}
}
// FIXME move to seperate API : connect
tusb_control_request_t request =
{
// FIXME mounted class flag is not set yet
tuh_cdc_mounted_cb(dev_addr);
}
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
.wValue = 0x03, // dtr on, cst on
.wIndex = p_cdc->itf_num,
.wLength = 0
};
OSAL_SUBTASK_END
TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) );
return true;
}
void cdch_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
tuh_cdc_xfer_isr( pipe_hdl.dev_addr, event, get_app_pipeid(pipe_hdl), xferred_bytes );
(void) ep_addr;
tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes );
}
void cdch_close(uint8_t dev_addr)
{
cdch_data_t * p_cdc = &cdch_data[dev_addr-1];
(void) hcd_pipe_close(p_cdc->pipe_notification);
(void) hcd_pipe_close(p_cdc->pipe_in);
(void) hcd_pipe_close(p_cdc->pipe_out);
tu_memclr(p_cdc, sizeof(cdch_data_t));
tuh_cdc_unmounted_cb(dev_addr);
}
#endif
+8 -31
View File
@@ -62,7 +62,7 @@
* \retval true if device supports
* \retval false if device does not support or is not mounted
*/
bool tuh_cdc_serial_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_cdc_serial_is_mounted(uint8_t dev_addr);
/** \brief Check if the interface is currently busy or not
* \param[in] dev_addr device address
@@ -73,7 +73,7 @@ bool tuh_cdc_serial_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESU
* can be scheduled. User needs to make sure the corresponding interface is mounted
* (by \ref tuh_cdc_serial_is_mounted) before calling this function.
*/
bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid);
/** \brief Perform USB OUT transfer to device
* \param[in] dev_addr device address
@@ -86,7 +86,7 @@ bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) ATTR_PURE ATTR_WARN
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
* interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
*/
tusb_error_t tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify);
bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify);
/** \brief Perform USB IN transfer to get data from device
* \param[in] dev_addr device address
@@ -99,22 +99,11 @@ tusb_error_t tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
* interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
*/
tusb_error_t tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify);
bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify);
//--------------------------------------------------------------------+
// CDC APPLICATION CALLBACKS
//--------------------------------------------------------------------+
/** \brief Callback function that will be invoked when a device with CDC Abstract Control Model interface is mounted
* \param[in] dev_addr Address of newly mounted device
* \note This callback should be used by Application to set-up interface-related data
*/
void tuh_cdc_mounted_cb(uint8_t dev_addr);
/** \brief Callback function that will be invoked when a device with CDC Abstract Control Model interface is unmounted
* \param[in] dev_addr Address of newly unmounted device
* \note This callback should be used by Application to tear-down interface-related data
*/
void tuh_cdc_unmounted_cb(uint8_t dev_addr);
/** \brief Callback function that is invoked when an transferring event occurred
* \param[in] dev_addr Address of device
@@ -137,22 +126,10 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
typedef struct {
uint8_t interface_number;
uint8_t interface_protocol;
cdc_acm_capability_t acm_capability;
pipe_handle_t pipe_notification, pipe_out, pipe_in;
} cdch_data_t;
extern cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; // TODO consider to move to cdch internal header file
void cdch_init(void);
tusb_error_t cdch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
void cdch_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close(uint8_t dev_addr);
void cdch_init(void);
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close(uint8_t dev_addr);
#endif
+1 -1
View File
@@ -38,7 +38,7 @@
#include "tusb_option.h"
#if (MODE_HOST_SUPPORTED && CFG_TUSB_HOST_CDC && CFG_TUSB_HOST_CDC_RNDIS)
#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
#define _TINY_USB_SOURCE_FILE_
+1 -1
View File
@@ -64,7 +64,7 @@ typedef struct {
}rndish_data_t;
void rndish_init(void);
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc) ATTR_WARN_UNUSED_RESULT;
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void rndish_close(uint8_t dev_addr);
+7 -7
View File
@@ -71,22 +71,22 @@ void cusd_init(void)
tu_varclr(&_cusd_itf);
}
tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf, uint16_t *p_len)
bool cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf, uint16_t *p_len)
{
cusd_interface_t* p_itf = &_cusd_itf;
// Open endpoint pair with usbd helper
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_desc_itf );
TU_ASSERT_ERR( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_itf->ep_out, &p_itf->ep_in) );
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(p_desc_itf);
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_itf->ep_out, &p_itf->ep_in) );
p_itf->itf_num = p_desc_itf->bInterfaceNumber;
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
// TODO Prepare for incoming data
// TU_ASSERT( dcd_edpt_xfer(rhport, p_itf->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_DCD_EDPT_XFER );
// TU_ASSERT( dcd_edpt_xfer(rhport, p_itf->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
return TUSB_ERROR_NONE;
return true;
}
bool cusd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
@@ -94,9 +94,9 @@ bool cusd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
return false;
}
tusb_error_t cusd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes)
bool cusd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
return TUSB_ERROR_NONE;
return true;
}
void cusd_reset(uint8_t rhport)
+2 -2
View File
@@ -63,10 +63,10 @@
#ifdef _TINY_USB_SOURCE_FILE_
void cusd_init(void);
tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
bool cusd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool cusd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
bool cusd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t cusd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes);
bool cusd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cusd_reset(uint8_t rhport);
#endif
+4 -4
View File
@@ -38,7 +38,7 @@
#include "tusb_option.h"
#if (MODE_HOST_SUPPORTED && CFG_TUSB_HOST_CUSTOM_CLASS)
#if (TUSB_OPT_HOST_ENABLED && CFG_TUSB_HOST_CUSTOM_CLASS)
#define _TINY_USB_SOURCE_FILE_
@@ -111,7 +111,7 @@ tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_
{
// FIXME quick hack to test lpc1k custom class with 2 bulk endpoints
uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
p_desc = descriptor_next(p_desc);
p_desc = tu_desc_next(p_desc);
//------------- Bulk Endpoints Descriptor -------------//
for(uint32_t i=0; i<2; i++)
@@ -121,10 +121,10 @@ tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
&custom_interface[dev_addr-1].pipe_in : &custom_interface[dev_addr-1].pipe_out;
*p_pipe_hdl = hcd_pipe_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
*p_pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
TU_ASSERT ( pipehandle_is_valid(*p_pipe_hdl), TUSB_ERROR_HCD_OPEN_PIPE_FAILED );
p_desc = descriptor_next(p_desc);
p_desc = tu_desc_next(p_desc);
}
(*p_length) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+2 -2
View File
@@ -62,7 +62,7 @@ static inline bool tusbh_custom_is_mounted(uint8_t dev_addr, uint16_t vendor_id,
{
(void) vendor_id; // TODO check this later
(void) product_id;
// return (tusbh_device_get_mounted_class_flag(dev_addr) & BIT_(TUSB_CLASS_MAPPED_INDEX_END-1) ) != 0;
// return (tusbh_device_get_mounted_class_flag(dev_addr) & TU_BIT(TUSB_CLASS_MAPPED_INDEX_END-1) ) != 0;
return false;
}
@@ -72,7 +72,7 @@ tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t p
#ifdef _TINY_USB_SOURCE_FILE_
void cush_init(void);
tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event);
void cush_close(uint8_t dev_addr);
+18 -18
View File
@@ -173,11 +173,11 @@ typedef struct ATTR_PACKED
/// Standard Mouse Buttons Bitmap
typedef enum
{
MOUSE_BUTTON_LEFT = BIT_(0), ///< Left button
MOUSE_BUTTON_RIGHT = BIT_(1), ///< Right button
MOUSE_BUTTON_MIDDLE = BIT_(2), ///< Middle button
MOUSE_BUTTON_BACKWARD = BIT_(3), ///< Backward button,
MOUSE_BUTTON_FORWARD = BIT_(4), ///< Forward button,
MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button
MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button
MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button
MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button,
MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button,
}hid_mouse_button_bm_t;
/// @}
@@ -199,23 +199,23 @@ typedef struct ATTR_PACKED
/// Keyboard modifier codes bitmap
typedef enum
{
KEYBOARD_MODIFIER_LEFTCTRL = BIT_(0), ///< Left Control
KEYBOARD_MODIFIER_LEFTSHIFT = BIT_(1), ///< Left Shift
KEYBOARD_MODIFIER_LEFTALT = BIT_(2), ///< Left Alt
KEYBOARD_MODIFIER_LEFTGUI = BIT_(3), ///< Left Window
KEYBOARD_MODIFIER_RIGHTCTRL = BIT_(4), ///< Right Control
KEYBOARD_MODIFIER_RIGHTSHIFT = BIT_(5), ///< Right Shift
KEYBOARD_MODIFIER_RIGHTALT = BIT_(6), ///< Right Alt
KEYBOARD_MODIFIER_RIGHTGUI = BIT_(7) ///< Right Window
KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control
KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift
KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt
KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window
KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control
KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift
KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt
KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window
}hid_keyboard_modifier_bm_t;
typedef enum
{
KEYBOARD_LED_NUMLOCK = BIT_(0), ///< Num Lock LED
KEYBOARD_LED_CAPSLOCK = BIT_(1), ///< Caps Lock LED
KEYBOARD_LED_SCROLLLOCK = BIT_(2), ///< Scroll Lock LED
KEYBOARD_LED_COMPOSE = BIT_(3), ///< Composition Mode
KEYBOARD_LED_KANA = BIT_(4) ///< Kana mode
KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED
KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED
KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED
KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode
KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode
}hid_keyboard_led_bm_t;
/// @}
+16 -17
View File
@@ -207,6 +207,7 @@ bool tud_hid_keyboard_key_press(char ch)
return tud_hid_keyboard_keycode(modifier, keycode);
}
#if 0 // should be at application
bool tud_hid_keyboard_key_sequence(const char* str, uint32_t interval_ms)
{
// Send each key in string
@@ -231,6 +232,7 @@ bool tud_hid_keyboard_key_sequence(const char* str, uint32_t interval_ms)
return true;
}
#endif
#endif // CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
@@ -318,29 +320,29 @@ void hidd_reset(uint8_t rhport)
#endif
}
tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
{
uint8_t const *p_desc = (uint8_t const *) desc_itf;
// TODO not support HID OUT Endpoint
TU_ASSERT(desc_itf->bNumEndpoints == 1, ERR_TUD_INVALID_DESCRIPTOR);
// TODO support HID OUT Endpoint
TU_ASSERT(desc_itf->bNumEndpoints == 1);
//------------- HID descriptor -------------//
p_desc += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType, ERR_TUD_INVALID_DESCRIPTOR);
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
//------------- Endpoint Descriptor -------------//
p_desc += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
tusb_desc_endpoint_t const *desc_edpt = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_edpt->bDescriptorType, ERR_TUD_INVALID_DESCRIPTOR);
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_edpt->bDescriptorType);
hidd_interface_t * p_hid = NULL;
/*------------- Boot protocol only keyboard & mouse -------------*/
if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT)
{
TU_ASSERT(desc_itf->bInterfaceProtocol == HID_PROTOCOL_KEYBOARD || desc_itf->bInterfaceProtocol == HID_PROTOCOL_MOUSE, ERR_TUD_INVALID_DESCRIPTOR);
TU_ASSERT(desc_itf->bInterfaceProtocol == HID_PROTOCOL_KEYBOARD || desc_itf->bInterfaceProtocol == HID_PROTOCOL_MOUSE);
#if CFG_TUD_HID_KEYBOARD && CFG_TUD_HID_KEYBOARD_BOOT
if (desc_itf->bInterfaceProtocol == HID_PROTOCOL_KEYBOARD)
@@ -374,7 +376,7 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
}
#endif
TU_ASSERT(p_hid, ERR_TUD_INVALID_DESCRIPTOR);
TU_ASSERT(p_hid);
p_hid->boot_protocol = true; // default mode is BOOT
}
/*------------- Generic (multiple report) -------------*/
@@ -386,12 +388,10 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
p_hid->desc_report = usbd_desc_set->hid_report.generic;
p_hid->get_report_cb = tud_hid_generic_get_report_cb;
p_hid->set_report_cb = tud_hid_generic_set_report_cb;
TU_ASSERT(p_hid, ERR_TUD_INVALID_DESCRIPTOR);
}
TU_VERIFY(p_hid->desc_report, ERR_TUD_INVALID_DESCRIPTOR);
TU_ASSERT( dcd_edpt_open(rhport, desc_edpt), ERR_TUD_EDPT_OPEN_FAILED );
TU_ASSERT(p_hid->desc_report);
TU_ASSERT(dcd_edpt_open(rhport, desc_edpt));
p_hid->itf_num = desc_itf->bInterfaceNumber;
p_hid->ep_in = desc_edpt->bEndpointAddress;
@@ -399,7 +399,7 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
*p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
return TUSB_ERROR_NONE;
return true;
}
// Handle class control request
@@ -418,7 +418,6 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
// Cast away the const on p_hid->desc_report because we know it won't be modified.
usbd_control_xfer(rhport, p_request, (void *)p_hid->desc_report, p_hid->desc_len);
}else
{
@@ -511,10 +510,10 @@ bool hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const
return true;
}
tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes)
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
// nothing to do
return TUSB_ERROR_NONE;
return true;
}
+2 -3
View File
@@ -95,7 +95,6 @@ static inline bool tud_hid_keyboard_key_release(void) { return tud_hid_keyboard_
#if CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
bool tud_hid_keyboard_key_press(char ch);
bool tud_hid_keyboard_key_sequence(const char* str, uint32_t interval_ms);
typedef struct{
uint8_t shift;
@@ -377,10 +376,10 @@ ATTR_WEAK void tud_hid_mouse_set_report_cb(uint8_t report_id, hid_report_type_t
#ifdef _TINY_USB_SOURCE_FILE_
void hidd_init(void);
tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
bool hidd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes);
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hidd_reset(uint8_t rhport);
#endif
+37 -36
View File
@@ -38,7 +38,7 @@
#include "tusb_option.h"
#if (MODE_HOST_SUPPORTED && HOST_CLASS_HID)
#if (TUSB_OPT_HOST_ENABLED && HOST_CLASS_HID)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
@@ -54,20 +54,19 @@
//--------------------------------------------------------------------+
// HID Interface common functions
//--------------------------------------------------------------------+
static inline tusb_error_t hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid)
static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid)
{
p_hid->pipe_hdl = hcd_pipe_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID);
p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID);
p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
p_hid->interface_number = interface_number;
TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl), TUSB_ERROR_HCD_FAILED);
TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl));
return TUSB_ERROR_NONE;
return true;
}
static inline void hidh_interface_close(hidh_interface_info_t *p_hid)
{
(void) hcd_pipe_close(p_hid->pipe_hdl);
tu_memclr(p_hid, sizeof(hidh_interface_info_t));
}
@@ -78,7 +77,7 @@ tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_int
// TODO change to use is configured function
TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY);
TU_VERIFY (report, TUSB_ERROR_INVALID_PARA);
TU_ASSSERT (!hcd_pipe_is_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY);
TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY);
TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ;
@@ -88,8 +87,9 @@ tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_int
//--------------------------------------------------------------------+
// KEYBOARD
//--------------------------------------------------------------------+
#if CFG_TUSB_HOST_HID_KEYBOARD
#if CFG_TUH_HID_KEYBOARD
#if 0
#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \
[0][keycode] = ascii,\
[1][keycode] = shift_modified,\
@@ -99,8 +99,9 @@ uint8_t const hid_keycode_to_ascii_tbl[2][128] =
{
HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII)
};
#endif
STATIC_VAR hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
//------------- KEYBOARD PUBLIC API (parameter validation required) -------------//
bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr)
@@ -116,7 +117,7 @@ tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report)
bool tuh_hid_keyboard_is_busy(uint8_t dev_addr)
{
return tuh_hid_keyboard_is_mounted(dev_addr) &&
hcd_pipe_is_busy( keyboardh_data[dev_addr-1].pipe_hdl );
hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl );
}
#endif
@@ -124,7 +125,7 @@ bool tuh_hid_keyboard_is_busy(uint8_t dev_addr)
//--------------------------------------------------------------------+
// MOUSE
//--------------------------------------------------------------------+
#if CFG_TUSB_HOST_HID_MOUSE
#if CFG_TUH_HID_MOUSE
STATIC_VAR hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
@@ -137,7 +138,7 @@ bool tuh_hid_mouse_is_mounted(uint8_t dev_addr)
bool tuh_hid_mouse_is_busy(uint8_t dev_addr)
{
return tuh_hid_mouse_is_mounted(dev_addr) &&
hcd_pipe_is_busy( mouseh_data[dev_addr-1].pipe_hdl );
hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl );
}
tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report)
@@ -163,11 +164,11 @@ tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report)
//--------------------------------------------------------------------+
void hidh_init(void)
{
#if CFG_TUSB_HOST_HID_KEYBOARD
#if CFG_TUH_HID_KEYBOARD
tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
#endif
#if CFG_TUSB_HOST_HID_MOUSE
#if CFG_TUH_HID_MOUSE
tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
#endif
@@ -180,9 +181,8 @@ void hidh_init(void)
CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256];
#endif
tusb_error_t hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
{
tusb_error_t error;
uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
//------------- HID descriptor -------------//
@@ -195,16 +195,15 @@ tusb_error_t hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_
tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA);
OSAL_SUBTASK_BEGIN
//------------- SET IDLE (0) request -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
HID_REQ_CONTROL_SET_IDLE, 0, p_interface_desc->bInterfaceNumber,
0, NULL ),
error
);
(void) error; // skip if set idle is failed
tusb_control_request_t request = {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = HID_REQ_CONTROL_SET_IDLE,
.wValue = 0, // idle_rate = 0
.wIndex = p_interface_desc->bInterfaceNumber,
.wLength = 0
};
TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
#if 0
//------------- Get Report Descriptor TODO HID parser -------------//
@@ -222,40 +221,42 @@ tusb_error_t hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_
if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass )
{
#if CFG_TUSB_HOST_HID_KEYBOARD
#if CFG_TUH_HID_KEYBOARD
if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol)
{
STASK_ASSERT_ERR ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) );
TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) );
tuh_hid_keyboard_mounted_cb(dev_addr);
} else
#endif
#if CFG_TUSB_HOST_HID_MOUSE
#if CFG_TUH_HID_MOUSE
if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol)
{
STASK_ASSERT_ERR ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) );
TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) );
tuh_hid_mouse_mounted_cb(dev_addr);
} else
#endif
{
STASK_RETURN(TUSB_ERROR_HIDH_NOT_SUPPORTED_PROTOCOL); // exit & restart task
// Not supported protocol
return false;
}
}else
{
STASK_RETURN(TUSB_ERROR_HIDH_NOT_SUPPORTED_SUBCLASS); // exit & restart task
// Not supported subclass
return false;
}
*p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t);
OSAL_SUBTASK_END
return true;
}
void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
{
(void) xferred_bytes; // TODO may need to use this para later
#if CFG_TUSB_HOST_HID_KEYBOARD
#if CFG_TUH_HID_KEYBOARD
if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
{
tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event);
@@ -263,7 +264,7 @@ void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_byte
}
#endif
#if CFG_TUSB_HOST_HID_MOUSE
#if CFG_TUH_HID_MOUSE
if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
{
tuh_hid_mouse_isr(pipe_hdl.dev_addr, event);
@@ -278,7 +279,7 @@ void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_byte
void hidh_close(uint8_t dev_addr)
{
#if CFG_TUSB_HOST_HID_KEYBOARD
#if CFG_TUH_HID_KEYBOARD
if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) )
{
hidh_interface_close(&keyboardh_data[dev_addr-1]);
@@ -286,7 +287,7 @@ void hidh_close(uint8_t dev_addr)
}
#endif
#if CFG_TUSB_HOST_HID_MOUSE
#if CFG_TUH_HID_MOUSE
if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) )
{
hidh_interface_close(&mouseh_data[dev_addr-1]);
+15 -15
View File
@@ -67,7 +67,7 @@ extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if
* \retval true if device supports Keyboard interface
* \retval false if device does not support Keyboard interface or is not mounted
*/
bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr);
/** \brief Check if the interface is currently busy or not
* \param[in] dev_addr device address
@@ -76,7 +76,7 @@ bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_
* \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report.
* Alternatively, asynchronous event API can be used
*/
bool tuh_hid_keyboard_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_hid_keyboard_is_busy(uint8_t dev_addr);
/** \brief Perform a get report from Keyboard interface
* \param[in] dev_addr device address
@@ -88,7 +88,7 @@ bool tuh_hid_keyboard_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNU
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report) /*ATTR_WARN_UNUSED_RESULT*/;
tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report);
//------------- Application Callback -------------//
/** \brief Callback function that is invoked when an transferring event occurred
@@ -132,7 +132,7 @@ void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr);
* \retval true if device supports Mouse interface
* \retval false if device does not support Mouse interface or is not mounted
*/
bool tuh_hid_mouse_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_hid_mouse_is_mounted(uint8_t dev_addr);
/** \brief Check if the interface is currently busy or not
* \param[in] dev_addr device address
@@ -141,7 +141,7 @@ bool tuh_hid_mouse_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNU
* \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report.
* Alternatively, asynchronous event API can be used
*/
bool tuh_hid_mouse_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_hid_mouse_is_busy(uint8_t dev_addr);
/** \brief Perform a get report from Mouse interface
* \param[in] dev_addr device address
@@ -153,7 +153,7 @@ bool tuh_hid_mouse_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report) /*ATTR_WARN_UNUSED_RESULT*/;
tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report);
//------------- Application Callback -------------//
/** \brief Callback function that is invoked when an transferring event occurred
@@ -192,11 +192,11 @@ void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr);
* The interface API includes status checking function, data transferring function and callback functions
* @{ */
bool tuh_hid_generic_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete) ATTR_WARN_UNUSED_RESULT;
tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete) ATTR_WARN_UNUSED_RESULT;
tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr) ATTR_WARN_UNUSED_RESULT;
tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr) ATTR_WARN_UNUSED_RESULT;
bool tuh_hid_generic_is_mounted(uint8_t dev_addr);
tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr);
tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr);
//------------- Application Callback -------------//
void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event);
@@ -215,10 +215,10 @@ typedef struct {
uint8_t interface_number;
}hidh_interface_info_t;
void hidh_init(void);
tusb_error_t hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void hidh_close(uint8_t dev_addr);
void hidh_init(void);
bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void hidh_close(uint8_t dev_addr);
#endif
+84
View File
@@ -0,0 +1,84 @@
/**************************************************************************/
/*!
@file cdc.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_class
* \defgroup ClassDriver_CDC Communication Device Class (CDC)
* Currently only Abstract Control Model subclass is supported
* @{ */
#ifndef _TUSB_MIDI_H__
#define _TUSB_MIDI_H__
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// FUNCTIONAL DESCRIPTOR (COMMUNICATION INTERFACE)
//--------------------------------------------------------------------+
/// Header Functional Descriptor (Communication Interface)
typedef struct ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above MIDI_FUCN_DESC_
uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
uint16_t wTotalLength ;
}midi_desc_func_header_t;
/// Union Functional Descriptor (Communication Interface)
typedef struct ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
uint8_t bControlInterface ; ///< Interface number of Communication Interface
uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface
}cdc_desc_func_union_t;
/** @} */
#ifdef __cplusplus
}
#endif
#endif
/** @} */
+360
View File
@@ -0,0 +1,360 @@
/**************************************************************************/
/*!
@file cdc_device.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MIDI)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "midi_device.h"
#include "class/audio/audio.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
// FIFO
tu_fifo_t rx_ff;
tu_fifo_t tx_ff;
uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_ff_mutex;
osal_mutex_def_t tx_ff_mutex;
#endif
// We need to pack messages into words before queueing their transmission so buffer across write
// calls.
uint8_t message_buffer[4];
uint8_t message_buffer_length;
uint8_t message_target_length;
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE];
} midid_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_ATTR_USBRAM midid_interface_t _midid_itf[CFG_TUD_MIDI];
bool tud_midi_n_connected(uint8_t itf) {
midid_interface_t* midi = &_midid_itf[itf];
return midi->itf_num != 0;
}
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id)
{
return tu_fifo_count(&_midid_itf[itf].rx_ff);
}
char tud_midi_n_read_char(uint8_t itf, uint8_t jack_id)
{
char ch;
return tu_fifo_read(&_midid_itf[itf].rx_ff, &ch) ? ch : (-1);
}
uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize)
{
return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize);
}
void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id)
{
tu_fifo_clear(&_midid_itf[itf].rx_ff);
}
void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) {
if (bufsize % 4 != 0) {
return;
}
for(uint32_t i=0; i<bufsize; i += 4) {
uint8_t header = buffer[i];
// uint8_t cable_number = (header & 0xf0) >> 4;
uint8_t code_index = header & 0x0f;
// We always copy over the first byte.
uint8_t count = 1;
// Ignore subsequent bytes based on the code.
if (code_index != 0x5 && code_index != 0xf) {
count = 2;
if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) {
count = 3;
}
}
tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count);
}
}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index)
{
TU_VERIFY( !dcd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); // skip if previous transfer not complete
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE);
if (count > 0)
{
TU_VERIFY( tud_midi_n_connected(itf_index) ); // fifo is empty if not connected
TU_ASSERT( dcd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
}
return true;
}
uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
{
midid_interface_t* midi = &_midid_itf[itf];
if (midi->itf_num == 0) {
return 0;
}
uint32_t i = 0;
while (i < bufsize) {
uint8_t data = buffer[i];
if (midi->message_buffer_length == 0) {
uint8_t msg = data >> 4;
midi->message_buffer[1] = data;
midi->message_buffer_length = 2;
// Check to see if we're still in a SysEx transmit.
if (midi->message_buffer[0] == 0x4) {
if (data == 0xf7) {
midi->message_buffer[0] = 0x5;
} else {
midi->message_buffer_length = 4;
}
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
midi->message_buffer[0] = jack_id << 4 | msg;
midi->message_target_length = 4;
} else if (msg == 0xf) {
if (data == 0xf0) {
midi->message_buffer[0] = 0x4;
midi->message_target_length = 4;
} else if (data == 0xf1 || data == 0xf3) {
midi->message_buffer[0] = 0x2;
midi->message_target_length = 3;
} else if (data == 0xf2) {
midi->message_buffer[0] = 0x3;
midi->message_target_length = 4;
} else {
midi->message_buffer[0] = 0x5;
midi->message_target_length = 2;
}
} else {
// Pack individual bytes if we don't support packing them into words.
midi->message_buffer[0] = jack_id << 4 | 0xf;
midi->message_buffer[2] = 0;
midi->message_buffer[3] = 0;
midi->message_buffer_length = 2;
midi->message_target_length = 2;
}
} else {
midi->message_buffer[midi->message_buffer_length] = data;
midi->message_buffer_length += 1;
// See if this byte ends a SysEx.
if (midi->message_buffer[0] == 0x4 && data == 0xf7) {
midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1);
midi->message_target_length = midi->message_buffer_length;
}
}
if (midi->message_buffer_length == midi->message_target_length) {
uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4);
if (written < 4) {
TU_ASSERT( written == 0 );
break;
}
midi->message_buffer_length = 0;
}
i++;
}
maybe_transmit(midi, itf);
return i;
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void midid_init(void)
{
tu_memclr(_midid_itf, sizeof(_midid_itf));
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
{
midid_interface_t* midi = &_midid_itf[i];
// config fifo
tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true);
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
#endif
}
}
void midid_reset(uint8_t rhport)
{
(void) rhport;
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
{
midid_interface_t* midi = &_midid_itf[i];
tu_memclr(midi, ITF_MEM_RESET_SIZE);
tu_fifo_clear(&midi->rx_ff);
tu_fifo_clear(&midi->tx_ff);
}
}
bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length)
{
// For now handle the audio control interface as well.
if ( AUDIO_SUBCLASS_AUDIO_CONTROL == p_interface_desc->bInterfaceSubClass) {
uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc );
(*p_length) = sizeof(tusb_desc_interface_t);
// Skip over the class specific descriptor.
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
return true;
}
if ( AUDIO_SUBCLASS_MIDI_STREAMING != p_interface_desc->bInterfaceSubClass ||
p_interface_desc->bInterfaceProtocol != AUDIO_PROTOCOL_V1 ) {
return false;
}
// Find available interface
midid_interface_t * p_midi = NULL;
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
{
if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
{
p_midi = &_midid_itf[i];
break;
}
}
p_midi->itf_num = p_interface_desc->bInterfaceNumber;
uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc );
(*p_length) = sizeof(tusb_desc_interface_t);
uint8_t found_endpoints = 0;
while (found_endpoints < p_interface_desc->bNumEndpoints) {
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
{
TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false);
uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
p_midi->ep_in = ep_addr;
} else {
p_midi->ep_out = ep_addr;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
found_endpoints += 1;
}
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
}
// Prepare for incoming data
TU_ASSERT( dcd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
return true;
}
bool midid_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
return false;
}
bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
//------------- Class Specific Request -------------//
if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return false;
return false;
}
bool midid_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes)
{
// TODO Support multiple interfaces
uint8_t const itf = 0;
midid_interface_t* p_midi = &_midid_itf[itf];
// receive new data
if ( edpt_addr == p_midi->ep_out )
{
midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes);
// prepare for next
TU_ASSERT( dcd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
} else if ( edpt_addr == p_midi->ep_in ) {
maybe_transmit(p_midi, itf);
}
// nothing to do with in and notif endpoint
return TUSB_ERROR_NONE;
}
#endif
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************/
/*!
@file midi_device.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_MIDI_DEVICE_H_
#define _TUSB_MIDI_DEVICE_H_
#include "common/tusb_common.h"
#include "device/usbd.h"
#include "class/audio/audio.h"
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUD_MIDI_EPSIZE
#define CFG_TUD_MIDI_EPSIZE 64
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** \addtogroup MIDI_Serial Serial
* @{
* \defgroup MIDI_Serial_Device Device
* @{ */
//--------------------------------------------------------------------+
// APPLICATION API (Multiple Interfaces)
// CFG_TUD_MIDI > 1
//--------------------------------------------------------------------+
bool tud_midi_n_connected (uint8_t itf);
uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id);
char tud_midi_n_read_char (uint8_t itf, uint8_t jack_id);
uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize);
void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id);
char tud_midi_n_peek (uint8_t itf, uint8_t jack_id, int pos);
uint32_t tud_midi_n_write_char (uint8_t itf, char ch);
uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
bool tud_midi_n_write_flush (uint8_t itf);
//--------------------------------------------------------------------+
// APPLICATION API (Interface0)
//--------------------------------------------------------------------+
static inline bool tud_midi_connected (void) { return tud_midi_n_connected(0); }
static inline uint32_t tud_midi_available (void) { return tud_midi_n_available(0, 0); }
static inline char tud_midi_read_char (void) { return tud_midi_n_read_char(0, 0); }
static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) { return tud_midi_n_read(0, 0, buffer, bufsize); }
static inline void tud_midi_read_flush (void) { tud_midi_n_read_flush(0, 0); }
static inline char tud_midi_peek (int pos) { return tud_midi_n_peek(0, 0, pos); }
static inline uint32_t tud_midi_write_char (char ch) { return tud_midi_n_write_char(0, ch); }
static inline uint32_t tud_midi_write (uint8_t jack_id, void const* buffer, uint32_t bufsize) { return tud_midi_n_write(0, jack_id, buffer, bufsize); }
static inline bool tud_midi_write_flush (void) { return tud_midi_n_write_flush(0); }
//--------------------------------------------------------------------+
// APPLICATION CALLBACK API (WEAK is optional)
//--------------------------------------------------------------------+
ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
//--------------------------------------------------------------------+
// USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
void midid_init (void);
bool midid_open (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
bool midid_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
bool midid_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void midid_reset (uint8_t rhport);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MIDI_DEVICE_H_ */
/** @} */
/** @} */
+25 -20
View File
@@ -62,7 +62,7 @@ enum
typedef struct {
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX)
// uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
//#endif
@@ -146,28 +146,29 @@ void mscd_init(void)
void mscd_reset(uint8_t rhport)
{
(void) rhport;
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf, uint16_t *p_len)
bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
{
// only support SCSI's BOT protocol
TU_VERIFY( ( MSC_SUBCLASS_SCSI == p_desc_itf->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == p_desc_itf->bInterfaceProtocol ), TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL );
TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
mscd_interface_t * p_msc = &_mscd_itf;
// Open endpoint pair with usbd helper
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_desc_itf );
TU_ASSERT_ERR( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next( itf_desc );
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
p_msc->itf_num = p_desc_itf->bInterfaceNumber;
p_msc->itf_num = itf_desc->bInterfaceNumber;
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
//------------- Queue Endpoint OUT for Command Block Wrapper -------------//
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_DCD_EDPT_XFER );
// Prepare for Command Block Wrapper
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
return TUSB_ERROR_NONE;
return true;
}
// Handle class control request
@@ -201,6 +202,9 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
(void) p_request;
// nothing to do
return true;
}
@@ -208,6 +212,7 @@ bool mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const
// return length of response (copied to buffer), -1 if it is not an built-in commands
int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t bufsize)
{
(void) bufsize; // TODO refractor later
int32_t ret;
switch ( p_cbw->command[0] )
@@ -324,7 +329,7 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
return ret;
}
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
msc_cbw_t const * p_cbw = &p_msc->cbw;
@@ -335,10 +340,10 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
case MSC_STAGE_CMD:
//------------- new CBW received -------------//
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
if(ep_addr != p_msc->ep_out) return TUSB_ERROR_NONE;
if(ep_addr != p_msc->ep_out) return true;
TU_ASSERT( event == XFER_RESULT_SUCCESS &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE, TUSB_ERROR_INVALID_PARA );
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
@@ -380,10 +385,10 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
p_csw->status = MSC_CSW_STATUS_PASSED;
}
}
else if ( !BIT_TEST_(p_cbw->dir, 7) )
else if ( !TU_BIT_TEST(p_cbw->dir, 7) )
{
// OUT transfer
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len), TUSB_ERROR_DCD_EDPT_XFER );
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
}
else
{
@@ -404,8 +409,8 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
p_msc->total_len = (uint32_t) cb_result;
p_csw->status = MSC_CSW_STATUS_PASSED;
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len, TUSB_ERROR_INVALID_PARA ); // cannot return more than host expect
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len), TUSB_ERROR_DCD_EDPT_XFER );
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
}else
{
p_msc->total_len = 0;
@@ -421,7 +426,7 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
case MSC_STAGE_DATA:
// OUT transfer, invoke callback if needed
if ( !BIT_TEST_(p_cbw->dir, 7) )
if ( !TU_BIT_TEST(p_cbw->dir, 7) )
{
if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
{
@@ -469,7 +474,7 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
// simulate an transfer complete with adjusted parameters --> this driver callback will fired again
dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
return TUSB_ERROR_NONE; // skip the rest
return true; // skip the rest
}
else
{
@@ -544,7 +549,7 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
}
}
return TUSB_ERROR_NONE;
return true;
}
/*------------------------------------------------------------------*/
+2 -8
View File
@@ -71,12 +71,6 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
#error CFG_TUD_MSC_PRODUCT_REV 4-byte string must be defined
#endif
// TODO highspeed device is 512
#ifndef CFG_TUD_MSC_EPSIZE
#define CFG_TUD_MSC_EPSIZE 64
#endif
#ifdef __cplusplus
extern "C" {
#endif
@@ -177,10 +171,10 @@ ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
#ifdef _TINY_USB_SOURCE_FILE_
void mscd_init(void);
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t event, uint32_t xferred_bytes);
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void mscd_reset(uint8_t rhport);
#endif
+94 -97
View File
@@ -38,7 +38,7 @@
#include "tusb_option.h"
#if MODE_HOST_SUPPORTED & CFG_TUSB_HOST_MSC
#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC
#define _TINY_USB_SOURCE_FILE_
@@ -51,13 +51,14 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION STATIC_VAR msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
//------------- Initalization Data -------------//
static osal_semaphore_def_t msch_sem_def;
static osal_semaphore_t msch_sem_hdl;
// buffer used to read scsi information when mounted, largest response data currently is inquiry
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4) STATIC_VAR uint8_t msch_buffer[sizeof(scsi_inquiry_data_t)];
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -75,7 +76,7 @@ bool tuh_msc_is_mounted(uint8_t dev_addr)
bool tuh_msc_is_busy(uint8_t dev_addr)
{
return msch_data[dev_addr-1].is_initialized &&
hcd_pipe_is_busy(msch_data[dev_addr-1].bulk_in);
hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in);
}
uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr)
@@ -109,23 +110,22 @@ static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun)
p_cbw->lun = lun;
}
static tusb_error_t msch_command_xfer(msch_interface_t * p_msch, void* p_buffer) ATTR_WARN_UNUSED_RESULT;
static tusb_error_t msch_command_xfer(msch_interface_t * p_msch, void* p_buffer)
static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer)
{
if ( NULL != p_buffer)
{ // there is data phase
if (p_msch->cbw.dir & TUSB_DIR_IN_MASK)
{
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false) );
TU_ASSERT_ERR( hcd_pipe_queue_xfer(p_msch->bulk_in , p_buffer, p_msch->cbw.xfer_bytes) );
TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED );
}else
{
TU_ASSERT_ERR( hcd_pipe_queue_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)) );
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out , p_buffer, p_msch->cbw.xfer_bytes, false) );
TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED );
TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED );
}
}
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true) );
TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED);
return TUSB_ERROR_NONE;
}
@@ -136,7 +136,7 @@ tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = sizeof(scsi_inquiry_data_t);
p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t);
@@ -144,12 +144,12 @@ tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
scsi_inquiry_t cmd_inquiry =
{
.cmd_code = SCSI_CMD_INQUIRY,
.alloc_length = sizeof(scsi_inquiry_data_t)
.alloc_length = sizeof(scsi_inquiry_resp_t)
};
memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
return TUSB_ERROR_NONE;
}
@@ -160,7 +160,7 @@ tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = sizeof(scsi_read_capacity10_data_t);
p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t);
@@ -174,7 +174,7 @@ tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p
memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
return TUSB_ERROR_NONE;
}
@@ -186,7 +186,7 @@ tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_dat
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
p_msch->cbw.xfer_bytes = 18;
p_msch->cbw.total_bytes = 18;
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t);
@@ -199,7 +199,7 @@ tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_dat
memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
return TUSB_ERROR_NONE;
}
@@ -211,7 +211,7 @@ tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t *
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = 0; // Number of bytes
p_msch->cbw.total_bytes = 0; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_OUT;
p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
@@ -225,8 +225,8 @@ tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t *
memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len);
// TODO MSCH refractor test uinit ready
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false) );
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true) );
TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED );
return TUSB_ERROR_NONE;
}
@@ -238,7 +238,7 @@ tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uin
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_read10_t);
@@ -247,12 +247,12 @@ tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uin
{
.cmd_code = SCSI_CMD_READ_10,
.lba = __n2be(lba),
.block_count = u16_le2be(block_count)
.block_count = tu_u16_le2be(block_count)
};
memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_buffer));
TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer));
return TUSB_ERROR_NONE;
}
@@ -264,7 +264,7 @@ tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffe
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_OUT;
p_msch->cbw.cmd_len = sizeof(scsi_write10_t);
@@ -273,12 +273,12 @@ tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffe
{
.cmd_code = SCSI_CMD_WRITE_10,
.lba = __n2be(lba),
.block_count = u16_le2be(block_count)
.block_count = tu_u16_le2be(block_count)
};
memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, (void*) p_buffer));
TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer));
return TUSB_ERROR_NONE;
}
@@ -289,133 +289,130 @@ tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffe
void msch_init(void)
{
tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
msch_sem_hdl = osal_semaphore_create(1, 0);
msch_sem_hdl = osal_semaphore_create(&msch_sem_def);
}
tusb_error_t msch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
{
tusb_error_t error;
TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
OSAL_SUBTASK_BEGIN
if (! ( MSC_SUBCLASS_SCSI == p_interface_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == p_interface_desc->bInterfaceProtocol ) )
{
return TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL;
}
msch_interface_t* p_msc = &msch_data[dev_addr-1];
//------------- Open Data Pipe -------------//
tusb_desc_endpoint_t const *p_endpoint;
p_endpoint = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_interface_desc );
tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
for(uint32_t i=0; i<2; i++)
{
STASK_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType);
STASK_ASSERT(TUSB_XFER_BULK == p_endpoint->bmAttributes.xfer);
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
&msch_data[dev_addr-1].bulk_in : &msch_data[dev_addr-1].bulk_out;
TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
(*p_pipe_hdl) = hcd_pipe_open(dev_addr, p_endpoint, TUSB_CLASS_MSC);
STASK_ASSERT( pipehandle_is_valid(*p_pipe_hdl) );
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
{
p_msc->ep_in = ep_desc->bEndpointAddress;
}else
{
p_msc->ep_out = ep_desc->bEndpointAddress;
}
p_endpoint = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_endpoint );
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
}
msch_data[dev_addr-1].interface_number = p_interface_desc->bInterfaceNumber;
p_msc->itf_numr = itf_desc->bInterfaceNumber;
(*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
//------------- Get Max Lun -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
MSC_REQ_GET_MAX_LUN, 0, msch_data[dev_addr-1].interface_number,
1, msch_buffer ),
error
);
STASK_ASSERT( TUSB_ERROR_NONE == error /* && TODO STALL means zero */);
msch_data[dev_addr-1].max_lun = msch_buffer[0];
tusb_control_request_t request = {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
.bRequest = MSC_REQ_GET_MAX_LUN,
.wValue = 0,
.wIndex = p_msc->itf_numr,
.wLength = 1
};
// TODO STALL means zero
TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) );
p_msc->max_lun = msch_buffer[0];
#if 0
//------------- Reset -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
MSC_REQ_RESET, 0, msch_data[dev_addr-1].interface_number,
0, NULL ),
error
);
request = (tusb_control_request_t) {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = MSC_REQ_RESET,
.wValue = 0,
.wIndex = p_msc->itf_numr,
.wLength = 0
};
TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
#endif
enum { SCSI_XFER_TIMEOUT = 2000 };
//------------- SCSI Inquiry -------------//
tusbh_msc_inquiry(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) );
memcpy(msch_data[dev_addr-1].vendor_id , ((scsi_inquiry_data_t*) msch_buffer)->vendor_id , 8);
memcpy(msch_data[dev_addr-1].product_id, ((scsi_inquiry_data_t*) msch_buffer)->product_id, 16);
memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8);
memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16);
//------------- SCSI Read Capacity 10 -------------//
tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
// NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence
// Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work
if ( hcd_pipe_is_stalled(msch_data[dev_addr-1].bulk_in) )
{ // clear stall TODO abstract clear stall function
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_ENDPOINT),
TUSB_REQ_CLEAR_FEATURE, 0, hcd_pipe_get_endpoint_addr(msch_data[dev_addr-1].bulk_in),
0, NULL ),
error
);
STASK_ASSERT_ERR(error);
if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) )
{
// clear stall TODO abstract clear stall function
request = (tusb_control_request_t) {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT },
.bRequest = TUSB_REQ_CLEAR_FEATURE,
.wValue = 0,
.wIndex = p_msc->ep_in,
.wLength = 0
};
hcd_pipe_clear_stall(msch_data[dev_addr-1].bulk_in);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error); // wait for SCSI status
STASK_ASSERT_ERR(error);
TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL ));
hcd_edpt_clear_stall(dev_addr, p_msc->ep_in);
TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status
//------------- SCSI Request Sense -------------//
(void) tuh_msc_request_sense(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
//------------- Re-read SCSI Read Capactity -------------//
tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
}
msch_data[dev_addr-1].last_lba = __be2n( ((scsi_read_capacity10_data_t*)msch_buffer)->last_lba );
msch_data[dev_addr-1].block_size = (uint16_t) __be2n( ((scsi_read_capacity10_data_t*)msch_buffer)->block_size );
p_msc->last_lba = __be2n( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba );
p_msc->block_size = (uint16_t) __be2n( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size );
p_msc->is_initialized = true;
msch_data[dev_addr-1].is_initialized = true;
tuh_msc_mounted_cb(dev_addr);
OSAL_SUBTASK_END
return true;
}
void msch_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
if ( pipehandle_is_equal(pipe_hdl, msch_data[pipe_hdl.dev_addr-1].bulk_in) )
msch_interface_t* p_msc = &msch_data[dev_addr-1];
if ( ep_addr == p_msc->ep_in )
{
if (msch_data[pipe_hdl.dev_addr-1].is_initialized)
if (p_msc->is_initialized)
{
tuh_msc_isr(pipe_hdl.dev_addr, event, xferred_bytes);
tuh_msc_isr(dev_addr, event, xferred_bytes);
}else
{ // still initializing under open subtask
osal_semaphore_post(msch_sem_hdl);
osal_semaphore_post(msch_sem_hdl, true);
}
}
}
void msch_close(uint8_t dev_addr)
{
(void) hcd_pipe_close(msch_data[dev_addr-1].bulk_in);
(void) hcd_pipe_close(msch_data[dev_addr-1].bulk_out);
tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t));
osal_semaphore_reset(msch_sem_hdl);
+16 -13
View File
@@ -60,7 +60,7 @@
* \retval true if device supports
* \retval false if device does not support or is not mounted
*/
bool tuh_msc_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_msc_is_mounted(uint8_t dev_addr);
/** \brief Check if the interface is currently busy or not
* \param[in] dev_addr device address
@@ -70,7 +70,7 @@ bool tuh_msc_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RE
* can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted)
* before calling this function
*/
bool tuh_msc_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
bool tuh_msc_is_busy(uint8_t dev_addr);
/** \brief Get SCSI vendor's name of MassStorage device
* \param[in] dev_addr device address
@@ -113,7 +113,7 @@ tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count) ATTR_WARN_UNUSED_RESULT;
tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count);
/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device
* \param[in] dev_addr device address
@@ -127,7 +127,7 @@ tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uin
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count) ATTR_WARN_UNUSED_RESULT;
tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count);
/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device
* \param[in] dev_addr device address
@@ -150,11 +150,11 @@ tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_dat
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw) ATTR_WARN_UNUSED_RESULT; // TODO to be refractor
tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor
//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in,
// uint8_t const * p_command, uint8_t cmd_len,
// uint8_t * p_response, uint32_t resp_len) ATTR_WARN_UNUSED_RESULT;
// uint8_t * p_response, uint32_t resp_len);
//------------- Application Callback -------------//
/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted
@@ -187,9 +187,11 @@ void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
typedef struct {
pipe_handle_t bulk_in, bulk_out;
uint8_t interface_number;
typedef struct
{
uint8_t itf_numr;
uint8_t ep_in;
uint8_t ep_out;
uint8_t max_lun;
uint16_t block_size;
@@ -203,10 +205,11 @@ typedef struct {
msc_csw_t csw;
}msch_interface_t;
void msch_init(void);
tusb_error_t msch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
void msch_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void msch_close(uint8_t dev_addr);
void msch_init(void);
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void msch_close(uint8_t dev_addr);
#endif
#ifdef __cplusplus
-132
View File
@@ -1,132 +0,0 @@
/**************************************************************************/
/*!
@file binary.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_Common
* \defgroup Group_Binary Binary
* @{ */
#ifndef _TUSB_BINARY_H_
#define _TUSB_BINARY_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "tusb_compiler.h"
//------------- Bit manipulation -------------//
#define BIT_(n) (1U << (n)) ///< n-th Bit
#define BIT_SET_(x, n) ( (x) | BIT_(n) ) ///< set n-th bit of x to 1
#define BIT_CLR_(x, n) ( (x) & (~BIT_(n)) ) ///< clear n-th bit of x
#define BIT_TEST_(x, n) ( ((x) & BIT_(n)) ? true : false ) ///< check if n-th bit of x is 1
static inline uint32_t bit_set(uint32_t value, uint8_t n) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t bit_set(uint32_t value, uint8_t n)
{
return value | BIT_(n);
}
static inline uint32_t bit_clear(uint32_t value, uint8_t n) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t bit_clear(uint32_t value, uint8_t n)
{
return value & (~BIT_(n));
}
static inline bool bit_test(uint32_t value, uint8_t n) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline bool bit_test(uint32_t value, uint8_t n)
{
return (value & BIT_(n)) ? true : false;
}
///< create a mask with n-bit lsb set to 1
static inline uint32_t bit_mask(uint8_t n) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t bit_mask(uint8_t n)
{
return (n < 32) ? ( BIT_(n) - 1 ) : UINT32_MAX;
}
static inline uint32_t bit_mask_range(uint8_t start, uint32_t end) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t bit_mask_range(uint8_t start, uint32_t end)
{
return bit_mask(end+1) & ~ bit_mask(start);
}
static inline uint32_t bit_set_range(uint32_t value, uint8_t start, uint8_t end, uint32_t pattern) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t bit_set_range(uint32_t value, uint8_t start, uint8_t end, uint32_t pattern)
{
return ( value & ~bit_mask_range(start, end) ) | (pattern << start);
}
//------------- Binary Constant -------------//
#if defined(__GNUC__) && !defined(__CC_ARM)
#define BIN8(x) ((uint8_t) (0b##x))
#define BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
#define BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
#else
// internal macro of B8, B16, B32
#define _B8__(x) (((x&0x0000000FUL)?1:0) \
+((x&0x000000F0UL)?2:0) \
+((x&0x00000F00UL)?4:0) \
+((x&0x0000F000UL)?8:0) \
+((x&0x000F0000UL)?16:0) \
+((x&0x00F00000UL)?32:0) \
+((x&0x0F000000UL)?64:0) \
+((x&0xF0000000UL)?128:0))
#define BIN8(d) ((uint8_t) _B8__(0x##d##UL))
#define BIN16(dmsb,dlsb) (((uint16_t)BIN8(dmsb)<<8) + BIN8(dlsb))
#define BIN32(dmsb,db2,db3,dlsb) \
(((uint32_t)BIN8(dmsb)<<24) \
+ ((uint32_t)BIN8(db2)<<16) \
+ ((uint32_t)BIN8(db3)<<8) \
+ BIN8(dlsb))
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_BINARY_H_ */
/** @} */
+14 -54
View File
@@ -1,6 +1,6 @@
/**************************************************************************/
/*!
@file compiler_gcc.h
@file tusb_compiler_gcc.h
@author hathach (tinyusb.org)
@section LICENSE
@@ -49,72 +49,34 @@
#define ALIGN_OF(x) __alignof__(x)
/// Normally, the compiler places the objects it generates in sections like data or bss & function in text. Sometimes, however, you need additional sections, or you need certain particular variables to appear in special sections, for example to map to special hardware. The section attribute specifies that a variable (or function) lives in a particular section
#define ATTR_SECTION(sec_name) __attribute__ (( section(#sec_name) ))
/// If this attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, an error that includes message is diagnosed. This is useful for compile-time checking
#define ATTR_ERROR(Message) __attribute__ ((error(Message)))
/// If this attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, a warning that includes message is diagnosed. This is useful for compile-time checking
#define ATTR_WARNING(Message) __attribute__ ((warning(Message)))
/** \defgroup Group_VariableAttr Variable Attributes
* @{ */
/// This attribute specifies a minimum alignment for the variable or structure field, measured in bytes
#define ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
/// The packed attribute specifies that a variable or structure field should have the smallest possible alignment—one byte for a variable, and one bit for a field, unless you specify a larger value with the aligned attribute
/// Place variable in a specific section
#define ATTR_SECTION(sec_name) __attribute__ (( section(#sec_name) ))
/// The packed attribute specifies that a variable or structure field should have
/// the smallest possible alignment—one byte for a variable, and one bit for a field.
#define ATTR_PACKED __attribute__ ((packed))
#define ATTR_PREPACKED
/** @} */
/** \defgroup Group_FuncAttr Function Attributes
* @{ */
/// Generally, functions are not inlined unless optimization is specified. For functions declared inline, this attribute inlines the function even if no optimization level is specified
/// This attribute inlines the function even if no optimization level is specified
#define ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
/// The nonnull attribute specifies that some function parameters should be non-null pointers. f the compiler determines that a null pointer is passed in an argument slot marked as non-null, and the -Wnonnull option is enabled, a warning is issued. All pointer arguments are marked as non-null
#define ATTR_NON_NULL __attribute__ ((nonull))
/// The deprecated attribute results in a warning if the function is used anywhere in the source file.
/// This is useful when identifying functions that are expected to be removed in a future version of a program.
#define ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess)))
/// Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure
#define ATTR_PURE __attribute__ ((pure))
/// \brief Many functions do not examine any values except their arguments, and have no effects except the return value. Basically this is just slightly more strict class than the pure attribute below, since function is not allowed to read global memory.
/// Note that a function that has pointer arguments and examines the data pointed to must not be declared const. Likewise, a function that calls a non-const function usually must not be const. It does not make sense for a const function to return void
#define ATTR_CONST __attribute__ ((const))
/// The deprecated attribute results in a warning if the function is used anywhere in the source file. This is useful when identifying functions that are expected to be removed in a future version of a program. The warning also includes the location of the declaration of the deprecated function, to enable users to easily find further information about why the function is deprecated, or what they should do instead. Note that the warnings only occurs for uses
#define ATTR_DEPRECATED __attribute__ ((deprecated))
/// Same as the deprecated attribute with optional message in the warning
#define ATTR_DEPRECATED_MESS(mess) __attribute__ ((deprecated(mess)))
/// The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions that can be overridden in user code
/// The weak attribute causes the declaration to be emitted as a weak symbol rather than a global.
/// This is primarily useful in defining library functions that can be overridden in user code
#define ATTR_WEAK __attribute__ ((weak))
/// The alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified
#define ATTR_ALIAS(func) __attribute__ ((alias(#func)))
/// The weakref attribute marks a declaration as a weak reference. It is equivalent with weak + alias attribute, but require function is static
#define ATTR_WEAKREF(func) __attribute__ ((weakref(#func)))
/// The warn_unused_result attribute causes a warning to be emitted if a caller of the function with this attribute does not use its return value. This is useful for functions where not checking the result is either a security problem or always a bug
/// Warn if a caller of the function with this attribute does not use its return value.
#define ATTR_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
/// This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly.
#define ATTR_USED __attribute__ ((used))
/// This attribute, attached to a function, means that the function is meant to be possibly unused. GCC does not produce a warning for this function.
/// Function is meant to be possibly unused. GCC does not produce a warning for this function.
#define ATTR_UNUSED __attribute__ ((unused))
/** @} */
/** \defgroup Group_BuiltinFunc Built-in Functions
* @{ */
// TODO mcu specific
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define __n2be(x) __builtin_bswap32(x) ///< built-in function to convert 32-bit from native to Big Endian
@@ -124,8 +86,6 @@
#define __be2n_16(u16) __n2be_16(u16)
#endif
/** @} */
#ifdef __cplusplus
}
#endif
+7 -24
View File
@@ -1,6 +1,6 @@
/**************************************************************************/
/*!
@file compiler_iar.h
@file tusb_compiler_iar.h
@author hathach (tinyusb.org)
@section LICENSE
@@ -36,15 +36,6 @@
*/
/**************************************************************************/
/** \file
* \brief IAR Compiler
*/
/** \ingroup Group_Compiler
* \defgroup Group_IAR IAR ARM
* @{
*/
#ifndef _TUSB_COMPILER_IAR_H_
#define _TUSB_COMPILER_IAR_H_
@@ -52,24 +43,17 @@
extern "C" {
#endif
#define ALIGN_OF(x) __ALIGNOF__(x)
#define ATTR_PREPACKED __packed
#define ATTR_PACKED
#define ALIGN_OF(x) __ALIGNOF__(x)
#define ATTR_ALIGNED(bytes) _Pragma(XSTRING_(data_alignment=##bytes))
//#define ATTR_SECTION(section) _Pragma((#section))
#define ATTR_PREPACKED __packed
#define ATTR_PACKED
#define ATTR_ALIGNED(bytes) _Pragma(XSTRING_(data_alignment=##bytes))
#ifndef ATTR_ALWAYS_INLINE
/// Generally, functions are not inlined unless optimization is specified. For functions declared inline, this attribute inlines the function even if no optimization level is specified
#define ATTR_ALWAYS_INLINE error
#endif
#define ATTR_PURE // TODO IAR pure function attribute
#define ATTR_CONST // TODO IAR const function attribute
#define ATTR_ALWAYS_INLINE
#define ATTR_DEPRECATED(mess)
#define ATTR_WEAK __weak
#define ATTR_WARN_UNUSED_RESULT
#define ATTR_USED
#define ATTR_UNUSED
// built-in function to convert 32-bit Big-Endian to Little-Endian
@@ -86,4 +70,3 @@
#endif /* _TUSB_COMPILER_IAR_H_ */
/** @} */
+111 -106
View File
@@ -47,32 +47,12 @@
extern "C" {
#endif
//--------------------------------------------------------------------+
// INCLUDES
//--------------------------------------------------------------------+
//------------- Standard Header -------------//
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
//------------- TUSB Option Header -------------//
#include "tusb_option.h"
//------------- Common Header -------------//
#include "tusb_compiler.h"
#include "tusb_verify.h"
#include "binary.h"
#include "tusb_error.h"
#include "tusb_timeout.h"
#include "tusb_types.h"
//--------------------------------------------------------------------+
// MACROS
//--------------------------------------------------------------------+
// Macros Helper
//--------------------------------------------------------------------+
#define TU_ARRAY_SZIE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
#define TU_MIN(_x, _y) ( (_x) < (_y) ) ? (_x) : (_y) )
#define TU_MAX(_x, _y) ( (_x) > (_y) ) ? (_x) : (_y) )
#define U16_HIGH_U8(u16) ((uint8_t) (((u16) >> 8) & 0x00ff))
#define U16_LOW_U8(u16) ((uint8_t) ((u16) & 0x00ff))
@@ -87,54 +67,77 @@
#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32)
#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32)
//------------- Bit -------------//
#define TU_BIT(n) (1U << (n)) ///< n-th Bit
#define TU_BIT_SET(x, n) ( (x) | TU_BIT(n) ) ///< set n-th bit of x to 1
#define TU_BIT_CLEAR(x, n) ( (x) & (~TU_BIT(n)) ) ///< clear n-th bit of x
#define TU_BIT_TEST(x, n) ( ((x) & TU_BIT(n)) ? true : false ) ///< check if n-th bit of x is 1
//------------- Endian Conversion -------------//
#define ENDIAN_BE(u32) \
(uint32_t) ( (((u32) & 0xFF) << 24) | (((u32) & 0xFF00) << 8) | (((u32) >> 8) & 0xFF00) | (((u32) >> 24) & 0xFF) )
#define ENDIAN_BE16(le16) ((uint16_t) ((U16_LOW_U8(le16) << 8) | U16_HIGH_U8(le16)) )
#ifndef __n2be_16
#define __n2be_16(u16) ((uint16_t) ((U16_LOW_U8(u16) << 8) | U16_HIGH_U8(u16)) )
#define __be2n_16(u16) __n2be_16(u16)
#endif
//------------- Binary constant -------------//
#if defined(__GNUC__) && !defined(__CC_ARM)
#define TU_BIN8(x) ((uint8_t) (0b##x))
#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
#else
// internal macro of B8, B16, B32
#define _B8__(x) (((x&0x0000000FUL)?1:0) \
+((x&0x000000F0UL)?2:0) \
+((x&0x00000F00UL)?4:0) \
+((x&0x0000F000UL)?8:0) \
+((x&0x000F0000UL)?16:0) \
+((x&0x00F00000UL)?32:0) \
+((x&0x0F000000UL)?64:0) \
+((x&0xF0000000UL)?128:0))
#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL))
#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb))
#define TU_BIN32(dmsb,db2,db3,dlsb) \
(((uint32_t)TU_BIN8(dmsb)<<24) \
+ ((uint32_t)TU_BIN8(db2)<<16) \
+ ((uint32_t)TU_BIN8(db3)<<8) \
+ TU_BIN8(dlsb))
#endif
// for declaration of reserved field, make use of _TU_COUNTER_
#define TU_RESERVED XSTRING_CONCAT_(reserved, _TU_COUNTER_)
/*------------------------------------------------------------------*/
/* Count number of arguments of __VA_ARGS__
* - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s
* - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
* - _RSEQ_N() is reverse sequential to N to add padding to have
* Nth position is the same as the number of arguments
* - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
*------------------------------------------------------------------*/
#ifndef VA_ARGS_NUM_
//--------------------------------------------------------------------+
// INCLUDES
//--------------------------------------------------------------------+
#define VA_ARGS_NUM_(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N())
#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__)
#define _GET_NTH_ARG( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define _RSEQ_N() \
62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#endif
// Standard Headers
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
// Tinyusb Common Headers
#include "tusb_option.h"
#include "tusb_compiler.h"
#include "tusb_verify.h"
//#include "binary.h"
#include "tusb_error.h"
#include "tusb_timeout.h"
#include "tusb_types.h"
//--------------------------------------------------------------------+
// INLINE FUNCTION
//--------------------------------------------------------------------+
#ifndef __n2be_16 // TODO clean up
#define __n2be_16(u16) ((uint16_t) ((U16_LOW_U8(u16) << 8) | U16_HIGH_U8(u16)) )
#define __be2n_16(u16) __n2be_16(u16)
#endif
#define tu_memclr(buffer, size) memset((buffer), 0, (size))
#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
@@ -167,60 +170,26 @@ static inline uint16_t tu_u16_le2be(uint16_t u16)
return ((uint16_t)(tu_u16_low(u16) << 8)) | tu_u16_high(u16);
}
//------------- Min -------------//
static inline uint8_t tu_min8(uint8_t x, uint8_t y)
{
return (x < y) ? x : y;
}
// Min
static inline uint8_t tu_min8(uint8_t x, uint8_t y) { return (x < y) ? x : y; }
static inline uint16_t tu_min16(uint16_t x, uint16_t y) { return (x < y) ? x : y; }
static inline uint32_t tu_min32(uint32_t x, uint32_t y) { return (x < y) ? x : y; }
static inline uint16_t tu_min16(uint16_t x, uint16_t y)
{
return (x < y) ? x : y;
}
// Max
static inline uint8_t tu_max8(uint8_t x, uint8_t y) { return (x > y) ? x : y; }
static inline uint16_t tu_max16(uint16_t x, uint16_t y) { return (x > y) ? x : y; }
static inline uint32_t tu_max32(uint32_t x, uint32_t y) { return (x > y) ? x : y; }
static inline uint32_t tu_min32(uint32_t x, uint32_t y)
{
return (x < y) ? x : y;
}
// Align
static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
static inline uint32_t tu_align_n (uint32_t alignment, uint32_t value) { return value & ((uint32_t) ~(alignment-1)); }
static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
//------------- Max -------------//
static inline uint32_t tu_max32(uint32_t x, uint32_t y)
{
return (x > y) ? x : y;
}
//------------- Align -------------//
static inline uint32_t tu_align32 (uint32_t value)
{
return (value & 0xFFFFFFE0UL);
}
static inline uint32_t tu_align16 (uint32_t value)
{
return (value & 0xFFFFFFF0UL);
}
static inline uint32_t tu_align_n (uint32_t alignment, uint32_t value)
{
return value & ((uint32_t) ~(alignment-1));
}
static inline uint32_t tu_align4k (uint32_t value)
{
return (value & 0xFFFFF000UL);
}
static inline uint32_t tu_offset4k(uint32_t value)
{
return (value & 0xFFFUL);
}
static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
//------------- Mathematics -------------//
static inline uint32_t tu_abs(int32_t value)
{
return (value < 0) ? (-value) : value;
}
static inline uint32_t tu_abs(int32_t value) { return (value < 0) ? (-value) : value; }
/// inclusive range checking
static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
@@ -228,10 +197,11 @@ static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
return (lower <= value) && (value <= upper);
}
// log2 of a value is its MSB's position
// TODO use clz
static inline uint8_t tu_log2(uint32_t value)
{
uint8_t result = 0; // log2 of a value is its MSB's position
uint8_t result = 0;
while (value >>= 1)
{
@@ -240,6 +210,41 @@ static inline uint8_t tu_log2(uint32_t value)
return result;
}
// Bit
static inline uint32_t tu_bit_set(uint32_t value, uint8_t n) { return value | TU_BIT(n); }
static inline uint32_t tu_bit_clear(uint32_t value, uint8_t n) { return value & (~TU_BIT(n)); }
static inline bool tu_bit_test(uint32_t value, uint8_t n) { return (value & TU_BIT(n)) ? true : false; }
/*------------------------------------------------------------------*/
/* Count number of arguments of __VA_ARGS__
* - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s
* - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
* - _RSEQ_N() is reverse sequential to N to add padding to have
* Nth position is the same as the number of arguments
* - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
*------------------------------------------------------------------*/
#ifndef VA_ARGS_NUM_
#define VA_ARGS_NUM_(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N())
#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__)
#define _GET_NTH_ARG( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define _RSEQ_N() \
62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#endif
#ifdef __cplusplus
}
#endif
+1 -32
View File
@@ -57,44 +57,13 @@
ENTRY(TUSB_ERROR_INVALID_PARA )\
ENTRY(TUSB_ERROR_DEVICE_NOT_READY )\
ENTRY(TUSB_ERROR_INTERFACE_IS_BUSY )\
ENTRY(TUSB_ERROR_HCD_FAILED )\
ENTRY(TUSB_ERROR_HCD_OPEN_PIPE_FAILED )\
ENTRY(TUSB_ERROR_USBH_MOUNT_DEVICE_NOT_RESPOND )\
ENTRY(TUSB_ERROR_USBH_MOUNT_CONFIG_DESC_TOO_LONG )\
ENTRY(TUSB_ERROR_USBH_DESCRIPTOR_CORRUPTED )\
ENTRY(TUSB_ERROR_USBH_XFER_STALLED )\
ENTRY(TUSB_ERROR_USBH_XFER_FAILED )\
ENTRY(TUSB_ERROR_OSAL_TIMEOUT )\
ENTRY(TUSB_ERROR_OSAL_WAITING ) /* only used by OSAL_NONE in the subtask */ \
ENTRY(TUSB_ERROR_OSAL_TASK_FAILED )\
ENTRY(TUSB_ERROR_OSAL_QUEUE_FAILED )\
ENTRY(TUSB_ERROR_OSAL_SEMAPHORE_FAILED )\
ENTRY(TUSB_ERROR_OSAL_MUTEX_FAILED )\
ENTRY(TUSB_ERROR_EHCI_NOT_ENOUGH_QTD )\
ENTRY(TUSB_ERROR_HIDD_DESCRIPTOR_INTERFACE )\
ENTRY(TUSB_ERROR_HIDH_NOT_SUPPORTED_PROTOCOL )\
ENTRY(TUSB_ERROR_HIDH_NOT_SUPPORTED_SUBCLASS )\
ENTRY(TUSB_ERROR_CDC_UNSUPPORTED_SUBCLASS )\
ENTRY(TUSB_ERROR_CDC_UNSUPPORTED_PROTOCOL )\
ENTRY(TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED )\
ENTRY(TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL )\
ENTRY(TUSB_ERROR_MSCH_UNKNOWN_SCSI_COMMAND )\
ENTRY(TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED )\
ENTRY(TUSB_ERROR_HUB_FEATURE_NOT_SUPPORTED )\
ENTRY(TUSB_ERROR_DESCRIPTOR_CORRUPTED )\
ENTRY(TUSB_ERROR_DCD_FAILED )\
ENTRY(TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT )\
ENTRY(TUSB_ERROR_DCD_NOT_ENOUGH_QTD )\
ENTRY(TUSB_ERROR_DCD_OPEN_PIPE_FAILED )\
ENTRY(TUSB_ERROR_DCD_EDPT_XFER )\
ENTRY(TUSB_ERROR_NOT_SUPPORTED_YET )\
ENTRY(TUSB_ERROR_USBD_DEVICE_NOT_CONFIGURED )\
ENTRY(TUSB_ERROR_NOT_SUPPORTED )\
ENTRY(TUSB_ERROR_NOT_ENOUGH_MEMORY )\
ENTRY(TUSB_ERROR_FAILED )\
\
ENTRY(ERR_TUD_INVALID_DESCRIPTOR) \
ENTRY(ERR_TUD_EDPT_OPEN_FAILED) \
/// \brief Error Code returned
typedef enum
+1 -3
View File
@@ -48,9 +48,7 @@ static void tu_fifo_lock(tu_fifo_t *f)
{
if (f->mutex)
{
uint32_t err;
(void) err;
osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER, &err);
osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
}
+1 -2
View File
@@ -67,13 +67,12 @@ typedef struct
uint8_t* buffer ; ///< buffer pointer
uint16_t depth ; ///< max items
uint16_t item_size ; ///< size of each item
bool overwritable ;
volatile uint16_t count ; ///< number of items in queue
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
bool overwritable ;
#if CFG_FIFO_MUTEX
tu_fifo_mutex_t mutex;
#endif
-10
View File
@@ -79,16 +79,6 @@ static inline void tu_timeout_restart(tu_timeout_t* tt)
tt->start = tusb_hal_millis();
}
static inline void tu_timeout_wait(uint32_t msec)
{
tu_timeout_t tt;
tu_timeout_set(&tt, msec);
// blocking delay
while ( !tu_timeout_expired(&tt) ) { }
}
#ifdef __cplusplus
}
#endif
+15 -19
View File
@@ -170,9 +170,9 @@ typedef enum
}misc_protocol_type_t;
enum {
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = BIT_(5),
TUSB_DESC_CONFIG_ATT_SELF_POWER = BIT_(6),
TUSB_DESC_CONFIG_ATT_BUS_POWER = BIT_(7)
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
TUSB_DESC_CONFIG_ATT_SELF_POWER = TU_BIT(6),
TUSB_DESC_CONFIG_ATT_BUS_POWER = TU_BIT(7)
};
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
@@ -181,14 +181,8 @@ enum {
typedef enum
{
TUSB_DEVICE_STATE_UNPLUG = 0 ,
TUSB_DEVICE_STATE_ADDRESSED ,
TUSB_DEVICE_STATE_CONFIGURED ,
TUSB_DEVICE_STATE_SUSPENDED ,
TUSB_DEVICE_STATE_REMOVING ,
TUSB_DEVICE_STATE_SAFE_REMOVE ,
TUSB_DEVICE_STATE_INVALID_PARAMETER
}tusb_device_state_t;
typedef enum
@@ -358,6 +352,7 @@ typedef struct ATTR_PACKED{
uint8_t type : 2; ///< Request type tusb_request_type_t.
uint8_t direction : 1; ///< Direction type. tusb_dir_t
} bmRequestType_bit;
uint8_t bmRequestType;
};
@@ -380,38 +375,39 @@ static inline uint8_t bm_request_type(uint8_t direction, uint8_t type, uint8_t r
//--------------------------------------------------------------------+
// Get direction from Endpoint address
static inline tusb_dir_t edpt_dir(uint8_t addr)
static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
{
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
}
// Get Endpoint number from address
static inline uint8_t edpt_number(uint8_t addr)
static inline uint8_t tu_edpt_number(uint8_t addr)
{
return addr & (~TUSB_DIR_IN_MASK);
}
static inline uint8_t edpt_addr(uint8_t num, tusb_dir_t dir)
static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
{
return num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0);
return num | (dir ? TUSB_DIR_IN_MASK : 0);
}
//--------------------------------------------------------------------+
// Descriptor helper
//--------------------------------------------------------------------+
static inline uint8_t const * descriptor_next(uint8_t const p_desc[])
static inline uint8_t const * tu_desc_next(void const* desc)
{
return p_desc + p_desc[DESC_OFFSET_LEN];
uint8_t const* desc8 = (uint8_t const*) desc;
return desc8 + desc8[DESC_OFFSET_LEN];
}
static inline uint8_t descriptor_type(uint8_t const p_desc[])
static inline uint8_t tu_desc_type(void const* desc)
{
return p_desc[DESC_OFFSET_TYPE];
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
}
static inline uint8_t descriptor_len(uint8_t const p_desc[])
static inline uint8_t tu_desc_len(void const* desc)
{
return p_desc[DESC_OFFSET_LEN];
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
}
// Length of the string descriptors in bytes with slen characters
+10 -6
View File
@@ -90,14 +90,18 @@ typedef struct ATTR_ALIGNED(4)
TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
/*------------------------------------------------------------------*/
/* Device API (Weak is optional)
/* Device API
*------------------------------------------------------------------*/
bool dcd_init (uint8_t rhport);
void dcd_set_address (uint8_t rhport, uint8_t dev_addr);
void dcd_set_config (uint8_t rhport, uint8_t config_num);
bool dcd_init (uint8_t rhport);
void dcd_connect (uint8_t rhport) ATTR_WEAK;
void dcd_disconnect (uint8_t rhport) ATTR_WEAK;
void dcd_int_enable (uint8_t rhport);
void dcd_int_disable(uint8_t rhport);
void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
void dcd_set_config (uint8_t rhport, uint8_t config_num);
// Get current frame number
uint32_t dcd_get_frame_number(uint8_t rhport);
/*------------------------------------------------------------------*/
/* Event Function
+103 -100
View File
@@ -36,8 +36,6 @@
*/
/**************************************************************************/
// This top level class manages the bus state and delegates events to class-specific drivers.
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
@@ -52,15 +50,6 @@
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
#ifndef CFG_TUD_TASK_STACK_SZ
#define CFG_TUD_TASK_STACK_SZ 150
#endif
#ifndef CFG_TUD_TASK_PRIO
#define CFG_TUD_TASK_PRIO 0
#endif
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
@@ -68,7 +57,7 @@ typedef struct {
uint8_t config_num;
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[2][8]; // map endpoint to driver ( 0xff is invalid )
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
}usbd_device_t;
@@ -88,13 +77,13 @@ tud_desc_set_t const* usbd_desc_set = &tud_desc_set;
typedef struct {
uint8_t class_code;
void (* init ) (void);
tusb_error_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request);
void (* init ) (void);
bool (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* control_request_complete ) (uint8_t rhport, tusb_control_request_t const * request);
tusb_error_t (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t, uint32_t);
void (* sof ) (uint8_t rhport);
void (* reset ) (uint8_t);
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t, uint32_t);
void (* sof ) (uint8_t rhport);
void (* reset ) (uint8_t);
} usbd_class_driver_t;
static usbd_class_driver_t const usbd_class_drivers[] =
@@ -139,6 +128,19 @@ static usbd_class_driver_t const usbd_class_drivers[] =
},
#endif
#if CFG_TUD_MIDI
{
.class_code = TUSB_CLASS_AUDIO,
.init = midid_init,
.open = midid_open,
.control_request = midid_control_request,
.control_request_complete = midid_control_request_complete,
.xfer_cb = midid_xfer_cb,
.sof = NULL,
.reset = midid_reset
},
#endif
#if CFG_TUD_CUSTOM_CLASS
{
.class_code = TUSB_CLASS_VENDOR_SPECIFIC,
@@ -153,24 +155,23 @@ static usbd_class_driver_t const usbd_class_drivers[] =
#endif
};
enum { USBD_CLASS_DRIVER_COUNT = sizeof(usbd_class_drivers) / sizeof(usbd_class_driver_t) };
enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SZIE(usbd_class_drivers) };
//--------------------------------------------------------------------+
// DCD Event
//--------------------------------------------------------------------+
OSAL_TASK_DEF(_usbd_task_def, "usbd", usbd_task, CFG_TUD_TASK_PRIO, CFG_TUD_TASK_STACK_SZ);
/*------------- event queue -------------*/
OSAL_QUEUE_DEF(_usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
// Event queue
// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr)
OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
//--------------------------------------------------------------------+
// Prototypes
//--------------------------------------------------------------------+
static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
static bool process_set_config(uint8_t rhport, uint8_t config_number);
static bool process_set_config(uint8_t rhport);
static void const* get_descriptor(tusb_control_request_t const * p_request, uint16_t* desc_len);
void usbd_control_reset (uint8_t rhport);
@@ -188,26 +189,20 @@ bool tud_mounted(void)
//--------------------------------------------------------------------+
// USBD Task
//--------------------------------------------------------------------+
tusb_error_t usbd_init (void)
bool usbd_init (void)
{
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
dcd_init(0);
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
dcd_init(1);
#endif
//------------- Task init -------------//
// Init device queue & task
_usbd_q = osal_queue_create(&_usbd_qdef);
TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
TU_ASSERT(_usbd_q != NULL);
osal_task_create(&_usbd_task_def);
//------------- class init -------------//
// Init class drivers
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) usbd_class_drivers[i].init();
return TUSB_ERROR_NONE;
// Init device controller driver
TU_ASSERT(dcd_init(TUD_OPT_RHPORT));
dcd_int_enable(TUD_OPT_RHPORT);
return true;
}
static void usbd_reset(uint8_t rhport)
@@ -224,8 +219,26 @@ static void usbd_reset(uint8_t rhport)
}
}
// Main device task implementation
static void usbd_task_body(void)
/* USB Device Driver task
* This top level thread manages all device controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
*
@code
int main(void)
{
application_init();
tusb_init();
while(1) // the mainloop
{
application_code();
tud_task(); // tinyusb device task
}
}
@endcode
*/
void tud_task (void)
{
// Loop until there is no more events in the queue
while (1)
@@ -249,14 +262,14 @@ static void usbd_task_body(void)
// Invoke the class callback associated with the endpoint address
uint8_t const ep_addr = event.xfer_complete.ep_addr;
if ( 0 == edpt_number(ep_addr) )
if ( 0 == tu_edpt_number(ep_addr) )
{
// control transfer DATA stage callback
usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
}
else
{
uint8_t const drv_id = _usbd_dev.ep2drv[edpt_dir(ep_addr)][edpt_number(ep_addr)];
uint8_t const drv_id = _usbd_dev.ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)];
TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,);
usbd_class_drivers[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
@@ -265,17 +278,18 @@ static void usbd_task_body(void)
break;
case DCD_EVENT_BUS_RESET:
// note: if task is too slow, we could clear the event of the new attached
usbd_reset(event.rhport);
// TODO remove since if task is too slow, we could clear the event of the new attached
osal_queue_reset(_usbd_q);
break;
case DCD_EVENT_UNPLUGGED:
// note: if task is too slow, we could clear the event of the new attached
usbd_reset(event.rhport);
// TODO remove since if task is too slow, we could clear the event of the new attached
osal_queue_reset(_usbd_q);
tud_umount_cb(); // invoke callback
// invoke callback
if (tud_umount_cb) tud_umount_cb();
break;
case DCD_EVENT_SOF:
@@ -299,25 +313,6 @@ static void usbd_task_body(void)
}
}
/* USB device task
* Thread that handles all device events. With an real RTOS, the task must be a forever loop and never return.
* For codign convenience with no RTOS, we use wrapped sub-function for processing to easily return at any time.
*/
void usbd_task( void* param)
{
(void) param;
#if CFG_TUSB_OS != OPT_OS_NONE
while (1) {
#endif
usbd_task_body();
#if CFG_TUSB_OS != OPT_OS_NONE
}
#endif
}
//--------------------------------------------------------------------+
// Control Request Parser & Handling
//--------------------------------------------------------------------+
@@ -356,7 +351,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
dcd_set_config(rhport, config);
_usbd_dev.config_num = config;
TU_ASSERT( TUSB_ERROR_NONE == process_set_config(rhport, config) );
TU_ASSERT( TUSB_ERROR_NONE == process_set_config(rhport) );
}
break;
@@ -378,7 +373,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
uint8_t const itf = tu_u16_low(p_request->wIndex);
uint8_t const drvid = _usbd_dev.itf2drv[ itf ];
TU_VERIFY (drvid < USBD_CLASS_DRIVER_COUNT );
TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT);
usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete );
@@ -427,7 +422,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// Process Set Configure Request
// This function parse configuration descriptor & open drivers accordingly
static bool process_set_config(uint8_t rhport, uint8_t config_number)
static bool process_set_config(uint8_t rhport)
{
uint8_t const * desc_cfg = (uint8_t const *) usbd_desc_set->config;
TU_ASSERT(desc_cfg != NULL);
@@ -438,12 +433,12 @@ static bool process_set_config(uint8_t rhport, uint8_t config_number)
while( p_desc < desc_cfg + cfg_len )
{
// Each interface always starts with Interface or Association descriptor
if ( TUSB_DESC_INTERFACE_ASSOCIATION == descriptor_type(p_desc) )
if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
{
p_desc = descriptor_next(p_desc); // ignore Interface Association
p_desc = tu_desc_next(p_desc); // ignore Interface Association
}else
{
TU_ASSERT( TUSB_DESC_INTERFACE == descriptor_type(p_desc) );
TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
tusb_desc_interface_t* desc_itf = (tusb_desc_interface_t*) p_desc;
@@ -453,44 +448,44 @@ static bool process_set_config(uint8_t rhport, uint8_t config_number)
{
if ( usbd_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break;
}
TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT ); // unsupported class
TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT );
// Interface number must not be used already TODO alternate interface
TU_ASSERT( 0xff == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] );
_usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id;
uint16_t len=0;
TU_ASSERT_ERR( usbd_class_drivers[drv_id].open( rhport, desc_itf, &len ), false );
TU_ASSERT( len >= sizeof(tusb_desc_interface_t) );
uint16_t itf_len=0;
TU_ASSERT( usbd_class_drivers[drv_id].open( rhport, desc_itf, &itf_len ) );
TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) );
mark_interface_endpoint(p_desc, len, drv_id);
mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, itf_len, drv_id);
p_desc += len; // next interface
p_desc += itf_len; // next interface
}
}
// invoke callback
tud_mount_cb();
if (tud_mount_cb) tud_mount_cb();
return TUSB_ERROR_NONE;
}
// Helper marking endpoint of interface belongs to class driver
static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
{
uint16_t len = 0;
while( len < desc_len )
{
if ( TUSB_DESC_ENDPOINT == descriptor_type(p_desc) )
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
_usbd_dev.ep2drv[ edpt_dir(ep_addr) ][ edpt_number(ep_addr) ] = driver_id;
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
}
len += descriptor_len(p_desc);
p_desc = descriptor_next(p_desc);
len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
}
@@ -578,12 +573,17 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
case DCD_EVENT_XFER_COMPLETE:
// skip zero-length control status complete event, should dcd notifies us.
if ( 0 == edpt_number(event->xfer_complete.ep_addr) && event->xfer_complete.len == 0) break;
if ( 0 == tu_edpt_number(event->xfer_complete.ep_addr) && event->xfer_complete.len == 0) break;
osal_queue_send(_usbd_q, event, in_isr);
TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS,);
break;
// Not an DCD event, just a convenient way to defer ISR function should we need
case USBD_EVT_FUNC_CALL:
osal_queue_send(_usbd_q, event, in_isr);
break;
default: break;
}
}
@@ -591,23 +591,23 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
// helper to send bus signal event
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = 0, .event_id = eid, };
dcd_event_t event = { .rhport = rhport, .event_id = eid, };
dcd_event_handler(&event, in_isr);
}
// helper to send setup received
void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
{
dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_SETUP_RECEIVED };
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
memcpy(&event.setup_received, setup, 8);
dcd_event_handler(&event, true);
dcd_event_handler(&event, in_isr);
}
// helper to send transfer complete event
void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
{
dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_XFER_COMPLETE };
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
event.xfer_complete.ep_addr = ep_addr;
event.xfer_complete.len = xferred_bytes;
@@ -619,30 +619,33 @@ void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
// Helper to parse an pair of endpoint descriptors (IN & OUT)
bool usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* ep_desc, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
{
for(int i=0; i<2; i++)
{
TU_ASSERT(TUSB_DESC_ENDPOINT == p_desc_ep->bDescriptorType &&
xfer_type == p_desc_ep->bmAttributes.xfer, TUSB_ERROR_DESCRIPTOR_CORRUPTED);
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType &&
xfer_type == ep_desc->bmAttributes.xfer );
TU_ASSERT( dcd_edpt_open(rhport, p_desc_ep), TUSB_ERROR_DCD_OPEN_PIPE_FAILED );
TU_ASSERT(dcd_edpt_open(rhport, ep_desc));
if ( edpt_dir(p_desc_ep->bEndpointAddress) == TUSB_DIR_IN )
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
{
(*ep_in) = p_desc_ep->bEndpointAddress;
(*ep_in) = ep_desc->bEndpointAddress;
}else
{
(*ep_out) = p_desc_ep->bEndpointAddress;
(*ep_out) = ep_desc->bEndpointAddress;
}
p_desc_ep = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_desc_ep );
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
}
return TUSB_ERROR_NONE;
return true;
}
void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr )
// Helper to defer an isr function
void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr)
{
dcd_event_t event =
{
@@ -653,7 +656,7 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr )
event.func_call.func = func;
event.func_call.param = param;
osal_queue_send(_usbd_q, &event, in_isr);
dcd_event_handler(&event, in_isr);
}
#endif
+1
View File
@@ -81,6 +81,7 @@ extern tud_desc_set_t tud_desc_set;
// APPLICATION API
//--------------------------------------------------------------------+
bool tud_mounted(void);
void tud_task (void);
//--------------------------------------------------------------------+
// APPLICATION CALLBACK (WEAK is optional)
+45 -18
View File
@@ -101,18 +101,19 @@ enum
ITF_NUM_TOTAL
};
enum {
ITF_STR_LANGUAGE = 0 ,
ITF_STR_MANUFACTURER ,
ITF_STR_PRODUCT ,
ITF_STR_SERIAL ,
enum
{
ITF_STR_LANGUAGE = 0 ,
ITF_STR_MANUFACTURER ,
ITF_STR_PRODUCT ,
ITF_STR_SERIAL ,
#if CFG_TUD_CDC
ITF_STR_CDC ,
ITF_STR_CDC ,
#endif
#if CFG_TUD_MSC
ITF_STR_MSC ,
ITF_STR_MSC ,
#endif
#if CFG_TUD_HID_KEYBOARD && CFG_TUD_HID_KEYBOARD_BOOT
@@ -133,23 +134,51 @@ enum {
#define _EP_OUT(x) (x)
// CDC
#define EP_CDC_NOTIF _EP_IN ( ITF_NUM_CDC+1 )
#ifdef CFG_TUD_DESC_CDC_EPNUM_NOTIF
#define EP_CDC_NOTIF _EP_IN (CFG_TUD_DESC_CDC_EPNUM_NOTIF)
#else
#define EP_CDC_NOTIF _EP_IN ( ITF_NUM_CDC+1 )
#endif
#define EP_CDC_NOTIF_SIZE 8
#define EP_CDC_OUT _EP_OUT( ITF_NUM_CDC+2 )
#define EP_CDC_IN _EP_IN ( ITF_NUM_CDC+2 )
#ifdef CFG_TUD_DESC_CDC_EPNUM
#define EP_CDC_OUT _EP_OUT( CFG_TUD_DESC_CDC_EPNUM )
#define EP_CDC_IN _EP_IN ( CFG_TUD_DESC_CDC_EPNUM )
#else
#define EP_CDC_OUT _EP_OUT( ITF_NUM_CDC+2 )
#define EP_CDC_IN _EP_IN ( ITF_NUM_CDC+2 )
#endif
// Mass Storage
#define EP_MSC_OUT _EP_OUT( ITF_NUM_MSC+1 )
#define EP_MSC_IN _EP_IN ( ITF_NUM_MSC+1 )
#ifdef CFG_TUD_DESC_MSC_EPNUM
#define EP_MSC_OUT _EP_OUT( CFG_TUD_DESC_MSC_EPNUM )
#define EP_MSC_IN _EP_IN ( CFG_TUD_DESC_MSC_EPNUM )
#else
#define EP_MSC_OUT _EP_OUT( ITF_NUM_MSC+1 )
#define EP_MSC_IN _EP_IN ( ITF_NUM_MSC+1 )
#endif
#if TUD_OPT_HIGH_SPEED
#define EP_MSC_SIZE 512
#else
#define EP_MSC_SIZE 64
#endif
// HID Keyboard with boot protocol
#define EP_HID_KBD_BOOT _EP_IN ( ITF_NUM_HID_BOOT_KBD+1 )
#ifdef CFG_TUD_DESC_HID_KEYBOARD_EPNUM
#define EP_HID_KBD_BOOT _EP_IN ( CFG_TUD_DESC_HID_KEYBOARD_EPNUM )
#else
#define EP_HID_KBD_BOOT _EP_IN ( ITF_NUM_HID_BOOT_KBD+1 )
#endif
#define EP_HID_KBD_BOOT_SZ 8
// HID Mouse with boot protocol
#define EP_HID_MSE_BOOT _EP_IN ( ITF_NUM_HID_BOOT_MSE+1 )
#ifdef CFG_TUD_DESC_HID_MOUSE_EPNUM
#define EP_HID_MSE_BOOT _EP_IN ( CFG_TUD_DESC_HID_MOUSE_EPNUM )
#else
#define EP_HID_MSE_BOOT _EP_IN ( ITF_NUM_HID_BOOT_MSE+1 )
#endif
#define EP_HID_MSE_BOOT_SZ 8
// HID composite = keyboard + mouse + gamepad + etc ...
@@ -466,7 +495,7 @@ desc_auto_cfg_t const _desc_auto_config_struct =
.bDescriptorType = TUSB_DESC_ENDPOINT,
.bEndpointAddress = EP_MSC_OUT,
.bmAttributes = { .xfer = TUSB_XFER_BULK },
.wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE},
.wMaxPacketSize = { .size = EP_MSC_SIZE},
.bInterval = 1
},
@@ -476,7 +505,7 @@ desc_auto_cfg_t const _desc_auto_config_struct =
.bDescriptorType = TUSB_DESC_ENDPOINT,
.bEndpointAddress = EP_MSC_IN,
.bmAttributes = { .xfer = TUSB_XFER_BULK },
.wMaxPacketSize = { .size = CFG_TUD_MSC_EPSIZE},
.wMaxPacketSize = { .size = EP_MSC_SIZE},
.bInterval = 1
}
},
@@ -563,7 +592,6 @@ desc_auto_cfg_t const _desc_auto_config_struct =
#endif // boot mouse
#if AUTO_DESC_HID_GENERIC
//------------- HID Generic Multiple report -------------//
.hid_generic =
{
@@ -601,7 +629,6 @@ desc_auto_cfg_t const _desc_auto_config_struct =
.bInterval = 0x0A
}
}
#endif // hid generic
};
+5 -2
View File
@@ -68,6 +68,7 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t _usbd_ctrl_buf[CFG_TUD_ENDOINT0_
void usbd_control_reset (uint8_t rhport)
{
(void) rhport;
tu_varclr(&_control_state);
}
@@ -82,7 +83,6 @@ bool usbd_control_status(uint8_t rhport, tusb_control_request_t const * request)
return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0);
}
// Each transaction is up to endpoint0's max packet size
static bool start_control_data_xact(uint8_t rhport)
{
@@ -126,8 +126,11 @@ bool usbd_control_xfer(uint8_t rhport, tusb_control_request_t const * request, v
}
// callback when a transaction complete on DATA stage of control endpoint
bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
(void) ep_addr;
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
{
memcpy(_control_state.buffer, _usbd_ctrl_buf, xferred_bytes);
+2 -4
View File
@@ -51,9 +51,7 @@ extern tud_desc_set_t const* usbd_desc_set;
//--------------------------------------------------------------------+
// INTERNAL API for stack management
//--------------------------------------------------------------------+
tusb_error_t usbd_init (void);
void usbd_task (void* param);
bool usbd_init (void);
// Carry out Data and Status stage of control transfer
// - If len = 0, it is equivalent to sending status only
@@ -70,7 +68,7 @@ void usbd_control_stall(uint8_t rhport);
/* Helper
*------------------------------------------------------------------*/
// helper to parse an pair of In and Out endpoint descriptors. They must be consecutive
tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
bool usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
void usbd_defer_func( osal_task_func_t func, void* param, bool in_isr );
+337 -536
View File
File diff suppressed because it is too large Load Diff
+175 -190
View File
@@ -57,8 +57,6 @@
* SITD: Split ISO Transfer Descriptor for full-speed
* SMASK: Start Split mask for Slipt Transaction
* CMASK: Complete Split mask for Slipt Transaction
* RO: Read Only
* R/WC: Read, Write to Clear
*/
#ifdef __cplusplus
@@ -68,9 +66,6 @@
//--------------------------------------------------------------------+
// EHCI CONFIGURATION & CONSTANTS
//--------------------------------------------------------------------+
#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed
#define EHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS)
#define EHCI_CFG_FRAMELIST_SIZE_BITS 7 /// Framelist Size (NXP specific) (0:1024) - (1:512) - (2:256) - (3:128) - (4:64) - (5:32) - (6:16) - (7:8)
#define EHCI_FRAMELIST_SIZE (1024 >> EHCI_CFG_FRAMELIST_SIZE_BITS)
@@ -86,15 +81,17 @@ TU_VERIFY_STATIC(EHCI_CFG_FRAMELIST_SIZE_BITS <= 7, "incorrect value");
//--------------------------------------------------------------------+
// EHCI Data Structure
//--------------------------------------------------------------------+
enum ehci_queue_element_type_{
EHCI_QUEUE_ELEMENT_ITD = 0 , ///< 0
EHCI_QUEUE_ELEMENT_QHD , ///< 1
EHCI_QUEUE_ELEMENT_SITD , ///< 2
EHCI_QUEUE_ELEMENT_FSTN ///< 3
enum
{
EHCI_QTYPE_ITD = 0 ,
EHCI_QTYPE_QHD ,
EHCI_QTYPE_SITD ,
EHCI_QTYPE_FSTN
};
/// EHCI PID
enum tusb_pid_{
enum
{
EHCI_PID_OUT = 0 ,
EHCI_PID_IN ,
EHCI_PID_SETUP
@@ -109,13 +106,14 @@ typedef union {
};
}ehci_link_t;
/// Queue Element Transfer Descriptor (section 3.5)
typedef struct {
/// Word 0: Next QTD Pointer
/// Queue Element Transfer Descriptor
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with ATTR_ALIGNED(32)
typedef struct
{
// Word 0: Next QTD Pointer
ehci_link_t next;
/// Word 1: Alternate Next QTD Pointer (not used)
// Word 1: Alternate Next QTD Pointer (not used)
union{
ehci_link_t alternate;
struct {
@@ -126,63 +124,58 @@ typedef struct {
};
};
/// Word 2: qTQ Token
volatile uint32_t pingstate_err : 1 ; ///< If the QH.EPSfield indicates a High-speed device and the PID_Codeindicates an OUT endpoint, then this is the state bit for the Ping protocol. 0b=OUT 1b=PING
volatile uint32_t non_hs_split_state : 1 ; ///< This bit is ignored by the host controller unless the QH.EPSfield indicates a full- or low-speed endpoint. When a Full- or Low-speed device, the host controller uses this bit to track the state of the split-transaction. The functional requirements of the host controller for managing this state bit and the split transaction protocol depends on whether the endpoint is in the periodic or asynchronous schedule. 0b=Start Split 1b=Complete Split
volatile uint32_t non_hs_period_missed_uframe : 1 ; ///< This bit is ignored unless the QH.EPSfield indicates a full- or low-speed endpoint and the queue head is in the periodic list. This bit is set when the host controller detected that a host-induced hold-off caused the host controller to miss a required complete-split transaction. If the host controller sets this bit to a one, then it remains a one for the duration of the transfer.
volatile uint32_t xact_err : 1 ; ///< Set to a one by the Host Controller during status update in the case where the host did not receive a valid response from the device (Timeout, CRC, Bad PID, etc.)
volatile uint32_t babble_err : 1 ; ///< Set to a 1 by the Host Controller during status update when a babble is detected during the transaction. In addition to setting this bit, the Host Controller also sets the Haltedbit to a 1
volatile uint32_t buffer_err : 1 ; ///< Set to a 1 by the Host Controller during status update to indicate that the Host Controller is unable to keep up with the reception of incoming data (overrun) or is unable to supply data fast enough during transmission (underrun)
volatile uint32_t halted : 1 ; ///< Set to a 1 by the Host Controller during status updates to indicate that a serious error has occurred at the device/endpoint addressed by this qTD. This can be caused by babble, the error counter counting down to zero, or reception of the STALL handshake from the device during a transaction. Any time that a transaction results in the Halted bit being set to a one, the Active bit is also set to 0
volatile uint32_t active : 1 ; ///< Set to 1 by software to enable the execution of transactions by the Host Controller
// Word 2: qTQ Token
volatile uint32_t ping_err : 1 ; ///< For Highspeed: 0 Out, 1 Ping. Full/Slow used as error indicator
volatile uint32_t non_hs_split_state : 1 ; ///< Used by HC to track the state of slipt transaction
volatile uint32_t non_hs_missed_uframe : 1 ; ///< HC misses a complete slip transaction
volatile uint32_t xact_err : 1 ; ///< Error (Timeout, CRC, Bad PID ... )
volatile uint32_t babble_err : 1 ; ///< Babble detected, also set Halted bit to 1
volatile uint32_t buffer_err : 1 ; ///< Data overrun/underrun error
volatile uint32_t halted : 1 ; ///< Serious error or STALL received
volatile uint32_t active : 1 ; ///< Start transfer, clear by HC when complete
uint32_t pid : 2 ; ///< This field is an encoding of the token which should be used for transactions associated with this transfer descriptor. 00=OUT 01=IN 10=SETUP
volatile uint32_t cerr : 2 ; ///< Error Counter, This field is a 2-bit down counter that keeps track of the number of consecutive Errors detected while executing this qTD
volatile uint32_t current_page : 3 ; ///< This field is used as an index into the qTD buffer pointer list
uint32_t int_on_complete : 1 ; ///< If this bit is set to a one, it specifies that when this qTD is completed, the Host Controller should issue an interrupt at the next interrupt threshold
uint32_t pid : 2 ; ///< 0: OUT, 1: IN, 2 Setup
volatile uint32_t err_count : 2 ; ///< Error Counter of consecutive errors
volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list
uint32_t int_on_complete : 1 ; ///< Interrupt on complete
volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction
volatile uint32_t data_toggle : 1 ; ///< Data Toogle bit
volatile uint32_t total_bytes : 15 ; ///< This field specifies the total number of bytes to be moved with this transfer descriptor
volatile uint32_t data_toggle : 1 ; ///< This is the data toggle sequence bit
uint32_t : 0 ; // padding to the end of current storage unit
// End of Word 2
/// 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];
} ehci_qtd_t; // XXX qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with ATTR_ALIGNED(32)
} ehci_qtd_t;
TU_VERIFY_STATIC( sizeof(ehci_qtd_t) == 32, "size is not correct" );
/// Queue Head (section 3.6)
typedef struct ATTR_ALIGNED(32) {
/// Word 0: Queue Head Horizontal Link Pointer
/// Queue Head
typedef struct ATTR_ALIGNED(32)
{
// Word 0: Next QHD
ehci_link_t next;
/// Word 1 : Endpoint Characteristics
uint32_t device_address : 7 ; ///< This field selects the specific device serving as the data source or sink
uint32_t non_hs_period_inactive_next_xact : 1 ; ///< This bit is used by system software to request that the host controller set the Active bit to zero. See Section 4.12.2.5 for full operational details
uint32_t endpoint_number : 4 ; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
uint32_t endpoint_speed : 2 ; ///< This is the speed of the associated endpoint 00b=Full 01b=Low 10b=High 11b=Reserved
uint32_t data_toggle_control : 1 ; ///< This bit specifies where the host controller should get the initial data toggle on an overlay transition. 0b=Ignore DT bit of qTD, 1b=Use DT bit of qTD
uint32_t head_list_flag : 1 ; ///< This bit is set by System Software to mark a queue head as being the head of the reclamation list. See Section 4.8 for operational model
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t non_hs_control_endpoint : 1 ; ///< If the QH.EPSfield indicates the endpoint is not a high-speed device, and the endpoint is an control endpoint, then software must set this bit to a one. Otherwise it should always set this bit to a zero.
uint32_t nak_count_reload : 4 ; ///< This field contains a value, which is used by the host controller to reload the Nak Counter field.
uint32_t : 0 ; // padding to the end of current storage unit
// End of Word 1
// Word 1: Endpoint Characteristics
uint32_t dev_addr : 7 ; ///< device address
uint32_t fl_inactive_next_xact : 1 ; ///< Only valid for Periodic with Full/Slow speed
uint32_t ep_number : 4 ; ///< EP number
uint32_t ep_speed : 2 ; ///< 0: Full, 1: Low, 2: High
uint32_t data_toggle_control : 1 ; ///< 0: use DT in qHD, 1: use DT in qTD
uint32_t head_list_flag : 1 ; ///< Head of the queue
uint32_t max_packet_size : 11 ; ///< Max packet size
uint32_t fl_ctrl_ep_flag : 1 ; ///< 1 if is Full/Low speed control endpoint
uint32_t nak_reload : 4 ; ///< Used by HC
/// Word 2 : Endpoint Capabilities
uint32_t interrupt_smask : 8 ; ///< This field is used for all endpoint speeds. Software should set this field to a zero when the queue head is on the asynchronous schedule. A non-zero value in this field indicates an interrupt endpoint
uint32_t non_hs_interrupt_cmask : 8 ; ///< This field is ignored by the host controller unless the EPSfield indicates this device is a low- or full-speed device and this queue head is in the periodic list. This field (along with the Activeand SplitX-statefields) is used to determine during which micro-frames the host controller should execute a complete-split transaction
uint32_t hub_address : 7 ; ///< This field is ignored by the host controller unless the EPSfield indicates a full- or low-speed device. The value is the USB device address of the USB 2.0 Hub below which the full- or low-speed device associated with this endpoint is attached. This field is used in the split-transaction protocol. See Section 4.12.
uint32_t hub_port : 7 ; ///< This field is ignored by the host controller unless the EPSfield indicates a full- or low-speed device. The value is the port number identifier on the USB 2.0 Hub (for hub at device address Hub Addrbelow), below which the full- or low-speed device associated with this endpoint is attached. This information is used in the split-transaction protocol. See Section 4.12.
uint32_t mult : 2 ; ///< This field is a multiplier used to key the host controller as the number of successive packets the host controller may submit to the endpoint in the current execution. 00b=Reserved 01b,10b,11b= 1 (2, 3) Transaction for this endpoint/micro frame
uint32_t : 0 ; // padding to the end of current storage unit
// End of Word 2
// Word 2: Endpoint Capabilities
uint32_t int_smask : 8 ; ///< Interrupt Schedule Mask
uint32_t fl_int_cmask : 8 ; ///< Split Completion Mask for Full/Slow speed
uint32_t fl_hub_addr : 7 ; ///< Hub Address for Full/Slow speed
uint32_t fl_hub_port : 7 ; ///< Hub Port for Full/Slow speed
uint32_t mult : 2 ; ///< Transaction per micro frame
/// Word 3: Current qTD Pointer
// Word 3: Current qTD Pointer
volatile uint32_t qtd_addr;
/// Word 4-11: Transfer Overlay
// Word 4-11: Transfer Overlay
volatile ehci_qtd_t qtd_overlay;
//--------------------------------------------------------------------+
@@ -190,13 +183,12 @@ typedef struct ATTR_ALIGNED(32) {
/// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
uint8_t used;
uint8_t is_removing;
uint8_t pid_non_control;
uint8_t class_code;
uint8_t removing; // removed from asyn list, waiting for async advance
uint8_t pid;
uint8_t interval_ms; // polling interval in frames (or milisecond)
uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set
uint8_t interval_ms; // polling interval in frames (or milisecond)
uint8_t reserved;
uint8_t reserved2[2];
ehci_qtd_t * volatile p_qtd_list_head; // head of the scheduled TD list
ehci_qtd_t * volatile p_qtd_list_tail; // tail of the scheduled TD list
@@ -206,10 +198,10 @@ TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
/// Highspeed Isochronous Transfer Descriptor (section 3.3)
typedef struct ATTR_ALIGNED(32) {
/// Word 0: Next Link Pointer
// Word 0: Next Link Pointer
ehci_link_t next;
/// Word 1-8: iTD Transaction Status and Control List
// Word 1-8: iTD Transaction Status and Control List
struct {
// iTD Control
volatile uint32_t offset : 12 ; ///< This field is a value that is an offset, expressed in bytes, from the beginning of a buffer.
@@ -224,7 +216,7 @@ typedef struct ATTR_ALIGNED(32) {
volatile uint32_t active : 1 ; ///< Set to 1 by software to enable the execution of an isochronous transaction by the Host Controller
} xact[8];
///Word 9-15 Buffer Page Pointer List (Plus)
// Word 9-15 Buffer Page Pointer List (Plus)
uint32_t BufferPointer[7];
// // FIXME: Store meta data into buffer pointer reserved for saving memory
@@ -237,29 +229,28 @@ typedef struct ATTR_ALIGNED(32) {
TU_VERIFY_STATIC( sizeof(ehci_itd_t) == 64, "size is not correct" );
/// Split (Full-Speed) Isochronous Transfer Descriptor
typedef struct ATTR_ALIGNED(32) {
/// Word 0: Next Link Pointer
typedef struct ATTR_ALIGNED(32)
{
// Word 0: Next Link Pointer
ehci_link_t next;
/// Word 1: siTD Endpoint Characteristics
uint32_t device_address : 7; ///< This field selects the specific device serving as the data source or sink.
uint32_t : 1; ///< reserved
uint32_t endpoint_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
uint32_t : 4; ///< This field is reserved and should be set to zero.
uint32_t hub_address : 7; ///< This field holds the device address of the transaction translators hub.
uint32_t : 1; ///< reserved
uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
uint32_t : 0; // padding to the end of current storage unit
// End of Word 1
// Word 1: siTD Endpoint Characteristics
uint32_t dev_addr : 7; ///< This field selects the specific device serving as the data source or sink.
uint32_t : 1; ///< reserved
uint32_t ep_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
uint32_t : 4; ///< This field is reserved and should be set to zero.
uint32_t hub_addr : 7; ///< This field holds the device address of the transaction translators hub.
uint32_t : 1; ///< reserved
uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
uint32_t : 0; // padding to the end of current storage unit
/// Word 2: Micro-frame Schedule Control
uint8_t interrupt_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
uint8_t non_hs_interrupt_cmask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
// Word 2: Micro-frame Schedule Control
uint8_t int_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
uint8_t fl_int_cmask; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
uint16_t reserved ; ///< reserved
// End of Word 2
/// Word 3: siTD Transfer Status and Control
// Word 3: siTD Transfer Status and Control
// Status [7:0] TODO indentical to qTD Token'status --> refractor later
volatile uint32_t : 1 ; // reserved
volatile uint32_t split_state : 1 ;
@@ -276,7 +267,6 @@ typedef struct ATTR_ALIGNED(32) {
volatile uint32_t page_select : 1 ; ///< Used to indicate which data page pointer should be concatenated with the CurrentOffsetfield to construct a data buffer pointer
uint32_t int_on_complete : 1 ; ///< Do not interrupt when transaction is complete. 1 = Do interrupt when transaction is complete
uint32_t : 0 ; // padding to the end of current storage unit
// End of Word 3
/// Word 4-5: Buffer Pointer List
uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
@@ -304,17 +294,17 @@ TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
// EHCI Operational Register
//--------------------------------------------------------------------+
enum ehci_interrupt_mask_{
EHCI_INT_MASK_USB = BIT_(0),
EHCI_INT_MASK_ERROR = BIT_(1),
EHCI_INT_MASK_PORT_CHANGE = BIT_(2),
EHCI_INT_MASK_USB = TU_BIT(0),
EHCI_INT_MASK_ERROR = TU_BIT(1),
EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2),
EHCI_INT_MASK_FRAMELIST_ROLLOVER = BIT_(3),
EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = BIT_(4),
EHCI_INT_MASK_ASYNC_ADVANCE = BIT_(5),
EHCI_INT_MASK_NXP_SOF = BIT_(7),
EHCI_INT_MASK_FRAMELIST_ROLLOVER = TU_BIT(3),
EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = TU_BIT(4),
EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5),
EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
EHCI_INT_MASK_NXP_ASYNC = BIT_(18),
EHCI_INT_MASK_NXP_PERIODIC = BIT_(19),
EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18),
EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19),
EHCI_INT_MASK_ALL =
EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE |
@@ -333,9 +323,9 @@ enum ehci_usbcmd_pos_ {
};
enum ehci_portsc_change_mask_{
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = BIT_(1),
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = BIT_(3),
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = BIT_(5),
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = TU_BIT(3),
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
EHCI_PORTSC_MASK_ALL =
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
@@ -343,135 +333,130 @@ enum ehci_portsc_change_mask_{
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
};
typedef volatile struct {
typedef volatile struct
{
union {
uint32_t usb_cmd ; ///< The Command Register indicates the command to be executed by the serial bus host controller. Writing to the register causes a command to be executed
uint32_t command;
struct {
uint32_t run_stop : 1 ; ///< Default 0b. 1=Run. 0=Stop. When set to a 1, the Host Controller proceeds with execution of the schedule. The Host Controller continues execution as long as this bit is set to a 1. When this bit is set to 0, the Host Controller completes the current and any actively pipelined transactions on the USB and then halts. The Host Controller must halt within 16 micro-frames after software clears the Run bit. The HC Halted bit in the status register indicates when the Host Controller has finished its pending pipelined transactions and has entered the stopped state. Software must not write a one to this field unless the host controller is in the Halted state (i.e. HCHaltedin the USBSTS register is a one). Doing so will yield undefined results.
uint32_t reset : 1 ; ///< his control bit is used by software to reset the host controller. The effects of this on Root Hub registers are similar to a Chip Hardware Reset. When software writes a one to this bit, the Host Controller resets its internal pipelines, timers, counters, state machines, etc. to their initial value. Any transaction currently in progress on USB is immediately terminated. A USB reset is not driven on downstream ports.This bit is set to zero by the Host Controller when the reset process is complete. Software cannot terminate the reset process early by writing a zero to this register. Software should not set this bit to a one when the HCHaltedbit in the USBSTS register is a zero. Attempting to reset an actively running host controller will result in undefined behavior.
uint32_t framelist_size : 2 ; ///< This field is R/W only if Programmable Frame List Flagin the HCCPARAMS registers is set to a one. This field specifies the size of the frame list.00b 1024 elements (4096 bytes) Default value 01b 512 elements (2048 bytes) 10b 256 elements (1024 bytes)
uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
uint32_t reset : 1 ; ///< SW write 1 to reset HC, clear by HC when complete
uint32_t framelist_size : 2 ; ///< Frame List size 0: 1024, 1: 512, 2: 256
uint32_t periodic_enable : 1 ; ///< This bit controls whether the host controller skips processing the Periodic Schedule. Values mean: 0b Do not process the Periodic Schedule 1b Use the PERIODICLISTBASE register to access the Periodic Schedule.
uint32_t async_enable : 1 ; ///< This bit controls whether the host controller skips processing the Asynchronous Schedule. Values mean: 0b Do not process the Asynchronous Schedule 1b Use the ASYNCLISTADDR register to access the Asynchronous Schedule.
uint32_t advacne_async : 1 ; ///< This bit is used as a doorbell by software to tell the host controller to issue an interrupt the next time it advances asynchronous schedule. Software must write a 1 to this bit to ringthe doorbell. When the host controller has evicted all appropriate cached schedule state, it sets the Interrupt on Async Advancestatus bit in the USBSTS register. If the Interrupt on Async Advance Enablebit in the USBINTR register is a one then the host controller will assert an interrupt at the next interrupt threshold. See Section 4.8.2 for operational details. The host controller sets this bit to a zero after it has set the Interrupt on Async Advance status bit in the USBSTS register to a one. Software should not write a one to this bit when the asynchronous schedule is disabled. Doing so will yield undefined results.
uint32_t light_reset : 1 ; ///< This control bit is not required. If implemented, it allows the driver to reset the EHCI controller without affecting the state of the ports or the relationship to the companion host controllers. For example, the PORSTC registers should not be reset to their default values and the CF bit setting should not go to zero (retaining port ownership relationships). A host software read of this bit as zero indicates the Light Host Controller Reset has completed and it is safe for host software to re-initialize the host controller. A host software read of this bit as a one indicates the Light Host Controller Reset has not yet completed.
uint32_t async_park : 2 ; ///< It contains a count of the number of successive transactions the host controller is allowed to execute from a high-speed queue head on the Asynchronous schedule before continuing traversal of the Asynchronous schedule. See Section 4.10.3.2 for full operational details. Valid values are 1h to 3h. Software must not write a zero to this bit when Park Mode Enableis a one as this will result in undefined behavior.
uint32_t : 1 ; ///< reserved
uint32_t async_park_enable : 1 ; ///< Software uses this bit to enable or disable Park mode. When this bit is one, Park mode is enabled. When this bit is a zero, Park mode is disabled.
uint32_t : 3 ; ///< reserved
uint32_t async_adv_doorbell : 1 ; ///< Tell HC to interrupt next time it advances async list. Clear by HC
uint32_t light_reset : 1 ; ///< Reset HC without affecting ports state
uint32_t async_park_count : 2 ; ///< not used by tinyusb
uint32_t : 1 ;
uint32_t async_park_enable : 1 ; ///< Enable park mode, not used by tinyusb
uint32_t : 3 ;
uint32_t nxp_framelist_size_msb : 1 ; ///< NXP customized : Bit 2 of the Frame List Size bits \n 011b: 128 elements \n 100b: 64 elements \n 101b: 32 elements \n 110b: 16 elements \n 111b: 8 elements
uint32_t int_threshold : 8 ; ///< Default 08h. This field is used by system software to select the maximum rate at which the host controller will issue interrupts. The only valid values are defined below. If software writes an invalid value to this register, the results are undefined. Value Maximum Interrupt Interval 00h Reserved 01h 1 micro-frame 02h 2 micro-frames 04h 4 micro-frames 08h 8 micro-frames (default, equates to 1 ms) 10h 16 micro-frames (2 ms) 20h 32 micro-frames (4 ms) 40h 64 micro-frames (8 ms) Refer to Section 4.15 for interrupts affected by this register. Any other value in this register yields undefined results. Software modifications to this bit while HCHalted bit is equal to zero results in undefined behavior.
uint32_t : 0 ; // padding to the boundary of storage unit
}usb_cmd_bit;
uint32_t int_threshold : 8 ; ///< Default 08h. Interrupt rate in unit of micro frame
}command_bm;
};
union {
uint32_t usb_sts ; ///< This register indicates pending interrupts and various states of the Host Controller. The status resulting from a transaction on the serial bus is not indicated in this register. Software sets a bit to 0 in this register by writing a 1 to it. See Section 4.15 for additional information concerning USB interrupt conditions.
uint32_t status;
struct {
uint32_t usb : 1 ; ///< R/WC The Host Controller sets this bit to 1 on the completion of a USB transaction, which results in the retirement of a Transfer Descriptor that had its IOC bit set. \n The Host Controller also sets this bit to 1 when a short packet is detected (actual number of bytes received was less than the expected number of bytes).
uint32_t usb_error : 1 ; ///< R/WC The Host Controller sets this bit to 1 when completion of a USB transaction results in an error condition (e.g., error counter underflow). If the TD on which the error interrupt occurred also had its IOC bit set, both this bit and USBINT bit are set. See Section 4.15.1 for a list of the USB errors that will result in this bit being set to a one.
uint32_t port_change_detect : 1 ; ///< R/WC The Host Controller sets this bit to a one when any port for which the Port Ownerbit is set to zero (see Section 2.3.9) has a change bit transition from a zero to a one or a Force Port Resumebit transition from a zero to a one as a result of a J-K transition detected on a suspended port.
uint32_t framelist_rollover : 1 ; ///< R/WC The Host Controller sets this bit to a one when the Frame List Index(see Section 2.3.4) rolls over from its maximum value to zero. The exact value at which the rollover occurs depends on the frame list size. For example, if the frame list size (as programmed in the Frame List Sizefield of the USBCMD register) is 1024, the Frame Index Registerrolls over every time FRINDEX[13] toggles. Similarly, if the size is 512, the Host Controller sets this bit to a one every time FRINDEX[12] toggles.
uint32_t pci_host_system_error : 1 ; ///< R/WC (not used by NXP) The Host Controller sets this bit to 1 when a serious error occurs during a host system access involving the Host Controller module. In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and PCI Target Abort. When this error occurs, the Host Controller clears the Run/Stop bit in the Command register to prevent further execution of the scheduled TDs.
uint32_t async_advance : 1 ; ///< R/WC 0=Default. System software can force the host controller to issue an interrupt the next time the host controller advances the asynchronous schedule by writing a one to the Interrupt on Async Advance Doorbell bit in the USBCMD register. This status bit indicates the assertion of that interrupt source.
uint32_t : 1 ; ///< These bits are reserved and should be set to zero.
uint32_t nxp_int_sof : 1 ; ///< R/WC NXP customized: this bit will be set every 125us and can be used by host controller driver as a time base.
uint32_t : 4 ; ///< These bits are reserved and should be set to zero.
uint32_t hc_halted : 1 ; ///< Read-Only 1=Default. This bit is a zero whenever the Run/Stop bit is a one. The Host Controller sets this bit to one after it has stopped executing as a result of the Run/Stop bit being set to 0, either by software or by the Host Controller hardware (e.g. internal error).
uint32_t reclamation : 1 ; ///< Read-Only 0=Default. This is a read-only status bit, which is used to detect an empty asynchronous schedule. The operational model of empty schedule detection is described in Section 4.8.3. The valid transitions for this bit are described in Section 4.8.6.
uint32_t period_schedule_status : 1 ; ///< Read-Only The bit reports the current real status of the Periodic Schedule. If this bit is a zero then the status of the Periodic Schedule is disabled. If this bit is a one then the status of the Periodic Schedule is enabled
uint32_t async_schedule_status : 1 ; ///< Read-Only 0=Default. The bit reports the current real status of the Asynchronous Schedule. If this bit is a zero then the status of the Asynchronous Schedule is disabled. If this bit is a one then the status of the Asynchronous Schedule is enabled.
uint32_t : 2 ; ///< reseved
uint32_t nxp_int_async : 1 ; ///< R/WC NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule.
uint32_t nxp_int_period : 1 ; ///< R/WC NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule.
uint32_t : 12 ; ///< reserved
uint32_t : 0 ; // padding to the boundary of storage unit
}usb_sts_bit;
uint32_t usb : 1 ; ///< qTD with IOC is retired
uint32_t usb_error : 1 ; ///< qTD retired due to error
uint32_t port_change_detect : 1 ; ///< Set when PortOwner or ForcePortResume change from 0 -> 1
uint32_t framelist_rollover : 1 ; ///< R/WC The Host Controller sets this bit to a one when the Frame List Index(see Section 2.3.4) rolls over from its maximum value to zero. The exact value at which the rollover occurs depends on the frame list size. For example, if the frame list size (as programmed in the Frame List Sizefield of the USBCMD register) is 1024, the Frame Index Registerrolls over every time FRINDEX[13] toggles. Similarly, if the size is 512, the Host Controller sets this bit to a one every time FRINDEX[12] toggles.
uint32_t pci_host_system_error : 1 ; ///< R/WC (not used by NXP) The Host Controller sets this bit to 1 when a serious error occurs during a host system access involving the Host Controller module. In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and PCI Target Abort. When this error occurs, the Host Controller clears the Run/Stop bit in the Command register to prevent further execution of the scheduled TDs.
uint32_t async_adv : 1 ; ///< Async Advance interrupt
uint32_t : 1 ;
uint32_t nxp_int_sof : 1 ; ///< NXP customized: this bit will be set every 125us and can be used by host controller driver as a time base.
uint32_t : 4 ;
uint32_t hc_halted : 1 ; ///< Opposite value to run_stop bit.
uint32_t reclamation : 1 ; ///< Used to detect empty async shecudle
uint32_t periodic_status : 1 ; ///< Periodic schedule status
uint32_t async_status : 1 ; ///< Async schedule status
uint32_t : 2 ;
uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule.
uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule.
uint32_t : 12 ;
}status_bm;
};
union{
uint32_t usb_int_enable ; ///< This register enables and disables reporting of the corresponding interrupt to the software. When a bit is set and the corresponding interrupt is active, an interrupt is generated to the host. Interrupt sources that are disabled in this register still appear in the USBSTS to allow the software to poll for events.
uint32_t inten;
struct {
uint32_t usb : 1 ; ///< When this bit is a one, and the USBINT bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold. The interrupt is acknowledged by software clearing the USBINTbit.
uint32_t usb_error : 1 ; ///< When this bit is a one, and the USBERRINT bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold. The interrupt is acknowledged by software clearing the USBERRINTbit.
uint32_t port_change_detect : 1 ; ///< When this bit is a one, and the Port Change Detect bit in the USBSTS register is a one, the host controller will issue an interrupt. The interrupt is acknowledged by software clearing the Port Change Detectbit.
uint32_t framelist_rollover : 1 ; ///< When this bit is a one, and the Frame List Rolloverbit in the USBSTS register is a one, the host controller will issue an interrupt. The interrupt is acknowledged by software clearing the Frame List Rollover bit.
uint32_t pci_host_system_error : 1 ; ///< (not used by NXP) When this bit is a one, and the Host System Error Statusbit in the USBSTS register is a one, the host controller will issue an interrupt. The interrupt is acknowledged by software clearing the Host System Error bit.
uint32_t async_advance : 1 ; ///< When this bit is a one, and the Interrupt on Async Advancebit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold. The interrupt is acknowledged by software clearing the Interrupt on Async Advancebit.
uint32_t : 1 ; ///< reserved
uint32_t nxp_int_sof : 1 ; ///< NXP customized: if this bit is one and the SRI bit in the USBSTS register is one, the host controller will issue an interrupt. In host mode, the SRI bit will be set every 125 micro sec and can be used by the host controller as a time base. The interrupt is acknowledged by software clearing the SRI bit in the USBSTS register.
uint32_t : 10 ; ///< reserved
uint32_t nxp_int_async : 1 ; ///< NXP customized: When this bit is a one, and the USBHSTASYNCINT bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold. The interrupt is acknowledged by software clearing the USBHSTASYNCINT bit.
uint32_t nxp_int_period : 1 ; ///< NXP customized: When this bit is a one, and the USBHSTPERINT bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold. The interrupt is acknowledged by software clearing the USBHSTPERINT bit.
uint32_t : 12 ; ///< reserved
uint32_t : 0 ; // padding to the boundary of storage unit
}usb_int_enable_bit;
uint32_t usb : 1 ;
uint32_t usb_error : 1 ;
uint32_t port_change_detect : 1 ;
uint32_t framelist_rollover : 1 ;
uint32_t pci_host_system_error : 1 ;
uint32_t async_adv : 1 ;
uint32_t : 1 ;
uint32_t nxp_int_sof : 1 ;
uint32_t : 10 ;
uint32_t nxp_int_async : 1 ;
uint32_t nxp_int_period : 1 ;
uint32_t : 12 ;
}inten_bm;
};
uint32_t frame_index ; ///< This register is used by the host controller to index into the periodic frame list. The register updates every 125 microseconds (once each micro-frame). Bits [N:3] are used to select a particular entry in the Periodic Frame List during periodic schedule execution. The number of bits used for the index depends on the size of the frame list as set by system software in the Frame List Sizefield in the USBCMD register
uint32_t ctrl_ds_seg ; ///< (not used by NXP) This 32-bit register corresponds to the most significant address bits [63:32] for all EHCI data structures. If the 64-bit Addressing Capabilityfield in HCCPARAMS is a zero, then this register is not used
uint32_t periodic_list_base ; ///< This 32-bit register contains the beginning address of the Periodic Frame List in the system memory. System software loads this register prior to starting the schedule execution by the Host Controller (see 4.1). The memory structure referenced by this physical memory pointer is assumed to be 4-Kbyte aligned. The contents of this register are combined with the Frame Index Register (FRINDEX) to enable the Host Controller to step through the Periodic Frame List in sequence.
uint32_t async_list_base ; ///< This 32-bit register contains the address of the next asynchronous queue head to be executed. Bits [4:0] of this register cannot be modified by system software and will always return a zero when read. The memory structure referenced by this physical memory pointer is assumed to be 32-byte (cache line) aligned
uint32_t tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
uint32_t reserved[8] ; ///< reserved by EHCI specs
uint32_t config_flag ; ///< (not used by NXP) configured flag register
uint32_t frame_index ; ///< Micro frame counter
uint32_t ctrl_ds_seg ; ///< Control Data Structure Segment
uint32_t periodic_list_base ; ///< Beginning address of perodic frame list
uint32_t async_list_addr ; ///< Address of next async QHD to be executed
uint32_t nxp_tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
uint32_t reserved[8] ;
uint32_t config_flag ; ///< not used by NXP
union {
uint32_t portsc ; ///< port status and control
struct {
uint32_t current_connect_status : 1; ///< RO 1=Device is present on port. 0=No device is present. Default = 0. This value reflects the current state of the port, and may not correspond directly to the event that caused the Connect Status Change bit (Bit 1) to be set. This field is zero if Port Power is zero.
uint32_t connect_status_change : 1; ///< R/WC 1=Change in Current Connect Status. 0=No change. Default = 0. Indicates a change has occurred in the port's Current Connect Status. The host controller sets this bit for all changes to the port device connect status, even if system software has not cleared an existing connect status change. For example, the insertion status changes twice before system software has cleared the changed condition, hub hardware will be "setting" an already-set bit (i.e., the bit will remain set). Software sets this bit to 0 by writing a 1 to it. This field is zero if Port Power is zero.
uint32_t port_enable : 1; ///< 1=Enable. 0=Disable. Default = 0. Ports can only be enabled by the host controller as a part of the reset and enable. Software cannot enable a port by writing a one to this field. The host controller will only set this bit to a one when the reset sequence determines that the attached device is a high-speed device. Ports can be disabled by either a fault condition (disconnect event or other fault condition) or by host software. Note that the bit status does not change until the port state actually changes.
uint32_t port_enable_change : 1; ///< R/WC 1=Port enabled/disabled status has changed. 0=No change. Default = 0. For the root hub, this bit gets set to a one only when a port is disabled due to the appropriate conditions existing at the EOF2 point (See Chapter 11 of the USB Specification for the definition of a Port Error). Software clears this bit by writing a 1 to it. This field is zero if Port Power is zero.
uint32_t over_current_active : 1; ///< RO Default = 0. 1=This port currently has an over-current condition. 0=This port does not have an over-current condition. This bit will automatically transition from a one to a zero when the over current condition is removed.
uint32_t over_current_change : 1; ///< R/WC Default = 0. 1=This bit gets set to a one when there is a change to Over-current Active. Software clears this bit by writing a one to this bit position.
uint32_t force_port_resume : 1; ///< 1= Resume detected/driven on port. 0=No resume (K-state) detected/driven on port. Default = 0. This functionality defined for manipulating this bit depends on the value of the Suspendbit. For example, if the port is not suspended (Suspendand Enabledbits are a one) and software transitions this bit to a one, then the effects on the bus are undefined. Software sets this bit to a 1 to drive resume signaling. The Host Controller sets this bit to a 1 if a J-to-K transition is detected while the port is in the Suspend state. When this bit transitions to a one because a J-to-K transition is detected, the Port Change Detectbit in the USBSTS register is also set to a one. If software sets this bit to a one, the host controller must not set the Port Change Detectbit.
uint32_t suspend : 1; ///< 1=Port in suspend state. 0=Port not in suspend state. Default = 0. Port Enabled Bit and Suspend bit of this register define the port states as follows: Bits [Port Enabled, Suspend] Port State 0X Disable 10 Enable 11 Suspend When in suspend state, downstream propagation of data is blocked on this port, except for port reset. The blocking occurs at the end of the current transaction, if a transaction was in progress when this bit was written to 1. In the suspend state, the port is sensitive to resume detection. Note that the bit status does not change until the port is suspended and that there may be a delay in suspending a port if there is a transaction currently in progress on the USB. A write of zero to this bit is ignored by the host controller. The host controller will unconditionally set this bit to a zero when: •Software sets the Force Port Resumebit to a zero (from a one). •Software sets the Port Resetbit to a one (from a zero).
uint32_t port_reset : 1; ///< 1=Port is in Reset. 0=Port is not in Reset. Default = 0. When software writes a one to this bit (from a zero)
uint32_t current_connect_status : 1; ///< 0: No device, 1: Device is present on port
uint32_t connect_status_change : 1; ///< Change in Current Connect Status
uint32_t port_enabled : 1; ///< Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
uint32_t port_enable_change : 1; ///< Port Enabled has changed
uint32_t over_current_active : 1; ///< Port has an over-current condition
uint32_t over_current_change : 1; ///< Change to Over-current Active
uint32_t force_port_resume : 1; ///< Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
uint32_t suspend : 1; ///< Port in suspend state
uint32_t port_reset : 1; ///< 1=Port is in Reset. 0=Port is not in Reset
uint32_t nxp_highspeed_status : 1; ///< NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
uint32_t line_status : 2; ///< hese bits reflect the current logical levels of the D+ (bit 11) and D- (bit 10) signal lines. These bits are used for detection of low-speed USB devices prior to the port reset and enable sequence. This field is valid only when the port enable bit is zero and the current connect status bit is set to a one. The encoding of the bits are: 00b SE0, 10b J-state, 01b K-state, 11b undefined
uint32_t port_power : 1; ///< 0= power off, 1= power on, Host/OTG controller requires port power control switches. This bit represents the current setting of the switch (0=off, 1=on). When power is not available on a port (i.e. PP equals a 0), the port is non-functional and will not report attaches, detaches, etc. When an over-current condition is detected on a powered port and PPC is a one, the PP bit in each affected port may be transitioned by the host controller driver from a one to a zero (removing power from the port).
uint32_t port_owner : 1; ///< (not used by NXP)
uint32_t port_indicator_control : 2; ///< Writing to this field effects the value of the pins USB0_IND1 and USB0_IND0. 00b: Port indication is off, 01b: Amber, 10b: green, 11b: undefined
uint32_t port_test_control : 4; ///< When this field is zero, the port is NOT operating in a test mode. A non-zero value indicates that it is operating in test mode
uint32_t wake_on_connect_enable : 1; ///< Default = 0b. Writing this bit to a one enables the port to be sensitive to device connects as wake-up events. See Section 4.3 for effects of this bit on resume event behavior. Refer to Section 4.3.1 for operational model.
uint32_t wake_on_disconnect_enable : 1; ///< Default = 0b. Writing this bit to a one enables the port to be sensitive to device disconnects as wake-up events. See Section 4.3 for effects of this bit on resume event behavior. Refer to Section 4.3.1 for operational model.
uint32_t wake_on_over_current_enable : 1; ///< Default = 0b. Writing this bit to a one enables the port to be sensitive to over-current conditions as wake-up events. See Section 4.3 for effects of this bit on resume event behavior. Refer to Section 4.3.1 for operational model.
uint32_t line_status : 2; ///< D+/D- state: 00: SE0, 10: J-state, 01: K-state
uint32_t port_power : 1; ///< 0= power off, 1= power on
uint32_t port_owner : 1; ///< not used by NXP
uint32_t port_indicator_control : 2; ///< 00b: off, 01b: Amber, 10b: green, 11b: undefined
uint32_t port_test_control : 4; ///< Port test mode, not used by tinyusb
uint32_t wake_on_connect_enable : 1; ///< Enables device connects as wake-up events
uint32_t wake_on_disconnect_enable : 1; ///< Enables device disconnects as wake-up events
uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events
uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
uint32_t : 1;
uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
uint32_t : 0; // padding to the boundary of storage unit
}portsc_bit;
}portsc_bm;
};
}ehci_registers_t;
//--------------------------------------------------------------------+
// EHCI Data Organization
//--------------------------------------------------------------------+
typedef struct {
//------------- Static Async/Period List Head, Each for one controller -------------//
ehci_qhd_t async_head[CONTROLLER_HOST_NUMBER]; /// head qhd of async list, also is used as control endpoint for address 0
typedef struct
{
ehci_link_t period_framelist[EHCI_FRAMELIST_SIZE];
#if EHCI_PERIODIC_LIST
// for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
// [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms
ehci_qhd_t period_head_arr[CONTROLLER_HOST_NUMBER][4];
#endif
//------------- Data for Address 0 (use async head as its queue head) -------------//
ehci_qtd_t addr0_qtd[3];
ehci_qhd_t period_head_arr[4];
// Note control qhd of dev0 is used as head of async list
struct {
struct {
ehci_qhd_t qhd;
ehci_qtd_t qtd[3];
}control;
ehci_qhd_t qhd;
ehci_qtd_t qtd;
}control[CFG_TUSB_HOST_DEVICE_MAX+1];
ehci_qhd_t qhd[HCD_MAX_ENDPOINT] ; ///< Queue Head Pool
ehci_qtd_t qtd[HCD_MAX_XFER] ATTR_ALIGNED(32) ; ///< Queue Element Transfer Pool
// ehci_itd_t itd[EHCI_MAX_ITD] ; ///< Iso Transfer Pool
// ehci_sitd_t sitd[EHCI_MAX_SITD] ; ///< Split (FS) Isochronous Transfer Pool
}device[CFG_TUSB_HOST_DEVICE_MAX];
ehci_qhd_t qhd_pool[HCD_MAX_ENDPOINT];
ehci_qtd_t qtd_pool[HCD_MAX_XFER] ATTR_ALIGNED(32);
ehci_registers_t* regs;
}ehci_data_t;
#ifdef __cplusplus
+78 -49
View File
@@ -43,83 +43,112 @@
#ifndef _TUSB_HCD_H_
#define _TUSB_HCD_H_
#include <common/tusb_common.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <common/tusb_common.h>
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef enum
{
HCD_EVENT_DEVICE_ATTACH,
HCD_EVENT_DEVICE_REMOVE,
HCD_EVENT_XFER_COMPLETE,
} hcd_eventid_t;
#if MODE_HOST_SUPPORTED
typedef struct
{
uint8_t rhport;
uint8_t event_id;
union
{
struct
{
uint8_t hub_addr;
uint8_t hub_port;
} attach, remove;
struct
{
uint8_t ep_addr;
uint8_t result;
uint32_t len;
} xfer_complete;
};
} hcd_event_t;
#if TUSB_OPT_HOST_ENABLED
// Max number of endpoints per device
enum {
HCD_MAX_ENDPOINT = CFG_TUSB_HOST_HUB + CFG_TUSB_HOST_HID_KEYBOARD + CFG_TUSB_HOST_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC +
CFG_TUSB_HOST_MSC*2 + CFG_TUSB_HOST_CDC*3,
HCD_MAX_ENDPOINT = CFG_TUSB_HOST_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC +
CFG_TUH_MSC*2 + CFG_TUH_CDC*3),
HCD_MAX_XFER = HCD_MAX_ENDPOINT*2,
};
//#define HCD_MAX_ENDPOINT 16
//#define HCD_MAX_XFER 16
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
// HCD API
//--------------------------------------------------------------------+
typedef struct {
uint8_t dev_addr;
uint8_t xfer_type;
uint8_t index;
uint8_t reserved;
} pipe_handle_t;
bool hcd_init(void);
void hcd_int_enable (uint8_t rhport);
void hcd_int_disable(uint8_t rhport);
static inline bool pipehandle_is_valid(pipe_handle_t pipe_hdl) ATTR_CONST ATTR_ALWAYS_INLINE ATTR_WARN_UNUSED_RESULT;
static inline bool pipehandle_is_valid(pipe_handle_t pipe_hdl)
{
return pipe_hdl.dev_addr > 0;
}
// PORT API
/// return the current connect status of roothub port
bool hcd_port_connect_status(uint8_t hostid);
void hcd_port_reset(uint8_t hostid);
tusb_speed_t hcd_port_speed_get(uint8_t hostid);
static inline bool pipehandle_is_equal(pipe_handle_t x, pipe_handle_t y) ATTR_CONST ATTR_ALWAYS_INLINE ATTR_WARN_UNUSED_RESULT;
static inline bool pipehandle_is_equal(pipe_handle_t x, pipe_handle_t y)
{
return (x.dev_addr == y.dev_addr) && (x.xfer_type == y.xfer_type) && (x.index == y.index);
}
// HCD closes all opened endpoints belong to this device
void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
//--------------------------------------------------------------------+
// USBH-HCD API
// Event function
//--------------------------------------------------------------------+
tusb_error_t hcd_init(void) ATTR_WARN_UNUSED_RESULT;
void hal_hcd_isr(uint8_t hostid);
void hcd_event_handler(hcd_event_t const* event, bool in_isr);
// Helper to send device attach event
void hcd_event_device_attach(uint8_t rhport);
// Helper to send device removal event
void hcd_event_device_remove(uint8_t rhport);
// Helper to send USB transfer event
void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
// Endpoints API
//--------------------------------------------------------------------+
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr);
bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr);
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
// TODO merge with pipe_xfer
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
//--------------------------------------------------------------------+
// PIPE API
//--------------------------------------------------------------------+
// TODO control xfer should be used via usbh layer
tusb_error_t hcd_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size) ATTR_WARN_UNUSED_RESULT;
tusb_error_t hcd_pipe_control_xfer(uint8_t dev_addr, tusb_control_request_t const * p_request, uint8_t data[]) ATTR_WARN_UNUSED_RESULT;
tusb_error_t hcd_pipe_control_close(uint8_t dev_addr) ATTR_WARN_UNUSED_RESULT;
pipe_handle_t hcd_pipe_open(uint8_t dev_addr, tusb_desc_endpoint_t const * endpoint_desc, uint8_t class_code) ATTR_WARN_UNUSED_RESULT;
tusb_error_t hcd_pipe_queue_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes) ATTR_WARN_UNUSED_RESULT; // only queue, not transferring yet
tusb_error_t hcd_pipe_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) ATTR_WARN_UNUSED_RESULT;
tusb_error_t hcd_pipe_close(pipe_handle_t pipe_hdl) /*ATTR_WARN_UNUSED_RESULT*/;
bool hcd_pipe_is_busy(pipe_handle_t pipe_hdl) ATTR_PURE;
bool hcd_pipe_is_error(pipe_handle_t pipe_hdl) ATTR_PURE;
bool hcd_pipe_is_stalled(pipe_handle_t pipe_hdl) ATTR_PURE; // stalled also counted as error
uint8_t hcd_pipe_get_endpoint_addr(pipe_handle_t pipe_hdl) ATTR_PURE;
tusb_error_t hcd_pipe_clear_stall(pipe_handle_t pipe_hdl);
bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes); // only queue, not transferring yet
bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete);
#if 0
tusb_error_t hcd_pipe_cancel()ATTR_WARN_UNUSED_RESULT;
tusb_error_t hcd_pipe_cancel();
#endif
//--------------------------------------------------------------------+
// PORT API
//--------------------------------------------------------------------+
/// return the current connect status of roothub port
bool hcd_port_connect_status(uint8_t hostid) ATTR_PURE ATTR_WARN_UNUSED_RESULT; // TODO make inline if possible
void hcd_port_reset(uint8_t hostid);
tusb_speed_t hcd_port_speed_get(uint8_t hostid) ATTR_PURE ATTR_WARN_UNUSED_RESULT; // TODO make inline if possible
void hcd_port_unplug(uint8_t hostid); // called by usbh to instruct hcd that it can execute unplug procedure
#ifdef __cplusplus
}
#endif
+102 -89
View File
@@ -38,7 +38,7 @@
#include "tusb_option.h"
#if (MODE_HOST_SUPPORTED && CFG_TUSB_HOST_HUB)
#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_HUB)
#define _TINY_USB_SOURCE_FILE_
@@ -46,14 +46,14 @@
// INCLUDE
//--------------------------------------------------------------------+
#include "hub.h"
#include "usbh_hub.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
pipe_handle_t pipe_status;
uint8_t interface_number;
typedef struct
{
uint8_t itf_num;
uint8_t ep_status;
uint8_t port_number;
uint8_t status_change; // data from status change interrupt endpoint
}usbh_hub_t;
@@ -67,76 +67,76 @@ ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION STATIC_VAR uint8_t hub_enum_buffer[sizeof(d
//--------------------------------------------------------------------+
// HUB
//--------------------------------------------------------------------+
tusb_error_t hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature)
bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature)
{
tusb_error_t error;
TU_ASSERT(HUB_FEATURE_PORT_CONNECTION_CHANGE <= feature && feature <= HUB_FEATURE_PORT_RESET_CHANGE);
OSAL_SUBTASK_BEGIN
STASK_ASSERT(HUB_FEATURE_PORT_CONNECTION_CHANGE <= feature &&
feature <= HUB_FEATURE_PORT_RESET_CHANGE);
tusb_control_request_t request = {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = HUB_REQUEST_CLEAR_FEATURE,
.wValue = feature,
.wIndex = hub_port,
.wLength = 0
};
//------------- Clear Port Feature request -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( hub_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_OTHER),
HUB_REQUEST_CLEAR_FEATURE, feature, hub_port,
0, NULL ),
error
);
STASK_ASSERT_ERR( error );
TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) );
//------------- Get Port Status to check if feature is cleared -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( hub_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_OTHER),
HUB_REQUEST_GET_STATUS, 0, hub_port,
4, hub_enum_buffer ),
error
);
STASK_ASSERT_ERR( error );
request = (tusb_control_request_t ) {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
.bRequest = HUB_REQUEST_GET_STATUS,
.wValue = 0,
.wIndex = hub_port,
.wLength = 4
};
TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) );
//------------- Check if feature is cleared -------------//
hub_port_status_response_t * p_port_status;
p_port_status = (hub_port_status_response_t *) hub_enum_buffer;
STASK_ASSERT( !BIT_TEST_(p_port_status->status_change.value, feature-16) );
TU_ASSERT( !TU_BIT_TEST(p_port_status->status_change.value, feature-16) );
OSAL_SUBTASK_END
return true;
}
tusb_error_t hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port)
bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port)
{
enum { RESET_DELAY = 200 }; // USB specs say only 50ms but many devices require much longer
tusb_error_t error;
OSAL_SUBTASK_BEGIN
//------------- Set Port Reset -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( hub_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_OTHER),
HUB_REQUEST_SET_FEATURE, HUB_FEATURE_PORT_RESET, hub_port,
0, NULL ),
error
);
STASK_ASSERT_ERR( error );
tusb_control_request_t request = {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = HUB_REQUEST_SET_FEATURE,
.wValue = HUB_FEATURE_PORT_RESET,
.wIndex = hub_port,
.wLength = 0
};
TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) );
osal_task_delay(RESET_DELAY); // TODO Hub wait for Status Endpoint on Reset Change
//------------- Get Port Status to check if port is enabled, powered and reset_change -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( hub_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_OTHER),
HUB_REQUEST_GET_STATUS, 0, hub_port,
4, hub_enum_buffer ),
error
);
STASK_ASSERT_ERR( error );
request = (tusb_control_request_t ) {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
.bRequest = HUB_REQUEST_GET_STATUS,
.wValue = 0,
.wIndex = hub_port,
.wLength = 4
};
TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) );
hub_port_status_response_t * p_port_status;
p_port_status = (hub_port_status_response_t *) hub_enum_buffer;
STASK_ASSERT ( p_port_status->status_change.reset && p_port_status->status_current.connect_status &&
p_port_status->status_current.port_power && p_port_status->status_current.port_enable);
TU_ASSERT ( p_port_status->status_change.reset && p_port_status->status_current.connect_status &&
p_port_status->status_current.port_power && p_port_status->status_current.port_enable);
OSAL_SUBTASK_END
return true;
}
// can only get the speed RIGHT AFTER hub_port_reset_subtask call
@@ -156,72 +156,87 @@ void hub_init(void)
// hub_enum_sem_hdl = osal_semaphore_create( OSAL_SEM_REF(hub_enum_semaphore) );
}
tusb_error_t hub_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
{
tusb_error_t error;
OSAL_SUBTASK_BEGIN
// not support multiple TT yet
if ( p_interface_desc->bInterfaceProtocol > 1 ) return TUSB_ERROR_HUB_FEATURE_NOT_SUPPORTED;
if ( itf_desc->bInterfaceProtocol > 1 ) return false;
//------------- Open Interrupt Status Pipe -------------//
tusb_desc_endpoint_t const *p_endpoint;
p_endpoint = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_interface_desc );
STASK_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType);
STASK_ASSERT(TUSB_XFER_INTERRUPT == p_endpoint->bmAttributes.xfer);
tusb_desc_endpoint_t const *ep_desc;
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
hub_data[dev_addr-1].pipe_status = hcd_pipe_open(dev_addr, p_endpoint, TUSB_CLASS_HUB);
STASK_ASSERT( pipehandle_is_valid(hub_data[dev_addr-1].pipe_status) );
hub_data[dev_addr-1].interface_number = p_interface_desc->bInterfaceNumber;
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer);
TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber;
hub_data[dev_addr-1].ep_status = ep_desc->bEndpointAddress;
(*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
//------------- Get Hub Descriptor -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_DEVICE),
HUB_REQUEST_GET_DESCRIPTOR, 0, 0,
sizeof(descriptor_hub_desc_t), hub_enum_buffer ),
error
);
STASK_ASSERT_ERR(error);
tusb_control_request_t request = {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
.bRequest = HUB_REQUEST_GET_DESCRIPTOR,
.wValue = 0,
.wIndex = 0,
.wLength = sizeof(descriptor_hub_desc_t)
};
TU_ASSERT( usbh_control_xfer( dev_addr, &request, hub_enum_buffer ) );
// only care about this field in hub descriptor
hub_data[dev_addr-1].port_number = ((descriptor_hub_desc_t*) hub_enum_buffer)->bNbrPorts;
//------------- Set Port_Power on all ports -------------//
static uint8_t i;
for(i=1; i <= hub_data[dev_addr-1].port_number; i++)
// TODO may only power port with attached
request = (tusb_control_request_t ) {
.bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
.bRequest = HUB_REQUEST_SET_FEATURE,
.wValue = HUB_FEATURE_PORT_POWER,
.wIndex = 0,
.wLength = 0
};
for(uint8_t i=1; i <= hub_data[dev_addr-1].port_number; i++)
{
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_OTHER),
HUB_REQUEST_SET_FEATURE, HUB_FEATURE_PORT_POWER, i,
0, NULL ),
error
);
request.wIndex = i;
TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
}
//------------- Queue the initial Status endpoint transfer -------------//
STASK_ASSERT_ERR ( hcd_pipe_xfer(hub_data[dev_addr-1].pipe_status, &hub_data[dev_addr-1].status_change, 1, true) );
TU_ASSERT( hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true) );
OSAL_SUBTASK_END
return true;
}
// is the response of interrupt endpoint polling
void hub_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
#include "usbh_hcd.h" // FIXME remove
void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
(void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports
(void) ep_addr;
usbh_hub_t * p_hub = &hub_data[pipe_hdl.dev_addr-1];
usbh_hub_t * p_hub = &hub_data[dev_addr-1];
if ( event == XFER_RESULT_SUCCESS )
{
for (uint8_t port=1; port <= p_hub->port_number; port++)
{ // TODO HUB ignore bit0 hub_status_change
if ( BIT_TEST_(p_hub->status_change, port) )
{
// TODO HUB ignore bit0 hub_status_change
if ( TU_BIT_TEST(p_hub->status_change, port) )
{
usbh_hub_port_plugged_isr(pipe_hdl.dev_addr, port);
hcd_event_t event =
{
.rhport = _usbh_devices[dev_addr].rhport,
.event_id = HCD_EVENT_DEVICE_ATTACH
};
event.attach.hub_addr = dev_addr;
event.attach.hub_port = port;
hcd_event_handler(&event, true);
break; // handle one port at a time, next port if any will be handled in the next cycle
}
}
@@ -230,21 +245,19 @@ void hub_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes
else
{
// TODO [HUB] check if hub is still plugged before polling status endpoint since failed usually mean hub unplugged
// TU_ASSERT ( TUSB_ERROR_NONE == hcd_pipe_xfer(pipe_hdl, &p_hub->status_change, 1, true) );
// TU_ASSERT ( hub_status_pipe_queue(dev_addr) );
}
}
void hub_close(uint8_t dev_addr)
{
(void) hcd_pipe_close(hub_data[dev_addr-1].pipe_status);
tu_memclr(&hub_data[dev_addr-1], sizeof(usbh_hub_t));
// osal_semaphore_reset(hub_enum_sem_hdl);
}
tusb_error_t hub_status_pipe_queue(uint8_t dev_addr)
bool hub_status_pipe_queue(uint8_t dev_addr)
{
return hcd_pipe_xfer(hub_data[dev_addr-1].pipe_status, &hub_data[dev_addr-1].status_change, 1, true);
return hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true);
}
+7 -7
View File
@@ -184,20 +184,20 @@ typedef struct {
TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct");
tusb_error_t hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port);
tusb_error_t hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature);
bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port);
bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature);
tusb_speed_t hub_port_get_speed(void);
tusb_error_t hub_status_pipe_queue(uint8_t dev_addr);
bool hub_status_pipe_queue(uint8_t dev_addr);
//--------------------------------------------------------------------+
// USBH-CLASS DRIVER API
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
void hub_init(void);
tusb_error_t hub_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
void hub_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void hub_close(uint8_t dev_addr);
void hub_init(void);
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hub_close(uint8_t dev_addr);
#endif
+203 -249
View File
@@ -38,17 +38,19 @@
#include <common/tusb_common.h>
#if MODE_HOST_SUPPORTED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "hal/hal.h"
#include "osal/osal.h"
#include "../hcd.h"
#include "../usbh_hcd.h"
#include "ohci.h"
// TODO remove
#include "chip.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -63,10 +65,10 @@ enum {
enum {
OHCI_CONTROL_CONTROL_BULK_RATIO = 3, ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1
OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = BIT_(2),
OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = BIT_(3),
OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = BIT_(4),
OHCI_CONTROL_LIST_BULK_ENABLE_MASK = BIT_(5),
OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = TU_BIT(2),
OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = TU_BIT(3),
OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = TU_BIT(4),
OHCI_CONTROL_LIST_BULK_ENABLE_MASK = TU_BIT(5),
};
enum {
@@ -78,42 +80,34 @@ enum {
OHCI_PERIODIC_START = 0x3E67
};
#ifdef __CC_ARM
#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
#endif
enum {
OHCI_INT_SCHEDULING_OVERUN_MASK = BIT_(0),
OHCI_INT_WRITEBACK_DONEHEAD_MASK = BIT_(1),
OHCI_INT_SOF_MASK = BIT_(2),
OHCI_INT_RESUME_DETECTED_MASK = BIT_(3),
OHCI_INT_UNRECOVERABLE_ERROR_MASK = BIT_(4),
OHCI_INT_FRAME_OVERFLOW_MASK = BIT_(5),
OHCI_INT_RHPORT_STATUS_CHANGE_MASK = BIT_(6),
OHCI_INT_SCHEDULING_OVERUN_MASK = TU_BIT(0),
OHCI_INT_WRITEBACK_DONEHEAD_MASK = TU_BIT(1),
OHCI_INT_SOF_MASK = TU_BIT(2),
OHCI_INT_RESUME_DETECTED_MASK = TU_BIT(3),
OHCI_INT_UNRECOVERABLE_ERROR_MASK = TU_BIT(4),
OHCI_INT_FRAME_OVERFLOW_MASK = TU_BIT(5),
OHCI_INT_RHPORT_STATUS_CHANGE_MASK = TU_BIT(6),
OHCI_INT_OWNERSHIP_CHANGE_MASK = BIT_(30),
OHCI_INT_MASTER_ENABLE_MASK = BIT_(31),
OHCI_INT_OWNERSHIP_CHANGE_MASK = TU_BIT(30),
OHCI_INT_MASTER_ENABLE_MASK = TU_BIT(31),
};
#ifdef __CC_ARM
#pragma diag_default 66 // return Keil 66 to normal severity
#endif
enum {
OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = BIT_(0),
OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = BIT_(1),
OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = BIT_(2),
OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = BIT_(3),
OHCI_RHPORT_PORT_RESET_STATUS_MASK = BIT_(4), ///< write '1' to reset port
OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0),
OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = TU_BIT(1),
OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = TU_BIT(2),
OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = TU_BIT(3),
OHCI_RHPORT_PORT_RESET_STATUS_MASK = TU_BIT(4), ///< write '1' to reset port
OHCI_RHPORT_PORT_POWER_STATUS_MASK = BIT_(8),
OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = BIT_(9),
OHCI_RHPORT_PORT_POWER_STATUS_MASK = TU_BIT(8),
OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = TU_BIT(9),
OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = BIT_(16),
OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = BIT_(17),
OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = BIT_(18),
OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = BIT_(19),
OHCI_RHPORT_PORT_RESET_CHANGE_MASK = BIT_(20),
OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = TU_BIT(16),
OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = TU_BIT(17),
OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = TU_BIT(18),
OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = TU_BIT(19),
OHCI_RHPORT_PORT_RESET_CHANGE_MASK = TU_BIT(20),
OHCI_RHPORT_ALL_CHANGE_MASK = OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK | OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK |
OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK | OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK | OHCI_RHPORT_PORT_RESET_CHANGE_MASK
@@ -137,12 +131,12 @@ enum {
enum {
OHCI_INT_ON_COMPLETE_YES = 0,
OHCI_INT_ON_COMPLETE_NO = BIN8(111)
OHCI_INT_ON_COMPLETE_NO = TU_BIN8(111)
};
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) STATIC_VAR ohci_data_t ohci_data;
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) static ohci_data_t ohci_data;
static ohci_ed_t * const p_ed_head[] =
{
@@ -153,15 +147,13 @@ static ohci_ed_t * const p_ed_head[] =
};
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
static void ed_list_remove(ohci_ed_t * p_head, ohci_ed_t * p_ed);
static ohci_ed_t * ed_list_find_previous(ohci_ed_t const * p_head, ohci_ed_t const * p_ed);
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
//--------------------------------------------------------------------+
// USBH-HCD API
//--------------------------------------------------------------------+
// Initialization according to 5.1.1.4
tusb_error_t hcd_init(void)
bool hcd_init(void)
{
//------------- Data Structure init -------------//
tu_memclr(&ohci_data, sizeof(ohci_data_t));
@@ -198,7 +190,7 @@ tusb_error_t hcd_init(void)
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power)
OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports
return TUSB_ERROR_NONE;
return true;
}
//--------------------------------------------------------------------+
@@ -206,23 +198,46 @@ tusb_error_t hcd_init(void)
//--------------------------------------------------------------------+
void hcd_port_reset(uint8_t hostid)
{
(void) hostid;
OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK;
}
bool hcd_port_connect_status(uint8_t hostid)
{
(void) hostid;
return OHCI_REG->rhport_status_bit[0].current_connect_status;
}
tusb_speed_t hcd_port_speed_get(uint8_t hostid)
{
(void) hostid;
return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
}
// TODO refractor abtract later
void hcd_port_unplug(uint8_t hostid)
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
// thus there is no need to make sure ED is not in HC's cahed as it will not for sure
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
{
// TODO OHCI
(void) rhport;
// addr0 serves as static head --> only set skip bit
if ( dev_addr == 0 )
{
ohci_data.control[0].ed.skip = 1;
}else
{
// remove control
ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr);
// remove bulk
ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr);
// remove interrupt
ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr);
// TODO remove ISO
}
}
//--------------------------------------------------------------------+
@@ -232,28 +247,29 @@ void hcd_port_unplug(uint8_t hostid)
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed)
{
return (p_ed->endpoint_number == 0 ) ? TUSB_XFER_CONTROL :
return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL :
(p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS :
(p_ed->is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
}
static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type, uint8_t interval)
{
(void) interval;
// address 0 is used as async head, which always on the list --> cannot be cleared
if (dev_addr != 0)
{
tu_memclr(p_ed, sizeof(ohci_ed_t));
}
p_ed->device_address = dev_addr;
p_ed->endpoint_number = endpoint_addr & 0x0F;
p_ed->direction = (xfer_type == TUSB_XFER_CONTROL) ? OHCI_PID_SETUP : ( (endpoint_addr & TUSB_DIR_IN_MASK) ? OHCI_PID_IN : OHCI_PID_OUT );
p_ed->speed = usbh_devices[dev_addr].speed;
p_ed->dev_addr = dev_addr;
p_ed->ep_number = endpoint_addr & 0x0F;
p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? OHCI_PID_SETUP : ( (endpoint_addr & TUSB_DIR_IN_MASK) ? OHCI_PID_IN : OHCI_PID_OUT );
p_ed->speed = _usbh_devices[dev_addr].speed;
p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
p_ed->max_package_size = max_packet_size;
p_ed->max_packet_size = max_packet_size;
p_ed->used = 1;
p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
@@ -274,181 +290,156 @@ static void gtd_init(ohci_gtd_t* p_td, void* data_ptr, uint16_t total_bytes)
p_td->buffer_end = total_bytes ? (((uint8_t*) data_ptr) + total_bytes-1) : NULL;
}
tusb_error_t hcd_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size)
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
(void) rhport;
ed_init(p_ed, dev_addr, max_packet_size, 0, TUSB_XFER_CONTROL, 0); // TODO binterval of control is ignored
ohci_ed_t* p_ed = &ohci_data.control[dev_addr].ed;
ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd;
if ( dev_addr != 0 )
{ // insert to control head
ed_list_insert( p_ed_head[TUSB_XFER_CONTROL], p_ed);
}else
{
p_ed->skip = 0; // addr0 is used as static control head --> only need to clear skip bit
}
return TUSB_ERROR_NONE;
}
tusb_error_t hcd_pipe_control_xfer(uint8_t dev_addr, tusb_control_request_t const * p_request, uint8_t data[])
{
ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd[0];
ohci_gtd_t *p_data = p_setup + 1;
ohci_gtd_t *p_status = p_setup + 2;
//------------- SETUP Phase -------------//
gtd_init(p_setup, (void*) p_request, 8);
gtd_init(p_setup, (void*) setup_packet, 8);
p_setup->index = dev_addr;
p_setup->pid = OHCI_PID_SETUP;
p_setup->data_toggle = BIN8(10); // DATA0
p_setup->next_td = (uint32_t) p_data;
//------------- DATA Phase -------------//
if (p_request->wLength > 0)
{
gtd_init(p_data, data, p_request->wLength);
p_data->index = dev_addr;
p_data->pid = p_request->bmRequestType_bit.direction ? OHCI_PID_IN : OHCI_PID_OUT;
p_data->data_toggle = BIN8(11); // DATA1
}else
{
p_data = p_setup;
}
p_data->next_td = (uint32_t) p_status;
//------------- STATUS Phase -------------//
gtd_init(p_status, NULL, 0); // zero-length data
p_status->index = dev_addr;
p_status->pid = p_request->bmRequestType_bit.direction ? OHCI_PID_OUT : OHCI_PID_IN; // reverse direction of data phase
p_status->data_toggle = BIN8(11); // DATA1
p_status->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
p_setup->data_toggle = TU_BIN8(10); // DATA0
p_setup->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
//------------- Attach TDs list to Control Endpoint -------------//
p_ed->td_head.address = (uint32_t) p_setup;
OHCI_REG->command_status_bit.control_list_filled = 1;
return TUSB_ERROR_NONE;
return true;
}
tusb_error_t hcd_pipe_control_close(uint8_t dev_addr)
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
(void) rhport;
if ( dev_addr == 0 )
{ // addr0 serves as static head --> only set skip bitx
p_ed->skip = 1;
}else
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
// FIXME control only for now
if ( epnum == 0 )
{
ed_list_remove( p_ed_head[ ed_get_xfer_type(p_ed)], p_ed );
ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
ohci_gtd_t *p_data = &ohci_data.control[dev_addr].gtd;
// TODO refractor to be USBH
usbh_devices[dev_addr].state = TUSB_DEVICE_STATE_UNPLUG;
gtd_init(p_data, buffer, buflen);
p_data->index = dev_addr;
p_data->pid = dir ? OHCI_PID_IN : OHCI_PID_OUT;
p_data->data_toggle = TU_BIN8(11); // DATA1
p_data->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
p_ed->td_head.address = (uint32_t) p_data;
OHCI_REG->command_status_bit.control_list_filled = 1;
}
return TUSB_ERROR_NONE;
return true;
}
//--------------------------------------------------------------------+
// BULK/INT/ISO PIPE API
//--------------------------------------------------------------------+
static inline uint8_t ed_get_index(ohci_ed_t const * const p_ed) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline uint8_t ed_get_index(ohci_ed_t const * const p_ed)
static inline ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
{
return p_ed - ohci_data.device[p_ed->device_address-1].ed;
}
if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed;
static inline ohci_ed_t * ed_from_pipe_handle(pipe_handle_t pipe_hdl) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline ohci_ed_t * ed_from_pipe_handle(pipe_handle_t pipe_hdl)
{
return &ohci_data.device[pipe_hdl.dev_addr-1].ed[pipe_hdl.index];
}
ohci_ed_t* ed_pool = ohci_data.ed_pool;
static inline ohci_ed_t * ed_find_free(uint8_t dev_addr) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline ohci_ed_t * ed_find_free(uint8_t dev_addr)
{
for(uint8_t i = 0; i < HCD_MAX_ENDPOINT; i++)
for(uint32_t i=0; i<HCD_MAX_ENDPOINT; i++)
{
if ( !ohci_data.device[dev_addr-1].ed[i].used )
if ( (ed_pool[i].dev_addr == dev_addr) &&
ep_addr == tu_edpt_addr(ed_pool[i].ep_number, ed_pool[i].pid == OHCI_PID_IN) )
{
return &ohci_data.device[dev_addr-1].ed[i];
return &ed_pool[i];
}
}
return NULL;
}
static ohci_ed_t * ed_list_find_previous(ohci_ed_t const * p_head, ohci_ed_t const * p_ed)
static inline ohci_ed_t * ed_find_free(void)
{
uint32_t max_loop = HCD_MAX_ENDPOINT*CFG_TUSB_HOST_DEVICE_MAX;
ohci_ed_t* ed_pool = ohci_data.ed_pool;
ohci_ed_t const * p_prev = p_head;
TU_ASSERT(p_prev, NULL);
while ( tu_align16(p_prev->next_ed) != 0 && /* not reach null */
tu_align16(p_prev->next_ed) != (uint32_t) p_ed && /* not found yet */
max_loop > 0)
for(uint8_t i = 0; i < HCD_MAX_ENDPOINT; i++)
{
p_prev = (ohci_ed_t const *) tu_align16(p_prev->next_ed);
max_loop--;
if ( !ed_pool[i].used ) return &ed_pool[i];
}
return ( tu_align16(p_prev->next_ed) == (uint32_t) p_ed ) ? (ohci_ed_t*) p_prev : NULL;
return NULL;
}
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
{
p_ed->next_ed |= p_pre->next_ed; // to reserve 4 lsb bits
p_pre->next_ed = (p_pre->next_ed & 0x0FUL) | ((uint32_t) p_ed);
p_ed->next = p_pre->next;
p_pre->next = (uint32_t) p_ed;
}
static void ed_list_remove(ohci_ed_t * p_head, ohci_ed_t * p_ed)
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
{
ohci_ed_t * const p_prev = ed_list_find_previous(p_head, p_ed);
ohci_ed_t* p_prev = p_head;
p_prev->next_ed = (p_prev->next_ed & 0x0fUL) | tu_align16(p_ed->next_ed);
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
p_ed->next_ed = (uint32_t) p_head;
p_ed->used = 0; // free ED
while( p_prev->next )
{
ohci_ed_t* ed = (ohci_ed_t*) p_prev->next;
if (ed->dev_addr == dev_addr)
{
// unlink ed
p_prev->next = ed->next;
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
ed->next = (uint32_t) p_head;
ed->used = 0;
}
// check next valid since we could remove it
if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next;
}
}
pipe_handle_t hcd_pipe_open(uint8_t dev_addr, tusb_desc_endpoint_t const * p_endpoint_desc, uint8_t class_code)
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
pipe_handle_t const null_handle = { .dev_addr = 0, .xfer_type = 0, .index = 0 };
(void) rhport;
// TODO iso support
TU_ASSERT(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS, null_handle );
TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
//------------- Prepare Queue Head -------------//
ohci_ed_t * const p_ed = ed_find_free(dev_addr);
TU_ASSERT(p_ed, null_handle);
ohci_ed_t * p_ed;
ed_init( p_ed, dev_addr, p_endpoint_desc->wMaxPacketSize.size, p_endpoint_desc->bEndpointAddress,
p_endpoint_desc->bmAttributes.xfer, p_endpoint_desc->bInterval );
p_ed->td_tail.class_code = class_code;
ed_list_insert( p_ed_head[p_endpoint_desc->bmAttributes.xfer], p_ed );
return (pipe_handle_t)
if ( ep_desc->bEndpointAddress == 0 )
{
.dev_addr = dev_addr,
.xfer_type = p_endpoint_desc->bmAttributes.xfer,
.index = ed_get_index(p_ed)
};
p_ed = &ohci_data.control[dev_addr].ed;
}else
{
p_ed = ed_find_free();
}
TU_ASSERT(p_ed);
ed_init( p_ed, dev_addr, ep_desc->wMaxPacketSize.size, ep_desc->bEndpointAddress,
ep_desc->bmAttributes.xfer, ep_desc->bInterval );
// control of dev0 is used as static async head
if ( dev_addr == 0 )
{
p_ed->skip = 0; // only need to clear skip bit
return true;
}
ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed );
return true;
}
static ohci_gtd_t * gtd_find_free(uint8_t dev_addr)
static ohci_gtd_t * gtd_find_free(void)
{
for(uint8_t i=0; i < HCD_MAX_XFER; i++)
{
if (!ohci_data.device[dev_addr-1].gtd[i].used)
{
return &ohci_data.device[dev_addr-1].gtd[i];
}
if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i];
}
return NULL;
@@ -463,97 +454,72 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
}
else
{ // TODO currently only support queue up to 2 TD each endpoint at a time
((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next_td = (uint32_t) p_gtd;
((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd;
}
}
static tusb_error_t pipe_queue_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
static bool pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
{
ohci_ed_t* const p_ed = ed_from_pipe_handle(pipe_hdl);
ohci_ed_t* const p_ed = ed_from_addr(dev_addr, ep_addr);
if ( !p_ed->is_iso )
{
ohci_gtd_t * const p_gtd = gtd_find_free(pipe_hdl.dev_addr);
TU_ASSERT(p_gtd, TUSB_ERROR_EHCI_NOT_ENOUGH_QTD); // TODO refractor error code
// not support ISO yet
TU_VERIFY ( !p_ed->is_iso );
gtd_init(p_gtd, buffer, total_bytes);
p_gtd->index = pipe_hdl.index;
if ( int_on_complete ) p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
ohci_gtd_t * const p_gtd = gtd_find_free();
TU_ASSERT(p_gtd); // not enough gtd
td_insert_to_ed(p_ed, p_gtd);
}else
{
TU_ASSERT_ERR(TUSB_ERROR_NOT_SUPPORTED_YET);
}
gtd_init(p_gtd, buffer, total_bytes);
p_gtd->index = p_ed-ohci_data.ed_pool;
return TUSB_ERROR_NONE;
if ( int_on_complete ) p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
td_insert_to_ed(p_ed, p_gtd);
return true;
}
tusb_error_t hcd_pipe_queue_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes)
bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes)
{
return pipe_queue_xfer(pipe_hdl, buffer, total_bytes, false);
return pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, false);
}
tusb_error_t hcd_pipe_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
{
TU_ASSERT_ERR( pipe_queue_xfer(pipe_hdl, buffer, total_bytes, true) );
(void) int_on_complete;
TU_ASSERT( pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, true) );
tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_pipe_handle(pipe_hdl) );
tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
return TUSB_ERROR_NONE;
return true;
}
/// pipe_close should only be called as a part of unmount/safe-remove process
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
// thus there is no need to make sure ED is not in HC's cahed as it will not for sure
tusb_error_t hcd_pipe_close(pipe_handle_t pipe_hdl)
bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
{
ohci_ed_t * const p_ed = ed_from_pipe_handle(pipe_hdl);
ed_list_remove( p_ed_head[ ed_get_xfer_type(p_ed)], p_ed );
return TUSB_ERROR_FAILED;
ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr);
return tu_align16(p_ed->td_head.address) != tu_align16(p_ed->td_tail);
}
bool hcd_pipe_is_busy(pipe_handle_t pipe_hdl)
bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
{
ohci_ed_t const * const p_ed = ed_from_pipe_handle(pipe_hdl);
return tu_align16(p_ed->td_head.address) != tu_align16(p_ed->td_tail.address);
}
bool hcd_pipe_is_error(pipe_handle_t pipe_hdl)
{
ohci_ed_t const * const p_ed = ed_from_pipe_handle(pipe_hdl);
return p_ed->td_head.halted;
}
bool hcd_pipe_is_stalled(pipe_handle_t pipe_hdl)
{
ohci_ed_t const * const p_ed = ed_from_pipe_handle(pipe_hdl);
ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr);
return p_ed->td_head.halted && p_ed->is_stalled;
}
uint8_t hcd_pipe_get_endpoint_addr(pipe_handle_t pipe_hdl)
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
ohci_ed_t const * const p_ed = ed_from_pipe_handle(pipe_hdl);
return p_ed->endpoint_number | (p_ed->direction == OHCI_PID_IN ? TUSB_DIR_IN_MASK : 0 );
}
ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);
tusb_error_t hcd_pipe_clear_stall(pipe_handle_t pipe_hdl)
{
ohci_ed_t * const p_ed = ed_from_pipe_handle(pipe_hdl);
p_ed->is_stalled = 0;
p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL
p_ed->is_stalled = 0;
p_ed->td_tail.address &= 0x0Ful; // set tail pointer back to NULL
p_ed->td_head.toggle = 0; // reset data toggle
p_ed->td_head.halted = 0;
p_ed->td_head.toggle = 0; // reset data toggle
p_ed->td_head.halted = 0;
if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1;
return TUSB_ERROR_NONE;
return true;
}
@@ -566,10 +532,10 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
while(td_head != NULL)
{
uint32_t next = td_head->next_td;
uint32_t next = td_head->next;
// make current's item become reverse's first item
td_head->next_td = (uint32_t) td_reverse_head;
td_head->next = (uint32_t) td_reverse_head;
td_reverse_head = td_head;
td_head = (ohci_td_item_t*) next; // advance to next item
@@ -578,13 +544,11 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
return td_reverse_head;
}
static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
{
return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.device); // check ohci_data_t for memory layout
return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout
}
static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd) ATTR_PURE ATTR_ALWAYS_INLINE;
static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
{
if ( gtd_is_control(p_qtd) )
@@ -592,13 +556,10 @@ static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
return &ohci_data.control[p_qtd->index].ed;
}else
{
uint8_t dev_addr_idx = (((uint32_t)p_qtd) - ((uint32_t)ohci_data.device)) / sizeof(ohci_data.device[0]);
return &ohci_data.device[dev_addr_idx].ed[p_qtd->index];
return &ohci_data.ed_pool[p_qtd->index];
}
}
static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
{ // 5.2.9 OHCI sample code
return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) +
@@ -607,18 +568,18 @@ static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_
static void done_queue_isr(uint8_t hostid)
{
uint8_t max_loop = (CFG_TUSB_HOST_DEVICE_MAX+1)*(HCD_MAX_XFER+OHCI_MAX_ITD);
(void) hostid;
// done head is written in reversed order of completion --> need to reverse the done queue first
ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
while( td_head != NULL && max_loop > 0)
while( td_head != NULL )
{
// TODO check if td_head is iso td
//------------- Non ISO transfer -------------//
ohci_gtd_t * const p_qtd = (ohci_gtd_t *) td_head;
xfer_result_t const event = (p_qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS :
(p_qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED;
(p_qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED;
p_qtd->used = 0; // free TD
if ( (p_qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
@@ -636,24 +597,17 @@ static void done_queue_isr(uint8_t hostid)
// the TailP must be set back to NULL for processing remaining TDs
if ((event != XFER_RESULT_SUCCESS))
{
p_ed->td_tail.address &= 0x0Ful;
p_ed->td_tail.address |= tu_align16(p_ed->td_head.address); // mark halted EP as empty queue
p_ed->td_tail &= 0x0Ful;
p_ed->td_tail |= tu_align16(p_ed->td_head.address); // mark halted EP as empty queue
if ( event == XFER_RESULT_STALLED ) p_ed->is_stalled = 1;
}
pipe_handle_t pipe_hdl =
{
.dev_addr = p_ed->device_address,
.xfer_type = ed_get_xfer_type(p_ed),
};
if ( pipe_hdl.xfer_type != TUSB_XFER_CONTROL) pipe_hdl.index = ed_get_index(p_ed);
usbh_xfer_isr(pipe_hdl, p_ed->td_tail.class_code, event, xferred_bytes);
hcd_event_xfer_complete(p_ed->dev_addr,
tu_edpt_addr(p_ed->ep_number, p_ed->pid == OHCI_PID_IN),
event, xferred_bytes);
}
td_head = (ohci_td_item_t*) td_head->next_td;
max_loop--;
td_head = (ohci_td_item_t*) td_head->next;
}
}
@@ -677,10 +631,10 @@ void hal_hcd_isr(uint8_t hostid)
{
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK;
usbh_hcd_rhport_plugged_isr(0);
hcd_event_device_attach(0);
}else
{
usbh_hcd_rhport_unplugged_isr(0);
hcd_event_device_remove(0);
}
}
+34 -41
View File
@@ -83,13 +83,14 @@ TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
typedef struct {
uint32_t reserved[2];
volatile uint32_t next_td;
volatile uint32_t next;
uint32_t reserved2;
}ohci_td_item_t;
typedef struct ATTR_ALIGNED(16) {
//------------- Word 0 -------------//
typedef struct ATTR_ALIGNED(16)
{
// Word 0
uint32_t used : 1;
uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer
uint32_t expected_bytes : 13; // TODO available for hcd
@@ -100,46 +101,39 @@ typedef struct ATTR_ALIGNED(16) {
volatile uint32_t data_toggle : 2;
volatile uint32_t error_count : 2;
volatile uint32_t condition_code : 4;
/*---------- End Word 1 ----------*/
//------------- Word 1 -------------//
// Word 1
volatile uint8_t* current_buffer_pointer;
//------------- Word 2 -------------//
volatile uint32_t next_td;
// Word 2 : next TD
volatile uint32_t next;
//------------- Word 3 -------------//
// Word 3
uint8_t* buffer_end;
} ohci_gtd_t;
TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" );
typedef struct ATTR_ALIGNED(16) {
//------------- Word 0 -------------//
uint32_t device_address : 7;
uint32_t endpoint_number : 4;
uint32_t direction : 2;
uint32_t speed : 1;
uint32_t skip : 1;
uint32_t is_iso : 1;
uint32_t max_package_size : 11;
uint32_t used : 1; // HCD
typedef struct ATTR_ALIGNED(16)
{
// Word 0
uint32_t dev_addr : 7;
uint32_t ep_number : 4;
uint32_t pid : 2; // 00b from TD, 01b Out, 10b In
uint32_t speed : 1;
uint32_t skip : 1;
uint32_t is_iso : 1;
uint32_t max_packet_size : 11;
// HCD: make use of 5 reserved bits
uint32_t used : 1;
uint32_t is_interrupt_xfer : 1;
uint32_t is_stalled : 1;
uint32_t : 2;
// Word 1
uint32_t td_tail;
//------------- Word 1 -------------//
union {
uint32_t address; // 4 lsb bits are free to use
struct {
uint32_t class_code : 4; // FIXME refractor to use interface number instead
uint32_t : 28;
};
}td_tail;
//------------- Word 2 -------------//
// Word 2
volatile union {
uint32_t address;
struct {
@@ -149,13 +143,14 @@ typedef struct ATTR_ALIGNED(16) {
};
}td_head;
//------------- Word 3 -------------//
uint32_t next_ed; // 4 lsb bits are free to use
// Word 3: next ED
uint32_t next;
} ohci_ed_t;
TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" );
typedef struct ATTR_ALIGNED(32) {
typedef struct ATTR_ALIGNED(32)
{
/*---------- Word 1 ----------*/
uint32_t starting_frame : 16;
uint32_t : 5; // can be used
@@ -163,13 +158,12 @@ typedef struct ATTR_ALIGNED(32) {
uint32_t frame_count : 3;
uint32_t : 1; // can be used
volatile uint32_t condition_code : 4;
/*---------- End Word 1 ----------*/
/*---------- Word 2 ----------*/
uint32_t buffer_page0; // 12 lsb bits can be used
/*---------- Word 3 ----------*/
volatile uint32_t next_td;
volatile uint32_t next;
/*---------- Word 4 ----------*/
uint32_t buffer_end;
@@ -181,7 +175,8 @@ typedef struct ATTR_ALIGNED(32) {
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
// structure with member alignment required from large to small
typedef struct ATTR_ALIGNED(256) {
typedef struct ATTR_ALIGNED(256)
{
ohci_hcca_t hcca;
ohci_ed_t bulk_head_ed; // static bulk head (dummy)
@@ -190,14 +185,12 @@ typedef struct ATTR_ALIGNED(256) {
// control endpoints has reserved resources
struct {
ohci_ed_t ed;
ohci_gtd_t gtd[3]; // setup, data, status
ohci_gtd_t gtd;
}control[CFG_TUSB_HOST_DEVICE_MAX+1];
struct {
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed[HCD_MAX_ENDPOINT];
ohci_gtd_t gtd[HCD_MAX_XFER];
}device[CFG_TUSB_HOST_DEVICE_MAX];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[HCD_MAX_ENDPOINT];
ohci_gtd_t gtd_pool[HCD_MAX_XFER];
} ohci_data_t;
+398 -369
View File
File diff suppressed because it is too large Load Diff
+16 -15
View File
@@ -64,9 +64,11 @@ typedef enum tusb_interface_status_{
} tusb_interface_status_t;
typedef struct {
uint8_t class_code;
void (* const init) (void);
tusb_error_t (* const open_subtask)(uint8_t, tusb_desc_interface_t const *, uint16_t*);
void (* const isr) (pipe_handle_t, xfer_result_t, uint32_t);
bool (* const open)(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t* outlen);
void (* const isr) (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t len);
void (* const close) (uint8_t);
} host_class_driver_t;
//--------------------------------------------------------------------+
@@ -76,34 +78,33 @@ typedef struct {
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
//tusb_error_t tusbh_configuration_set (uint8_t dev_addr, uint8_t configure_number) ATTR_WARN_UNUSED_RESULT;
tusb_device_state_t tuh_device_get_state (uint8_t dev_addr) ATTR_WARN_UNUSED_RESULT ATTR_PURE;
static inline bool tuh_device_is_configured(uint8_t dev_addr) ATTR_ALWAYS_INLINE ATTR_WARN_UNUSED_RESULT ATTR_PURE;
void tuh_task(void);
tusb_device_state_t tuh_device_get_state (uint8_t dev_addr);
static inline bool tuh_device_is_configured(uint8_t dev_addr)
{
return tuh_device_get_state(dev_addr) == TUSB_DEVICE_STATE_CONFIGURED;
}
uint32_t tuh_device_get_mounted_class_flag(uint8_t dev_addr);
//--------------------------------------------------------------------+
// APPLICATION CALLBACK
//--------------------------------------------------------------------+
ATTR_WEAK uint8_t tuh_device_attached_cb (tusb_desc_device_t const *p_desc_device) ATTR_WARN_UNUSED_RESULT;
ATTR_WEAK void tuh_device_mount_succeed_cb (uint8_t dev_addr);
ATTR_WEAK void tuh_device_mount_failed_cb(tusb_error_t error, tusb_desc_device_t const *p_desc_device); // TODO refractor remove desc_device
ATTR_WEAK uint8_t tuh_device_attached_cb (tusb_desc_device_t const *p_desc_device);
/** Callback invoked when device is mounted (configured) */
ATTR_WEAK void tuh_mount_cb (uint8_t dev_addr);
/** Callback invoked when device is unmounted (bus reset/unplugged) */
ATTR_WEAK void tuh_umount_cb(uint8_t dev_addr);
//--------------------------------------------------------------------+
// CLASS-USBH & INTERNAL API
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
bool usbh_init(void);
void usbh_enumeration_task(void* param);
tusb_error_t usbh_init(void);
tusb_error_t usbh_control_xfer_subtask(uint8_t dev_addr, uint8_t bmRequestType, uint8_t bRequest,
uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t* data);
bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t* request, uint8_t* data);
#endif
+11 -18
View File
@@ -52,23 +52,12 @@
#include "common/tusb_common.h"
#include "osal/osal.h"
#ifdef _TEST_
#include "hcd.h"
#endif
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
typedef struct ATTR_ALIGNED(4){
uint8_t core_id;
uint8_t hub_addr;
uint8_t hub_port;
uint8_t reserve;
} usbh_enumerate_t;
typedef struct {
//------------- port -------------//
uint8_t core_id;
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
uint8_t speed;
@@ -83,7 +72,6 @@ typedef struct {
//------------- device -------------//
volatile uint8_t state; // device state, value from enum tusbh_device_state_t
uint32_t flag_supported_class; // a bitmap of supported class
//------------- control pipe -------------//
struct {
@@ -91,19 +79,24 @@ typedef struct {
// uint8_t xferred_bytes; TODO not yet necessary
tusb_control_request_t request;
osal_semaphore_def_t sem_def;
osal_semaphore_t sem_hdl; // used to synchronize with HCD when control xfer complete
osal_mutex_def_t mutex_def;
osal_mutex_t mutex_hdl; // used to exclusively occupy control pipe
} control;
} usbh_device_info_t;
extern usbh_device_info_t usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; // including zero-address
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
} usbh_device_t;
extern usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; // including zero-address
//--------------------------------------------------------------------+
// callback from HCD ISR
//--------------------------------------------------------------------+
void usbh_xfer_isr(pipe_handle_t pipe_hdl, uint8_t class_code, xfer_result_t event, uint32_t xferred_bytes);
void usbh_hcd_rhport_plugged_isr(uint8_t hostid);
void usbh_hcd_rhport_unplugged_isr(uint8_t hostid);
#ifdef __cplusplus
}
+1 -3
View File
@@ -68,8 +68,6 @@ typedef void (*osal_task_func_t)( void * );
* uint32_t tusb_hal_millis(void)
*
* Task
* osal_task_def_t
* bool osal_task_create(osal_task_def_t* taskdef)
* void osal_task_delay(uint32_t msec)
*
* Queue
@@ -83,7 +81,7 @@ typedef void (*osal_task_func_t)( void * );
* osal_semaphore_def_t, osal_semaphore_t
* osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
* bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
* void osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec, uint32_t *p_error)
* bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
* void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
*
* Mutex
+5 -24
View File
@@ -65,27 +65,6 @@ static inline bool in_isr(void)
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
#define OSAL_TASK_DEF(_name, _str, _func, _prio, _stack_sz) \
static uint8_t _name##_##buf[_stack_sz*sizeof(StackType_t)]; \
osal_task_def_t _name = { .func = _func, .prio = _prio, .stack_sz = _stack_sz, .buf = _name##_##buf, .strname = _str };
typedef struct
{
osal_task_func_t func;
uint16_t prio;
uint16_t stack_sz;
void* buf;
const char* strname;
StaticTask_t stask;
}osal_task_def_t;
static inline bool osal_task_create(osal_task_def_t* taskdef)
{
return NULL != xTaskCreateStatic(taskdef->func, taskdef->strname, taskdef->stack_sz, NULL, taskdef->prio, (StackType_t*) taskdef->buf, &taskdef->stask);
}
static inline void osal_task_delay(uint32_t msec)
{
vTaskDelay( pdMS_TO_TICKS(msec) );
@@ -107,10 +86,10 @@ static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
return in_isr ? xSemaphoreGiveFromISR(sem_hdl, NULL) : xSemaphoreGive(sem_hdl);
}
static inline void osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec, uint32_t *err)
static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
{
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? portMAX_DELAY : pdMS_TO_TICKS(msec);
(*err) = (xSemaphoreTake(sem_hdl, ticks) ? TUSB_ERROR_NONE : TUSB_ERROR_OSAL_TIMEOUT);
return xSemaphoreTake(sem_hdl, ticks);
}
static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
@@ -139,7 +118,9 @@ static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
#define OSAL_QUEUE_DEF(_name, _depth, _type) \
// role device/host is used by OS NONE for mutex (disable usb isr) only
#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
static _type _name##_##buf[_depth];\
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
+3 -22
View File
@@ -46,27 +46,6 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
#define OSAL_TASK_DEF(_name, _str, _func, _prio, _stack_sz) \
static os_stack_t _name##_##buf[_stack_sz]; \
osal_task_def_t _name = { .func = _func, .prio = _prio, .stack_sz = _stack_sz, .buf = _name##_##buf, .strname = _str };
typedef struct
{
struct os_task mynewt_task;
osal_task_func_t func;
uint16_t prio;
uint16_t stack_sz;
void* buf;
const char* strname;
}osal_task_def_t;
static inline bool osal_task_create(osal_task_def_t* taskdef)
{
return OS_OK == os_task_init(&taskdef->mynewt_task, taskdef->strname, taskdef->func, NULL, taskdef->prio, OS_WAIT_FOREVER,
(os_stack_t*) taskdef->buf, taskdef->stack_sz);
}
static inline void osal_task_delay(uint32_t msec)
{
os_time_delay( os_time_ms_to_ticks32(msec) );
@@ -75,7 +54,9 @@ static inline void osal_task_delay(uint32_t msec)
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
#define OSAL_QUEUE_DEF(_name, _depth, _type) \
// role device/host is used by OS NONE for mutex (disable usb isr) only
#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
static _type _name##_##buf[_depth];\
static struct os_event* _name##_##evbuf[_depth];\
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, .evbuf = _name##_##evbuf};\
+82 -31
View File
@@ -51,15 +51,11 @@
//--------------------------------------------------------------------+
// TASK API
// Virtually do nothing in osal none
//--------------------------------------------------------------------+
#define OSAL_TASK_DEF(_name, _str, _func, _prio, _stack_sz) osal_task_def_t _name;
typedef uint8_t osal_task_def_t;
static inline bool osal_task_create(osal_task_def_t* taskdef)
static inline void osal_task_delay(uint32_t msec)
{
(void) taskdef;
return true;
uint32_t start = tusb_hal_millis();
while ( ( tusb_hal_millis() - start ) < msec ) {}
}
//--------------------------------------------------------------------+
@@ -90,17 +86,15 @@ static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
sem_hdl->count = 0;
}
static inline tusb_error_t osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
// TODO blocking for now
static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
{
(void) msec;
while (true) {
while (sem_hdl->count == 0) {
}
if (sem_hdl->count == 0) {
sem_hdl->count--;
break;
}
}
return TUSB_ERROR_NONE;
while (sem_hdl->count == 0) { }
sem_hdl->count--;
return true;
}
//--------------------------------------------------------------------+
@@ -124,40 +118,97 @@ static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
//--------------------------------------------------------------------+
#include "common/tusb_fifo.h"
#define OSAL_QUEUE_DEF(_name, _depth, _type) TU_FIFO_DEF(_name, _depth, _type, false)
// extern to avoid including dcd.h and hcd.h
#if TUSB_OPT_DEVICE_ENABLED
extern void dcd_int_disable(uint8_t rhport);
extern void dcd_int_enable(uint8_t rhport);
#endif
typedef tu_fifo_t osal_queue_def_t;
typedef tu_fifo_t* osal_queue_t;
#if TUSB_OPT_HOST_ENABLED
extern void hcd_int_disable(uint8_t rhport);
extern void hcd_int_enable(uint8_t rhport);
#endif
typedef struct
{
uint8_t role; // device or host
tu_fifo_t ff;
}osal_queue_def_t;
typedef osal_queue_def_t* osal_queue_t;
// role device/host is used by OS NONE for mutex (disable usb isr) only
#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
osal_queue_def_t _name = { \
.role = _role, \
.ff = { \
.buffer = _name##_buf, \
.depth = _depth, \
.item_size = sizeof(_type), \
.overwritable = false, \
}\
}
// lock queue by disable usb isr
static inline void _osal_q_lock(osal_queue_t qhdl)
{
#if TUSB_OPT_DEVICE_ENABLED
if (qhdl->role == OPT_MODE_DEVICE) dcd_int_disable(TUD_OPT_RHPORT);
#endif
#if TUSB_OPT_HOST_ENABLED
if (qhdl->role == OPT_MODE_HOST) hcd_int_disable(TUH_OPT_RHPORT);
#endif
}
// unlock queue
static inline void _osal_q_unlock(osal_queue_t qhdl)
{
#if TUSB_OPT_DEVICE_ENABLED
if (qhdl->role == OPT_MODE_DEVICE) dcd_int_enable(TUD_OPT_RHPORT);
#endif
#if TUSB_OPT_HOST_ENABLED
if (qhdl->role == OPT_MODE_HOST) hcd_int_enable(TUH_OPT_RHPORT);
#endif
}
static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
{
tu_fifo_clear(qdef);
tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
static inline bool osal_queue_send(osal_queue_t const queue_hdl, void const * data, bool in_isr)
static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr)
{
if (!in_isr) {
tusb_hal_int_disable_all();
_osal_q_lock(qhdl);
}
bool success = tu_fifo_write( (tu_fifo_t*) queue_hdl, data);
bool success = tu_fifo_write(&qhdl->ff, data);
if (!in_isr) {
tusb_hal_int_enable_all();
_osal_q_unlock(qhdl);
}
return success;
}
static inline void osal_queue_reset(osal_queue_t const queue_hdl)
static inline void osal_queue_reset(osal_queue_t const qhdl)
{
// tusb_hal_int_disable_all();
tu_fifo_clear( (tu_fifo_t*) queue_hdl);
tu_fifo_clear(&qhdl->ff);
// tusb_hal_int_enable_all();
}
static inline bool osal_queue_receive(osal_queue_t const queue_hdl, void* data) {
tusb_hal_int_disable_all();
bool success = tu_fifo_read(queue_hdl, data);
tusb_hal_int_enable_all();
// non blocking
static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data)
{
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
_osal_q_unlock(qhdl);
return success;
}
+39 -16
View File
@@ -72,6 +72,20 @@ static void bus_reset(void) {
bool dcd_init (uint8_t rhport)
{
(void) rhport;
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {}
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {}
USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val;
USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val;
// Configure registers
USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers;
USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
@@ -82,13 +96,16 @@ bool dcd_init (uint8_t rhport)
return true;
}
void dcd_connect (uint8_t rhport)
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_IRQn);
}
void dcd_disconnect (uint8_t rhport)
{
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
@@ -107,6 +124,12 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
// Nothing to do
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
(void) rhport;
return USB->DEVICE.FNUM.bit.FNUM;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
@@ -115,8 +138,8 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = edpt_dir(desc_edpt->bEndpointAddress);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
uint32_t size_value = 0;
@@ -151,8 +174,8 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
@@ -191,19 +214,19 @@ bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
return false;
}
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
return (edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
return (tu_edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
}
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
} else {
ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
@@ -219,10 +242,10 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
} else {
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
@@ -236,10 +259,10 @@ bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
// USBD shouldn't check control endpoint state
if ( 0 == ep_addr ) return false;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
return ep->EPINTFLAG.bit.TRCPT1 == 0 && ep->EPSTATUS.bit.BK1RDY == 1;
}
return ep->EPINTFLAG.bit.TRCPT0 == 0 && ep->EPSTATUS.bit.BK0RDY == 1;
@@ -1,81 +0,0 @@
/**************************************************************************/
/*!
@file hal_nrf5x.c
@author hathach
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2018, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD21
#include "sam.h"
#include "tusb_hal.h"
/*------------------------------------------------------------------*/
/* TUSB HAL
*------------------------------------------------------------------*/
bool tusb_hal_init(void)
{
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {}
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {}
USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val;
USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val;
tusb_hal_int_enable(0);
return true;
}
void tusb_hal_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_IRQn);
}
void tusb_hal_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
#endif
+44 -16
View File
@@ -73,6 +73,19 @@ bool dcd_init (uint8_t rhport)
{
(void) rhport;
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {}
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {}
USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
USB->DEVICE.QOSCTRL.bit.CQOS = 3;
USB->DEVICE.QOSCTRL.bit.DQOS = 3;
// Configure registers
USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers;
USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
@@ -82,13 +95,22 @@ bool dcd_init (uint8_t rhport)
return true;
}
void dcd_connect (uint8_t rhport)
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_0_IRQn);
NVIC_EnableIRQ(USB_1_IRQn);
NVIC_EnableIRQ(USB_2_IRQn);
NVIC_EnableIRQ(USB_3_IRQn);
}
void dcd_disconnect (uint8_t rhport)
{
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_3_IRQn);
NVIC_DisableIRQ(USB_2_IRQn);
NVIC_DisableIRQ(USB_1_IRQn);
NVIC_DisableIRQ(USB_0_IRQn);
}
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
@@ -107,6 +129,12 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
// Nothing to do
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
(void) rhport;
return USB->DEVICE.FNUM.bit.FNUM;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
@@ -115,8 +143,8 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = edpt_dir(desc_edpt->bEndpointAddress);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
uint32_t size_value = 0;
@@ -151,8 +179,8 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
@@ -190,19 +218,19 @@ bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
return false;
}
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
return (edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
return (tu_edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
}
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
} else {
ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
@@ -218,10 +246,10 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
} else {
ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
@@ -235,10 +263,10 @@ bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
// USBD shouldn't check control endpoint state
if ( 0 == ep_addr ) return false;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
return ep->EPINTFLAG.bit.TRCPT1 == 0 && ep->EPSTATUS.bit.BK1RDY == 1;
}
return ep->EPINTFLAG.bit.TRCPT0 == 0 && ep->EPSTATUS.bit.BK0RDY == 1;
@@ -1,86 +0,0 @@
/**************************************************************************/
/*!
@file hal_nrf5x.c
@author hathach
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2018, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51
#include "sam.h"
#include "tusb_hal.h"
/*------------------------------------------------------------------*/
/* TUSB HAL
*------------------------------------------------------------------*/
bool tusb_hal_init(void)
{
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {}
while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {}
USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
USB->DEVICE.QOSCTRL.bit.CQOS = 3;
USB->DEVICE.QOSCTRL.bit.DQOS = 3;
tusb_hal_int_enable(0);
return true;
}
void tusb_hal_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_0_IRQn);
NVIC_EnableIRQ(USB_1_IRQn);
NVIC_EnableIRQ(USB_2_IRQn);
NVIC_EnableIRQ(USB_3_IRQn);
}
void tusb_hal_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_3_IRQn);
NVIC_DisableIRQ(USB_2_IRQn);
NVIC_DisableIRQ(USB_1_IRQn);
NVIC_DisableIRQ(USB_0_IRQn);
}
#endif
+39 -26
View File
@@ -187,7 +187,7 @@ static void xact_in_prepare(uint8_t epnum)
}
//--------------------------------------------------------------------+
// Tinyusb DCD API
// Controller API
//--------------------------------------------------------------------+
bool dcd_init (uint8_t rhport)
{
@@ -195,18 +195,22 @@ bool dcd_init (uint8_t rhport)
return true;
}
void dcd_connect (uint8_t rhport)
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USBD_IRQn);
}
void dcd_disconnect (uint8_t rhport)
{
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USBD_IRQn);
}
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
(void) dev_addr;
// Set Address is automatically update by hw controller
}
@@ -217,23 +221,32 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
// Nothing to do
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
(void) rhport;
return NRF_USBD->FRAMECNTR;
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = edpt_dir(desc_edpt->bEndpointAddress);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
_dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
if ( dir == TUSB_DIR_OUT )
{
NRF_USBD->INTENSET = BIT_(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN |= BIT_(epnum);
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN |= TU_BIT(epnum);
}else
{
NRF_USBD->INTENSET = BIT_(USBD_INTEN_ENDEPIN0_Pos + epnum);
NRF_USBD->EPINEN |= BIT_(epnum);
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
NRF_USBD->EPINEN |= TU_BIT(epnum);
}
__ISB(); __DSB();
@@ -244,8 +257,8 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
{
(void) rhport;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
@@ -291,15 +304,15 @@ bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
// control is never got halted
if ( ep_addr == 0 ) return false;
uint8_t const epnum = edpt_number(ep_addr);
return (edpt_dir(ep_addr) == TUSB_DIR_IN ) ? NRF_USBD->HALTED.EPIN[epnum] : NRF_USBD->HALTED.EPOUT[epnum];
uint8_t const epnum = tu_edpt_number(ep_addr);
return (tu_edpt_dir(ep_addr) == TUSB_DIR_IN ) ? NRF_USBD->HALTED.EPIN[epnum] : NRF_USBD->HALTED.EPOUT[epnum];
}
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( edpt_number(ep_addr) == 0 )
if ( tu_edpt_number(ep_addr) == 0 )
{
NRF_USBD->TASKS_EP0STALL = 1;
}else
@@ -314,7 +327,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( edpt_number(ep_addr) )
if ( tu_edpt_number(ep_addr) )
{
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
__ISB(); __DSB();
@@ -326,10 +339,10 @@ bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
(void) rhport;
// USBD shouldn't check control endpoint state
if ( 0 == edpt_number(ep_addr) ) return false;
if ( 0 == tu_edpt_number(ep_addr) ) return false;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
@@ -362,11 +375,11 @@ void USBD_IRQHandler(void)
volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET;
for(int i=0; i<USBD_INTEN_EPDATA_Pos+1; i++)
for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++)
{
if ( BIT_TEST_(inten, i) && regevt[i] )
if ( TU_BIT_TEST(inten, i) && regevt[i] )
{
int_status |= BIT_(i);
int_status |= TU_BIT(i);
// event clear
regevt[i] = 0;
@@ -441,7 +454,7 @@ void USBD_IRQHandler(void)
*/
for(uint8_t epnum=0; epnum<8; epnum++)
{
if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
if ( TU_BIT_TEST(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
@@ -481,7 +494,7 @@ void USBD_IRQHandler(void)
// CBI In: Endpoint -> Host (transaction complete)
for(uint8_t epnum=0; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, epnum ) || ( epnum == 0 && is_control_in) )
if ( TU_BIT_TEST(data_status, epnum ) || ( epnum == 0 && is_control_in) )
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
@@ -502,7 +515,7 @@ void USBD_IRQHandler(void)
// CBI OUT: Host -> Endpoint
for(uint8_t epnum=0; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, 16+epnum ) || ( epnum == 0 && is_control_out) )
if ( TU_BIT_TEST(data_status, 16+epnum ) || ( epnum == 0 && is_control_out) )
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
+1 -26
View File
@@ -128,27 +128,6 @@ static void hfclk_disable(void)
nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
}
/*------------------------------------------------------------------*/
/* TUSB HAL
*------------------------------------------------------------------*/
bool tusb_hal_init(void)
{
return true;
}
void tusb_hal_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USBD_IRQn);
}
void tusb_hal_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USBD_IRQn);
}
/*------------------------------------------------------------------*/
/* Controller Start up Sequence (USBD 51.4 specs)
*------------------------------------------------------------------*/
@@ -166,7 +145,6 @@ void tusb_hal_nrf_power_event (uint32_t event)
/* Enable the peripheral */
// ERRATA 171, 187, 166
// Somehow Errata 187 check failed for pca10056 1.0.0 (2018.19)
if ( nrf_drv_usbd_errata_187() )
{
// CRITICAL_REGION_ENTER();
@@ -230,7 +208,6 @@ void tusb_hal_nrf_power_event (uint32_t event)
// CRITICAL_REGION_EXIT();
}
// Somehow Errata 187 check failed for pca10056 1.0.0 (2018.19)
if ( nrf_drv_usbd_errata_187() )
{
// CRITICAL_REGION_ENTER();
@@ -268,9 +245,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
NVIC_EnableIRQ(USBD_IRQn);
// Wait for HFCLK
while ( !hfclk_running() )
{
}
while ( !hfclk_running() ) { }
// Enable pull up
nrf_usbd_pullup_enable();
+61 -44
View File
@@ -1,30 +1,30 @@
/**
* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA
*
*
* 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -35,7 +35,7 @@
* 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.
*
*
*/
#ifndef NRF_DRV_USBD_ERRATA_H__
@@ -68,34 +68,47 @@
*/
static inline bool nrf_drv_usbd_errata_type_52840(void)
{
return ((((*(uint32_t *)0xF0000FE0) & 0xFF) == 0x08) &&
(((*(uint32_t *)0xF0000FE4) & 0x0F) == 0x0));
return (*(uint32_t *)0x10000130UL == 0x8UL);
}
/**
* @brief Internal auxiliary function to check if the program is running on first sample of
* NRF52840 chip
* @retval true It is NRF52480 chip and it is first sample version
* @brief Internal auxiliary function to check if the program is running on Engineering A revision
* @retval true It is NRF52480 chip and it is Engineering A revision
* @retval false It is other chip
*/
static inline bool nrf_drv_usbd_errata_type_52840_proto1(void)
static inline bool nrf_drv_usbd_errata_type_52840_eng_a(void)
{
return ( nrf_drv_usbd_errata_type_52840() &&
( ((*(uint32_t *)0xF0000FE8) & 0xF0) == 0x00 ) &&
( ((*(uint32_t *)0xF0000FEC) & 0xF0) == 0x00 ) );
return (nrf_drv_usbd_errata_type_52840() && (*(uint32_t *)0x10000134UL == 0x0UL));
}
/**
* @brief Internal auxiliary function to check if the program is running on first final product of
* NRF52840 chip
* @retval true It is NRF52480 chip and it is first final product
* @brief Internal auxiliary function to check if the program is running on Engineering B revision
* @retval true It is NRF52480 chip and it is Engineering B revision
* @retval false It is other chip
*/
static inline bool nrf_drv_usbd_errata_type_52840_fp1(void)
static inline bool nrf_drv_usbd_errata_type_52840_eng_b(void)
{
return ( nrf_drv_usbd_errata_type_52840() &&
( ((*(uint32_t *)0xF0000FE8) & 0xF0) == 0x20 ) &&
( ((*(uint32_t *)0xF0000FEC) & 0xF0) == 0x00 ) );
return (nrf_drv_usbd_errata_type_52840() && (*(uint32_t *)0x10000134UL == 0x1UL));
}
/**
* @brief Internal auxiliary function to check if the program is running on Engineering C revision
* @retval true It is NRF52480 chip and it is Engineering C revision
* @retval false It is other chip
*/
static inline bool nrf_drv_usbd_errata_type_52840_eng_c(void)
{
return (nrf_drv_usbd_errata_type_52840() && (*(uint32_t *)0x10000134UL == 0x2UL));
}
/**
* @brief Internal auxiliary function to check if the program is running on Engineering D revision
* @retval true It is NRF52480 chip and it is Engineering D revision
* @retval false It is other chip
*/
static inline bool nrf_drv_usbd_errata_type_52840_eng_d(void)
{
return (nrf_drv_usbd_errata_type_52840() && (*(uint32_t *)0x10000134UL == 0x3UL));
}
/**
@@ -108,7 +121,7 @@ static inline bool nrf_drv_usbd_errata_type_52840_fp1(void)
*/
static inline bool nrf_drv_usbd_errata_104(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_proto1();
return (NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_eng_a());
}
/**
@@ -121,7 +134,7 @@ static inline bool nrf_drv_usbd_errata_104(void)
*/
static inline bool nrf_drv_usbd_errata_154(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_proto1();
return (NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_eng_a());
}
/**
@@ -134,7 +147,7 @@ static inline bool nrf_drv_usbd_errata_154(void)
*/
static inline bool nrf_drv_usbd_errata_166(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && true;
return (NRF_DRV_USBD_ERRATA_ENABLE && true);
}
/**
@@ -147,7 +160,7 @@ static inline bool nrf_drv_usbd_errata_166(void)
*/
static inline bool nrf_drv_usbd_errata_171(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && true;
return (NRF_DRV_USBD_ERRATA_ENABLE && true);
}
/**
@@ -160,20 +173,11 @@ static inline bool nrf_drv_usbd_errata_171(void)
*/
static inline bool nrf_drv_usbd_errata_187(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_fp1();
}
/**
* @brief Function to check if chip requires errata ???
*
* Errata: SIZE.EPOUT not writable
*
* @retval true Errata should be implemented
* @retval false Errata should not be implemented
*/
static inline bool nrf_drv_usbd_errata_sizeepout_rw(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_proto1();
return (NRF_DRV_USBD_ERRATA_ENABLE &&
(nrf_drv_usbd_errata_type_52840_eng_b() ||
nrf_drv_usbd_errata_type_52840_eng_c() ||
nrf_drv_usbd_errata_type_52840_eng_d())
);
}
/**
@@ -186,7 +190,20 @@ static inline bool nrf_drv_usbd_errata_sizeepout_rw(void)
*/
static inline bool nrf_drv_usb_errata_199(void)
{
return NRF_DRV_USBD_ERRATA_ENABLE && true;
return (NRF_DRV_USBD_ERRATA_ENABLE && true);
}
/**
* @brief Function to check if chip requires errata 200
*
* Errata: SIZE.EPOUT not writable
*
* @retval true Errata should be implemented
* @retval false Errata should not be implemented
*/
static inline bool nrf_drv_usbd_errata_200(void)
{
return (NRF_DRV_USBD_ERRATA_ENABLE && nrf_drv_usbd_errata_type_52840_eng_a());
}
/** @} */
@@ -0,0 +1,410 @@
/**************************************************************************/
/*!
@file dcd_lpc11_13_15.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX)
#include "chip.h"
#include "device/dcd.h"
#include "dcd_lpc11_13_15.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// Number of endpoints
#define EP_COUNT 10
// only SRAM1 & USB RAM can be used for transfer
#define SRAM_REGION 0x20000000
/* Although device controller are the same. DMA of
* - LPC11u can only transfer up to nbytes = 64
* - LPC13 can transfer nbytes = 1023 (maximum)
* - LPC15 can ???
*/
enum {
DMA_NBYTES_MAX = (CFG_TUSB_MCU == OPT_MCU_LPC11UXX ? 64 : 1023)
};
enum {
INT_SOF_MASK = TU_BIT(30),
INT_DEVICE_STATUS_MASK = TU_BIT(31)
};
enum {
CMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
CMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
CMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
CMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), ///< reflect the softconnect only, does not reflect the actual attached state
CMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
CMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
CMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
CMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
CMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
};
typedef struct ATTR_PACKED
{
// Bits 21:6 (aligned 64) used in conjunction with bit 31:22 of DATABUFSTART
volatile uint16_t buffer_offset;
volatile uint16_t nbytes : 10 ;
uint16_t is_iso : 1 ;
uint16_t toggle_mode : 1 ;
uint16_t toggle_reset : 1 ;
uint16_t stall : 1 ;
uint16_t disable : 1 ;
volatile uint16_t active : 1 ;
}ep_cmd_sts_t;
TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" );
typedef struct
{
uint16_t total_bytes;
uint16_t xferred_bytes;
uint16_t nbytes;
}xfer_dma_t;
// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering.
// current_td is used to keep track of number of remaining & xferred bytes of the current request.
typedef struct
{
// 256 byte aligned, 2 for double buffer (not used)
// Each cmd_sts can only transfer up to DMA_NBYTES_MAX bytes each
ep_cmd_sts_t ep[EP_COUNT][2];
xfer_dma_t dma[EP_COUNT];
ATTR_ALIGNED(64) uint8_t setup_packet[8];
}dcd_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// EP list must be 256-byte aligned
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) static dcd_data_t _dcd;
static inline uint16_t get_buf_offset(void const * buffer)
{
uint32_t addr = (uint32_t) buffer;
TU_ASSERT( (addr & 0x3f) == 0, 0 );
return ( (addr >> 6) & 0xFFFFUL ) ;
}
static inline uint8_t ep_addr2id(uint8_t endpoint_addr)
{
return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
}
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB0_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB0_IRQn);
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
(void) rhport;
(void) 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;
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
(void) rhport;
return LPC_USB->INFO & (TU_BIT(11) - 1);
}
bool dcd_init(uint8_t rhport)
{
(void) rhport;
LPC_USB->EPLISTSTART = (uint32_t) _dcd.ep;
LPC_USB->DATABUFSTART = SRAM_REGION;
LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
LPC_USB->INTEN = INT_DEVICE_STATUS_MASK;
LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
NVIC_EnableIRQ(USB0_IRQn);
return true;
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( tu_edpt_number(ep_addr) == 0 )
{
// TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
_dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 1;
}
else
{
uint8_t const ep_id = ep_addr2id(ep_addr);
_dcd.ep[ep_id][0].stall = 1;
}
}
bool dcd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const ep_id = ep_addr2id(ep_addr);
return _dcd.ep[ep_id][0].stall;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const ep_id = ep_addr2id(ep_addr);
_dcd.ep[ep_id][0].stall = 0;
_dcd.ep[ep_id][0].toggle_reset = 1;
_dcd.ep[ep_id][0].toggle_mode = 0;
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
(void) rhport;
// TODO not support ISO yet
if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return false;
//------------- Prepare Queue Head -------------//
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
// Check if endpoint is available
TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable );
tu_memclr(_dcd.ep[ep_id], 2*sizeof(ep_cmd_sts_t));
_dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
// Enable EP interrupt
LPC_USB->INTEN |= TU_BIT(ep_id);
return true;
}
bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const ep_id = ep_addr2id(ep_addr);
return _dcd.ep[ep_id][0].active;
}
static void prepare_ep_xfer(uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes)
{
uint16_t const nbytes = tu_min16(total_bytes, DMA_NBYTES_MAX);
_dcd.dma[ep_id].nbytes = nbytes;
_dcd.ep[ep_id][0].buffer_offset = buf_offset;
_dcd.ep[ep_id][0].nbytes = nbytes;
_dcd.ep[ep_id][0].active = 1;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void) rhport;
uint8_t const ep_id = ep_addr2id(ep_addr);
tu_varclr(&_dcd.dma[ep_id]);
_dcd.dma[ep_id].total_bytes = total_bytes;
prepare_ep_xfer(ep_id, get_buf_offset(buffer), total_bytes);
return true;
}
//--------------------------------------------------------------------+
// IRQ
//--------------------------------------------------------------------+
static void bus_reset(void)
{
tu_memclr(&_dcd, sizeof(dcd_data_t));
// disable all non-control endpoints on bus reset
for(uint8_t ep_id = 2; ep_id < EP_COUNT; ep_id++)
{
_dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
}
_dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet);
LPC_USB->EPINUSE = 0;
LPC_USB->EPBUFCFG = 0;
LPC_USB->EPSKIP = 0xFFFFFFFF;
LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
LPC_USB->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints
}
static void process_xfer_isr(uint32_t int_status)
{
for(uint8_t ep_id = 0; ep_id < EP_COUNT; ep_id++ )
{
if ( TU_BIT_TEST(int_status, ep_id) )
{
ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0];
xfer_dma_t* xfer_dma = &_dcd.dma[ep_id];
xfer_dma->xferred_bytes += xfer_dma->nbytes - ep_cs->nbytes;
if ( (ep_cs->nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) )
{
// There is more data to transfer
// buff_offset has been already increased by hw to correct value for next transfer
prepare_ep_xfer(ep_id, ep_cs->buffer_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes);
}
else
{
xfer_dma->total_bytes = xfer_dma->xferred_bytes;
uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0);
// TODO no way determine if the transfer is failed or not
dcd_event_xfer_complete(0, ep_addr, xfer_dma->xferred_bytes, XFER_RESULT_SUCCESS, true);
}
}
}
}
void USB_IRQHandler(void)
{
uint32_t const dev_cmd_stat = LPC_USB->DEVCMDSTAT;
uint32_t int_status = LPC_USB->INTSTAT & LPC_USB->INTEN;
LPC_USB->INTSTAT = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;
//------------- Device Status -------------//
if ( int_status & INT_DEVICE_STATUS_MASK )
{
LPC_USB->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
if ( dev_cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
{
bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
}
if (dev_cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)
{
// device disconnect
if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
{
// debouncing as this can be set when device is powering
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
}
}
// TODO support suspend & resume
if (dev_cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
{
if (dev_cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK)
{ // suspend signal, bus idle for more than 3ms
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
{
dcd_event_bus_signal(0, DCD_EVENT_SUSPENDED, true);
}
}
}
// else
// { // resume signal
// dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
// }
// }
}
// Setup Receive
if ( TU_BIT_TEST(int_status, 0) && (dev_cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) )
{
// Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints
_dcd.ep[0][0].active = _dcd.ep[1][0].active = 0;
_dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 0;
LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK;
dcd_event_setup_received(0, _dcd.setup_packet, true);
// keep waiting for next setup
_dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet);
// clear bit0
int_status = TU_BIT_CLEAR(int_status, 0);
}
// Endpoint transfer complete interrupt
process_xfer_isr(int_status);
}
#endif
@@ -1,55 +1,51 @@
/**************************************************************************/
/*!
@file hal_lpc175x_6x.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#ifndef _TUSB_HAL_MCU_H_
#define _TUSB_HAL_MCU_H_
#include "LPC17xx.h"
#include "lpc17xx_pinsel.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HAL_MCU_H_ */
/**************************************************************************/
/*!
@file dcd_lpc11_13_15.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#ifndef _TUSB_DCD_LPC11_13_15_H_
#define _TUSB_DCD_LPC11_13_15_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC11_13_15_H_ */
@@ -1,579 +0,0 @@
/**************************************************************************/
/*!
@file dcd_lpc_11uxx_13uxx.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
#define _TINY_USB_SOURCE_FILE_
// NOTE: despite of being very the same to lpc13uxx controller, lpc11u's controller cannot queue transfer more than
// endpoint's max packet size and need some soft DMA helper
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "hal/hal.h"
#include "osal/osal.h"
#include "device/dcd.h"
#include "usbd_dcd.h"
#include "dcd_lpc_11uxx_13uxx.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_11U_13U_QHD_COUNT 10
enum {
DCD_11U_13U_MAX_BYTE_PER_TD = (CFG_TUSB_MCU == OPT_MCU_LPC11UXX ? 64 : 1023)
};
#ifdef __CC_ARM
#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
#endif
enum {
INT_MASK_SOF = BIT_(30),
INT_MASK_DEVICE_STATUS = BIT_(31)
};
#ifdef __CC_ARM
#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
#endif
enum {
CMDSTAT_DEVICE_ADDR_MASK = BIT_(7 )-1,
CMDSTAT_DEVICE_ENABLE_MASK = BIT_(7 ),
CMDSTAT_SETUP_RECEIVED_MASK = BIT_(8 ),
CMDSTAT_DEVICE_CONNECT_MASK = BIT_(16), ///< reflect the softconnect only, does not reflect the actual attached state
CMDSTAT_DEVICE_SUSPEND_MASK = BIT_(17),
CMDSTAT_CONNECT_CHANGE_MASK = BIT_(24),
CMDSTAT_SUSPEND_CHANGE_MASK = BIT_(25),
CMDSTAT_RESET_CHANGE_MASK = BIT_(26),
CMDSTAT_VBUS_DEBOUNCED_MASK = BIT_(28),
};
// buffer input must be 64 byte alignment
typedef struct ATTR_PACKED
{
volatile uint16_t buff_addr_offset ; ///< The address offset is updated by hardware after each successful reception/transmission of a packet. Hardware increments the original value with the integer value when the packet size is divided by 64.
volatile uint16_t nbytes : 10 ; ///< For OUT endpoints this is the number of bytes that can be received in this buffer. For IN endpoints this is the number of bytes that must be transmitted. HW decrements this value with the packet size every time when a packet is successfully transferred. Note: If a short packet is received on an OUT endpoint, the active bit will be cleared and the NBytes value indicates the remaining buffer space that is not used. Software calculates the received number of bytes by subtracting the remaining NBytes from the programmed value.
uint16_t is_isochronous : 1 ;
uint16_t feedback_toggle : 1 ; ///< For bulk endpoints and isochronous endpoints this bit is reserved and must be set to zero. For the control endpoint zero this bit is used as the toggle value. When the toggle reset bit is set, the data toggle is updated with the value programmed in this bit. When the endpoint is used as an interrupt endpoint, it can be set to the following values. 0: Interrupt endpoint in toggle mode 1: Interrupt endpoint in rate feedback mode. This means that the data toggle is fixed to zero for all data packets. When the interrupt endpoint is in rate feedback mode, the TR bit must always be set to zero.
uint16_t toggle_reset : 1 ; ///< When software sets this bit to one, the HW will set the toggle value equal to the value indicated in the “toggle value” (TV) bit. For the control endpoint zero, this is not needed to be used because the hardware resets the endpoint toggle to one for both directions when a setup token is received. For the other endpoints, the toggle can only be reset to zero when the endpoint is reset.
uint16_t stall : 1 ; ///< 0: The selected endpoint is not stalled 1: The selected endpoint is stalled The Active bit has always higher priority than the Stall bit. This means that a Stall handshake is only sent when the active bit is zero and the stall bit is one. Software can only modify this bit when the active bit is zero.
uint16_t disable : 1 ; ///< 0: The selected endpoint is enabled. 1: The selected endpoint is disabled. If a USB token is received for an endpoint that has the disabled bit set, hardware will ignore the token and not return any data or handshake. When a bus reset is received, software must set the disable bit of all endpoints to 1. Software can only modify this bit when the active bit is zero.
volatile uint16_t active : 1 ; ///< The buffer is enabled. HW can use the buffer to store received OUT data or to transmit data on the IN endpoint. Software can only set this bit to 1. As long as this bit is set to one, software is not allowed to update any of the values in this 32-bit word. In case software wants to deactivate the buffer, it must write a one to the corresponding “skip” bit in the USB Endpoint skip register. Hardware can only write this bit to zero. It will do this when it receives a short packet or when the NBytes field transitions to zero or when software has written a one to the “skip” bit.
}dcd_11u_13u_qhd_t;
TU_VERIFY_STATIC( sizeof(dcd_11u_13u_qhd_t) == 4, "size is not correct" );
// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering.
// If there is another dcd_edpt_xfer request, the new request will be saved and executed when the first is done.
// next_td stored the 2nd request information
// current_td is used to keep track of number of remaining & xferred bytes of the current request.
// queued_bytes_in_buff keep track of number of bytes queued to each buffer (in case of short packet)
typedef struct {
dcd_11u_13u_qhd_t qhd[DCD_11U_13U_QHD_COUNT][2]; ///< must be 256 byte alignment, 2 for double buffer
// start at 80, the size should not exceed 48 (for setup_request align at 128)
struct {
uint16_t buff_addr_offset;
uint16_t total_bytes;
}next_td[DCD_11U_13U_QHD_COUNT];
uint32_t current_ioc; ///< interrupt on complete mask for current TD
uint32_t next_ioc; ///< interrupt on complete mask for next TD
// must start from 128
ATTR_ALIGNED(64) tusb_control_request_t setup_request;
struct {
uint16_t remaining_bytes; ///< expected bytes of the queued transfer
uint16_t xferred_total; ///< xferred bytes of the current transfer
uint16_t queued_bytes_in_buff[2]; ///< expected bytes that are queued for each buffer
}current_td[DCD_11U_13U_QHD_COUNT];
uint8_t class_code[DCD_11U_13U_QHD_COUNT]; ///< class where the endpoints belongs to TODO no need for control endpoints
}dcd_11u_13u_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// CFG_TUSB_MEM_SECTION must have ATTR_ALIGNED(64) for lpc11u & lpc13u
#ifdef __ICCARM__
ATTR_ALIGNED(256) CFG_TUSB_MEM_SECTION // for IAR the first ATTR_ALIGNED takes effect
#else
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) // GCC & Keil the last ATTR_ALIGNED takes effect
#endif
STATIC_VAR dcd_11u_13u_data_t dcd_data;
static inline uint16_t addr_offset(void const * p_buffer) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint16_t addr_offset(void const * p_buffer)
{
TU_ASSERT( (((uint32_t) p_buffer) & 0x3f) == 0, 0 );
return (uint16_t) ( (((uint32_t) p_buffer) >> 6 ) & 0xFFFF) ;
}
static void queue_xfer_to_buffer(uint8_t ep_id, uint8_t buff_idx, uint16_t buff_addr_offset, uint16_t total_bytes);
static void pipe_queue_xfer(uint8_t ep_id, uint16_t buff_addr_offset, uint16_t total_bytes);
static void queue_xfer_in_next_td(uint8_t ep_id);
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
void dcd_connect(uint8_t rhport)
{
(void) rhport;
LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_CONNECT_MASK;
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
LPC_USB->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK;
LPC_USB->DEVCMDSTAT |= dev_addr;
}
bool dcd_init(uint8_t rhport)
{
(void) rhport;
LPC_USB->EPLISTSTART = (uint32_t) dcd_data.qhd;
LPC_USB->DATABUFSTART = 0x20000000; // only SRAM1 & USB RAM can be used for transfer
LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
LPC_USB->INTEN = INT_MASK_DEVICE_STATUS;
LPC_USB->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
NVIC_EnableIRQ(USB_IRQn);
return true;
}
static void bus_reset(void)
{
tu_memclr(&dcd_data, sizeof(dcd_11u_13u_data_t));
for(uint8_t ep_id = 2; ep_id < DCD_11U_13U_QHD_COUNT; ep_id++)
{ // disable all non-control endpoints on bus reset
dcd_data.qhd[ep_id][0].disable = dcd_data.qhd[ep_id][1].disable = 1;
}
dcd_data.qhd[0][1].buff_addr_offset = addr_offset(&dcd_data.setup_request);
LPC_USB->EPINUSE = 0;
LPC_USB->EPBUFCFG = 0; // all start with single buffer
LPC_USB->EPSKIP = 0xFFFFFFFF;
LPC_USB->INTSTAT = LPC_USB->INTSTAT; // clear all pending interrupt
LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
LPC_USB->INTEN = INT_MASK_DEVICE_STATUS | BIT_(0) | BIT_(1); // enable device status & control endpoints
}
static void endpoint_non_control_isr(uint32_t int_status)
{
for(uint8_t ep_id = 2; ep_id < DCD_11U_13U_QHD_COUNT; ep_id++ )
{
if ( BIT_TEST_(int_status, ep_id) )
{
dcd_11u_13u_qhd_t * const arr_qhd = dcd_data.qhd[ep_id];
// when double buffering, the complete buffer is opposed to the current active buffer in EPINUSE
uint8_t const buff_idx = LPC_USB->EPINUSE & BIT_(ep_id) ? 0 : 1;
uint16_t const xferred_bytes = dcd_data.current_td[ep_id].queued_bytes_in_buff[buff_idx] - arr_qhd[buff_idx].nbytes;
dcd_data.current_td[ep_id].xferred_total += xferred_bytes;
// there are still data to transfer.
if ( (arr_qhd[buff_idx].nbytes == 0) && (dcd_data.current_td[ep_id].remaining_bytes > 0) )
{ // NOTE although buff_addr_offset has been increased when xfer is completed
// but we still need to increase it one more as we are using double buffering.
queue_xfer_to_buffer(ep_id, buff_idx, arr_qhd[buff_idx].buff_addr_offset+1, dcd_data.current_td[ep_id].remaining_bytes);
}
// short packet or (no more byte and both buffers are finished)
else if ( (arr_qhd[buff_idx].nbytes > 0) || !arr_qhd[1-buff_idx].active )
{ // current TD (request) is completed
LPC_USB->EPSKIP = BIT_SET_(LPC_USB->EPSKIP, ep_id); // skip other endpoint in case of short-package
dcd_data.current_td[ep_id].remaining_bytes = 0;
if ( BIT_TEST_(dcd_data.current_ioc, ep_id) )
{
edpt_hdl_t edpt_hdl =
{
.rhport = 0,
.index = ep_id,
.class_code = dcd_data.class_code[ep_id]
};
dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
// TODO no way determine if the transfer is failed or not
dcd_xfer_complete(edpt_hdl, dcd_data.current_td[ep_id].xferred_total, true);
}
//------------- Next TD is available -------------//
if ( dcd_data.next_td[ep_id].total_bytes != 0 )
{
queue_xfer_in_next_td(ep_id);
}
}else
{
// transfer complete, there is no more remaining bytes, but this buffer is not the last transaction (the other is)
}
}
}
}
static void endpoint_control_isr(uint32_t int_status)
{
uint8_t const ep_id = ( int_status & BIT_(0) ) ? 0 : 1;
// there are still data to transfer.
if ( (dcd_data.qhd[ep_id][0].nbytes == 0) && (dcd_data.current_td[ep_id].remaining_bytes > 0) )
{
queue_xfer_to_buffer(ep_id, 0, dcd_data.qhd[ep_id][0].buff_addr_offset, dcd_data.current_td[ep_id].remaining_bytes);
}else
{
dcd_data.current_td[ep_id].remaining_bytes = 0;
if ( BIT_TEST_(dcd_data.current_ioc, ep_id) )
{
edpt_hdl_t edpt_hdl = { .rhport = 0 };
dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, ep_id);
// FIXME xferred_byte for control xfer is not needed now !!!
dcd_xfer_complete(edpt_hdl, 0, true);
}
}
}
void hal_dcd_isr(uint8_t rhport)
{
(void) rhport;
uint32_t const int_enable = LPC_USB->INTEN;
uint32_t const int_status = LPC_USB->INTSTAT & int_enable;
LPC_USB->INTSTAT = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;
uint32_t const dev_cmd_stat = LPC_USB->DEVCMDSTAT;
dcd_event_t event = { .rhport = rhport };
//------------- Device Status -------------//
if ( int_status & INT_MASK_DEVICE_STATUS )
{
LPC_USB->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
if ( dev_cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
{
bus_reset();
event.event_id = DCD_EVENT_BUS_RESET;
dcd_event_handler(&event, true);
}
if (dev_cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)
{ // device disconnect
if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
{ // debouncing as this can be set when device is powering
event.event_id = DCD_EVENT_UNPLUGGED;
dcd_event_handler(&event, true);
}
}
// TODO support suspend & resume
if (dev_cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
{
if (dev_cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK)
{ // suspend signal, bus idle for more than 3ms
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
{
event.event_id = DCD_EVENT_SUSPENDED;
dcd_event_handler(&event, true);
}
}
}
// else
// { // resume signal
// event.event_id = DCD_EVENT_RESUME;
// dcd_event_handler(&event, true);
// }
// }
}
//------------- Setup Received -------------//
if ( BIT_TEST_(int_status, 0) && (dev_cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) )
{ // received control request from host
// copy setup request & acknowledge so that the next setup can be received by hw
event.event_id = DCD_EVENT_SETUP_RECEIVED;
event.setup_received = dcd_data.setup_request;
dcd_event_handler(&event, true);
// NXP control flowchart clear Active & Stall on both Control IN/OUT endpoints
dcd_data.qhd[0][0].stall = dcd_data.qhd[1][0].stall = 0;
LPC_USB->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK;
dcd_data.qhd[0][1].buff_addr_offset = addr_offset(&dcd_data.setup_request);
}
//------------- Control Endpoint -------------//
else if ( int_status & 0x03 )
{
endpoint_control_isr(int_status);
}
//------------- Non-Control Endpoints -------------//
if( int_status & ~(0x03UL) )
{
endpoint_non_control_isr(int_status);
}
}
//--------------------------------------------------------------------+
// CONTROL PIPE API
//--------------------------------------------------------------------+
void dcd_control_stall(uint8_t rhport)
{
(void) rhport;
// TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
dcd_data.qhd[0][0].stall = dcd_data.qhd[1][0].stall = 1;
}
bool dcd_control_xfer(uint8_t rhport, uint8_t dir, uint8_t * p_buffer, uint16_t length, bool int_on_complete)
{
(void) rhport;
// determine Endpoint where Data & Status phase occurred (IN or OUT)
uint8_t const ep_data = (dir == TUSB_DIR_IN) ? 1 : 0;
uint8_t const ep_status = 1 - ep_data;
dcd_data.current_ioc = int_on_complete ? BIT_SET_(dcd_data.current_ioc, ep_status) : BIT_CLR_(dcd_data.current_ioc, ep_status);
//------------- Data Phase -------------//
if (length)
{
dcd_data.current_td[ep_data].remaining_bytes = length;
dcd_data.current_td[ep_data].xferred_total = 0;
queue_xfer_to_buffer(ep_data, 0, addr_offset(p_buffer), length);
}
//------------- Status Phase -------------//
dcd_data.current_td[ep_status].remaining_bytes = 0;
dcd_data.current_td[ep_status].xferred_total = 0;
queue_xfer_to_buffer(ep_status, 0, 0, 0);
return true;
}
//--------------------------------------------------------------------+
// PIPE HELPER
//--------------------------------------------------------------------+
static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_addr2phy(uint8_t endpoint_addr)
{
return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
}
#if 0
static inline uint8_t edpt_phy2log(uint8_t physical_endpoint) ATTR_CONST ATTR_ALWAYS_INLINE;
static inline uint8_t edpt_phy2log(uint8_t physical_endpoint)
{
return physical_endpoint/2;
}
#endif
//--------------------------------------------------------------------+
// BULK/INTERRUPT/ISOCHRONOUS PIPE API
//--------------------------------------------------------------------+
void dcd_edpt_stall(edpt_hdl_t edpt_hdl)
{
dcd_data.qhd[edpt_hdl.index][0].stall = dcd_data.qhd[edpt_hdl.index][1].stall = 1;
}
bool dcd_pipe_is_stalled(edpt_hdl_t edpt_hdl)
{
return dcd_data.qhd[edpt_hdl.index][0].stall || dcd_data.qhd[edpt_hdl.index][1].stall;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t edpt_addr)
{
uint8_t ep_id = edpt_addr2phy(edpt_addr);
// uint8_t active_buffer = BIT_TEST_(LPC_USB->EPINUSE, ep_id) ? 1 : 0;
dcd_data.qhd[ep_id][0].stall = dcd_data.qhd[ep_id][1].stall = 0;
// since the next transfer always take place on buffer0 --> clear buffer0 toggle
dcd_data.qhd[ep_id][0].toggle_reset = 1;
dcd_data.qhd[ep_id][0].feedback_toggle = 0;
//------------- clear stall must carry on any previously queued transfer -------------//
if ( dcd_data.next_td[ep_id].total_bytes != 0 )
{
queue_xfer_in_next_td(ep_id);
}
}
edpt_hdl_t dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc, uint8_t class_code)
{
(void) rhport;
edpt_hdl_t const null_handle = { 0 };
if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return null_handle; // TODO not support ISO yet
TU_ASSERT (p_endpoint_desc->wMaxPacketSize.size <= 64, null_handle); // TODO ISO can be 1023, but ISO not supported now
// TODO prevent to open if endpoint size is not 64
//------------- Prepare Queue Head -------------//
uint8_t ep_id = edpt_addr2phy(p_endpoint_desc->bEndpointAddress);
TU_ASSERT( dcd_data.qhd[ep_id][0].disable && dcd_data.qhd[ep_id][1].disable, null_handle ); // endpoint must not previously opened, normally this means running out of endpoints
tu_memclr(dcd_data.qhd[ep_id], 2*sizeof(dcd_11u_13u_qhd_t));
dcd_data.qhd[ep_id][0].is_isochronous = dcd_data.qhd[ep_id][1].is_isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
dcd_data.class_code[ep_id] = class_code;
dcd_data.qhd[ep_id][0].disable = dcd_data.qhd[ep_id][1].disable = 0;
LPC_USB->EPBUFCFG |= BIT_(ep_id);
LPC_USB->INTEN |= BIT_(ep_id);
return (edpt_hdl_t)
{
.rhport = 0,
.index = ep_id,
.class_code = class_code
};
}
bool dcd_edpt_busy(edpt_hdl_t edpt_hdl)
{
return dcd_data.qhd[edpt_hdl.index][0].active || dcd_data.qhd[edpt_hdl.index][1].active;
}
static void queue_xfer_to_buffer(uint8_t ep_id, uint8_t buff_idx, uint16_t buff_addr_offset, uint16_t total_bytes)
{
uint16_t const queued_bytes = tu_min16(total_bytes, DCD_11U_13U_MAX_BYTE_PER_TD);
dcd_data.current_td[ep_id].queued_bytes_in_buff[buff_idx] = queued_bytes;
dcd_data.current_td[ep_id].remaining_bytes -= queued_bytes;
dcd_data.qhd[ep_id][buff_idx].buff_addr_offset = buff_addr_offset;
dcd_data.qhd[ep_id][buff_idx].nbytes = queued_bytes;
dcd_data.qhd[ep_id][buff_idx].active = 1;
}
static void pipe_queue_xfer(uint8_t ep_id, uint16_t buff_addr_offset, uint16_t total_bytes)
{
dcd_data.current_td[ep_id].remaining_bytes = total_bytes;
dcd_data.current_td[ep_id].xferred_total = 0;
dcd_data.current_td[ep_id].queued_bytes_in_buff[0] = 0;
dcd_data.current_td[ep_id].queued_bytes_in_buff[1] = 0;
LPC_USB->EPINUSE = BIT_CLR_(LPC_USB->EPINUSE , ep_id); // force HW to use buffer0
// need to queue buffer1 first, as activate buffer0 can causes controller does transferring immediately
// while buffer1 is not ready yet
if ( total_bytes > DCD_11U_13U_MAX_BYTE_PER_TD)
{
queue_xfer_to_buffer(ep_id, 1, buff_addr_offset+1, total_bytes - DCD_11U_13U_MAX_BYTE_PER_TD);
}
queue_xfer_to_buffer(ep_id, 0, buff_addr_offset, total_bytes);
}
static void queue_xfer_in_next_td(uint8_t ep_id)
{
dcd_data.current_ioc |= ( dcd_data.next_ioc & BIT_(ep_id) ); // copy next IOC to current IOC
pipe_queue_xfer(ep_id, dcd_data.next_td[ep_id].buff_addr_offset, dcd_data.next_td[ep_id].total_bytes);
dcd_data.next_td[ep_id].total_bytes = 0; // clear this field as it is used to indicate whehther next TD available
}
tusb_error_t dcd_edpt_queue_xfer(edpt_hdl_t edpt_hdl, uint8_t * buffer, uint16_t total_bytes)
{
TU_ASSERT( !dcd_edpt_busy(edpt_hdl), TUSB_ERROR_INTERFACE_IS_BUSY); // endpoint must not in transferring
dcd_data.current_ioc = BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
pipe_queue_xfer(edpt_hdl.index, addr_offset(buffer), total_bytes);
return TUSB_ERROR_NONE;
}
tusb_error_t dcd_edpt_xfer(edpt_hdl_t edpt_hdl, uint8_t* buffer, uint16_t total_bytes, bool int_on_complete)
{
if( dcd_edpt_busy(edpt_hdl) || dcd_pipe_is_stalled(edpt_hdl) )
{ // save this transfer data to next td if pipe is busy or already been stalled
dcd_data.next_td[edpt_hdl.index].buff_addr_offset = addr_offset(buffer);
dcd_data.next_td[edpt_hdl.index].total_bytes = total_bytes;
dcd_data.next_ioc = int_on_complete ? BIT_SET_(dcd_data.next_ioc, edpt_hdl.index) : BIT_CLR_(dcd_data.next_ioc, edpt_hdl.index);
}else
{
dcd_data.current_ioc = int_on_complete ? BIT_SET_(dcd_data.current_ioc, edpt_hdl.index) : BIT_CLR_(dcd_data.current_ioc, edpt_hdl.index);
pipe_queue_xfer(edpt_hdl.index, addr_offset(buffer), total_bytes);
}
return TUSB_ERROR_NONE;
}
#endif
+602
View File
@@ -0,0 +1,602 @@
/**************************************************************************/
/*!
@file dcd_lpc175x_6x.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#include "device/dcd.h"
#include "dcd_lpc17_40.h"
#include "chip.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_ENDPOINT_MAX 32
typedef struct ATTR_ALIGNED(4)
{
//------------- Word 0 -------------//
uint32_t next;
//------------- Word 1 -------------//
uint16_t atle_mode : 2; // 00: normal, 01: ATLE (auto length extraction)
uint16_t next_valid : 1;
uint16_t : 1; ///< reserved
uint16_t isochronous : 1; // is an iso endpoint
uint16_t max_packet_size : 11;
volatile uint16_t buflen; // bytes for non-iso, number of packets for iso endpoint
//------------- Word 2 -------------//
volatile uint32_t buffer;
//------------- Word 3 -------------//
volatile uint16_t retired : 1; // initialized to zero
volatile uint16_t status : 4;
volatile uint16_t iso_last_packet_valid : 1;
volatile uint16_t atle_lsb_extracted : 1; // used in ATLE mode
volatile uint16_t atle_msb_extracted : 1; // used in ATLE mode
volatile uint16_t atle_mess_len_position : 6; // used in ATLE mode
uint16_t : 2;
volatile uint16_t present_count; // For non-iso : The number of bytes transferred by the DMA engine
// For iso : number of packets
//------------- Word 4 -------------//
// uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso
}dma_desc_t;
TU_VERIFY_STATIC( sizeof(dma_desc_t) == 16, "size is not correct"); // TODO not support ISO for now
typedef struct
{
// must be 128 byte aligned
volatile dma_desc_t* udca[DCD_ENDPOINT_MAX];
// TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory)
dma_desc_t dd[DCD_ENDPOINT_MAX];
struct
{
uint8_t* out_buffer;
uint8_t out_bytes;
volatile bool out_received; // indicate if data is already received in endpoint
uint8_t in_bytes;
} control;
} dcd_data_t;
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(128) static dcd_data_t _dcd;
//--------------------------------------------------------------------+
// SIE Command
//--------------------------------------------------------------------+
static void sie_cmd_code (sie_cmdphase_t phase, uint8_t code_data)
{
LPC_USB->DevIntClr = (DEV_INT_COMMAND_CODE_EMPTY_MASK | DEV_INT_COMMAND_DATA_FULL_MASK);
LPC_USB->CmdCode = (phase << 8) | (code_data << 16);
uint32_t const wait_flag = (phase == SIE_CMDPHASE_READ) ? DEV_INT_COMMAND_DATA_FULL_MASK : DEV_INT_COMMAND_CODE_EMPTY_MASK;
while ((LPC_USB->DevIntSt & wait_flag) == 0) {}
LPC_USB->DevIntClr = wait_flag;
}
static void sie_write (uint8_t cmd_code, uint8_t data_len, uint8_t data)
{
sie_cmd_code(SIE_CMDPHASE_COMMAND, cmd_code);
if (data_len)
{
sie_cmd_code(SIE_CMDPHASE_WRITE, data);
}
}
static uint8_t sie_read (uint8_t cmd_code)
{
sie_cmd_code(SIE_CMDPHASE_COMMAND , cmd_code);
sie_cmd_code(SIE_CMDPHASE_READ , cmd_code);
return (uint8_t) LPC_USB->CmdData;
}
//--------------------------------------------------------------------+
// PIPE HELPER
//--------------------------------------------------------------------+
static inline uint8_t ep_addr2idx(uint8_t ep_addr)
{
return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
}
static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size)
{
// follows example in 11.10.4.2
LPC_USB->ReEp |= TU_BIT(ep_id);
LPC_USB->EpInd = ep_id; // select index before setting packet size
LPC_USB->MaxPSize = max_packet_size;
while ((LPC_USB->DevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK) == 0) {}
LPC_USB->DevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK;
}
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
static void bus_reset(void)
{
// step 7 : slave mode set up
LPC_USB->EpIntClr = 0xFFFFFFFF; // clear all pending interrupt
LPC_USB->DevIntClr = 0xFFFFFFFF; // clear all pending interrupt
LPC_USB->EpIntEn = 0x03UL; // control endpoint cannot use DMA, non-control all use DMA
LPC_USB->EpIntPri = 0x03UL; // fast for control endpoint
// step 8 : DMA set up
LPC_USB->EpDMADis = 0xFFFFFFFF; // firstly disable all dma
LPC_USB->DMARClr = 0xFFFFFFFF; // clear all pending interrupt
LPC_USB->EoTIntClr = 0xFFFFFFFF;
LPC_USB->NDDRIntClr = 0xFFFFFFFF;
LPC_USB->SysErrIntClr = 0xFFFFFFFF;
tu_memclr(&_dcd, sizeof(dcd_data_t));
}
bool dcd_init(uint8_t rhport)
{
(void) rhport;
//------------- user manual 11.13 usb device controller initialization -------------//
// step 6 : set up control endpoint
set_ep_size(0, CFG_TUD_ENDOINT0_SIZE);
set_ep_size(1, CFG_TUD_ENDOINT0_SIZE);
bus_reset();
LPC_USB->DevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_FAST_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK);
LPC_USB->UDCAH = (uint32_t) _dcd.udca;
LPC_USB->DMAIntEn = (DMA_INT_END_OF_XFER_MASK /*| DMA_INT_NEW_DD_REQUEST_MASK*/ | DMA_INT_ERROR_MASK);
sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 1); // connect
// USB IRQ priority should be set by application previously
NVIC_ClearPendingIRQ(USB_IRQn);
NVIC_EnableIRQ(USB_IRQn);
return TUSB_ERROR_NONE;
}
void dcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void) rhport;
sie_write(SIE_CMDCODE_SET_ADDRESS, 1, 0x80 | dev_addr); // 7th bit is : device_enable
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
(void) rhport;
(void) config_num;
sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1);
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
(void) rhport;
return (uint32_t) sie_read(SIE_CMDCODE_READ_FRAME_NUMBER);
}
//--------------------------------------------------------------------+
// CONTROL HELPER
//--------------------------------------------------------------------+
static inline uint8_t byte2dword(uint8_t bytes)
{
return (bytes + 3) / 4; // length in dwords
}
static void control_ep_write(void const * buffer, uint8_t len)
{
uint32_t const * buf32 = (uint32_t const *) buffer;
LPC_USB->Ctrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0
LPC_USB->TxPLen = (uint32_t) len;
for (uint8_t count = 0; count < byte2dword(len); count++)
{
LPC_USB->TxData = *buf32; // NOTE: cortex M3 have no problem with alignment
buf32++;
}
LPC_USB->Ctrl = 0;
// select control IN & validate the endpoint
sie_write(SIE_CMDCODE_ENDPOINT_SELECT+1, 0, 0);
sie_write(SIE_CMDCODE_BUFFER_VALIDATE , 0, 0);
}
static uint8_t control_ep_read(void * buffer, uint8_t len)
{
LPC_USB->Ctrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0
while ((LPC_USB->RxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout
len = tu_min8(len, (uint8_t) (LPC_USB->RxPLen & USBRXPLEN_PACKET_LENGTH_MASK) );
uint32_t *buf32 = (uint32_t*) buffer;
for (uint8_t count=0; count < byte2dword(len); count++)
{
*buf32 = LPC_USB->RxData;
buf32++;
}
LPC_USB->Ctrl = 0;
// select control OUT & clear the endpoint
sie_write(SIE_CMDCODE_ENDPOINT_SELECT+0, 0, 0);
sie_write(SIE_CMDCODE_BUFFER_CLEAR , 0, 0);
return len;
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const ep_id = ep_addr2idx(p_endpoint_desc->bEndpointAddress);
// Endpoint type is fixed to endpoint number
// 1: interrupt, 2: Bulk, 3: Iso and so on
switch ( p_endpoint_desc->bmAttributes.xfer )
{
case TUSB_XFER_INTERRUPT:
TU_ASSERT((epnum % 3) == 1);
break;
case TUSB_XFER_BULK:
TU_ASSERT((epnum % 3) == 2 || (epnum == 15));
break;
case TUSB_XFER_ISOCHRONOUS:
TU_ASSERT((epnum % 3) == 3 && (epnum != 15));
break;
default:
break;
}
//------------- Realize Endpoint with Max Packet Size -------------//
set_ep_size(ep_id, p_endpoint_desc->wMaxPacketSize.size);
//------------- first DD prepare -------------//
dma_desc_t* const dd = &_dcd.dd[ep_id];
tu_memclr(dd, sizeof(dma_desc_t));
dd->isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
dd->retired = 1; // invalid at first
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id, 1, 0); // clear all endpoint status
return true;
}
bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t ep_id = ep_addr2idx( ep_addr );
return (_dcd.udca[ep_id] != NULL && !_dcd.udca[ep_id]->retired);
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( tu_edpt_number(ep_addr) == 0 )
{
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+0, 1, SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK);
}else
{
uint8_t ep_id = ep_addr2idx( ep_addr );
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, SIE_SET_ENDPOINT_STALLED_MASK);
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t ep_id = ep_addr2idx(ep_addr);
sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0);
}
bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const ep_state = sie_read(SIE_CMDCODE_ENDPOINT_SELECT + ep_addr2idx(ep_addr));
return (ep_state & SIE_SELECT_ENDPOINT_STALL_MASK) ? true : false;
}
static bool control_xact(uint8_t rhport, uint8_t dir, uint8_t * buffer, uint8_t len)
{
(void) rhport;
if ( dir )
{
_dcd.control.in_bytes = len;
control_ep_write(buffer, len);
}else
{
if ( _dcd.control.out_received )
{
// Already received the DATA OUT packet
_dcd.control.out_received = false;
_dcd.control.out_buffer = NULL;
_dcd.control.out_bytes = 0;
uint8_t received = control_ep_read(buffer, len);
dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true);
}else
{
_dcd.control.out_buffer = buffer;
_dcd.control.out_bytes = len;
}
}
return true;
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
// Control transfer is not DMA support, and must be done in slave mode
if ( tu_edpt_number(ep_addr) == 0 )
{
return control_xact(rhport, tu_edpt_dir(ep_addr), buffer, (uint8_t) total_bytes);
}
else
{
uint8_t ep_id = ep_addr2idx(ep_addr);
dma_desc_t* dd = &_dcd.dd[ep_id];
// Prepare DMA descriptor
// Isochronous & max packet size must be preserved, Other fields of dd should be clear
uint16_t const ep_size = dd->max_packet_size;
uint8_t is_iso = dd->isochronous;
tu_memclr(dd, sizeof(dma_desc_t));
dd->isochronous = is_iso;
dd->max_packet_size = ep_size;
dd->buffer = (uint32_t) buffer;
dd->buflen = total_bytes;
_dcd.udca[ep_id] = dd;
if ( ep_id % 2 )
{
// Clear EP interrupt before Enable DMA
LPC_USB->EpIntEn &= ~TU_BIT(ep_id);
LPC_USB->EpDMAEn = TU_BIT(ep_id);
// endpoint IN need to actively raise DMA request
LPC_USB->DMARSet = TU_BIT(ep_id);
}else
{
// Enable DMA
LPC_USB->EpDMAEn = TU_BIT(ep_id);
}
return true;
}
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
// handle control xfer (slave mode)
static void control_xfer_isr(uint8_t rhport, uint32_t ep_int_status)
{
// Control out complete
if ( ep_int_status & TU_BIT(0) )
{
bool is_setup = sie_read(SIE_CMDCODE_ENDPOINT_SELECT+0) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK;
LPC_USB->EpIntClr = TU_BIT(0);
if (is_setup)
{
uint8_t setup_packet[8];
control_ep_read(setup_packet, 8); // TODO read before clear setup above
dcd_event_setup_received(rhport, setup_packet, true);
}
else if ( _dcd.control.out_buffer )
{
// software queued transfer previously
uint8_t received = control_ep_read(_dcd.control.out_buffer, _dcd.control.out_bytes);
_dcd.control.out_buffer = NULL;
_dcd.control.out_bytes = 0;
dcd_event_xfer_complete(rhport, 0, received, XFER_RESULT_SUCCESS, true);
}else
{
// hardware auto ack packet -> mark as received
_dcd.control.out_received = true;
}
}
// Control In complete
if ( ep_int_status & TU_BIT(1) )
{
LPC_USB->EpIntClr = TU_BIT(1);
dcd_event_xfer_complete(rhport, TUSB_DIR_IN_MASK, _dcd.control.in_bytes, XFER_RESULT_SUCCESS, true);
}
}
// handle bus event signal
static void bus_event_isr(uint8_t rhport)
{
uint8_t const dev_status = sie_read(SIE_CMDCODE_DEVICE_STATUS);
if (dev_status & SIE_DEV_STATUS_RESET_MASK)
{
bus_reset();
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
}
if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)
{
// device is disconnected, require using VBUS (P1_30)
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
if (dev_status & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
{
if (dev_status & SIE_DEV_STATUS_SUSPEND_MASK)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
}
else
{
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
}
}
// Helper to complete a DMA descriptor for non-control transfer
static void dd_complete_isr(uint8_t rhport, uint8_t ep_id)
{
dma_desc_t* const dd = &_dcd.dd[ep_id];
uint8_t result = (dd->status == DD_STATUS_NORMAL || dd->status == DD_STATUS_DATA_UNDERUN) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED;
uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0);
dcd_event_xfer_complete(rhport, ep_addr, dd->present_count, result, true);
}
// main USB IRQ handler
void hal_dcd_isr(uint8_t rhport)
{
uint32_t const dev_int_status = LPC_USB->DevIntSt & LPC_USB->DevIntEn;
LPC_USB->DevIntClr = dev_int_status;// Acknowledge handled interrupt
// Bus event
if (dev_int_status & DEV_INT_DEVICE_STATUS_MASK)
{
bus_event_isr(rhport);
}
// Endpoint interrupt
uint32_t const ep_int_status = LPC_USB->EpIntSt & LPC_USB->EpIntEn;
// Control Endpoint are fast
if (dev_int_status & DEV_INT_ENDPOINT_FAST_MASK)
{
// Note clear USBEpIntClr will also clear the setup received bit --> clear after handle setup packet
// Only clear USBEpIntClr 1 endpoint each, and should wait for CDFULL bit set
control_xfer_isr(rhport, ep_int_status);
}
// non-control IN are slow
if (dev_int_status & DEV_INT_ENDPOINT_SLOW_MASK)
{
for ( uint8_t ep_id = 3; ep_id < DCD_ENDPOINT_MAX; ep_id += 2 )
{
if ( TU_BIT_TEST(ep_int_status, ep_id) )
{
LPC_USB->EpIntClr = TU_BIT(ep_id);
// Clear Ep interrupt for next DMA
LPC_USB->EpIntEn &= ~TU_BIT(ep_id);
dd_complete_isr(rhport, ep_id);
}
}
}
// DMA transfer complete (RAM <-> EP) for Non-Control
// OUT: USB transfer is fully complete
// IN : UBS transfer is still on-going -> enable EpIntEn to know when it is complete
uint32_t const dma_int_status = LPC_USB->DMAIntSt & LPC_USB->DMAIntEn;
if (dma_int_status & DMA_INT_END_OF_XFER_MASK)
{
uint32_t const eot = LPC_USB->EoTIntSt;
LPC_USB->EoTIntClr = eot; // acknowledge interrupt source
for ( uint8_t ep_id = 2; ep_id < DCD_ENDPOINT_MAX; ep_id++ )
{
if ( TU_BIT_TEST(eot, ep_id) )
{
if ( ep_id & 0x01 )
{
// IN enable EpInt for end of usb transfer
LPC_USB->EpIntEn |= TU_BIT(ep_id);
}else
{
// OUT
dd_complete_isr(rhport, ep_id);
}
}
}
}
// Errors
if ( (dev_int_status & DEV_INT_ERROR_MASK) || (dma_int_status & DMA_INT_ERROR_MASK) )
{
uint32_t error_status = sie_read(SIE_CMDCODE_READ_ERROR_STATUS);
(void) error_status;
TU_BREAKPOINT();
}
}
#endif
+164
View File
@@ -0,0 +1,164 @@
/**************************************************************************/
/*!
@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.
*/
/**************************************************************************/
#ifndef _TUSB_DCD_LPC175X_6X_H_
#define _TUSB_DCD_LPC175X_6X_H_
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Register Interface
//--------------------------------------------------------------------+
//------------- USB Interrupt USBIntSt -------------//
//enum {
// DCD_USB_REQ_LOW_PRIO_MASK = TU_BIT(0),
// DCD_USB_REQ_HIGH_PRIO_MASK = TU_BIT(1),
// DCD_USB_REQ_DMA_MASK = TU_BIT(2),
// DCD_USB_REQ_NEED_CLOCK_MASK = TU_BIT(8),
// DCD_USB_REQ_ENABLE_MASK = TU_BIT(31)
//};
//------------- Device Interrupt USBDevInt -------------//
enum {
DEV_INT_FRAME_MASK = TU_BIT(0),
DEV_INT_ENDPOINT_FAST_MASK = TU_BIT(1),
DEV_INT_ENDPOINT_SLOW_MASK = TU_BIT(2),
DEV_INT_DEVICE_STATUS_MASK = TU_BIT(3),
DEV_INT_COMMAND_CODE_EMPTY_MASK = TU_BIT(4),
DEV_INT_COMMAND_DATA_FULL_MASK = TU_BIT(5),
DEV_INT_RX_ENDPOINT_PACKET_MASK = TU_BIT(6),
DEV_INT_TX_ENDPOINT_PACKET_MASK = TU_BIT(7),
DEV_INT_ENDPOINT_REALIZED_MASK = TU_BIT(8),
DEV_INT_ERROR_MASK = TU_BIT(9)
};
//------------- DMA Interrupt USBDMAInt-------------//
enum {
DMA_INT_END_OF_XFER_MASK = TU_BIT(0),
DMA_INT_NEW_DD_REQUEST_MASK = TU_BIT(1),
DMA_INT_ERROR_MASK = TU_BIT(2)
};
//------------- USBCtrl -------------//
enum {
USBCTRL_READ_ENABLE_MASK = TU_BIT(0),
USBCTRL_WRITE_ENABLE_MASK = TU_BIT(1),
};
//------------- USBRxPLen -------------//
enum {
USBRXPLEN_PACKET_LENGTH_MASK = (TU_BIT(10)-1),
USBRXPLEN_DATA_VALID_MASK = TU_BIT(10),
USBRXPLEN_PACKET_READY_MASK = TU_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 = TU_BIT(0),
SIE_DEV_STATUS_CONNECT_CHANGE_MASK = TU_BIT(1),
SIE_DEV_STATUS_SUSPEND_MASK = TU_BIT(2),
SIE_DEV_STATUS_SUSPEND_CHANGE_MASK = TU_BIT(3),
SIE_DEV_STATUS_RESET_MASK = TU_BIT(4)
};
//------------- SIE Select Endpoint Command -------------//
enum {
SIE_SELECT_ENDPOINT_FULL_EMPTY_MASK = TU_BIT(0), // 0: empty, 1 full. IN endpoint checks empty, OUT endpoint check full
SIE_SELECT_ENDPOINT_STALL_MASK = TU_BIT(1),
SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK = TU_BIT(2), // clear by SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT
SIE_SELECT_ENDPOINT_PACKET_OVERWRITTEN_MASK = TU_BIT(3), // previous packet is overwritten by a SETUP packet
SIE_SELECT_ENDPOINT_NAK_MASK = TU_BIT(4), // last packet response is NAK (auto clear by an ACK)
SIE_SELECT_ENDPOINT_BUFFER1_FULL_MASK = TU_BIT(5),
SIE_SELECT_ENDPOINT_BUFFER2_FULL_MASK = TU_BIT(6)
};
typedef enum
{
SIE_SET_ENDPOINT_STALLED_MASK = TU_BIT(0),
SIE_SET_ENDPOINT_DISABLED_MASK = TU_BIT(5),
SIE_SET_ENDPOINT_RATE_FEEDBACK_MASK = TU_BIT(6),
SIE_SET_ENDPOINT_CONDITION_STALLED_MASK = TU_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
};
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC175X_6X_H_ */
@@ -1,6 +1,6 @@
/**************************************************************************/
/*!
@file usbh_hub.h
@file hal_lpc175x_6x.c
@author hathach (tinyusb.org)
@section LICENSE
@@ -36,29 +36,38 @@
*/
/**************************************************************************/
/** \ingroup group_usbh
* @{ */
#ifndef _TUSB_USBH_HUB_H_
#define _TUSB_USBH_HUB_H_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#if (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#include "chip.h"
extern void hal_hcd_isr(uint8_t hostid);
extern void hal_dcd_isr(uint8_t rhport);
void USB_IRQHandler(void)
{
#if TUSB_OPT_HOST_ENABLED
hal_hcd_isr(0);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(0);
#endif
}
//FIXME move later
void hcd_int_enable(uint8_t rhport)
{
(void) rhport;
NVIC_EnableIRQ(USB_IRQn);
}
void hcd_int_disable(uint8_t rhport)
{
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
#endif
void usbh_hub_port_plugged_isr(uint8_t hub_addr, uint8_t hub_port);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_USBH_HUB_H_ */
/** @} */
-543
View File
@@ -1,543 +0,0 @@
/**************************************************************************/
/*!
@file dcd_lpc175x_6x.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X)
#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_MEM_SECTION 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;
tu_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
dcd_event_t event = { .rhport = 0 };
//------------- 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
event.event_id = DCD_EVENT_SETUP_RECEIVED;
pipe_control_read(&event.setup_received, 8); // TODO read before clear setup above
dcd_event_handler(&event, true);
}
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
dcd_event_t event = { .rhport = rhport };
//------------- 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();
event.event_id = DCD_EVENT_BUS_RESET;
dcd_event_handler(&event, true);
}
if (dev_status_reg & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)
{ // device is disconnected, require using VBUS (P1_30)
event.event_id = DCD_EVENT_UNPLUGGED;
dcd_event_handler(&event, true);
}
if (dev_status_reg & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
{
if (dev_status_reg & SIE_DEV_STATUS_SUSPEND_MASK)
{
event.event_id = DCD_EVENT_SUSPENDED;
dcd_event_handler(&event, true);
}
// else
// { // resume signal
// event.event_id = DCD_EVENT_RESUME;
// dcd_event_handler(&event, true);
// }
// }
}
}
//------------- 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 = tu_min16(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 = tu_min16(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, uint8_t dir, uint8_t * p_buffer, uint16_t length, bool int_on_complete)
{
(void) rhport;
TU_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
TU_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 ????
TU_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];
tu_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];
tu_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
-240
View File
@@ -1,240 +0,0 @@
/**************************************************************************/
/*!
@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;
TU_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_ */
/** @} */
-120
View File
@@ -1,120 +0,0 @@
/**************************************************************************/
/*!
@file hal_lpc175x_6x.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "common/tusb_common.h"
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X
#include "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 TUSB_OPT_DEVICE_ENABLED
LPC_PINCON->PINSEL4 = bit_set_range(LPC_PINCON->PINSEL4, 18, 19, BIN8(01)); // P2_9 as USB Connect
// P1_30 as VBUS, ignore if it is already in VBUS mode
if ( !(!BIT_TEST_(LPC_PINCON->PINSEL3, 28) && BIT_TEST_(LPC_PINCON->PINSEL3, 29)) )
{
// some board like lpcxpresso1769 does not connect VBUS signal to pin P1_30, this allow those board to overwrite
// by always pulling P1_30 to high
PINSEL_ConfigPin( &(PINSEL_CFG_Type) {
.Portnum = 1, .Pinnum = 30,
.Funcnum = 2, .Pinmode = PINSEL_PINMODE_PULLDOWN} );
}
LPC_USB->USBClkCtrl = USBCLK_DEVCIE;
while ((LPC_USB->USBClkSt & USBCLK_DEVCIE) != USBCLK_DEVCIE);
#endif
return true;
}
void USB_IRQHandler(void)
{
#if MODE_HOST_SUPPORTED
hal_hcd_isr(0);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(0);
#endif
}
void check_failed(uint8_t *file, uint32_t line)
{
(void) file;
(void) line;
}
#endif
@@ -1,413 +1,396 @@
/**************************************************************************/
/*!
@file dcd_lpc43xx.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_LPC43XX
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "tusb_hal.h"
#include "osal/osal.h"
#include "device/dcd.h"
#include "dcd_lpc43xx.h"
#include "LPC43xx.h"
#include "lpc43xx_cgu.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
typedef struct {
dcd_qhd_t qhd[DCD_QHD_MAX] ATTR_ALIGNED(64); ///< Must be at 2K alignment
dcd_qtd_t qtd[DCD_QTD_MAX] ATTR_ALIGNED(32);
}dcd_data_t;
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data0;
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data1;
#endif
static LPC_USB0_Type * const LPC_USB[2] = { LPC_USB0, ((LPC_USB0_Type*) LPC_USB1_BASE) };
static dcd_data_t* const dcd_data_ptr[2] =
{
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
&dcd_data0,
#else
NULL,
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
&dcd_data1
#else
NULL
#endif
};
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
void dcd_connect(uint8_t rhport)
{
LPC_USB[rhport]->USBCMD_D |= BIT_(0);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
LPC_USB[rhport]->DEVICEADDR = (dev_addr << 25) | BIT_(24);
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
}
/// follows LPC43xx User Manual 23.10.3
static void bus_reset(uint8_t rhport)
{
LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
// The reset value for all endpoint types is the control endpoint. If one endpoint
// direction is enabled and the paired endpoint of opposite direction is disabled, then the
// endpoint type of the unused direction must bechanged from the control type to any other
// type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior
// for the data PID tracking on the active endpoint.
lpc_usb->ENDPTCTRL1 = lpc_usb->ENDPTCTRL2 = lpc_usb->ENDPTCTRL3 = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
// USB1 only has 3 non-control endpoints
if ( rhport == 0)
{
lpc_usb->ENDPTCTRL4 = lpc_usb->ENDPTCTRL5 = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
}
//------------- Clear All Registers -------------//
lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK;
lpc_usb->ENDPTNAKEN = 0;
lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;
lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE;
while (lpc_usb->ENDPTPRIME);
lpc_usb->ENDPTFLUSH = 0xFFFFFFFF;
while (lpc_usb->ENDPTFLUSH);
// read reset bit in portsc
//------------- Queue Head & Queue TD -------------//
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
tu_memclr(p_dcd, sizeof(dcd_data_t));
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1;
p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDOINT0_SIZE;
p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
p_dcd->qhd[0].int_on_setup = 1; // OUT only
}
bool dcd_init(uint8_t rhport)
{
LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
tu_memclr(p_dcd, sizeof(dcd_data_t));
lpc_usb->ENDPOINTLISTADDR = (uint32_t) p_dcd->qhd; // Endpoint List Address has to be 2K alignment
lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF;
lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0
lpc_usb->USBCMD_D |= BIT_(0); // connect
// enable interrupt
NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
return true;
}
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
// index to bit position in register
static inline uint8_t ep_idx2bit(uint8_t ep_idx)
{
return ep_idx/2 + ( (ep_idx%2) ? 16 : 0);
}
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
p_qtd->next = QTD_NEXT_INVALID;
p_qtd->active = 1;
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
if (data_ptr != NULL)
{
p_qtd->buffer[0] = (uint32_t) data_ptr;
for(uint8_t i=1; i<5; i++)
{
p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
}
}
}
static inline volatile uint32_t * get_endpt_ctrl_reg(uint8_t rhport, uint8_t ep_idx)
{
return &(LPC_USB[rhport]->ENDPTCTRL0) + ep_idx/2;
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
if ( epnum == 0)
{
// Stall both Control IN and OUT
(*endpt_ctrl) |= ( (ENDPTCTRL_MASK_STALL << 16) || (ENDPTCTRL_MASK_STALL << 0) );
}else
{
(*endpt_ctrl) |= ENDPTCTRL_MASK_STALL << (ep_idx & 0x01 ? 16 : 0);
}
}
// TOOD implement later
bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
{
return false;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
// data toggle also need to be reset
(*endpt_ctrl) |= ENDPTCTRL_MASK_TOGGLE_RESET << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0);
(*endpt_ctrl) &= ~(ENDPTCTRL_MASK_STALL << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0));
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
// TODO USB1 only has 4 non-control enpoint (USB0 has 5)
// TODO not support ISO yet
TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
uint8_t const epnum = edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const dir = edpt_dir(p_endpoint_desc->bEndpointAddress);
uint8_t const ep_idx = 2*epnum + dir;
//------------- Prepare Queue Head -------------//
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
tu_memclr(p_qhd, sizeof(dcd_qhd_t));
p_qhd->zero_length_termination = 1;
p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
//------------- Endpoint Control Register -------------//
volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
// endpoint must not be already enabled
TU_VERIFY( !( (*endpt_ctrl) & (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)) ) );
(*endpt_ctrl) |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
return true;
}
bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
dcd_qhd_t const * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
return p_qtd->active;
// return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
if ( epnum == 0 )
{
// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
// wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
while(LPC_USB[rhport]->ENDPTSETUPSTAT & BIT_(0)) {}
}
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
dcd_qhd_t * p_qhd = &p_dcd->qhd[ep_idx];
dcd_qtd_t * p_qtd = &p_dcd->qtd[ep_idx];
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
p_qtd->int_on_complete = true;
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
// start transfer
LPC_USB[rhport]->ENDPTPRIME = BIT_( ep_idx2bit(ep_idx) ) ;
return true;
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
void hal_dcd_isr(uint8_t rhport)
{
LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
uint32_t const int_enable = lpc_usb->USBINTR_D;
uint32_t const int_status = lpc_usb->USBSTS_D & int_enable;
lpc_usb->USBSTS_D = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;// disabled interrupt sources
if (int_status & INT_MASK_RESET)
{
bus_reset(rhport);
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
dcd_event_handler(&event, true);
}
if (int_status & INT_MASK_SUSPEND)
{
if (lpc_usb->PORTSC1_D & PORTSC_SUSPEND_MASK)
{ // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if ((lpc_usb->DEVICEADDR >> 25) & 0x0f)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPENDED };
dcd_event_handler(&event, true);
}
}
}
// TODO disconnection does not generate interrupt !!!!!!
// if (int_status & INT_MASK_PORT_CHANGE)
// {
// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) )
// {
// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
// dcd_event_handler(&event, true);
// }
// }
if (int_status & INT_MASK_USB)
{
uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE;
lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge
dcd_data_t* const p_dcd = dcd_data_ptr[rhport];
if (lpc_usb->ENDPTSETUPSTAT)
{
//------------- Set up Received -------------//
// 23.10.10.2 Operational model for setup transfers
lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
event.setup_received = p_dcd->qhd[0].setup_request;
dcd_event_handler(&event, true);
}
if ( edpt_complete )
{
for(uint8_t ep_idx = 0; ep_idx < DCD_QHD_MAX; ep_idx++)
{
if ( BIT_TEST_(edpt_complete, ep_idx2bit(ep_idx)) )
{
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
uint8_t ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 );
dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
}
}
}
}
if (int_status & INT_MASK_SOF)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF };
dcd_event_handler(&event, true);
}
if (int_status & INT_MASK_NAK) {}
if (int_status & INT_MASK_ERROR) TU_ASSERT(false, );
}
#endif
/**************************************************************************/
/*!
@file dcd_lpc43xx.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "tusb_hal.h"
#include "device/dcd.h"
#include "dcd_lpc18_43.h"
#include "chip.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define QHD_MAX 12
#define QTD_NEXT_INVALID 0x01
typedef struct {
// Must be at 2K alignment
dcd_qhd_t qhd[QHD_MAX] ATTR_ALIGNED(64);
dcd_qtd_t qtd[QHD_MAX] ATTR_ALIGNED(32);
}dcd_data_t;
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data0;
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data1;
#endif
static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 };
static dcd_data_t* const dcd_data_ptr[2] =
{
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
&dcd_data0,
#else
NULL,
#endif
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
&dcd_data1
#else
NULL
#endif
};
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
/// follows LPC43xx User Manual 23.10.3
static void bus_reset(uint8_t rhport)
{
LPC_USBHS_T* lpc_usb = LPC_USB[rhport];
// The reset value for all endpoint types is the control endpoint. If one endpoint
// direction is enabled and the paired endpoint of opposite direction is disabled, then the
// endpoint type of the unused direction must bechanged from the control type to any other
// type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior
// for the data PID tracking on the active endpoint.
// USB0 has 5 but USB1 only has 3 non-control endpoints
for( int i=1; i < (rhport ? 6 : 4); i++)
{
lpc_usb->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
}
//------------- Clear All Registers -------------//
lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK;
lpc_usb->ENDPTNAKEN = 0;
lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;
lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE;
while (lpc_usb->ENDPTPRIME);
lpc_usb->ENDPTFLUSH = 0xFFFFFFFF;
while (lpc_usb->ENDPTFLUSH);
// read reset bit in portsc
//------------- Queue Head & Queue TD -------------//
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
tu_memclr(p_dcd, sizeof(dcd_data_t));
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1;
p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDOINT0_SIZE;
p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
p_dcd->qhd[0].int_on_setup = 1; // OUT only
}
bool dcd_init(uint8_t rhport)
{
LPC_USBHS_T* const lpc_usb = LPC_USB[rhport];
dcd_data_t* p_dcd = dcd_data_ptr[rhport];
tu_memclr(p_dcd, sizeof(dcd_data_t));
lpc_usb->ENDPOINTLISTADDR = (uint32_t) p_dcd->qhd; // Endpoint List Address has to be 2K alignment
lpc_usb->USBSTS_D = lpc_usb->USBSTS_D;
lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF;
lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0
lpc_usb->USBCMD_D |= TU_BIT(0); // connect
return true;
}
void dcd_int_enable(uint8_t rhport)
{
NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
LPC_USB[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
(void) rhport;
(void) config_num;
// nothing to do
}
uint32_t dcd_get_frame_number(uint8_t rhport)
{
return LPC_USB[rhport]->FRINDEX_D >> 3;
}
//--------------------------------------------------------------------+
// HELPER
//--------------------------------------------------------------------+
// index to bit position in register
static inline uint8_t ep_idx2bit(uint8_t ep_idx)
{
return ep_idx/2 + ( (ep_idx%2) ? 16 : 0);
}
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
p_qtd->next = QTD_NEXT_INVALID;
p_qtd->active = 1;
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
if (data_ptr != NULL)
{
p_qtd->buffer[0] = (uint32_t) data_ptr;
for(uint8_t i=1; i<5; i++)
{
p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
}
}
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if ( epnum == 0)
{
// Stall both Control IN and OUT
LPC_USB[rhport]->ENDPTCTRL[epnum] |= ( (ENDPTCTRL_MASK_STALL << 16) || (ENDPTCTRL_MASK_STALL << 0) );
}else
{
LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_STALL << (dir ? 16 : 0);
}
}
bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
return LPC_USB[rhport]->ENDPTCTRL[epnum] & (ENDPTCTRL_MASK_STALL << (dir ? 16 : 0));
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
// data toggle also need to be reset
LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_TOGGLE_RESET << ( dir ? 16 : 0 );
LPC_USB[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_MASK_STALL << ( dir ? 16 : 0));
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
// TODO not support ISO yet
TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
uint8_t const ep_idx = 2*epnum + dir;
// USB0 has 5, USB1 has 3 non-control endpoints
TU_ASSERT( epnum <= (rhport ? 3 : 5) );
//------------- Prepare Queue Head -------------//
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
tu_memclr(p_qhd, sizeof(dcd_qhd_t));
p_qhd->zero_length_termination = 1;
p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
// Enable EP Control
LPC_USB[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
return true;
}
bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
return p_qtd->active;
// return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const ep_idx = 2*epnum + dir;
if ( epnum == 0 )
{
// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
// wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
while(LPC_USB[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {}
}
dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
p_qtd->int_on_complete = true;
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
// start transfer
LPC_USB[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ;
return true;
}
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
void hal_dcd_isr(uint8_t rhport)
{
LPC_USBHS_T* const lpc_usb = LPC_USB[rhport];
uint32_t const int_enable = lpc_usb->USBINTR_D;
uint32_t const int_status = lpc_usb->USBSTS_D & int_enable;
lpc_usb->USBSTS_D = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;// disabled interrupt sources
if (int_status & INT_MASK_RESET)
{
bus_reset(rhport);
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
}
if (int_status & INT_MASK_SUSPEND)
{
if (lpc_usb->PORTSC1_D & PORTSC_SUSPEND_MASK)
{
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if ((lpc_usb->DEVICEADDR >> 25) & 0x0f)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
}
}
}
// TODO disconnection does not generate interrupt !!!!!!
// if (int_status & INT_MASK_PORT_CHANGE)
// {
// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) )
// {
// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
// dcd_event_handler(&event, true);
// }
// }
if (int_status & INT_MASK_USB)
{
uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE;
lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge
dcd_data_t* const p_dcd = dcd_data_ptr[rhport];
if (lpc_usb->ENDPTSETUPSTAT)
{
//------------- Set up Received -------------//
// 23.10.10.2 Operational model for setup transfers
lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge
dcd_event_setup_received(rhport, (uint8_t*) &p_dcd->qhd[0].setup_request, true);
}
if ( edpt_complete )
{
for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++)
{
if ( TU_BIT_TEST(edpt_complete, ep_idx2bit(ep_idx)) )
{
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 );
dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
}
}
}
}
if (int_status & INT_MASK_SOF)
{
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
if (int_status & INT_MASK_NAK) {}
if (int_status & INT_MASK_ERROR) TU_ASSERT(false, );
}
#endif
@@ -1,160 +1,156 @@
/**************************************************************************/
/*!
@file dcd_lpc43xx.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_dcd
* \defgroup group_dcd_lpc143xx LPC43xx
* @{ */
#ifndef _TUSB_DCD_LPC43XX_H_
#define _TUSB_DCD_LPC43XX_H_
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define DCD_QHD_MAX 12
#define DCD_QTD_MAX 12
#define QTD_NEXT_INVALID 0x01
/*---------- ENDPTCTRL ----------*/
enum {
ENDPTCTRL_MASK_STALL = BIT_(0),
ENDPTCTRL_MASK_TOGGLE_INHIBIT = BIT_(5), ///< used for test only
ENDPTCTRL_MASK_TOGGLE_RESET = BIT_(6),
ENDPTCTRL_MASK_ENABLE = BIT_(7)
};
/*---------- USBCMD ----------*/
enum {
USBCMD_MASK_RUN_STOP = BIT_(0),
USBCMD_MASK_RESET = BIT_(1),
USBCMD_MASK_SETUP_TRIPWIRE = BIT_(13),
USBCMD_MASK_ADD_QTD_TRIPWIRE = BIT_(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) 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 reserved[2];
} dcd_qtd_t;
TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
typedef struct
{
// Word 0: Capabilities and Characteristics
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t : 2 ;
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
uint32_t iso_mult : 2 ; ///<
uint32_t : 0 ;
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
// Word 2-9: Transfer Overlay
volatile dcd_qtd_t qtd_overlay;
// Word 10-11: Setup request (control OUT only)
volatile tusb_control_request_t setup_request;
//--------------------------------------------------------------------+
/// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
/// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
uint8_t reserved[16];
} dcd_qhd_t;
TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC43XX_H_ */
/** @} */
/**************************************************************************/
/*!
@file dcd_lpc43xx.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_dcd
* \defgroup group_dcd_lpc143xx LPC43xx
* @{ */
#ifndef _TUSB_DCD_LPC43XX_H_
#define _TUSB_DCD_LPC43XX_H_
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
/*---------- ENDPTCTRL ----------*/
enum {
ENDPTCTRL_MASK_STALL = TU_BIT(0),
ENDPTCTRL_MASK_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only
ENDPTCTRL_MASK_TOGGLE_RESET = TU_BIT(6),
ENDPTCTRL_MASK_ENABLE = TU_BIT(7)
};
/*---------- USBCMD ----------*/
enum {
USBCMD_MASK_RUN_STOP = TU_BIT(0),
USBCMD_MASK_RESET = TU_BIT(1),
USBCMD_MASK_SETUP_TRIPWIRE = TU_BIT(13),
USBCMD_MASK_ADD_QTD_TRIPWIRE = TU_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 = TU_BIT(0),
INT_MASK_ERROR = TU_BIT(1),
INT_MASK_PORT_CHANGE = TU_BIT(2),
INT_MASK_RESET = TU_BIT(6),
INT_MASK_SOF = TU_BIT(7),
INT_MASK_SUSPEND = TU_BIT(8),
INT_MASK_NAK = TU_BIT(16)
};
//------------- PORTSC -------------//
enum {
PORTSC_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0),
PORTSC_FORCE_PORT_RESUME_MASK = TU_BIT(6),
PORTSC_SUSPEND_MASK = TU_BIT(7)
};
typedef struct
{
// Word 0: Next QTD Pointer
uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
// Word 1: qTQ Token
uint32_t : 3 ;
volatile uint32_t xact_err : 1 ;
uint32_t : 1 ;
volatile uint32_t buffer_err : 1 ;
volatile uint32_t halted : 1 ;
volatile uint32_t active : 1 ;
uint32_t : 2 ;
uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
uint32_t : 3 ;
uint32_t int_on_complete : 1 ;
volatile uint32_t total_bytes : 15 ;
uint32_t : 0 ;
// Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
//------------- DCD Area -------------//
uint16_t expected_bytes;
uint8_t reserved[2];
} dcd_qtd_t;
TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
typedef struct
{
// Word 0: Capabilities and Characteristics
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
uint32_t : 2 ;
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
uint32_t iso_mult : 2 ; ///<
uint32_t : 0 ;
// Word 1: Current qTD Pointer
volatile uint32_t qtd_addr;
// Word 2-9: Transfer Overlay
volatile dcd_qtd_t qtd_overlay;
// Word 10-11: Setup request (control OUT only)
volatile tusb_control_request_t setup_request;
//--------------------------------------------------------------------+
/// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
/// thus there are 16 bytes padding free that we can make use of.
//--------------------------------------------------------------------+
uint8_t reserved[16];
} dcd_qhd_t;
TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DCD_LPC43XX_H_ */
/** @} */
@@ -1,44 +1,74 @@
/**************************************************************************/
/*!
@file hcd.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 "hcd.h"
#if MODE_HOST_SUPPORTED
#endif
/**************************************************************************/
/*!
@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_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX)
#include "chip.h"
extern void hal_dcd_isr(uint8_t rhport);
extern void hal_hcd_isr(uint8_t hostid);
#if CFG_TUSB_RHPORT0_MODE
void USB0_IRQHandler(void)
{
#if TUSB_OPT_HOST_ENABLED
hal_hcd_isr(0);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(0);
#endif
}
#endif
#if CFG_TUSB_RHPORT1_MODE
void USB1_IRQHandler(void)
{
#if TUSB_OPT_HOST_ENABLED
hal_hcd_isr(1);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(1);
#endif
}
#endif
#endif
+62
View File
@@ -0,0 +1,62 @@
/**************************************************************************/
/*!
@file hcd_lpc18_43.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2018, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX)
#include "chip.h"
// LPC18xx and 43xx use EHCI driver
void hcd_int_enable(uint8_t rhport)
{
NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
void hcd_int_disable(uint8_t rhport)
{
NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
uint32_t hcd_ehci_register_addr(uint8_t rhport)
{
return (uint32_t) (rhport ? &LPC_USB1->USBCMD_H : &LPC_USB0->USBCMD_H );
}
#endif
@@ -1,165 +0,0 @@
/**************************************************************************/
/*!
@file hal_lpc43xx.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb.h"
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX
#include "LPC43xx.h"
#include "lpc43xx_cgu.h"
enum {
LPC43XX_USBMODE_DEVICE = 2,
LPC43XX_USBMODE_HOST = 3
};
enum {
LPC43XX_USBMODE_VBUS_LOW = 0,
LPC43XX_USBMODE_VBUS_HIGH = 1
};
void tusb_hal_int_enable(uint8_t rhport)
{
NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
void tusb_hal_int_disable(uint8_t rhport)
{
NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn);
}
static void hal_controller_reset(uint8_t rhport)
{ // TODO timeout expired to prevent trap
volatile uint32_t * p_reg_usbcmd;
p_reg_usbcmd = (rhport ? &LPC_USB1->USBCMD_D : &LPC_USB0->USBCMD_D);
// NXP chip powered with non-host mode --> sts bit is not correctly reflected
(*p_reg_usbcmd) |= BIT_(1);
// tu_timeout_t timeout;
// tu_timeout_set(&timeout, 2); // should not take longer the time to stop controller
while( ((*p_reg_usbcmd) & BIT_(1)) /*&& !tu_timeout_expired(&timeout)*/) {}
//
// return tu_timeout_expired(&timeout) ? TUSB_ERROR_OSAL_TIMEOUT : TUSB_ERROR_NONE;
}
bool tusb_hal_init(void)
{
LPC_CREG->CREG0 &= ~(1<<5); /* Turn on the phy */
//------------- USB0 -------------//
#if CFG_TUSB_RHPORT0_MODE
CGU_EnableEntity(CGU_CLKSRC_PLL0, DISABLE); /* Disable PLL first */
TU_VERIFY( CGU_ERROR_SUCCESS == CGU_SetPLL0()); /* the usb core require output clock = 480MHz */
CGU_EntityConnect(CGU_CLKSRC_XTAL_OSC, CGU_CLKSRC_PLL0);
CGU_EnableEntity(CGU_CLKSRC_PLL0, ENABLE); /* Enable PLL after all setting is done */
// reset controller & set role
hal_controller_reset(0);
#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
LPC_USB0->USBMODE_H = LPC43XX_USBMODE_HOST | (LPC43XX_USBMODE_VBUS_HIGH << 5);
#else // TODO OTG
LPC_USB0->USBMODE_D = LPC43XX_USBMODE_DEVICE;
LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/;
#if CFG_TUD_FULLSPEED // TODO for easy testing
LPC_USB0->PORTSC1_D |= (1<<24); // force full speed
#endif
#endif
#endif
//------------- USB1 -------------//
#if CFG_TUSB_RHPORT1_MODE
// Host require to config P2_5, TODO confirm whether device mode require P2_5 or not
scu_pinmux(0x2, 5, MD_PLN | MD_EZI | MD_ZI, FUNC2); // USB1_VBUS monitor presence, must be high for bus reset occur
/* connect CLK_USB1 to 60 MHz clock */
CGU_EntityConnect(CGU_CLKSRC_PLL1, CGU_BASE_USB1); /* FIXME Run base BASE_USB1_CLK clock from PLL1 (assume PLL1 is 60 MHz, no division required) */
LPC_SCU->SFSUSB = (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 0x16 : 0x12; // enable USB1 with on-chip FS PHY
hal_controller_reset(1);
#if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST
LPC_USB1->USBMODE_H = LPC43XX_USBMODE_HOST | (LPC43XX_USBMODE_VBUS_HIGH << 5);
#else // TODO OTG
LPC_USB1->USBMODE_D = LPC43XX_USBMODE_DEVICE;
#endif
LPC_USB1->PORTSC1_D |= (1<<24); // TODO abstract, force rhport to fullspeed
#endif
return true;
}
void hal_dcd_isr(uint8_t rhport);
#if CFG_TUSB_RHPORT0_MODE
void USB0_IRQHandler(void)
{
#if MODE_HOST_SUPPORTED
hal_hcd_isr(0);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(0);
#endif
}
#endif
#if CFG_TUSB_RHPORT1_MODE
void USB1_IRQHandler(void)
{
#if MODE_HOST_SUPPORTED
hal_hcd_isr(1);
#endif
#if TUSB_OPT_DEVICE_ENABLED
hal_dcd_isr(1);
#endif
}
#endif
void check_failed(uint8_t *file, uint32_t line)
{
(void) file;
(void) line;
}
#endif
+9 -23
View File
@@ -39,28 +39,28 @@
#include "tusb_option.h"
#if TUSB_OPT_HOST_ENABLED || TUSB_OPT_DEVICE_ENABLED
#define _TINY_USB_SOURCE_FILE_
#include "tusb.h"
#include "device/usbd_pvt.h"
static bool _initialized = false;
// TODO clean up
#if TUSB_OPT_DEVICE_ENABLED
#include "device/usbd_pvt.h"
#endif
tusb_error_t tusb_init(void)
bool tusb_init(void)
{
// skip if already initialized
if (_initialized) return TUSB_ERROR_NONE;
if (_initialized) return true;
TU_VERIFY( tusb_hal_init(), TUSB_ERROR_FAILED ) ; // hardware init
#if MODE_HOST_SUPPORTED
TU_ASSERT_ERR( usbh_init() ); // host stack init
#if TUSB_OPT_HOST_ENABLED
TU_VERIFY( usbh_init() ); // init host stack
#endif
#if TUSB_OPT_DEVICE_ENABLED
TU_ASSERT_ERR ( usbd_init() ); // device stack init
TU_VERIFY ( usbd_init() ); // init device stack
#endif
_initialized = true;
@@ -68,20 +68,6 @@ tusb_error_t tusb_init(void)
return TUSB_ERROR_NONE;
}
#if CFG_TUSB_OS == OPT_OS_NONE
void tusb_task(void)
{
#if MODE_HOST_SUPPORTED
usbh_enumeration_task(NULL);
#endif
#if TUSB_OPT_DEVICE_ENABLED
usbd_task(NULL);
#endif
}
#endif
/*------------------------------------------------------------------*/
/* Debug
*------------------------------------------------------------------*/
+22 -31
View File
@@ -52,18 +52,18 @@
#include "common/tusb_fifo.h"
//------------- HOST -------------//
#if MODE_HOST_SUPPORTED
#if TUSB_OPT_HOST_ENABLED
#include "host/usbh.h"
#if HOST_CLASS_HID
#include "class/hid/hid_host.h"
#endif
#if CFG_TUSB_HOST_MSC
#if CFG_TUH_MSC
#include "class/msc/msc_host.h"
#endif
#if CFG_TUSB_HOST_CDC
#if CFG_TUH_CDC
#include "class/cdc/cdc_host.h"
#endif
@@ -89,6 +89,10 @@
#include "class/msc/msc_device.h"
#endif
#if CFG_TUD_MIDI
#include "class/midi/midi_device.h"
#endif
#if CFG_TUD_CUSTOM_CLASS
#include "class/custom/custom_device.h"
#endif
@@ -101,37 +105,25 @@
/** \ingroup group_application_api
* @{ */
/** \brief Initialize the usb stack
* \return Error Code of the \ref TUSB_ERROR enum
* \note Function will initialize the stack according to configuration in the configure file (tusb_config.h)
*/
tusb_error_t tusb_init(void);
// Initialize device/host stack
bool tusb_init(void);
#if CFG_TUSB_OS == OPT_OS_NONE
/** \brief Run all tinyusb's internal tasks (e.g host task, device task).
* \note This function is only required when using no RTOS (\ref CFG_TUSB_OS == OPT_OS_NONE). All the stack functions
* & callback are invoked within this function, so it should be called periodically within the mainloop
*
@code
int main(void)
{
your_init_code();
tusb_init();
// TODO
// bool tusb_teardown(void);
// other config code
while(1) // the mainloop
{
your_application_code();
// backward compatible only. TODO remove later
ATTR_DEPRECATED("Please use either tud_task() or tuh_task()")
static inline void tusb_task(void)
{
#if TUSB_OPT_HOST_ENABLED
tuh_task();
#endif
tusb_task(); // handle tinyusb event, task etc ...
}
}
@endcode
*
*/
void tusb_task(void);
#endif
#if TUSB_OPT_DEVICE_ENABLED
tud_task();
#endif
}
/** @} */
@@ -140,4 +132,3 @@ void tusb_task(void);
#endif
#endif /* _TUSB_H_ */
+1 -51
View File
@@ -51,64 +51,14 @@ extern "C" {
//--------------------------------------------------------------------+
// HAL API
//--------------------------------------------------------------------+
/** \ingroup group_mcu
* \defgroup group_hal Hardware Abtract Layer (HAL)
* Hardware Abstraction Layer (HAL) is an abstraction layer, between the physical hardware and the tinyusb stack.
* Its function is to hide differences in hardware from most of MCUs, so that most of the stack code does not need to be changed to
* run on systems with a different MCU.
* HAL are sets of routines that emulate some platform-specific details, giving programs direct access to the hardware resources.
* @{ */
/** \brief Initialize USB controller hardware
* \returns true if succeeded
* \note This function is invoked by \ref tusb_init as part of the initialization.
*/
bool tusb_hal_init(void);
/** \brief Enable USB Interrupt on a specific USB Controller
* \param[in] rhport is a zero-based index to identify USB controller's ID
*/
void tusb_hal_int_enable(uint8_t rhport);
/** \brief Disable USB Interrupt on a specific USB Controller
* \param[in] rhport is a zero-based index to identify USB controller's ID
*/
void tusb_hal_int_disable(uint8_t rhport);
// Only required to implement if using No RTOS (osal_none)
// TODO could be remove
uint32_t tusb_hal_millis(void);
// Enable all ports' interrupt
static inline void tusb_hal_int_enable_all(void)
{
#ifdef CFG_TUSB_RHPORT0_MODE
tusb_hal_int_enable(0);
#endif
#ifdef CFG_TUSB_RHPORT0_MODE
tusb_hal_int_enable(1);
#endif
}
// Disable all ports' interrupt
static inline void tusb_hal_int_disable_all(void)
{
#ifdef CFG_TUSB_RHPORT0_MODE
tusb_hal_int_disable(0);
#endif
#ifdef CFG_TUSB_RHPORT0_MODE
tusb_hal_int_disable(1);
#endif
}
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HAL_H_ */
/** @} */
+36 -31
View File
@@ -48,13 +48,14 @@
/** \defgroup group_mcu Supported MCU
* \ref CFG_TUSB_MCU must be defined to one of these
* @{ */
#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx series
#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx (not supported yet)
#define OPT_MCU_LPC13UXX 3 ///< NXP LPC13xx 12 bit ADC series
#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x series
#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x series (not supported yet)
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx series (not supported yet)
#define OPT_MCU_LPC43XX 7 ///< NXP LPC43xx series
#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
#define OPT_MCU_LPC13XX 3 ///< NXP LPC13xx
#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
@@ -86,13 +87,16 @@
//--------------------------------------------------------------------
// CONTROLLER
// Only 1 roothub port can be configured to be device and/or host.
// tinyusb does not support dual devices or dual host configuration
//--------------------------------------------------------------------
/** \defgroup group_mode Controller Mode Selection
* \brief CFG_TUSB_CONTROLLER_N_MODE must be defined with these
* @{ */
#define OPT_MODE_HOST 0x02 ///< Host Mode
#define OPT_MODE_DEVICE 0x01 ///< Device Mode
#define OPT_MODE_NONE 0x00 ///< Disabled
#define OPT_MODE_NONE 0x00 ///< Disabled
#define OPT_MODE_DEVICE 0x01 ///< Device Mode
#define OPT_MODE_HOST 0x02 ///< Host Mode
#define OPT_MODE_HIGH_SPEED 0x10 ///< High speed
/** @} */
#ifndef CFG_TUSB_RHPORT0_MODE
@@ -103,21 +107,26 @@
#define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE
#endif
#define CONTROLLER_HOST_NUMBER (\
((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) ? 1 : 0) + \
((CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 1 : 0))
#define MODE_HOST_SUPPORTED (CONTROLLER_HOST_NUMBER > 0)
#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST)) || \
((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE))
#error "tinyusb does not support same modes on more than 1 roothub port"
#endif
// Which roothub port is configured as host
#define TUH_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 1 : -1) )
#define TUSB_OPT_HOST_ENABLED ( TUH_OPT_RHPORT >= 0 )
// Which roothub port is configured as device
#define TUD_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) ? 1 : -1) )
#if TUD_OPT_RHPORT == 0
#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED )
#else
#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED )
#endif
#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 )
#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST)) || ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE))
#error "tinyusb does not support same modes on more than 1 roothub port"
#endif
//--------------------------------------------------------------------+
// COMMON OPTIONS
@@ -139,6 +148,10 @@
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
@@ -189,41 +202,33 @@
//--------------------------------------------------------------------
// HOST OPTIONS
//--------------------------------------------------------------------
#if MODE_HOST_SUPPORTED
#if TUSB_OPT_HOST_ENABLED
#ifndef CFG_TUSB_HOST_DEVICE_MAX
#define CFG_TUSB_HOST_DEVICE_MAX 1
#warning CFG_TUSB_HOST_DEVICE_MAX is not defined, default value is 1
#endif
//------------- HUB CLASS -------------//
#if CFG_TUSB_HOST_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1)
#if CFG_TUH_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1)
#error there is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX
#endif
//------------- HID CLASS -------------//
#define HOST_CLASS_HID ( CFG_TUSB_HOST_HID_KEYBOARD + CFG_TUSB_HOST_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC )
// #if HOST_CLASS_HID
// #define HOST_HCD_XFER_INTERRUPT
// #endif
#define HOST_CLASS_HID ( CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC )
#ifndef CFG_TUSB_HOST_ENUM_BUFFER_SIZE
#define CFG_TUSB_HOST_ENUM_BUFFER_SIZE 256
#endif
//------------- CLASS -------------//
#endif // MODE_HOST_SUPPORTED
#endif // TUSB_OPT_HOST_ENABLED
//------------------------------------------------------------------
// Configuration Validation
//------------------------------------------------------------------
#if (CFG_TUSB_OS != OPT_OS_NONE) && !defined (CFG_TUD_TASK_PRIO)
#error CFG_TUD_TASK_PRIO need to be defined (hint: use the highest if possible)
#endif
#if CFG_TUD_ENDOINT0_SIZE > 64
#error Control Endpoint Max Package Size cannot larger than 64
#error Control Endpoint Max Packet Size cannot be larger than 64
#endif
#endif /* _TUSB_OPTION_H_ */