Merge pull request #10 from hathach/devlocal

merge for local develop
This commit is contained in:
hathach 2018-11-20 11:06:16 +07:00 committed by GitHub
commit aa71b8fd87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 853 additions and 815 deletions

3
.gitmodules vendored
View File

@ -4,6 +4,3 @@
[submodule "hw/mcu/microchip/samd/asf4"]
path = hw/mcu/microchip/samd/asf4
url = https://github.com/adafruit/asf4.git
[submodule "hw/mcu/microchip/samd/samd-peripherals"]
path = hw/mcu/microchip/samd/samd-peripherals
url = https://github.com/adafruit/samd-peripherals.git

View File

@ -19,7 +19,7 @@
arm_target_device_name="nRF52840_xxAA"
arm_target_interface_type="SWD"
build_treat_warnings_as_errors="Yes"
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;BOARD_PCA10056"
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;BOARD_PCA10056;CFG_TUSB_MCU=OPT_MCU_NRF5X"
c_user_include_directories="../src;$(rootDir)/hw/cmsis/Include;$(rootDir)/hw;$(rootDir)/src;$(nrfxDir)/..;$(nrfxDir);$(nrfxDir)/mdk;$(nrfxDir)/hal;$(nrfxDir)/drivers/include"
debug_register_definition_file="ses_nrf5x/nrf52840_Registers.xml"
debug_target_connection="J-Link"
@ -118,8 +118,8 @@
arm_target_device_name="ATSAMD51J19"
arm_target_interface_type="SWD"
build_treat_warnings_as_errors="Yes"
c_preprocessor_definitions="__SAME51_FAMILY;__SAMD51J19A__;ARM_MATH_CM4;FLASH_PLACEMENT=1;BOARD_METRO_M4_EXPRESS;USE_SIMPLE_ASSERT"
c_user_include_directories="../src;$(rootDir)/hw/cmsis/Include;$(rootDir)/hw;$(rootDir)/src;$(asf4Dir);$(asf4Dir)/include;$(asf4Dir)/hri;$(asf4Dir)/hal/include;$(asf4Dir)/hal/utils/include;$(asf4Dir)/hpl/port"
c_preprocessor_definitions="__SAME51_FAMILY;__SAMD51J19A__;ARM_MATH_CM4;FLASH_PLACEMENT=1;USE_SIMPLE_ASSERT;BOARD_METRO_M4_EXPRESS;CIRCUITPY_GCLK_INIT_1ST=0xffff;CFG_TUSB_MCU=OPT_MCU_SAMD51"
c_user_include_directories="../src;$(rootDir)/hw/cmsis/Include;$(rootDir)/hw;$(rootDir)/src;$(asf4Dir);$(asf4Dir)/include;$(asf4Dir)/config;$(asf4Dir)/hri;$(asf4Dir)/hal/include;$(asf4Dir)/hal/utils/include;$(asf4Dir)/hpl/port;$(asf4Dir)/hpl/gclk"
debug_register_definition_file="ses_samd51/ATSAME51J19A_Registers.xml"
debug_target_connection="J-Link"
gcc_entry_point="Reset_Handler"
@ -156,6 +156,23 @@
<folder Name="gcc">
<file file_name="../../../../hw/mcu/microchip/samd/asf4/samd51/gcc/system_samd51.c" />
</folder>
<folder Name="hpl">
<folder Name="core">
<file file_name="../../../../../../../adafruit/circuitpython/thach_cp/ports/atmel-samd/asf4/samd51/hpl/core/hpl_init.c" />
</folder>
<folder Name="osc32kctrl">
<file file_name="../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c" />
</folder>
<folder Name="oscctrl">
<file file_name="../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c" />
</folder>
<folder Name="mclk">
<file file_name="../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/mclk/hpl_mclk.c" />
</folder>
<folder Name="gclk">
<file file_name="../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/gclk/hpl_gclk.c" />
</folder>
</folder>
</folder>
</folder>
</folder>
@ -163,15 +180,6 @@
</folder>
</folder>
<configuration Name="Debug" build_treat_warnings_as_errors="Yes" />
<folder Name="src">
<file file_name="../src/main.c" />
<folder Name="segger_rtt">
<file file_name="../src/segger_rtt/SEGGER_RTT.c" />
<file file_name="../src/segger_rtt/SEGGER_RTT.h" />
<file file_name="../src/segger_rtt/SEGGER_RTT_Conf.h" />
<file file_name="../src/segger_rtt/SEGGER_RTT_SES.c" />
</folder>
</folder>
<folder Name="ses_samd51">
<file file_name="ses_samd51/ATSAME51J19A_MemoryMap.xml" />
<file file_name="ses_samd51/ATSAME51J19A_Registers.xml" />
@ -181,5 +189,11 @@
<file file_name="ses_samd51/SAME51_Startup.s" />
<file file_name="ses_samd51/SAME51_Target.js" />
</folder>
<folder
Name="src"
exclude=""
filter="*.c;*.h"
path="../src"
recurse="Yes" />
</project>
</solution>

View File

@ -36,9 +36,6 @@
*/
/**************************************************************************/
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -47,15 +44,39 @@
#include "tusb.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void print_greeting(void);
void led_blinking_task(void);
/*------------- MAIN -------------*/
int main(void)
{
board_init();
print_greeting();
tusb_init();
while (1)
{
tusb_task();
led_blinking_task();
#if CFG_TUD_CDC
extern void virtual_com_task(void);
virtual_com_task();
#endif
#if CFG_TUD_HID
extern void usb_hid_task(void);
usb_hid_task();
#endif
}
return 0;
}
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
@ -90,8 +111,6 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
tud_cdc_write_str("tinyusb usb cdc\n");
}
}
#else
#define virtual_com_task()
#endif
//--------------------------------------------------------------------+
@ -151,32 +170,8 @@ void tud_hid_generic_set_report_cb(uint8_t report_id, hid_report_type_t report_t
{
// TODO not Implemented
}
#else
#define usb_hid_task()
#endif
/*------------- MAIN -------------*/
int main(void)
{
board_init();
print_greeting();
tusb_init();
while (1)
{
tusb_task();
led_blinking_task();
virtual_com_task();
usb_hid_task();
}
return 0;
}
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+

View File

@ -48,7 +48,12 @@
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUSB_MCU OPT_MCU_NRF5X
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU should be defined using compiler flags
#endif
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#define CFG_TUSB_DEBUG 2
@ -82,7 +87,7 @@
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_HID_KEYBOARD 0

View File

@ -19,7 +19,7 @@
arm_target_device_name="nRF52840_xxAA"
arm_target_interface_type="SWD"
build_treat_warnings_as_errors="Yes"
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;BOARD_PCA10056"
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;BOARD_PCA10056;CFG_TUSB_MCU=OPT_MCU_NRF5X"
c_user_include_directories="../src;$(tusbDir)/hw/cmsis/Include;$(tusbDir)/hw;$(tusbDir)/src;$(nrfxDir)/..;$(nrfxDir);$(nrfxDir)/mdk;$(nrfxDir)/hal;$(nrfxDir)/drivers/include;$(freertosDir)/Source/include;$(freertosDir)/Source/portable/GCC/ARM_CM4F"
debug_register_definition_file="$(ProjectDir)/nrf52840_Registers.xml"
debug_target_connection="J-Link"

View File

@ -37,7 +37,12 @@
/**************************************************************************/
#include "bsp/board.h"
#include "sam.h"
#include "hal/include/hal_gpio.h"
#include "hal/include/hal_init.h"
#include "peripheral_clk_config.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@ -46,6 +51,8 @@
void board_init(void)
{
init_mcu();
gpio_set_pin_direction(BOARD_LED0, GPIO_DIRECTION_OUT);
gpio_set_pin_level(BOARD_LED0, 1-LED_STATE_ON);
@ -53,6 +60,24 @@ void board_init(void)
// Tick init
SysTick_Config(SystemCoreClock/1000);
#endif
/* USB Clock init
* The USB module requires a GCLK_USB of 48 MHz ~ 0.25% clock
* for low speed and full speed operation. */
hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, CONF_GCLK_USB_SRC | GCLK_PCHCTRL_CHEN);
hri_mclk_set_AHBMASK_USB_bit(MCLK);
hri_mclk_set_APBBMASK_USB_bit(MCLK);
// USB Pin Init
gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
gpio_set_pin_level(PIN_PA24, false);
gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
gpio_set_pin_level(PIN_PA25, false);
gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM);
gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP);
}
void board_led_control(uint32_t led_id, bool state)
@ -76,4 +101,4 @@ uint32_t tusb_hal_millis(void)
{
return board_tick2ms(system_ticks);
}
#endif
#endif

@ -1 +0,0 @@
Subproject commit f20fcf642b5654ee68d7d551ea7db39716ef83bf

View File

@ -45,7 +45,6 @@
// INCLUDE
//--------------------------------------------------------------------+
#include "cdc_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -63,7 +62,7 @@ typedef struct
/*------------- From this point, data is not cleared by bus reset -------------*/
char wanted_char;
CFG_TUSB_MEM_ALIGN cdc_line_coding_t line_coding;
cdc_line_coding_t line_coding;
// FIFO
tu_fifo_t rx_ff;
@ -199,23 +198,23 @@ void cdcd_init(void)
for(uint8_t i=0; i<CFG_TUD_CDC; i++)
{
cdcd_interface_t* ser = &_cdcd_itf[i];
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
ser->wanted_char = -1;
p_cdc->wanted_char = -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
ser->line_coding.bit_rate = 115200;
ser->line_coding.stop_bits = 0;
ser->line_coding.parity = 0;
ser->line_coding.data_bits = 8;
p_cdc->line_coding.bit_rate = 115200;
p_cdc->line_coding.stop_bits = 0;
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
// config fifo
tu_fifo_config(&ser->rx_ff, ser->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, true);
tu_fifo_config(&ser->tx_ff, ser->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false);
tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, true);
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&ser->rx_ff, osal_mutex_create(&ser->rx_ff_mutex));
tu_fifo_config_mutex(&ser->tx_ff, osal_mutex_create(&ser->tx_ff_mutex));
tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
#endif
}
}
@ -299,57 +298,64 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
return TUSB_ERROR_NONE;
}
void cdcd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool cdcd_control_request_complete(uint8_t rhport, tusb_control_request_t const * request)
{
//------------- Class Specific Request -------------//
if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return;
TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
// TODO Support multiple interfaces
uint8_t const itf = 0;
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
// Invoke callback
if (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) {
if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest )
{
if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
}
return true;
}
tusb_error_t cdcd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request)
{
//------------- Class Specific Request -------------//
if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
TU_ASSERT(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
// TODO Support multiple interfaces
uint8_t const itf = 0;
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
if ((CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) )
switch ( request->bRequest )
{
uint16_t len = tu_min16(sizeof(cdc_line_coding_t), p_request->wLength);
dcd_edpt_xfer(rhport, 0, (uint8_t*) &p_cdc->line_coding, len);
}
else if ( (CDC_REQUEST_GET_LINE_CODING == p_request->bRequest))
{
uint16_t len = tu_min16(sizeof(cdc_line_coding_t), p_request->wLength);
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, (uint8_t*) &p_cdc->line_coding, len);
}
else if (CDC_REQUEST_SET_CONTROL_LINE_STATE == p_request->bRequest )
{
// CDC PSTN v1.2 section 6.3.12
// Bit 0: Indicates if DTE is present or not.
// This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
// Bit 1: Carrier control for half-duplex modems.
// This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
p_cdc->line_state = (uint8_t) p_request->wValue;
case CDC_REQUEST_SET_LINE_CODING:
usbd_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
break;
// Invoke callback
if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, BIT_TEST_(p_request->wValue, 0), BIT_TEST_(p_request->wValue, 1));
case CDC_REQUEST_GET_LINE_CODING:
usbd_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
break;
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
// CDC PSTN v1.2 section 6.3.12
// Bit 0: Indicates if DTE is present or not.
// This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
// Bit 1: Carrier control for half-duplex modems.
// This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
p_cdc->line_state = (uint8_t) request->wValue;
// Invoke callback
if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, BIT_TEST_(request->wValue, 0), BIT_TEST_(request->wValue, 1));
usbd_control_status(rhport, request);
break;
default: return false; // stall unsupported request
}
else
{
return TUSB_ERROR_FAILED; // stall unsupported request
}
return TUSB_ERROR_NONE;
return true;
}
tusb_error_t cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)

View File

@ -114,8 +114,8 @@ ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_li
void cdcd_init (void);
tusb_error_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t cdcd_control_request (uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
void cdcd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
bool cdcd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t cdcd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void cdcd_reset (uint8_t rhport);

View File

@ -89,9 +89,9 @@ tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
return TUSB_ERROR_NONE;
}
tusb_error_t cusd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
bool cusd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
return false;
}
tusb_error_t cusd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes)

View File

@ -64,8 +64,8 @@
void cusd_init(void);
tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t cusd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
void cusd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool cusd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
bool cusd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t cusd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void cusd_reset(uint8_t rhport);
#endif

View File

@ -46,7 +46,6 @@
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "hid_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -403,110 +402,112 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
return TUSB_ERROR_NONE;
}
tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid, TUSB_ERROR_FAILED);
TU_ASSERT(p_hid);
//------------- STD Request -------------//
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
{
//------------- STD Request -------------//
uint8_t const desc_type = tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low (p_request->wValue);
(void) desc_index;
if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
// TODO: Handle zero length packet.
uint16_t remaining_bytes = p_hid->desc_len - bytes_already_sent;
if (remaining_bytes > 64) {
remaining_bytes = 64;
}
memcpy(_shared_control_buffer, p_hid->desc_report + bytes_already_sent, remaining_bytes);
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, remaining_bytes);
usbd_control_xfer(rhport, p_request, p_hid->desc_report, p_hid->desc_len);
}else
{
return TUSB_ERROR_FAILED;
return false; // stall unsupported request
}
}
//------------- Class Specific Request -------------//
else if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
{
if( HID_REQ_CONTROL_GET_REPORT == p_request->bRequest )
//------------- Class Specific Request -------------//
switch( p_request->bRequest )
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
case HID_REQ_CONTROL_GET_REPORT:
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
uint16_t xferlen;
if ( p_hid->get_report_cb )
{
xferlen = p_hid->get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->report_buf, p_request->wLength);
}else
{
// For boot Interface only: re-use report_buf -> report has no change
xferlen = p_request->wLength;
uint16_t xferlen;
if ( p_hid->get_report_cb )
{
xferlen = p_hid->get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->report_buf, p_request->wLength);
}else
{
// For boot Interface only: re-use report_buf -> report has no change
xferlen = p_request->wLength;
}
TU_ASSERT( xferlen > 0 );
usbd_control_xfer(rhport, p_request, p_hid->report_buf, xferlen);
}
break;
TU_ASSERT( xferlen > 0 );
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, xferlen);
}
else if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
{
dcd_edpt_xfer(rhport, 0, _shared_control_buffer, p_request->wLength);
}
else if (HID_REQ_CONTROL_SET_IDLE == p_request->bRequest)
{
// TODO idle rate of report
p_hid->idle_rate = tu_u16_high(p_request->wValue);
}
else if (HID_REQ_CONTROL_GET_IDLE == p_request->bRequest)
{
// TODO idle rate of report
_shared_control_buffer[0] = p_hid->idle_rate;
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}
else if (HID_REQ_CONTROL_GET_PROTOCOL == p_request->bRequest )
{
_shared_control_buffer[0] = 1-p_hid->boot_protocol; // 0 is Boot, 1 is Report protocol
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}
else if (HID_REQ_CONTROL_SET_PROTOCOL == p_request->bRequest )
{
p_hid->boot_protocol = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
}else
{
return TUSB_ERROR_FAILED;
case HID_REQ_CONTROL_SET_REPORT:
usbd_control_xfer(rhport, p_request, p_hid->report_buf, p_request->wLength);
break;
case HID_REQ_CONTROL_SET_IDLE:
// TODO idle rate of report
p_hid->idle_rate = tu_u16_high(p_request->wValue);
usbd_control_status(rhport, p_request);
break;
case HID_REQ_CONTROL_GET_IDLE:
// TODO idle rate of report
usbd_control_xfer(rhport, p_request, &p_hid->idle_rate, 1);
break;
case HID_REQ_CONTROL_GET_PROTOCOL:
{
uint8_t protocol = 1-p_hid->boot_protocol; // 0 is Boot, 1 is Report protocol
usbd_control_xfer(rhport, p_request, &protocol, 1);
}
break;
case HID_REQ_CONTROL_SET_PROTOCOL:
p_hid->boot_protocol = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
usbd_control_status(rhport, p_request);
break;
default: return false; // stall unsupported request
}
}else
{
return TUSB_ERROR_FAILED;
return false; // stall unsupported request
}
return TUSB_ERROR_NONE;
return true;
}
void hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
if (p_hid == NULL) {
return;
}
TU_ASSERT(p_hid);
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
p_request->bRequest == HID_REQ_CONTROL_SET_REPORT)
{
if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
if ( p_hid->set_report_cb )
{
p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, _shared_control_buffer, p_request->wLength);
}
if ( p_hid->set_report_cb )
{
p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->report_buf, p_request->wLength);
}
}
return true;
}
tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes)

View File

@ -378,8 +378,8 @@ ATTR_WEAK void tud_hid_mouse_set_report_cb(uint8_t report_id, hid_report_type_t
void hidd_init(void);
tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
void hidd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
bool hidd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void hidd_reset(uint8_t rhport);
@ -390,3 +390,4 @@ void hidd_reset(uint8_t rhport);
#endif
#endif /* _TUSB_HID_DEVICE_H_ */

View File

@ -47,7 +47,6 @@
#include "common/tusb_common.h"
#include "msc_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -60,7 +59,31 @@ enum
MSC_STAGE_STATUS
};
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN mscd_interface_t _mscd_itf;
typedef struct {
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
// uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
//#endif
CFG_TUSB_MEM_ALIGN msc_csw_t csw;
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
// Bulk Only Transfer (BOT) Protocol
uint8_t stage;
uint32_t total_len;
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
// Sense Response Data
uint8_t sense_key;
uint8_t add_sense_code;
uint8_t add_sense_qualifier;
}mscd_interface_t;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
//--------------------------------------------------------------------+
@ -147,29 +170,39 @@ tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
return TUSB_ERROR_NONE;
}
tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
TU_ASSERT(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS, TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT);
TU_ASSERT(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
if(MSC_REQ_RESET == p_request->bRequest)
switch ( p_request->bRequest )
{
// TODO: Actually reset.
case MSC_REQ_RESET:
// TODO: Actually reset interface.
usbd_control_status(rhport, p_request);
break;
case MSC_REQ_GET_MAX_LUN:
{
// returned MAX LUN is minus 1 by specs
uint8_t maxlun = CFG_TUD_MSC_MAXLUN-1;
usbd_control_xfer(rhport, p_request, &maxlun, 1);
}
break;
default: return false; // stall unsupported request
}
else if (MSC_REQ_GET_MAX_LUN == p_request->bRequest)
{
// returned MAX LUN is minus 1 by specs
_shared_control_buffer[0] = CFG_TUD_MSC_MAXLUN-1;
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}else
{
return TUSB_ERROR_FAILED; // stall unsupported request
}
return TUSB_ERROR_NONE;
return true;
}
void mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
return;
// nothing to do
return true;
}
// For backwards compatibility we support static block counts.
@ -296,7 +329,7 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
return ret;
}
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, uint8_t event, uint32_t xferred_bytes)
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
msc_cbw_t const * p_cbw = &p_msc->cbw;

View File

@ -81,32 +81,6 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
extern "C" {
#endif
typedef struct {
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
// uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
//#endif
CFG_TUSB_MEM_ALIGN msc_csw_t csw;
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
// Bulk Only Transfer (BOT) Protocol
uint8_t stage;
uint32_t total_len;
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
// Sense Response Data
uint8_t sense_key;
uint8_t add_sense_code;
uint8_t add_sense_qualifier;
}mscd_interface_t;
extern mscd_interface_t _mscd_itf;
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Device Device
@ -198,8 +172,8 @@ ATTR_WEAK bool tud_lun_capacity_cb(uint8_t lun, uint32_t* last_valid_sector, uin
void mscd_init(void);
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
void mscd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void mscd_reset(uint8_t rhport);

View File

@ -203,12 +203,14 @@ typedef enum
TUSB_EVENT_XFER_STALLED,
}tusb_event_t;
enum {
enum
{
DESC_OFFSET_LEN = 0,
DESC_OFFSET_TYPE = 1
};
enum {
enum
{
INTERFACE_INVALID_NUMBER = 0xff
};

View File

@ -1,264 +0,0 @@
/**************************************************************************/
/*!
@file usbd.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#define _TINY_USB_SOURCE_FILE_
#include "tusb.h"
#include "control.h"
#include "device/usbd_pvt.h"
control_t control_state;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _shared_control_buffer[64];
void controld_reset(uint8_t rhport) {
control_state.current_stage = CONTROL_STAGE_SETUP;
}
void controld_init(void) {
}
// Helper to send STATUS (zero length) packet
// Note dir is value of direction bit in setup packet (i.e DATA stage direction)
static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
{
uint8_t ep_addr = 0;
// Invert the direction.
if (dir == TUSB_DIR_OUT) {
ep_addr |= TUSB_DIR_IN_MASK;
}
// status direction is reversed to one in the setup packet
return dcd_edpt_xfer(rhport, ep_addr, NULL, 0);
}
static inline void dcd_control_stall(uint8_t rhport)
{
dcd_edpt_stall(rhport, 0 | TUSB_DIR_IN_MASK);
}
// return len of descriptor and change pointer to descriptor's buffer
static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
{
(void) rhport;
tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low( p_request->wValue );
uint8_t const * desc_data = NULL ;
uint16_t len = 0;
switch(desc_type)
{
case TUSB_DESC_DEVICE:
desc_data = (uint8_t const *) usbd_desc_set->device;
len = sizeof(tusb_desc_device_t);
break;
case TUSB_DESC_CONFIGURATION:
desc_data = (uint8_t const *) usbd_desc_set->config;
len = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
break;
case TUSB_DESC_STRING:
// String Descriptor always uses the desc set from user
if ( desc_index < tud_desc_set.string_count )
{
desc_data = tud_desc_set.string_arr[desc_index];
TU_VERIFY( desc_data != NULL, 0 );
len = desc_data[0]; // first byte of descriptor is its size
}else
{
// out of range
/* The 0xee string is indeed a Microsoft USB extension.
* It can be used to tell Windows what driver it should use for the device !!!
*/
return 0;
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
// TODO If not highspeed capable stall this request otherwise
// return the descriptor that could work in highspeed
return 0;
break;
default: return 0;
}
TU_ASSERT( desc_data != NULL, 0);
// up to Host's length
len = tu_min16(p_request->wLength, len );
(*pp_buffer) = desc_data;
return len;
}
tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes) {
if (control_state.current_stage == CONTROL_STAGE_STATUS && xferred_bytes == 0) {
control_state.current_stage = CONTROL_STAGE_SETUP;
return TUSB_ERROR_NONE;
}
tusb_error_t error = TUSB_ERROR_NONE;
control_state.total_transferred += xferred_bytes;
tusb_control_request_t const *p_request = &control_state.current_request;
if (p_request->wLength == control_state.total_transferred || xferred_bytes < 64) {
control_state.current_stage = CONTROL_STAGE_STATUS;
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
// Do the user callback after queueing the STATUS packet because the callback could be slow.
if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
{
tud_control_interface_control_complete_cb(rhport, tu_u16_low(p_request->wIndex), p_request);
}
} else {
if (TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient) {
error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, control_state.total_transferred);
} else {
error = controld_process_control_request(rhport, p_request, control_state.total_transferred);
}
}
return error;
}
// This tracks the state of a control request.
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request) {
tusb_error_t error = TUSB_ERROR_NONE;
memcpy(&control_state.current_request, p_request, sizeof(tusb_control_request_t));
if (p_request->wLength == 0) {
control_state.current_stage = CONTROL_STAGE_STATUS;
} else {
control_state.current_stage = CONTROL_STAGE_DATA;
control_state.total_transferred = 0;
}
if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
{
error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, 0);
} else {
error = controld_process_control_request(rhport, p_request, 0);
}
if (error != TUSB_ERROR_NONE) {
dcd_control_stall(rhport); // Stall errored requests
} else if (control_state.current_stage == CONTROL_STAGE_STATUS) {
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
return error;
}
// This handles the actual request and its response.
tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
{
tusb_error_t error = TUSB_ERROR_NONE;
uint8_t ep_addr = 0;
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr |= TUSB_DIR_IN_MASK;
}
//------------- Standard Request e.g in enumeration -------------//
if( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) {
switch (p_request->bRequest) {
case TUSB_REQ_GET_DESCRIPTOR: {
uint8_t const * buffer = NULL;
uint16_t const len = get_descriptor(rhport, p_request, &buffer);
if (len) {
uint16_t remaining_bytes = len - bytes_already_sent;
if (remaining_bytes > 64) {
remaining_bytes = 64;
}
memcpy(_shared_control_buffer, buffer + bytes_already_sent, remaining_bytes);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, remaining_bytes);
} else {
return TUSB_ERROR_FAILED;
}
break;
}
case TUSB_REQ_GET_CONFIGURATION:
memcpy(_shared_control_buffer, &control_state.config, 1);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 1);
break;
case TUSB_REQ_SET_ADDRESS:
dcd_set_address(rhport, (uint8_t) p_request->wValue);
break;
case TUSB_REQ_SET_CONFIGURATION:
control_state.config = p_request->wValue;
tud_control_set_config_cb (rhport, control_state.config);
break;
default:
return TUSB_ERROR_FAILED;
}
} else if (p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT &&
p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
//------------- Endpoint Request -------------//
switch (p_request->bRequest) {
case TUSB_REQ_GET_STATUS: {
uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
memcpy(_shared_control_buffer, &status, 2);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 2);
break;
}
case TUSB_REQ_CLEAR_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
break;
case TUSB_REQ_SET_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
break;
default:
return TUSB_ERROR_FAILED;
}
} else {
//------------- Unsupported Request -------------//
return TUSB_ERROR_FAILED;
}
return error;
}
#endif

View File

@ -1,98 +0,0 @@
/**************************************************************************/
/*!
@file usbd.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_usbd
* @{ */
#ifndef _TUSB_CONTROL_H_
#define _TUSB_CONTROL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "tusb.h"
typedef enum {
CONTROL_STAGE_SETUP, // Waiting for a setup token.
CONTROL_STAGE_DATA, // In the process of sending or receiving data.
CONTROL_STAGE_STATUS // In the process of transmitting the STATUS ZLP.
} control_stage_t;
typedef struct {
control_stage_t current_stage;
tusb_control_request_t current_request;
uint16_t total_transferred;
uint8_t config;
} control_t;
extern uint8_t _shared_control_buffer[64];
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * const p_request);
// Callback when the configuration of the device is changed.
tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number);
// Called when the DATA stage of a control transaction is complete.
void tud_control_interface_control_complete_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request);
tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent);
//--------------------------------------------------------------------+
// INTERNAL API
//--------------------------------------------------------------------+
void controld_init(void);
tusb_error_t controld_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
// This tracks the state of a control request.
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request);
// This handles the actual request and its response.
tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void controld_reset(uint8_t rhport);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONTROL_H_ */
/** @} */

View File

@ -124,6 +124,10 @@ void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_
/*------------------------------------------------------------------*/
/* Endpoint API
* Note:
* - Address of control endpoint OUT is 0x00, In is 0x80
* - When stalling control endpoint both control OUT and IN must be stalled
* (according to USB spec, stalled control is only recovered with setup token)
*------------------------------------------------------------------*/
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);

View File

@ -44,7 +44,6 @@
#define _TINY_USB_SOURCE_FILE_
#include "control.h"
#include "tusb.h"
#include "usbd.h"
#include "device/usbd_pvt.h"
@ -68,16 +67,13 @@
typedef struct {
uint8_t config_num;
// map interface number to driver (0xff is invalid)
uint8_t itf2drv[16];
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[2][8]; // map endpoint to driver ( 0xff is invalid )
// map endpoint to driver ( 0xff is invalid )
uint8_t ep2drv[2][8];
}usbd_device_t;
static usbd_device_t _usbd_dev;
// Auto descriptor is enabled, descriptor set point to auto generated one
#if CFG_TUD_DESC_AUTO
extern tud_desc_set_t const _usbd_auto_desc_set;
@ -94,9 +90,8 @@ typedef struct {
void (* init ) (void);
tusb_error_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
// Control request is called one or more times for a request and can queue multiple data packets.
tusb_error_t (* control_request ) (uint8_t rhport, tusb_control_request_t const *, uint16_t bytes_already_sent);
void (* control_request_complete ) (uint8_t rhport, tusb_control_request_t const *);
bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* control_request_complete ) (uint8_t rhport, tusb_control_request_t const * request);
tusb_error_t (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, tusb_event_t, uint32_t);
void (* sof ) (uint8_t rhport);
void (* reset ) (uint8_t);
@ -104,16 +99,6 @@ typedef struct {
static usbd_class_driver_t const usbd_class_drivers[] =
{
{
.class_code = TUSB_CLASS_UNSPECIFIED,
.init = controld_init,
.open = NULL,
.control_request = NULL,
.control_request_complete = NULL,
.xfer_cb = controld_xfer_cb,
.sof = NULL,
.reset = controld_reset
},
#if CFG_TUD_CDC
{
.class_code = TUSB_CLASS_CDC,
@ -181,9 +166,16 @@ OSAL_QUEUE_DEF(_usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
//--------------------------------------------------------------------+
// INTERNAL FUNCTION
// Prototypes
//--------------------------------------------------------------------+
static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
static bool process_set_config(uint8_t rhport, uint8_t config_number);
static void const* get_descriptor(tusb_control_request_t const * p_request, uint16_t* desc_len);
void usbd_control_reset (uint8_t rhport);
tusb_error_t usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes);
void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) );
//--------------------------------------------------------------------+
// APPLICATION API
@ -194,7 +186,7 @@ bool tud_mounted(void)
}
//--------------------------------------------------------------------+
// IMPLEMENTATION
// USBD Task
//--------------------------------------------------------------------+
tusb_error_t usbd_init (void)
{
@ -223,9 +215,8 @@ static void usbd_reset(uint8_t rhport)
tu_varclr(&_usbd_dev);
memset(_usbd_dev.itf2drv, 0xff, sizeof(_usbd_dev.itf2drv)); // invalid mapping
memset(_usbd_dev.ep2drv , 0xff, sizeof(_usbd_dev.ep2drv )); // invalid mapping
// Always map the 0th endpoint to the control driver.
_usbd_dev.ep2drv[TUSB_DIR_IN][0] = 0;
_usbd_dev.ep2drv[TUSB_DIR_OUT][0] = 0;
usbd_control_reset(rhport);
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
{
@ -233,30 +224,41 @@ static void usbd_reset(uint8_t rhport)
}
}
// Main device task implementation
static void usbd_task_body(void)
{
dcd_event_t event;
// Loop until there is no more events in the queue
while (1)
{
dcd_event_t event;
if ( !osal_queue_receive(_usbd_q, &event) ) return;
switch ( event.event_id )
{
case DCD_EVENT_SETUP_RECEIVED:
// Setup tokens are unique to the Control endpoint so we delegate to it directly.
controld_process_setup_request(event.rhport, &event.setup_received);
// Process control request, if failed control endpoint is stalled
if ( !process_control_request(event.rhport, &event.setup_received) )
{
usbd_control_stall(event.rhport);
}
break;
case DCD_EVENT_XFER_COMPLETE:
{
// Invoke the class callback associated with the endpoint address
uint8_t const ep_addr = event.xfer_complete.ep_addr;
uint8_t const drv_id = _usbd_dev.ep2drv[edpt_dir(ep_addr)][edpt_number(ep_addr)];
if ( drv_id < USBD_CLASS_DRIVER_COUNT )
if ( 0 == edpt_number(ep_addr) )
{
// control transfer DATA stage callback
usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
}
else
{
uint8_t const drv_id = _usbd_dev.ep2drv[edpt_dir(ep_addr)][edpt_number(ep_addr)];
TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,);
usbd_class_drivers[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
}
}
@ -316,66 +318,142 @@ void usbd_task( void* param)
#endif
}
void tud_control_interface_control_complete_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request) {
if (_usbd_dev.itf2drv[ interface ] < USBD_CLASS_DRIVER_COUNT)
{
const usbd_class_driver_t *driver = &usbd_class_drivers[_usbd_dev.itf2drv[interface]];
if (driver->control_request_complete != NULL) {
driver->control_request_complete(rhport, p_request);
}
}
}
//--------------------------------------------------------------------+
// Control Request Parser & Handling
//--------------------------------------------------------------------+
tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent) {
if (_usbd_dev.itf2drv[ interface ] < USBD_CLASS_DRIVER_COUNT)
// This handles the actual request and its response.
// return false will cause its caller to stall control endpoint
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
usbd_control_set_complete_callback(NULL);
if ( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type )
{
//------------- Standard Device Requests e.g in enumeration -------------//
void* data_buf = NULL;
uint16_t data_len = 0;
switch ( p_request->bRequest )
{
return usbd_class_drivers[_usbd_dev.itf2drv[interface]].control_request(rhport, p_request, bytes_already_sent);
case TUSB_REQ_SET_ADDRESS:
dcd_set_address(rhport, (uint8_t) p_request->wValue);
break;
case TUSB_REQ_GET_CONFIGURATION:
data_buf = &_usbd_dev.config_num;
data_len = 1;
break;
case TUSB_REQ_SET_CONFIGURATION:
{
uint8_t const config = (uint8_t) p_request->wValue;
dcd_set_config(rhport, config);
_usbd_dev.config_num = config;
TU_ASSERT( TUSB_ERROR_NONE == process_set_config(rhport, config) );
}
break;
case TUSB_REQ_GET_DESCRIPTOR:
data_buf = (void*) get_descriptor(p_request, &data_len);
if ( data_buf == NULL || data_len == 0 ) return false;
break;
default: return false;
}
return TUSB_ERROR_FAILED;
usbd_control_xfer(rhport, p_request, data_buf, data_len);
}
else if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
{
//------------- Class/Interface Specific Request -------------//
uint8_t const itf = tu_u16_low(p_request->wIndex);
uint8_t const drvid = _usbd_dev.itf2drv[ itf ];
TU_VERIFY (drvid < USBD_CLASS_DRIVER_COUNT );
usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete );
// control endpoint will be stalled if driver return false
return usbd_class_drivers[drvid].control_request(rhport, p_request);
}
else if ( p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT &&
p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
{
//------------- Endpoint Request -------------//
switch ( p_request->bRequest )
{
case TUSB_REQ_GET_STATUS:
{
uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
usbd_control_xfer(rhport, p_request, &status, 2);
}
break;
case TUSB_REQ_CLEAR_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
usbd_control_status(rhport, p_request);
break;
case TUSB_REQ_SET_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
usbd_control_status(rhport, p_request);
break;
default: return false;
}
}
else
{
//------------- Unsupported Request -------------//
return false;
}
return true;
}
// Process Set Configure Request
// TODO Host (windows) can get HID report descriptor before set configured
// may need to open interface before set configured
tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number)
// This function parse configuration descriptor & open drivers accordingly
static bool process_set_config(uint8_t rhport, uint8_t config_number)
{
dcd_set_config(rhport, config_number);
_usbd_dev.config_num = config_number;
//------------- parse configuration & open drivers -------------//
uint8_t const * desc_cfg = (uint8_t const *) usbd_desc_set->config;
TU_ASSERT(desc_cfg != NULL, TUSB_ERROR_DESCRIPTOR_CORRUPTED);
TU_ASSERT(desc_cfg != NULL);
uint8_t const * p_desc = desc_cfg + sizeof(tusb_desc_configuration_t);
uint16_t const cfg_len = ((tusb_desc_configuration_t*)desc_cfg)->wTotalLength;
while( p_desc < desc_cfg + cfg_len )
{
// Each interface always starts with Interface or Association descriptor
if ( TUSB_DESC_INTERFACE_ASSOCIATION == descriptor_type(p_desc) )
{
p_desc = descriptor_next(p_desc); // ignore Interface Association
}else
{
TU_ASSERT( TUSB_DESC_INTERFACE == descriptor_type(p_desc), TUSB_ERROR_NOT_SUPPORTED_YET );
TU_ASSERT( TUSB_DESC_INTERFACE == descriptor_type(p_desc) );
tusb_desc_interface_t* p_desc_itf = (tusb_desc_interface_t*) p_desc;
uint8_t const class_code = p_desc_itf->bInterfaceClass;
tusb_desc_interface_t* desc_itf = (tusb_desc_interface_t*) p_desc;
// Check if class is supported
uint8_t drv_id;
for (drv_id = 0; drv_id < USBD_CLASS_DRIVER_COUNT; drv_id++)
{
if ( usbd_class_drivers[drv_id].class_code == class_code ) break;
if ( usbd_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break;
}
TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT, TUSB_ERROR_NOT_SUPPORTED_YET );
TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT ); // unsupported class
// Interface number must not be used
TU_ASSERT( 0xff == _usbd_dev.itf2drv[p_desc_itf->bInterfaceNumber], TUSB_ERROR_FAILED);
_usbd_dev.itf2drv[p_desc_itf->bInterfaceNumber] = drv_id;
// Interface number must not be used already TODO alternate interface
TU_ASSERT( 0xff == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] );
_usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id;
uint16_t len=0;
TU_ASSERT_ERR( usbd_class_drivers[drv_id].open( rhport, p_desc_itf, &len ) );
TU_ASSERT( len >= sizeof(tusb_desc_interface_t), TUSB_ERROR_FAILED );
TU_ASSERT_ERR( usbd_class_drivers[drv_id].open( rhport, desc_itf, &len ), false );
TU_ASSERT( len >= sizeof(tusb_desc_interface_t) );
mark_interface_endpoint(p_desc, len, drv_id);
@ -408,8 +486,62 @@ static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, ui
}
}
// return descriptor's buffer and update desc_len
static void const* get_descriptor(tusb_control_request_t const * p_request, uint16_t* desc_len)
{
tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low( p_request->wValue );
uint8_t const * desc_data = NULL;
uint16_t len = 0;
*desc_len = 0;
switch(desc_type)
{
case TUSB_DESC_DEVICE:
desc_data = (uint8_t const *) usbd_desc_set->device;
len = sizeof(tusb_desc_device_t);
break;
case TUSB_DESC_CONFIGURATION:
desc_data = (uint8_t const *) usbd_desc_set->config;
len = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
break;
case TUSB_DESC_STRING:
// String Descriptor always uses the desc set from user
if ( desc_index < tud_desc_set.string_count )
{
desc_data = tud_desc_set.string_arr[desc_index];
TU_VERIFY( desc_data != NULL, NULL );
len = desc_data[0]; // first byte of descriptor is its size
}else
{
// out of range
/* The 0xEE index string is a Microsoft USB extension.
* It can be used to tell Windows what driver it should use for the device !!!
*/
return NULL;
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
// TODO If not highspeed capable stall this request otherwise
// return the descriptor that could work in highspeed
return NULL;
break;
default: return NULL;
}
*desc_len = len;
return desc_data;
}
//--------------------------------------------------------------------+
// USBD-DCD Callback API
// DCD Event Handler
//--------------------------------------------------------------------+
void dcd_event_handler(dcd_event_t const * event, bool in_isr)
{
@ -434,6 +566,9 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
break;
case DCD_EVENT_XFER_COMPLETE:
// skip zero-length control status complete event, should dcd notifies us.
if ( 0 == edpt_number(event->xfer_complete.ep_addr) && event->xfer_complete.len == 0) break;
osal_queue_send(_usbd_q, event, in_isr);
TU_ASSERT(event->xfer_complete.result == DCD_XFER_SUCCESS,);
break;
@ -442,8 +577,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
}
}
void dcd_event_handler(dcd_event_t const * event, bool in_isr);
// helper to send bus signal event
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{

170
src/device/usbd_control.c Normal file
View File

@ -0,0 +1,170 @@
/**************************************************************************/
/*!
@file usbd_control.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#define _TINY_USB_SOURCE_FILE_
#include "tusb.h"
#include "device/usbd_pvt.h"
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80
};
typedef struct
{
tusb_control_request_t request;
void* buffer;
uint16_t total_len;
uint16_t total_transferred;
bool (*complete_cb) (uint8_t, tusb_control_request_t const *);
} usbd_control_xfer_t;
static usbd_control_xfer_t _control_state;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _usbd_ctrl_buf[CFG_TUD_ENDOINT0_SIZE];
void usbd_control_reset (uint8_t rhport)
{
tu_varclr(&_control_state);
}
void usbd_control_stall(uint8_t rhport)
{
dcd_edpt_stall(rhport, 0);
}
bool usbd_control_status(uint8_t rhport, tusb_control_request_t const * request)
{
// status direction is reversed to one in the setup packet
return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0);
}
// Each transaction is up to endpoint0's max packet size
static bool start_control_data_xact(uint8_t rhport)
{
uint16_t const xact_len = tu_min16(_control_state.total_len - _control_state.total_transferred, CFG_TUD_ENDOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_IN )
{
ep_addr = EDPT_CTRL_IN;
memcpy(_usbd_ctrl_buf, _control_state.buffer, xact_len);
}
return dcd_edpt_xfer(rhport, ep_addr, _usbd_ctrl_buf, xact_len);
}
// TODO may find a better way
void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) )
{
_control_state.complete_cb = fp;
}
bool usbd_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
{
_control_state.request = (*request);
_control_state.buffer = buffer;
_control_state.total_len = tu_min16(len, request->wLength);
_control_state.total_transferred = 0;
if ( buffer != NULL && len )
{
// Data stage
TU_ASSERT( start_control_data_xact(rhport) );
}else
{
// Status stage
TU_ASSERT( usbd_control_status(rhport, request) );
}
return true;
}
// callback when a transaction complete on DATA stage of control endpoint
tusb_error_t usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)
{
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
{
memcpy(_control_state.buffer, _usbd_ctrl_buf, xferred_bytes);
}
_control_state.total_transferred += xferred_bytes;
_control_state.buffer += xferred_bytes;
if ( _control_state.total_len == _control_state.total_transferred || xferred_bytes < CFG_TUD_ENDOINT0_SIZE )
{
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
if ( _control_state.complete_cb )
{
is_ok = _control_state.complete_cb(rhport, &_control_state.request);
}
if ( is_ok )
{
// Send status
TU_ASSERT( usbd_control_status(rhport, &_control_state.request), TUSB_ERROR_FAILED );
}else
{
// stall due to callback
usbd_control_stall(rhport);
}
}
else
{
// More data to transfer
TU_ASSERT(start_control_data_xact(rhport), TUSB_ERROR_FAILED);
}
return TUSB_ERROR_NONE;
}
#endif

View File

@ -54,15 +54,24 @@ extern tud_desc_set_t const* usbd_desc_set;
tusb_error_t usbd_init (void);
void usbd_task (void* param);
// Carry out Data and Status stage of control transfer
// - If len = 0, it is equivalent to sending status only
// - If len > wLength : it will be truncated
bool usbd_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len);
// Send STATUS (zero length) packet
bool usbd_control_status(uint8_t rhport, tusb_control_request_t const * request);
// Stall control endpoint until new setup packet arrived
void usbd_control_stall(uint8_t rhport);
/*------------------------------------------------------------------*/
/* Endpoint helper
/* Helper
*------------------------------------------------------------------*/
// helper to parse an pair of In and Out endpoint descriptors. They must be consecutive
tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
/*------------------------------------------------------------------*/
/* Other Helpers
*------------------------------------------------------------------*/
void usbd_defer_func( osal_task_func_t func, void* param, bool in_isr );

View File

@ -44,13 +44,6 @@
#include "tusb_hal.h"
/*------------------------------------------------------------------*/
/* MACRO TYPEDEF CONSTANT ENUM
*------------------------------------------------------------------*/
#define USB_NVIC_PRIO 7
void tusb_hal_nrf_power_event(uint32_t event);
/*------------------------------------------------------------------*/
/* TUSB HAL
*------------------------------------------------------------------*/

View File

@ -64,9 +64,7 @@ enum
USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
};
/*------------------------------------------------------------------*/
/* VARIABLE DECLARATION
*------------------------------------------------------------------*/
// Transfer descriptor
typedef struct
{
uint8_t* buffer;
@ -78,35 +76,119 @@ typedef struct
// indicate packet is already ACK
volatile bool data_received;
} nom_xfer_t;
} xfer_td_t;
/*static*/ struct
// Data for managing dcd
static struct
{
// All 8 endpoints including control IN & OUT (offset 1)
nom_xfer_t xfer[8][2];
xfer_td_t xfer[8][2];
// Only one DMA can run at a time
volatile bool dma_running;
}_dcd;
void bus_reset(void)
/*------------------------------------------------------------------*/
/* Control / Bulk / Interrupt (CBI) Transfer
*------------------------------------------------------------------*/
// helper to start DMA
static void edpt_dma_start(volatile uint32_t* reg_startep)
{
for(int i=0; i<8; i++)
// Only one dma can be active
if ( _dcd.dma_running )
{
NRF_USBD->TASKS_STARTEPIN[i] = 0;
NRF_USBD->TASKS_STARTEPOUT[i] = 0;
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
{
// If called within ISR, use usbd task to defer later
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
return;
}
else
{
// Otherwise simply block wait
while ( _dcd.dma_running ) { }
}
}
NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
_dcd.dma_running = true;
tu_varclr(&_dcd);
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
(*reg_startep) = 1;
__ISB(); __DSB();
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
// DMA is complete
static void edpt_dma_end(void)
{
TU_ASSERT(_dcd.dma_running, );
_dcd.dma_running = false;
}
// helper getting td
static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
{
return &_dcd.xfer[epnum][dir];
}
/*------------- CBI OUT Transfer -------------*/
// Prepare for a CBI transaction OUT, call at the start
// Allow ACK incoming data
static void xact_out_prepare(uint8_t epnum)
{
if ( epnum == 0 )
{
NRF_USBD->TASKS_EP0RCVOUT = 1;
}
else
{
// Write zero value to SIZE register will allow hw to ACK (accept data)
// If it is not already done by DMA
NRF_USBD->SIZE.EPOUT[epnum] = 0;
}
__ISB(); __DSB();
}
// Start DMA to move data from Endpoint -> RAM
static void xact_out_dma(uint8_t epnum)
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint8_t const xact_len = NRF_USBD->SIZE.EPOUT[epnum];
// Trigger DMA move data from Endpoint -> SRAM
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
/*------------- CBI IN Transfer -------------*/
// Prepare for a CBI transaction IN, call at the start
// it start DMA to transfer data from RAM -> Endpoint
static void xact_in_prepare(uint8_t epnum)
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
uint8_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
xfer->buffer += xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
}
//--------------------------------------------------------------------+
// Tinyusb DCD API
//--------------------------------------------------------------------+
bool dcd_init (uint8_t rhport)
{
(void) rhport;
@ -135,101 +217,6 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
// Nothing to do
}
/*------------------------------------------------------------------*/
/* Control
*------------------------------------------------------------------*/
static void edpt_dma_start(volatile uint32_t* reg_startep)
{
// Only one dma can be active
if ( _dcd.dma_running )
{
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
{
// If called within ISR, use usbd task to defer later
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
return;
}
else
{
// Otherwise simply block wait
while ( _dcd.dma_running ) { }
}
}
_dcd.dma_running = true;
(*reg_startep) = 1;
__ISB(); __DSB();
}
static void edpt_dma_end(void)
{
TU_ASSERT(_dcd.dma_running, );
_dcd.dma_running = false;
}
/*------------------------------------------------------------------*/
/*
*------------------------------------------------------------------*/
static inline nom_xfer_t* get_td(uint8_t epnum, uint8_t dir)
{
return &_dcd.xfer[epnum][dir];
}
/*------------- Bulk/Int OUT transfer -------------*/
/**
* Prepare Bulk/Int out transaction, Endpoint start to accept/ACK Data
* @param epnum
*/
static void xact_out_prepare(uint8_t epnum)
{
// Write zero value to SIZE register will allow hw to ACK (accept data)
// If it is not already done by DMA
NRF_USBD->SIZE.EPOUT[epnum] = 0;
__ISB(); __DSB();
}
static void xact_out_dma(uint8_t epnum)
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint8_t const xact_len = NRF_USBD->SIZE.EPOUT[epnum];
// Trigger DMA move data from Endpoint -> SRAM
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
/*------------- Bulk/Int IN transfer -------------*/
/**
* Prepare Bulk/Int in transaction, use DMA to transfer data from Memory -> Endpoint
* @param epnum
*/
static void xact_in_prepare(uint8_t epnum)
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
uint8_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
xfer->buffer += xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
}
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
@ -253,16 +240,6 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
void control_status_token(uint8_t addr) {
NRF_USBD->EPIN[0].PTR = 0;
NRF_USBD->EPIN[0].MAXCNT = 0;
// Status Phase also require Easy DMA has to be free as well !!!!
NRF_USBD->TASKS_EP0STATUS = 1;
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, addr, 0, DCD_XFER_SUCCESS, false);
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
@ -270,27 +247,36 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
nom_xfer_t* xfer = get_td(epnum, dir);
xfer_td_t* xfer = get_td(epnum, dir);
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->actual_len = 0;
// How does the control endpoint handle a ZLP in the data phase?
if (epnum == 0 && total_bytes == 0) {
control_status_token(ep_addr);
} else if ( dir == TUSB_DIR_OUT )
// Control endpoint with zero-length packet --> status stage
if ( epnum == 0 && total_bytes == 0 )
{
// Status Phase also require Easy DMA has to be free as well !!!!
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
edpt_dma_end();
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, ep_addr, 0, DCD_XFER_SUCCESS, false);
}
else if ( dir == TUSB_DIR_OUT )
{
if ( xfer->data_received )
{
// nrf52840 auto ACK OUT packet after DMA is done
// Data already received previously --> trigger DMA to copy to SRAM
xact_out_dma(epnum);
}else
}
else
{
xact_out_prepare(epnum);
}
}else
}
else
{
xact_in_prepare(epnum);
}
@ -313,7 +299,7 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( ep_addr == 0)
if ( edpt_number(ep_addr) == 0 )
{
NRF_USBD->TASKS_EP0STALL = 1;
}else
@ -328,7 +314,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
if ( ep_addr )
if ( edpt_number(ep_addr) )
{
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
__ISB(); __DSB();
@ -340,19 +326,35 @@ bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
(void) rhport;
// USBD shouldn't check control endpoint state
if ( 0 == ep_addr ) return false;
if ( 0 == edpt_number(ep_addr) ) return false;
uint8_t const epnum = edpt_number(ep_addr);
uint8_t const dir = edpt_dir(ep_addr);
nom_xfer_t* xfer = get_td(epnum, dir);
xfer_td_t* xfer = get_td(epnum, dir);
return xfer->actual_len < xfer->total_len;
}
/*------------------------------------------------------------------*/
/*
/* Interrupt Handler
*------------------------------------------------------------------*/
void bus_reset(void)
{
for(int i=0; i<8; i++)
{
NRF_USBD->TASKS_STARTEPIN[i] = 0;
NRF_USBD->TASKS_STARTEPOUT[i] = 0;
}
NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
tu_varclr(&_dcd);
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
}
void USBD_IRQHandler(void)
{
uint32_t const inten = NRF_USBD->INTEN;
@ -388,19 +390,52 @@ void USBD_IRQHandler(void)
// Setup tokens are specific to the Control endpoint.
if ( int_status & USBD_INTEN_EP0SETUP_Msk )
{
uint8_t setup[8] = {
uint8_t const setup[8] = {
NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
};
if (setup[1] != TUSB_REQ_SET_ADDRESS) {
// nrf5x hw auto handle set address, there is no need to inform usb stack
tusb_control_request_t const * request = (tusb_control_request_t const *) setup;
if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_SET_ADDRESS == request->bRequest) )
{
dcd_event_setup_received(0, setup, true);
}
}
/*------------- Bulk/Interrupt Transfer -------------*/
//--------------------------------------------------------------------+
/* Control/Bulk/Interrupt (CBI) Transfer
*
* Data flow is:
* (bus) (dma)
* Host <-------> Endpoint <-------> RAM
*
* For CBI OUT:
* - Host -> Endpoint
* EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPOUT[i]
* to start DMA. This step can occur automatically (without sw),
* which means data may or may not ready (data_received flag).
* - Endpoint -> RAM
* ENDEPOUT[i] interrupted, transaction complete, sw prepare next transaction
*
* For CBI IN:
* - RAM -> Endpoint
* ENDEPIN[i] interrupted indicate DMA is complete. HW will start
* to move daat to host
* - Endpoint -> Host
* EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPIN[i].
* Transaction is complete, sw prepare next transaction
*
* Note: in both Control In and Out of Data stage from Host <-> Endpoint
* EP0DATADONE will be set as interrupt source
*/
//--------------------------------------------------------------------+
/* Bulk/Int OUT: data from DMA -> SRAM
* Note: Since nrf controller auto ACK next packet without SW awareness
/* CBI OUT: Endpoint -> SRAM (aka transaction complete)
* Note: Since nRF controller auto ACK next packet without SW awareness
* We must handle this stage before Host -> Endpoint just in case
* 2 event happens at once
*/
@ -408,10 +443,10 @@ void USBD_IRQHandler(void)
{
if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
// Data in endpoint has been consumed
xfer->data_received = false;
// Transfer complete if transaction len < Max Packet Size or total len is transferred
@ -428,21 +463,27 @@ void USBD_IRQHandler(void)
}
}
// Ended event for Bulk/Int : nothing to do
// Ended event for CBI IN : nothing to do
}
if ( int_status & USBD_INTEN_EPDATA_Msk || int_status & USBD_INTEN_EP0DATADONE_Msk)
// Endpoint <-> Host
if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) )
{
uint32_t data_status = NRF_USBD->EPDATASTATUS;
nrf_usbd_epdatastatus_clear(data_status);
// Bulk/Int In: data from Endpoint -> Host
// EP0DATADONE is set with either Control Out on IN Data
// Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN.
// We will use BMREQUESTTYPE in setup packet to determine the direction
bool const is_control_in = (int_status & USBD_INTEN_EP0DATADONE_Msk) && (NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
// CBI In: Endpoint -> Host (transaction complete)
for(uint8_t epnum=0; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, epnum ) || (epnum == 0 && BIT_TEST_(int_status, USBD_INTEN_EP0DATADONE_Pos)))
if ( BIT_TEST_(data_status, epnum ) || ( epnum == 0 && is_control_in) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_IN);
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
xfer->actual_len += NRF_USBD->EPIN[epnum].MAXCNT;
@ -458,12 +499,12 @@ void USBD_IRQHandler(void)
}
}
// Bulk/Int OUT: data from Host -> Endpoint
// CBI OUT: Host -> Endpoint
for(uint8_t epnum=0; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, 16+epnum ) )
if ( BIT_TEST_(data_status, 16+epnum ) || ( epnum == 0 && is_control_out) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
if (xfer->actual_len < xfer->total_len)
{

View File

@ -148,10 +148,8 @@
#define CFG_TUD_ENDOINT0_SIZE 64
#endif
#ifndef CFG_TUD_ENUM_BUFFER_SIZE
#ifndef CFG_TUD_CTRL_BUFSIZE
#define CFG_TUD_CTRL_BUFSIZE 256
#else
#define CFG_TUD_CTRL_BUFSIZE CFG_TUD_ENUM_BUFFER_SIZE
#endif
#ifndef CFG_TUD_DESC_AUTO