mirror of
https://github.com/hathach/tinyusb.git
synced 2026-03-05 23:26:49 +00:00
Merge branch 'master' into fork/HiFiPhile/dwc2_iso_incomp
This commit is contained in:
@ -298,7 +298,7 @@ The following tools are provided freely to support the development of the TinyUS
|
||||
|
||||
.. _Changelog: docs/info/changelog.rst
|
||||
.. _Contributors: CONTRIBUTORS.rst
|
||||
.. _Getting Started: docs/reference/getting_started.rst
|
||||
.. _Getting Started: docs/getting_started.rst
|
||||
.. _Supported Boards: docs/reference/boards.rst
|
||||
.. _Dependencies: docs/reference/dependencies.rst
|
||||
.. _Concurrency: docs/reference/concurrency.rst
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* After device is enumerated in dfu mode run the following commands
|
||||
*
|
||||
* To transfer firmware from host to device (best to test with text file)
|
||||
@ -48,21 +48,18 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
const char* upload_image[2]=
|
||||
{
|
||||
"Hello world from TinyUSB DFU! - Partition 0",
|
||||
"Hello world from TinyUSB DFU! - Partition 1"
|
||||
};
|
||||
const char *upload_image[2] = {"Hello world from TinyUSB DFU! - Partition 0",
|
||||
"Hello world from TinyUSB DFU! - Partition 1"};
|
||||
|
||||
/* Blink pattern
|
||||
* - 250 ms : device not mounted
|
||||
* - 1000 ms : device mounted
|
||||
* - 2500 ms : device is suspended
|
||||
*/
|
||||
enum {
|
||||
enum {
|
||||
BLINK_NOT_MOUNTED = 250,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
BLINK_MOUNTED = 1000,
|
||||
BLINK_SUSPENDED = 2500,
|
||||
};
|
||||
|
||||
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
@ -70,21 +67,16 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
void led_blinking_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
// init device stack on configured roothub port
|
||||
tusb_rhport_init_t dev_init = {
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO
|
||||
};
|
||||
tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
|
||||
tusb_init(BOARD_TUD_RHPORT, &dev_init);
|
||||
|
||||
board_init_after_tusb();
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
tud_task(); // tinyusb device task
|
||||
led_blinking_task();
|
||||
}
|
||||
@ -95,29 +87,25 @@ int main(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
void tud_mount_cb(void) {
|
||||
blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
void tud_umount_cb(void) {
|
||||
blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// remote_wakeup_en : if host allow us to perform remote wakeup
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en)
|
||||
{
|
||||
(void) remote_wakeup_en;
|
||||
void tud_suspend_cb(bool remote_wakeup_en) {
|
||||
(void)remote_wakeup_en;
|
||||
blink_interval_ms = BLINK_SUSPENDED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void)
|
||||
{
|
||||
void tud_resume_cb(void) {
|
||||
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
@ -129,19 +117,17 @@ void tud_resume_cb(void)
|
||||
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
|
||||
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
|
||||
// During this period, USB host won't try to communicate with us.
|
||||
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
|
||||
{
|
||||
if ( state == DFU_DNBUSY )
|
||||
{
|
||||
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) {
|
||||
if (state == DFU_DNBUSY) {
|
||||
// For this example
|
||||
// - Atl0 Flash is fast : 1 ms
|
||||
// - Alt1 EEPROM is slow: 100 ms
|
||||
return (alt == 0) ? 1 : 100;
|
||||
}
|
||||
else if (state == DFU_MANIFEST)
|
||||
{
|
||||
} else if (state == DFU_MANIFEST) {
|
||||
// since we don't buffer entire image and do any flashing in manifest stage
|
||||
return 0;
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -150,15 +136,13 @@ uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
|
||||
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
|
||||
// This callback could be returned before flashing op is complete (async).
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, uint16_t length)
|
||||
{
|
||||
(void) alt;
|
||||
(void) block_num;
|
||||
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, const uint8_t *data, uint16_t length) {
|
||||
(void)alt;
|
||||
(void)block_num;
|
||||
|
||||
//printf("\r\nReceived Alt %u BlockNum %u of length %u\r\n", alt, wBlockNum, length);
|
||||
|
||||
for(uint16_t i=0; i<length; i++)
|
||||
{
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
printf("%c", data[i]);
|
||||
}
|
||||
|
||||
@ -169,9 +153,8 @@ void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, u
|
||||
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
|
||||
// Application can do checksum, or actual flashing if buffered entire image previously.
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_manifest_cb(uint8_t alt)
|
||||
{
|
||||
(void) alt;
|
||||
void tud_dfu_manifest_cb(uint8_t alt) {
|
||||
(void)alt;
|
||||
printf("Download completed, enter manifestation\r\n");
|
||||
|
||||
// flashing op for manifest is complete without error
|
||||
@ -182,27 +165,28 @@ void tud_dfu_manifest_cb(uint8_t alt)
|
||||
// Invoked when received DFU_UPLOAD request
|
||||
// Application must populate data with up to length bytes and
|
||||
// Return the number of written bytes
|
||||
uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length)
|
||||
{
|
||||
(void) block_num;
|
||||
(void) length;
|
||||
uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t *data, uint16_t length) {
|
||||
(void)block_num;
|
||||
(void)length;
|
||||
|
||||
uint16_t const xfer_len = (uint16_t) strlen(upload_image[alt]);
|
||||
if (block_num != 0u) {
|
||||
return 0; // for this example we only support single block upload
|
||||
}
|
||||
|
||||
const uint16_t xfer_len = tu_min16((uint16_t)strlen(upload_image[alt]), length);
|
||||
memcpy(data, upload_image[alt], xfer_len);
|
||||
|
||||
return xfer_len;
|
||||
}
|
||||
|
||||
// Invoked when the Host has terminated a download or upload transfer
|
||||
void tud_dfu_abort_cb(uint8_t alt)
|
||||
{
|
||||
(void) alt;
|
||||
void tud_dfu_abort_cb(uint8_t alt) {
|
||||
(void)alt;
|
||||
printf("Host aborted transfer\r\n");
|
||||
}
|
||||
|
||||
// Invoked when a DFU_DETACH request is received
|
||||
void tud_dfu_detach_cb(void)
|
||||
{
|
||||
void tud_dfu_detach_cb(void) {
|
||||
printf("Host detach, we should probably reboot\r\n");
|
||||
}
|
||||
|
||||
@ -210,13 +194,14 @@ void tud_dfu_detach_cb(void)
|
||||
// BLINKING TASK + Indicator pulse
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
void led_blinking_task(void) {
|
||||
static uint32_t start_ms = 0;
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
|
||||
if (board_millis() - start_ms < blink_interval_ms) {
|
||||
return; // not enough time
|
||||
}
|
||||
start_ms += blink_interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
#include "class/dfu/dfu_device.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
@ -33,49 +32,45 @@
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
|
||||
#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
|
||||
PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
|
||||
#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
|
||||
#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
static const tusb_desc_device_t desc_device =
|
||||
{.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0201,
|
||||
|
||||
#if CFG_TUD_CDC
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
#else
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
#endif
|
||||
#if CFG_TUD_CDC
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
#else
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
#endif
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
.bNumConfigurations = 0x01};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *) &desc_device;
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
return (const uint8_t *)&desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -83,20 +78,17 @@ uint8_t const * tud_descriptor_device_cb(void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Number of Alternate Interface (each for 1 flash partition)
|
||||
#define ALT_COUNT 2
|
||||
#define ALT_COUNT 2
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
ITF_NUM_DFU_MODE,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_DESC_LEN(ALT_COUNT))
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_DESC_LEN(ALT_COUNT))
|
||||
#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
uint8_t const desc_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
@ -107,12 +99,90 @@ uint8_t const desc_configuration[] =
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void)index; // for multiple configurations
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BOS Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* Microsoft OS 2.0 registry property descriptor
|
||||
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
|
||||
device should create DeviceInterfaceGUIDs. It can be done by driver and
|
||||
in case of real PnP solution device should expose MS "Microsoft OS 2.0
|
||||
registry property descriptor". Such descriptor can insert any record
|
||||
into Windows registry per device/configuration/interface. In our case it
|
||||
will insert "DeviceInterfaceGUIDs" multistring property.
|
||||
GUID is freshly generated and should be OK to use.
|
||||
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
|
||||
(Section Microsoft OS compatibility descriptors)
|
||||
*/
|
||||
|
||||
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
||||
#define MS_OS_20_DESC_LEN 0xA2
|
||||
#define VENDOR_REQUEST_MICROSOFT 1
|
||||
|
||||
// BOS Descriptor is required for webUSB
|
||||
const uint8_t desc_bos[] = {
|
||||
// total length, number of device caps
|
||||
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
|
||||
|
||||
// Microsoft OS 2.0 descriptor
|
||||
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
|
||||
|
||||
const uint8_t *tud_descriptor_bos_cb(void) {
|
||||
return desc_bos;
|
||||
}
|
||||
|
||||
const uint8_t desc_ms_os_20[] = {
|
||||
// Set header: length, type, windows version, total length
|
||||
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000),
|
||||
U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
|
||||
|
||||
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
|
||||
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
|
||||
|
||||
// MS OS 2.0 Registry property descriptor: length, type
|
||||
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
|
||||
U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A),// wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
|
||||
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
|
||||
'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
|
||||
U16_TO_U8S_LE(0x0050),// wPropertyDataLength
|
||||
//bPropertyData: {3E7E0711-DF3B-4158-A32F-E5951B2AB9A1}.
|
||||
'{', 0x00, '3', 0x00, 'E', 0x00, '7', 0x00, 'E', 0x00, '0', 0x00, '7', 0x00, '1', 0x00, '1', 0x00, '-', 0x00,
|
||||
'D', 0x00, 'F', 0x00, '3', 0x00, 'B', 0x00, '-', 0x00, '4', 0x00, '1', 0x00, '5', 0x00, '8', 0x00, '-', 0x00,
|
||||
'A', 0x00, '3', 0x00, '2', 0x00, 'F', 0x00, '-', 0x00, 'E', 0x00, '5', 0x00, '9', 0x00, '5', 0x00, '1', 0x00,
|
||||
'B', 0x00, '2', 0x00, 'A', 0x00, 'B', 0x00, '9', 0x00, 'A', 0x00, '1', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
|
||||
// Invoked when a control transfer occurred on an interface of this class
|
||||
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||
// return false to stall control endpoint (e.g unsupported request)
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) {
|
||||
// nothing to with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
|
||||
if (request->bRequest == VENDOR_REQUEST_MICROSOFT) {
|
||||
if (request->wIndex == 7) {
|
||||
return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, MS_OS_20_DESC_LEN);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stall unknown request
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
@ -126,25 +196,24 @@ enum {
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
static char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"FLASH", // 4: DFU Partition 1
|
||||
"EEPROM", // 5: DFU Partition 2
|
||||
static const char *string_desc_arr[] = {
|
||||
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"FLASH", // 4: DFU Partition 1
|
||||
"EEPROM", // 5: DFU Partition 2
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32 + 1];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// 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) {
|
||||
(void) langid;
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void)langid;
|
||||
size_t chr_count;
|
||||
|
||||
switch ( index ) {
|
||||
switch (index) {
|
||||
case STRID_LANGID:
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
@ -165,21 +234,21 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
chr_count = strlen(str);
|
||||
const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
if (chr_count > max_count) {
|
||||
chr_count = max_count;
|
||||
}
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
for (size_t i = 0; i < chr_count; i++) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
#include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
#include "class/dfu/dfu_rt_device.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
@ -33,48 +32,45 @@
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
|
||||
#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
|
||||
PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
|
||||
#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
|
||||
#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
static const tusb_desc_device_t desc_device = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0201,
|
||||
|
||||
#if CFG_TUD_CDC
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
#else
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
#endif
|
||||
#if CFG_TUD_CDC
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
#else
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
#endif
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
uint8_t const *tud_descriptor_device_cb(void) {
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
@ -82,16 +78,14 @@ uint8_t const * tud_descriptor_device_cb(void)
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
ITF_NUM_DFU_RT,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_RT_DESC_LEN)
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_RT_DESC_LEN)
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
uint8_t const desc_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
@ -103,12 +97,108 @@ uint8_t const desc_configuration[] =
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index;// for multiple configurations
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BOS Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* Microsoft OS 2.0 registry property descriptor
|
||||
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
|
||||
device should create DeviceInterfaceGUIDs. It can be done by driver and
|
||||
in case of real PnP solution device should expose MS "Microsoft OS 2.0
|
||||
registry property descriptor". Such descriptor can insert any record
|
||||
into Windows registry per device/configuration/interface. In our case it
|
||||
will insert "DeviceInterfaceGUIDs" multistring property.
|
||||
GUID is freshly generated and should be OK to use.
|
||||
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
|
||||
(Section Microsoft OS compatibility descriptors)
|
||||
*/
|
||||
|
||||
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
||||
#define MS_OS_20_DESC_LEN 0xA2
|
||||
#define VENDOR_REQUEST_MICROSOFT 1
|
||||
|
||||
// BOS Descriptor is required for webUSB
|
||||
const uint8_t desc_bos[] = {
|
||||
// total length, number of device caps
|
||||
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
|
||||
|
||||
// Microsoft OS 2.0 descriptor
|
||||
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
|
||||
|
||||
const uint8_t *tud_descriptor_bos_cb(void) {
|
||||
return desc_bos;
|
||||
}
|
||||
|
||||
uint8_t const desc_ms_os_20[] = {
|
||||
// Set header: length, type, windows version, total length
|
||||
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
|
||||
|
||||
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
|
||||
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sub-compatible
|
||||
|
||||
// MS OS 2.0 Registry property descriptor: length, type
|
||||
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
|
||||
U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A),// wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
|
||||
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
|
||||
'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
|
||||
U16_TO_U8S_LE(0x0050),// wPropertyDataLength
|
||||
//bPropertyData: {F7CC2C68-3B14-4D72-B876-1A981AD2C9E5}.
|
||||
'{', 0x00, 'F', 0x00, '7', 0x00, 'C', 0x00, 'C', 0x00, '2', 0x00, 'C', 0x00, '6', 0x00, '8', 0x00, '-', 0x00,
|
||||
'3', 0x00, 'B', 0x00, '1', 0x00, '4', 0x00, '-', 0x00, '4', 0x00, 'D', 0x00, '7', 0x00, '2', 0x00, '-', 0x00,
|
||||
'B', 0x00, '8', 0x00, '7', 0x00, '6', 0x00, '-', 0x00, '1', 0x00, 'A', 0x00, '9', 0x00, '8', 0x00, '1', 0x00,
|
||||
'A', 0x00, 'D', 0x00, '2', 0x00, 'C', 0x00, '9', 0x00, 'E', 0x00, '5', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
|
||||
// Invoked when a control transfer occurred on an interface of this class
|
||||
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||
// return false to stall control endpoint (e.g unsupported request)
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
// nothing to with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
|
||||
if (request->bRequest == VENDOR_REQUEST_MICROSOFT) {
|
||||
if (request->wIndex == 7) {
|
||||
return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, MS_OS_20_DESC_LEN);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (request->bmRequestType_bit.type) {
|
||||
case TUSB_REQ_TYPE_VENDOR:
|
||||
switch (request->bRequest) {
|
||||
case VENDOR_REQUEST_MICROSOFT:
|
||||
if (request->wIndex == 7) {
|
||||
return tud_control_xfer(rhport, request, (void *) (uintptr_t) desc_ms_os_20, MS_OS_20_DESC_LEN);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// stall unknown request
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
@ -122,24 +212,23 @@ enum {
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
static char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"TinyUSB DFU runtime", // 4: DFU runtime
|
||||
static char const *string_desc_arr[] = {
|
||||
(const char[]){0x09, 0x04},// 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"TinyUSB DFU runtime", // 4: DFU runtime
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32 + 1];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// 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) {
|
||||
(void) langid;
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void)langid;
|
||||
size_t chr_count;
|
||||
|
||||
switch ( index ) {
|
||||
switch (index) {
|
||||
case STRID_LANGID:
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
@ -160,21 +249,21 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
chr_count = strlen(str);
|
||||
const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
if (chr_count > max_count) {
|
||||
chr_count = max_count;
|
||||
}
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
for (size_t i = 0; i < chr_count; i++) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
@ -99,6 +99,7 @@ int main(void) {
|
||||
#if CFG_TUSB_OS == OPT_OS_FREERTOS
|
||||
init_freertos_task();
|
||||
#else
|
||||
board_delay(100); // wait for uart to be ready
|
||||
init_tinyusb();
|
||||
while (1) {
|
||||
tuh_task(); // tinyusb host task
|
||||
@ -289,6 +290,7 @@ void app_main(void) {
|
||||
|
||||
void usb_host_task(void *param) {
|
||||
(void) param;
|
||||
board_delay(100); // wait for uart to be ready
|
||||
init_tinyusb();
|
||||
while (1) {
|
||||
tuh_task();
|
||||
|
||||
@ -50,21 +50,17 @@
|
||||
typedef struct {
|
||||
uint8_t attrs;
|
||||
uint8_t alt;
|
||||
uint8_t state;
|
||||
uint8_t status;
|
||||
|
||||
dfu_state_t state;
|
||||
dfu_status_t status;
|
||||
|
||||
bool flashing_in_progress;
|
||||
bool flashing_in_progress;
|
||||
uint16_t block;
|
||||
uint16_t length;
|
||||
} dfu_state_ctx_t;
|
||||
|
||||
// Only a single dfu state is allowed
|
||||
static dfu_state_ctx_t _dfu_ctx;
|
||||
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(transfer_buf, CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
} _dfu_epbuf;
|
||||
TU_ATTR_ALIGNED(4) uint8_t _transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
|
||||
|
||||
static void reset_state(void) {
|
||||
_dfu_ctx.state = DFU_IDLE;
|
||||
@ -253,6 +249,8 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
|
||||
tud_control_status(rhport, request);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
tud_dfu_detach_cb();
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
break;
|
||||
|
||||
@ -275,6 +273,8 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
|
||||
tud_control_status(rhport, request);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
tud_dfu_abort_cb(_dfu_ctx.alt);
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
break;
|
||||
|
||||
@ -283,10 +283,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
|
||||
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
|
||||
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
|
||||
const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
|
||||
const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _transfer_buf,
|
||||
request->wLength);
|
||||
|
||||
return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, xfer_len);
|
||||
return tud_control_xfer(rhport, request, _transfer_buf, xfer_len);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -303,10 +303,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
|
||||
_dfu_ctx.block = request->wValue;
|
||||
_dfu_ctx.length = request->wLength;
|
||||
|
||||
if (request->wLength) {
|
||||
if (request->wLength > 0) {
|
||||
// Download with payload -> transition to DOWNLOAD SYNC
|
||||
_dfu_ctx.state = DFU_DNLOAD_SYNC;
|
||||
return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, request->wLength);
|
||||
return tud_control_xfer(rhport, request, _transfer_buf, request->wLength);
|
||||
} else {
|
||||
// Download is complete -> transition to MANIFEST SYNC
|
||||
_dfu_ctx.state = DFU_MANIFEST_SYNC;
|
||||
@ -352,6 +352,8 @@ void tud_dfu_finish_flashing(uint8_t status) {
|
||||
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
? DFU_MANIFEST_SYNC
|
||||
: DFU_MANIFEST_WAIT_RESET;
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
} else {
|
||||
// failed while flashing, move to dfuError
|
||||
@ -378,10 +380,12 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
if (_dfu_ctx.flashing_in_progress) {
|
||||
_dfu_ctx.state = DFU_DNBUSY;
|
||||
tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_epbuf.transfer_buf, _dfu_ctx.length);
|
||||
tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _transfer_buf, _dfu_ctx.length);
|
||||
} else {
|
||||
_dfu_ctx.state = DFU_DNLOAD_IDLE;
|
||||
}
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -409,6 +413,8 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus
|
||||
} else {
|
||||
_dfu_ctx.state = DFU_IDLE;
|
||||
}
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -60,7 +60,7 @@ typedef struct {
|
||||
static usbd_control_xfer_t _ctrl_xfer;
|
||||
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_SIZE);
|
||||
TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE);
|
||||
} _ctrl_epbuf;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -88,13 +88,13 @@ bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) {
|
||||
// Each transaction has up to Endpoint0's max packet size.
|
||||
// This function can also transfer an zero-length packet
|
||||
static bool data_stage_xact(uint8_t rhport) {
|
||||
const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
|
||||
const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE);
|
||||
uint8_t ep_addr = EDPT_CTRL_OUT;
|
||||
|
||||
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
|
||||
ep_addr = EDPT_CTRL_IN;
|
||||
if (0u != xact_len) {
|
||||
TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
|
||||
TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len));
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
|
||||
// Data Stage is complete when all request's length are transferred or
|
||||
// a short packet is sent including zero-length packet.
|
||||
if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
|
||||
(xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
|
||||
(xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) {
|
||||
// DATA stage is complete
|
||||
bool is_ok = true;
|
||||
|
||||
|
||||
@ -339,6 +339,40 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t epin_write_tx_fifo(uint8_t rhport, uint8_t epnum) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
dwc2_dep_t* const epin = &dwc2->ep[0][epnum];
|
||||
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
|
||||
|
||||
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
|
||||
const uint16_t remain_packets = tsiz.packet_count;
|
||||
|
||||
uint16_t total_bytes_written = 0;
|
||||
// Process every single packet (only whole packets can be written to fifo)
|
||||
for (uint16_t i = 0; i < remain_packets; i++) {
|
||||
tsiz.value = epin->tsiz;
|
||||
const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
|
||||
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
|
||||
|
||||
// Check if dtxfsts has enough space available
|
||||
if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Push packet to Tx-FIFO
|
||||
if (xfer->ff) {
|
||||
volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
|
||||
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
|
||||
total_bytes_written += xact_bytes;
|
||||
} else {
|
||||
dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
|
||||
xfer->buffer += xact_bytes;
|
||||
total_bytes_written += xact_bytes;
|
||||
}
|
||||
}
|
||||
return total_bytes_written;
|
||||
}
|
||||
|
||||
// Since this function returns void, it is not possible to return a boolean success message
|
||||
// We must make sure that this function is not called when the EP is disabled
|
||||
// Must be called from critical section
|
||||
@ -352,7 +386,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
|
||||
|
||||
// EP0 is limited to one packet per xfer
|
||||
if (epnum == 0) {
|
||||
total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size);
|
||||
total_bytes = tu_min16(_dcd_data.ep0_pending[dir], CFG_TUD_ENDPOINT0_SIZE);
|
||||
_dcd_data.ep0_pending[dir] -= total_bytes;
|
||||
num_packets = 1;
|
||||
} else {
|
||||
@ -390,12 +424,21 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
|
||||
}
|
||||
dep->diepdma = (uintptr_t) xfer->buffer;
|
||||
dep->diepctl = depctl.value; // enable endpoint
|
||||
// Advance buffer pointer for EP0
|
||||
if (epnum == 0) {
|
||||
xfer->buffer += total_bytes;
|
||||
}
|
||||
} else {
|
||||
dep->diepctl = depctl.value; // enable endpoint
|
||||
|
||||
// Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
|
||||
if (dir == TUSB_DIR_IN && total_bytes != 0) {
|
||||
dwc2->diepempmsk |= (1u << epnum); //-V629
|
||||
const uint16_t xferred_bytes = epin_write_tx_fifo(rhport, epnum);
|
||||
|
||||
// Enable TXFE interrupt if there are still data to be sent
|
||||
// EP0 only sends one packet at a time, so no need to check for EP0
|
||||
if ((epnum != 0) && (xfer->total_len - xferred_bytes > 0)) {
|
||||
dwc2->diepempmsk |= (1u << epnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -849,11 +892,9 @@ static void handle_rxflvl_irq(uint8_t rhport) {
|
||||
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
|
||||
xfer->total_len -= tsiz.xfer_size;
|
||||
if (epnum == 0) {
|
||||
xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
|
||||
_dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -913,32 +954,10 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
|
||||
// - 64 bytes or
|
||||
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
|
||||
if (diepint_bm.txfifo_empty && tu_bit_test(dwc2->diepempmsk, epnum)) {
|
||||
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
|
||||
const uint16_t remain_packets = tsiz.packet_count;
|
||||
|
||||
// Process every single packet (only whole packets can be written to fifo)
|
||||
for (uint16_t i = 0; i < remain_packets; i++) {
|
||||
tsiz.value = epin->tsiz;
|
||||
const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
|
||||
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
|
||||
|
||||
// Check if dtxfsts has enough space available
|
||||
if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Push packet to Tx-FIFO
|
||||
if (xfer->ff != NULL) {
|
||||
volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
|
||||
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
|
||||
} else {
|
||||
dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
|
||||
xfer->buffer += xact_bytes;
|
||||
}
|
||||
}
|
||||
epin_write_tx_fifo(rhport, epnum);
|
||||
|
||||
// Turn off TXFE if all bytes are written.
|
||||
tsiz.value = epin->tsiz;
|
||||
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
|
||||
if (tsiz.xfer_size == 0) {
|
||||
dwc2->diepempmsk &= ~(1u << epnum);
|
||||
}
|
||||
|
||||
@ -485,6 +485,10 @@
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_BUFSIZE
|
||||
#define CFG_TUD_ENDPOINT0_BUFSIZE CFG_TUD_ENDPOINT0_SIZE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_INTERFACE_MAX
|
||||
#define CFG_TUD_INTERFACE_MAX 16
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user