Initial project setup

This commit is contained in:
2025-12-13 11:59:11 +02:00
commit 3218e6039f
2176 changed files with 355321 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "device/dcd.h"
#include "fuzz/fuzz_private.h"
#include <assert.h>
#include <cstdint>
#include <limits>
#define UNUSED(x) (void)(x)
//--------------------------------------------------------------------+
// State tracker
//--------------------------------------------------------------------+
struct State {
bool interrupts_enabled;
bool sof_enabled;
uint8_t address;
};
tu_static State state = {false, 0, 0};
//--------------------------------------------------------------------+
// Controller API
// All no-ops as we are fuzzing.
//--------------------------------------------------------------------+
extern "C" {
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
UNUSED(rhport);
UNUSED(rh_init);
return true;
}
void dcd_int_handler(uint8_t rhport) {
assert(_fuzz_data_provider.has_value());
if (!state.interrupts_enabled) {
return;
}
// Choose if we want to generate a signal based on the fuzzed data.
if (_fuzz_data_provider->ConsumeBool()) {
dcd_event_bus_signal(
rhport,
// Choose a random event based on the fuzz data.
(dcd_eventid_t)_fuzz_data_provider->ConsumeIntegralInRange<uint8_t>(
DCD_EVENT_INVALID + 1, DCD_EVENT_COUNT - 1),
// Identify trigger as either an interrupt or a syncrhonous call
// depending on fuzz data.
_fuzz_data_provider->ConsumeBool());
}
if (_fuzz_data_provider->ConsumeBool()) {
constexpr size_t kSetupFrameLength = 8;
std::vector<uint8_t> setup =
_fuzz_data_provider->ConsumeBytes<uint8_t>(kSetupFrameLength);
// Fuzz consumer may return less than requested. If this is the case
// we want to make sure that at least that length is allocated and available
// to the signal handler.
if (setup.size() != kSetupFrameLength) {
setup.resize(kSetupFrameLength);
}
dcd_event_setup_received(rhport, setup.data(),
// Identify trigger as either an interrupt or a
// syncrhonous call depending on fuzz data.
_fuzz_data_provider->ConsumeBool());
}
}
void dcd_int_enable(uint8_t rhport) {
state.interrupts_enabled = true;
UNUSED(rhport);
return;
}
void dcd_int_disable(uint8_t rhport) {
state.interrupts_enabled = false;
UNUSED(rhport);
return;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
UNUSED(rhport);
state.address = dev_addr;
// Respond with status.
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
return;
}
void dcd_remote_wakeup(uint8_t rhport) {
UNUSED(rhport);
return;
}
void dcd_connect(uint8_t rhport) {
UNUSED(rhport);
return;
}
void dcd_disconnect(uint8_t rhport) {
UNUSED(rhport);
return;
}
void dcd_sof_enable(uint8_t rhport, bool en) {
state.sof_enabled = en;
UNUSED(rhport);
return;
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
UNUSED(rhport);
UNUSED(desc_ep);
return _fuzz_data_provider->ConsumeBool();
}
// Close all non-control endpoints, cancel all pending transfers if any.
// Invoked when switching from a non-zero Configuration by SET_CONFIGURE
// therefore required for multiple configuration support.
void dcd_edpt_close_all(uint8_t rhport) {
UNUSED(rhport);
return;
}
// Close an endpoint.
// Since it is weak, caller must TU_ASSERT this function's existence before
// calling it.
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
UNUSED(rhport);
UNUSED(ep_addr);
return;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to
// notify the stack
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer,
uint16_t total_bytes) {
UNUSED(rhport);
UNUSED(buffer);
UNUSED(total_bytes);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
std::vector<uint8_t> temp =
_fuzz_data_provider->ConsumeBytes<uint8_t>(total_bytes);
std::copy(temp.begin(), temp.end(), buffer);
}
// Ignore output data as it's not useful for fuzzing without a more
// complex fuzzed backend. But we need to make sure it's not
// optimised out.
volatile uint8_t *dont_optimise0 = buffer;
volatile uint16_t dont_optimise1 = total_bytes;
UNUSED(dont_optimise0);
UNUSED(dont_optimise1);
return _fuzz_data_provider->ConsumeBool();
}
/* TODO: implement a fuzzed version of this.
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff,
uint16_t total_bytes) {}
*/
// Stall endpoint, any queuing transfer should be removed from endpoint
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
UNUSED(rhport);
UNUSED(ep_addr);
return;
}
// clear stall, data toggle is also reset to DATA0
// This API never calls with control endpoints, since it is auto cleared when
// receiving setup packet
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
UNUSED(rhport);
UNUSED(ep_addr);
return;
}
}

View File

@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
project(${PROJECT})
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT})
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT} noos)

View File

@@ -0,0 +1,11 @@
include ../../make.mk
INC += \
src \
$(TOP)/hw \
# Example source
SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc))
include ../../rules.mk

View File

@@ -0,0 +1,174 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include <cassert>
#include <fuzzer/FuzzedDataProvider.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "class/cdc/cdc_device.h"
#include "fuzz/fuzz.h"
#include "tusb.h"
#include <cstdint>
#include <string>
#include <vector>
extern "C" {
#define FUZZ_ITERATIONS 500
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void cdc_task(FuzzedDataProvider *provider);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzedDataProvider provider(Data, Size);
std::vector<uint8_t> callback_data = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {
return 0;
}
tud_int_handler(provider.ConsumeIntegral<uint8_t>());
tud_task(); // tinyusb device task
cdc_task(&provider);
}
return 0;
}
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
enum CdcApiFuncs {
kCdcNConnected,
kCdcNGetLineState,
kCdcNGetLineCoding,
kCdcNSetWantedChar,
kCdcNAvailable,
kCdcNRead,
kCdcNReadChar,
kCdcNReadFlush,
kCdcNPeek,
kCdcNWrite,
kCdcNWriteChar,
kCdcNWriteStr,
kCdcNWriteFlush,
kCdcNWriteAvailable,
kCdcNWriteClear,
// We don't need to fuzz tud_cdc_<not n>* as they are just wrappers
// calling with n==0.
kMaxValue,
};
void cdc_task(FuzzedDataProvider *provider) {
assert(provider != NULL);
const int kMaxBufferSize = 4096;
switch (provider->ConsumeEnum<CdcApiFuncs>()) {
case kCdcNConnected:
// TODO: Fuzz interface number
(void)tud_cdc_n_connected(0);
break;
case kCdcNGetLineState:
// TODO: Fuzz interface number
(void)tud_cdc_n_get_line_state(0);
break;
case kCdcNGetLineCoding: {
cdc_line_coding_t coding;
// TODO: Fuzz interface number
(void)tud_cdc_n_get_line_coding(0, &coding);
} break;
case kCdcNSetWantedChar:
// TODO: Fuzz interface number
(void)tud_cdc_n_set_wanted_char(0, provider->ConsumeIntegral<char>());
break;
case kCdcNAvailable:
// TODO: Fuzz interface number
(void)tud_cdc_n_available(0);
break;
case kCdcNRead: {
std::vector<uint8_t> buffer;
buffer.resize(provider->ConsumeIntegralInRange<size_t>(0, kMaxBufferSize));
// TODO: Fuzz interface number
(void)tud_cdc_n_read(0, buffer.data(), buffer.size());
break;
}
case kCdcNReadChar:
// TODO: Fuzz interface number
tud_cdc_n_read_char(0);
break;
case kCdcNReadFlush:
// TODO: Fuzz interface number
tud_cdc_n_read_flush(0);
break;
case kCdcNPeek: {
uint8_t peak = 0;
tud_cdc_n_peek(0, &peak);
break;
}
case kCdcNWrite: {
std::vector<uint8_t> buffer = provider->ConsumeBytes<uint8_t>(
provider->ConsumeIntegralInRange<size_t>(0, kMaxBufferSize));
// TODO: Fuzz interface number
(void)tud_cdc_n_write(0, buffer.data(), buffer.size());
} break;
case kCdcNWriteChar:
// TODO: Fuzz interface number
(void)tud_cdc_n_write_char(0, provider->ConsumeIntegral<char>());
break;
case kCdcNWriteStr: {
std::string str = provider->ConsumeRandomLengthString(kMaxBufferSize);
// TODO: Fuzz interface number
(void)tud_cdc_n_write_str(0, str.c_str());
break;
}
case kCdcNWriteFlush:
// TODO: Fuzz interface number
(void)tud_cdc_n_write_flush(0);
break;
case kCdcNWriteAvailable:
// TODO: Fuzz interface number
(void)tud_cdc_n_write_available(0);
break;
case kCdcNWriteClear:
// TODO: Fuzz interface number
(void)tud_cdc_n_write_clear(0);
break;
case kMaxValue:
// Noop.
break;
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,229 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save
* device driver after the first plug.
* Auto ProductID layout's Bitmap:
* [MSB] HID | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID \
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \
_PID_MAP(VENDOR, 4))
#define USB_VID 0xCafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const *tud_descriptor_device_cb(void) {
static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
// 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,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL };
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
// full speed configuration
uint8_t const desc_fs_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),
// Interface number, string index, EP notification address and size, EP data
// address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT,
EPNUM_CDC_IN, 64),
};
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and
// other_speed_configuration
// high speed configuration
uint8_t const desc_hs_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),
// Interface number, string index, EP notification address and size, EP data
// address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT,
EPNUM_CDC_IN, 512),
};
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change
// configuration based on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete. device_qualifier descriptor describes
// information about a high-speed capable device that would change if the device
// were operating at the other speed. If not highspeed capable stall this
// request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *)&desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete Configuration descriptor in the other speed
// e.g if high speed then this is for full speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// if link speed is high return fullspeed config, and vice versa
// Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
memcpy(desc_other_speed_config,
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration
: desc_hs_configuration,
CONFIG_TOTAL_LEN);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
return desc_other_speed_config;
}
#endif // highspeed
// 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
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration
: desc_fs_configuration;
#else
return desc_fs_configuration;
#endif
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456789012", // 3: Serials, should use chip ID
"TinyUSB CDC", // 4: CDC Interface
};
static uint16_t _desc_str[32];
// 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;
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t)strlen(str);
if (chr_count > 31)
chr_count = 31;
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
project(${PROJECT})
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT})
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT} noos)

View File

@@ -0,0 +1,11 @@
include ../../make.mk
INC += \
src \
$(TOP)/hw \
# Example source
SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc))
include ../../rules.mk

View File

@@ -0,0 +1,61 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include <cassert>
#include <fuzzer/FuzzedDataProvider.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "class/cdc/cdc_device.h"
#include "fuzz/fuzz.h"
#include "tusb.h"
#include <cstdint>
#include <string>
#include <vector>
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#define FUZZ_ITERATIONS 500
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzedDataProvider provider(Data, Size);
std::vector<uint8_t> callback_data = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {
return 0;
}
tud_int_handler(provider.ConsumeIntegral<uint8_t>());
tud_task(); // tinyusb device task
}
return 0;
}

View File

@@ -0,0 +1,114 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 1
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,224 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save
* device driver after the first plug.
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID \
(0x4000 | _PID_MAP(MSC, 0) | _PID_MAP(HID, 1) | _PID_MAP(MIDI, 2) | \
_PID_MAP(VENDOR, 3))
#define USB_VID 0xCafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const *tud_descriptor_device_cb(void) {
tu_static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
// 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,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_MSC = 0, ITF_NUM_TOTAL };
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x85
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
// full speed configuration
uint8_t const desc_fs_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),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
};
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and
// other_speed_configuration
// high speed configuration
uint8_t const desc_hs_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),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
};
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change
// configuration based on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete. device_qualifier descriptor describes
// information about a high-speed capable device that would change if the device
// were operating at the other speed. If not highspeed capable stall this
// request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *)&desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete Configuration descriptor in the other speed
// e.g if high speed then this is for full speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// if link speed is high return fullspeed config, and vice versa
// Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
memcpy(desc_other_speed_config,
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration
: desc_hs_configuration,
CONFIG_TOTAL_LEN);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
return desc_other_speed_config;
}
#endif // highspeed
// 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
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration
: desc_fs_configuration;
#else
return desc_fs_configuration;
#endif
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456789012", // 3: Serials, should use chip ID
"TinyUSB MSC", // 4: MSC Interface
};
tu_static uint16_t _desc_str[32];
// 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;
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t)strlen(str);
if (chr_count > 31)
chr_count = 31;
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
project(${PROJECT})
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT})
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT} noos)

View File

@@ -0,0 +1,68 @@
include ../../make.mk
# suppress warning caused by lwip
CFLAGS += \
-Wno-error=null-dereference \
-Wno-error=unused-parameter \
-Wno-error=unused-variable
INC += \
src \
$(TOP)/hw \
$(TOP)/lib/lwip/src/include \
$(TOP)/lib/lwip/src/include/ipv4 \
$(TOP)/lib/lwip/src/include/lwip/apps \
$(TOP)/lib/networking
# Example source
SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc))
# lwip sources
SRC_C += \
lib/lwip/src/core/altcp.c \
lib/lwip/src/core/altcp_alloc.c \
lib/lwip/src/core/altcp_tcp.c \
lib/lwip/src/core/def.c \
lib/lwip/src/core/dns.c \
lib/lwip/src/core/inet_chksum.c \
lib/lwip/src/core/init.c \
lib/lwip/src/core/ip.c \
lib/lwip/src/core/mem.c \
lib/lwip/src/core/memp.c \
lib/lwip/src/core/netif.c \
lib/lwip/src/core/pbuf.c \
lib/lwip/src/core/raw.c \
lib/lwip/src/core/stats.c \
lib/lwip/src/core/sys.c \
lib/lwip/src/core/tcp.c \
lib/lwip/src/core/tcp_in.c \
lib/lwip/src/core/tcp_out.c \
lib/lwip/src/core/timeouts.c \
lib/lwip/src/core/udp.c \
lib/lwip/src/core/ipv4/autoip.c \
lib/lwip/src/core/ipv4/dhcp.c \
lib/lwip/src/core/ipv4/etharp.c \
lib/lwip/src/core/ipv4/icmp.c \
lib/lwip/src/core/ipv4/igmp.c \
lib/lwip/src/core/ipv4/ip4.c \
lib/lwip/src/core/ipv4/ip4_addr.c \
lib/lwip/src/core/ipv4/ip4_frag.c \
lib/lwip/src/core/ipv6/dhcp6.c \
lib/lwip/src/core/ipv6/ethip6.c \
lib/lwip/src/core/ipv6/icmp6.c \
lib/lwip/src/core/ipv6/inet6.c \
lib/lwip/src/core/ipv6/ip6.c \
lib/lwip/src/core/ipv6/ip6_addr.c \
lib/lwip/src/core/ipv6/ip6_frag.c \
lib/lwip/src/core/ipv6/mld6.c \
lib/lwip/src/core/ipv6/nd6.c \
lib/lwip/src/netif/ethernet.c \
lib/lwip/src/netif/slipif.c \
lib/lwip/src/apps/http/httpd.c \
lib/lwip/src/apps/http/fs.c \
lib/networking/dhserver.c \
lib/networking/dnserver.c \
lib/networking/rndis_reports.c
include ../../rules.mk

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
#ifndef __CC_H__
#define __CC_H__
//#include "cpu.h"
typedef int sys_prot_t;
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0)
#endif /* __CC_H__ */

View File

@@ -0,0 +1,99 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include <cassert>
#include <fuzzer/FuzzedDataProvider.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "class/cdc/cdc_device.h"
#include "class/net/net_device.h"
#include "fuzz/fuzz.h"
#include "tusb.h"
#include <cstdint>
#include <string>
#include <vector>
extern "C" {
#define FUZZ_ITERATIONS 500
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void net_task(FuzzedDataProvider *provider);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzedDataProvider provider(Data, Size);
std::vector<uint8_t> callback_data = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {
return 0;
}
tud_int_handler(provider.ConsumeIntegral<uint8_t>());
tud_task(); // tinyusb device task
net_task(&provider);
}
return 0;
}
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
enum NetApiFuncs {
kNetworkRecvRenew,
kNetworkCanXmit,
kNetworkXmit,
kMaxValue,
};
void net_task(FuzzedDataProvider *provider) {
assert(provider != NULL);
switch (provider->ConsumeEnum<NetApiFuncs>()) {
case kNetworkRecvRenew:
tud_network_recv_renew();
break;
case kNetworkCanXmit:
(void)tud_network_can_xmit(provider->ConsumeIntegral<uint16_t>());
case kNetworkXmit:
// TODO: Actually pass real values here later.
tud_network_xmit(NULL, 0);
case kMaxValue:
// Noop.
break;
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt
*
*/
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
#define MEM_ALIGNMENT 4
#define LWIP_RAW 0
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define LWIP_DHCP 0
#define LWIP_ICMP 1
#define LWIP_UDP 1
#define LWIP_TCP 1
#define LWIP_IPV4 1
#define LWIP_IPV6 0
#define ETH_PAD_SIZE 0
#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67))
#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF (2 * TCP_MSS)
#define TCP_WND (TCP_MSS)
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
#define LWIP_HTTPD_CGI 0
#define LWIP_HTTPD_SSI 0
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_SINGLE_NETIF 1
#define PBUF_POOL_SIZE 2
#define HTTPD_USE_CUSTOM_FSDATA 0
#define LWIP_MULTICAST_PING 1
#define LWIP_BROADCAST_PING 1
#define LWIP_IPV6_MLD 0
#define LWIP_IPV6_SEND_ROUTER_SOLICIT 0
#endif /* __LWIPOPTS_H__ */

View File

@@ -0,0 +1,122 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// Network class has 2 drivers: ECM/RNDIS and NCM.
// Only one of the drivers can be enabled
#define CFG_TUD_ECM_RNDIS 1
#define CFG_TUD_NCM (1-CFG_TUD_ECM_RNDIS)
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,229 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save
* device driver after the first plug.
* Auto ProductID layout's Bitmap:
* [MSB] HID | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID \
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \
_PID_MAP(VENDOR, 4))
#define USB_VID 0xCafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const *tud_descriptor_device_cb(void) {
tu_static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
// 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,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL };
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
// full speed configuration
uint8_t const desc_fs_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),
// Interface number, string index, EP notification address and size, EP data
// address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT,
EPNUM_CDC_IN, 64),
};
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and
// other_speed_configuration
// high speed configuration
uint8_t const desc_hs_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),
// Interface number, string index, EP notification address and size, EP data
// address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT,
EPNUM_CDC_IN, 512),
};
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change
// configuration based on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete. device_qualifier descriptor describes
// information about a high-speed capable device that would change if the device
// were operating at the other speed. If not highspeed capable stall this
// request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *)&desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long
// enough for transfer to complete Configuration descriptor in the other speed
// e.g if high speed then this is for full speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// if link speed is high return fullspeed config, and vice versa
// Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
memcpy(desc_other_speed_config,
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration
: desc_hs_configuration,
CONFIG_TOTAL_LEN);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
return desc_other_speed_config;
}
#endif // highspeed
// 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
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration
: desc_fs_configuration;
#else
return desc_fs_configuration;
#endif
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456789012", // 3: Serials, should use chip ID
"TinyUSB CDC", // 4: CDC Interface
};
tu_static uint16_t _desc_str[32];
// 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;
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t)strlen(str);
if (chr_count > 31)
chr_count = 31;
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,73 @@
# List of supported OIDs
RNDIS_OID_GEN_SUPPORTED_LIST="\x00\x01\x01\x01"
# Hardware status
RNDIS_OID_GEN_HARDWARE_STATUS="\x00\x01\x01\x02"
# Media types supported (encoded)
RNDIS_OID_GEN_MEDIA_SUPPORTED="\x00\x01\x01\x03"
# Media types in use (encoded)
RNDIS_OID_GEN_MEDIA_IN_USE="\x00\x01\x01\x04"
RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD="\x00\x01\x01\x05"
# Maximum frame size in bytes
RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE="\x00\x01\x01\x06"
# Link speed in units of 100 bps
RNDIS_OID_GEN_LINK_SPEED="\x00\x01\x01\x07"
# Transmit buffer space
RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE="\x00\x01\x01\x08"
# Receive buffer space
RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE="\x00\x01\x01\x09"
# NDIS version number used by the driver
RNDIS_OID_GEN_DRIVER_VERSION="\x00\x01\x01\x10"
# Maximum total packet length in bytes
RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE="\x00\x01\x01\x11"
# Optional protocol flags (encoded)
RNDIS_OID_GEN_PROTOCOL_OPTIONS="\x00\x01\x01\x12"
# Optional NIC flags (encoded)
RNDIS_OID_GEN_MAC_OPTIONS="\x00\x01\x01\x13"
# Whether the NIC is connected to the network
RNDIS_OID_GEN_MEDIA_CONNECT_STATUS="\x00\x01\x01\x14"
# The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction
RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS="\x00\x01\x01\x15"
# Vendor-assigned version number of the driver
RNDIS_OID_GEN_VENDOR_DRIVER_VERSION="\x00\x01\x01\x16"
# The custom GUIDs (Globally Unique Identifier) supported by the miniport driver
RNDIS_OID_GEN_SUPPORTED_GUIDS="\x00\x01\x01\x17"
# List of network-layer addresses associated with the binding between a transport and the driver
RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES="\x00\x01\x01\x18"
# Size of packets' additional headers
RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET="\x00\x01\x01\x19"
RNDIS_OID_GEN_MEDIA_CAPABILITIES="\x00\x01\x02\x01"
# Physical media supported by the miniport driver (encoded)
RNDIS_OID_GEN_PHYSICAL_MEDIUM="\x00\x01\x02\x02"
# Permanent station address
RNDIS_OID_802_3_PERMANENT_ADDRESS="\x01\x01\x01\x01"
# Current station address
RNDIS_OID_802_3_CURRENT_ADDRESS="\x01\x01\x01\x02"
# Current multicast address list
RNDIS_OID_802_3_MULTICAST_LIST="\x01\x01\x01\x03"
# Maximum size of multicast address list
RNDIS_OID_802_3_MAXIMUM_LIST_SIZE="\x01\x01\x01\x04"
# Directed packets. Directed packets contain a destination address equal to the station address of the NIC.
RNDIS_PACKET_TYPE_DIRECTED="\x00\x00\x00\x01"
# Multicast address packets sent to addresses in the multicast address list.
RNDIS_PACKET_TYPE_MULTICAST="\x00\x00\x00\x02"
# All multicast address packets, not just the ones enumerated in the multicast address list.
RNDIS_PACKET_TYPE_ALL_MULTICAST="\x00\x00\x00\x04"
# Broadcast packets.
RNDIS_PACKET_TYPE_BROADCAST="\x00\x00\x00\x08"
# All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge.
RNDIS_PACKET_TYPE_SOURCE_ROUTING="\x00\x00\x00\x10"
# Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not.
RNDIS_PACKET_TYPE_PROMISCUOUS="\x00\x00\x00\x20"
# SMT packets that an FDDI NIC receives.
RNDIS_PACKET_TYPE_SMT="\x00\x00\x00\x40"
# All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle.
RNDIS_PACKET_TYPE_ALL_LOCAL="\x00\x00\x00\x80"
# Packets sent to the current group address.
RNDIS_PACKET_TYPE_GROUP="\x00\x00\x10\x00"
# All functional address packets, not just the ones in the current functional address.
RNDIS_PACKET_TYPE_ALL_FUNCTIONAL="\x00\x00\x20\x00"
# Functional address packets sent to addresses included in the current functional address.
RNDIS_PACKET_TYPE_FUNCTIONAL="\x00\x00\x40\x00"
# NIC driver frames that a Token Ring NIC receives.
RNDIS_PACKET_TYPE_MAC_FRAME="\x00\x00\x80\x00"
RNDIS_PACKET_TYPE_NO_LOCAL="\x00\x01\x00\x00"

View File

@@ -0,0 +1,34 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "fuzzer/FuzzedDataProvider.h"
#include <optional>
std::optional<FuzzedDataProvider> _fuzz_data_provider;
extern "C" int fuzz_init(const uint8_t *data, size_t size) {
_fuzz_data_provider.emplace(data, size);
return 0;
}

View File

@@ -0,0 +1,37 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
int fuzz_init(const uint8_t *data, size_t size);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,30 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#pragma once
#include "fuzzer/FuzzedDataProvider.h"
#include <optional>
extern std::optional<FuzzedDataProvider> _fuzz_data_provider;

View File

@@ -0,0 +1,128 @@
# ---------------------------------------
# Common make definition for all examples
# ---------------------------------------
#-------------- TOP and CURRENT_PATH ------------
# Set TOP to be the path to get from the current directory (where make was
# invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns
# the name of this makefile relative to where make was invoked.
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
# strip off /tools/top.mk to get for example ../../..
# and Set TOP to an absolute path
TOP = $(abspath $(subst make.mk,../..,$(THIS_MAKEFILE)))
# Set CURRENT_PATH to the relative path from TOP to the current directory, ie examples/device/cdc_msc_freertos
CURRENT_PATH = $(subst $(TOP)/,,$(abspath .))
# Detect whether shell style is windows or not
# https://stackoverflow.com/questions/714100/os-detecting-makefile/52062069#52062069
ifeq '$(findstring ;,$(PATH))' ';'
# PATH contains semicolon - so we're definitely on Windows.
CMDEXE := 1
# makefile shell commands should use syntax for DOS CMD, not unix sh
# Unfortunately, SHELL may point to sh or bash, which can't accept DOS syntax.
# We can't just use sh, because while sh and/or bash shell may be available,
# many Windows environments won't have utilities like realpath used below, so...
# Force DOS command shell on Windows.
SHELL := cmd.exe
endif
# Build directory
BUILD := _build
PROJECT := $(notdir $(CURDIR))
# Handy check parameter function
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined make flag: $1$(if $2, ($2))))
#-------------- Fuzz harness compiler ------------
CC ?= clang
CXX ?= clang++
GDB ?= gdb
OBJCOPY = objcopy
SIZE = size
MKDIR = mkdir
ifeq ($(CMDEXE),1)
CP = copy
RM = del
PYTHON = python
else
SED = sed
CP = cp
RM = rm
PYTHON = python3
endif
#-------------- Fuzz harness flags ------------
COVERAGE_FLAGS ?= -fsanitize-coverage=trace-pc-guard
SANITIZER_FLAGS ?= -fsanitize=fuzzer \
-fsanitize=address
CFLAGS += $(COVERAGE_FLAGS) $(SANITIZER_FLAGS)
#-------------- Source files and compiler flags --------------
INC += $(TOP)/test
# Compiler Flags
CFLAGS += \
-ggdb \
-fdata-sections \
-ffunction-sections \
-fno-strict-aliasing \
-Wall \
-Wextra \
-Werror \
-Wfatal-errors \
-Wdouble-promotion \
-Wstrict-prototypes \
-Wstrict-overflow \
-Werror-implicit-function-declaration \
-Wfloat-equal \
-Wundef \
-Wshadow \
-Wwrite-strings \
-Wsign-compare \
-Wmissing-format-attribute \
-Wunreachable-code \
-Wcast-align \
-Wcast-qual \
-Wnull-dereference \
-Wuninitialized \
-Wunused \
-Wredundant-decls \
-O1
CFLAGS += \
-Wno-error=unreachable-code \
-DOPT_MCU_FUZZ=1 \
-DCFG_TUSB_MCU=OPT_MCU_FUZZ \
-D_FUZZ
CXXFLAGS += \
-xc++ \
-Wno-c++11-narrowing \
-fno-implicit-templates
# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion
# -Wconversion
# Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -Og
else
CFLAGS += $(CFLAGS_OPTIMIZED)
endif
# Log level is mapped to TUSB DEBUG option
ifneq ($(LOG),)
CFLAGS += -DCFG_TUSB_DEBUG=$(LOG)
endif

View File

@@ -0,0 +1,162 @@
#include "fuzz/fuzz_private.h"
#include "tusb.h"
#include <cassert>
#include <array>
#include <limits>
#if CFG_TUD_MSC==1
// Whether host does safe eject.
// tud_msc_get_maxlun_cb returns a uint8_t so the max logical units that are
// allowed is 255, so we need to keep track of 255 fuzzed logical units.
static std::array<bool, std::numeric_limits<uint8_t>::max()> ejected = {false};
extern "C" {
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16,
// 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
uint8_t product_id[16], uint8_t product_rev[4]) {
(void)lun;
assert(_fuzz_data_provider.has_value());
std::string vid = _fuzz_data_provider->ConsumeBytesAsString(8);
std::string pid = _fuzz_data_provider->ConsumeBytesAsString(16);
std::string rev = _fuzz_data_provider->ConsumeBytesAsString(4);
memcpy(vendor_id, vid.c_str(), strlen(vid.c_str()));
memcpy(product_id, pid.c_str(), strlen(pid.c_str()));
memcpy(product_rev, rev.c_str(), strlen(rev.c_str()));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
// RAM disk is ready until ejected
if (ejected[lun]) {
// Additional Sense 3A-00 is NOT_FOUND
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
return false;
}
return _fuzz_data_provider->ConsumeBool();
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and
// SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size Application update
// block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count,
uint16_t *block_size) {
(void)lun;
*block_count = _fuzz_data_provider->ConsumeIntegral<uint32_t>();
*block_size = _fuzz_data_provider->ConsumeIntegral<uint16_t>();
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start,
bool load_eject) {
(void)power_condition;
assert(_fuzz_data_provider.has_value());
if (load_eject) {
if (start) {
// load disk storage
} else {
// unload disk storage
ejected[lun] = true;
}
}
return _fuzz_data_provider->ConsumeBool();
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
void *buffer, uint32_t bufsize) {
assert(_fuzz_data_provider.has_value());
(void)lun;
(void)lba;
(void)offset;
std::vector<uint8_t> consumed_buffer = _fuzz_data_provider->ConsumeBytes<uint8_t>(
_fuzz_data_provider->ConsumeIntegralInRange<uint32_t>(0, bufsize));
memcpy(buffer, consumed_buffer.data(), consumed_buffer.size());
// Sometimes return an error code;
if (_fuzz_data_provider->ConsumeBool()) {
return _fuzz_data_provider->ConsumeIntegralInRange(
std::numeric_limits<int32_t>::min(), -1);
}
return consumed_buffer.size();
}
bool tud_msc_is_writable_cb(uint8_t lun) {
assert(_fuzz_data_provider.has_value());
(void)lun;
return _fuzz_data_provider->ConsumeBool();
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
uint8_t *buffer, uint32_t bufsize) {
// Ignore these as they are outputs and don't affect the return value.
(void)lun;
(void)lba;
(void)offset;
(void)buffer;
assert(_fuzz_data_provider.has_value());
// -ve error codes -> bufsize.
return _fuzz_data_provider->ConsumeIntegralInRange<int32_t>(
std::numeric_limits<int32_t>::min(), bufsize);
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer,
uint16_t bufsize) {
(void)buffer;
(void)bufsize;
assert(_fuzz_data_provider.has_value());
switch (scsi_cmd[0]) {
case SCSI_CMD_TEST_UNIT_READY:
break;
case SCSI_CMD_INQUIRY:
break;
case SCSI_CMD_MODE_SELECT_6:
break;
case SCSI_CMD_MODE_SENSE_6:
break;
case SCSI_CMD_START_STOP_UNIT:
break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
break;
case SCSI_CMD_READ_CAPACITY_10:
break;
case SCSI_CMD_REQUEST_SENSE:
break;
case SCSI_CMD_READ_FORMAT_CAPACITY:
break;
case SCSI_CMD_READ_10:
break;
case SCSI_CMD_WRITE_10:
break;
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
return _fuzz_data_provider->ConsumeIntegralInRange<int32_t>(
std::numeric_limits<int32_t>::min(), -1);
}
return 0;
}
}
#endif

View File

@@ -0,0 +1,74 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "tusb_config.h"
#if defined(CFG_TUD_ECM_RNDIS) || defined(CFG_TUD_NCM)
#include "class/net/net_device.h"
#include "fuzz_private.h"
#include <cassert>
#include <cstdint>
#include <vector>
#include "lwip/sys.h"
extern "C" {
bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
assert(_fuzz_data_provider.has_value());
(void)src;
(void)size;
return _fuzz_data_provider->ConsumeBool();
}
// client must provide this: copy from network stack packet pointer to dst
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
(void)ref;
(void)arg;
assert(_fuzz_data_provider.has_value());
uint16_t size = _fuzz_data_provider->ConsumeIntegral<uint16_t>();
std::vector<uint8_t> temp = _fuzz_data_provider->ConsumeBytes<uint8_t>(size);
memcpy(dst, temp.data(), temp.size());
return size;
}
/* lwip has provision for using a mutex, when applicable */
sys_prot_t sys_arch_protect(void) { return 0; }
void sys_arch_unprotect(sys_prot_t pval) { (void)pval; }
//------------- ECM/RNDIS -------------//
// client must provide this: initialize any network state back to the beginning
void tud_network_init_cb(void) {
// NoOp.
}
// client must provide this: 48-bit MAC address
// TODO removed later since it is not part of tinyusb stack
uint8_t tud_network_mac_address[6] = {0};
} // extern "C"
#endif

View File

@@ -0,0 +1,160 @@
# ---------------------------------------
# Common make rules for all examples
# ---------------------------------------
# Set all as default goal
.DEFAULT_GOAL := all
# ---------------------------------------
# Compiler Flags
# ---------------------------------------
LIBS_GCC ?= -lm
# libc
LIBS += $(LIBS_GCC)
ifneq ($(BOARD), spresense)
LIBS += -lc -Wl,-Bstatic -lc++ -Wl,-Bdynamic
endif
# TinyUSB Stack source
SRC_C += \
src/tusb.c \
src/common/tusb_fifo.c \
src/device/usbd.c \
src/device/usbd_control.c \
src/class/audio/audio_device.c \
src/class/cdc/cdc_device.c \
src/class/dfu/dfu_device.c \
src/class/dfu/dfu_rt_device.c \
src/class/hid/hid_device.c \
src/class/midi/midi_device.c \
src/class/msc/msc_device.c \
src/class/mtp/mtp_device.c \
src/class/net/ecm_rndis_device.c \
src/class/net/ncm_device.c \
src/class/usbtmc/usbtmc_device.c \
src/class/video/video_device.c \
src/class/vendor/vendor_device.c
# Fuzzers are c++
SRC_CXX += \
test/fuzz/dcd_fuzz.cc \
test/fuzz/fuzz.cc \
test/fuzz/msc_fuzz.cc \
test/fuzz/net_fuzz.cc \
test/fuzz/usbd_fuzz.cc
# TinyUSB stack include
INC += $(TOP)/src
CFLAGS += $(addprefix -I,$(INC))
CXXFLAGS += -std=c++17
# LTO makes it difficult to analyze map file for optimizing size purpose
# We will run this option in ci
ifeq ($(NO_LTO),1)
CFLAGS := $(filter-out -flto,$(CFLAGS))
endif
ifneq ($(LD_FILE),)
LDFLAGS_LD_FILE ?= -Wl,-T,$(TOP)/$(LD_FILE)
endif
LDFLAGS += $(CFLAGS) $(LDFLAGS_LD_FILE) -fuse-ld=lld -Wl,-Map=$@.map -Wl,--cref -Wl,-gc-sections
ifneq ($(SKIP_NANOLIB), 1)
endif
ASFLAGS += $(CFLAGS)
# Assembly files can be name with upper case .S, convert it to .s
SRC_S := $(SRC_S:.S=.s)
# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
# assembly file should be placed first in linking order
# '_asm' suffix is added to object of assembly file
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o))
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_CXX:.cc=_cxx.o))
# Verbose mode
ifeq ("$(V)","1")
$(info CFLAGS $(CFLAGS) ) $(info )
$(info LDFLAGS $(LDFLAGS)) $(info )
$(info ASFLAGS $(ASFLAGS)) $(info )
endif
# ---------------------------------------
# Rules
# ---------------------------------------
all: $(BUILD)/$(PROJECT)
OBJ_DIRS = $(sort $(dir $(OBJ)))
$(OBJ): | $(OBJ_DIRS)
$(OBJ_DIRS):
ifeq ($(CMDEXE),1)
@$(MKDIR) $(subst /,\,$@)
else
@$(MKDIR) -p $@
endif
$(BUILD)/$(PROJECT): $(OBJ)
@echo LINK $@
@ $(CXX) -o $@ $(LIB_FUZZING_ENGINE) $^ $(LIBS) $(LDFLAGS)
# We set vpath to point to the top of the tree so that the source files
# can be located. By following this scheme, it allows a single build rule
# to be used to compile all .c files.
vpath %.c . $(TOP)
$(BUILD)/obj/%.o: %.c
@echo CC $(notdir $@)
@$(CC) $(CFLAGS) -c -MD -o $@ $<
# All cpp srcs
vpath %.cc . $(TOP)
$(BUILD)/obj/%_cxx.o: %.cc
@echo CXX $(notdir $@)
@$(CXX) $(CFLAGS) $(CXXFLAGS) -c -MD -o $@ $<
# ASM sources lower case .s
vpath %.s . $(TOP)
$(BUILD)/obj/%_asm.o: %.s
@echo AS $(notdir $@)
@$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
# ASM sources upper case .S
vpath %.S . $(TOP)
$(BUILD)/obj/%_asm.o: %.S
@echo AS $(notdir $@)
@$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
.PHONY: clean
clean:
ifeq ($(CMDEXE),1)
rd /S /Q $(subst /,\,$(BUILD))
else
$(RM) -rf $(BUILD)
endif
# ---------------- GNU Make End -----------------------
# get depenecies
.PHONY: get-deps
get-deps:
$(PYTHON) $(TOP)/tools/get_deps.py $(FAMILY)
size: $(BUILD)/$(PROJECT)
-@echo ''
@$(SIZE) $<
-@echo ''
# linkermap must be install previously at https://github.com/hathach/linkermap
linkermap: $(BUILD)/$(PROJECT)
@linkermap -v $<.map
# Print out the value of a make variable.
# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile
print-%:
@echo $* = $($*)

View File

@@ -0,0 +1,73 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2022 Nathaniel Brough
*
* 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.
*
*/
#include "fuzz/fuzz_private.h"
#include "tusb.h"
// #include "usb_descriptors.h"
#ifndef CFG_FUZZ_MAX_STRING_LEN
#define CFG_FUZZ_MAX_STRING_LEN 1000
#endif
extern "C" {
/* TODO: Implement a fuzzed version of this.
uint8_t const *tud_descriptor_bos_cb(void) { }
*/
/* TODO: Implement a fuzzed version of this.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {}
*/
/* TODO: Implement a fuzzed version of this.
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {}
*/
void tud_mount_cb(void) {
// NOOP
}
void tud_umount_cb(void) {
// NOOP
}
void tud_suspend_cb(bool remote_wakeup_en) {
(void)remote_wakeup_en;
// NOOP
}
void tud_resume_cb(void) {
// NOOP
}
/* TODO: Implement a fuzzed version of this.
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request) {}
*/
/* TODO: Implement a fuzzed version of this.
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {}
*/
}

View File

@@ -0,0 +1,40 @@
{
"boards": [
{
"name": "stm32l412nucleo",
"uid": "41003B000E504E5457323020",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "774470029",
"args": "-device STM32L412KB"
}
},
{
"name": "stm32f746disco",
"uid": "210041000C51343237303334",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "770935966",
"args": "-device STM32F746NG"
}
},
{
"name": "lpcxpresso43s67",
"uid": "08F000044528BAAA8D858F58C50700F5",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "728973776",
"args": "-device LPC43S67_M4"
}
}
]
}

View File

@@ -0,0 +1,50 @@
import argparse
import json
import os
def main():
parser = argparse.ArgumentParser()
parser.add_argument('config_file', help='Configuration JSON file')
args = parser.parse_args()
config_file = args.config_file
# if config file is not found, try to find it in the same directory as this script
if not os.path.exists(config_file):
config_file = os.path.join(os.path.dirname(__file__), config_file)
with open(config_file) as f:
config = json.load(f)
matrix = {
'arm-gcc': [],
'esp-idf': []
}
for board in config['boards']:
name = board['name']
flasher = board['flasher']
if flasher['name'] == 'esptool':
toolchain = 'esp-idf'
else:
toolchain = 'arm-gcc'
build_board = f'-b {name}'
if 'build' in board:
if 'args' in board['build']:
build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
if 'flags_on' in board['build']:
for f in board['build']['flags_on']:
if f == '':
matrix[toolchain].append(build_board)
else:
matrix[toolchain].append(f'{build_board} -f1 {f.replace(" ", " -f1 ")}')
else:
matrix[toolchain].append(build_board)
else:
matrix[toolchain].append(build_board)
print(json.dumps(matrix))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,753 @@
#!/usr/bin/env python3
#
# The MIT License (MIT)
#
# Copyright (c) 2023 HiFiPhile
#
# 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.
# udev rules :
# ACTION=="add", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", SYMLINK+="ttyUSB_%c.%s{bInterfaceNumber}"
# ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ENV{ID_FS_USAGE}=="filesystem", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode /media/blkUSB_%c.%s{bInterfaceNumber}"
import argparse
import os
import random
import re
import sys
import time
import serial
import subprocess
import json
import glob
from multiprocessing import Pool
import fs
import hashlib
import ctypes
from pymtp import MTP
ENUM_TIMEOUT = 30
STATUS_OK = "\033[32mOK\033[0m"
STATUS_FAILED = "\033[31mFailed\033[0m"
STATUS_SKIPPED = "\033[33mSkipped\033[0m"
verbose = False
test_only = []
WCH_RISCV_CONTENT = """
adapter driver wlinke
adapter speed 6000
transport select sdi
wlink_set_address 0x00000000
set _CHIPNAME wch_riscv
sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
echo "Ready for Remote Connections"
"""
# -------------------------------------------------------------
# Path
# -------------------------------------------------------------
OPENCOD_ADI_PATH = f'{os.getenv("HOME")}/app/openocd_adi'
TINYUSB_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# get usb serial by id
def get_serial_dev(id, vendor_str, product_str, ifnum):
if vendor_str and product_str:
# known vendor and product
vendor_str = vendor_str.replace(' ', '_')
product_str = product_str.replace(' ', '_')
return f'/dev/serial/by-id/usb-{vendor_str}_{product_str}_{id}-if{ifnum:02d}'
else:
# just use id: mostly for cp210x/ftdi flasher
pattern = f'/dev/serial/by-id/usb-*_{id}-if*'
port_list = glob.glob(pattern)
return port_list[0]
# get usb disk by id
def get_disk_dev(id, vendor_str, lun):
return f'/dev/disk/by-id/usb-{vendor_str}_Mass_Storage_{id}-0:{lun}'
def get_hid_dev(id, vendor_str, product_str, event):
return f'/dev/input/by-id/usb-{vendor_str}_{product_str}_{id}-{event}'
def open_serial_dev(port):
timeout = ENUM_TIMEOUT
ser = None
while timeout > 0:
if os.path.exists(port):
try:
ser = serial.Serial(port, baudrate=115200, timeout=5)
break
except serial.SerialException:
print(f'serial {port} not reaady {timeout} sec')
pass
time.sleep(0.1)
timeout -= 0.1
assert timeout > 0, f'Cannot open port f{port}' if os.path.exists(port) else f'Port {port} not existed'
return ser
def read_disk_file(uid, lun, fname):
# open_fs("fat://{dev}) require 'pip install pyfatfs'
dev = get_disk_dev(uid, 'TinyUSB', lun)
timeout = ENUM_TIMEOUT
while timeout > 0:
if os.path.exists(dev):
fat = fs.open_fs(f'fat://{dev}?read_only=true')
try:
with fat.open(fname, 'rb') as f:
data = f.read()
finally:
fat.close()
assert data, f'Cannot read file {fname} from {dev}'
return data
time.sleep(1)
timeout -= 1
assert timeout > 0, f'Storage {dev} not existed'
return None
def open_mtp_dev(uid):
mtp = MTP()
timeout = ENUM_TIMEOUT
while timeout > 0:
# run_cmd(f"gio mount -u mtp://TinyUsb_TinyUsb_Device_{uid}/")
for raw in mtp.detect_devices():
mtp.device = mtp.mtp.LIBMTP_Open_Raw_Device(ctypes.byref(raw))
if mtp.device:
sn = mtp.get_serialnumber().decode('utf-8')
#print(f'mtp serial = {sn}')
if sn == uid:
return mtp
time.sleep(1)
timeout -= 1
return None
# -------------------------------------------------------------
# Flashing firmware
# -------------------------------------------------------------
def run_cmd(cmd, cwd=None):
r = subprocess.run(cmd, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if r.returncode != 0:
title = f'COMMAND FAILED: {cmd}'
print()
if os.getenv('CI'):
print(f"::group::{title}")
print(r.stdout.decode("utf-8"))
print(f"::endgroup::")
else:
print(title)
print(r.stdout.decode("utf-8"))
elif verbose:
print(cmd)
print(r.stdout.decode("utf-8"))
return r
def flash_jlink(board, firmware):
flasher = board['flasher']
script = ['halt', 'r', f'loadfile {firmware}.elf', 'r', 'go', 'exit']
f_jlink = f'{board["name"]}_{os.path.basename(firmware)}.jlink'
with open(f_jlink, 'w') as f:
f.writelines(f'{s}\n' for s in script)
ret = run_cmd(f'JLinkExe -USB {flasher["uid"]} {flasher["args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}')
os.remove(f_jlink)
return ret
def reset_jlink(board):
flasher = board['flasher']
script = ['halt', 'r', 'go', 'exit']
f_jlink = f'{board["name"]}_reset.jlink'
if not os.path.exists(f_jlink):
with open(f_jlink, 'w') as f:
f.writelines(f'{s}\n' for s in script)
ret = run_cmd(f'JLinkExe -USB {flasher["uid"]} {flasher["args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}')
return ret
def flash_stlink(board, firmware):
flasher = board['flasher']
return run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={flasher["uid"]} --write {firmware}.elf --go')
def reset_stlink(board):
flasher = board['flasher']
return run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={flasher["uid"]} --rst --go')
def flash_stflash(board, firmware):
flasher = board['flasher']
ret = run_cmd(f'st-flash --serial {flasher["uid"]} write {firmware}.bin 0x8000000')
return ret
def reset_stflash(board):
flasher = board['flasher']
return subprocess.CompletedProcess(args=['dummy'], returncode=0)
def flash_openocd(board, firmware):
flasher = board['flasher']
ret = run_cmd(f'openocd -c "tcl_port disabled" -c "gdb_port disabled" -c "adapter serial {flasher["uid"]}" '
f'{flasher["args"]} -c "init; halt; program {firmware}.elf verify; reset; exit"')
return ret
def reset_openocd(board):
flasher = board['flasher']
ret = run_cmd(f'openocd -c "tcl_port disabled" -c "gdb_port disabled" -c "adapter serial {flasher["uid"]}" '
f'{flasher["args"]} -c "init; reset run; exit"')
return ret
def flash_openocd_wch(board, firmware):
flasher = board['flasher']
f_wch = f"wch-riscv_{board['uid']}.cfg"
if not os.path.exists(f_wch):
with open(f_wch, 'w') as file:
file.write(WCH_RISCV_CONTENT)
ret = run_cmd(f'openocd_wch -c "adapter serial {flasher["uid"]}" -f {f_wch} '
f'-c "program {firmware}.elf reset exit"')
return ret
def reset_openocd_wch(board):
flasher = board['flasher']
f_wch = f"wch-riscv_{board['uid']}.cfg"
if not os.path.exists(f_wch):
with open(f_wch, 'w') as file:
file.write(WCH_RISCV_CONTENT)
ret = run_cmd(f'openocd_wch -c "adapter serial {flasher["uid"]}" -f {f_wch} -c "program reset exit"')
return ret
def flash_openocd_adi(board, firmware):
flasher = board['flasher']
ret = run_cmd(f'{OPENCOD_ADI_PATH}/src/openocd -c "adapter serial {flasher["uid"]}" -s {OPENCOD_ADI_PATH}/tcl '
f'{flasher["args"]} -c "program {firmware}.elf reset exit"')
return ret
def reset_openocd_adi(board):
flasher = board['flasher']
ret = run_cmd(f'{OPENCOD_ADI_PATH}/src/openocd -c "adapter serial {flasher["uid"]}" -s {OPENCOD_ADI_PATH}/tcl '
f'{flasher["args"]} -c "program reset exit"')
return ret
def flash_wlink_rs(board, firmware):
flasher = board['flasher']
# wlink use index for probe selection and lacking usb serial support
ret = run_cmd(f'wlink flash {firmware}.elf')
return ret
def reset_wlink_rs(board):
flasher = board['flasher']
# wlink use index for probe selection and lacking usb serial support
ret = run_cmd(f'wlink reset')
return ret
def flash_esptool(board, firmware):
flasher = board['flasher']
port = get_serial_dev(flasher["uid"], None, None, 0)
fw_dir = os.path.dirname(f'{firmware}.bin')
with open(f'{fw_dir}/config.env') as f:
idf_target = json.load(f)['IDF_TARGET']
with open(f'{fw_dir}/flash_args') as f:
flash_args = f.read().strip().replace('\n', ' ')
command = (f'esptool.py --chip {idf_target} -p {port} {flasher["args"]} '
f'--before=default_reset --after=hard_reset write_flash {flash_args}')
ret = run_cmd(command, cwd=fw_dir)
return ret
def reset_esptool(board):
flasher = board['flasher']
return subprocess.CompletedProcess(args=['dummy'], returncode=0)
def flash_uniflash(board, firmware):
flasher = board['flasher']
ret = run_cmd(f'dslite.sh {flasher["args"]} -f {firmware}.hex')
return ret
def reset_uniflash(board):
flasher = board['flasher']
return subprocess.CompletedProcess(args=['dummy'], returncode=0)
# -------------------------------------------------------------
# Tests: dual
# -------------------------------------------------------------
def test_dual_host_info_to_device_cdc(board):
uid = board['uid']
declared_devs = [f'{d["vid_pid"]}_{d["serial"]}' for d in board['tests']['dev_attached']]
port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
ser = open_serial_dev(port)
# read from cdc, first line should contain vid/pid and serial
data = ser.read(10000)
ser.close()
if len(data) == 0:
assert False, 'No data from device'
lines = data.decode('utf-8', errors='ignore').splitlines()
enum_dev_sn = []
for l in lines:
vid_pid_sn = re.search(r'ID ([0-9a-fA-F]+):([0-9a-fA-F]+) SN (\w+)', l)
if vid_pid_sn:
print(f'\r\n {l} ', end='')
enum_dev_sn.append(f'{vid_pid_sn.group(1)}_{vid_pid_sn.group(2)}_{vid_pid_sn.group(3)}')
if set(declared_devs) != set(enum_dev_sn):
failed_msg = f'Expected {declared_devs}, Enumerated {enum_dev_sn}'
print('\n'.join(lines))
assert False, failed_msg
return 0
# -------------------------------------------------------------
# Tests: host
# -------------------------------------------------------------
def test_host_device_info(board):
flasher = board['flasher']
declared_devs = [f'{d["vid_pid"]}_{d["serial"]}' for d in board['tests']['dev_attached']]
port = get_serial_dev(flasher["uid"], None, None, 0)
ser = open_serial_dev(port)
# reset device since we can miss the first line
ret = globals()[f'reset_{flasher["name"].lower()}'](board)
assert ret.returncode == 0, 'Failed to reset device'
data = ser.read(10000)
ser.close()
if len(data) == 0:
assert False, 'No data from device'
lines = data.decode('utf-8', errors='ignore').splitlines()
enum_dev_sn = []
for l in lines:
vid_pid_sn = re.search(r'ID ([0-9a-fA-F]+):([0-9a-fA-F]+) SN (\w+)', l)
if vid_pid_sn:
print(f'\r\n {l} ', end='')
enum_dev_sn.append(f'{vid_pid_sn.group(1)}_{vid_pid_sn.group(2)}_{vid_pid_sn.group(3)}')
if set(declared_devs) != set(enum_dev_sn):
failed_msg = f'Expected {declared_devs}, Enumerated {enum_dev_sn}'
print('\n'.join(lines))
assert False, failed_msg
return 0
# -------------------------------------------------------------
# Tests: device
# -------------------------------------------------------------
def test_device_board_test(board):
# Dummy test
pass
def test_device_cdc_dual_ports(board):
uid = board['uid']
port = [
get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0),
get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 2)
]
ser = [open_serial_dev(p) for p in port]
str_test = [ b"test_no1", b"test_no2" ]
# Echo test write to each port and read back
for i in range(len(str_test)):
s = str_test[i]
l = len(s)
ser[i].write(s)
ser[i].flush()
rd = [ ser[i].read(l) for i in range(len(ser)) ]
assert rd[0] == s.lower(), f'Port1 wrong data: expected {s.lower()} was {rd[0]}'
assert rd[1] == s.upper(), f'Port2 wrong data: expected {s.upper()} was {rd[1]}'
ser[0].close()
ser[1].close()
def test_device_cdc_msc(board):
uid = board['uid']
# Echo test
port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
ser = open_serial_dev(port)
test_str = b"test_str"
ser.write(test_str)
ser.flush()
rd_str = ser.read(len(test_str))
ser.close()
assert rd_str == test_str, f'CDC wrong data: expected: {test_str} was {rd_str}'
# Block test
data = read_disk_file(uid,0,'README.TXT')
readme = \
b"This is tinyusb's MassStorage Class demo.\r\n\r\n\
If you find any bugs or get any questions, feel free to file an\r\n\
issue at github.com/hathach/tinyusb"
assert data == readme, 'MSC wrong data'
def test_device_cdc_msc_freertos(board):
test_device_cdc_msc(board)
def test_device_dfu(board):
uid = board['uid']
# Wait device enum
timeout = ENUM_TIMEOUT
while timeout > 0:
ret = run_cmd(f'dfu-util -l')
stdout = ret.stdout.decode()
if f'serial="{uid}"' in stdout and 'Found DFU: [cafe:4000]' in stdout:
break
time.sleep(1)
timeout = timeout - 1
assert timeout > 0, 'Device not available'
f_dfu0 = f'dfu0_{uid}'
f_dfu1 = f'dfu1_{uid}'
# Test upload
try:
os.remove(f_dfu0)
os.remove(f_dfu1)
except OSError:
pass
ret = run_cmd(f'dfu-util -S {uid} -a 0 -U {f_dfu0}')
assert ret.returncode == 0, 'Upload failed'
ret = run_cmd(f'dfu-util -S {uid} -a 1 -U {f_dfu1}')
assert ret.returncode == 0, 'Upload failed'
with open(f_dfu0) as f:
assert 'Hello world from TinyUSB DFU! - Partition 0' in f.read(), 'Wrong uploaded data'
with open(f_dfu1) as f:
assert 'Hello world from TinyUSB DFU! - Partition 1' in f.read(), 'Wrong uploaded data'
os.remove(f_dfu0)
os.remove(f_dfu1)
def test_device_dfu_runtime(board):
uid = board['uid']
# Wait device enum
timeout = ENUM_TIMEOUT
while timeout > 0:
ret = run_cmd(f'dfu-util -l')
stdout = ret.stdout.decode()
if f'serial="{uid}"' in stdout and 'Found Runtime: [cafe:4000]' in stdout:
break
time.sleep(1)
timeout = timeout - 1
assert timeout > 0, 'Device not available'
def test_device_hid_boot_interface(board):
uid = board['uid']
kbd = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'event-kbd')
mouse1 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse')
mouse2 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse')
# Wait device enum
timeout = ENUM_TIMEOUT
while timeout > 0:
if os.path.exists(kbd) and os.path.exists(mouse1) and os.path.exists(mouse2):
break
time.sleep(1)
timeout = timeout - 1
assert timeout > 0, 'HID device not available'
def test_device_hid_composite_freertos(id):
# TODO implement later
pass
def test_device_mtp(board):
uid = board['uid']
# --- BEFORE: mute C-level stderr for libmtp vid/pid warnings ---
fd = sys.stderr.fileno()
_saved = os.dup(fd)
_null = os.open(os.devnull, os.O_WRONLY)
os.dup2(_null, fd)
mtp = open_mtp_dev(uid)
# --- AFTER: restore stderr ---
os.dup2(_saved, fd)
os.close(_null)
os.close(_saved)
if mtp is None or mtp.device is None:
assert False, 'MTP device not found'
try:
assert b"TinyUSB" == mtp.get_manufacturer(), 'MTP wrong manufacturer'
assert b"MTP Example" == mtp.get_modelname(), 'MTP wrong model'
assert b'1.0' == mtp.get_deviceversion(), 'MTP wrong version'
assert b'TinyUSB MTP' == mtp.get_devicename(), 'MTP wrong device name'
# read and compare readme.txt and logo.png
f1_expect = b'TinyUSB MTP Filesystem example'
f2_md5_expect = '40ef23fc2891018d41a05d4a0d5f822f' # md5sum of logo.png
f1 = uid.encode("utf-8") + b'_file1'
f2 = uid.encode("utf-8") + b'_file2'
f3 = uid.encode("utf-8") + b'_file3'
mtp.get_file_to_file(1, f1)
with open(f1, 'rb') as file:
f1_data = file.read()
os.remove(f1)
assert f1_data == f1_expect, 'MTP file1 wrong data'
mtp.get_file_to_file(2, f2)
with open(f2, 'rb') as file:
f2_data = file.read()
os.remove(f2)
assert f2_md5_expect == hashlib.md5(f2_data).hexdigest(), 'MTP file2 wrong data'
# test send file
with open(f3, "wb") as file:
f3_data = os.urandom(random.randint(1024, 3*1024))
file.write(f3_data)
file.close()
fid = mtp.send_file_from_file(f3, b'file3')
f3_readback = f3 + b'_readback'
mtp.get_file_to_file(fid, f3_readback)
with open(f3_readback, 'rb') as f:
f3_rb_data = f.read()
os.remove(f3_readback)
assert f3_rb_data == f3_data, 'MTP file3 wrong data'
os.remove(f3)
mtp.delete_object(fid)
finally:
mtp.disconnect()
# -------------------------------------------------------------
# Main
# -------------------------------------------------------------
# device tests
# note don't test 2 examples with cdc or 2 msc next to each other
device_tests = [
'device/cdc_dual_ports',
'device/dfu',
'device/cdc_msc',
'device/dfu_runtime',
'device/cdc_msc_freertos',
'device/hid_boot_interface',
'device/mtp'
]
dual_tests = [
'dual/host_info_to_device_cdc',
]
host_test = [
'host/device_info',
]
def test_example(board, f1, example):
"""
Test example firmware
:param board: board dict
:param f1: flags on
:param example: example name
:return: 0 if success/skip, 1 if failed
"""
name = board['name']
err_count = 0
f1_str = ""
if f1 != "":
f1_str = '-f1_' + f1.replace(' ', '_')
fw_dir = f'{TINYUSB_ROOT}/cmake-build/cmake-build-{name}{f1_str}/{example}'
if not os.path.exists(fw_dir):
fw_dir = f'{TINYUSB_ROOT}/examples/cmake-build-{name}{f1_str}/{example}'
fw_name = f'{fw_dir}/{os.path.basename(example)}'
print(f'{name+f1_str:40} {example:30} ...', end='')
if not os.path.exists(fw_dir) or not (os.path.exists(f'{fw_name}.elf') or os.path.exists(f'{fw_name}.bin')):
print('Skip (no binary)')
return 0
if verbose:
print(f'Flashing {fw_name}.elf')
# flash firmware. It may fail randomly, retry a few times
max_rety = 3
start_s = time.time()
for i in range(max_rety):
ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name)
if ret.returncode == 0:
try:
globals()[f'test_{example.replace("/", "_")}'](board)
print(' OK', end='')
break
except Exception as e:
if i == max_rety - 1:
err_count += 1
print(f'{STATUS_FAILED}: {e}')
else:
print(f'\n Test failed: {e}, retry {i+2}/{max_rety}', end='')
time.sleep(0.5)
else:
print(f'\n Flash failed, retry {i+2}/{max_rety}', end='')
time.sleep(0.5)
if ret.returncode != 0:
err_count += 1
print(f' Flash {STATUS_FAILED}', end='')
print(f' in {time.time() - start_s:.1f}s')
return err_count
def test_board(board):
name = board['name']
flasher = board['flasher']
# default to all tests
test_list = []
if len(test_only) > 0:
test_list = test_only
else:
if 'tests' in board:
board_tests = board['tests']
if 'device' in board_tests and board_tests['device'] == True:
test_list += list(device_tests)
if 'dual' in board_tests and board_tests['dual'] == True:
test_list += dual_tests
if 'host' in board_tests and board_tests['host'] == True:
test_list += host_test
if 'only' in board_tests:
test_list = board_tests['only']
if 'skip' in board_tests:
for skip in board_tests['skip']:
if skip in test_list:
test_list.remove(skip)
print(f'{name:25} {skip:30} ... Skip')
err_count = 0
flags_on_list = [""]
if 'build' in board and 'flags_on' in board['build']:
flags_on_list = board['build']['flags_on']
for f1 in flags_on_list:
for test in test_list:
err_count += test_example(board, f1, test)
# flash board_test last to disable board's usb
test_example(board, flags_on_list[0], 'device/board_test')
return name, err_count
def main():
"""
Hardware test on specified boards
"""
global verbose
global test_only
duration = time.time()
parser = argparse.ArgumentParser()
parser.add_argument('config_file', help='Configuration JSON file')
parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified')
parser.add_argument('-s', '--skip', action='append', default=[], help='Skip boards from test')
parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
config_file = args.config_file
boards = args.board
skip_boards = args.skip
verbose = args.verbose
test_only = args.test_only
# if config file is not found, try to find it in the same directory as this script
if not os.path.exists(config_file):
config_file = os.path.join(os.path.dirname(__file__), config_file)
with open(config_file) as f:
config = json.load(f)
if len(boards) == 0:
config_boards = [e for e in config['boards'] if e['name'] not in skip_boards]
else:
config_boards = [e for e in config['boards'] if e['name'] in boards]
err_count = 0
with Pool(processes=os.cpu_count()) as pool:
mret = pool.map(test_board, config_boards)
err_count = sum(e[1] for e in mret)
# generate skip list for next re-run if failed
skip_fname = f'{config_file}.skip'
if err_count > 0:
skip_boards += [name for name, err in mret if err == 0]
with open(skip_fname, 'w') as f:
f.write(' '.join(f'-s {i}' for i in skip_boards))
elif os.path.exists(skip_fname):
os.remove(skip_fname)
duration = time.time() - duration
print()
print("-" * 30)
print(f'Total failed: {err_count} in {duration:.1f}s')
print("-" * 30)
sys.exit(err_count)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
fs
pyfatfs

View File

@@ -0,0 +1,269 @@
{
"boards": [
{
"name": "espressif_p4_function_ev",
"uid": "6055F9F98715",
"build" : {
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE"]
},
"tests": {
"only": ["device/cdc_msc_freertos", "device/hid_composite_freertos", "host/device_info"],
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002427"}]
},
"flasher": {
"name": "esptool",
"uid": "4ea4f48f6bc3ee11bbb9d00f9e1b1c54",
"args": "-b 1500000"
},
"comment": "Use TS3USB30 mux to test both device and host"
},
{
"name": "espressif_s3_devkitm",
"uid": "84F703C084E4",
"build" : {
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE"]
},
"tests": {
"only": ["device/cdc_msc_freertos", "device/hid_composite_freertos", "host/device_info"],
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2005402"}]
},
"flasher": {
"name": "esptool",
"uid": "3ea619acd1cdeb11a0a0b806e93fd3f1",
"args": "-b 1500000"
},
"comment": "Use TS3USB30 mux to test both device and host"
},
{
"name": "feather_nrf52840_express",
"uid": "1F0479CD0F764471",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "000682804350",
"args": "-device nrf52840_xxaa"
}
},
{
"name": "max32666fthr",
"uid": "0C81464124010B20FF0A08CC2C",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "openocd_adi",
"uid": "E6614C311B597D32",
"args": "-f interface/cmsis-dap.cfg -f target/max32665.cfg"
}
},
{
"name": "metro_m4_express",
"uid": "9995AD485337433231202020FF100A34",
"build" : {
"args": ["MAX3421_HOST=1"]
},
"tests": {
"device": true, "host": false, "dual": true,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002130"}]
},
"flasher": {
"name": "jlink",
"uid": "123456",
"args": "-device ATSAMD51J19"
}
},
{
"name": "mimxrt1064_evk",
"uid": "BAE96FB95AFA6DBB8F00005002001200",
"tests": {
"device": true, "host": true, "dual": true,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2023299"}]
},
"flasher": {
"name": "jlink",
"uid": "000725299165",
"args": "-device MIMXRT1064xxx6A"
}
},
{
"name": "lpcxpresso11u37",
"uid": "17121919",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "000724441579",
"args": "-device LPC11U37/401"
}
},
{
"name": "ra4m1_ek",
"uid": "152E163038303131393346E46F26574B",
"tests": {
"device": true, "host": false, "dual": false,
"skip": ["device/cdc_msc", "device/cdc_msc_freertos"]
},
"comment": "MSC is slow to enumerated #2602",
"flasher": {
"name": "jlink",
"uid": "000831174392",
"args": "-device R7FA4M1AB"
}
},
{
"name": "raspberry_pi_pico",
"uid": "E6614C311B764A37",
"build" : {
"flags_on": ["CFG_TUH_RPI_PIO_USB"]
},
"tests": {
"device": true, "host": true, "dual": true,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002470"}]
},
"flasher": {
"name": "openocd",
"uid": "E6614103E72C1D2F",
"args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
}
},
{
"name": "raspberry_pi_pico_w",
"uid": "E6614C311B764A37",
"tests": {
"device": false, "host": true, "dual": false,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2023934"}]
},
"flasher": {
"name": "openocd",
"uid": "E6633861A3819D38",
"args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
},
"comment": "Test native host"
},
{
"name": "raspberry_pi_pico2",
"uid": "560AE75E1C7152C9",
"build" : {
"flags_on": ["CFG_TUH_RPI_PIO_USB"]
},
"tests": {
"device": true, "host": true, "dual": true,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "533D004242"}]
},
"flasher": {
"name": "openocd",
"uid": "E6633861A3978538",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
{
"name": "stm32f072disco",
"uid": "3A001A001357364230353532",
"flasher": {
"name": "jlink",
"uid": "779541626",
"args": "-device stm32f072rb"
}
},
{
"name": "stm32f723disco",
"uid": "460029001951373031313335",
"build" : {
"flags_on": ["", "CFG_TUH_DWC2_DMA_ENABLE"]
},
"tests": {
"device": true, "host": true, "dual": false,
"dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2003414"}]
},
"flasher": {
"name": "jlink",
"uid": "000776606156",
"args": "-device stm32f723ie"
},
"comment": "Device port0 FS (slave only), Host port1 HS with DMA"
},
{
"name": "stm32h743nucleo",
"uid": "110018000951383432343236",
"build" : {
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
},
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "openocd",
"uid": "004C00343137510F39383538",
"args": "-f interface/stlink.cfg -f target/stm32h7x.cfg"
}
},
{
"name": "stm32g0b1nucleo",
"uid": "4D0038000450434E37343120",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "openocd",
"uid": "066FFF495087534867063844",
"args": "-f interface/stlink.cfg -f target/stm32g0x.cfg"
}
}
],
"boards-skip": [
{
"name": "stm32f769disco",
"uid": "21002F000F51363531383437",
"build" : {
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
},
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "000778170924",
"args": "-device stm32f769ni"
}
},
{
"name": "mimxrt1015_evk",
"uid": "DC28F865D2111D228D00B0543A70463C",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "000726284213",
"args": "-device MIMXRT1015DAF5A"
}
},
{
"name": "nanoch32v203",
"uid": "CDAB277B0FBC03E339E339E3",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "EBCA8F0670AF",
"args": ""
}
},
{
"name": "stm32f407disco",
"uid": "30001A000647313332353735",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "000773661813",
"args": "-device stm32f407vg"
}
}
]
}

View File

@@ -0,0 +1,3 @@
#!/bin/bash
ruby vendor/ceedling/bin/ceedling $*

View File

@@ -0,0 +1,437 @@
# =========================================================================
# Ceedling - Test-Centered Build System for C
# ThrowTheSwitch.org
# Copyright (c) 2010-25 Mike Karlesky, Mark VanderVoord, & Greg Williams
# SPDX-License-Identifier: MIT
# =========================================================================
---
:project:
# how to use ceedling. If you're not sure, leave this as `gem` and `?`
:which_ceedling: gem
:ceedling_version: 1.0.0
:verbosity: 3
# optional features. If you don't need them, keep them turned off for performance
:use_mocks: TRUE
:use_test_preprocessor: :mocks # options are :none, :mocks, :tests, or :all
:use_deep_dependencies: :all # options are :none, :mocks, :tests, or :all
:use_backtrace: :simple # options are :none, :simple, or :gdb
:use_decorators: :auto # decorate Ceedling's output text. options are :auto, :all, or :none
# tweak the way ceedling handles automatic tasks
:build_root: _build
:test_file_prefix: test_
:default_tasks:
- test:all
# performance options. If your tools start giving mysterious errors, consider
# dropping this to 1 to force single-tasking
:test_threads: 8
:compile_threads: 8
# enable release build (more details in release_build section below)
:release_build: FALSE
# Specify where to find mixins and any that should be enabled automatically
:mixins:
:enabled: []
:load_paths: []
# further details to configure the way Ceedling handles test code
:test_build:
:use_assembly: FALSE
:test_runner:
# Insert additional #include statements in a generated runner
:includes:
- osal.h
# further details to configure the way Ceedling handles release code
:release_build:
:output: MyApp.out
:use_assembly: FALSE
:artifacts: []
# Plugins are optional Ceedling features which can be enabled. Ceedling supports
# a variety of plugins which may effect the way things are compiled, reported,
# or may provide new command options. Refer to the readme in each plugin for
# details on how to use it.
:plugins:
:load_paths: []
:enabled:
#- beep # beeps when finished, so you don't waste time waiting for ceedling
- module_generator # handy for quickly creating source, header, and test templates
#- gcov # test coverage using gcov. Requires gcc, gcov, and a coverage analyzer like gcovr
#- bullseye # test coverage using bullseye. Requires bullseye for your platform
#- command_hooks # write custom actions to be called at different points during the build process
#- compile_commands_json_db # generate a compile_commands.json file
#- dependencies # automatically fetch 3rd party libraries, etc.
#- subprojects # managing builds and test for static libraries
#- fake_function_framework # use FFF instead of CMock
# Report options (You'll want to choose one stdout option, but may choose multiple stored options if desired)
#- report_build_warnings_log
#- report_tests_gtestlike_stdout
#- report_tests_ide_stdout
#- report_tests_log_factory
- report_tests_pretty_stdout
#- report_tests_raw_output_log
#- report_tests_teamcity_stdout
# Specify which reports you'd like from the log factory
:report_tests_log_factory:
:reports:
- json
- junit
- cppunit
- html
# override the default extensions for your system and toolchain
:extension:
#:header: .h
#:source: .c
#:assembly: .s
#:dependencies: .d
#:object: .o
:executable: .out
#:testpass: .pass
#:testfail: .fail
#:subprojects: .a
# This is where Ceedling should look for your source and test files.
# see documentation for the many options for specifying this.
:paths:
:test:
- +:test/**
- -:test/support
:source:
- ../../src/**
:include:
- ../../src/**
:support:
- test/support
:libraries: []
# You can even specify specific files to add or remove from your test
# and release collections. Usually it's better to use paths and let
# Ceedling do the work for you!
:files:
:test: []
:source: []
# Compilation symbols to be injected into builds
# See documentation for advanced options:
# - Test name matchers for different symbols per test executable build
# - Referencing symbols in multiple lists using advanced YAML
# - Specifying symbols used during test preprocessing
:defines:
:test:
- _UNITY_TEST_
:release: []
# Enable to inject name of a test as a unique compilation symbol into its respective executable build.
:use_test_definition: FALSE
# Configure additional command line flags provided to tools used in each build step
# :flags:
# :release:
# :compile: # Add '-Wall' and '--02' to compilation of all files in release target
# - -Wall
# - --O2
# :test:
# :compile:
# '(_|-)special': # Add '-pedantic' to compilation of all files in all test executables with '_special' or '-special' in their names
# - -pedantic
# '*': # Add '-foo' to compilation of all files in all test executables
# - -foo
# Configuration Options specific to CMock. See CMock docs for details
:cmock:
# Core configuration
:plugins: # What plugins should be used by CMock?
- :ignore
- :ignore_arg
- :return_thru_ptr
- :callback
- :array
:verbosity: 2 # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
:when_no_prototypes: :warn # the options being :ignore, :warn, or :error
# File configuration
:skeleton_path: '' # Subdirectory to store stubs when generated (default: '')
:mock_prefix: 'mock_' # Prefix to append to filenames for mocks
:mock_suffix: '' # Suffix to append to filenames for mocks
# Parser configuration
:strippables: ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)']
:attributes:
- __ramfunc
- __irq
- __fiq
- register
- extern
:c_calling_conventions:
- __stdcall
- __cdecl
- __fastcall
:treat_externs: :exclude # the options being :include or :exclud
:treat_inlines: :exclude # the options being :include or :exclud
# Type handling configuration
#:unity_helper_path: '' # specify a string of where to find a unity_helper.h file to discover custom type assertions
:treat_as: # optionally add additional types to map custom types
uint8: HEX8
uint16: HEX16
uint32: UINT32
int8: INT8
bool: UINT8
#:treat_as_array: {} # hint to cmock that these types are pointers to something
#:treat_as_void: [] # hint to cmock that these types are actually aliases of void
:memcmp_if_unknown: true # allow cmock to use the memory comparison assertions for unknown types
:when_ptr: :compare_data # hint to cmock how to handle pointers in general, the options being :compare_ptr, :compare_data, or :smart
# Mock generation configuration
:weak: '' # Symbol to use to declare weak functions
:enforce_strict_ordering: true # Do we want cmock to enforce ordering of all function calls?
:fail_on_unexpected_calls: true # Do we want cmock to fail when it encounters a function call that wasn't expected?
:callback_include_count: true # Do we want cmock to include the number of calls to this callback, when using callbacks?
:callback_after_arg_check: false # Do we want cmock to enforce an argument check first when using a callback?
#:includes: [] # You can add additional includes here, or specify the location with the options below
#:includes_h_pre_orig_header: []
#:includes_h_post_orig_header: []
#:includes_c_pre_header: []
#:includes_c_post_header: []
#:array_size_type: [] # Specify a type or types that should be used for array lengths
#:array_size_name: 'size|len' # Specify a name or names that CMock might automatically recognize as the length of an array
:exclude_setjmp_h: false # Don't use setjmp when running CMock. Note that this might result in late reporting or out-of-order failures.
# Configuration options specific to Unity.
:unity:
:defines:
- UNITY_EXCLUDE_FLOAT
# You can optionally have ceedling create environment variables for you before
# performing the rest of its tasks.
:environment: []
# :environment:
# # List enforces order allowing later to reference earlier with inline Ruby substitution
# - :var1: value
# - :var2: another value
# - :path: # Special PATH handling with platform-specific path separators
# - #{ENV['PATH']} # Environment variables can use inline Ruby substitution
# - /another/path/to/include
# LIBRARIES
# These libraries are automatically injected into the build process. Those specified as
# common will be used in all types of builds. Otherwise, libraries can be injected in just
# tests or releases. These options are MERGED with the options in supplemental yaml files.
:libraries:
:placement: :end
:flag: "-l${1}"
:path_flag: "-L ${1}"
:system: [] # for example, you might list 'm' to grab the math library
:test: []
:release: []
################################################################
# PLUGIN CONFIGURATION
################################################################
# Add -gcov to the plugins list to make sure of the gcov plugin
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
:summaries: TRUE # Enable simple coverage summaries to console after tests
:report_task: FALSE # Disabled dedicated report generation task (this enables automatic report generation)
:utilities:
- gcovr # Use gcovr to create the specified reports (default).
#- ReportGenerator # Use ReportGenerator to create the specified reports.
:reports: # Specify one or more reports to generate.
# Make an HTML summary report.
- HtmlBasic
# - HtmlDetailed
# - Text
# - Cobertura
# - SonarQube
# - JSON
# - HtmlInline
# - HtmlInlineAzure
# - HtmlInlineAzureDark
# - HtmlChart
# - MHtml
# - Badges
# - CsvSummary
# - Latex
# - LatexSummary
# - PngChart
# - TeamCitySummary
# - lcov
# - Xml
# - XmlSummary
:gcovr:
# :html_artifact_filename: TestCoverageReport.html
# :html_title: Test Coverage Report
:html_medium_threshold: 75
:html_high_threshold: 90
# :html_absolute_paths: TRUE
# :html_encoding: UTF-8
# :module_generator:
# :project_root: ./
# :source_root: source/
# :inc_root: includes/
# :test_root: tests/
# :naming: :snake #options: :bumpy, :camel, :caps, or :snake
# :includes:
# :tst: []
# :src: []
# :boilerplates:
# :src: ""
# :inc: ""
# :tst: ""
# :dependencies:
# :libraries:
# - :name: WolfSSL
# :source_path: third_party/wolfssl/source
# :build_path: third_party/wolfssl/build
# :artifact_path: third_party/wolfssl/install
# :fetch:
# :method: :zip
# :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip
# :environment:
# - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE
# :build:
# - "autoreconf -i"
# - "./configure --enable-tls13 --enable-singlethreaded"
# - make
# - make install
# :artifacts:
# :static_libraries:
# - lib/wolfssl.a
# :dynamic_libraries:
# - lib/wolfssl.so
# :includes:
# - include/**
# :subprojects:
# :paths:
# - :name: libprojectA
# :source:
# - ./subprojectA/source
# :include:
# - ./subprojectA/include
# :build_root: ./subprojectA/build
# :defines: []
# :command_hooks:
# :pre_mock_preprocess:
# :post_mock_preprocess:
# :pre_test_preprocess:
# :post_test_preprocess:
# :pre_mock_generate:
# :post_mock_generate:
# :pre_runner_generate:
# :post_runner_generate:
# :pre_compile_execute:
# :post_compile_execute:
# :pre_link_execute:
# :post_link_execute:
# :pre_test_fixture_execute:
# :post_test_fixture_execute:
# :pre_test:
# :post_test:
# :pre_release:
# :post_release:
# :pre_build:
# :post_build:
# :post_error:
################################################################
# TOOLCHAIN CONFIGURATION
################################################################
#:tools:
# Ceedling defaults to using gcc for compiling, linking, etc.
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
# See documentation to configure a given toolchain for use
#:tools:
# :test_compiler:
# :executable: gcc
# :name: 'gcc compiler'
# :arguments:
# - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE #expands to -I search paths
# - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR #expands to -I search paths
# - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR #expands to all -D defined symbols
# #- -fsanitize=address
# - -c ${1} #source code input file (Ruby method call param list sub)
# - -o ${2} #object file output (Ruby method call param list sub)
# :test_linker:
# :executable: gcc
# :name: 'gcc linker'
# :arguments:
# #- -fsanitize=address
# - ${1} #list of object files to link (Ruby method call param list sub)
# - -o ${2} #executable file output (Ruby method call param list sub)
# :test_compiler:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_linker:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_assembler:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_fixture:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_includes_preprocessor:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_file_preprocessor:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_file_preprocessor_directives:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :test_dependencies_generator:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :release_compiler:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :release_linker:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :release_assembler:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
# :release_dependencies_generator:
# :executable:
# :arguments: []
# :name:
# :optional: FALSE
...

View File

@@ -0,0 +1,284 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019, hathach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "unity.h"
// Files to test
#include "osal/osal.h"
#include "tusb_fifo.h"
#include "tusb.h"
#include "usbd.h"
TEST_SOURCE_FILE("usbd_control.c")
TEST_SOURCE_FILE("msc_device.c")
// Mock File
#include "mock_dcd.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
uint32_t tusb_time_millis_api(void) {
return 0;
}
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80,
EDPT_MSC_OUT = 0x01,
EDPT_MSC_IN = 0x81,
};
uint8_t const rhport = 0;
enum
{
ITF_NUM_MSC,
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
uint8_t const data_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, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64),
};
tusb_control_request_t const request_set_configuration =
{
.bmRequestType = 0x00,
.bRequest = TUSB_REQ_SET_CONFIGURATION,
.wValue = 1,
.wIndex = 0,
.wLength = 0
};
uint8_t const* desc_configuration;
enum
{
DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount
DISK_BLOCK_SIZE = 512
};
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE];
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
// Some inquiry_resp's fields are already filled with default values, application can update them
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t* inquiry_resp, uint32_t bufsize) {
(void) lun;
(void) bufsize;
const char vid[] = "TinyUSB";
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
memcpy(inquiry_resp->product_id, pid, strlen(pid));
memcpy(inquiry_resp->product_rev, rev, strlen(rev));
return sizeof(scsi_inquiry_resp_t); // 36 bytes
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true; // RAM disk is always ready
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
(void) lun;
(void) power_condition;
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
uint8_t const* addr = msc_disk[lba] + offset;
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
{
(void) lun;
uint8_t* addr = msc_disk[lba] + offset;
memcpy(addr, buffer, bufsize);
return bufsize;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
void const* response = NULL;
uint16_t resplen = 0;
return resplen;
}
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
uint8_t const * tud_descriptor_device_cb(void)
{
return NULL;
}
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
return desc_configuration;
}
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
return NULL;
}
void setUp(void)
{
dcd_int_disable_Ignore();
dcd_int_enable_Ignore();
if ( !tud_inited() ) {
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
dcd_init_ExpectAndReturn(0, &dev_init, true);
tusb_init(0, &dev_init);
}
dcd_event_bus_reset(rhport, TUSB_SPEED_HIGH, false);
tud_task();
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
void test_msc(void)
{
// Read 1 LBA = 0, Block count = 1
msc_cbw_t cbw_read10 =
{
.signature = MSC_CBW_SIGNATURE,
.tag = 0xCAFECAFE,
.total_bytes = 512,
.lun = 0,
.dir = TUSB_DIR_IN_MASK,
.cmd_len = sizeof(scsi_read10_t)
};
scsi_read10_t cmd_read10 =
{
.cmd_code = SCSI_CMD_READ_10,
.lba = tu_htonl(0),
.block_count = tu_htons(1)
};
memcpy(cbw_read10.command, &cmd_read10, cbw_read10.cmd_len);
desc_configuration = data_desc_configuration;
uint8_t const* desc_ep = tu_desc_next(tu_desc_next(desc_configuration));
dcd_event_setup_received(rhport, (uint8_t*) &request_set_configuration, false);
// open endpoints
dcd_edpt_open_ExpectAndReturn(rhport, (tusb_desc_endpoint_t const *) desc_ep, true);
dcd_edpt_open_ExpectAndReturn(rhport, (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep), true);
// Prepare SCSI command
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), true);
dcd_edpt_xfer_IgnoreArg_buffer();
dcd_edpt_xfer_ReturnMemThruPtr_buffer( (uint8_t*) &cbw_read10, sizeof(msc_cbw_t));
// command received
dcd_event_xfer_complete(rhport, EDPT_MSC_OUT, sizeof(msc_cbw_t), 0, true);
// control status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, true);
// SCSI Data transfer
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 512, true);
dcd_edpt_xfer_IgnoreArg_buffer();
dcd_event_xfer_complete(rhport, EDPT_MSC_IN, 512, 0, true); // complete
// SCSI Status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 13, true);
dcd_edpt_xfer_IgnoreArg_buffer();
dcd_event_xfer_complete(rhport, EDPT_MSC_IN, 13, 0, true);
// Prepare for next command
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), true);
dcd_edpt_xfer_IgnoreArg_buffer();
tud_task();
}

View File

@@ -0,0 +1,248 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019, Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "unity.h"
// Files to test
#include "osal/osal.h"
#include "tusb_fifo.h"
#include "tusb.h"
#include "usbd.h"
TEST_SOURCE_FILE("usbd_control.c")
// Mock File
#include "mock_dcd.h"
#include "mock_msc_device.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
uint32_t tusb_time_millis_api(void) {
return 0;
}
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80
};
uint8_t const rhport = 0;
tusb_desc_device_t const data_desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// 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,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = 0xCafe,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
uint8_t const data_desc_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUD_CONFIG_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
tusb_control_request_t const req_get_desc_device =
{
.bmRequestType = 0x80,
.bRequest = TUSB_REQ_GET_DESCRIPTOR,
.wValue = (TUSB_DESC_DEVICE << 8),
.wIndex = 0x0000,
.wLength = 64
};
tusb_control_request_t const req_get_desc_configuration =
{
.bmRequestType = 0x80,
.bRequest = TUSB_REQ_GET_DESCRIPTOR,
.wValue = (TUSB_DESC_CONFIGURATION << 8),
.wIndex = 0x0000,
.wLength = 256
};
uint8_t const* desc_device;
uint8_t const* desc_configuration;
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
uint8_t const * tud_descriptor_device_cb(void) {
return desc_device;
}
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
return desc_configuration;
}
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
return NULL;
}
void setUp(void) {
dcd_int_disable_Ignore();
dcd_int_enable_Ignore();
if ( !tud_inited() ) {
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
mscd_init_Expect();
dcd_init_ExpectAndReturn(0, &dev_init, true);
tusb_init(0, &dev_init);
}
}
void tearDown(void) {
}
//--------------------------------------------------------------------+
// Get Descriptor
//--------------------------------------------------------------------+
//------------- Device -------------//
void test_usbd_get_device_descriptor(void)
{
desc_device = (uint8_t const *) &data_desc_device;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_device, false);
// data
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*)&data_desc_device, sizeof(tusb_desc_device_t), sizeof(tusb_desc_device_t), true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, sizeof(tusb_desc_device_t), 0, false);
// status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false);
dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_device, 1);
tud_task();
}
void test_usbd_get_device_descriptor_null(void)
{
desc_device = NULL;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_device, false);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_IN);
tud_task();
}
//------------- Configuration -------------//
void test_usbd_get_configuration_descriptor(void)
{
desc_configuration = data_desc_configuration;
uint16_t total_len = ((tusb_desc_configuration_t const*) data_desc_configuration)->wTotalLength;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
// data
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*) data_desc_configuration, total_len, total_len, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, total_len, 0, false);
// status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false);
dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_configuration, 1);
tud_task();
}
void test_usbd_get_configuration_descriptor_null(void)
{
desc_configuration = NULL;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_IN);
tud_task();
}
//--------------------------------------------------------------------+
// Control ZLP
//--------------------------------------------------------------------+
void test_usbd_control_in_zlp(void)
{
// 128 byte total len, with EP0 size = 64, and request length = 256
// ZLP must be return
uint8_t zlp_desc_configuration[CFG_TUD_ENDPOINT0_SIZE*2] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, 0, 0, CFG_TUD_ENDPOINT0_SIZE*2, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
desc_configuration = zlp_desc_configuration;
// request, then 1st, 2nd xact + ZLP + status
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
// 1st transaction
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN,
zlp_desc_configuration, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDPOINT0_SIZE, 0, false);
// 2nd transaction
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN,
zlp_desc_configuration + CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDPOINT0_SIZE, 0, false);
// Expect Zero length Packet
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false);
// Status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false);
dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_configuration, 1);
tud_task();
}

View File

@@ -0,0 +1,106 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
// testing framework
#include "unity.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
//#error CFG_TUSB_MCU must be defined
#define CFG_TUSB_MCU OPT_MCU_NRF5X
#endif
#ifndef CFG_TUSB_RHPORT0_MODE
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#endif
#define CFG_TUSB_OS OPT_OS_NONE
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 1
#endif
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_TASK_QUEUE_SZ 100
#define CFG_TUD_ENDPOINT0_SIZE 64
//------------- CLASS -------------//
//#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 1
//#define CFG_TUD_HID 0
//#define CFG_TUD_MIDI 0
//#define CFG_TUD_VENDOR 0
//------------- CDC -------------//
// FIFO size of CDC TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 512
#define CFG_TUD_CDC_TX_BUFSIZE 512
//------------- MSC -------------//
// Buffer size of Device Mass storage
#define CFG_TUD_MSC_BUFSIZE 512
//------------- HID -------------//
// Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 64
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,82 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023, Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include <string.h>
#include "unity.h"
#include "tusb_common.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
//------------- IMPLEMENTATION -------------//
void setUp(void)
{
}
void tearDown(void)
{
}
void test_TU_ARGS_NUM(void)
{
TEST_ASSERT_EQUAL( 0, TU_ARGS_NUM());
TEST_ASSERT_EQUAL( 1, TU_ARGS_NUM(a1));
TEST_ASSERT_EQUAL( 2, TU_ARGS_NUM(a1, a2));
TEST_ASSERT_EQUAL( 3, TU_ARGS_NUM(a1, a2, a3));
TEST_ASSERT_EQUAL( 4, TU_ARGS_NUM(a1, a2, a3, a4));
TEST_ASSERT_EQUAL( 5, TU_ARGS_NUM(a1, a2, a3, a4, a5));
TEST_ASSERT_EQUAL( 6, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6));
TEST_ASSERT_EQUAL( 7, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7));
TEST_ASSERT_EQUAL( 8, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8));
TEST_ASSERT_EQUAL( 9, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9));
TEST_ASSERT_EQUAL(10, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10));
TEST_ASSERT_EQUAL(11, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11));
TEST_ASSERT_EQUAL(12, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12));
TEST_ASSERT_EQUAL(13, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13));
TEST_ASSERT_EQUAL(14, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14));
TEST_ASSERT_EQUAL(15, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15));
TEST_ASSERT_EQUAL(16, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16));
TEST_ASSERT_EQUAL(17, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17));
TEST_ASSERT_EQUAL(18, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18));
TEST_ASSERT_EQUAL(19, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19));
TEST_ASSERT_EQUAL(20, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20));
TEST_ASSERT_EQUAL(21, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21));
TEST_ASSERT_EQUAL(22, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22));
TEST_ASSERT_EQUAL(23, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23));
TEST_ASSERT_EQUAL(24, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24));
TEST_ASSERT_EQUAL(25, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25));
TEST_ASSERT_EQUAL(26, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26));
TEST_ASSERT_EQUAL(27, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27));
TEST_ASSERT_EQUAL(28, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28));
TEST_ASSERT_EQUAL(29, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29));
TEST_ASSERT_EQUAL(30, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30));
TEST_ASSERT_EQUAL(31, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31));
TEST_ASSERT_EQUAL(32, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32));
}

View File

@@ -0,0 +1,378 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include <string.h>
#include "unity.h"
#include "osal/osal.h"
#include "tusb_fifo.h"
#define FIFO_SIZE 64
uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)];
tu_fifo_t tu_ff = TU_FIFO_INIT(tu_ff_buf, FIFO_SIZE, uint8_t, false);
tu_fifo_t* ff = &tu_ff;
tu_fifo_buffer_info_t info;
uint8_t test_data[4096];
uint8_t rd_buf[FIFO_SIZE];
void setUp(void)
{
tu_fifo_clear(ff);
memset(&info, 0, sizeof(tu_fifo_buffer_info_t));
for(int i=0; i<sizeof(test_data); i++) test_data[i] = i;
memset(rd_buf, 0, sizeof(rd_buf));
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
// Tests
//--------------------------------------------------------------------+
void test_normal(void)
{
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i);
for(uint8_t i=0; i < FIFO_SIZE; i++)
{
uint8_t c;
tu_fifo_read(ff, &c);
TEST_ASSERT_EQUAL(i, c);
}
}
void test_item_size(void)
{
uint8_t ff4_buf[FIFO_SIZE * sizeof(uint32_t)];
tu_fifo_t ff4 = TU_FIFO_INIT(ff4_buf, FIFO_SIZE, uint32_t, false);
uint32_t data4[2*FIFO_SIZE];
for(uint32_t i=0; i<sizeof(data4)/4; i++) data4[i] = i;
// fill up fifo
tu_fifo_write_n(&ff4, data4, FIFO_SIZE);
uint32_t rd_buf4[FIFO_SIZE];
uint16_t rd_count;
// read 0 -> 4
rd_count = tu_fifo_read_n(&ff4, rd_buf4, 5);
TEST_ASSERT_EQUAL( 5, rd_count );
TEST_ASSERT_EQUAL_UINT32_ARRAY( data4, rd_buf4, rd_count ); // 0 -> 4
tu_fifo_write_n(&ff4, data4+FIFO_SIZE, 5);
// read all 5 -> 68
rd_count = tu_fifo_read_n(&ff4, rd_buf4, FIFO_SIZE);
TEST_ASSERT_EQUAL( FIFO_SIZE, rd_count );
TEST_ASSERT_EQUAL_UINT32_ARRAY( data4+5, rd_buf4, rd_count ); // 5 -> 68
}
void test_read_n(void)
{
uint16_t rd_count;
// fill up fifo
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, test_data+i);
// case 1: Read index + count < depth
// read 0 -> 4
rd_count = tu_fifo_read_n(ff, rd_buf, 5);
TEST_ASSERT_EQUAL( 5, rd_count );
TEST_ASSERT_EQUAL_MEMORY( test_data, rd_buf, rd_count ); // 0 -> 4
// case 2: Read index + count > depth
// write 10, 11, 12
tu_fifo_write(ff, test_data+FIFO_SIZE);
tu_fifo_write(ff, test_data+FIFO_SIZE+1);
tu_fifo_write(ff, test_data+FIFO_SIZE+2);
rd_count = tu_fifo_read_n(ff, rd_buf, 7);
TEST_ASSERT_EQUAL( 7, rd_count );
TEST_ASSERT_EQUAL_MEMORY( test_data+5, rd_buf, rd_count ); // 5 -> 11
// Should only read until empty
TEST_ASSERT_EQUAL( FIFO_SIZE-5+3-7, tu_fifo_read_n(ff, rd_buf, 100) );
}
void test_write_n(void)
{
// case 1: wr + count < depth
tu_fifo_write_n(ff, test_data, 32); // wr = 32, count = 32
uint16_t rd_count;
rd_count = tu_fifo_read_n(ff, rd_buf, 16); // wr = 32, count = 16
TEST_ASSERT_EQUAL( 16, rd_count );
TEST_ASSERT_EQUAL_MEMORY( test_data, rd_buf, rd_count );
// case 2: wr + count > depth
tu_fifo_write_n(ff, test_data+32, 40); // wr = 72 -> 8, count = 56
tu_fifo_read_n(ff, rd_buf, 32); // count = 24
TEST_ASSERT_EQUAL_MEMORY( test_data+16, rd_buf, rd_count);
TEST_ASSERT_EQUAL(24, tu_fifo_count(ff));
}
void test_write_double_overflowed(void)
{
tu_fifo_set_overwritable(ff, true);
uint8_t rd_buf[FIFO_SIZE] = { 0 };
uint8_t* buf = test_data;
// full
buf += tu_fifo_write_n(ff, buf, FIFO_SIZE);
TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff));
// write more, should still full
buf += tu_fifo_write_n(ff, buf, FIFO_SIZE-8);
TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff));
// double overflowed: in total, write more than > 2*FIFO_SIZE
buf += tu_fifo_write_n(ff, buf, 16);
TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff));
// reading back should give back data from last FIFO_SIZE write
tu_fifo_read_n(ff, rd_buf, FIFO_SIZE);
TEST_ASSERT_EQUAL_MEMORY(buf-16, rd_buf+FIFO_SIZE-16, 16);
// TODO whole buffer should match, but we deliberately not implement it
// TEST_ASSERT_EQUAL_MEMORY(buf-FIFO_SIZE, rd_buf, FIFO_SIZE);
}
static uint16_t help_write(uint16_t total, uint16_t n)
{
tu_fifo_write_n(ff, test_data, n);
total = tu_min16(FIFO_SIZE, total + n);
TEST_ASSERT_EQUAL(total, tu_fifo_count(ff));
TEST_ASSERT_EQUAL(FIFO_SIZE - total, tu_fifo_remaining(ff));
return total;
}
void test_write_overwritable2(void)
{
tu_fifo_set_overwritable(ff, true);
// based on actual crash tests detected by fuzzing
uint16_t total = 0;
total = help_write(total, 12);
total = help_write(total, 55);
total = help_write(total, 73);
total = help_write(total, 55);
total = help_write(total, 75);
total = help_write(total, 84);
total = help_write(total, 1);
total = help_write(total, 10);
total = help_write(total, 12);
total = help_write(total, 25);
total = help_write(total, 192);
}
void test_peek(void)
{
uint8_t temp;
temp = 10; tu_fifo_write(ff, &temp);
temp = 20; tu_fifo_write(ff, &temp);
temp = 30; tu_fifo_write(ff, &temp);
temp = 0;
tu_fifo_peek(ff, &temp);
TEST_ASSERT_EQUAL(10, temp);
tu_fifo_read(ff, &temp);
tu_fifo_read(ff, &temp);
tu_fifo_peek(ff, &temp);
TEST_ASSERT_EQUAL(30, temp);
}
void test_get_read_info_when_no_wrap()
{
uint8_t ch = 1;
// write 6 items
for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch);
// read 2 items
tu_fifo_read(ff, &ch);
tu_fifo_read(ff, &ch);
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(4, info.len_lin);
TEST_ASSERT_EQUAL(0, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info.ptr_lin);
TEST_ASSERT_NULL(info.ptr_wrap);
}
void test_get_read_info_when_wrapped()
{
uint8_t ch = 1;
// make fifo full
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &ch);
// read 6 items
for(uint8_t i=0; i < 6; i++) tu_fifo_read(ff, &ch);
// write 2 items
tu_fifo_write(ff, &ch);
tu_fifo_write(ff, &ch);
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin);
TEST_ASSERT_EQUAL(2, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info.ptr_lin);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap);
}
void test_get_write_info_when_no_wrap()
{
uint8_t ch = 1;
// write 2 items
tu_fifo_write(ff, &ch);
tu_fifo_write(ff, &ch);
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE-2, info.len_lin);
TEST_ASSERT_EQUAL(0, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info .ptr_lin);
// application should check len instead of ptr.
// TEST_ASSERT_NULL(info.ptr_wrap);
}
void test_get_write_info_when_wrapped()
{
uint8_t ch = 1;
// write 6 items
for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch);
// read 2 items
tu_fifo_read(ff, &ch);
tu_fifo_read(ff, &ch);
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin);
TEST_ASSERT_EQUAL(2, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info .ptr_lin);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap);
}
void test_empty(void)
{
uint8_t temp;
TEST_ASSERT_TRUE(tu_fifo_empty(ff));
// read info
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(0, info.len_lin);
TEST_ASSERT_EQUAL(0, info.len_wrap);
TEST_ASSERT_NULL(info.ptr_lin);
TEST_ASSERT_NULL(info.ptr_wrap);
// write info
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin);
TEST_ASSERT_EQUAL(0, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info .ptr_lin);
// application should check len instead of ptr.
// TEST_ASSERT_NULL(info.ptr_wrap);
// write 1 then re-check empty
tu_fifo_write(ff, &temp);
TEST_ASSERT_FALSE(tu_fifo_empty(ff));
}
void test_full(void)
{
TEST_ASSERT_FALSE(tu_fifo_full(ff));
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i);
TEST_ASSERT_TRUE(tu_fifo_full(ff));
// read info
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin);
TEST_ASSERT_EQUAL(0, info.len_wrap);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_lin);
// skip this, application must check len instead of buffer
// TEST_ASSERT_NULL(info.ptr_wrap);
// write info
}
void test_rd_idx_wrap()
{
tu_fifo_t ff10;
uint8_t buf[10];
uint8_t dst[10];
tu_fifo_config(&ff10, buf, 10, 1, 1);
uint16_t n;
ff10.wr_idx = 6;
ff10.rd_idx = 15;
n = tu_fifo_read_n(&ff10, dst, 4);
TEST_ASSERT_EQUAL(n, 4);
TEST_ASSERT_EQUAL(ff10.rd_idx, 0);
n = tu_fifo_read_n(&ff10, dst, 4);
TEST_ASSERT_EQUAL(n, 4);
TEST_ASSERT_EQUAL(ff10.rd_idx, 4);
n = tu_fifo_read_n(&ff10, dst, 4);
TEST_ASSERT_EQUAL(n, 2);
TEST_ASSERT_EQUAL(ff10.rd_idx, 6);
}