Merge remote-tracking branch 'mainline/master' into HEAD
Update fsp to 4.0.0
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
# TODO make tinyusb as library that depends on 'tusb_config' interface that exposes the tusb_config.h file
|
||||
# This file is WIP and should not used yet
|
||||
# TODO more docs and example on how to use this file
|
||||
# Usage: requires target tinyusb_config which expose tusb_config.h file
|
||||
# TINYUSB_TARGET_PREFIX and TINYUSB_TARGET_SUFFIX can be used to change the name of the target
|
||||
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
|
||||
# Add tinyusb to a target, if user don't want to compile tinyusb as a library
|
||||
function(add_tinyusb TARGET)
|
||||
target_sources(${TARGET} PUBLIC
|
||||
target_sources(${TARGET} PRIVATE
|
||||
# common
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tusb.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c
|
||||
# device
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c
|
||||
@@ -21,15 +25,80 @@ function(add_tinyusb TARGET)
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c
|
||||
# host
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/usbh.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/hub.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c
|
||||
)
|
||||
|
||||
target_include_directories(${TARGET} PUBLIC
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}
|
||||
# TODO for net driver, should be removed/changed
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
|
||||
)
|
||||
|
||||
# enable all possible warnings
|
||||
target_compile_options(${TARGET} PUBLIC
|
||||
|
||||
)
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Werror
|
||||
-Wfatal-errors
|
||||
-Wdouble-promotion
|
||||
-Wstrict-prototypes
|
||||
-Wstrict-overflow
|
||||
-Werror-implicit-function-declaration
|
||||
-Wfloat-equal
|
||||
-Wundef
|
||||
-Wshadow
|
||||
-Wwrite-strings
|
||||
-Wsign-compare
|
||||
-Wmissing-format-attribute
|
||||
-Wunreachable-code
|
||||
-Wcast-align
|
||||
-Wcast-function-type
|
||||
-Wcast-qual
|
||||
-Wnull-dereference
|
||||
-Wuninitialized
|
||||
-Wunused
|
||||
-Wreturn-type
|
||||
-Wredundant-decls
|
||||
)
|
||||
elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
|
||||
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
#------------------------------------
|
||||
# TinyUSB as library target
|
||||
#------------------------------------
|
||||
set(TINYUSB_TARGET "tinyusb")
|
||||
set(TINYUSB_CONFIG_TARGET "tinyusb_config")
|
||||
|
||||
if (DEFINED TINYUSB_TARGET_PREFIX)
|
||||
set(TINYUSB_TARGET "${TINYUSB_TARGET_PREFIX}${TINYUSB_TARGET}")
|
||||
set(TINYUSB_CONFIG_TARGET "${TINYUSB_TARGET_PREFIX}${TINYUSB_CONFIG_TARGET}")
|
||||
endif ()
|
||||
|
||||
if (DEFINED TINYUSB_TARGET_SUFFIX)
|
||||
set(TINYUSB_TARGET "${TINYUSB_TARGET}${TINYUSB_TARGET_SUFFIX}")
|
||||
set(TINYUSB_CONFIG_TARGET "${TINYUSB_CONFIG_TARGET}${TINYUSB_TARGET_SUFFIX}")
|
||||
endif ()
|
||||
|
||||
add_library(${TINYUSB_TARGET} STATIC)
|
||||
add_tinyusb(${TINYUSB_TARGET})
|
||||
|
||||
# Check if tinyusb_config target is defined
|
||||
if (NOT TARGET ${TINYUSB_CONFIG_TARGET})
|
||||
message(FATAL_ERROR "${TINYUSB_CONFIG_TARGET} target is not defined")
|
||||
endif()
|
||||
|
||||
# Link with tinyusb_config target
|
||||
target_link_libraries(${TINYUSB_TARGET} PUBLIC
|
||||
${TINYUSB_CONFIG_TARGET}
|
||||
)
|
||||
|
||||
# export target name
|
||||
# set(TINYUSB_TARGET ${TINYUSB_TARGET} PARENT_SCOPE)
|
||||
# set(TINYUSB_CONFIG_TARGET ${TINYUSB_CONFIG_TARGET} PARENT_SCOPE)
|
||||
|
||||
@@ -35,26 +35,19 @@
|
||||
|
||||
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||
#define CDCH_DEBUG 2
|
||||
|
||||
#define TU_LOG_CDCH(...) TU_LOG(CDCH_DEBUG, __VA_ARGS__)
|
||||
#define TU_LOG_DRV(...) TU_LOG(CDCH_DEBUG, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
// Host CDC Interface
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
SERIAL_PROTOCOL_ACM = 0,
|
||||
SERIAL_PROTOCOL_FTDI,
|
||||
SERIAL_PROTOCOL_CP210X,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
|
||||
uint8_t serial_protocol;
|
||||
uint8_t serial_drid; // Serial Driver ID
|
||||
cdc_acm_capability_t acm_capability;
|
||||
uint8_t ep_notif;
|
||||
|
||||
@@ -76,13 +69,103 @@ typedef struct {
|
||||
|
||||
} cdch_interface_t;
|
||||
|
||||
CFG_TUH_MEM_SECTION
|
||||
static cdch_interface_t cdch_data[CFG_TUH_CDC];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Serial Driver
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- ACM prototypes -------------//
|
||||
static void acm_process_config(tuh_xfer_t* xfer);
|
||||
|
||||
static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
//------------- FTDI prototypes -------------//
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
#include "serial/ftdi_sio.h"
|
||||
|
||||
static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST };
|
||||
enum {
|
||||
FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
|
||||
};
|
||||
|
||||
// Store last request baudrate since divisor to baudrate is not easy
|
||||
static uint32_t _ftdi_requested_baud;
|
||||
|
||||
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
|
||||
static void ftdi_process_config(tuh_xfer_t* xfer);
|
||||
|
||||
static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
#endif
|
||||
|
||||
//------------- CP210X prototypes -------------//
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
#include "serial/cp210x.h"
|
||||
|
||||
static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST };
|
||||
enum {
|
||||
CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
|
||||
};
|
||||
|
||||
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
static void cp210x_process_config(tuh_xfer_t* xfer);
|
||||
|
||||
static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
#endif
|
||||
|
||||
enum {
|
||||
SERIAL_DRIVER_ACM = 0,
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
SERIAL_DRIVER_FTDI,
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
SERIAL_DRIVER_CP210X,
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void (*const process_set_config)(tuh_xfer_t* xfer);
|
||||
bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
} cdch_serial_driver_t;
|
||||
|
||||
// Note driver list must be in the same order as SERIAL_DRIVER enum
|
||||
static const cdch_serial_driver_t serial_drivers[] = {
|
||||
{ .process_set_config = acm_process_config,
|
||||
.set_control_line_state = acm_set_control_line_state,
|
||||
.set_baudrate = acm_set_baudrate
|
||||
},
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
{ .process_set_config = ftdi_process_config,
|
||||
.set_control_line_state = ftdi_sio_set_modem_ctrl,
|
||||
.set_baudrate = ftdi_sio_set_baudrate
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
{ .process_set_config = cp210x_process_config,
|
||||
.set_control_line_state = cp210x_set_modem_ctrl,
|
||||
.set_baudrate = cp210x_set_baudrate
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0])
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
CFG_TUH_MEM_SECTION
|
||||
static cdch_interface_t cdch_data[CFG_TUH_CDC];
|
||||
|
||||
static inline cdch_interface_t* get_itf(uint8_t idx)
|
||||
{
|
||||
TU_ASSERT(idx < CFG_TUH_CDC, NULL);
|
||||
@@ -130,39 +213,6 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t c
|
||||
static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num);
|
||||
static void cdch_internal_control_complete(tuh_xfer_t* xfer);
|
||||
|
||||
//------------- FTDI prototypes -------------//
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
#include "serial/ftdi_sio.h"
|
||||
|
||||
static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST };
|
||||
enum {
|
||||
FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
|
||||
};
|
||||
|
||||
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
|
||||
static void process_ftdi_config(tuh_xfer_t* xfer);
|
||||
|
||||
static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
#endif
|
||||
|
||||
//------------- CP210X prototypes -------------//
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
#include "serial/cp210x.h"
|
||||
|
||||
static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST };
|
||||
enum {
|
||||
CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
|
||||
};
|
||||
|
||||
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
static void process_cp210x_config(tuh_xfer_t* xfer);
|
||||
|
||||
static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -322,174 +372,138 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
|
||||
|
||||
if (xfer->result == XFER_RESULT_SUCCESS)
|
||||
{
|
||||
if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) {
|
||||
switch (xfer->setup->bRequest) {
|
||||
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
|
||||
p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
break;
|
||||
switch (p_cdc->serial_drid) {
|
||||
case SERIAL_DRIVER_ACM:
|
||||
switch (xfer->setup->bRequest) {
|
||||
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
|
||||
p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
break;
|
||||
|
||||
case CDC_REQUEST_SET_LINE_CODING: {
|
||||
uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
|
||||
memcpy(&p_cdc->line_coding, xfer->buffer, len);
|
||||
case CDC_REQUEST_SET_LINE_CODING: {
|
||||
uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
|
||||
memcpy(&p_cdc->line_coding, xfer->buffer, len);
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) {
|
||||
switch (xfer->setup->bRequest) {
|
||||
case FTDI_SIO_MODEM_CTRL:
|
||||
p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
|
||||
break;
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
case SERIAL_DRIVER_FTDI:
|
||||
switch (xfer->setup->bRequest) {
|
||||
case FTDI_SIO_MODEM_CTRL:
|
||||
p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
case FTDI_SIO_SET_BAUD_RATE:
|
||||
// convert from divisor to baudrate is not supported
|
||||
p_cdc->line_coding.bit_rate = _ftdi_requested_baud;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
case SERIAL_DRIVER_CP210X:
|
||||
switch(xfer->setup->bRequest) {
|
||||
case CP210X_SET_MHS:
|
||||
p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
|
||||
break;
|
||||
|
||||
case CP210X_SET_BAUDRATE: {
|
||||
uint32_t baudrate;
|
||||
memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
|
||||
p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
xfer->complete_cb = p_cdc->user_control_cb;
|
||||
xfer->complete_cb(xfer);
|
||||
}
|
||||
|
||||
static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_CDCH("CDC ACM Set Control Line State\r\n");
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
|
||||
.wValue = tu_htole16(line_state),
|
||||
.wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = p_cdc->daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = cdch_internal_control_complete,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
cdch_interface_t* p_cdc = get_itf(idx);
|
||||
TU_VERIFY(p_cdc);
|
||||
|
||||
switch(p_cdc->serial_protocol) {
|
||||
case SERIAL_PROTOCOL_ACM:
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
return acm_set_control_line_state(p_cdc, line_state, complete_cb, user_data);
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
case SERIAL_PROTOCOL_FTDI:
|
||||
return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data);
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
case SERIAL_PROTOCOL_CP210X:
|
||||
return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data);
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
if (xfer->complete_cb) {
|
||||
xfer->complete_cb(xfer);
|
||||
}
|
||||
}
|
||||
|
||||
bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_CDCH("CDC ACM Set Line Conding\r\n");
|
||||
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = CDC_REQUEST_SET_LINE_CODING,
|
||||
.wValue = 0,
|
||||
.wIndex = tu_htole16(p_cdc->bInterfaceNumber),
|
||||
.wLength = tu_htole16(sizeof(cdc_line_coding_t))
|
||||
};
|
||||
|
||||
// use usbh enum buf to hold line coding since user line_coding variable does not live long enough
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t));
|
||||
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = p_cdc->daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = enum_buf,
|
||||
.complete_cb = cdch_internal_control_complete,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
cdch_interface_t* p_cdc = get_itf(idx);
|
||||
TU_VERIFY(p_cdc);
|
||||
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
|
||||
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
|
||||
|
||||
switch(p_cdc->serial_protocol) {
|
||||
case SERIAL_PROTOCOL_ACM:
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data);
|
||||
if ( complete_cb ) {
|
||||
return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
|
||||
}else {
|
||||
// blocking
|
||||
xfer_result_t result = XFER_RESULT_INVALID;
|
||||
bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
case SERIAL_PROTOCOL_FTDI:
|
||||
// FTDI need to set baud rate and data bits, parity, stop bits separately
|
||||
return ftdi_sio_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data);
|
||||
#endif
|
||||
if (user_data) {
|
||||
// user_data is not NULL, return result via user_data
|
||||
*((xfer_result_t*) user_data) = result;
|
||||
}
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
case SERIAL_PROTOCOL_CP210X:
|
||||
return cp210x_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data);
|
||||
#endif
|
||||
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
|
||||
|
||||
default: return false;
|
||||
p_cdc->line_state = (uint8_t) line_state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
cdch_interface_t* p_cdc = get_itf(idx);
|
||||
TU_VERIFY(p_cdc);
|
||||
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
|
||||
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
|
||||
|
||||
switch(p_cdc->serial_protocol) {
|
||||
case SERIAL_PROTOCOL_ACM: {
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
cdc_line_coding_t line_coding = p_cdc->line_coding;
|
||||
line_coding.bit_rate = baudrate;
|
||||
return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
|
||||
if ( complete_cb ) {
|
||||
return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
|
||||
}else {
|
||||
// blocking
|
||||
xfer_result_t result = XFER_RESULT_INVALID;
|
||||
bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
|
||||
|
||||
if (user_data) {
|
||||
// user_data is not NULL, return result via user_data
|
||||
*((xfer_result_t*) user_data) = result;
|
||||
}
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
case SERIAL_PROTOCOL_FTDI:
|
||||
// FTDI need to set baud rate and data bits, parity, stop bits separately
|
||||
return ftdi_sio_set_baudrate(p_cdc, baudrate, complete_cb, user_data);
|
||||
#endif
|
||||
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
case SERIAL_PROTOCOL_CP210X:
|
||||
return cp210x_set_baudrate(p_cdc, baudrate, complete_cb, user_data);
|
||||
#endif
|
||||
p_cdc->line_coding.bit_rate = baudrate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
default: return false;
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
cdch_interface_t* p_cdc = get_itf(idx);
|
||||
// only ACM support this set line coding request
|
||||
TU_VERIFY(p_cdc && p_cdc->serial_drid == SERIAL_DRIVER_ACM);
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
|
||||
if ( complete_cb ) {
|
||||
return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data);
|
||||
}else {
|
||||
// blocking
|
||||
xfer_result_t result = XFER_RESULT_INVALID;
|
||||
bool ret = acm_set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
|
||||
|
||||
if (user_data) {
|
||||
// user_data is not NULL, return result via user_data
|
||||
*((xfer_result_t*) user_data) = result;
|
||||
}
|
||||
|
||||
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
|
||||
|
||||
p_cdc->line_coding = *line_coding;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,6 +536,8 @@ void cdch_close(uint8_t daddr)
|
||||
cdch_interface_t* p_cdc = &cdch_data[idx];
|
||||
if (p_cdc->daddr == daddr)
|
||||
{
|
||||
TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
|
||||
|
||||
// Invoke application callback
|
||||
if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx);
|
||||
|
||||
@@ -534,8 +550,7 @@ void cdch_close(uint8_t daddr)
|
||||
}
|
||||
}
|
||||
|
||||
bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
|
||||
{
|
||||
bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
// TODO handle stall response, retry failed transfer ...
|
||||
TU_ASSERT(event == XFER_RESULT_SUCCESS);
|
||||
|
||||
@@ -543,41 +558,40 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
cdch_interface_t * p_cdc = get_itf(idx);
|
||||
TU_ASSERT(p_cdc);
|
||||
|
||||
if ( ep_addr == p_cdc->stream.tx.ep_addr )
|
||||
{
|
||||
if ( ep_addr == p_cdc->stream.tx.ep_addr ) {
|
||||
// invoke tx complete callback to possibly refill tx fifo
|
||||
if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx);
|
||||
|
||||
if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) )
|
||||
{
|
||||
if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) {
|
||||
// If there is no data left, a ZLP should be sent if:
|
||||
// - xferred_bytes is multiple of EP Packet size and not zero
|
||||
tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
|
||||
}
|
||||
}
|
||||
else if ( ep_addr == p_cdc->stream.rx.ep_addr )
|
||||
{
|
||||
tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
|
||||
|
||||
else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
// FTDI reserve 2 bytes for status
|
||||
if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) {
|
||||
uint8_t status[2];
|
||||
tu_edpt_stream_read(&p_cdc->stream.rx, status, 2);
|
||||
(void) status; // TODO handle status
|
||||
}
|
||||
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
|
||||
// FTDI reserve 2 bytes for status
|
||||
// FTDI status
|
||||
// uint8_t status[2] = {
|
||||
// p_cdc->stream.rx.ep_buf[0],
|
||||
// p_cdc->stream.rx.ep_buf[1]
|
||||
// };
|
||||
tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
|
||||
}
|
||||
|
||||
// invoke receive callback
|
||||
if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx);
|
||||
|
||||
// prepare for next transfer if needed
|
||||
tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
|
||||
}else if ( ep_addr == p_cdc->ep_notif )
|
||||
{
|
||||
}else if ( ep_addr == p_cdc->ep_notif ) {
|
||||
// TODO handle notification endpoint
|
||||
}else
|
||||
{
|
||||
}else {
|
||||
TU_ASSERT(false);
|
||||
}
|
||||
|
||||
@@ -587,15 +601,10 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
// ACM
|
||||
CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
|
||||
CONFIG_ACM_SET_LINE_CODING,
|
||||
CONFIG_ACM_COMPLETE,
|
||||
};
|
||||
|
||||
static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep)
|
||||
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
|
||||
static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
|
||||
{
|
||||
for(size_t i=0; i<2; i++)
|
||||
{
|
||||
@@ -618,57 +627,6 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t c
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
|
||||
|
||||
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
|
||||
TU_VERIFY(p_cdc);
|
||||
|
||||
p_cdc->serial_protocol = SERIAL_PROTOCOL_ACM;
|
||||
|
||||
//------------- Control Interface -------------//
|
||||
uint8_t const * p_desc = tu_desc_next(itf_desc);
|
||||
|
||||
// Communication Functional Descriptors
|
||||
while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
|
||||
{
|
||||
if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
|
||||
{
|
||||
// save ACM bmCapabilities
|
||||
p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// Open notification endpoint of control interface if any
|
||||
if (itf_desc->bNumEndpoints == 1)
|
||||
{
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
|
||||
TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
|
||||
p_cdc->ep_notif = desc_ep->bEndpointAddress;
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
//------------- Data Interface (if any) -------------//
|
||||
if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
|
||||
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
|
||||
{
|
||||
// next to endpoint descriptor
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
// data endpoints expected to be in pairs
|
||||
TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
@@ -721,7 +679,88 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i
|
||||
usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
|
||||
}
|
||||
|
||||
static void process_acm_config(tuh_xfer_t* xfer)
|
||||
|
||||
bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
|
||||
// fake transfer to kick-off process
|
||||
tuh_xfer_t xfer;
|
||||
xfer.daddr = daddr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.user_data = 0; // initial state
|
||||
|
||||
uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
|
||||
cdch_interface_t * p_cdc = get_itf(idx);
|
||||
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
|
||||
|
||||
serial_drivers[p_cdc->serial_drid].process_set_config(&xfer);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ACM
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
|
||||
CONFIG_ACM_SET_LINE_CODING,
|
||||
CONFIG_ACM_COMPLETE,
|
||||
};
|
||||
|
||||
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
|
||||
|
||||
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
|
||||
TU_VERIFY(p_cdc);
|
||||
|
||||
p_cdc->serial_drid = SERIAL_DRIVER_ACM;
|
||||
|
||||
//------------- Control Interface -------------//
|
||||
uint8_t const * p_desc = tu_desc_next(itf_desc);
|
||||
|
||||
// Communication Functional Descriptors
|
||||
while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
|
||||
{
|
||||
if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
|
||||
{
|
||||
// save ACM bmCapabilities
|
||||
p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// Open notification endpoint of control interface if any
|
||||
if (itf_desc->bNumEndpoints == 1)
|
||||
{
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
|
||||
TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
|
||||
p_cdc->ep_notif = desc_ep->bEndpointAddress;
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
//------------- Data Interface (if any) -------------//
|
||||
if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
|
||||
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
|
||||
{
|
||||
// next to endpoint descriptor
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
// data endpoints expected to be in pairs
|
||||
TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void acm_process_config(tuh_xfer_t* xfer)
|
||||
{
|
||||
uintptr_t const state = xfer->user_data;
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
@@ -735,72 +774,104 @@ static void process_acm_config(tuh_xfer_t* xfer)
|
||||
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
|
||||
if (p_cdc->acm_capability.support_line_request)
|
||||
{
|
||||
TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_acm_config,
|
||||
CONFIG_ACM_SET_LINE_CODING), );
|
||||
TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config,
|
||||
CONFIG_ACM_SET_LINE_CODING), );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
|
||||
case CONFIG_ACM_SET_LINE_CODING:
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
if (p_cdc->acm_capability.support_line_request)
|
||||
{
|
||||
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
|
||||
TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, process_acm_config, CONFIG_ACM_COMPLETE), );
|
||||
TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
|
||||
case CONFIG_ACM_COMPLETE:
|
||||
// itf_num+1 to account for data interface as well
|
||||
set_config_complete(p_cdc, idx, itf_num+1);
|
||||
break;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
TU_LOG_DRV("CDC ACM Set Control Line State\r\n");
|
||||
|
||||
tuh_xfer_t xfer;
|
||||
xfer.daddr = daddr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.user_data = 0;
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
|
||||
.wValue = tu_htole16(line_state),
|
||||
.wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
// fake transfer to kick-off process
|
||||
uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
|
||||
cdch_interface_t * p_cdc = get_itf(idx);
|
||||
TU_ASSERT(p_cdc);
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
|
||||
switch (p_cdc->serial_protocol) {
|
||||
case SERIAL_PROTOCOL_ACM:
|
||||
process_acm_config(&xfer);
|
||||
break;
|
||||
|
||||
#if CFG_TUH_CDC_FTDI
|
||||
case SERIAL_PROTOCOL_FTDI:
|
||||
process_ftdi_config(&xfer);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_CDC_CP210X
|
||||
case SERIAL_PROTOCOL_CP210X:
|
||||
process_cp210x_config(&xfer);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: return false;
|
||||
}
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = p_cdc->daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_DRV("CDC ACM Set Line Conding\r\n");
|
||||
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = CDC_REQUEST_SET_LINE_CODING,
|
||||
.wValue = 0,
|
||||
.wIndex = tu_htole16(p_cdc->bInterfaceNumber),
|
||||
.wLength = tu_htole16(sizeof(cdc_line_coding_t))
|
||||
};
|
||||
|
||||
// use usbh enum buf to hold line coding since user line_coding variable does not live long enough
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t));
|
||||
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = p_cdc->daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = enum_buf,
|
||||
.complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_VERIFY(p_cdc->acm_capability.support_line_request);
|
||||
cdc_line_coding_t line_coding = p_cdc->line_coding;
|
||||
line_coding.bit_rate = baudrate;
|
||||
return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// FTDI
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -822,9 +893,9 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint
|
||||
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
|
||||
TU_VERIFY(p_cdc);
|
||||
|
||||
TU_LOG_CDCH("FTDI opened\r\n");
|
||||
TU_LOG_DRV("FTDI opened\r\n");
|
||||
|
||||
p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI;
|
||||
p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
|
||||
|
||||
// endpoint pair
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
|
||||
@@ -866,9 +937,10 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u
|
||||
|
||||
static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_LOG_CDCH("CDC FTDI Set Control Line State\r\n");
|
||||
TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, cdch_internal_control_complete, user_data));
|
||||
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
|
||||
complete_cb ? cdch_internal_control_complete : NULL, user_data));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -901,14 +973,17 @@ static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
|
||||
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
|
||||
TU_LOG_CDCH("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\n", baudrate, divisor);
|
||||
TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\n", baudrate, divisor);
|
||||
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, cdch_internal_control_complete, user_data));
|
||||
_ftdi_requested_baud = baudrate;
|
||||
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
|
||||
complete_cb ? cdch_internal_control_complete : NULL, user_data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_ftdi_config(tuh_xfer_t* xfer) {
|
||||
static void ftdi_process_config(tuh_xfer_t* xfer) {
|
||||
uintptr_t const state = xfer->user_data;
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
|
||||
@@ -918,13 +993,13 @@ static void process_ftdi_config(tuh_xfer_t* xfer) {
|
||||
switch(state) {
|
||||
// Note may need to read FTDI eeprom
|
||||
case CONFIG_FTDI_RESET:
|
||||
TU_ASSERT(ftdi_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),);
|
||||
TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),);
|
||||
break;
|
||||
|
||||
case CONFIG_FTDI_MODEM_CTRL:
|
||||
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
|
||||
TU_ASSERT(
|
||||
ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),);
|
||||
ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
|
||||
break;
|
||||
#else
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
@@ -933,7 +1008,7 @@ static void process_ftdi_config(tuh_xfer_t* xfer) {
|
||||
case CONFIG_FTDI_SET_BAUDRATE: {
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
|
||||
TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, process_ftdi_config, CONFIG_FTDI_SET_DATA),);
|
||||
TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),);
|
||||
break;
|
||||
#else
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
@@ -985,8 +1060,8 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui
|
||||
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
|
||||
TU_VERIFY(p_cdc);
|
||||
|
||||
TU_LOG_CDCH("CP210x opened\r\n");
|
||||
p_cdc->serial_protocol = SERIAL_PROTOCOL_CP210X;
|
||||
TU_LOG_DRV("CP210x opened\r\n");
|
||||
p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
|
||||
|
||||
// endpoint pair
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
|
||||
@@ -1033,19 +1108,22 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe
|
||||
}
|
||||
|
||||
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_CDCH("CDC CP210x Set BaudRate = %lu\n", baudrate);
|
||||
baudrate = tu_htole32(baudrate);
|
||||
return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baudrate, 4, complete_cb, user_data);
|
||||
TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\n", baudrate);
|
||||
uint32_t baud_le = tu_htole32(baudrate);
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
|
||||
complete_cb ? cdch_internal_control_complete : NULL, user_data);
|
||||
}
|
||||
|
||||
static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_LOG_CDCH("CDC CP210x Set Control Line State\r\n");
|
||||
TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
|
||||
p_cdc->user_control_cb = complete_cb;
|
||||
return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, cdch_internal_control_complete, user_data);
|
||||
return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
|
||||
complete_cb ? cdch_internal_control_complete : NULL, user_data);
|
||||
}
|
||||
|
||||
static void process_cp210x_config(tuh_xfer_t* xfer) {
|
||||
static void cp210x_process_config(tuh_xfer_t* xfer) {
|
||||
uintptr_t const state = xfer->user_data;
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
|
||||
@@ -1054,13 +1132,13 @@ static void process_cp210x_config(tuh_xfer_t* xfer) {
|
||||
|
||||
switch (state) {
|
||||
case CONFIG_CP210X_IFC_ENABLE:
|
||||
TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, process_cp210x_config, CONFIG_CP210X_SET_BAUDRATE),);
|
||||
TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),);
|
||||
break;
|
||||
|
||||
case CONFIG_CP210X_SET_BAUDRATE: {
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
|
||||
TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, process_cp210x_config, CONFIG_CP210X_SET_LINE_CTL),);
|
||||
TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),);
|
||||
break;
|
||||
#else
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
@@ -1078,7 +1156,8 @@ static void process_cp210x_config(tuh_xfer_t* xfer) {
|
||||
|
||||
case CONFIG_CP210X_SET_DTR_RTS:
|
||||
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
|
||||
TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cp210x_config, CONFIG_CP210X_COMPLETE),);
|
||||
TU_ASSERT(
|
||||
cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
|
||||
break;
|
||||
#else
|
||||
TU_ATTR_FALLTHROUGH;
|
||||
|
||||
@@ -134,7 +134,12 @@ bool tuh_cdc_read_clear (uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint (Request) API
|
||||
// Each Function will make a USB transfer request to/from device
|
||||
// Each Function will make a USB control transfer request to/from device
|
||||
// - If complete_cb is provided, the function will return immediately and invoke
|
||||
// the callback when request is complete.
|
||||
// - If complete_cb is NULL, the function will block until request is complete.
|
||||
// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result.
|
||||
// - The function will return true if transfer is successful, false otherwise.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Request to Set Control Line State: DTR (bit 0), RTS (bit 1)
|
||||
@@ -144,6 +149,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
|
||||
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set Line Coding (ACM only)
|
||||
// Should only use if you don't work with serial devices such as FTDI/CP210x
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Get Line Coding (ACM only)
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
#include "hid_host.h"
|
||||
|
||||
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||
#define HIDH_DEBUG 2
|
||||
#define TU_LOG_DRV(...) TU_LOG(HIDH_DEBUG, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -68,7 +72,7 @@ tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID];
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
TU_ASSERT(daddr && idx < CFG_TUH_HID, NULL);
|
||||
TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL);
|
||||
hidh_interface_t* p_hid = &_hidh_itf[idx];
|
||||
return (p_hid->daddr == daddr) ? p_hid : NULL;
|
||||
}
|
||||
@@ -207,7 +211,7 @@ static void set_protocol_complete(tuh_xfer_t* xfer)
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_LOG2("HID Set Protocol = %d\r\n", protocol);
|
||||
TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
@@ -246,7 +250,7 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol)
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_LOG2("HID Set Report complete\r\n");
|
||||
TU_LOG_DRV("HID Set Report complete\r\n");
|
||||
|
||||
if (tuh_hid_set_report_complete_cb)
|
||||
{
|
||||
@@ -266,7 +270,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
@@ -298,7 +302,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r
|
||||
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
// SET IDLE request, device can stall if not support this request
|
||||
TU_LOG2("HID Set Idle \r\n");
|
||||
TU_LOG_DRV("HID Set Idle \r\n");
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
@@ -367,7 +371,7 @@ bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx)
|
||||
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len)
|
||||
{
|
||||
TU_LOG2("HID Send Report %d\r\n", report_id);
|
||||
TU_LOG_DRV("HID Send Report %d\r\n", report_id);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
@@ -430,7 +434,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
||||
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
TU_LOG2(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2);
|
||||
tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||
}else
|
||||
@@ -448,8 +452,9 @@ void hidh_close(uint8_t daddr)
|
||||
hidh_interface_t* p_hid = &_hidh_itf[i];
|
||||
if (p_hid->daddr == daddr)
|
||||
{
|
||||
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
p_hid->daddr = 0;
|
||||
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
|
||||
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
p_hid->daddr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +470,7 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
|
||||
|
||||
TU_LOG2("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||
TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||
@@ -592,7 +597,7 @@ static void process_set_config(tuh_xfer_t* xfer)
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||
{
|
||||
TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||
TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(daddr, idx, NULL, 0);
|
||||
@@ -763,7 +768,7 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
for ( uint8_t i = 0; i < report_num; i++ )
|
||||
{
|
||||
info = report_info_arr+i;
|
||||
TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||
TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||
}
|
||||
|
||||
return report_num;
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||
#define MSCH_DEBUG 2
|
||||
|
||||
#define TU_LOG_MSCH(...) TU_LOG(MSCH_DEBUG, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -82,6 +81,7 @@ CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
|
||||
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
|
||||
static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||
|
||||
// FIXME potential nul reference
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline msch_interface_t* get_itf(uint8_t dev_addr)
|
||||
{
|
||||
@@ -305,11 +305,15 @@ void msch_init(void)
|
||||
void msch_close(uint8_t dev_addr)
|
||||
{
|
||||
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
|
||||
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured, );
|
||||
|
||||
TU_LOG_MSCH(" MSCh close addr = %d\r\n", dev_addr);
|
||||
|
||||
// invoke Application Callback
|
||||
if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||
if (p_msc->mounted) {
|
||||
if(tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||
}
|
||||
|
||||
tu_memclr(p_msc, sizeof(msch_interface_t));
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
extern char const* const tu_str_speed[];
|
||||
extern char const* const tu_str_std_request[];
|
||||
extern char const* const tu_str_xfer_result[];
|
||||
#endif
|
||||
|
||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
|
||||
|
||||
@@ -59,14 +59,6 @@
|
||||
#define TUP_USBIP_OHCI
|
||||
#define TUP_OHCI_RHPORTS 2
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
// TODO USB0 has 6, USB1 has 4
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 FS
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
|
||||
#define TUP_DCD_ENDPOINT_MAX 5
|
||||
|
||||
@@ -78,12 +70,28 @@
|
||||
// TODO USB0 has 5, USB1 has 6
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
// USB0 has 6 with HS PHY, USB1 has 4 only FS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
|
||||
// NOTE: MCXN943 port 1 use chipidea HS, port 0 use chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MIMXRT)
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 HS
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32)
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
@@ -193,6 +201,11 @@
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32G0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
|
||||
@@ -148,21 +148,26 @@ uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s);
|
||||
|
||||
// Must be called in the transfer complete callback
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes)
|
||||
{
|
||||
void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) {
|
||||
tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes);
|
||||
}
|
||||
|
||||
// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_read_xfer_complete_offset(tu_edpt_stream_t* s, uint32_t xferred_bytes, uint32_t skip_offset) {
|
||||
if (skip_offset < xferred_bytes) {
|
||||
tu_fifo_write_n(&s->ff, s->ep_buf + skip_offset, (uint16_t) (xferred_bytes - skip_offset));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of bytes available for reading
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s)
|
||||
{
|
||||
uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) {
|
||||
return (uint32_t) tu_fifo_count(&s->ff);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch)
|
||||
{
|
||||
bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) {
|
||||
return tu_fifo_peek(&s->ff, ch);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,22 @@ typedef struct TU_ATTR_ALIGNED(4)
|
||||
|
||||
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Memory API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// clean/flush data cache: write cache -> memory.
|
||||
// Required before an DMA TX transfer to make sure data is in memory
|
||||
void dcd_dcache_clean(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// invalidate data cache: mark cache as invalid, next read will read from memory
|
||||
// Required BOTH before and after an DMA RX transfer
|
||||
void dcd_dcache_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// clean and invalidate data cache
|
||||
// Required before an DMA transfer where memory is both read/write by DMA
|
||||
void dcd_dcache_clean_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@@ -516,9 +516,9 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
_usbd_dev.connected = 1;
|
||||
|
||||
// mark both in & out control as free
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = false;
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0;
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0;
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = false;
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = 0;
|
||||
_usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0;
|
||||
|
||||
// Process control request
|
||||
@@ -540,12 +540,13 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
|
||||
TU_LOG(USBD_DBG, "on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
|
||||
|
||||
_usbd_dev.ep_status[epnum][ep_dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][ep_dir].busy = 0;
|
||||
_usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
|
||||
|
||||
if ( 0 == epnum )
|
||||
{
|
||||
usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
|
||||
usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete
|
||||
.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -553,7 +554,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
TU_ASSERT(driver, );
|
||||
|
||||
TU_LOG(USBD_DBG, " %s xfer callback\r\n", driver->name);
|
||||
driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
|
||||
driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1244,7 +1245,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
|
||||
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer()
|
||||
// could return and USBD task can preempt and clear the busy
|
||||
_usbd_dev.ep_status[epnum][dir].busy = true;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 1;
|
||||
|
||||
if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) )
|
||||
{
|
||||
@@ -1252,7 +1253,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
}else
|
||||
{
|
||||
// DCD error, mark endpoint as ready to allow next transfer
|
||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||
TU_LOG(USBD_DBG, "FAILED\r\n");
|
||||
TU_BREAKPOINT();
|
||||
@@ -1278,7 +1279,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
|
||||
|
||||
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
|
||||
// and usbd task can preempt and clear the busy
|
||||
_usbd_dev.ep_status[epnum][dir].busy = true;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 1;
|
||||
|
||||
if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes))
|
||||
{
|
||||
@@ -1287,7 +1288,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
|
||||
}else
|
||||
{
|
||||
// DCD error, mark endpoint as ready to allow next transfer
|
||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||
TU_LOG(USBD_DBG, "failed\r\n");
|
||||
TU_BREAKPOINT();
|
||||
@@ -1317,8 +1318,8 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
TU_LOG(USBD_DBG, " Stall EP %02X\r\n", ep_addr);
|
||||
dcd_edpt_stall(rhport, ep_addr);
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = true;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = true;
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = 1;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1334,8 +1335,8 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
TU_LOG(USBD_DBG, " Clear Stall EP %02X\r\n", ep_addr);
|
||||
dcd_edpt_clear_stall(rhport, ep_addr);
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = false;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,9 +1367,9 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_edpt_close(rhport, ep_addr);
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = false;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = false;
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1403,9 +1404,9 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep
|
||||
TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX);
|
||||
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed));
|
||||
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = false;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = false;
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||
return dcd_edpt_iso_activate(rhport, desc_ep);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@
|
||||
#include "tusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
// Debug level of USBD Control
|
||||
#define USBD_CONTROL_DEBUG 2
|
||||
|
||||
#if CFG_TUSB_DEBUG >= USBD_CONTROL_DEBUG
|
||||
extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
|
||||
#endif
|
||||
|
||||
@@ -188,7 +191,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
|
||||
{
|
||||
TU_VERIFY(_ctrl_xfer.buffer);
|
||||
memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
|
||||
TU_LOG_MEM(2, _usbd_ctrl_buf, xferred_bytes, 2);
|
||||
TU_LOG_MEM(USBD_CONTROL_DEBUG, _usbd_ctrl_buf, xferred_bytes, 2);
|
||||
}
|
||||
|
||||
_ctrl_xfer.total_xferred += (uint16_t) xferred_bytes;
|
||||
@@ -205,7 +208,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
|
||||
// callback can still stall control in status phase e.g out data does not make sense
|
||||
if ( _ctrl_xfer.complete_cb )
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
#if CFG_TUSB_DEBUG >= USBD_CONTROL_DEBUG
|
||||
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@
|
||||
// Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Max number of endpoints per device
|
||||
// TODO optimize memory usage
|
||||
#ifndef CFG_TUH_ENDPOINT_MAX
|
||||
#define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3)
|
||||
#define CFG_TUH_ENDPOINT_MAX 16
|
||||
// #ifdef TUP_HCD_ENDPOINT_MAX
|
||||
// #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX
|
||||
// #else
|
||||
@@ -102,6 +104,22 @@ typedef struct
|
||||
uint8_t speed;
|
||||
} hcd_devtree_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Memory API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// clean/flush data cache: write cache -> memory.
|
||||
// Required before an DMA TX transfer to make sure data is in memory
|
||||
void hcd_dcache_clean(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// invalidate data cache: mark cache as invalid, next read will read from memory
|
||||
// Required BOTH before and after an DMA RX transfer
|
||||
void hcd_dcache_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// clean and invalidate data cache
|
||||
// Required before an DMA transfer where memory is both read/write by DMA
|
||||
void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -157,7 +175,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
|
||||
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
|
||||
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
|
||||
bool hcd_edpt_clear_stall(uint8_t daddr, uint8_t ep_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH implemented API
|
||||
@@ -182,6 +200,7 @@ void hcd_event_device_attach(uint8_t rhport, bool in_isr)
|
||||
event.event_id = HCD_EVENT_DEVICE_ATTACH;
|
||||
event.connection.hub_addr = 0;
|
||||
event.connection.hub_port = 0;
|
||||
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
@@ -212,7 +231,6 @@ void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred
|
||||
event.xfer_complete.result = result;
|
||||
event.xfer_complete.len = xferred_bytes;
|
||||
|
||||
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
#include "usbh_classdriver.h"
|
||||
#include "hub.h"
|
||||
|
||||
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||
#define HUB_DEBUG 2
|
||||
#define TU_LOG_DRV(...) TU_LOG(HUB_DEBUG, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -218,7 +222,10 @@ void hub_close(uint8_t dev_addr)
|
||||
TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, );
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
|
||||
if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t));
|
||||
if (p_hub->ep_in) {
|
||||
TU_LOG_DRV(" HUB close addr = %d\r\n", dev_addr);
|
||||
tu_memclr(p_hub, sizeof( hub_interface_t));
|
||||
}
|
||||
}
|
||||
|
||||
bool hub_edpt_status_xfer(uint8_t dev_addr)
|
||||
@@ -320,34 +327,35 @@ static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
|
||||
static void connection_port_reset_complete (tuh_xfer_t* xfer);
|
||||
|
||||
// callback as response of interrupt endpoint polling
|
||||
bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports
|
||||
(void) ep_addr;
|
||||
TU_ASSERT(result == XFER_RESULT_SUCCESS);
|
||||
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
|
||||
TU_LOG2(" Hub Status Change = 0x%02X\r\n", p_hub->status_change);
|
||||
uint8_t const status_change = p_hub->status_change;
|
||||
TU_LOG2(" Hub Status Change = 0x%02X\r\n", status_change);
|
||||
|
||||
// Hub bit 0 is for the hub device events
|
||||
if (tu_bit_test(p_hub->status_change, 0))
|
||||
{
|
||||
if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false)
|
||||
{
|
||||
if ( status_change == 0 ) {
|
||||
// The status change event was neither for the hub, nor for any of its ports.
|
||||
// This shouldn't happen, but it does with some devices.
|
||||
// Initiate the next interrupt poll here.
|
||||
return hub_edpt_status_xfer(dev_addr);
|
||||
}
|
||||
|
||||
if (tu_bit_test(status_change, 0)) {
|
||||
// Hub bit 0 is for the hub device events
|
||||
if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) {
|
||||
//Hub status control transfer failed, retry
|
||||
hub_edpt_status_xfer(dev_addr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// Hub bits 1 to n are hub port events
|
||||
for (uint8_t port=1; port <= p_hub->port_count; port++)
|
||||
{
|
||||
if ( tu_bit_test(p_hub->status_change, port) )
|
||||
{
|
||||
if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false)
|
||||
{
|
||||
for (uint8_t port=1; port <= p_hub->port_count; port++) {
|
||||
if ( tu_bit_test(status_change, port) ) {
|
||||
if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) {
|
||||
//Hub status control transfer failed, retry
|
||||
hub_edpt_status_xfer(dev_addr);
|
||||
}
|
||||
@@ -357,7 +365,6 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32
|
||||
}
|
||||
|
||||
// NOTE: next status transfer is queued by usbh.c after handling this request
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
195
src/host/usbh.c
195
src/host/usbh.c
@@ -61,6 +61,8 @@ typedef struct
|
||||
uint8_t hub_addr;
|
||||
uint8_t hub_port;
|
||||
uint8_t speed;
|
||||
|
||||
// enumeration is in progress, done when all interfaces are configured
|
||||
volatile uint8_t enumerating;
|
||||
|
||||
// struct TU_ATTR_PACKED {
|
||||
@@ -79,10 +81,12 @@ typedef struct {
|
||||
|
||||
// Device State
|
||||
struct TU_ATTR_PACKED {
|
||||
volatile uint8_t connected : 1;
|
||||
volatile uint8_t addressed : 1;
|
||||
volatile uint8_t configured : 1;
|
||||
volatile uint8_t suspended : 1;
|
||||
volatile uint8_t connected : 1; // After 1st transfer
|
||||
volatile uint8_t addressed : 1; // After SET_ADDR
|
||||
volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
|
||||
volatile uint8_t suspended : 1; // Bus suspended
|
||||
|
||||
// volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
|
||||
};
|
||||
|
||||
// Device Descriptor
|
||||
@@ -246,7 +250,7 @@ static inline usbh_device_t* get_device(uint8_t dev_addr)
|
||||
}
|
||||
|
||||
static bool enum_new_device(hcd_event_t* event);
|
||||
static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
|
||||
static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
|
||||
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
|
||||
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
@@ -418,7 +422,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
|
||||
case HCD_EVENT_DEVICE_REMOVE:
|
||||
TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
|
||||
process_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port);
|
||||
process_removing_device(event.rhport, event.connection.hub_addr, event.connection.hub_port);
|
||||
|
||||
#if CFG_TUH_HUB
|
||||
// TODO remove
|
||||
@@ -436,7 +440,8 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const ep_dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
TU_LOG_USBH("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
|
||||
TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len,
|
||||
tu_str_xfer_result[event.xfer_complete.result]);
|
||||
|
||||
if (event.dev_addr == 0)
|
||||
{
|
||||
@@ -447,7 +452,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
|
||||
else
|
||||
{
|
||||
usbh_device_t* dev = get_device(event.dev_addr);
|
||||
TU_ASSERT(dev, );
|
||||
TU_VERIFY(dev && dev->connected, );
|
||||
|
||||
dev->ep_status[epnum][ep_dir].busy = 0;
|
||||
dev->ep_status[epnum][ep_dir].claimed = 0;
|
||||
@@ -571,19 +576,19 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
|
||||
|
||||
TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
|
||||
|
||||
while (result == XFER_RESULT_INVALID)
|
||||
{
|
||||
while (result == XFER_RESULT_INVALID) {
|
||||
// Note: this can be called within an callback ie. part of tuh_task()
|
||||
// therefore event with RTOS tuh_task() still need to be invoked
|
||||
if (tuh_task_event_ready())
|
||||
{
|
||||
if (tuh_task_event_ready()) {
|
||||
tuh_task();
|
||||
}
|
||||
|
||||
// TODO probably some timeout to prevent hanged
|
||||
}
|
||||
|
||||
// update transfer result
|
||||
// update transfer result, user_data is expected to point to xfer_result_t
|
||||
if (xfer->user_data != 0) {
|
||||
*((xfer_result_t*) xfer->user_data) = result;
|
||||
}
|
||||
xfer->result = result;
|
||||
xfer->actual_len = _ctrl_xfer.actual_len;
|
||||
}
|
||||
@@ -736,29 +741,33 @@ void usbh_int_set(bool enabled)
|
||||
// TODO has some duplication code with device, refactor later
|
||||
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
|
||||
{
|
||||
// Note: addr0 only use tuh_control_xfer
|
||||
usbh_device_t* dev = get_device(dev_addr);
|
||||
|
||||
// addr0 only use tuh_control_xfer
|
||||
TU_ASSERT(dev);
|
||||
TU_ASSERT(dev && dev->connected);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
return tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex);
|
||||
TU_VERIFY(tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex));
|
||||
TU_LOG_USBH("[%u] Claimed EP 0x%02x\r\n", dev_addr, ep_addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO has some duplication code with device, refactor later
|
||||
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
|
||||
{
|
||||
// Note: addr0 only use tuh_control_xfer
|
||||
usbh_device_t* dev = get_device(dev_addr);
|
||||
|
||||
// addr0 only use tuh_control_xfer
|
||||
TU_ASSERT(dev);
|
||||
TU_VERIFY(dev && dev->connected);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
return tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
|
||||
TU_VERIFY(tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex));
|
||||
TU_LOG_USBH("[%u] Released EP 0x%02x\r\n", dev_addr, ep_addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO has some duplication code with device, refactor later
|
||||
@@ -866,6 +875,10 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
|
||||
{
|
||||
switch (event->event_id)
|
||||
{
|
||||
// case HCD_EVENT_DEVICE_REMOVE:
|
||||
// // mark device as removing to prevent further xfer before the event is processed in usbh task
|
||||
// break;
|
||||
|
||||
default:
|
||||
osal_queue_send(_usbh_q, event, in_isr);
|
||||
break;
|
||||
@@ -877,7 +890,7 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// generic helper to get a descriptor
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
// if blocking, user_data is pointed to xfer_result
|
||||
static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
@@ -905,15 +918,7 @@ static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
bool const ret = tuh_control_xfer(&xfer);
|
||||
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
if ( !complete_cb && user_data )
|
||||
{
|
||||
*((xfer_result_t*) user_data) = xfer.result;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
|
||||
@@ -971,7 +976,7 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void*
|
||||
}
|
||||
|
||||
// Get HID report descriptor
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
// if blocking, user_data is pointed to xfer_result
|
||||
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
@@ -1000,15 +1005,7 @@ bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
bool const ret = tuh_control_xfer(&xfer);
|
||||
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
if ( !complete_cb && user_data )
|
||||
{
|
||||
*((xfer_result_t*) user_data) = xfer.result;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
@@ -1040,15 +1037,7 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
bool ret = tuh_control_xfer(&xfer);
|
||||
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
if ( !complete_cb && user_data )
|
||||
{
|
||||
*((xfer_result_t*) user_data) = xfer.result;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
|
||||
@@ -1080,15 +1069,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
bool ret = tuh_control_xfer(&xfer);
|
||||
|
||||
// if blocking, user_data could be pointed to xfer_result
|
||||
if ( !complete_cb && user_data )
|
||||
{
|
||||
*((xfer_result_t*) user_data) = xfer.result;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -1141,7 +1122,7 @@ uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_i
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
// Detaching
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
@@ -1150,47 +1131,79 @@ static inline bool is_hub_addr(uint8_t daddr)
|
||||
return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
|
||||
}
|
||||
|
||||
//static void mark_removing_device_isr(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
|
||||
// for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
|
||||
// usbh_device_t *dev = &_usbh_devices[dev_id];
|
||||
// uint8_t const daddr = dev_id + 1;
|
||||
//
|
||||
// // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
|
||||
// if (dev->rhport == rhport && dev->connected &&
|
||||
// (hub_addr == 0 || dev->hub_addr == hub_addr) &&
|
||||
// (hub_port == 0 || dev->hub_port == hub_port)) {
|
||||
// if (is_hub_addr(daddr)) {
|
||||
// // If the device itself is a usb hub, mark all downstream devices.
|
||||
// // FIXME recursive calls
|
||||
// mark_removing_device_isr(rhport, daddr, 0);
|
||||
// }
|
||||
//
|
||||
// dev->removing = 1;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// a device unplugged from rhport:hub_addr:hub_port
|
||||
static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
|
||||
static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
|
||||
{
|
||||
//------------- find the all devices (star-network) under port that is unplugged -------------//
|
||||
// TODO mark as disconnected in ISR, also handle dev0
|
||||
for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ )
|
||||
{
|
||||
usbh_device_t* dev = &_usbh_devices[dev_id];
|
||||
uint8_t const dev_addr = dev_id+1;
|
||||
|
||||
// TODO Hub multiple level
|
||||
if (dev->rhport == rhport &&
|
||||
(hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub
|
||||
(hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub
|
||||
dev->connected)
|
||||
{
|
||||
TU_LOG_USBH(" Address = %u\r\n", dev_addr);
|
||||
#if 0
|
||||
// index as hub addr, value is hub port (0xFF for invalid)
|
||||
uint8_t removing_hubs[CFG_TUH_HUB];
|
||||
memset(removing_hubs, TUSB_INDEX_INVALID_8, sizeof(removing_hubs));
|
||||
|
||||
if (is_hub_addr(dev_addr))
|
||||
{
|
||||
TU_LOG(USBH_DEBUG, "HUB address = %u is unmounted\r\n", dev_addr);
|
||||
// If the device itself is a usb hub, unplug downstream devices.
|
||||
// FIXME un-roll recursive calls to prevent potential stack overflow
|
||||
process_device_unplugged(rhport, dev_addr, 0);
|
||||
}else
|
||||
{
|
||||
// Invoke callback before closing driver
|
||||
if (tuh_umount_cb) tuh_umount_cb(dev_addr);
|
||||
removing_hubs[hub_addr-CFG_TUH_DEVICE_MAX] = hub_port;
|
||||
|
||||
// consecutive non-removing hub
|
||||
uint8_t nop_count = 0;
|
||||
#endif
|
||||
|
||||
for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
|
||||
usbh_device_t *dev = &_usbh_devices[dev_id];
|
||||
uint8_t const daddr = dev_id + 1;
|
||||
|
||||
// hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
|
||||
if (dev->rhport == rhport && dev->connected &&
|
||||
(hub_addr == 0 || dev->hub_addr == hub_addr) &&
|
||||
(hub_port == 0 || dev->hub_port == hub_port)) {
|
||||
TU_LOG_USBH("Device unplugged address = %u\r\n", daddr);
|
||||
|
||||
if (is_hub_addr(daddr)) {
|
||||
TU_LOG(USBH_DEBUG, " is a HUB device %u\r\n", daddr);
|
||||
|
||||
// Submit removed event If the device itself is a hub (un-rolled recursive)
|
||||
// TODO a better to unroll recursrive is using array of removing_hubs and mark it here
|
||||
hcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = HCD_EVENT_DEVICE_REMOVE;
|
||||
event.connection.hub_addr = daddr;
|
||||
event.connection.hub_port = 0;
|
||||
|
||||
hcd_event_handler(&event, false);
|
||||
} else {
|
||||
// Invoke callback before closing driver (maybe call it later ?)
|
||||
if (tuh_umount_cb) tuh_umount_cb(daddr);
|
||||
}
|
||||
|
||||
// Close class driver
|
||||
for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
|
||||
{
|
||||
TU_LOG_USBH("%s close\r\n", usbh_class_drivers[drv_id].name);
|
||||
usbh_class_drivers[drv_id].close(dev_addr);
|
||||
for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) {
|
||||
usbh_class_drivers[drv_id].close(daddr);
|
||||
}
|
||||
|
||||
hcd_device_close(rhport, dev_addr);
|
||||
hcd_device_close(rhport, daddr);
|
||||
clear_device(dev);
|
||||
// abort on-going control xfer if any
|
||||
if (_ctrl_xfer.daddr == dev_addr) _set_control_xfer_stage(CONTROL_STAGE_IDLE);
|
||||
if (_ctrl_xfer.daddr == daddr) _set_control_xfer_stage(CONTROL_STAGE_IDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1242,6 +1255,7 @@ static void process_enumeration(tuh_xfer_t* xfer)
|
||||
{
|
||||
failed_count++;
|
||||
osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit
|
||||
TU_LOG1("Enumeration attempt %u\r\n", failed_count);
|
||||
TU_ASSERT(tuh_control_xfer(xfer), );
|
||||
}else
|
||||
{
|
||||
@@ -1481,6 +1495,7 @@ static uint8_t get_new_address(bool is_hub)
|
||||
{
|
||||
uint8_t start;
|
||||
uint8_t end;
|
||||
|
||||
if ( is_hub )
|
||||
{
|
||||
start = CFG_TUH_DEVICE_MAX;
|
||||
@@ -1491,7 +1506,7 @@ static uint8_t get_new_address(bool is_hub)
|
||||
end = start + CFG_TUH_DEVICE_MAX;
|
||||
}
|
||||
|
||||
for ( uint8_t idx = start; idx < end; idx++)
|
||||
for (uint8_t idx = start; idx < end; idx++)
|
||||
{
|
||||
if (!_usbh_devices[idx].connected) return (idx+1);
|
||||
}
|
||||
|
||||
@@ -46,18 +46,44 @@ static const ci_hs_controller_t _ci_controller[] =
|
||||
{
|
||||
// RT1010 and RT1020 only has 1 USB controller
|
||||
#if FSL_FEATURE_SOC_USBHS_COUNT == 1
|
||||
{ .reg_base = USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 }
|
||||
{ .reg_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
|
||||
#else
|
||||
{ .reg_base = USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 },
|
||||
{ .reg_base = USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 }
|
||||
{ .reg_base = USB1_BASE, .irqnum = USB_OTG1_IRQn},
|
||||
{ .reg_base = USB2_BASE, .irqnum = USB_OTG2_IRQn}
|
||||
#endif
|
||||
};
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
//------------- DCD -------------//
|
||||
#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
//------------- HCD -------------//
|
||||
#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
//------------- DCache -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_is_cache_mem(uint32_t addr) {
|
||||
return !(0x20000000 <= addr && addr < 0x20100000);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void imxrt_dcache_clean(void* addr, uint32_t data_size) {
|
||||
if (imxrt_is_cache_mem((uint32_t) addr)) {
|
||||
SCB_CleanDCache_by_Addr((uint32_t *) addr, (int32_t) data_size);
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void imxrt_dcache_invalidate(void* addr, uint32_t data_size) {
|
||||
if (imxrt_is_cache_mem((uint32_t) addr)) {
|
||||
SCB_InvalidateDCache_by_Addr(addr, (int32_t) data_size);
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void imxrt_dcache_clean_invalidate(void* addr, uint32_t data_size) {
|
||||
if (imxrt_is_cache_mem((uint32_t) addr)) {
|
||||
SCB_CleanInvalidateDCache_by_Addr(addr, (int32_t) data_size);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] =
|
||||
{
|
||||
{ .reg_base = LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
|
||||
{ .reg_base = LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
|
||||
{ .reg_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
|
||||
{ .reg_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
|
||||
};
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
|
||||
52
src/portable/chipidea/ci_hs/ci_hs_mcx.h
Normal file
52
src/portable/chipidea/ci_hs/ci_hs_mcx.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _CI_HS_MCX_H_
|
||||
#define _CI_HS_MCX_H_
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
// NOTE: MCX N9 has 2 different USB Controller
|
||||
// - USB0 is KHCI FullSpeed
|
||||
// - USB1 is ChipIdea HighSpeed, therefore rhport = 1 is actually index 0
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] = {
|
||||
{.reg_base = USBHS1__USBC_BASE, .irqnum = USB1_HS_IRQn}
|
||||
};
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline ci_hs_regs_t* CI_HS_REG(uint8_t port) {
|
||||
(void) port;
|
||||
return ((ci_hs_regs_t*) _ci_controller[0].reg_base);
|
||||
}
|
||||
|
||||
#define CI_DCD_INT_ENABLE(_p) do { (void) _p; NVIC_EnableIRQ (_ci_controller[0].irqnum); } while (0)
|
||||
#define CI_DCD_INT_DISABLE(_p) do { (void) _p; NVIC_DisableIRQ(_ci_controller[0].irqnum); } while (0)
|
||||
|
||||
#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
|
||||
#endif
|
||||
@@ -31,13 +31,21 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// DCCPARAMS
|
||||
enum {
|
||||
DCCPARAMS_DEN_MASK = 0x1Fu, ///< DEN bit 4:0
|
||||
};
|
||||
|
||||
// USBCMD
|
||||
enum {
|
||||
USBCMD_RUN_STOP = TU_BIT(0),
|
||||
USBCMD_RESET = TU_BIT(1),
|
||||
USBCMD_SETUP_TRIPWIRE = TU_BIT(13),
|
||||
USBCMD_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) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
|
||||
// Interrupt Threshold bit 23:16
|
||||
USBCMD_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) endpoint’s linked list. This bit is set and
|
||||
// cleared by software during the process of adding a new dTD
|
||||
|
||||
USBCMD_INTR_THRESHOLD_MASK = 0x00FF0000u, // Interrupt Threshold bit 23:16
|
||||
};
|
||||
|
||||
// PORTSC1
|
||||
@@ -72,6 +80,7 @@ enum {
|
||||
|
||||
// USBMode
|
||||
enum {
|
||||
USBMOD_CM_MASK = TU_BIT(0) | TU_BIT(1),
|
||||
USBMODE_CM_DEVICE = 2,
|
||||
USBMODE_CM_HOST = 3,
|
||||
|
||||
@@ -134,7 +143,6 @@ typedef struct
|
||||
{
|
||||
uint32_t reg_base;
|
||||
uint32_t irqnum;
|
||||
uint8_t ep_count; // Max bi-directional Endpoints
|
||||
}ci_hs_controller_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -28,35 +28,53 @@
|
||||
|
||||
#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_HS)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "device/dcd.h"
|
||||
#include "ci_hs_type.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "ci_hs_imxrt.h"
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
|
||||
void dcd_dcache_clean(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_clean(addr, data_size);
|
||||
}
|
||||
|
||||
void dcd_dcache_invalidate(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
void dcd_dcache_clean_invalidate(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_clean_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
|
||||
// MCX N9 only port 1 use this controller
|
||||
#include "ci_hs_mcx.h"
|
||||
#else
|
||||
#error "Unsupported MCUs"
|
||||
#endif
|
||||
|
||||
TU_ATTR_WEAK void dcd_dcache_clean(void* addr, uint32_t data_size) {
|
||||
(void) addr; (void) data_size;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void dcd_dcache_invalidate(void* addr, uint32_t data_size) {
|
||||
(void) addr; (void) data_size;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void dcd_dcache_clean_invalidate(void* addr, uint32_t data_size) {
|
||||
(void) addr; (void) data_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
// Clean means to push any cached changes to RAM and invalidate "removes" the
|
||||
// entry from the cache.
|
||||
#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1
|
||||
#define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr
|
||||
#else
|
||||
#define CleanInvalidateDCache_by_Addr(_addr, _dsize)
|
||||
#endif
|
||||
|
||||
|
||||
// ENDPTCTRL
|
||||
enum {
|
||||
ENDPTCTRL_STALL = TU_BIT(0),
|
||||
@@ -160,6 +178,16 @@ typedef struct {
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
|
||||
static dcd_data_t _dcd_data;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Prototypes and Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline uint8_t ci_ep_count(ci_hs_regs_t const* dcd_reg)
|
||||
{
|
||||
return dcd_reg->DCCPARAMS & DCCPARAMS_DEN_MASK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -174,7 +202,8 @@ static void bus_reset(uint8_t rhport)
|
||||
// endpoint type of the unused direction must be changed from the control type to any other
|
||||
// type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
|
||||
// for the data PID tracking on the active endpoint.
|
||||
for( uint8_t i=1; i < _ci_controller[rhport].ep_count; i++)
|
||||
uint8_t const ep_count = ci_ep_count(dcd_reg);
|
||||
for( uint8_t i=1; i < ep_count; i++)
|
||||
{
|
||||
dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
|
||||
}
|
||||
@@ -202,7 +231,7 @@ static void bus_reset(uint8_t rhport)
|
||||
|
||||
_dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
|
||||
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
}
|
||||
|
||||
void dcd_init(uint8_t rhport)
|
||||
@@ -211,26 +240,34 @@ void dcd_init(uint8_t rhport)
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX, );
|
||||
|
||||
// Reset controller
|
||||
dcd_reg->USBCMD |= USBCMD_RESET;
|
||||
while( dcd_reg->USBCMD & USBCMD_RESET ) {}
|
||||
|
||||
// Set mode to device, must be set immediately after reset
|
||||
dcd_reg->USBMODE = USBMODE_CM_DEVICE;
|
||||
uint32_t usbmode = dcd_reg->USBMODE & ~USBMOD_CM_MASK;
|
||||
usbmode |= USBMODE_CM_DEVICE;
|
||||
dcd_reg->USBMODE = usbmode;
|
||||
|
||||
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
|
||||
|
||||
#if !TUD_OPT_HIGH_SPEED
|
||||
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
|
||||
#endif
|
||||
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
|
||||
dcd_reg->USBSTS = dcd_reg->USBSTS;
|
||||
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
|
||||
|
||||
dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
|
||||
dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
|
||||
uint32_t usbcmd = dcd_reg->USBCMD;
|
||||
usbcmd &= ~USBCMD_INTR_THRESHOLD_MASK; // Interrupt Threshold Interval = 0
|
||||
usbcmd |= USBCMD_RUN_STOP; // run
|
||||
|
||||
dcd_reg->USBCMD = usbcmd;
|
||||
}
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
@@ -286,7 +323,7 @@ static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
|
||||
{
|
||||
// Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
|
||||
// address to 32-byte boundaries. Buffer must be word aligned
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
|
||||
dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
|
||||
|
||||
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
|
||||
|
||||
@@ -343,8 +380,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
|
||||
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// Must not exceed max endpoint number
|
||||
TU_ASSERT( epnum < _ci_controller[rhport].ep_count );
|
||||
TU_ASSERT(epnum < ci_ep_count(dcd_reg));
|
||||
|
||||
//------------- Prepare Queue Head -------------//
|
||||
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
@@ -359,11 +398,9 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
|
||||
|
||||
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
|
||||
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
// Enable EP Control
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
|
||||
|
||||
if ( dir == TUSB_DIR_OUT )
|
||||
@@ -382,7 +419,8 @@ void dcd_edpt_close_all (uint8_t rhport)
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// Disable all non-control endpoints
|
||||
for( uint8_t epnum=1; epnum < _ci_controller[rhport].ep_count; epnum++)
|
||||
uint8_t const ep_count = ci_ep_count(dcd_reg);
|
||||
for (uint8_t epnum = 1; epnum < ep_count; epnum++)
|
||||
{
|
||||
_dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
|
||||
_dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
|
||||
@@ -420,7 +458,7 @@ static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
|
||||
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
|
||||
|
||||
// flush cache
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
if ( epnum == 0 )
|
||||
{
|
||||
@@ -498,7 +536,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
|
||||
}
|
||||
}
|
||||
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
|
||||
dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -611,20 +649,11 @@ void dcd_int_handler(uint8_t rhport)
|
||||
if (int_status & INTR_USB)
|
||||
{
|
||||
// Make sure we read the latest version of _dcd_data.
|
||||
CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
|
||||
dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
|
||||
|
||||
if (dcd_reg->ENDPTSETUPSTAT)
|
||||
{
|
||||
//------------- Set up Received -------------//
|
||||
// 23.10.10.2 Operational model for setup transfers
|
||||
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
|
||||
|
||||
dcd_event_setup_received(rhport, (uint8_t*)(uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
|
||||
}
|
||||
|
||||
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
|
||||
// nothing to do, we will submit xfer as error to usbd
|
||||
// if (int_status & INTR_ERROR) { }
|
||||
@@ -637,6 +666,15 @@ void dcd_int_handler(uint8_t rhport)
|
||||
if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up Received
|
||||
// 23.10.10.2 Operational model for setup transfers
|
||||
// Must be after normal transfer complete since it is possible to have both previous control status + new setup
|
||||
// in the same frame and we should handle previous status first.
|
||||
if (dcd_reg->ENDPTSETUPSTAT) {
|
||||
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
|
||||
dcd_event_setup_received(rhport, (uint8_t *) (uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (int_status & INTR_SOF)
|
||||
|
||||
@@ -41,6 +41,19 @@
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
|
||||
#include "ci_hs_imxrt.h"
|
||||
|
||||
void hcd_dcache_clean(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_clean(addr, data_size);
|
||||
}
|
||||
|
||||
void hcd_dcache_invalidate(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
void hcd_dcache_clean_invalidate(void* addr, uint32_t data_size) {
|
||||
imxrt_dcache_clean_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
#else
|
||||
@@ -51,8 +64,6 @@
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -76,6 +87,8 @@ bool hcd_init(uint8_t rhport)
|
||||
#endif
|
||||
|
||||
// FIXME force full speed, still have issue with Highspeed enumeration
|
||||
// 1. Have issue when plug/unplug devices, maybe the port is not reset properly
|
||||
// 2. Also does not seems to detect disconnection
|
||||
hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
|
||||
|
||||
return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -81,6 +81,8 @@ typedef union {
|
||||
};
|
||||
}ehci_link_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ehci_link_t) == 4, "size is not correct" );
|
||||
|
||||
/// Queue Element Transfer Descriptor
|
||||
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
|
||||
typedef struct
|
||||
@@ -162,11 +164,12 @@ typedef struct TU_ATTR_ALIGNED(32)
|
||||
uint8_t pid;
|
||||
uint8_t interval_ms; // polling interval in frames (or millisecond)
|
||||
|
||||
uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set
|
||||
uint8_t reserved2[2];
|
||||
uint8_t TU_RESERVED[4];
|
||||
|
||||
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
|
||||
// Attached TD management, note usbh will only queue 1 TD per QHD.
|
||||
// buffer for dcache invalidate since td's buffer is modified by HC and finding initial buffer address is not trivial
|
||||
uint32_t attached_buffer;
|
||||
ehci_qtd_t * volatile attached_qtd;
|
||||
} ehci_qhd_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
|
||||
@@ -245,14 +248,6 @@ typedef struct TU_ATTR_ALIGNED(32)
|
||||
/// Word 4-5: Buffer Pointer List
|
||||
uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
|
||||
|
||||
// union{
|
||||
// uint32_t BufferPointer1;
|
||||
// struct {
|
||||
// volatile uint32_t TCount : 3;
|
||||
// volatile uint32_t TPosition : 2;
|
||||
// };
|
||||
// };
|
||||
|
||||
/*---------- Word 6 ----------*/
|
||||
ehci_link_t back;
|
||||
|
||||
@@ -267,16 +262,22 @@ TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
|
||||
//--------------------------------------------------------------------+
|
||||
// EHCI Operational Register
|
||||
//--------------------------------------------------------------------+
|
||||
enum ehci_interrupt_mask_{
|
||||
enum {
|
||||
// Bit 0-5 has maskable in interrupt enabled register
|
||||
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 = 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_HC_HALTED = TU_BIT(12),
|
||||
EHCI_INT_MASK_RECLAIMATION = TU_BIT(13),
|
||||
EHCI_INT_MASK_PERIODIC_SCHED_STATUS = TU_BIT(14),
|
||||
EHCI_INT_MASK_ASYNC_SCHED_STATUS = TU_BIT(15),
|
||||
|
||||
EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18),
|
||||
EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19),
|
||||
|
||||
@@ -287,7 +288,7 @@ enum ehci_interrupt_mask_{
|
||||
EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC
|
||||
};
|
||||
|
||||
enum ehci_usbcmd_pos_ {
|
||||
enum {
|
||||
EHCI_USBCMD_POS_RUN_STOP = 0,
|
||||
EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
|
||||
EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
|
||||
@@ -296,24 +297,27 @@ enum ehci_usbcmd_pos_ {
|
||||
EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
|
||||
};
|
||||
|
||||
enum ehci_portsc_change_mask_{
|
||||
enum {
|
||||
EHCI_PORTSC_MASK_CURRENT_CONNECT_STATUS = TU_BIT(0),
|
||||
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
|
||||
EHCI_PORTSC_MASK_PORT_EANBLED = TU_BIT(2),
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = TU_BIT(3),
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE = TU_BIT(3),
|
||||
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
|
||||
EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
|
||||
EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
|
||||
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
|
||||
ECHI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
|
||||
|
||||
EHCI_PORTSC_MASK_ALL =
|
||||
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE |
|
||||
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
|
||||
EHCI_PORTSC_MASK_W1C =
|
||||
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE |
|
||||
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
|
||||
};
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
union {
|
||||
uint32_t command;
|
||||
uint32_t command; // 0x00
|
||||
|
||||
struct {
|
||||
uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
|
||||
@@ -333,7 +337,7 @@ typedef volatile struct
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t status;
|
||||
uint32_t status; // 0x04
|
||||
|
||||
struct {
|
||||
uint32_t usb : 1 ; ///< qTD with IOC is retired
|
||||
@@ -357,7 +361,7 @@ typedef volatile struct
|
||||
};
|
||||
|
||||
union{
|
||||
uint32_t inten;
|
||||
uint32_t inten; // 0x08
|
||||
|
||||
struct {
|
||||
uint32_t usb : 1 ;
|
||||
@@ -375,43 +379,87 @@ typedef volatile struct
|
||||
}inten_bm;
|
||||
};
|
||||
|
||||
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 frame_index ; ///< 0x0C Micro frame counter
|
||||
uint32_t ctrl_ds_seg ; ///< 0x10 Control Data Structure Segment
|
||||
uint32_t periodic_list_base ; ///< 0x14 Beginning address of perodic frame list
|
||||
uint32_t async_list_addr ; ///< 0x18 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
|
||||
uint32_t config_flag ; ///< 0x40 not used by NXP
|
||||
|
||||
union {
|
||||
uint32_t portsc ; ///< port status and control
|
||||
struct {
|
||||
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; ///< 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 TU_RESERVED : 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
|
||||
// mixed with RW and R/WC bits, care should be taken when writing to this register
|
||||
uint32_t portsc ; ///< 0x44 port status and control
|
||||
const struct {
|
||||
uint32_t current_connect_status : 1; ///< 00: 0: No device, 1: Device is present on port
|
||||
uint32_t connect_status_change : 1; ///< 01: [R/WC] Change in Current Connect Status
|
||||
uint32_t port_enabled : 1; ///< 02: 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; ///< 03: [R/WC] Port Enabled has changed
|
||||
uint32_t over_current_active : 1; ///< 04: Port has an over-current condition
|
||||
uint32_t over_current_change : 1; ///< 05: [R/WC] Change to Over-current Active
|
||||
uint32_t force_port_resume : 1; ///< 06: Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
|
||||
uint32_t suspend : 1; ///< 07: Port in suspend state
|
||||
uint32_t port_reset : 1; ///< 08: 1=Port is in Reset. 0=Port is not in Reset
|
||||
uint32_t nxp_highspeed_status : 1; ///< 09: 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; ///< 10-11: D+/D- state: 00: SE0, 10: J-state, 01: K-state
|
||||
uint32_t port_power : 1; ///< 12: 0= power off, 1= power on
|
||||
uint32_t port_owner : 1; ///< 13: not used by NXP
|
||||
uint32_t port_indicator_control : 2; ///< 14-15: 00b: off, 01b: Amber, 10b: green, 11b: undefined
|
||||
uint32_t port_test_control : 4; ///< 16-19: Port test mode, not used by tinyusb
|
||||
uint32_t wake_on_connect_enable : 1; ///< 20: Enables device connects as wake-up events
|
||||
uint32_t wake_on_disconnect_enable : 1; ///< 21: Enables device disconnects as wake-up events
|
||||
uint32_t wake_on_over_current_enable : 1; ///< 22: Enables over-current conditions as wake-up events
|
||||
uint32_t nxp_phy_clock_disable : 1; ///< 23: 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; ///< 24: 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 TU_RESERVED : 1; ///< 25
|
||||
uint32_t nxp_port_speed : 2; ///< 26-27: 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 TU_RESERVED : 4;
|
||||
}portsc_bm;
|
||||
};
|
||||
}ehci_registers_t;
|
||||
} ehci_registers_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Capability Registers
|
||||
//--------------------------------------------------------------------+
|
||||
typedef volatile struct {
|
||||
uint8_t caplength; // 0x00
|
||||
uint8_t TU_RESERVED; // 0x01
|
||||
uint16_t hciversion; // 0x02
|
||||
|
||||
union {
|
||||
uint32_t hcsparams; // 0x04
|
||||
struct {
|
||||
uint32_t num_ports : 4; // [00:03]
|
||||
uint32_t port_power_control : 1; // [04]
|
||||
uint32_t TU_RESERVED : 2; // [05:06]
|
||||
uint32_t port_route_rule : 1; // [07]
|
||||
uint32_t n_pcc : 4; // [08:11] Number of Ports per Companion Controller
|
||||
uint32_t n_cc : 4; // [12:15] Number of Companion Controllers
|
||||
uint32_t port_ind : 1; // [16] Port Indicators
|
||||
uint32_t TU_RESERVED : 3; // [17:19]
|
||||
uint32_t n_ptt : 4; // [20:23] ChipIdea: Number of Ports per Transaction Translator
|
||||
uint32_t n_tt : 4; // [24:27] ChipIdea: Number of Transaction Translators
|
||||
uint32_t TU_RESERVED : 4; // [28:31]
|
||||
} hcsparams_bm;
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t hccparams; // 0x08
|
||||
struct {
|
||||
uint32_t addr_64bit : 1; // [00] 64-bit Addressing Capability
|
||||
uint32_t programmable_frame_list_flag : 1; // [01] Programmable Frame List Flag
|
||||
uint32_t async_park_cap : 1; // [02] Asynchronous Schedule Park Capability
|
||||
uint32_t TU_RESERVED : 1; // [03]
|
||||
uint32_t iso_schedule_threshold : 4; // [4:7] Isochronous Scheduling Threshold
|
||||
uint32_t eecp : 8; // [8:15] EHCI Extended Capabilities Pointer
|
||||
uint32_t TU_RESERVED : 16;// [16:31]
|
||||
} hccparams_bm;
|
||||
};
|
||||
|
||||
uint32_t hcsp_portroute; // 0x0C HCSP Port Route Register
|
||||
} ehci_cap_registers_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(ehci_cap_registers_t) == 16, "size is not correct");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ static void process_pipe0_bemp(uint8_t rhport)
|
||||
static void process_pipe_nrdy(uint8_t rhport, unsigned num)
|
||||
{
|
||||
(void)rhport;
|
||||
unsigned result;
|
||||
xfer_result_t result;
|
||||
uint16_t volatile *ctr = get_pipectr(num);
|
||||
// TU_LOG1("NRDY %d %x\n", num, *ctr);
|
||||
switch (*ctr & RUSB2_PIPE_CTR_PID_Msk) {
|
||||
|
||||
@@ -43,6 +43,10 @@ extern IRQn_Type _usb_hs_irqn;
|
||||
#define CFG_TUSB_RHPORT1_MODE 0
|
||||
#endif
|
||||
|
||||
#if defined(__ICCARM__)
|
||||
#define __builtin_ctz(x) __iar_builtin_CLZ(__iar_builtin_RBIT(x))
|
||||
#endif
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_enable(uint8_t rhport)
|
||||
{
|
||||
#ifdef CFG_TUSB_RHPORT1_MODE
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
* L0x2, L0x3 1024 byte buffer
|
||||
* L1 512 byte buffer
|
||||
* L4x2, L4x3 1024 byte buffer
|
||||
* G0 2048 byte buffer
|
||||
*
|
||||
* To use this driver, you must:
|
||||
* - If you are using a device with crystal-less USB, set up the clock recovery system (CRS)
|
||||
@@ -185,12 +186,12 @@ static void dcd_ep_ctr_handler(void);
|
||||
static uint8_t open_ep_count;
|
||||
static uint16_t ep_buf_ptr; ///< Points to first free memory location
|
||||
static void dcd_pma_alloc_reset(void);
|
||||
static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length);
|
||||
static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length);
|
||||
static void dcd_pma_free(uint8_t ep_addr);
|
||||
static void dcd_ep_free(uint8_t ep_addr);
|
||||
static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
|
||||
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes);
|
||||
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes);
|
||||
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes);
|
||||
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes);
|
||||
|
||||
static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
|
||||
static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
|
||||
@@ -209,17 +210,6 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr)
|
||||
return &xfer_status[epnum][dir];
|
||||
}
|
||||
|
||||
// Using a function due to better type checks
|
||||
// This seems better than having to do type casts everywhere else
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
|
||||
*reg = (uint16_t)(*reg & ~mask);
|
||||
}
|
||||
|
||||
// Bits in ISTR are cleared upon writing 0
|
||||
TU_ATTR_ALWAYS_INLINE static inline void clear_istr_bits(uint16_t mask) {
|
||||
USB->ISTR = ~mask;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
@@ -242,7 +232,9 @@ void dcd_init (uint8_t rhport)
|
||||
{
|
||||
asm("NOP");
|
||||
}
|
||||
reg16_clear_bits(&USB->CNTR, USB_CNTR_PDWN);// Remove powerdown
|
||||
|
||||
USB->CNTR &= ~USB_CNTR_PDWN;
|
||||
|
||||
// Wait startup time, for F042 and F070, this is <= 1 us.
|
||||
for(uint32_t i = 0; i<200; i++) // should be a few us
|
||||
{
|
||||
@@ -250,8 +242,9 @@ void dcd_init (uint8_t rhport)
|
||||
}
|
||||
USB->CNTR = 0; // Enable USB
|
||||
|
||||
#ifndef STM32G0 // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address
|
||||
USB->BTABLE = DCD_STM32_BTABLE_BASE;
|
||||
|
||||
#endif
|
||||
USB->ISTR = 0; // Clear pending interrupts
|
||||
|
||||
// Reset endpoints to disabled
|
||||
@@ -312,7 +305,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
}
|
||||
else
|
||||
{
|
||||
USB->CNTR &= (uint16_t) ~USB_CNTR_SOFM;
|
||||
USB->CNTR &= ~USB_CNTR_SOFM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,6 +351,13 @@ void dcd_int_enable (uint8_t rhport)
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
NVIC_EnableIRQ(USBWakeUp_IRQn);
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
|
||||
#ifdef STM32G0B0xx
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#else
|
||||
NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
|
||||
#endif
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
|
||||
NVIC_EnableIRQ(USB_HP_IRQn);
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
@@ -408,6 +408,13 @@ void dcd_int_disable(uint8_t rhport)
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
NVIC_DisableIRQ(USBWakeUp_IRQn);
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
|
||||
#ifdef STM32G0B0xx
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
#else
|
||||
NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
|
||||
#endif
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
|
||||
NVIC_DisableIRQ(USB_HP_IRQn);
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
@@ -439,7 +446,7 @@ void dcd_remote_wakeup(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
USB->CNTR |= (uint16_t) USB_CNTR_RESUME;
|
||||
USB->CNTR |= USB_CNTR_RESUME;
|
||||
remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms.
|
||||
}
|
||||
|
||||
@@ -470,7 +477,6 @@ static void dcd_handle_bus_reset(void)
|
||||
//__IO uint16_t * const epreg = &(EPREG(0));
|
||||
USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag
|
||||
|
||||
|
||||
for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
|
||||
{
|
||||
// Clear all EPREG (or maybe this is automatic? I'm not sure)
|
||||
@@ -540,9 +546,6 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
|
||||
|
||||
if((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */
|
||||
{
|
||||
// The setup_received function uses memcpy, so this must first copy the setup data into
|
||||
// user memory, to allow for the 32-bit access that memcpy performs.
|
||||
uint8_t userMemBuf[8];
|
||||
uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex);
|
||||
/* Get SETUP Packet*/
|
||||
if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again.
|
||||
@@ -550,8 +553,15 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
|
||||
// Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
|
||||
pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
|
||||
pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
|
||||
dcd_read_packet_memory(userMemBuf, *pcd_ep_rx_address_ptr(USB,EPindex), 8);
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
|
||||
#else
|
||||
// The setup_received function uses memcpy, so this must first copy the setup data into
|
||||
// user memory, to allow for the 32-bit access that memcpy performs.
|
||||
uint8_t userMemBuf[8];
|
||||
dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB,EPindex), 8);
|
||||
dcd_event_setup_received(0, (uint8_t*)userMemBuf, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -574,7 +584,7 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
|
||||
|
||||
if (count != 0U)
|
||||
{
|
||||
uint16_t addr = *pcd_ep_rx_address_ptr(USB, EPindex);
|
||||
uint16_t addr = pcd_get_ep_rx_address(USB, EPindex);
|
||||
|
||||
if (xfer->ff)
|
||||
{
|
||||
@@ -657,13 +667,13 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
|
||||
/* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
|
||||
if(int_status & USB_ISTR_SOF) {
|
||||
clear_istr_bits(USB_ISTR_SOF);
|
||||
USB->ISTR &=~USB_ISTR_SOF;
|
||||
dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
|
||||
}
|
||||
|
||||
if(int_status & USB_ISTR_RESET) {
|
||||
// USBRST is start of reset.
|
||||
clear_istr_bits(USB_ISTR_RESET);
|
||||
USB->ISTR &=~USB_ISTR_RESET;
|
||||
dcd_handle_bus_reset();
|
||||
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
|
||||
return; // Don't do the rest of the things here; perhaps they've been cleared?
|
||||
@@ -678,9 +688,10 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
|
||||
if (int_status & USB_ISTR_WKUP)
|
||||
{
|
||||
reg16_clear_bits(&USB->CNTR, USB_CNTR_LPMODE);
|
||||
reg16_clear_bits(&USB->CNTR, USB_CNTR_FSUSP);
|
||||
clear_istr_bits(USB_ISTR_WKUP);
|
||||
USB->CNTR &= ~USB_CNTR_LPMODE;
|
||||
USB->CNTR &= ~USB_CNTR_FSUSP;
|
||||
|
||||
USB->ISTR &=~USB_ISTR_WKUP;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
|
||||
@@ -694,20 +705,20 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
USB->CNTR |= USB_CNTR_LPMODE;
|
||||
|
||||
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
|
||||
clear_istr_bits(USB_ISTR_SUSP);
|
||||
USB->ISTR &=~USB_ISTR_SUSP;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
|
||||
if(int_status & USB_ISTR_ESOF) {
|
||||
if(remoteWakeCountdown == 1u)
|
||||
{
|
||||
USB->CNTR &= (uint16_t)(~USB_CNTR_RESUME);
|
||||
USB->CNTR &= ~USB_CNTR_RESUME;
|
||||
}
|
||||
if(remoteWakeCountdown > 0u)
|
||||
{
|
||||
remoteWakeCountdown--;
|
||||
}
|
||||
clear_istr_bits(USB_ISTR_ESOF);
|
||||
USB->ISTR &=~USB_ISTR_ESOF;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,8 +739,8 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re
|
||||
uint8_t const dev_addr = (uint8_t) request->wValue;
|
||||
|
||||
// Setting new address after the whole request is complete
|
||||
reg16_clear_bits(&USB->DADDR, USB_DADDR_ADD);
|
||||
USB->DADDR = (uint16_t)(USB->DADDR | dev_addr); // leave the enable bit set
|
||||
USB->DADDR &= ~USB_DADDR_ADD;
|
||||
USB->DADDR |= dev_addr; // leave the enable bit set
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,7 +767,7 @@ static void dcd_pma_alloc_reset(void)
|
||||
*
|
||||
* During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
|
||||
*/
|
||||
static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length)
|
||||
static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
|
||||
{
|
||||
xfer_ctl_t* epXferCtl = xfer_ctl_ptr(ep_addr);
|
||||
|
||||
@@ -768,6 +779,13 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length)
|
||||
return epXferCtl->pma_ptr;
|
||||
}
|
||||
|
||||
// Ensure allocated buffer is aligned
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
length = (length + 3) & ~0x03;
|
||||
#else
|
||||
length = (length + 1) & ~0x01;
|
||||
#endif
|
||||
|
||||
open_ep_count++;
|
||||
|
||||
uint16_t addr = ep_buf_ptr;
|
||||
@@ -778,7 +796,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length)
|
||||
|
||||
epXferCtl->pma_ptr = addr;
|
||||
epXferCtl->pma_alloc_size = length;
|
||||
//TU_LOG2("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
|
||||
//TU_LOG1("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
@@ -931,14 +949,14 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc
|
||||
|
||||
if( (dir == TUSB_DIR_IN) || (wType == USB_EP_ISOCHRONOUS) )
|
||||
{
|
||||
*pcd_ep_tx_address_ptr(USB, ep_idx) = pma_addr;
|
||||
pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
|
||||
pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size);
|
||||
pcd_clear_tx_dtog(USB, ep_idx);
|
||||
}
|
||||
|
||||
if( (dir == TUSB_DIR_OUT) || (wType == USB_EP_ISOCHRONOUS) )
|
||||
{
|
||||
*pcd_ep_rx_address_ptr(USB, ep_idx) = pma_addr;
|
||||
pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
|
||||
pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size);
|
||||
pcd_clear_rx_dtog(USB, ep_idx);
|
||||
}
|
||||
@@ -1018,8 +1036,8 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
|
||||
|
||||
pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS);
|
||||
|
||||
*pcd_ep_tx_address_ptr(USB, ep_idx) = pma_addr;
|
||||
*pcd_ep_rx_address_ptr(USB, ep_idx) = pma_addr;
|
||||
pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
|
||||
pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1069,7 +1087,7 @@ static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
|
||||
}
|
||||
|
||||
uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix);
|
||||
uint16_t addr_ptr = *pcd_ep_tx_address_ptr(USB,ep_ix);
|
||||
uint16_t addr_ptr = pcd_get_ep_tx_address(USB, ep_ix);
|
||||
|
||||
if (xfer->ff)
|
||||
{
|
||||
@@ -1203,6 +1221,40 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
|
||||
{
|
||||
const uint8_t* srcVal = src;
|
||||
volatile uint32_t* dst32 = (volatile uint32_t*)(USB_PMAADDR + dst);
|
||||
|
||||
for (uint32_t n = wNBytes / 4; n > 0; --n) {
|
||||
*dst32++ = tu_unaligned_read32(srcVal);
|
||||
srcVal += 4;
|
||||
}
|
||||
|
||||
wNBytes = wNBytes & 0x03;
|
||||
if (wNBytes)
|
||||
{
|
||||
uint32_t wrVal = *srcVal;
|
||||
wNBytes--;
|
||||
|
||||
if (wNBytes)
|
||||
{
|
||||
wrVal |= *++srcVal << 8;
|
||||
wNBytes--;
|
||||
|
||||
if (wNBytes)
|
||||
{
|
||||
wrVal |= *++srcVal << 16;
|
||||
}
|
||||
}
|
||||
|
||||
*dst32 = wrVal;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
// Packet buffer access can only be 8- or 16-bit.
|
||||
/**
|
||||
* @brief Copy a buffer from user memory area to packet memory area (PMA).
|
||||
@@ -1214,7 +1266,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
|
||||
* @param wNBytes no. of bytes to be copied.
|
||||
* @retval None
|
||||
*/
|
||||
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes)
|
||||
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
|
||||
{
|
||||
uint32_t n = (uint32_t)wNBytes >> 1U;
|
||||
uint16_t temp1, temp2;
|
||||
@@ -1237,7 +1289,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, si
|
||||
srcVal++;
|
||||
}
|
||||
|
||||
if (wNBytes & 0x01)
|
||||
if (wNBytes)
|
||||
{
|
||||
temp1 = *srcVal;
|
||||
*pdwVal = temp1;
|
||||
@@ -1245,6 +1297,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, si
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Copy from FIFO to packet memory area (PMA).
|
||||
@@ -1263,7 +1316,37 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
|
||||
|
||||
// We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
|
||||
// last lin byte will be combined with wrapped part
|
||||
// To ensure PMA is always access 16bit aligned (dst aligned to 16 bit)
|
||||
// To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
if((cnt_lin & 0x03) && cnt_wrap)
|
||||
{
|
||||
// Copy first linear part
|
||||
dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x03);
|
||||
dst += cnt_lin &~0x03;
|
||||
|
||||
// Copy last linear bytes & first wrapped bytes to buffer
|
||||
uint32_t i;
|
||||
uint8_t tmp[4];
|
||||
for (i = 0; i < (cnt_lin & 0x03); i++)
|
||||
{
|
||||
tmp[i] = ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i];
|
||||
}
|
||||
uint32_t wCnt = cnt_wrap;
|
||||
for (; i < 4 && wCnt > 0; i++, wCnt--)
|
||||
{
|
||||
tmp[i] = *(uint8_t*)info.ptr_wrap;
|
||||
info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
|
||||
}
|
||||
|
||||
// Write unaligned buffer
|
||||
dcd_write_packet_memory(dst, &tmp, 4);
|
||||
dst += 4;
|
||||
|
||||
// Copy rest of wrapped byte
|
||||
if (wCnt)
|
||||
dcd_write_packet_memory(dst, info.ptr_wrap, wCnt);
|
||||
}
|
||||
#else
|
||||
if((cnt_lin & 0x01) && cnt_wrap)
|
||||
{
|
||||
// Copy first linear part
|
||||
@@ -1278,6 +1361,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
|
||||
// Copy rest of wrapped byte
|
||||
dcd_write_packet_memory(dst, ((uint8_t*)info.ptr_wrap) + 1, cnt_wrap - 1);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// Copy linear part
|
||||
@@ -1296,13 +1380,47 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
|
||||
{
|
||||
uint8_t* dstVal = dst;
|
||||
volatile uint32_t* src32 = (volatile uint32_t*)(USB_PMAADDR + src);
|
||||
|
||||
for (uint32_t n = wNBytes / 4; n > 0; --n) {
|
||||
tu_unaligned_write32(dstVal, *src32++);
|
||||
dstVal += 4;
|
||||
}
|
||||
|
||||
wNBytes = wNBytes & 0x03;
|
||||
if (wNBytes)
|
||||
{
|
||||
uint32_t rdVal = *src32;
|
||||
|
||||
*dstVal = tu_u32_byte0(rdVal);
|
||||
wNBytes--;
|
||||
|
||||
if (wNBytes)
|
||||
{
|
||||
*++dstVal = tu_u32_byte1(rdVal);
|
||||
wNBytes--;
|
||||
|
||||
if (wNBytes)
|
||||
{
|
||||
*++dstVal = tu_u32_byte2(rdVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @brief Copy a buffer from packet memory area (PMA) to user memory area.
|
||||
* Uses byte-access of system memory and 16-bit access of packet memory
|
||||
* @param wNBytes no. of bytes to be copied.
|
||||
* @retval None
|
||||
*/
|
||||
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes)
|
||||
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
|
||||
{
|
||||
uint32_t n = (uint32_t)wNBytes >> 1U;
|
||||
// The GCC optimizer will combine access to 32-bit sizes if we let it. Force
|
||||
@@ -1329,6 +1447,7 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wN
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Copy a buffer from user packet memory area (PMA) to FIFO.
|
||||
@@ -1346,9 +1465,39 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
|
||||
uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
|
||||
uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap);
|
||||
|
||||
|
||||
// We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
|
||||
// last lin byte will be combined with wrapped part
|
||||
// To ensure PMA is always access 16bit aligned (src aligned to 16 bit)
|
||||
// To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
if((cnt_lin & 0x03) && cnt_wrap)
|
||||
{
|
||||
// Copy first linear part
|
||||
dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x03);
|
||||
src += cnt_lin &~0x03;
|
||||
|
||||
// Copy last linear bytes & first wrapped bytes
|
||||
uint8_t tmp[4];
|
||||
dcd_read_packet_memory(tmp, src, 4);
|
||||
src += 4;
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < (cnt_lin & 0x03); i++)
|
||||
{
|
||||
((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i] = tmp[i];
|
||||
}
|
||||
uint32_t wCnt = cnt_wrap;
|
||||
for (; i < 4 && wCnt > 0; i++, wCnt--)
|
||||
{
|
||||
*(uint8_t*)info.ptr_wrap = tmp[i];
|
||||
info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
|
||||
}
|
||||
|
||||
// Copy rest of wrapped byte
|
||||
if (wCnt)
|
||||
dcd_read_packet_memory(info.ptr_wrap, src, wCnt);
|
||||
}
|
||||
#else
|
||||
if((cnt_lin & 0x01) && cnt_wrap)
|
||||
{
|
||||
// Copy first linear part
|
||||
@@ -1356,16 +1505,17 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
|
||||
src += cnt_lin &~0x01;
|
||||
|
||||
// Copy last linear byte & first wrapped byte
|
||||
uint16_t tmp;
|
||||
dcd_read_packet_memory(&tmp, src, 2);
|
||||
|
||||
((uint8_t*)info.ptr_lin)[cnt_lin - 1] = (uint8_t)tmp;
|
||||
((uint8_t*)info.ptr_wrap)[0] = (uint8_t)(tmp >> 8U);
|
||||
uint8_t tmp[2];
|
||||
dcd_read_packet_memory(tmp, src, 2);
|
||||
src += 2;
|
||||
|
||||
((uint8_t*)info.ptr_lin)[cnt_lin - 1] = tmp[0];
|
||||
((uint8_t*)info.ptr_wrap)[0] = tmp[1];
|
||||
|
||||
// Copy rest of wrapped byte
|
||||
dcd_read_packet_memory(((uint8_t*)info.ptr_wrap) + 1, src, cnt_wrap - 1);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// Copy linear part
|
||||
|
||||
@@ -82,6 +82,34 @@
|
||||
#include "stm32g4xx.h"
|
||||
#define PMA_LENGTH (1024u)
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
|
||||
#include "stm32g0xx.h"
|
||||
#define PMA_32BIT_ACCESS
|
||||
#define PMA_LENGTH (2048u)
|
||||
#undef USB_PMAADDR
|
||||
#define USB_PMAADDR USB_DRD_PMAADDR
|
||||
#define USB_TypeDef USB_DRD_TypeDef
|
||||
#define EP0R CHEP0R
|
||||
#define USB_EP_CTR_RX USB_EP_VTRX
|
||||
#define USB_EP_CTR_TX USB_EP_VTTX
|
||||
#define USB_EP_T_FIELD USB_CHEP_UTYPE
|
||||
#define USB_EPREG_MASK USB_CHEP_REG_MASK
|
||||
#define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
|
||||
#define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
|
||||
#define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
|
||||
#define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
|
||||
#define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
|
||||
#define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
|
||||
#define USB_EPRX_STAT USB_CH_RX_VALID
|
||||
#define USB_EPKIND_MASK USB_EP_KIND_MASK
|
||||
#define USB USB_DRD_FS
|
||||
#define USB_CNTR_FRES USB_CNTR_USBRST
|
||||
#define USB_CNTR_RESUME USB_CNTR_L2RES
|
||||
#define USB_ISTR_EP_ID USB_ISTR_IDN
|
||||
#define USB_EPADDR_FIELD USB_CHEP_ADDR
|
||||
#define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
|
||||
#define USB_CNTR_FSUSP USB_CNTR_SUSPEN
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
|
||||
#include "stm32wbxx.h"
|
||||
#define PMA_LENGTH (1024u)
|
||||
@@ -113,15 +141,31 @@
|
||||
#define PMA_STRIDE (1u)
|
||||
#endif
|
||||
|
||||
// And for type-safety create a new macro for the volatile address of PMAADDR
|
||||
// For type-safety create a new macro for the volatile address of PMAADDR
|
||||
// The compiler should warn us if we cast it to a non-volatile type?
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
|
||||
#else
|
||||
// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
|
||||
static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
|
||||
|
||||
// prototypes
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx);
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx);
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue);
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
|
||||
{
|
||||
size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
|
||||
total_word_offset *= PMA_STRIDE;
|
||||
return &(pma[total_word_offset]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Aligned buffer size according to hardware */
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size)
|
||||
@@ -139,13 +183,24 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si
|
||||
/* SetENDPOINT */
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue)
|
||||
{
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
__O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
|
||||
*reg = wRegValue;
|
||||
#else
|
||||
__O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
|
||||
*reg = (uint16_t)wRegValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* GetENDPOINT */
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
__I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
|
||||
#else
|
||||
__I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
|
||||
#endif
|
||||
return *reg;
|
||||
}
|
||||
|
||||
@@ -195,34 +250,24 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx,
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
|
||||
#else
|
||||
__I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
|
||||
return *regPtr & 0x3ffU;
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
|
||||
#else
|
||||
__I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
|
||||
return *regPtr & 0x3ffU;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets counter of rx buffer with no. of blocks.
|
||||
* @param dwReg Register
|
||||
* @param wCount Counter.
|
||||
* @param wNBlocks no. of Blocks.
|
||||
* @retval None
|
||||
*/
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_cnt_reg(__O uint16_t * pdwReg, size_t wCount)
|
||||
{
|
||||
/* We assume that the buffer size is already aligned to hardware requirements. */
|
||||
uint16_t blocksize = (wCount > 62) ? 1 : 0;
|
||||
uint16_t numblocks = wCount / (blocksize ? 32 : 2);
|
||||
|
||||
/* There should be no remainder in the above calculation */
|
||||
TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/);
|
||||
|
||||
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
|
||||
*pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,57 +286,103 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,
|
||||
pcd_set_endpoint(USBx, bEpIdx,regVal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
|
||||
total_word_offset *= PMA_STRIDE;
|
||||
return &(pma[total_word_offset]);
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
return pma32[2*bEpIdx] & 0x0000FFFFu ;
|
||||
#else
|
||||
return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pointers to the PMA table entries (using the ARM address space)
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_address_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u);
|
||||
}
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u);
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
|
||||
#else
|
||||
return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u);
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_address_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u);
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
|
||||
#else
|
||||
*pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
|
||||
{
|
||||
return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u);
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
|
||||
#else
|
||||
*pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
|
||||
#else
|
||||
__IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
|
||||
*reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
|
||||
#else
|
||||
__IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
|
||||
*reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t blocksize, uint32_t numblocks)
|
||||
{
|
||||
__IO uint16_t *pdwReg = pcd_ep_tx_cnt_ptr((USBx),(bEpIdx));
|
||||
wCount = pcd_aligned_buffer_size(wCount);
|
||||
pcd_set_ep_cnt_reg(pdwReg, wCount);
|
||||
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
|
||||
#ifdef PMA_32BIT_ACCESS
|
||||
(void) USBx;
|
||||
pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
|
||||
#else
|
||||
__IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u);
|
||||
*pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10);
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount)
|
||||
{
|
||||
__IO uint16_t *pdwReg = pcd_ep_rx_cnt_ptr((USBx),(bEpIdx));
|
||||
wCount = pcd_aligned_buffer_size(wCount);
|
||||
pcd_set_ep_cnt_reg(pdwReg, wCount);
|
||||
|
||||
/* We assume that the buffer size is already aligned to hardware requirements. */
|
||||
uint16_t blocksize = (wCount > 62) ? 1 : 0;
|
||||
uint16_t numblocks = wCount / (blocksize ? 32 : 2);
|
||||
|
||||
/* There should be no remainder in the above calculation */
|
||||
TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/);
|
||||
|
||||
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
|
||||
pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
|
||||
{
|
||||
pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -439,6 +439,10 @@ char const* const tu_str_std_request[] =
|
||||
"Synch Frame"
|
||||
};
|
||||
|
||||
char const* const tu_str_xfer_result[] = {
|
||||
"OK", "FAILED", "STALLED", "TIMEOUT"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void dump_str_line(uint8_t const* buf, uint16_t count)
|
||||
|
||||
@@ -166,6 +166,10 @@
|
||||
// WCH
|
||||
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
|
||||
|
||||
|
||||
// NXP LPC MCX
|
||||
#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
|
||||
|
||||
// Helper to check if configured MCU is one of listed
|
||||
// Apply _TU_CHECK_MCU with || as separator to list of input
|
||||
#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
|
||||
@@ -274,7 +278,7 @@
|
||||
// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler
|
||||
// to generate unaligned access code.
|
||||
// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM
|
||||
#if TUD_OPT_HIGH_SPEED && (CFG_TUSB_MCU == OPT_MCU_LPC54XXX || CFG_TUSB_MCU == OPT_MCU_LPC55XX)
|
||||
#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX)
|
||||
#define TUP_MCU_STRICT_ALIGN 1
|
||||
#else
|
||||
#define TUP_MCU_STRICT_ALIGN 0
|
||||
|
||||
Reference in New Issue
Block a user