Merge branch 'master' into add-app-driver

This commit is contained in:
hathach
2020-08-07 12:13:13 +07:00
205 changed files with 31347 additions and 1184 deletions

252
src/class/bth/bth_device.c Executable file
View File

@@ -0,0 +1,252 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jerzy Kasenberg
*
* 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.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_BTH)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "bth_device.h"
#include <common/tusb_types.h>
#include <device/usbd_pvt.h>
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num;
uint8_t ep_ev;
uint8_t ep_acl_in;
uint8_t ep_acl_out;
uint8_t ep_voice[2]; // Not used yet
uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
} btd_interface_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf;
static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
{
// skip if previous transfer not complete
TU_VERIFY(!usbd_edpt_busy(TUD_OPT_RHPORT, ep));
TU_ASSERT(usbd_edpt_xfer(TUD_OPT_RHPORT, ep, data, len));
return true;
}
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
bool tud_bt_event_send(void *event, uint16_t event_len)
{
return bt_tx_data(_btd_itf.ep_ev, event, event_len);
}
bool tud_bt_acl_data_send(void *event, uint16_t event_len)
{
return bt_tx_data(_btd_itf.ep_acl_in, event, event_len);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void btd_init(void)
{
tu_memclr(&_btd_itf, sizeof(_btd_itf));
}
void btd_reset(uint8_t rhport)
{
(void)rhport;
}
uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
{
tusb_desc_endpoint_t const *desc_ep;
uint16_t drv_len = 0;
// Size of single alternative of ISO interface
const uint16_t iso_alt_itf_size = sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t);
// Size of hci interface
const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t);
// Ensure this is BT Primary Controller
TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0);
// Distinguish interface by number of endpoints, as both interface have same class, subclass and protocol
if (itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size)
{
_btd_itf.itf_num = itf_desc->bInterfaceNumber;
desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(dcd_edpt_open(rhport, desc_ep), 0);
_btd_itf.ep_ev = desc_ep->bEndpointAddress;
// Open endpoint pair
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
&_btd_itf.ep_acl_in), 0);
// Prepare for incoming data from host
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
drv_len = hci_itf_size;
}
else if (itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size)
{
uint8_t dir;
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
_btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
// Store endpoint size for alternative
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
_btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
// Store endpoint size for alternative
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
drv_len += iso_alt_itf_size;
for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) {
// Make sure rest of alternatives matches
itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep);
if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE ||
TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass ||
TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass ||
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol)
{
// Not an Iso interface instance
break;
}
TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
// Verify that alternative endpoint are same as first ones
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
_btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
// Verify that alternative endpoint are same as first ones
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
_btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
drv_len += iso_alt_itf_size;
}
}
return drv_len;
}
bool btd_control_complete(uint8_t rhport, tusb_control_request_t const *request)
{
(void)rhport;
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength);
return true;
}
bool btd_control_request(uint8_t rhport, tusb_control_request_t const *request)
{
(void)rhport;
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
{
// HCI command packet addressing for single function Primary Controllers
TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
}
else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
{
if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex)
{
// TODO: Set interface it would involve changing size of endpoint size
}
else
{
// HCI command packet for Primary Controller function in a composite device
TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num);
}
}
else return false;
return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength);
}
bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void)result;
// received new data from host
if (ep_addr == _btd_itf.ep_acl_out)
{
if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes);
// prepare for next data
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
}
else if (ep_addr == _btd_itf.ep_ev)
{
if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes);
}
else if (ep_addr == _btd_itf.ep_acl_in)
{
if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes);
}
return TUSB_ERROR_NONE;
}
#endif

110
src/class/bth/bth_device.h Executable file
View File

@@ -0,0 +1,110 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jerzy Kasenberg
*
* 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 _TUSB_BTH_DEVICE_H_
#define _TUSB_BTH_DEVICE_H_
#include <common/tusb_common.h>
#include <device/usbd.h>
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUD_BTH_EVENT_EPSIZE
#define CFG_TUD_BTH_EVENT_EPSIZE 16
#endif
#ifndef CFG_TUD_BTH_DATA_EPSIZE
#define CFG_TUD_BTH_DATA_EPSIZE 64
#endif
typedef struct TU_ATTR_PACKED
{
uint16_t op_code;
uint8_t param_length;
uint8_t param[255];
} bt_hci_cmd_t;
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
// Invoked when HCI command was received over USB from Bluetooth host.
// Detailed format is described in Bluetooth core specification Vol 2,
// Part E, 5.4.1.
// Length of the command is from 3 bytes (2 bytes for OpCode,
// 1 byte for parameter total length) to 258.
TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
// Invoked when ACL data was received over USB from Bluetooth host.
// Detailed format is described in Bluetooth core specification Vol 2,
// Part E, 5.4.2.
// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
// and 16 bits for data total length) to endpoint size.
TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
// Called when event sent with tud_bt_event_send() was delivered to BT stack.
// Controller can release/reuse buffer with Event packet at this point.
TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes);
// Called when ACL data that was sent with tud_bt_acl_data_send()
// was delivered to BT stack.
// Controller can release/reuse buffer with ACL packet at this point.
TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes);
// Bluetooth controller calls this function when it wants to send even packet
// as described in Bluetooth core specification Vol 2, Part E, 5.4.4.
// Event has at least 2 bytes, first is Event code second contains parameter
// total length. Controller can release/reuse event memory after
// tud_bt_event_sent_cb() is called.
bool tud_bt_event_send(void *event, uint16_t event_len);
// Bluetooth controller calls this to send ACL data packet
// as described in Bluetooth core specification Vol 2, Part E, 5.4.2
// Minimum length is 4 bytes, (12 bits for Handle, 4 bits for flags
// and 16 bits for data total length). Upper limit is not limited
// to endpoint size since buffer is allocate by controller
// and must not be reused till tud_bt_acl_data_sent_cb() is called.
bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void btd_init (void);
void btd_reset (uint8_t rhport);
uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool btd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool btd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_BTH_DEVICE_H_ */

View File

@@ -61,8 +61,8 @@ typedef struct
#endif
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
}cdcd_interface_t;
@@ -82,9 +82,9 @@ static void _prep_out_transaction (uint8_t itf)
// Prepare for incoming data but only allow what we can store in the ring buffer.
uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
if ( max_read >= TU_ARRAY_SIZE(p_cdc->epout_buf) )
if ( max_read >= sizeof(p_cdc->epout_buf) )
{
usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, TU_ARRAY_SIZE(p_cdc->epout_buf));
usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
}
}
@@ -148,7 +148,7 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
#if 0 // TODO issue with circuitpython's REPL
// flush if queue more than endpoint size
if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE )
if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EP_BUFSIZE )
{
tud_cdc_n_write_flush(itf);
}
@@ -164,7 +164,7 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
// skip if previous transfer not complete yet
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in), 0 );
uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, TU_ARRAY_SIZE(p_cdc->epin_buf));
uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
if ( count )
{
TU_VERIFY( tud_cdc_n_connected(itf), 0 ); // fifo is empty if not connected
@@ -222,14 +222,14 @@ void cdcd_reset(uint8_t rhport)
}
}
bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// Only support ACM subclass
TU_VERIFY ( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
// Note: 0xFF can be used with RNDIS
TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA));
TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA), 0);
// Find available interface
cdcd_interface_t * p_cdc = NULL;
@@ -242,30 +242,30 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
break;
}
}
TU_ASSERT(p_cdc);
TU_ASSERT(p_cdc, 0);
//------------- Control Interface -------------//
p_cdc->itf_num = itf_desc->bInterfaceNumber;
uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const * p_desc = tu_desc_next( itf_desc );
(*p_length) = sizeof(tusb_desc_interface_t);
// Communication Functional Descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
// notification endpoint if any
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
@@ -273,18 +273,19 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
{
// next to endpoint descriptor
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
// Open endpoint pair
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
(*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
drv_len += 2*sizeof(tusb_desc_endpoint_t);
}
// Prepare for incoming data
_prep_out_transaction(cdc_id);
return true;
return drv_len;
}
// Invoked when class request DATA stage is finished.
@@ -363,7 +364,7 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request
tud_control_status(rhport, request);
// Invoke callback
if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts);
if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
}
break;
@@ -419,7 +420,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
{
// There is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EPSIZE)) )
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EP_BUFSIZE)) )
{
usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, NULL, 0);
}

View File

@@ -34,8 +34,13 @@
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUD_CDC_EPSIZE
#define CFG_TUD_CDC_EPSIZE 64
#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE)
#warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name
#define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE
#endif
#ifndef CFG_TUD_CDC_EP_BUFSIZE
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifdef __cplusplus
@@ -95,7 +100,7 @@ uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
// Force sending data if possible, return number of forced bytes
uint32_t tud_cdc_n_write_flush (uint8_t itf);
// Return number of characters available for writing
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
uint32_t tud_cdc_n_write_available (uint8_t itf);
//--------------------------------------------------------------------+
@@ -229,12 +234,12 @@ static inline uint32_t tud_cdc_write_available(void)
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void cdcd_init (void);
void cdcd_reset (uint8_t rhport);
bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void cdcd_init (void);
void cdcd_reset (uint8_t rhport);
uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -44,6 +44,14 @@ typedef enum {
DFU_REQUEST_ABORT = 6,
} dfu_requests_t;
typedef struct TU_ATTR_PACKED
{
uint8_t status;
uint8_t poll_timeout[3];
uint8_t state;
uint8_t istring;
} dfu_status_t;
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
@@ -56,24 +64,25 @@ void dfu_rtd_reset(uint8_t rhport)
(void) rhport;
}
bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void) rhport;
(void) max_len;
// Ensure this is DFU Runtime
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS);
TU_VERIFY(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT);
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS &&
itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT, 0);
uint8_t const * p_desc = tu_desc_next( itf_desc );
(*p_length) = sizeof(tusb_desc_interface_t);
uint16_t drv_len = sizeof(tusb_desc_interface_t);
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
{
(*p_length) += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
return true;
return drv_len;
}
bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
@@ -87,10 +96,19 @@ bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * req
bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request)
{
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
// dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_SET_INTERFACE == request->bRequest )
{
tud_control_status(rhport, request);
return true;
}
// Handle class request only from here
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
switch ( request->bRequest )
{
case DFU_REQUEST_DETACH:
@@ -98,6 +116,14 @@ bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * requ
tud_dfu_rt_reboot_to_dfu();
break;
case DFU_REQUEST_GETSTATUS:
{
// status = OK, poll timeout = 0, state = app idle, istring = 0
uint8_t status_response[6] = { 0, 0, 0, 0, 0, 0 };
tud_control_xfer(rhport, request, status_response, sizeof(status_response));
}
break;
default: return false; // stall unsupported request
}

View File

@@ -63,12 +63,12 @@ TU_ATTR_WEAK void tud_dfu_rt_reboot_to_dfu(void); // TODO rename to _cb conventi
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void dfu_rtd_init(void);
void dfu_rtd_reset(uint8_t rhport);
bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request);
bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request);
bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void dfu_rtd_init(void);
void dfu_rtd_reset(uint8_t rhport);
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request);
bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request);
bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -48,8 +48,8 @@ typedef struct
uint8_t idle_rate; // up to application to handle idle rate
uint16_t report_desc_len;
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
tusb_hid_descriptor_hid_t const * hid_descriptor;
} hidd_interface_t;
@@ -86,7 +86,7 @@ bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
if (report_id)
{
len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1);
len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
p_hid->epin_buf[0] = report_id;
memcpy(p_hid->epin_buf+1, report, len);
@@ -94,7 +94,7 @@ bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
}else
{
// If report id = 0, skip ID field
len = tu_min8(len, CFG_TUD_HID_BUFSIZE);
len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE);
memcpy(p_hid->epin_buf, report, len);
}
@@ -158,11 +158,13 @@ void hidd_reset(uint8_t rhport)
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
uint8_t const *p_desc = (uint8_t const *) desc_itf;
// len = interface + hid + n*endpoints
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
TU_ASSERT(max_len >= drv_len, 0);
// Find available interface
hidd_interface_t * p_hid = NULL;
@@ -175,29 +177,38 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
break;
}
}
TU_ASSERT(p_hid);
TU_ASSERT(p_hid, 0);
uint8_t const *p_desc = (uint8_t const *) desc_itf;
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType);
TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType, 0);
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in));
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
p_hid->boot_mode = false; // default mode is REPORT
p_hid->itf_num = desc_itf->bInterfaceNumber;
memcpy(&p_hid->report_desc_len, &(p_hid->hid_descriptor->wReportLength), 2);
*p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
// Use offsetof to avoid pointer to the odd/misaligned address
memcpy(&p_hid->report_desc_len, (uint8_t*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength), 2);
// Prepare for output endpoint
if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
if (p_hid->ep_out)
{
if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
{
TU_LOG1_FAILED();
TU_BREAKPOINT();
}
}
return true;
return drv_len;
}
// Handle class control request

View File

@@ -39,8 +39,14 @@
// Class Driver Default Configure & Validation
//--------------------------------------------------------------------+
#ifndef CFG_TUD_HID_BUFSIZE
#define CFG_TUD_HID_BUFSIZE 16
#if !defined(CFG_TUD_HID_EP_BUFSIZE) & defined(CFG_TUD_HID_BUFSIZE)
// TODO warn user to use new name later on
// #warning CFG_TUD_HID_BUFSIZE is renamed to CFG_TUD_HID_EP_BUFSIZE, please update to use the new name
#define CFG_TUD_HID_EP_BUFSIZE CFG_TUD_HID_BUFSIZE
#endif
#ifndef CFG_TUD_HID_EP_BUFSIZE
#define CFG_TUD_HID_EP_BUFSIZE 16
#endif
//--------------------------------------------------------------------+
@@ -300,12 +306,12 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void hidd_init (void);
void hidd_reset (uint8_t rhport);
bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hidd_init (void);
void hidd_reset (uint8_t rhport);
uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -67,8 +67,8 @@ typedef struct
uint8_t read_target_length;
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
} midid_interface_t;
@@ -160,7 +160,7 @@ static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index)
// skip if previous transfer not complete
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) );
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE);
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
if (count > 0)
{
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
@@ -290,31 +290,30 @@ void midid_reset(uint8_t rhport)
}
}
bool midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_length)
uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
// 1st Interface is Audio Control v1
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_PROTOCOL_V1 == desc_itf->bInterfaceProtocol);
AUDIO_PROTOCOL_V1 == desc_itf->bInterfaceProtocol, 0);
uint16_t drv_len = tu_desc_len(desc_itf);
uint8_t const * p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
p_desc = tu_desc_next(p_desc);
}
// 2nd Interface is MIDI Streaming
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc));
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
AUDIO_PROTOCOL_V1 == desc_midi->bInterfaceProtocol );
AUDIO_PROTOCOL_V1 == desc_midi->bInterfaceProtocol, 0);
// Find available interface
midid_interface_t * p_midi = NULL;
@@ -327,40 +326,46 @@ bool midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
}
}
p_midi->itf_num = desc_midi->bInterfaceNumber;
p_midi->itf_num = desc_midi->bInterfaceNumber;
// next descriptor
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
p_desc = tu_desc_next(p_desc);
// Find and open endpoint descriptors
uint8_t found_endpoints = 0;
while (found_endpoints < desc_midi->bNumEndpoints)
while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
{
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false);
uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
p_midi->ep_in = ep_addr;
} else {
p_midi->ep_out = ep_addr;
}
TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
drv_len += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
found_endpoints += 1;
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
{
p_midi->ep_in = ep_addr;
} else {
p_midi->ep_out = ep_addr;
}
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
found_endpoints += 1;
}
drv_len += p_desc[DESC_OFFSET_LEN];
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
*p_length = drv_len;
// Prepare for incoming data
TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
if ( !usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EP_BUFSIZE) )
{
TU_LOG1_FAILED();
TU_BREAKPOINT();
}
return true;
return drv_len;
}
bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
@@ -399,7 +404,7 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes);
// prepare for next
TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EP_BUFSIZE), false );
} else if ( ep_addr == p_midi->ep_in ) {
maybe_transmit(p_midi, itf);
}

View File

@@ -36,8 +36,14 @@
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUD_MIDI_EPSIZE
#define CFG_TUD_MIDI_EPSIZE 64
#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
#warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
#define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
#endif
#ifndef CFG_TUD_MIDI_EP_BUFSIZE
#define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifdef __cplusplus
@@ -136,12 +142,12 @@ static inline bool tud_midi_send (uint8_t const packet[4])
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void midid_init (void);
void midid_reset (uint8_t rhport);
bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void midid_init (void);
void midid_reset (uint8_t rhport);
uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -65,7 +65,7 @@ typedef struct
}mscd_interface_t;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -80,7 +80,8 @@ static inline uint32_t rdwr10_get_lba(uint8_t const command[])
// copy first to prevent mis-aligned access
uint32_t lba;
memcpy(&lba, &p_rdwr10->lba, 4);
// use offsetof to avoid pointer to the odd/misaligned address
memcpy(&lba, (uint8_t*) p_rdwr10 + offsetof(scsi_write10_t, lba), 4);
// lba is in Big Endian format
return tu_ntohl(lba);
@@ -93,7 +94,8 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
// copy first to prevent mis-aligned access
uint16_t block_count;
memcpy(&block_count, &p_rdwr10->block_count, 2);
// use offsetof to avoid pointer to the odd/misaligned address
memcpy(&block_count, (uint8_t*) p_rdwr10 + offsetof(scsi_write10_t, block_count), 2);
return tu_ntohs(block_count);
}
@@ -103,7 +105,7 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
static lookup_entry_t const _msc_scsi_cmd_lookup[] =
static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
@@ -118,7 +120,7 @@ static lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
static lookup_table_t const _msc_scsi_cmd_table =
static tu_lookup_table_t const _msc_scsi_cmd_table =
{
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
@@ -154,25 +156,33 @@ void mscd_reset(uint8_t rhport)
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// only support SCSI's BOT protocol
TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
// msc driver length is fixed
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
// Max length mus be at least 1 interface + 2 endpoints
TU_ASSERT(max_len >= drv_len, 0);
mscd_interface_t * p_msc = &_mscd_itf;
p_msc->itf_num = itf_desc->bInterfaceNumber;
// Open endpoint pair
TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
p_msc->itf_num = itf_desc->bInterfaceNumber;
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
// Prepare for Command Block Wrapper
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
{
TU_LOG1_FAILED();
TU_BREAKPOINT();
}
return true;
return drv_len;
}
// Handle class control request
@@ -408,7 +418,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_ASSERT( event == XFER_RESULT_SUCCESS &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
TU_LOG2(" SCSI Command: %s\r\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
TU_LOG2(" SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
// TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
@@ -554,7 +564,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
else
{
// READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
// We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE
// We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);

View File

@@ -38,12 +38,19 @@
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
#ifndef CFG_TUD_MSC_BUFSIZE
#error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
#if !defined(CFG_TUD_MSC_EP_BUFSIZE) & defined(CFG_TUD_MSC_BUFSIZE)
// TODO warn user to use new name later on
// #warning CFG_TUD_MSC_BUFSIZE is renamed to CFG_TUD_MSC_EP_BUFSIZE, please update to use the new name
#define CFG_TUD_MSC_EP_BUFSIZE CFG_TUD_MSC_BUFSIZE
#endif
#ifndef CFG_TUD_MSC_EP_BUFSIZE
#error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better
#endif
TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Device Device
@@ -151,12 +158,12 @@ TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void mscd_init (void);
void mscd_reset (uint8_t rhport);
bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void mscd_init (void);
void mscd_reset (uint8_t rhport);
uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request);
bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -135,7 +135,7 @@ void netd_reset(uint8_t rhport)
netd_init();
}
bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
@@ -145,10 +145,10 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
0x00 == itf_desc->bInterfaceProtocol);
TU_VERIFY ( is_rndis || is_ecm );
TU_VERIFY(is_rndis || is_ecm, 0);
// confirm interface hasn't already been allocated
TU_ASSERT(0 == _netd_itf.ep_notif);
TU_ASSERT(0 == _netd_itf.ep_notif, 0);
// sanity check the descriptor
_netd_itf.ecm_mode = is_ecm;
@@ -156,25 +156,25 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
//------------- Management Interface -------------//
_netd_itf.itf_num = itf_desc->bInterfaceNumber;
(*p_length) = sizeof(tusb_desc_interface_t);
uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const * p_desc = tu_desc_next( itf_desc );
// Communication Functional Descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// notification endpoint (if any)
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
_netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface -------------//
@@ -182,19 +182,19 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
// - CDC-ECM data interface has 2 alternate settings
// - 0 : zero endpoints for inactive (default)
// - 1 : IN & OUT endpoints for active networking
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc));
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
do
{
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass);
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
(*p_length) += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) );
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
if ( _netd_itf.ecm_mode )
{
@@ -204,7 +204,7 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
}else
{
// Open endpoint pair for RNDIS
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
tud_network_init_cb();
@@ -215,9 +215,9 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
tud_network_recv_renew();
}
(*p_length) += 2*sizeof(tusb_desc_endpoint_t);
drv_len += 2*sizeof(tusb_desc_endpoint_t);
return true;
return drv_len;
}
// Invoked when class request DATA stage is finished.
@@ -326,7 +326,7 @@ bool netd_control_request(uint8_t rhport, tusb_control_request_t const * request
{
if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
{
rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *)notify.rndis_buf;
rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
@@ -356,7 +356,7 @@ static void handle_incoming_packet(uint32_t len)
}
else
{
rndis_data_packet_t *r = (rndis_data_packet_t *)pnt;
rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
if (len >= sizeof(rndis_data_packet_t))
if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
@@ -450,7 +450,7 @@ void tud_network_xmit(struct pbuf *p)
if (!_netd_itf.ecm_mode)
{
rndis_data_packet_t *hdr = (rndis_data_packet_t *)transmitted;
rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = len;

View File

@@ -37,7 +37,7 @@
#include "netif/ethernet.h"
/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
#define CFG_TUD_NET_ENDPOINT_SIZE ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64)
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
/* Maximum Tranmission Unit (in bytes) of the network, including Ethernet header */
#define CFG_TUD_NET_MTU (1500 + SIZEOF_ETH_HDR)
@@ -72,13 +72,13 @@ void tud_network_xmit(struct pbuf *p);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void netd_init (void);
void netd_reset (uint8_t rhport);
bool netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool netd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool netd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void netd_report (uint8_t *buf, uint16_t len);
void netd_init (void);
void netd_reset (uint8_t rhport);
uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool netd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool netd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void netd_report (uint8_t *buf, uint16_t len);
#ifdef __cplusplus
}

View File

@@ -260,37 +260,39 @@ void usbtmcd_init_cb(void)
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
}
bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void)rhport;
uint16_t drv_len;
uint8_t const * p_desc;
uint8_t found_endpoints = 0;
TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS);
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS);
TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 0);
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS, 0);
#ifndef NDEBUG
// Only 2 or 3 endpoints are allowed for USBTMC.
TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3));
TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3), 0);
#endif
TU_ASSERT(usbtmc_state.state == STATE_CLOSED);
TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
// Interface
(*p_length) = 0u;
drv_len = 0u;
p_desc = (uint8_t const *) itf_desc;
usbtmc_state.itf_id = itf_desc->bInterfaceNumber;
usbtmc_state.rhport = rhport;
while (found_endpoints < itf_desc->bNumEndpoints)
while (found_endpoints < itf_desc->bNumEndpoints && drv_len <= max_len)
{
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
{
tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc;
switch(ep_desc->bmAttributes.xfer) {
case TUSB_XFER_BULK:
TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE);
TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE, 0);
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
{
usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress;
@@ -301,45 +303,46 @@ bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
break;
case TUSB_XFER_INTERRUPT:
#ifndef NDEBUG
TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN);
TU_ASSERT(usbtmc_state.ep_int_in == 0);
TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN, 0);
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
#endif
usbtmc_state.ep_int_in = ep_desc->bEndpointAddress;
break;
default:
TU_ASSERT(false);
TU_ASSERT(false, 0);
}
TU_VERIFY( usbd_edpt_open(rhport, ep_desc));
TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 0);
found_endpoints++;
}
(*p_length) = (uint8_t)((*p_length) + p_desc[DESC_OFFSET_LEN]);
p_desc = tu_desc_next(p_desc);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// bulk endpoints are required, but interrupt IN is optional
#ifndef NDEBUG
TU_ASSERT(usbtmc_state.ep_bulk_in != 0);
TU_ASSERT(usbtmc_state.ep_bulk_out != 0);
TU_ASSERT(usbtmc_state.ep_bulk_in != 0, 0);
TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
if (itf_desc->bNumEndpoints == 2)
{
TU_ASSERT(usbtmc_state.ep_int_in == 0);
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
}
else if (itf_desc->bNumEndpoints == 3)
{
TU_ASSERT(usbtmc_state.ep_int_in != 0);
TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
}
#if (CFG_TUD_USBTMC_ENABLE_488)
if(usbtmc_state.capabilities->bmIntfcCapabilities488.is488_2 ||
usbtmc_state.capabilities->bmDevCapabilities488.SR1)
{
TU_ASSERT(usbtmc_state.ep_int_in != 0);
TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
}
#endif
#endif
atomicChangeState(STATE_CLOSED, STATE_NAK);
tud_usbtmc_open_cb(itf_desc->iInterface);
return true;
return drv_len;
}
// Tell USBTMC class to set its bulk-in EP to ACK so that it can
// receive USBTMC commands.

View File

@@ -108,12 +108,12 @@ bool tud_usbtmc_start_bus_read(void);
/* "callbacks" from USB device core */
bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
void usbtmcd_reset_cb(uint8_t rhport);
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * request);
bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request);
void usbtmcd_init_cb(void);
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
void usbtmcd_reset_cb(uint8_t rhport);
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * request);
bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request);
void usbtmcd_init_cb(void);
/************************************************************
* USBTMC Descriptor Templates

View File

@@ -166,9 +166,12 @@ void vendord_reset(uint8_t rhport)
}
}
bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass);
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass, 0);
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
TU_VERIFY(max_len >= drv_len, 0);
// Find available interface
vendord_interface_t* p_vendor = NULL;
@@ -180,18 +183,21 @@ bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
break;
}
}
TU_VERIFY(p_vendor);
TU_VERIFY(p_vendor, 0);
// Open endpoint pair with usbd helper
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in));
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
p_vendor->itf_num = itf_desc->bInterfaceNumber;
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
// Prepare for incoming data
TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)));
if ( !usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)) )
{
TU_LOG1_FAILED();
TU_BREAKPOINT();
}
return true;
return drv_len;
}
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)

View File

@@ -118,10 +118,10 @@ static inline uint32_t tud_vendor_write_available (void)
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void vendord_init(void);
void vendord_reset(uint8_t rhport);
bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void vendord_init(void);
void vendord_reset(uint8_t rhport);
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@@ -106,7 +106,7 @@ static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x :
static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
// Align
static inline uint32_t tu_align_n(uint32_t value, uint32_t alignment)
static inline uint32_t tu_align(uint32_t value, uint32_t alignment)
{
return value & ((uint32_t) ~(alignment-1));
}
@@ -215,8 +215,11 @@ static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value
void tu_print_mem(void const *buf, uint16_t count, uint8_t indent);
#ifndef tu_printf
#define tu_printf printf
#ifdef CFG_TUSB_DEBUG_PRINTF
extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
#define tu_printf CFG_TUSB_DEBUG_PRINTF
#else
#define tu_printf printf
#endif
static inline
@@ -232,31 +235,32 @@ void tu_print_var(uint8_t const* buf, uint32_t bufsize)
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\n", (uint32_t) (_x) )
#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\n", (uint32_t) (_x) )
#define TU_LOG1_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
#define TU_LOG1_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
// Log with debug level 2
#if CFG_TUSB_DEBUG > 1
#define TU_LOG2 TU_LOG1
#define TU_LOG2_MEM TU_LOG1_MEM
#define TU_LOG2_VAR TU_LOG1_VAR
#define TU_LOG2_LOCATION() TU_LOG1_LOCATION()
#define TU_LOG2_INT TU_LOG1_INT
#define TU_LOG2_HEX TU_LOG1_HEX
#define TU_LOG2_LOCATION() TU_LOG1_LOCATION()
#endif
typedef struct
{
uint32_t key;
char const * data;
}lookup_entry_t;
const char* data;
} tu_lookup_entry_t;
typedef struct
{
uint16_t count;
lookup_entry_t const* items;
} lookup_table_t;
tu_lookup_entry_t const* items;
} tu_lookup_table_t;
static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t key)
static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key)
{
for(uint16_t i=0; i<p_table->count; i++)
{
@@ -274,6 +278,8 @@ static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t ke
#define TU_LOG1_VAR(...)
#define TU_LOG1_INT(...)
#define TU_LOG1_HEX(...)
#define TU_LOG1_LOCATION()
#define TU_LOG1_FAILED()
#endif
#ifndef TU_LOG2
@@ -282,6 +288,7 @@ static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t ke
#define TU_LOG2_VAR(...)
#define TU_LOG2_INT(...)
#define TU_LOG2_HEX(...)
#define TU_LOG2_LOCATION()
#endif
#ifdef __cplusplus

View File

@@ -99,7 +99,26 @@
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
#else
#elif defined(__ICCARM__)
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_PREPACKED
#define TU_ATTR_WEAK __attribute__ ((weak))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
// Endian conversion use well-known host to network (big endian) naming
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
#endif
#define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
#define TU_BSWAP32(u32) (__iar_builtin_REV(u32))
#else
#error "Compiler attribute porting is required"
#endif

View File

@@ -71,41 +71,46 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
return true;
}
static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
{
return (idx < depth) ? idx : (idx-depth);
}
// retrieve data from fifo
static void _tu_ff_pull(tu_fifo_t* f, void * buffer)
static inline void _ff_pull(tu_fifo_t* f, void * buffer, uint16_t n)
{
memcpy(buffer,
f->buffer + (f->rd_idx * f->item_size),
f->item_size);
f->item_size*n);
f->rd_idx = (f->rd_idx + 1) % f->depth;
f->count--;
f->rd_idx = _ff_mod(f->rd_idx + n, f->depth);
f->count -= n;
}
// send data to fifo
static void _tu_ff_push(tu_fifo_t* f, void const * data)
static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t n)
{
memcpy( f->buffer + (f->wr_idx * f->item_size),
data,
f->item_size);
memcpy(f->buffer + (f->wr_idx * f->item_size),
data,
f->item_size*n);
f->wr_idx = (f->wr_idx + 1) % f->depth;
f->wr_idx = _ff_mod(f->wr_idx + n, f->depth);
if (tu_fifo_full(f))
{
f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size)
f->rd_idx = f->wr_idx; // keep the full state (rd == wr && count = depth)
}
else
{
f->count++;
f->count += n;
}
}
/******************************************************************************/
/*!
@brief Read one byte out of the RX buffer.
@brief Read one element out of the RX buffer.
This function will return the byte located at the array index of the
This function will return the element located at the array index of the
read pointer, and then increment the read pointer index. If the read
pointer exceeds the maximum buffer size, it will roll over to zero.
@@ -123,7 +128,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
tu_fifo_lock(f);
_tu_ff_pull(f, buffer);
_ff_pull(f, buffer, 1);
tu_fifo_unlock(f);
@@ -132,8 +137,8 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
/******************************************************************************/
/*!
@brief This function will read n elements into the array index specified by
the write pointer and increment the write index. If the write index
@brief This function will read n elements from the array index specified by
the read pointer and increment the read index. If the read index
exceeds the max buffer size, then it will roll over to zero.
@param[in] f
@@ -148,32 +153,37 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
/******************************************************************************/
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count)
{
if( tu_fifo_empty(f) ) return 0;
if(tu_fifo_empty(f)) return 0;
tu_fifo_lock(f);
/* Limit up to fifo's count */
if ( count > f->count ) count = f->count;
// Limit up to fifo's count
if(count > f->count) count = f->count;
uint8_t* buf8 = (uint8_t*) buffer;
uint16_t len = 0;
while (len < count)
if(count + f->rd_idx <= f->depth)
{
_tu_ff_pull(f, buf8);
_ff_pull(f, buffer, count);
}
else
{
uint16_t const part1 = f->depth - f->rd_idx;
len++;
buf8 += f->item_size;
// Part 1: from rd_idx to end
_ff_pull(f, buffer, part1);
buffer = ((uint8_t*) buffer) + part1*f->item_size;
// Part 2: start to remaining
_ff_pull(f, buffer, count-part1);
}
tu_fifo_unlock(f);
return len;
return count;
}
/******************************************************************************/
/*!
@brief Reads one item without removing it from the FIFO
@brief Read one item without removing it from the FIFO
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -189,12 +199,16 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
{
if ( pos >= f->count ) return false;
tu_fifo_lock(f);
// rd_idx is pos=0
uint16_t index = (f->rd_idx + pos) % f->depth;
uint16_t index = _ff_mod(f->rd_idx + pos, f->depth);
memcpy(p_buffer,
f->buffer + (index * f->item_size),
f->item_size);
tu_fifo_unlock(f);
return true;
}
@@ -221,7 +235,7 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
tu_fifo_lock(f);
_tu_ff_push(f, data);
_ff_push(f, data, 1);
tu_fifo_unlock(f);
@@ -249,23 +263,42 @@ uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
tu_fifo_lock(f);
// Not overwritable limit up to full
if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f));
uint8_t const* buf8 = (uint8_t const*) data;
uint16_t len = 0;
while (len < count)
if (!f->overwritable)
{
_tu_ff_push(f, buf8);
len++;
buf8 += f->item_size;
// Not overwritable limit up to full
count = tu_min16(count, tu_fifo_remaining(f));
}
else if (count > f->depth)
{
// Only copy last part
buf8 = buf8 + (count - f->depth) * f->item_size;
count = f->depth;
f->wr_idx = 0;
f->rd_idx = 0;
f->count = 0;
}
if (count + f->wr_idx <= f->depth )
{
_ff_push(f, buf8, count);
}
else
{
uint16_t const part1 = f->depth - f->wr_idx;
// Part 1: from wr_idx to end
_ff_push(f, buf8, part1);
buf8 += part1*f->item_size;
// Part 2: start to remaining
_ff_push(f, buf8, count-part1);
}
tu_fifo_unlock(f);
return len;
return count;
}
/******************************************************************************/

View File

@@ -60,11 +60,17 @@ typedef struct TU_ATTR_ALIGNED(4)
uint8_t rhport;
uint8_t event_id;
union {
// USBD_EVT_SETUP_RECEIVED
union
{
// BUS RESET
struct {
tusb_speed_t speed;
} bus_reset;
// SETUP_RECEIVED
tusb_control_request_t setup_received;
// USBD_EVT_XFER_COMPLETE
// XFER_COMPLETE
struct {
uint8_t ep_addr;
uint8_t result;
@@ -143,6 +149,9 @@ extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
// helper to send bus signal event
extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
// helper to send bus reset event
extern void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr);
// helper to send setup received
extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);

View File

@@ -40,7 +40,8 @@
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
typedef struct {
typedef struct
{
struct TU_ATTR_PACKED
{
volatile uint8_t connected : 1;
@@ -53,6 +54,8 @@ typedef struct {
uint8_t self_powered : 1; // configuration descriptor's attribute
};
uint8_t speed;
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
@@ -185,6 +188,19 @@ static usbd_class_driver_t const _usbd_driver[] =
.sof = NULL,
},
#endif
#if CFG_TUD_BTH
{
DRIVER_NAME("BTH")
.init = btd_init,
.reset = btd_reset,
.open = btd_open,
.control_request = btd_control_request,
.control_complete = btd_control_complete,
.xfer_cb = btd_xfer_cb,
.sof = NULL
},
#endif
};
enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
@@ -288,6 +304,11 @@ void usbd_driver_print_control_complete_name(bool (*control_complete) (uint8_t,
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
tusb_speed_t tud_speed_get(void)
{
return (tusb_speed_t) _usbd_dev.speed;
}
bool tud_mounted(void)
{
return _usbd_dev.configured;
@@ -335,7 +356,6 @@ bool tud_init (void)
// Init device controller driver
dcd_init(TUD_OPT_RHPORT);
tud_connect();
dcd_int_enable(TUD_OPT_RHPORT);
return true;
@@ -356,6 +376,14 @@ static void usbd_reset(uint8_t rhport)
}
}
bool tud_task_event_ready(void)
{
// Skip if stack is not initialized
if ( !tusb_inited() ) return false;
return !osal_queue_empty(_usbd_q);
}
/* USB Device Driver task
* This top level thread manages all device controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
@@ -386,16 +414,21 @@ void tud_task (void)
if ( !osal_queue_receive(_usbd_q, &event) ) return;
TU_LOG2("USBD %s", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
TU_LOG2("%s", (event.event_id != DCD_EVENT_XFER_COMPLETE && event.event_id != DCD_EVENT_SETUP_RECEIVED) ? "\r\n" : " ");
#if CFG_TUSB_DEBUG >= 2
if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG2("\r\n"); // extra line for setup
TU_LOG2("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
#endif
switch ( event.event_id )
{
case DCD_EVENT_BUS_RESET:
TU_LOG2("\r\n");
usbd_reset(event.rhport);
_usbd_dev.speed = event.bus_reset.speed;
break;
case DCD_EVENT_UNPLUGGED:
TU_LOG2("\r\n");
usbd_reset(event.rhport);
// invoke callback
@@ -433,7 +466,7 @@ void tud_task (void)
if ( 0 == epnum )
{
usbd_control_xfer_cb(event.rhport, ep_addr, 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
{
@@ -447,14 +480,17 @@ void tud_task (void)
break;
case DCD_EVENT_SUSPEND:
TU_LOG2("\r\n");
if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
break;
case DCD_EVENT_RESUME:
TU_LOG2("\r\n");
if (tud_resume_cb) tud_resume_cb();
break;
case DCD_EVENT_SOF:
TU_LOG2("\r\n");
for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
{
usbd_class_driver_t const * driver = get_driver(i);
@@ -463,6 +499,7 @@ void tud_task (void)
break;
case USBD_EVENT_FUNC_CALL:
TU_LOG2("\r\n");
if ( event.func_call.func ) event.func_call.func(event.func_call.param);
break;
@@ -514,6 +551,17 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
{
//------------- Device Requests e.g in enumeration -------------//
case TUSB_REQ_RCPT_DEVICE:
if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type )
{
uint8_t const itf = tu_u16_low(p_request->wIndex);
TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]);
TU_VERIFY(driver);
// forward to class driver: "non-STD request to Interface"
return invoke_class_control(rhport, driver, p_request);
}
if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
{
// Non standard request is not supported
@@ -595,7 +643,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
uint8_t const itf = tu_u16_low(p_request->wIndex);
TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]);
usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]);
TU_VERIFY(driver);
// all requests to Interface (STD or Class) is forwarded to class driver.
@@ -719,16 +767,20 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc;
uint8_t drv_id;
uint16_t drv_len;
uint16_t const remaining_len = desc_end-p_desc;
uint8_t drv_id;
for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++)
{
usbd_class_driver_t const *driver = get_driver(drv_id);
drv_len = 0;
if ( driver->open(rhport, desc_itf, &drv_len) )
uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len);
if ( drv_len > 0 )
{
// Open successfully, check if length is correct
TU_ASSERT( sizeof(tusb_desc_interface_t) <= drv_len && drv_len <= remaining_len);
// Interface number must not be used already
TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]);
@@ -738,11 +790,9 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
// If IAD exist, assign all interfaces to the same driver
if (desc_itf_assoc)
{
// IAD's first interface number and class/subclass/protocol should match with opened interface
TU_ASSERT(desc_itf_assoc->bFirstInterface == desc_itf->bInterfaceNumber &&
desc_itf_assoc->bFunctionClass == desc_itf->bInterfaceClass &&
desc_itf_assoc->bFunctionSubClass == desc_itf->bInterfaceSubClass &&
desc_itf_assoc->bFunctionProtocol == desc_itf->bInterfaceProtocol);
// IAD's first interface number and class should match with opened interface
TU_ASSERT(desc_itf_assoc->bFirstInterface == desc_itf->bInterfaceNumber &&
desc_itf_assoc->bFunctionClass == desc_itf->bInterfaceClass);
for(uint8_t i=1; i<desc_itf_assoc->bInterfaceCount; i++)
{
@@ -750,16 +800,16 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
}
}
mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, drv_len, drv_id); // TODO refactor
p_desc += drv_len; // next interface
break;
}
}
// Assert if cannot find supported driver
TU_ASSERT( drv_id < TOTAL_DRIVER_COUNT && drv_len >= sizeof(tusb_desc_interface_t) );
mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, drv_len, drv_id); // TODO refactor
p_desc += drv_len; // next interface
// Failed if cannot find supported driver
TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT);
}
// invoke callback
@@ -823,8 +873,10 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
if (!tud_descriptor_bos_cb) return false;
tusb_desc_bos_t const* desc_bos = (tusb_desc_bos_t const*) tud_descriptor_bos_cb();
uint16_t total_len;
memcpy(&total_len, &desc_bos->wTotalLength, 2); // possibly mis-aligned memory
// Use offsetof to avoid pointer to the odd/misaligned address
memcpy(&total_len, (uint8_t*) desc_bos + offsetof(tusb_desc_bos_t, wTotalLength), 2);
return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len);
}
@@ -838,7 +890,8 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
TU_ASSERT(desc_config);
uint16_t total_len;
memcpy(&total_len, &desc_config->wTotalLength, 2); // possibly mis-aligned memory
// Use offsetof to avoid pointer to the odd/misaligned address
memcpy(&total_len, (uint8_t*) desc_config + offsetof(tusb_desc_configuration_t, wTotalLength), 2);
return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len);
}
@@ -848,34 +901,41 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
TU_LOG2(" String[%u]\r\n", desc_index);
// String Descriptor always uses the desc set from user
if ( desc_index == 0xEE )
{
// The 0xEE index string is a Microsoft OS Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
return false;
}
else
{
uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex);
TU_ASSERT(desc_str);
uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex);
TU_VERIFY(desc_str);
// first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]);
}
// first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]);
break;
case TUSB_DESC_DEVICE_QUALIFIER:
TU_LOG2(" Device Qualifier\r\n");
// TODO If not highspeed capable stall this request otherwise
// return the descriptor that could work in highspeed
return false;
// Host sends this request to ask why our device with USB BCD from 2.0
// but is running at Full/Low Speed. If not highspeed capable stall this request,
// otherwise return the descriptor that could work in highspeed mode
if ( tud_descriptor_device_qualifier_cb )
{
uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb();
TU_ASSERT(desc_qualifier);
// first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) desc_qualifier, desc_qualifier[0]);
}else
{
return false;
}
break;
case TUSB_DESC_OTHER_SPEED_CONFIG:
TU_LOG2(" Other Speed Configuration\r\n");
// After Device Qualifier descriptor is received host will ask for this descriptor
return false; // not supported
break;
default: return false;
}
return true;
}
//--------------------------------------------------------------------+
@@ -925,7 +985,14 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = eid, };
dcd_event_t event = { .rhport = rhport, .event_id = eid };
dcd_event_handler(&event, in_isr);
}
void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
event.bus_reset.speed = speed;
dcd_event_handler(&event, in_isr);
}
@@ -1009,12 +1076,21 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
TU_VERIFY( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) );
// 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;
TU_LOG2("OK\r\n");
return true;
if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) )
{
TU_LOG2("OK\r\n");
return true;
}else
{
_usbd_dev.ep_status[epnum][dir].busy = false;
TU_LOG2("failed\r\n");
TU_BREAKPOINT();
return false;
}
}
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)

View File

@@ -42,14 +42,22 @@
//--------------------------------------------------------------------+
// Init device stack
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
bool tud_init (void);
// Task function should be called in main/rtos loop
void tud_task (void);
// Check if there is pending events need proccessing by tud_task()
bool tud_task_event_ready(void);
// Interrupt handler, name alias to DCD
#define tud_int_handler dcd_int_handler
// Get current bus speed
tusb_speed_t tud_speed_get(void);
// Check if device is connected and configured
bool tud_mounted(void);
@@ -65,6 +73,8 @@ static inline bool tud_ready(void)
// Remote wake up host, only if suspended and enabled by host
bool tud_remote_wakeup(void);
// Enable pull-up resistor on D+ D-
// Return false on unsupported MCUs
static inline bool tud_disconnect(void)
{
TU_VERIFY(dcd_disconnect);
@@ -72,6 +82,8 @@ static inline bool tud_disconnect(void)
return true;
}
// Disable pull-up resistor on D+ D-
// Return false on unsupported MCUs
static inline bool tud_connect(void)
{
TU_VERIFY(dcd_connect);
@@ -107,6 +119,10 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index);
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid);
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void);
// Invoked when device is mounted (configured)
TU_ATTR_WEAK void tud_mount_cb(void);
@@ -122,6 +138,8 @@ TU_ATTR_WEAK void tud_resume_cb(void);
// Invoked when received control request with VENDOR TYPE
TU_ATTR_WEAK bool tud_vendor_control_request_cb(uint8_t rhport, tusb_control_request_t const * request);
// Invoked when vendor control request is complete
TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request);
@@ -435,6 +453,60 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
//------------- BT Radio -------------//
#define TUD_BT_APP_CLASS (TUSB_CLASS_WIRELESS_CONTROLLER)
#define TUD_BT_APP_SUBCLASS 0x01
#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01
#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02
#ifndef CFG_TUD_BTH_ISO_ALT_COUNT
#define CFG_TUD_BTH_ISO_ALT_COUNT 0
#endif
// Length of template descriptor: 30 bytes + number of ISO alternatives * 23
#define TUD_BTH_DESC_LEN (9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7))
/* Primary Interface */
#define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
9, TUSB_DESC_INTERFACE, _itfnum, _stridx, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \
/* Endpoint In for events */ \
7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \
/* Endpoint In for ACL data */ \
7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \
/* Endpoint Out for ACL data */ \
7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1
#define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\
/* Interface with 2 endpoints */ \
9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \
/* Isochronous endpoints */ \
7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \
7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1
#define _FIRST(a, ...) a
#define _REST(a, ...) __VA_ARGS__
#define TUD_BTH_ISO_ITF_0(_itfnum, ...)
#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \
TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__)
// BT Primary controller descriptor
// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes
#define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \
TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__)
#ifdef __cplusplus
}

View File

@@ -32,6 +32,10 @@
#include "device/usbd_pvt.h"
#include "dcd.h"
#if CFG_TUSB_DEBUG >= 2
extern void usbd_driver_print_control_complete_name(bool (*control_complete) (uint8_t, tusb_control_request_t const *));
#endif
enum
{
EDPT_CTRL_OUT = 0x00,
@@ -192,7 +196,6 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
if ( _ctrl_xfer.complete_cb )
{
#if CFG_TUSB_DEBUG >= 2
extern void usbd_driver_print_control_complete_name(bool (*control_complete) (uint8_t, tusb_control_request_t const *));
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
#endif

View File

@@ -39,17 +39,17 @@
typedef struct
{
#if CFG_TUSB_DEBUG >= 2
#if CFG_TUSB_DEBUG >= 2
char const* name;
#endif
#endif
void (* init ) (void);
void (* reset ) (uint8_t rhport);
bool (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* control_complete ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void (* sof ) (uint8_t rhport); /* optional */
void (* init ) (void);
void (* reset ) (uint8_t rhport);
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* control_complete ) (uint8_t rhport, tusb_control_request_t const * request);
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void (* sof ) (uint8_t rhport); /* optional */
} usbd_class_driver_t;
// Invoked when initializing device stack to get additional class drivers.

View File

@@ -91,6 +91,9 @@ TU_ATTR_WEAK void tuh_umount_cb(uint8_t dev_addr);
//--------------------------------------------------------------------+
// CLASS-USBH & INTERNAL API
//--------------------------------------------------------------------+
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
bool usbh_init(void);
bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t* request, uint8_t* data);

View File

@@ -78,8 +78,9 @@ static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
//------------- Queue -------------//
static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data);
static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr);
static inline bool osal_queue_receive(osal_queue_t qhdl, void* data);
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
static inline bool osal_queue_empty(osal_queue_t qhdl);
#if 0 // TODO remove subtask related macros later
// Sub Task

View File

@@ -58,7 +58,23 @@ static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semde
static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
{
return in_isr ? xSemaphoreGiveFromISR(sem_hdl, NULL) : xSemaphoreGive(sem_hdl);
if ( !in_isr )
{
return xSemaphoreGive(sem_hdl) != 0;
}
else
{
BaseType_t xHigherPriorityTaskWoken;
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}
static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
@@ -118,14 +134,35 @@ static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
return xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
}
static inline bool osal_queue_receive(osal_queue_t const queue_hdl, void* data)
static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
{
return xQueueReceive(queue_hdl, data, portMAX_DELAY);
return xQueueReceive(qhdl, data, portMAX_DELAY);
}
static inline bool osal_queue_send(osal_queue_t const queue_hdl, void const * data, bool in_isr)
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
{
return in_isr ? xQueueSendToBackFromISR(queue_hdl, data, NULL) : xQueueSendToBack(queue_hdl, data, OSAL_TIMEOUT_WAIT_FOREVER);
if ( !in_isr )
{
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
}
else
{
BaseType_t xHigherPriorityTaskWoken;
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}
static inline bool osal_queue_empty(osal_queue_t qhdl)
{
return uxQueueMessagesWaiting(qhdl) == 0;
}
#ifdef __cplusplus

View File

@@ -125,7 +125,7 @@ static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
return (osal_queue_t) qdef;
}
static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data)
static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
{
struct os_event* ev;
ev = os_eventq_get(&qhdl->evq);
@@ -137,7 +137,7 @@ static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data)
return true;
}
static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr)
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
{
(void) in_isr;
@@ -161,6 +161,12 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b
return true;
}
static inline bool osal_queue_empty(osal_queue_t qhdl)
{
return STAILQ_EMPTY(&qhdl->evq.evq_list);
}
#ifdef __cplusplus
}
#endif

View File

@@ -142,7 +142,7 @@ typedef osal_queue_def_t* osal_queue_t;
}\
}
// lock queue by disable usb isr
// lock queue by disable USB interrupt
static inline void _osal_q_lock(osal_queue_t qhdl)
{
(void) qhdl;
@@ -176,8 +176,7 @@ static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
return (osal_queue_t) qdef;
}
// non blocking
static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data)
static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
{
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
@@ -186,7 +185,7 @@ static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data)
return success;
}
static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr)
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
{
if (!in_isr) {
_osal_q_lock(qhdl);
@@ -203,6 +202,13 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b
return success;
}
static inline bool osal_queue_empty(osal_queue_t qhdl)
{
// Skip queue lock/unlock since this function is primarily called
// with interrupt disabled before going into low power mode
return tu_fifo_empty(&qhdl->ff);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,842 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jerzy Kasenberg
*
* 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.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_DA1469X
#include "DA1469xAB.h"
#include "device/dcd.h"
/*------------------------------------------------------------------*/
/* MACRO TYPEDEF CONSTANT ENUM
*------------------------------------------------------------------*/
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#define USE_SOF 0
#define EP_MAX 4
#define NFSR_NODE_RESET 0
#define NFSR_NODE_RESUME 1
#define NFSR_NODE_OPERATIONAL 2
#define NFSR_NODE_SUSPEND 3
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
typedef struct
{
union
{
__IOM uint32_t epc_in;
__IOM uint32_t USB_EPC0_REG; /*!< (@ 0x00000080) Endpoint Control 0 Register */
__IOM uint32_t USB_EPC1_REG; /*!< (@ 0x000000A0) Endpoint Control Register 1 */
__IOM uint32_t USB_EPC3_REG; /*!< (@ 0x000000C0) Endpoint Control Register 3 */
__IOM uint32_t USB_EPC5_REG; /*!< (@ 0x000000E0) Endpoint Control Register 5 */
};
union
{
__IOM uint32_t txd;
__IOM uint32_t USB_TXD0_REG; /*!< (@ 0x00000084) Transmit Data 0 Register */
__IOM uint32_t USB_TXD1_REG; /*!< (@ 0x000000A4) Transmit Data Register 1 */
__IOM uint32_t USB_TXD2_REG; /*!< (@ 0x000000C4) Transmit Data Register 2 */
__IOM uint32_t USB_TXD3_REG; /*!< (@ 0x000000E4) Transmit Data Register 3 */
};
union
{
__IOM uint32_t txs;
__IOM uint32_t USB_TXS0_REG; /*!< (@ 0x00000088) Transmit Status 0 Register */
__IOM uint32_t USB_TXS1_REG; /*!< (@ 0x000000A8) Transmit Status Register 1 */
__IOM uint32_t USB_TXS2_REG; /*!< (@ 0x000000C8) Transmit Status Register 2 */
__IOM uint32_t USB_TXS3_REG; /*!< (@ 0x000000E8) Transmit Status Register 3 */
};
union
{
__IOM uint32_t txc;
__IOM uint32_t USB_TXC0_REG; /*!< (@ 0x0000008C) Transmit command 0 Register */
__IOM uint32_t USB_TXC1_REG; /*!< (@ 0x000000AC) Transmit Command Register 1 */
__IOM uint32_t USB_TXC2_REG; /*!< (@ 0x000000CC) Transmit Command Register 2 */
__IOM uint32_t USB_TXC3_REG; /*!< (@ 0x000000EC) Transmit Command Register 3 */
};
union
{
__IOM uint32_t epc_out;
__IOM uint32_t USB_EP0_NAK_REG; /*!< (@ 0x00000090) EP0 INNAK and OUTNAK Register */
__IOM uint32_t USB_EPC2_REG; /*!< (@ 0x000000B0) Endpoint Control Register 2 */
__IOM uint32_t USB_EPC4_REG; /*!< (@ 0x000000D0) Endpoint Control Register 4 */
__IOM uint32_t USB_EPC6_REG; /*!< (@ 0x000000F0) Endpoint Control Register 6 */
};
union
{
__IOM uint32_t rxd;
__IOM uint32_t USB_RXD0_REG; /*!< (@ 0x00000094) Receive Data 0 Register */
__IOM uint32_t USB_RXD1_REG; /*!< (@ 0x000000B4) Receive Data Register,1 */
__IOM uint32_t USB_RXD2_REG; /*!< (@ 0x000000D4) Receive Data Register 2 */
__IOM uint32_t USB_RXD3_REG; /*!< (@ 0x000000F4) Receive Data Register 3 */
};
union
{
__IOM uint32_t rxs;
__IOM uint32_t USB_RXS0_REG; /*!< (@ 0x00000098) Receive Status 0 Register */
__IOM uint32_t USB_RXS1_REG; /*!< (@ 0x000000B8) Receive Status Register 1 */
__IOM uint32_t USB_RXS2_REG; /*!< (@ 0x000000D8) Receive Status Register 2 */
__IOM uint32_t USB_RXS3_REG; /*!< (@ 0x000000F8) Receive Status Register 3 */
};
union
{
__IOM uint32_t rxc;
__IOM uint32_t USB_RXC0_REG; /*!< (@ 0x0000009C) Receive Command 0 Register */
__IOM uint32_t USB_RXC1_REG; /*!< (@ 0x000000BC) Receive Command Register 1 */
__IOM uint32_t USB_RXC2_REG; /*!< (@ 0x000000DC) Receive Command Register 2 */
__IOM uint32_t USB_RXC3_REG; /*!< (@ 0x000000FC) Receive Command Register 3 */
};
} EPx_REGS;
#define EP_REGS(first_ep_reg) (EPx_REGS*)(&USB->first_ep_reg)
// Dialog register fields and bit mask are very long. Filed masks repeat register names.
// Those convenience macros are a way to reduce complexity of register modification lines.
#define GET_BIT(val, field) (val & field ## _Msk) >> field ## _Pos
#define REG_GET_BIT(reg, field) (USB->reg & USB_ ## reg ## _ ## field ## _Msk)
#define REG_SET_BIT(reg, field) USB->reg |= USB_ ## reg ## _ ## field ## _Msk
#define REG_CLR_BIT(reg, field) USB->reg &= ~USB_ ## reg ## _ ## field ## _Msk
#define REG_SET_VAL(reg, field, val) USB->reg = (USB->reg & ~USB_ ## reg ## _ ## field ## _Msk) | (val << USB_ ## reg ## _ ## field ## _Pos)
typedef struct {
EPx_REGS * regs;
uint8_t * buffer;
// Total length of current transfer
uint16_t total_len;
// Bytes transferred so far
uint16_t transferred;
uint16_t max_packet_size;
// Packet size sent or received so far. It is used to modify transferred field
// after ACK is received or when filling ISO endpoint with size larger then
// FIFO size.
uint16_t last_packet_size;
uint8_t ep_addr;
// DATA0/1 toggle bit 1 DATA1 is expected or transmitted
uint8_t data1 : 1;
// Endpoint is stalled
uint8_t stall : 1;
} xfer_ctl_t;
static struct
{
bool vbus_present;
bool in_reset;
xfer_ctl_t xfer_status[EP_MAX][2];
} _dcd =
{
.vbus_present = false,
.xfer_status =
{
{ { .regs = EP_REGS(USB_EPC0_REG) }, { .regs = EP_REGS(USB_EPC0_REG) } },
{ { .regs = EP_REGS(USB_EPC1_REG) }, { .regs = EP_REGS(USB_EPC1_REG) } },
{ { .regs = EP_REGS(USB_EPC3_REG) }, { .regs = EP_REGS(USB_EPC3_REG) } },
{ { .regs = EP_REGS(USB_EPC5_REG) }, { .regs = EP_REGS(USB_EPC5_REG) } },
}
};
// Two endpoint 0 descriptor definition for unified dcd_edpt_open()
static const tusb_desc_endpoint_t ep0OUT_desc =
{
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
.bEndpointAddress = 0x00,
.bmAttributes = { .xfer = TUSB_XFER_CONTROL },
.wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
.bInterval = 0
};
static const tusb_desc_endpoint_t ep0IN_desc =
{
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
.bEndpointAddress = 0x80,
.bmAttributes = { .xfer = TUSB_XFER_CONTROL },
.wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
.bInterval = 0
};
#define XFER_CTL_BASE(_ep, _dir) &_dcd.xfer_status[_ep][_dir]
// Function could be called when VBUS change was detected.
void tusb_vbus_changed(bool present)
{
if (present != _dcd.vbus_present)
{
_dcd.vbus_present = present;
if (present)
{
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk |
USB_USB_MAMSK_REG_USB_M_WARN_Msk;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk;
}
else
{
USB->USB_MCTRL_REG = 0;
}
}
}
static void transmit_packet(xfer_ctl_t * xfer)
{
int left_to_send;
uint8_t const *src;
EPx_REGS *regs = xfer->regs;
uint32_t txc;
txc = USB_USB_TXC1_REG_USB_TX_EN_Msk;
if (xfer->data1) txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk;
src = &xfer->buffer[xfer->transferred];
left_to_send = xfer->total_len - xfer->transferred;
if (left_to_send > xfer->max_packet_size - xfer->last_packet_size)
{
left_to_send = xfer->max_packet_size - xfer->last_packet_size;
}
// Loop checks TCOUNT all the time since this value is saturated to 31
// and can't be read just once before.
while ((regs->txs & USB_USB_TXS1_REG_USB_TCOUNT_Msk) > 0 && left_to_send > 0)
{
regs->txd = *src++;
xfer->last_packet_size++;
left_to_send--;
}
if (tu_edpt_number(xfer->ep_addr) != 0)
{
if (left_to_send > 0)
{
// Max packet size is set to value greater then FIFO. Enable fifo level warning
// to handle larger packets.
txc |= USB_USB_TXC1_REG_USB_TFWL_Msk;
}
else
{
// Whole packet already in fifo, no need to refill it later. Mark last.
txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
}
}
// Enable transfer with correct interrupts enabled
regs->txc = txc;
}
static void receive_packet(xfer_ctl_t *xfer, uint16_t bytes_in_fifo)
{
EPx_REGS *regs = xfer->regs;
uint16_t remaining = xfer->total_len - xfer->transferred;
uint16_t receive_this_time = bytes_in_fifo;
if (remaining <= bytes_in_fifo) receive_this_time = remaining;
uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size;
for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd;
xfer->transferred += receive_this_time;
xfer->last_packet_size += receive_this_time;
}
static void handle_ep0_rx(void)
{
int packet_size;
uint32_t rxs0 = USB->USB_RXS0_REG;
xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_OUT);
packet_size = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT);
if (rxs0 & USB_USB_RXS0_REG_USB_SETUP_Msk)
{
xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN);
// Setup packet is in
for (int i = 0; i < packet_size; ++i) _setup_packet[i] = USB->USB_RXD0_REG;
xfer->stall = 0;
xfer->data1 = 1;
xfer_in->stall = 0;
xfer_in->data1 = 1;
REG_SET_BIT(USB_TXC0_REG, USB_TOGGLE_TX0);
REG_CLR_BIT(USB_EPC0_REG, USB_STALL);
dcd_event_setup_received(0, _setup_packet,true);
}
else
{
if (GET_BIT(rxs0, USB_USB_RXS0_REG_USB_TOGGLE_RX0) != xfer->data1)
{
// Toggle bit does not match discard packet
REG_SET_BIT(USB_RXC0_REG, USB_FLUSH);
}
else
{
receive_packet(xfer, packet_size);
xfer->data1 ^= 1;
if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
{
dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true);
}
else
{
xfer->last_packet_size = 0;
// Re-enable reception
REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
}
}
}
}
static void handle_ep0_tx(void)
{
uint32_t txs0;
xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_IN);
EPx_REGS *regs = xfer->regs;
txs0 = regs->USB_TXS0_REG;
if (GET_BIT(txs0, USB_USB_TXS0_REG_USB_TX_DONE))
{
// ACK received
if (GET_BIT(txs0, USB_USB_TXS0_REG_USB_ACK_STAT))
{
xfer->transferred += xfer->last_packet_size;
xfer->last_packet_size = 0;
xfer->data1 ^= 1;
REG_SET_VAL(USB_TXC0_REG, USB_TOGGLE_TX0, xfer->data1);
if (xfer->transferred == xfer->total_len)
{
dcd_event_xfer_complete(0, 0 | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
return;
}
}
else
{
// Start from the beginning
xfer->last_packet_size = 0;
}
transmit_packet(xfer);
}
}
static void handle_epx_rx_ev(uint8_t ep)
{
uint32_t rxs;
int packet_size;
xfer_ctl_t *xfer = XFER_CTL_BASE(ep, TUSB_DIR_OUT);
EPx_REGS *regs = xfer->regs;
rxs = regs->rxs;
if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR))
{
regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
}
else
{
packet_size = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT);
receive_packet(xfer, packet_size);
if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST))
{
if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1)
{
// Toggle bit does not match discard packet
regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
}
else
{
xfer->data1 ^= 1;
if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
{
dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true);
}
else
{
xfer->last_packet_size = 0;
// Re-enable reception
regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
}
}
}
}
}
static void handle_rx_ev(void)
{
if (USB->USB_RXEV_REG & 1)
handle_epx_rx_ev(1);
if (USB->USB_RXEV_REG & 2)
handle_epx_rx_ev(2);
if (USB->USB_RXEV_REG & 4)
handle_epx_rx_ev(3);
}
static void handle_epx_tx_ev(xfer_ctl_t *xfer)
{
uint32_t usb_txs1_reg;
EPx_REGS *regs = xfer->regs;
usb_txs1_reg = regs->USB_TXS1_REG;
if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_TX_DONE))
{
if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_ACK_STAT))
{
// ACK received, update transfer state and DATA0/1 bit
xfer->transferred += xfer->last_packet_size;
xfer->last_packet_size = 0;
xfer->data1 ^= 1;
if (xfer->transferred == xfer->total_len)
{
dcd_event_xfer_complete(0, xfer->ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true);
return;
}
}
else
{
xfer->last_packet_size = 0;
}
transmit_packet(xfer);
}
}
static void handle_tx_ev(void)
{
if (USB->USB_TXEV_REG & 1)
handle_epx_tx_ev(XFER_CTL_BASE(1, TUSB_DIR_IN));
if (USB->USB_TXEV_REG & 2)
handle_epx_tx_ev(XFER_CTL_BASE(2, TUSB_DIR_IN));
if (USB->USB_TXEV_REG & 4)
handle_epx_tx_ev(XFER_CTL_BASE(3, TUSB_DIR_IN));
}
static void handle_bus_reset(void)
{
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_ALTMSK_REG = 0;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
(void)USB->USB_ALTEV_REG;
_dcd.in_reset = true;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
#if USE_SOF
USB_USB_MAMSK_REG_USB_M_FRAME_Msk |
#endif
USB_USB_MAMSK_REG_USB_M_WARN_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk;
USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_SD3_Msk |
USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
// There is no information about end of reset state
// USB_FRAME event will be used to enable reset detection again
REG_SET_BIT(USB_MAEV_REG, USB_FRAME);
dcd_edpt_open (0, &ep0OUT_desc);
dcd_edpt_open (0, &ep0IN_desc);
}
static void handle_alt_ev(void)
{
uint32_t alt_ev = USB->USB_ALTEV_REG;
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET))
{
handle_bus_reset();
}
else
{
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESUME))
{
USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_SD3_Msk;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_SD3))
{
USB->USB_NFSR_REG = NFSR_NODE_SUSPEND;
USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_SD3_Msk | USB_USB_ALTMSK_REG_USB_M_SD5_Msk;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
}
}
static void handle_epx_tx_refill(uint8_t ep)
{
transmit_packet(XFER_CTL_BASE(ep, TUSB_DIR_IN));
}
static void handle_fifo_warning(void)
{
uint32_t fifo_warning = USB->USB_FWEV_REG;
if (fifo_warning & 0x01)
handle_epx_tx_refill(1);
if (fifo_warning & 0x02)
handle_epx_tx_refill(2);
if (fifo_warning & 0x04)
handle_epx_tx_refill(3);
if (fifo_warning & 0x10)
handle_epx_rx_ev(1);
if (fifo_warning & 0x20)
handle_epx_rx_ev(2);
if (fifo_warning & 0x40)
handle_epx_rx_ev(3);
}
static void handle_ep0_nak(void)
{
uint32_t ep0_nak = USB->USB_EP0_NAK_REG;
if (REG_GET_BIT(USB_EPC0_REG, USB_STALL))
{
if (GET_BIT(ep0_nak, USB_USB_EP0_NAK_REG_USB_EP0_INNAK))
{
// EP0 is stalled and NAK was sent, it means that RX is enabled
// Disable RX for now.
REG_CLR_BIT(USB_RXC0_REG, USB_RX_EN);
REG_SET_BIT(USB_TXC0_REG, USB_TX_EN);
}
if (GET_BIT(ep0_nak, USB_USB_EP0_NAK_REG_USB_EP0_OUTNAK))
{
REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
}
}
else
{
REG_CLR_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
}
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
void dcd_init(uint8_t rhport)
{
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
tusb_vbus_changed((CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_VBUS_AVAILABLE_Msk) != 0);
dcd_connect(rhport);
}
void dcd_int_enable(uint8_t rhport)
{
(void)rhport;
NVIC_EnableIRQ(USB_IRQn);
}
void dcd_int_disable(uint8_t rhport)
{
(void)rhport;
NVIC_DisableIRQ(USB_IRQn);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
// Set default address for one ZLP
USB->USB_EPC0_REG = USB_USB_EPC0_REG_USB_DEF_Msk;
USB->USB_FAR_REG = (dev_addr & USB_USB_FAR_REG_USB_AD_Msk) | USB_USB_FAR_REG_USB_AD_EN_Msk;
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
}
void dcd_connect(uint8_t rhport)
{
(void)rhport;
REG_SET_BIT(USB_MCTRL_REG, USB_NAT);
}
void dcd_disconnect(uint8_t rhport)
{
(void)rhport;
REG_CLR_BIT(USB_MCTRL_REG, USB_NAT);
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
uint8_t iso_mask = 0;
(void)rhport;
TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 1023);
TU_ASSERT(epnum < EP_MAX);
xfer->max_packet_size = desc_edpt->wMaxPacketSize.size;
xfer->ep_addr = desc_edpt->bEndpointAddress;
xfer->data1 = 0;
if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1) iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk;
if (epnum == 0)
{
USB->USB_MAMSK_REG |= USB_USB_MAMSK_REG_USB_M_EP0_RX_Msk |
USB_USB_MAMSK_REG_USB_M_EP0_TX_Msk;
}
else
{
if (dir == TUSB_DIR_OUT)
{
xfer->regs->epc_out = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
USB->USB_RXMSK_REG |= 0x101 << (epnum - 1);
REG_SET_BIT(USB_MAMSK_REG, USB_M_RX_EV);
}
else
{
xfer->regs->epc_in = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
USB->USB_TXMSK_REG |= 0x101 << (epnum - 1);
REG_SET_BIT(USB_MAMSK_REG, USB_M_TX_EV);
}
}
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
(void)rhport;
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->last_packet_size = 0;
xfer->transferred = 0;
if (dir == TUSB_DIR_OUT)
{
if (epnum != 0)
{
if (xfer->max_packet_size > 64)
{
// For endpoint size greater then FIFO size enable FIFO level warning interrupt
// when FIFO has less then 17 bytes free.
xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk;
}
else
{
// If max_packet_size would fit in FIFO no need for FIFO level warning interrupt.
xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk;
}
}
// USB_RX_EN bit is in same place for all endpoints.
xfer->regs->rxc = USB_USB_RXC0_REG_USB_RX_EN_Msk;
}
else // IN
{
transmit_packet(xfer);
}
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
(void)rhport;
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->stall = 1;
if (epnum == 0)
{
// EP0 has just one registers to control stall for IN and OUT
REG_SET_BIT(USB_EPC0_REG, USB_STALL);
if (dir == TUSB_DIR_OUT)
{
xfer->regs->USB_RXC0_REG = USB_USB_RXC0_REG_USB_RX_EN_Msk;
}
else
{
if (xfer->regs->USB_RXC0_REG & USB_USB_RXC0_REG_USB_RX_EN_Msk)
{
// If RX is also enabled TX will not be stalled since RX has
// higher priority. Enable NAK interrupt to handle stall.
REG_SET_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
}
else
{
xfer->regs->USB_TXC0_REG |= USB_USB_TXC0_REG_USB_TX_EN_Msk;
}
}
}
else
{
if (dir == TUSB_DIR_OUT)
{
xfer->regs->epc_out |= USB_USB_EPC1_REG_USB_STALL_Msk;
xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
}
else
{
xfer->regs->epc_in |= USB_USB_EPC1_REG_USB_STALL_Msk;
xfer->regs->txc |= USB_USB_TXC1_REG_USB_TX_EN_Msk | USB_USB_TXC1_REG_USB_LAST_Msk;
}
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
(void)rhport;
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
// Clear stall is called in response to Clear Feature ENDPOINT_HALT, reset toggle
xfer->data1 = 0;
xfer->stall = 0;
if (dir == TUSB_DIR_OUT)
{
xfer->regs->epc_out &= ~USB_USB_EPC1_REG_USB_STALL_Msk;
}
else
{
xfer->regs->epc_in &= ~USB_USB_EPC1_REG_USB_STALL_Msk;
}
if (epnum == 0)
{
REG_CLR_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
}
}
/*------------------------------------------------------------------*/
/* Interrupt Handler
*------------------------------------------------------------------*/
void dcd_int_handler(uint8_t rhport)
{
uint32_t int_status = USB->USB_MAEV_REG;
(void)rhport;
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_WARN))
{
handle_fifo_warning();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_CH_EV))
{
// TODO: for now just clear interrupt
(void)USB->USB_CHARGER_STAT_REG;
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_NAK))
{
handle_ep0_nak();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_RX))
{
handle_ep0_rx();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_TX))
{
handle_ep0_tx();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_RX_EV))
{
handle_rx_ev();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_NAK))
{
(void)USB->USB_NAKEV_REG;
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_FRAME))
{
if (_dcd.in_reset)
{
// Enable reset detection
_dcd.in_reset = false;
(void)USB->USB_ALTEV_REG;
}
#if USE_SOF
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
#else
// SOF was used to re-enable reset detection
// No need to keep it enabled
USB->USB_MAMSK_REG &= ~USB_USB_MAMSK_REG_USB_M_FRAME_Msk;
#endif
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_TX_EV))
{
handle_tx_ev();
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_ALT))
{
handle_alt_ev();
}
}
#endif

View File

@@ -54,6 +54,9 @@
// FIFO size in bytes
#define EP_FIFO_SIZE 1024
// Max number of IN EP FIFOs
#define EP_FIFO_NUM 5
typedef struct {
uint8_t *buffer;
uint16_t total_len;
@@ -71,6 +74,16 @@ static uint32_t _setup_packet[2];
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
static xfer_ctl_t xfer_status[EP_MAX][2];
// Keep count of how many FIFOs are in use
static uint8_t _allocated_fifos = 1; //FIFO0 is always in use
// Will either return an unused FIFO number, or 0 if all are used.
static uint8_t get_free_fifo(void)
{
if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++;
return 0;
}
// Setup the control endpoint 0.
static void bus_reset(void)
{
@@ -152,8 +165,6 @@ static void enum_done_processing(void)
*------------------------------------------------------------------*/
void dcd_init(uint8_t rhport)
{
(void)rhport;
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
@@ -191,6 +202,8 @@ void dcd_init(uint8_t rhport)
USB_ENUMDONEMSK_M |
USB_RESETDETMSK_M |
USB_DISCONNINTMSK_M; // host most only
dcd_connect(rhport);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
@@ -271,8 +284,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
// - Offset: GRXFSIZ + 16 + Size*(epnum-1)
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
uint8_t fifo_num = get_free_fifo();
TU_ASSERT(fifo_num != 0);
in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M);
in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
epnum << USB_D_TXFNUM1_S |
fifo_num << USB_D_TXFNUM1_S |
desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
desc_edpt->wMaxPacketSize.size << 0;
@@ -282,8 +299,8 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
// Both TXFD and TXSA are in unit of 32-bit words.
// IN FIFO 0 was configured during enumeration, hence the "+ 16".
uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16;
uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_MAX-1);
uint32_t const fifo_offset = allocated_size + fifo_size*(epnum-1);
uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1);
uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1);
// DIEPTXF starts at FIFO #1.
USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset;
@@ -361,7 +378,8 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
}
// Flush the FIFO, and wait until we have confirmed it cleared.
USB0.grstctl |= ((epnum - 1) << USB_TXFNUM_S);
uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V);
USB0.grstctl |= (fifo_num << USB_TXFNUM_S);
USB0.grstctl |= USB_TXFFLSH_M;
while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ;
} else {
@@ -637,6 +655,13 @@ static void handle_epin_ints(void)
USB0.dtknqr4_fifoemptymsk &= ~(1 << n);
}
}
// XFER Timeout
if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
// Clear interrupt or enpoint will hang.
USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
// Maybe retry?
}
}
}
}
@@ -653,6 +678,8 @@ static void _dcd_int_handler(void* arg)
// start of reset
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
USB0.gintsts = USB_USBRST_M;
// FIFOs will be reassigned when the endpoints are reopen
_allocated_fifos = 1;
bus_reset();
}

View File

@@ -26,7 +26,8 @@
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAMD21)
#if TUSB_OPT_DEVICE_ENABLED && \
(CFG_TUSB_MCU == OPT_MCU_SAMD21 || CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X)
#include "sam.h"
#include "device/dcd.h"
@@ -37,7 +38,6 @@
static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
// ready for receiving SETUP packet
static inline void prepare_setup(void)
{
@@ -64,7 +64,6 @@ static void bus_reset(void)
prepare_setup();
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
@@ -94,7 +93,7 @@ void dcd_init (uint8_t rhport)
USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST;
}
#if CFG_TUSB_MCU == OPT_MCU_SAMD51
#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
void dcd_int_enable(uint8_t rhport)
{
@@ -127,6 +126,11 @@ void dcd_int_disable(uint8_t rhport)
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
#else
#error "No implementation available for dcd_int_enable / dcd_int_disable"
#endif
void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
@@ -183,7 +187,7 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re
}
// Just finished status stage, prepare for next setup packet
// Note: we may already prepare setup when the last EP0 OUT complete.
// Note: we may already prepare setup when queueing the control status.
// but it has no harm to do it again here
prepare_setup();
}
@@ -235,6 +239,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
bank->ADDR.reg = (uint32_t) buffer;
// A SETUP token can occur immediately after an ZLP Status.
// So make sure we have a valid buffer for setup packet.
// Status = ZLP EP0 with direction opposite to one in the dir bit of current setup
if ( (epnum == 0) && (buffer == NULL) && (total_bytes == 0) && (dir != tu_edpt_dir(_setup_packet[0])) ) {
prepare_setup();
}
if ( dir == TUSB_DIR_OUT )
{
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
@@ -297,7 +309,7 @@ void maybe_transfer_complete(void) {
// Handle IN completions
if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN];
uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, total_transfer_size, XFER_RESULT_SUCCESS, true);
@@ -306,15 +318,8 @@ void maybe_transfer_complete(void) {
// Handle OUT completions
if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT];
uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
// A SETUP token can occur immediately after an OUT packet
// so make sure we have a valid buffer for the control endpoint.
if (epnum == 0) {
prepare_setup();
}
uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
dcd_event_xfer_complete(0, epnum, total_transfer_size, XFER_RESULT_SUCCESS, true);
@@ -381,7 +386,10 @@ void dcd_int_handler (uint8_t rhport)
// This copies the data elsewhere so we can reuse the buffer.
dcd_event_setup_received(0, _setup_packet, true);
USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
// Although Setup packet only set RXSTP bit,
// TRCPT0 bit could already be set by previous ZLP OUT Status (not handled until now).
// Since control status complete event is optional, we can just clear TRCPT0 and skip the status event
USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP | USB_DEVICE_EPINTFLAG_TRCPT0;
}
// Handle complete transfer

View File

@@ -51,12 +51,6 @@ typedef struct
// Endpoint 0-5, each can only be either OUT or In
xfer_desc_t _dcd_xfer[EP_COUNT];
// Indicate that DATA Toggle for Control Status is incorrect, which must always be DATA1 by USB Specs.
// However SAMG DToggle is read-only, therefore we must duplicate the status phase ( D0 then D1 )
// as walk-around to resolve this. The D0 status packet is likely to be discarded by USB Host safely.
// Note: Only needed for IN Status e.g CDC_SET_LINE_CODING, since out data is sent by host
volatile bool _walkaround_incorrect_dtoggle_control_status;
void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
{
xfer->epsize = epsize;
@@ -110,6 +104,32 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
}
}
//! Bitmap for all status bits in CSR that are not affected by a value 1.
#define CSR_NO_EFFECT_1_ALL (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT | UDP_CSR_RXSETUP | UDP_CSR_TXCOMP)
// Per Specs: CSR need synchronization each write
static inline void csr_write(uint8_t epnum, uint32_t value)
{
uint32_t const csr = value;
UDP->UDP_CSR[epnum] = csr;
volatile uint32_t nop_count;
for (nop_count = 0; nop_count < 20; nop_count ++) __NOP();
}
// Per Specs: CSR need synchronization each write
static inline void csr_set(uint8_t epnum, uint32_t mask)
{
csr_write(epnum, UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL | mask);
}
// Per Specs: CSR need synchronization each write
static inline void csr_clear(uint8_t epnum, uint32_t mask)
{
csr_write(epnum, (UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL) & ~mask);
}
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
@@ -117,13 +137,12 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
// Set up endpoint 0, clear all other endpoints
static void bus_reset(void)
{
_walkaround_incorrect_dtoggle_control_status = false;
tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
xfer_epsize_set(&_dcd_xfer[0], CFG_TUD_ENDPOINT0_SIZE);
// Enable EP0 control
UDP->UDP_CSR[0] = UDP_CSR_EPEDS_Msk;
csr_write(0, UDP_CSR_EPEDS_Msk);
// Enable interrupt : EP0, Suspend, Resume, Wakeup
UDP->UDP_IER = UDP_IER_EP0INT_Msk | UDP_IER_RXSUSP_Msk | UDP_IER_RXRSM_Msk | UDP_IER_WAKEUP_Msk;
@@ -135,9 +154,8 @@ static void bus_reset(void)
// Initialize controller to device mode
void dcd_init (uint8_t rhport)
{
(void) rhport;
tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
dcd_connect(rhport);
}
// Enable device interrupt
@@ -189,7 +207,6 @@ void dcd_disconnect(uint8_t rhport)
UDP->UDP_TXVC = UDP_TXVC_TXVDIS_Msk;
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
@@ -240,8 +257,8 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
xfer_epsize_set(&_dcd_xfer[epnum], ep_desc->wMaxPacketSize.size);
// Configure type and eanble EP
UDP->UDP_CSR[epnum] = UDP_CSR_EPEDS_Msk | UDP_CSR_EPTYPE(ep_desc->bmAttributes.xfer + 4*dir);
// Configure type and enable EP
csr_write(epnum, UDP_CSR_EPEDS_Msk | UDP_CSR_EPTYPE(ep_desc->bmAttributes.xfer + 4*dir));
// Enable EP Interrupt for IN
if (dir == TUSB_DIR_IN) UDP->UDP_IER |= (1 << epnum);
@@ -262,47 +279,15 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
if (dir == TUSB_DIR_OUT)
{
// Clear EP0 direction bit
if (epnum == 0) UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk;
// Enable interrupt when starting OUT transfer
if (epnum != 0) UDP->UDP_IER |= (1 << epnum);
}
else
{
if (epnum == 0)
{
// Previous EP0 direction is OUT --> This transfer is ZLP control status.
if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DIR_Msk) )
{
// Set EP0 dir bit
UDP->UDP_CSR[epnum] |= UDP_CSR_DIR_Msk;
// DATA Toggle is 0, USB Specs requires Status Stage must be DATA1
// Since SAMG DToggle is read-only, we mark this and implement the walk-around
if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DTGLE_Msk) )
{
TU_LOG2("Incorrect DATA TOGGLE, Control Status must be DATA1\n");
// DTGLE is read-only on SAMG, this statement has no effect
UDP->UDP_CSR[epnum] |= UDP_CSR_DTGLE_Msk;
// WALKROUND: duplicate IN transfer to send DATA1 status packet
// set flag for irq to skip reporting first incorrect packet
_walkaround_incorrect_dtoggle_control_status = true;
UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk;
while ( UDP->UDP_CSR[epnum] & UDP_CSR_TXPKTRDY_Msk ) {}
_walkaround_incorrect_dtoggle_control_status = false;
}
}
}
xact_ep_write(epnum, xfer->buffer, xfer_packet_len(xfer));
// TX ready for transfer
UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk;
csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
}
return true;
@@ -313,10 +298,14 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
// For EP0 USBD will stall both EP0 Out and In with 0x00 and 0x80
// only handle one by skipping 0x80
if ( ep_addr == tu_edpt_addr(0, TUSB_DIR_IN_MASK) ) return;
uint8_t const epnum = tu_edpt_number(ep_addr);
// Set force stall bit
UDP->UDP_CSR[epnum] |= UDP_CSR_FORCESTALL_Msk;
csr_set(epnum, UDP_CSR_FORCESTALL_Msk);
}
// clear stall, data toggle is also reset to DATA0
@@ -327,11 +316,11 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
uint8_t const epnum = tu_edpt_number(ep_addr);
// clear stall
UDP->UDP_CSR[epnum] &= ~UDP_CSR_FORCESTALL_Msk;
csr_clear(epnum, UDP_CSR_FORCESTALL_Msk);
// must also reset EP to clear data toggle
UDP->UDP_RST_EP = tu_bit_set(UDP->UDP_RST_EP, epnum);
UDP->UDP_RST_EP = tu_bit_clear(UDP->UDP_RST_EP, epnum);
UDP->UDP_RST_EP |= (1 << epnum);
UDP->UDP_RST_EP &= ~(1 << epnum);
}
//--------------------------------------------------------------------+
@@ -369,7 +358,7 @@ void dcd_int_handler(uint8_t rhport)
if ( intr_status & TU_BIT(0) )
{
// setup packet
if (UDP->UDP_CSR[0] & UDP_CSR_RXSETUP)
if ( UDP->UDP_CSR[0] & UDP_CSR_RXSETUP )
{
// get setup from FIFO
uint8_t setup[8];
@@ -382,18 +371,18 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_setup_received(rhport, setup, true);
// Set EP direction bit according to DATA stage
if (setup[0] & 0x80)
// MUST only be set before RXSETUP is clear per specs
if ( tu_edpt_dir(setup[0]) )
{
UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk;
}else
csr_set(0, UDP_CSR_DIR_Msk);
}
else
{
UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk;
csr_clear(0, UDP_CSR_DIR_Msk);
}
// Clear Setup bit & stall bit if needed
UDP->UDP_CSR[0] &= ~(UDP_CSR_RXSETUP_Msk | UDP_CSR_FORCESTALL_Msk);
return;
// Clear Setup, stall and other on-going transfer bits
csr_clear(0, UDP_CSR_RXSETUP_Msk | UDP_CSR_TXPKTRDY_Msk | UDP_CSR_TXCOMP_Msk | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT_Msk | UDP_CSR_FORCESTALL_Msk);
}
}
@@ -416,22 +405,18 @@ void dcd_int_handler(uint8_t rhport)
xact_ep_write(epnum, xfer->buffer, xact_len);
// TX ready for transfer
UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk;
csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
}else
{
// WALKAROUND: Skip reporting this incorrect DATA Toggle status IN transfer
if ( !(_walkaround_incorrect_dtoggle_control_status && (epnum == 0) && (xfer->actual_len == 0)) )
{
// xfer is complete
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
// xfer is complete
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
// Required since control OUT can happen right after before stack handle this event
xfer_end(xfer);
}
// Required since control OUT can happen right after before stack handle this event
xfer_end(xfer);
}
// Clear TX Complete bit
UDP->UDP_CSR[epnum] &= ~UDP_CSR_TXCOMP_Msk;
csr_clear(epnum, UDP_CSR_TXCOMP_Msk);
}
//------------- Endpoint OUT -------------//
@@ -456,13 +441,13 @@ void dcd_int_handler(uint8_t rhport)
}
// Clear DATA Bank0/1 bit
UDP->UDP_CSR[epnum] &= ~banks_complete;
csr_clear(epnum, banks_complete);
}
// Stall sent to host
if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk)
{
UDP->UDP_CSR[epnum] &= ~UDP_CSR_STALLSENT_Msk;
csr_clear(epnum, UDP_CSR_STALLSENT_Msk);
}
}
}

View File

@@ -32,13 +32,6 @@
#include "nrf_clock.h"
#include "nrf_power.h"
#include "nrfx_usbd_errata.h"
#ifdef SOFTDEVICE_PRESENT
// For enable/disable hfclk with SoftDevice
#include "nrf_sdm.h"
#include "nrf_soc.h"
#endif
#include "device/dcd.h"
// TODO remove later
@@ -58,6 +51,11 @@ enum
USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
};
enum
{
EP_COUNT = 8
};
// Transfer descriptor
typedef struct
{
@@ -76,36 +74,73 @@ typedef struct
static struct
{
// All 8 endpoints including control IN & OUT (offset 1)
xfer_td_t xfer[8][2];
xfer_td_t xfer[EP_COUNT][2];
// Only one DMA can run at a time
volatile bool dma_running;
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
// However, in critical section with interrupt disabled, the DMA can be finished and added up
// until handled by dcd_init_handler() when exiting critical section.
volatile uint8_t dma_pending;
}_dcd;
/*------------------------------------------------------------------*/
/* Control / Bulk / Interrupt (CBI) Transfer
*------------------------------------------------------------------*/
// NVIC_GetEnableIRQ is only available in CMSIS v5
#ifndef NVIC_GetEnableIRQ
static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
}
else
{
return(0U);
}
}
#endif
// helper to start DMA
static void edpt_dma_start(volatile uint32_t* reg_startep)
{
// Only one dma can be active
if ( _dcd.dma_running )
if ( _dcd.dma_pending )
{
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
{
// If called within ISR, use usbd task to defer later
// 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 ) { }
if ( __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) )
{
// Called in critical section with interrupt disabled. We have to manually check
// for the DMA complete by comparing current pending DMA with number of ENDED Events
uint32_t ended = 0;
while ( _dcd.dma_pending < ((uint8_t) ended) )
{
ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
for (uint8_t i=0; i<EP_COUNT; i++)
{
ended += NRF_USBD->EVENTS_ENDEPIN[i] + NRF_USBD->EVENTS_ENDEPOUT[i];
}
}
}else
{
// Called in non-critical thread-mode, should be 99% of the time.
// Should be safe to blocking wait until previous DMA transfer complete
while ( _dcd.dma_pending ) { }
}
}
}
_dcd.dma_running = true;
_dcd.dma_pending++;
(*reg_startep) = 1;
__ISB(); __DSB();
@@ -114,8 +149,8 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
// DMA is complete
static void edpt_dma_end(void)
{
TU_ASSERT(_dcd.dma_running, );
_dcd.dma_running = false;
TU_ASSERT(_dcd.dma_pending, );
_dcd.dma_pending = 0;
}
// helper getting td
@@ -236,6 +271,10 @@ void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
NRF_USBD->USBPULLUP = 0;
// Disable Pull-up does not trigger Power USB Removed, in fact it have no
// impact on the USB Power status at all -> need to submit unplugged event to the stack.
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
}
// connect by enabling internal pull-up resistor on D+/D-
@@ -289,9 +328,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
if ( control_status )
{
// Status Phase also require Easy DMA has to be free as well !!!!
// Status Phase also requires Easy DMA has to be available as well !!!!
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
// Therefore dma_running state will be corrected right away
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
edpt_dma_end();
if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);
@@ -564,9 +605,26 @@ void dcd_int_handler(uint8_t rhport)
// HFCLK helper
//--------------------------------------------------------------------+
#ifdef SOFTDEVICE_PRESENT
// check if SD is present and enabled
static bool is_sd_enabled(void)
// For enable/disable hfclk with SoftDevice
#include "nrf_mbr.h"
#include "nrf_sdm.h"
#include "nrf_soc.h"
#ifndef SD_MAGIC_NUMBER
#define SD_MAGIC_NUMBER 0x51B1E5DB
#endif
static inline bool is_sd_existed(void)
{
return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER;
}
// check if SD is existed and enabled
static inline bool is_sd_enabled(void)
{
if ( !is_sd_existed() ) return false;
uint8_t sd_en = false;
(void) sd_softdevice_is_enabled(&sd_en);
return sd_en;
@@ -639,6 +697,8 @@ void tusb_hal_nrf_power_event (uint32_t event)
switch ( event )
{
case USB_EVT_DETECTED:
TU_LOG2("Power USB Detect\r\n");
if ( !NRF_USBD->ENABLE )
{
/* Prepare for READY event receiving */
@@ -689,6 +749,12 @@ void tusb_hal_nrf_power_event (uint32_t event)
break;
case USB_EVT_READY:
TU_LOG2("Power USB Ready\r\n");
// Skip if pull-up is enabled and HCLK is already running.
// Application probably call this more than necessary.
if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
/* Waiting for USBD peripheral enabled */
while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
@@ -756,6 +822,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
break;
case USB_EVT_REMOVED:
TU_LOG2("Power USB Removed\r\n");
if ( NRF_USBD->ENABLE )
{
// Abort all transfers
@@ -775,7 +842,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
hfclk_disable();
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false);
}
break;

View File

@@ -182,7 +182,10 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
while (countdown > 3)
{
ep->EPDAT = *(uint32_t *)xfer->data_ptr;
uint32_t u32;
memcpy(&u32, xfer->data_ptr, 4);
ep->EPDAT = u32;
xfer->data_ptr += 4; countdown -= 4;
}
while (countdown--)
@@ -449,7 +452,9 @@ void dcd_int_handler(uint8_t rhport)
USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk;
USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_SUSPENDIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk;
USBD->CEPINTSTS = 0x1ffc;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
tusb_speed_t speed = (USBD->OPER & USBD_OPER_CURSPD_Msk) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
dcd_event_bus_reset(0, speed, true);
}
if (bus_state & USBD_BUSINTSTS_RESUMEIF_Msk)

View File

@@ -181,6 +181,8 @@ void dcd_init(uint8_t rhport)
LPC_USB->UDCAH = (uint32_t) _dcd.udca;
LPC_USB->DMAIntEn = (DMA_INT_END_OF_XFER_MASK /*| DMA_INT_NEW_DD_REQUEST_MASK*/ | DMA_INT_ERROR_MASK);
dcd_connect(rhport);
// Clear pending IRQ
NVIC_ClearPendingIRQ(USB_IRQn);
}

View File

@@ -82,11 +82,14 @@ enum {
};
// PORTSC1
#define PORTSC1_PORT_SPEED_POS 26
enum {
PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0),
PORTSC1_FORCE_PORT_RESUME = TU_BIT(6),
PORTSC1_SUSPEND = TU_BIT(7),
PORTSC1_FORCE_FULL_SPEED = TU_BIT(24),
PORTSC1_PORT_SPEED = TU_BIT(26) | TU_BIT(27)
};
// OTGSC
@@ -236,7 +239,7 @@ typedef struct
{
dcd_registers_t* regs; // registers
const IRQn_Type irqnum; // IRQ number
const uint8_t ep_count; // Max bi-directional Endpoints
const uint8_t ep_count; // Max bi-directional Endpoints
}dcd_controller_t;
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
@@ -244,7 +247,7 @@ typedef struct
// Therefore QHD_MAX is 2 x max endpoint count
#define QHD_MAX (8*2)
dcd_controller_t _dcd_controller[] =
static const dcd_controller_t _dcd_controller[] =
{
// RT1010 and RT1020 only has 1 USB controller
#if FSL_FEATURE_SOC_USBHS_COUNT == 1
@@ -258,7 +261,7 @@ typedef struct
#else
#define QHD_MAX (6*2)
dcd_controller_t _dcd_controller[] =
static const dcd_controller_t _dcd_controller[] =
{
{ .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
{ .regs = (dcd_registers_t*) LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
@@ -273,7 +276,8 @@ typedef struct {
dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); // for portability, TinyUSB only queue 1 TD for each Qhd
}dcd_data_t;
static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048);
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
static dcd_data_t _dcd_data;
//--------------------------------------------------------------------+
// CONTROLLER API
@@ -341,7 +345,8 @@ void dcd_init(uint8_t rhport)
dcd_reg->USBSTS = dcd_reg->USBSTS;
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND /*| INTR_SOF*/;
dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
}
void dcd_int_enable(uint8_t rhport)
@@ -478,7 +483,8 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
// Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the
// address to 32-byte boundaries.
CleanInvalidateDCache_by_Addr((uint32_t*) buffer, total_bytes + 31);
// void* cast to suppress cast-align warning, buffer must be
CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) buffer, 4), total_bytes + 31);
//------------- Prepare qtd -------------//
qtd_init(p_qtd, buffer, total_bytes);
@@ -510,7 +516,8 @@ void dcd_int_handler(uint8_t rhport)
if (int_status & INTR_RESET)
{
bus_reset(rhport);
dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
uint32_t speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
}
if (int_status & INTR_SUSPEND)

View File

@@ -134,7 +134,8 @@ static void _dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct
{
(void) driver;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
tusb_speed_t speed = (dev->speed == 3) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
dcd_event_bus_reset(0, speed, true);
DEV_CONNECT(dev);
}

View File

@@ -144,6 +144,12 @@
# define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
#endif
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#ifndef USE_SOF
# define USE_SOF 0
#endif
/***************************************************
* Checks, structs, defines, function definitions, etc.
*/
@@ -199,7 +205,6 @@ static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
void dcd_init (uint8_t rhport)
{
(void)rhport;
/* Clocks should already be enabled */
/* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */
@@ -235,10 +240,11 @@ void dcd_init (uint8_t rhport)
pcd_set_endpoint(USB,i,0u);
}
USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_SOFM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
USB->CNTR |= USB_CNTR_RESETM | (USE_SOF ? USB_CNTR_SOFM : 0) | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
dcd_handle_bus_reset();
// Data-line pull-up is left disconnected.
// Enable pull-up if supported
if ( dcd_connect ) dcd_connect(rhport);
}
// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
@@ -542,10 +548,12 @@ void dcd_int_handler(uint8_t rhport) {
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
#if USE_SOF
if(int_status & USB_ISTR_SOF) {
reg16_clear_bits(&USB->ISTR, USB_ISTR_SOF);
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
#endif
if(int_status & USB_ISTR_ESOF) {
if(remoteWakeCountdown == 1u)
@@ -698,7 +706,6 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc
default:
TU_ASSERT(false);
return false;
}
pcd_set_eptype(USB, epnum, wType);

File diff suppressed because it is too large Load Diff

View File

@@ -70,8 +70,7 @@ typedef enum
SIZXY = 7
} ep_regs_index_t;
#define EP_REGS(epnum, dir) &USBOEPCNF_1 + 64*dir + 8*(epnum - 1)
#define EP_REGS(epnum, dir) ((ep_regs_t) ((uintptr_t)&USBOEPCNF_1 + 64*dir + 8*(epnum - 1)))
static void bus_reset(void)
{
@@ -134,6 +133,9 @@ void dcd_init (uint8_t rhport)
// Enable reset and wait for it before continuing.
USBIE |= RSTRIE;
// Enable pullup.
USBCNF |= PUR_EN;
USBKEYPID = 0;
}

View File

@@ -95,6 +95,10 @@
#if CFG_TUD_NET
#include "class/net/net_device.h"
#endif
#if CFG_TUD_BTH
#include "class/bth/bth_device.h"
#endif
#endif
@@ -105,6 +109,8 @@
* @{ */
// Initialize device/host stack
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
bool tusb_init(void);
// Check if stack is initialized

View File

@@ -58,6 +58,7 @@
#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51
#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
// STM32
#define OPT_MCU_STM32F0 300 ///< ST STM32F0
@@ -92,6 +93,9 @@
// Espressif
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
/** @} */
/** \defgroup group_supported_os Supported RTOS
@@ -114,31 +118,35 @@
/** \addtogroup group_configuration
* @{ */
//--------------------------------------------------------------------
// CONTROLLER
// Only 1 roothub port can be configured to be device and/or host.
// tinyusb does not support dual devices or dual host configuration
// RootHub Mode Configuration
// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
//--------------------------------------------------------------------
/** \defgroup group_mode Controller Mode Selection
* \brief CFG_TUSB_CONTROLLER_N_MODE must be defined with these
* @{ */
// Lower 4-bit is operational mode
#define OPT_MODE_NONE 0x00 ///< Disabled
#define OPT_MODE_DEVICE 0x01 ///< Device Mode
#define OPT_MODE_HOST 0x02 ///< Host Mode
#define OPT_MODE_HIGH_SPEED 0x10 ///< High speed
/** @} */
// Higher 4-bit is max operational speed (corresponding to tusb_speed_t)
#define OPT_MODE_FULL_SPEED 0x00 ///< Max Full Speed
#define OPT_MODE_LOW_SPEED 0x10 ///< Max Low Speed
#define OPT_MODE_HIGH_SPEED 0x20 ///< Max High Speed
#ifndef CFG_TUSB_RHPORT0_MODE
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE
#endif
#ifndef CFG_TUSB_RHPORT1_MODE
#define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE
#endif
#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST)) || \
#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST ) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST )) || \
((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE))
#error "tinyusb does not support same modes on more than 1 roothub port"
#error "TinyUSB currently does not support same modes on more than 1 roothub port"
#endif
// Which roothub port is configured as host
@@ -156,7 +164,6 @@
#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 )
//--------------------------------------------------------------------+
// COMMON OPTIONS
//--------------------------------------------------------------------+
@@ -168,15 +175,15 @@
// place data in accessible RAM for usb controller
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#define CFG_TUSB_OS OPT_OS_NONE
#endif
//--------------------------------------------------------------------
@@ -184,7 +191,7 @@
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
#ifndef CFG_TUD_CDC
@@ -219,6 +226,10 @@
#define CFG_TUD_NET 0
#endif
#ifndef CFG_TUD_BTH
#define CFG_TUD_BTH 0
#endif
//--------------------------------------------------------------------
// HOST OPTIONS
//--------------------------------------------------------------------