initial commit

This commit is contained in:
shell committed 2026-10-11 21:48:59 +08:00
commit b44ce7bd77
2716 files changed
+559005

No files matched your search

+218
View File
@@ -0,0 +1,218 @@
/*
* 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()) {
// Only generate bus signal events that don't carry additional union data.
// DCD_EVENT_XFER_COMPLETE, DCD_EVENT_SOF, and DCD_EVENT_BUS_RESET need
// properly initialized union fields; USBD_EVENT_FUNC_CALL is internal only.
// Valid bus-signal-only events: UNPLUGGED(2), SUSPEND(4), RESUME(5).
static const dcd_eventid_t bus_signal_events[] = {
DCD_EVENT_UNPLUGGED, DCD_EVENT_SUSPEND, DCD_EVENT_RESUME};
uint8_t idx = _fuzz_data_provider->ConsumeIntegralInRange<uint8_t>(0, 2);
dcd_event_bus_signal(rhport, bus_signal_events[idx],
_fuzz_data_provider->ConsumeBool());
}
// Optionally generate a BUS_RESET event with a valid speed value.
if (_fuzz_data_provider->ConsumeBool()) {
tusb_speed_t speed = (tusb_speed_t)_fuzz_data_provider->ConsumeIntegralInRange<uint8_t>(
TUSB_SPEED_FULL, TUSB_SPEED_HIGH);
dcd_event_bus_reset(rhport, speed, _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, false);
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, bool is_isr) {
UNUSED(rhport);
UNUSED(buffer);
UNUSED(total_bytes);
UNUSED(is_isr);
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;
}
}
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(cdc)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} 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_NAME} 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_NAME} noos)
+11
View File
@@ -0,0 +1,11 @@
include ../../make.mk
INC += \
src \
# Example source
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(EXAMPLE_PATH)/, $(wildcard src/*.cc))
include ../../rules.mk
Binary file not shown.
+178
View File
@@ -0,0 +1,178 @@
/*
* 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
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
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;
}
}
}
@@ -0,0 +1,116 @@
/*
* 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, default to max bulk packet size (HS 512, FS 64). Larger is faster.
// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_EPSIZE (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_ */
@@ -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;
}
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(msc)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} 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_NAME} 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_NAME} noos)
+11
View File
@@ -0,0 +1,11 @@
include ../../make.mk
INC += \
src \
# Example source
SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(EXAMPLE_PATH)/, $(wildcard src/*.cc))
include ../../rules.mk
Binary file not shown.
+64
View File
@@ -0,0 +1,64 @@
/*
* 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());
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
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;
}
@@ -0,0 +1,116 @@
/*
* 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, default to max bulk packet size (HS 512, FS 64). Larger is faster.
// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_EPSIZE (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_ */
@@ -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;
}
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.5)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
project(net)
# Checks this example is valid for the family and initializes the project
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
add_executable(${PROJECT_NAME})
# Example source
target_sources(${PROJECT_NAME} 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_NAME} 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_NAME} noos)
+67
View File
@@ -0,0 +1,67 @@
include ../../make.mk
# suppress warning caused by lwip
CFLAGS += \
-Wno-error=null-dereference \
-Wno-error=unused-parameter \
-Wno-error=unused-variable
INC += \
src \
$(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 $(EXAMPLE_PATH)/, $(wildcard src/*.c))
SRC_CXX += $(addprefix $(EXAMPLE_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
+75
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__ */
+103
View File
@@ -0,0 +1,103 @@
/*
* 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
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
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;
}
}
}
+71
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__ */
@@ -0,0 +1,124 @@
/*
* 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, default to max bulk packet size (HS 512, FS 64). Larger is faster.
// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_EPSIZE (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_ */
@@ -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;
}
+73
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"
+34
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;
}
+37
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
+30
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;
+128
View File
@@ -0,0 +1,128 @@
# ---------------------------------------
# Common make definition for all examples
# ---------------------------------------
#-------------- TOP and EXAMPLE_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 EXAMPLE_PATH to the relative path from TOP to the current directory, ie examples/device/cdc_msc_freertos
EXAMPLE_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
+162
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
+74
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
+160
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/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/printer/printer_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 $* = $($*)
+73
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) {}
*/
}
+44
View File
@@ -0,0 +1,44 @@
{
"boards": [
{
"name": "stm32l412nucleo",
"uid": "41003B000E504E5457323020",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "774470029",
"args": "-device STM32L412KB"
}
},
{
"name": "stm32f746disco",
"uid": "210041000C51343237303334",
"variant": [
{ "name": "stm32f746disco", "flags": "" },
{ "name": "stm32f746disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"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"
}
}
]
}
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env bash
# Run HIL test remotely on ci.lan
# Usage: test/hil/hil_ci.sh [-b BOARD] [-t TEST] [extra hil_test.py args...]
# Example:
# test/hil/hil_ci.sh -b stm32f723disco
# test/hil/hil_ci.sh -b stm32f723disco -t host/cdc_msc_hid -r 1
#
# Env overrides: REMOTE, REMOTE_DIR, CONFIG (path to HIL config json),
# ROOT_DIR (tinyusb checkout to test; defaults to the script's own checkout).
set -euo pipefail
REMOTE=${REMOTE:-ci.lan}
REMOTE_DIR=${REMOTE_DIR:-/tmp/tinyusb-hil}
ROOT_DIR=${ROOT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}
CONFIG=${CONFIG:-$ROOT_DIR/test/hil/tinyusb.json}
[[ -f "$ROOT_DIR/test/hil/hil_test.py" && -d "$ROOT_DIR/examples" ]] || {
echo "error: $ROOT_DIR does not look like a tinyusb checkout" >&2
exit 1
}
# Parse -b BOARD from arguments to know which build to copy
BOARD=""
ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
-b)
[[ $# -ge 2 ]] || { echo "error: -b requires a BOARD argument" >&2; exit 1; }
BOARD="$2"
ARGS+=("$1" "$2")
shift 2
;;
*)
ARGS+=("$1")
shift
;;
esac
done
# Setup remote directory. Use `bash -s` + heredoc so REMOTE_DIR (user-overridable)
# is passed as a positional parameter and never reinterpreted by the remote shell.
echo "==> Setting up remote $REMOTE:$REMOTE_DIR"
ssh "$REMOTE" bash -s -- "$REMOTE_DIR" <<'REMOTE'
set -e
rm -rf -- "$1"
mkdir -p -- "$1/test/hil" "$1/examples"
REMOTE
# Copy HIL test script and config
echo "==> Copying test scripts"
scp -q "$ROOT_DIR/test/hil/hil_test.py" \
"$ROOT_DIR/test/hil/pymtp.py" \
"$CONFIG" \
"$REMOTE:$REMOTE_DIR/test/hil/"
# Copy only firmware binaries (elf/bin/hex) plus esptool metadata
# (config.env + flash_args needed by the esptool flasher), preserving structure
copy_board_binaries() {
local src="$1"
rsync -a --prune-empty-dirs \
--include='*/' --include='*.elf' --include='*.bin' --include='*.hex' \
--include='config.env' --include='flash_args' \
--exclude='*' \
"$src" "$REMOTE:$REMOTE_DIR/examples/"
}
if [ -n "$BOARD" ]; then
# Copy the board's build dir plus its variant dirs. Variant names come from
# $CONFIG (they are not required to be prefixed with the board name); the
# cmake-build-<BOARD>-* glob is kept as a fallback for ad-hoc local builds.
# Collect only dirs that actually exist, deduplicated.
declare -A SEEN_DIRS=()
BUILD_DIRS=()
add_build_dir() {
[[ -d "$1" && -z "${SEEN_DIRS[$1]:-}" ]] || return 0
SEEN_DIRS[$1]=1
BUILD_DIRS+=("$1")
}
shopt -s nullglob
for d in "$ROOT_DIR"/examples/cmake-build-"$BOARD" "$ROOT_DIR"/examples/cmake-build-"$BOARD"-*; do
add_build_dir "$d"
done
shopt -u nullglob
while IFS= read -r v; do
add_build_dir "$ROOT_DIR/examples/cmake-build-$v"
done < <(python3 -c '
import json, sys
cfg = json.load(open(sys.argv[1]))
for b in cfg.get("boards", []):
if b["name"] == sys.argv[2]:
for v in b.get("variant") or []:
print(v["name"])
' "$CONFIG" "$BOARD")
if [ ${#BUILD_DIRS[@]} -eq 0 ]; then
echo "Error: no build directory found for $BOARD under $ROOT_DIR/examples/"
echo "Build first with: cd examples && cmake --preset $BOARD && cmake --build --preset $BOARD"
exit 1
fi
echo "==> Copying binaries for $BOARD (${#BUILD_DIRS[@]} build dir(s))"
for d in "${BUILD_DIRS[@]}"; do
copy_board_binaries "$d"
done
else
echo "==> Copying all built binaries"
# Use `%/` parameter expansion to strip the trailing slash from the glob —
# rsync needs the bare dir name so the per-board cmake-build-<BOARD>/ subdir
# is preserved on the remote (hil_test.py looks up binaries by that path).
for dir in "$ROOT_DIR"/examples/cmake-build-*/; do
[ -d "$dir" ] && copy_board_binaries "${dir%/}"
done
fi
# Run test. Use `bash -s` so REMOTE_DIR + ARGS reach the remote shell as positional
# parameters; quoting and metacharacters in args are preserved.
CONFIG_BASENAME="$(basename "$CONFIG")"
echo "==> Running HIL test on $REMOTE"
rc=0
ssh "$REMOTE" bash -s -- "$REMOTE_DIR" "${ARGS[@]}" "test/hil/$CONFIG_BASENAME" <<'REMOTE' || rc=$?
cd -- "$1"
shift
# Flasher CLIs live in the user bin dirs on ci.lan (esptool/idf in ~/.local/bin,
# STM32CubeProgrammer's STM32_Programmer_CLI in ~/bin); the non-interactive shell
# subprocess used for flashing doesn't source profile/rc, so add them explicitly.
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
python3 -u test/hil/hil_test.py -B examples "$@"
REMOTE
# Copy the generated report back to the local checkout (best-effort; the run's
# exit code is preserved regardless of whether a report was produced).
scp -q "$REMOTE:$REMOTE_DIR/hil_report.md" "$ROOT_DIR/hil_report.md" \
&& echo "==> Report copied to $ROOT_DIR/hil_report.md" \
|| echo "==> warning: no hil_report.md copied back" >&2
exit $rc
+75
View File
@@ -0,0 +1,75 @@
import argparse
import json
import os
def _resolve_config_path(config_file):
if os.path.exists(config_file):
return config_file
script_relative = os.path.join(os.path.dirname(__file__), config_file)
if os.path.exists(script_relative):
return script_relative
raise FileNotFoundError(f'Config file not found: {config_file}')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('config_files', nargs='+', help='Configuration JSON file(s)')
args = parser.parse_args()
# Toolchain buckets must match the toolchains instantiated by the hil-build
# job in .github/workflows/build.yml. Keep all keys present (even if empty)
# so `fromJSON(hil_json)[toolchain]` always resolves to a list.
matrix = {
'arm-gcc': [],
'riscv-gcc': [],
'esp-idf': []
}
seen = {toolchain: set() for toolchain in matrix}
def append_build_arg(toolchain, build_arg):
if build_arg not in seen[toolchain]:
seen[toolchain].add(build_arg)
matrix[toolchain].append(build_arg)
for config_file in args.config_files:
with open(_resolve_config_path(config_file)) as f:
config = json.load(f)
for board in config['boards']:
name = board['name']
flasher = board['flasher']
# esptool boards must build under esp-idf; others default to arm-gcc
# but may opt into another bucket via an explicit "toolchain" field
# (e.g. RISC-V boards like ch32v20x need "riscv-gcc").
if flasher['name'] == 'esptool':
toolchain = 'esp-idf'
else:
toolchain = board.get('toolchain', 'arm-gcc')
build_board = f'-b {name}'
if 'build' in board and 'args' in board['build']:
build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
# Each variant builds into cmake-build-<variant.name> with its own cmake
# -D defines and raw CFLAGS. No 'variant' -> a single build named after
# the board.
variants = board.get('variant') or [{'name': name, 'flags': ''}]
for v in variants:
arg = build_board
if v['name'] != name:
arg += f' --build-name {v["name"]}'
for d in v.get('defines', []):
arg += f' -D{d}'
for tok in v.get('flags', '').split():
arg += f' --cflag={tok}'
append_build_arg(toolchain, arg)
print(json.dumps(matrix))
if __name__ == '__main__':
main()
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
# System packages (install separately):
# sudo apt install mtools libmtp9 alsa-utils iperf
# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun)
# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64
# alsa-utils - arecord (device/audio_test_freertos)
# iperf - throughput tests (device/net_lwip_*)
hidapi
pyserial
esptool
+533
View File
@@ -0,0 +1,533 @@
{
"boards": [
{
"name": "ek_tm4c123gxl",
"uid": "010105186C60A110",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "lm4flash",
"uid": "0E205D19",
"args": "-v"
}
},
{
"name": "espressif_p4_function_ev",
"uid": "6055F9F98715",
"variant": [
{ "name": "espressif_p4_function_ev", "flags": "" },
{ "name": "espressif_p4_function_ev-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"only": [
"device/cdc_msc_freertos",
"device/hid_composite_freertos",
"device/audio_test_freertos",
"host/device_info",
"host/msc_file_explorer_freertos"
],
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2002427",
"is_cdc": true
},
{
"vid_pid": "21c4_0cc7",
"serial": "900058944CB80A53",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"flasher": {
"name": "esptool",
"uid": "4ea4f48f6bc3ee11bbb9d00f9e1b1c54",
"args": "-b 1500000",
"comment": "use --force for ESP32-P4 v0.1"
},
"comment": "Use TS3USB30 mux to test both device and host"
},
{
"name": "espressif_s3_devkitm",
"uid": "84F703C084E4",
"variant": [
{ "name": "espressif_s3_devkitm", "flags": "" },
{ "name": "espressif_s3_devkitm-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"only": [
"device/cdc_msc_freertos",
"device/hid_composite_freertos",
"device/audio_test_freertos",
"host/device_info",
"host/msc_file_explorer_freertos"
],
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2005402",
"is_cdc": true
},
{
"vid_pid": "048d_04d2",
"serial": "\u0409",
"is_msc": true,
"block_size": 512,
"block_count": 30720000,
"msc_inquiry": "General UDisk 5.00",
"comment": "General UDisk reports iSerialNumber=U+0409"
}
]
},
"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": "681295394",
"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,
"skip": ["device/audio_test_freertos"],
"dev_attached": [
{
"vid_pid": "067b_2303",
"serial": "0",
"is_cdc": true
}
],
"comment": "pl23x; audio_test_freertos skipped: samd51 iso-IN capture fails (arecord EIO)"
},
"flasher": {
"name": "jlink",
"uid": "123456",
"args": "-device ATSAMD51J19"
}
},
{
"name": "mimxrt1015_evk",
"uid": "DC28F865D2111D228D00B0543A70463C",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000726284213",
"args": "-device MIMXRT1015DAF5A"
}
},
{
"name": "mimxrt1064_evk",
"uid": "BAE96FB95AFA6DBB8F00005002001200",
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "10c4_ea60",
"serial": "0001",
"is_cdc": true,
"comment": "cp2102"
},
{
"vid_pid": "21c4_0cc7",
"serial": "900058874D871F66",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"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
},
"flasher": {
"name": "jlink",
"uid": "000831174392",
"args": "-device R7FA4M1AB"
}
},
{
"name": "raspberry_pi_pico",
"uid": "E6614C311B764A37",
"variant": [
{ "name": "raspberry_pi_pico", "flags": "-DCFG_TUH_RPI_PIO_USB=1" }
],
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "1a86_7523",
"serial": "0",
"is_cdc": true,
"comment": "ch34x"
},
{
"vid_pid": "048d_04d2",
"serial": "\u0409",
"is_msc": true,
"block_size": 512,
"block_count": 30720000,
"msc_inquiry": "General UDisk 5.00",
"comment": "General UDisk reports iSerialNumber=U+0409"
}
]
},
"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": "E6614864D35DAE36",
"tests": {
"device": false,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2002694",
"is_cdc": true
},
{
"vid_pid": "2008_2018",
"serial": "O20070925A002746",
"is_msc": true,
"block_size": 512,
"block_count": 4124152,
"msc_inquiry": "USB2.0 Flash Disk 2.10"
}
]
},
"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",
"tests": {
"device": false,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "0951_1603",
"serial": "820000000000000045B46338",
"is_msc": true,
"block_size": 512,
"block_count": 3987456,
"msc_inquiry": "Kingston DataTraveler 2.0 1.00"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E6633861A3978538",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
{
"name": "adafruit_fruit_jam",
"uid": "2B0DC7A45781189E",
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "0403_6001",
"serial": "0",
"is_cdc": true
},
{
"vid_pid": "058f_6387",
"serial": "A8BEE062633D",
"is_msc": true,
"block_size": 512,
"block_count": 7639040,
"msc_inquiry": "Generic Flash Disk 8.07"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E663AC91D3359B38",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
{
"name": "stm32f072disco",
"uid": "3A001A001357364230353532",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "779541626",
"args": "-device stm32f072rb"
},
"comment": "2x16 access scheme with 1KB USB SRAM"
},
{
"name": "stm32f407disco",
"uid": "30001A000647313332353735",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000773661813",
"args": "-device stm32f407vg"
}
},
{
"name": "stm32f723disco",
"uid": "460029001951373031313335",
"variant": [
{ "name": "stm32f723disco", "flags": "" },
{ "name": "stm32f723disco-DMA", "flags": "-DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2003414",
"is_cdc": true
},
{
"vid_pid": "21c4_0cc7",
"serial": "90005893730A1A63",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"flasher": {
"name": "jlink",
"uid": "000776606156",
"args": "-device stm32f723ie"
},
"comment": "Device port0 FS (slave only), Host port1 HS with DMA"
},
{
"name": "stm32h743nucleo",
"uid": "110018000951383432343236",
"variant": [
{ "name": "stm32h743nucleo", "flags": "" },
{ "name": "stm32h743nucleo-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"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"
},
"comment": "32-bit scheme, 2KB USB SRAM"
},
{
"name": "stm32l476disco",
"uid": "3C0050001150334258343920",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "777632258",
"args": "-device STM32L476VG"
}
},
{
"name": "stm32u083nucleo",
"uid": "300044000D5036394E373620",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "stlink",
"uid": "0668FF575457657187061314"
}
},
{
"name": "nanoch32v203",
"uid": "CDAB277B0FBC03E339E339E3",
"toolchain": "riscv-gcc",
"variant": [
{"name": "nanoch32v203-fsdev", "defines": ["RHPORT_DEVICE=0"]},
{"name": "nanoch32v203-usbfs", "defines": ["RHPORT_DEVICE=1"]}
],
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "EBCA8F0670AF",
"args": ""
}
},
{
"name": "ch32v103r_r1_1v0",
"uid": "CDAB3E8749BC54EF0F410025",
"toolchain": "riscv-gcc",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "BC4954081051",
"args": ""
}
},
{
"name": "ch582m_evt",
"uid": "D443627B5450",
"toolchain": "riscv-gcc",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "7FD88F0604B5",
"args": ""
}
}
],
"boards-skip": [
{
"name": "stm32f769disco",
"uid": "21002F000F51363531383437",
"variant": [
{ "name": "stm32f769disco", "flags": "" },
{ "name": "stm32f769disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000778170924",
"args": "-device stm32f769ni"
}
}
]
}
+131
View File
@@ -0,0 +1,131 @@
cmake_minimum_required(VERSION 3.20)
project(tinyusb_unit_tests LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
# Command to invoke Ceedling. Supports multi-word commands such as "bundle exec ceedling".
set(CEEDLING_COMMAND "ceedling" CACHE STRING "Command used to invoke Ceedling (Ruby gem).")
separate_arguments(CEEDLING_COMMAND_LIST NATIVE_COMMAND "${CEEDLING_COMMAND}")
if (CEEDLING_COMMAND_LIST STREQUAL "")
message(FATAL_ERROR "CEEDLING_COMMAND is empty; set it to a valid Ceedling invocation.")
endif ()
list(GET CEEDLING_COMMAND_LIST 0 CEEDLING_LAUNCHER)
find_program(CEEDLING_LAUNCHER_PATH NAMES ${CEEDLING_LAUNCHER})
if (NOT CEEDLING_LAUNCHER_PATH)
message(FATAL_ERROR "Could not find '${CEEDLING_LAUNCHER}' on PATH; adjust CEEDLING_COMMAND or PATH.")
endif ()
list(REMOVE_AT CEEDLING_COMMAND_LIST 0)
list(INSERT CEEDLING_COMMAND_LIST 0 ${CEEDLING_LAUNCHER_PATH})
set(CEEDLING_WORKDIR ${CMAKE_CURRENT_LIST_DIR})
set(CEEDLING_BUILD_DIR ${CEEDLING_WORKDIR}/_build)
# Helper to add a Ceedling-backed test target that compiles into a real CMake executable.
function(add_ceedling_test TARGET_NAME TEST_SOURCE PRODUCT_SOURCES MOCK_SOURCES)
set(runner ${CEEDLING_BUILD_DIR}/test/runners/${TARGET_NAME}_runner.c)
add_custom_target(ceedling_gen_${TARGET_NAME}
COMMAND ${CEEDLING_COMMAND_LIST} test:${TARGET_NAME}
WORKING_DIRECTORY ${CEEDLING_WORKDIR}
BYPRODUCTS ${runner}
USES_TERMINAL
COMMENT "Generate Ceedling runner/mocks for ${TARGET_NAME}"
)
add_executable(${TARGET_NAME}
${TEST_SOURCE}
${runner}
${MOCK_SOURCES}
${CEEDLING_BUILD_DIR}/vendor/unity/src/unity.c
${CEEDLING_BUILD_DIR}/vendor/cmock/src/cmock.c
${PRODUCT_SOURCES}
)
set_source_files_properties(
${runner}
${MOCK_SOURCES}
${CEEDLING_BUILD_DIR}/vendor/unity/src/unity.c
${CEEDLING_BUILD_DIR}/vendor/cmock/src/cmock.c
PROPERTIES GENERATED TRUE
)
add_dependencies(${TARGET_NAME} ceedling_gen_${TARGET_NAME})
target_include_directories(${TARGET_NAME} PRIVATE
${CEEDLING_WORKDIR}/test
${CEEDLING_WORKDIR}/test/support
${CEEDLING_BUILD_DIR}/test/runners
${CEEDLING_BUILD_DIR}/test/mocks/${TARGET_NAME}
${CEEDLING_BUILD_DIR}/vendor/unity/src
${CEEDLING_BUILD_DIR}/vendor/cmock/src
${CEEDLING_WORKDIR}/../../src
${CEEDLING_WORKDIR}/../../src/common
${CEEDLING_WORKDIR}/../../src/device
${CEEDLING_WORKDIR}/../../src/class
${CEEDLING_WORKDIR}/../../src/class/msc
${CEEDLING_WORKDIR}/../../src/host
${CEEDLING_WORKDIR}/../../src/typec
${CEEDLING_WORKDIR}/../../src/osal
)
target_compile_definitions(${TARGET_NAME} PRIVATE _UNITY_TEST_)
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra)
add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME})
endfunction()
# Custom targets to keep plain Ceedling entry-points available.
add_custom_target(ceedling_all
COMMAND ${CEEDLING_COMMAND_LIST} test:all
WORKING_DIRECTORY ${CEEDLING_WORKDIR}
USES_TERMINAL
COMMENT "Run Ceedling (Unity) unit tests"
)
add_custom_target(ceedling_clean
COMMAND ${CEEDLING_COMMAND_LIST} clean
WORKING_DIRECTORY ${CEEDLING_WORKDIR}
USES_TERMINAL
COMMENT "Clean Ceedling build outputs"
)
add_custom_target(ceedling_clobber
COMMAND ${CEEDLING_COMMAND_LIST} clobber
WORKING_DIRECTORY ${CEEDLING_WORKDIR}
USES_TERMINAL
COMMENT "Clobber Ceedling build outputs"
)
# Per-test wiring: mocks are generated under _build/test/mocks/<name>/.
add_ceedling_test(
test_common_func
${CEEDLING_WORKDIR}/test/test_common_func.c
""
""
)
add_ceedling_test(
test_fifo
${CEEDLING_WORKDIR}/test/test_fifo.c
${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c
""
)
add_ceedling_test(
test_usbd
${CEEDLING_WORKDIR}/test/device/usbd/test_usbd.c
"${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c"
"${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_dcd.c;${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_msc_device.c"
)
add_ceedling_test(
test_msc_device
${CEEDLING_WORKDIR}/test/device/msc/test_msc_device.c
"${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c"
"${CEEDLING_BUILD_DIR}/test/mocks/test_msc_device/mock_dcd.c"
)
enable_testing()
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
ruby vendor/ceedling/bin/ceedling $*
+440
View File
@@ -0,0 +1,440 @@
# =========================================================================
# 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_
- CFG_TUD_EDPT_DEDICATED_HWFIFO=1
- CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE=6
- CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE=0
: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
...
@@ -0,0 +1,266 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2026 Saulo Verissimo
*
* 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"
#include "tusb_types.h"
#include "class/audio/audio.h"
#include "class/midi/midi.h"
#include "device/usbd.h"
void setUp(void) {}
void tearDown(void) {}
//--------------------------------------------------------------------+
// UMP Word Count: all 16 message types
//--------------------------------------------------------------------+
void test_ump_word_count_1word_types(void) {
uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7};
for (int i = 0; i < 5; i++) {
TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i]));
}
}
void test_ump_word_count_2word_types(void) {
uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA};
for (int i = 0; i < 5; i++) {
TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i]));
}
}
void test_ump_word_count_3word_types(void) {
TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xB));
TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xC));
}
void test_ump_word_count_4word_types(void) {
uint8_t types[] = {0x5, 0xD, 0xE, 0xF};
for (int i = 0; i < 4; i++) {
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i]));
}
}
void test_ump_word_count_covers_all_16(void) {
for (uint8_t mt = 0; mt <= 0xF; mt++) {
uint8_t wc = midi2_ump_word_count(mt);
TEST_ASSERT_TRUE(wc >= 1 && wc <= 4);
}
}
//--------------------------------------------------------------------+
// CS Endpoint subtypes (defined in midi.h)
//--------------------------------------------------------------------+
void test_cs_endpoint_subtypes(void) {
TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL);
TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0);
}
//--------------------------------------------------------------------+
// Descriptor macro length calculations
//--------------------------------------------------------------------+
void test_midi1_desc_len(void) {
TEST_ASSERT_EQUAL(TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2,
TUD_MIDI_DESC_LEN);
}
void test_midi2_alt1_head_len(void) {
TEST_ASSERT_EQUAL(16, TUD_MIDI2_DESC_ALT1_HEAD_LEN);
}
void test_midi2_alt1_ep_len(void) {
// EP(7) + CS base(4) + numgtbs
TEST_ASSERT_EQUAL(12, TUD_MIDI2_DESC_ALT1_EP_LEN(1));
TEST_ASSERT_EQUAL(13, TUD_MIDI2_DESC_ALT1_EP_LEN(2));
TEST_ASSERT_EQUAL(18, TUD_MIDI2_DESC_ALT1_EP_LEN(7));
}
void test_midi2_desc_len(void) {
int expected = TUD_MIDI_DESC_LEN + TUD_MIDI2_DESC_ALT1_HEAD_LEN + TUD_MIDI2_DESC_ALT1_EP_LEN(1) * 2;
TEST_ASSERT_EQUAL(expected, TUD_MIDI2_DESC_LEN);
}
void test_midi2_desc_len_greater_than_midi1(void) {
TEST_ASSERT_TRUE(TUD_MIDI2_DESC_LEN > TUD_MIDI_DESC_LEN);
}
//--------------------------------------------------------------------+
// Descriptor macro byte validation
//--------------------------------------------------------------------+
void test_midi2_descriptor_bytes(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
TEST_ASSERT_EQUAL(TUD_MIDI2_DESC_LEN, sizeof(desc));
// First byte: Audio Control Interface descriptor length = 9
TEST_ASSERT_EQUAL(9, desc[0]);
TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[1]);
TEST_ASSERT_EQUAL(0, desc[2]);
// Find Alt Setting 1 by scanning
int alt1_offset = -1;
int pos = 0;
while (pos < (int)sizeof(desc)) {
if (desc[pos + 1] == TUSB_DESC_INTERFACE && desc[pos + 3] == 1) {
alt1_offset = pos;
break;
}
pos += desc[pos];
}
TEST_ASSERT_TRUE_MESSAGE(alt1_offset >= 0, "Alt Setting 1 interface not found");
TEST_ASSERT_EQUAL(9, desc[alt1_offset]);
TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[alt1_offset + 1]);
TEST_ASSERT_EQUAL(1, desc[alt1_offset + 2]); // bInterfaceNumber
TEST_ASSERT_EQUAL(1, desc[alt1_offset + 3]); // bAlternateSetting
TEST_ASSERT_EQUAL(2, desc[alt1_offset + 4]); // bNumEndpoints
TEST_ASSERT_EQUAL(TUSB_CLASS_AUDIO, desc[alt1_offset + 5]);
// MS Header after Alt Setting 1 interface: bcdMSC = 0x0200
int ms2_offset = alt1_offset + 9;
TEST_ASSERT_EQUAL(7, desc[ms2_offset]);
TEST_ASSERT_EQUAL(TUSB_DESC_CS_INTERFACE, desc[ms2_offset + 1]);
TEST_ASSERT_EQUAL(MIDI_CS_INTERFACE_HEADER, desc[ms2_offset + 2]);
TEST_ASSERT_EQUAL(0x00, desc[ms2_offset + 3]);
TEST_ASSERT_EQUAL(0x02, desc[ms2_offset + 4]);
// USB-MIDI 2.0 Table 5-2: wTotalLength shall match bLength (= 0x0007)
TEST_ASSERT_EQUAL(0x07, desc[ms2_offset + 5]);
TEST_ASSERT_EQUAL(0x00, desc[ms2_offset + 6]);
}
void test_midi2_descriptor_alt1_cs_endpoint_subtype(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
int cs_ep_count = 0;
int pos = 0;
while (pos < (int)sizeof(desc)) {
if (desc[pos + 1] == TUSB_DESC_CS_ENDPOINT &&
desc[pos + 2] == MIDI_CS_ENDPOINT_GENERAL_2_0) {
cs_ep_count++;
TEST_ASSERT_EQUAL(1, desc[pos + 3]);
}
pos += desc[pos];
}
TEST_ASSERT_EQUAL(2, cs_ep_count);
}
void test_midi2_descriptor_has_both_alt_settings(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
int alt0_count = 0;
int alt1_count = 0;
int pos = 0;
while (pos < (int)sizeof(desc)) {
if (desc[pos + 1] == TUSB_DESC_INTERFACE) {
if (desc[pos + 3] == 0) alt0_count++;
if (desc[pos + 3] == 1) alt1_count++;
}
pos += desc[pos];
}
TEST_ASSERT_TRUE(alt0_count >= 2);
TEST_ASSERT_EQUAL(1, alt1_count);
}
void test_midi2_descriptor_endpoint_addresses(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x02, 0x82, 64) };
int ep_out_count = 0;
int ep_in_count = 0;
int pos = 0;
while (pos < (int)sizeof(desc)) {
if (desc[pos + 1] == TUSB_DESC_ENDPOINT) {
uint8_t ep_addr = desc[pos + 2];
if (ep_addr == 0x02) ep_out_count++;
if (ep_addr == 0x82) ep_in_count++;
TEST_ASSERT_EQUAL(TUSB_XFER_BULK, desc[pos + 3]);
TEST_ASSERT_EQUAL(64, desc[pos + 4]);
TEST_ASSERT_EQUAL(0, desc[pos + 5]);
}
pos += desc[pos];
}
TEST_ASSERT_EQUAL(2, ep_out_count);
TEST_ASSERT_EQUAL(2, ep_in_count);
}
void test_midi2_descriptor_nonzero_itfnum(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(2, 0, 0x03, 0x83, 64) };
TEST_ASSERT_EQUAL(2, desc[2]);
int pos = desc[0];
while (pos < (int)sizeof(desc)) {
if (desc[pos + 1] == TUSB_DESC_INTERFACE) {
TEST_ASSERT_EQUAL(3, desc[pos + 2]);
break;
}
pos += desc[pos];
}
}
//--------------------------------------------------------------------+
// Descriptor traversal integrity
//--------------------------------------------------------------------+
void test_midi2_descriptor_no_zero_length(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
int pos = 0;
int desc_count = 0;
while (pos < (int)sizeof(desc)) {
TEST_ASSERT_TRUE_MESSAGE(desc[pos] > 0, "Zero-length descriptor found");
TEST_ASSERT_TRUE_MESSAGE(desc[pos] <= (int)sizeof(desc) - pos,
"Descriptor length exceeds remaining bytes");
pos += desc[pos];
desc_count++;
}
TEST_ASSERT_EQUAL((int)sizeof(desc), pos);
TEST_ASSERT_TRUE(desc_count > 5);
}
void test_midi2_descriptor_valid_types(void) {
uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) };
int pos = 0;
while (pos < (int)sizeof(desc)) {
uint8_t dtype = desc[pos + 1];
bool valid = (dtype == TUSB_DESC_INTERFACE ||
dtype == TUSB_DESC_ENDPOINT ||
dtype == TUSB_DESC_CS_INTERFACE ||
dtype == TUSB_DESC_CS_ENDPOINT);
TEST_ASSERT_TRUE_MESSAGE(valid, "Invalid descriptor type found");
pos += desc[pos];
}
}
//--------------------------------------------------------------------+
// Edge cases
//--------------------------------------------------------------------+
void test_ump_word_count_with_values_beyond_0xf(void) {
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0x10));
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0xFF));
}
@@ -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), false, 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, false, true);
// SCSI Data transfer
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 512, false, 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, false, 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), false, true);
dcd_edpt_xfer_IgnoreArg_buffer();
tud_task();
}
@@ -0,0 +1,298 @@
/*
* 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.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
};
// Vendor OUT control request (direction OUT, type Vendor, recipient Device), 8-byte data stage
tusb_control_request_t const req_vendor_out =
{
.bmRequestType = 0x40,
.bRequest = 0x01,
.wValue = 0x0000,
.wIndex = 0x0000,
.wLength = 8
};
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;
}
// Backing buffer for the vendor OUT data stage. Sized to EP0 max packet so an (untested) regression
// that drops the clamp can't corrupt memory here; the regression is caught by the expectation below.
static uint8_t vendor_out_buf[CFG_TUD_ENDPOINT0_SIZE];
bool tud_vendor_control_xfer_cb(uint8_t rhport_, uint8_t stage, tusb_control_request_t const* request) {
(void) request;
if (stage == CONTROL_STAGE_SETUP) {
// Offer only an 8-byte capacity even though the data stage may receive a larger packet
return tud_control_xfer(rhport_, request, vendor_out_buf, 8);
}
return true;
}
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), false, 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, false, 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, false, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, total_len, 0, false);
// status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, false, 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, false, 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, false, 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, false, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false);
// Status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, false, 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();
}
//--------------------------------------------------------------------+
// Control OUT data stage host overrun
//--------------------------------------------------------------------+
// A non-compliant host sends an OUT data packet larger than the buffer the class offered:
// wLength = 8, but the DCD reports a full CFG_TUD_ENDPOINT0_SIZE packet. usbd must clamp the
// copy/accounting to the 8-byte capacity so total_xferred reaches wLength, ends the data stage,
// and queues the IN status stage. Without the clamp total_xferred overshoots wLength and usbd
// re-arms an OUT data packet (EDPT_CTRL_OUT) instead, failing the EDPT_CTRL_IN expectation below.
void test_usbd_control_out_overrun_clamp(void)
{
dcd_event_setup_received(rhport, (uint8_t*) &req_vendor_out, false);
// Data stage: usbd arms an 8-byte OUT into its internal bounce buffer (buffer ptr is internal)
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 8, false, true);
dcd_edpt_xfer_IgnoreArg_buffer();
// Host overrun: DCD reports a full max packet, larger than the 8-byte capacity
dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, CFG_TUD_ENDPOINT0_SIZE, XFER_RESULT_SUCCESS, false);
// Clamp -> total_xferred == wLength -> data stage done -> IN status stage queued
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, false, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false);
dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_vendor_out, 1);
tud_task();
}
@@ -0,0 +1,101 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2026 Saulo Verissimo
*
* 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"
#include "tusb_option.h"
#include "class/midi/midi.h"
#include "class/midi/midi2_host.h"
void setUp(void) {}
void tearDown(void) {}
//--------------------------------------------------------------------+
// UMP Word Count (shared helper, defined in midi.h)
//--------------------------------------------------------------------+
void test_midi2_host_ump_word_count_1word(void) {
uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7};
for (int i = 0; i < 5; i++) {
TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i]));
}
}
void test_midi2_host_ump_word_count_2word(void) {
uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA};
for (int i = 0; i < 5; i++) {
TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i]));
}
}
void test_midi2_host_ump_word_count_4word(void) {
uint8_t types[] = {0x5, 0xD, 0xE, 0xF};
for (int i = 0; i < 4; i++) {
TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i]));
}
}
//--------------------------------------------------------------------+
// Callback struct field validation
//--------------------------------------------------------------------+
void test_midi2_descriptor_cb_struct_fields(void) {
tuh_midi2_descriptor_cb_t desc = {
.protocol_version = 1,
.bcdMSC_hi = 0x02,
.bcdMSC_lo = 0x00,
.rx_cable_count = 1,
.tx_cable_count = 1
};
TEST_ASSERT_EQUAL(1, desc.protocol_version);
TEST_ASSERT_EQUAL(0x02, desc.bcdMSC_hi);
TEST_ASSERT_EQUAL(0x00, desc.bcdMSC_lo);
TEST_ASSERT_EQUAL(1, desc.rx_cable_count);
TEST_ASSERT_EQUAL(1, desc.tx_cable_count);
}
void test_midi2_mount_cb_struct_fields(void) {
tuh_midi2_mount_cb_t mount = {
.daddr = 1,
.bInterfaceNumber = 0,
.protocol_version = 1,
.alt_setting_active = 1,
.rx_cable_count = 2,
.tx_cable_count = 2
};
TEST_ASSERT_EQUAL(1, mount.daddr);
TEST_ASSERT_EQUAL(0, mount.bInterfaceNumber);
TEST_ASSERT_EQUAL(1, mount.protocol_version);
TEST_ASSERT_EQUAL(1, mount.alt_setting_active);
TEST_ASSERT_EQUAL(2, mount.rx_cable_count);
TEST_ASSERT_EQUAL(2, mount.tx_cable_count);
}
//--------------------------------------------------------------------+
// CS Endpoint subtypes
//--------------------------------------------------------------------+
void test_midi2_host_cs_endpoint_subtypes(void) {
TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL);
TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0);
}
@@ -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_ */
@@ -0,0 +1,192 @@
/*
* 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));
}
void test_tu_scatter_read32(void) {
// Test data: 0x04030201
uint8_t buf1[] = {0x01, 0x02, 0x03, 0x04};
uint8_t buf2[] = {0x05, 0x06, 0x07, 0x08};
// len1=1, len2=0: read 1 byte from buf1
TEST_ASSERT_EQUAL_HEX32(0x01, tu_scatter_read32(buf1, 1, buf2, 0));
// len1=1, len2=1: read 1 byte from buf1, 1 byte from buf2
TEST_ASSERT_EQUAL_HEX32(0x0501, tu_scatter_read32(buf1, 1, buf2, 1));
// len1=1, len2=2: read 1 byte from buf1, 2 bytes from buf2
TEST_ASSERT_EQUAL_HEX32(0x060501, tu_scatter_read32(buf1, 1, buf2, 2));
// len1=1, len2=3: read 1 byte from buf1, 3 bytes from buf2
TEST_ASSERT_EQUAL_HEX32(0x07060501, tu_scatter_read32(buf1, 1, buf2, 3));
// len1=2, len2=0: read 2 bytes from buf1
TEST_ASSERT_EQUAL_HEX32(0x0201, tu_scatter_read32(buf1, 2, buf2, 0));
// len1=2, len2=1: read 2 bytes from buf1, 1 byte from buf2
TEST_ASSERT_EQUAL_HEX32(0x050201, tu_scatter_read32(buf1, 2, buf2, 1));
// len1=2, len2=2: read 2 bytes from buf1, 2 bytes from buf2
TEST_ASSERT_EQUAL_HEX32(0x06050201, tu_scatter_read32(buf1, 2, buf2, 2));
// len1=3, len2=0: read 3 bytes from buf1
TEST_ASSERT_EQUAL_HEX32(0x030201, tu_scatter_read32(buf1, 3, buf2, 0));
// len1=3, len2=1: read 3 bytes from buf1, 1 byte from buf2
TEST_ASSERT_EQUAL_HEX32(0x05030201, tu_scatter_read32(buf1, 3, buf2, 1));
}
void test_tu_scatter_write32(void) {
uint8_t buf1[4];
uint8_t buf2[4];
// len1=1, len2=0: write 1 byte to buf1
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x01, buf1, 1, buf2, 0);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x00, buf2[0]);
// len1=1, len2=1: write 1 byte to buf1, 1 byte to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x0201, buf1, 1, buf2, 1);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]);
// len1=1, len2=2: write 1 byte to buf1, 2 bytes to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x030201, buf1, 1, buf2, 2);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf2[1]);
// len1=1, len2=3: write 1 byte to buf1, 3 bytes to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x04030201, buf1, 1, buf2, 3);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf2[1]);
TEST_ASSERT_EQUAL_HEX8(0x04, buf2[2]);
// len1=2, len2=0: write 2 bytes to buf1
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x0201, buf1, 2, buf2, 0);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]);
// len1=2, len2=1: write 2 bytes to buf1, 1 byte to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x030201, buf1, 2, buf2, 1);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf2[0]);
// len1=2, len2=2: write 2 bytes to buf1, 2 bytes to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x04030201, buf1, 2, buf2, 2);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf2[0]);
TEST_ASSERT_EQUAL_HEX8(0x04, buf2[1]);
// len1=3, len2=0: write 3 bytes to buf1
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x030201, buf1, 3, buf2, 0);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf1[2]);
// len1=3, len2=1: write 3 bytes to buf1, 1 byte to buf2
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
tu_scatter_write32(0x04030201, buf1, 3, buf2, 1);
TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]);
TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]);
TEST_ASSERT_EQUAL_HEX8(0x03, buf1[2]);
TEST_ASSERT_EQUAL_HEX8(0x04, buf2[0]);
}
+627
View File
@@ -0,0 +1,627 @@
/*
* 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, false);
tu_fifo_t *ff = &tu_ff;
tu_fifo_buffer_info_t info;
uint8_t test_data[4096];
uint8_t rd_buf[FIFO_SIZE];
static const tu_hwfifo_access_t hwfifo_access_32 = {
.data_stride = 4,
.param = 0,
};
static const tu_hwfifo_access_t hwfifo_access_16 = {
.data_stride = 2,
.param = 0,
};
void setUp(void) {
tu_fifo_clear(ff);
memset(&info, 0, sizeof(tu_fifo_buffer_info_t));
for (size_t 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_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.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr);
TEST_ASSERT_NULL(info.wrapped.ptr);
}
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.linear.len);
TEST_ASSERT_EQUAL(2, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 6, info.linear.ptr);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr);
}
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.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr);
// application should check len instead of ptr.
// TEST_ASSERT_NULL(info.wrapped.ptr);
}
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.linear.len);
TEST_ASSERT_EQUAL(2, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 6, info.linear.ptr);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr);
}
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.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_NULL(info.linear.ptr);
TEST_ASSERT_NULL(info.wrapped.ptr);
// write info
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(FIFO_SIZE, info.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr);
// application should check len instead of ptr.
// TEST_ASSERT_NULL(info.wrapped.ptr);
// 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.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr);
// skip this, application must check len instead of buffer
// TEST_ASSERT_NULL(info.wrapped.ptr);
// write info
}
void test_rd_idx_wrap(void) {
tu_fifo_t ff10;
uint8_t buf[10];
uint8_t dst[10];
tu_fifo_config(&ff10, buf, 10, 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);
}
void test_advance_write_pointer_cases(void) {
tu_fifo_clear(ff);
tu_fifo_advance_write_pointer(ff, 3);
TEST_ASSERT_EQUAL(3, ff->wr_idx);
TEST_ASSERT_EQUAL(3, tu_fifo_count(ff));
// advance to cross depth but stay within 0..2*depth window
ff->wr_idx = FIFO_SIZE - 2; // 62
ff->rd_idx = 0;
tu_fifo_advance_write_pointer(ff, 10); // 62 + 10 = 72 within window
TEST_ASSERT_EQUAL(72, ff->wr_idx);
TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff));
// advance past the unused index space (beyond 2*depth)
ff->wr_idx = (uint16_t)(2 * FIFO_SIZE - 3); // 125
ff->rd_idx = 0;
tu_fifo_advance_write_pointer(ff, 6); // forces wrap across unused space
TEST_ASSERT_EQUAL(3, ff->wr_idx);
TEST_ASSERT_EQUAL(3, tu_fifo_count(ff));
}
void test_advance_read_pointer_cases(void) {
tu_fifo_clear(ff);
ff->wr_idx = 6;
tu_fifo_advance_read_pointer(ff, 3);
TEST_ASSERT_EQUAL(3, ff->rd_idx);
TEST_ASSERT_EQUAL(3, tu_fifo_count(ff));
ff->wr_idx = FIFO_SIZE + 10; // 74
ff->rd_idx = FIFO_SIZE - 10; // 54
tu_fifo_advance_read_pointer(ff, 20); // move to match write index within window
TEST_ASSERT_EQUAL(74, ff->rd_idx);
TEST_ASSERT_EQUAL(0, tu_fifo_count(ff));
ff->wr_idx = 9;
ff->rd_idx = (uint16_t)(2 * FIFO_SIZE - 1); // 127
tu_fifo_advance_read_pointer(ff, 6); // crosses unused index space
TEST_ASSERT_EQUAL(5, ff->rd_idx);
TEST_ASSERT_EQUAL(4, tu_fifo_count(ff));
}
void test_write_n_fixed_addr_rw32_nowrap(void) {
tu_fifo_clear(ff);
volatile uint32_t reg = 0x11223344;
uint8_t expected[8] = {0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11};
for (uint8_t n = 1; n <= 8; n++) {
tu_fifo_clear(ff);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, &hwfifo_access_32);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[8] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_write_n_fixed_addr_rw32_wrapped(void) {
tu_fifo_clear(ff);
volatile uint32_t reg = 0xA1B2C3D4;
uint8_t expected[8] = {0xD4, 0xC3, 0xB2, 0xA1, 0xD4, 0xC3, 0xB2, 0xA1};
for (uint8_t n = 1; n <= 8; n++) {
tu_fifo_clear(ff);
// Position the fifo near the end so writes wrap
ff->wr_idx = FIFO_SIZE - 3;
ff->rd_idx = FIFO_SIZE - 3;
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, &hwfifo_access_32);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[8] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_read_n_fixed_addr_rw32_nowrap(void) {
uint8_t pattern[8] = {0x10, 0x21, 0x32, 0x43, 0x54, 0x65, 0x76, 0x87};
uint32_t reg_expected[8] = {
0x00000010, 0x00002110, 0x00322110, 0x43322110, 0x00000054, 0x00006554, 0x00766554, 0x87766554};
for (uint8_t n = 1; n <= 8; n++) {
tu_fifo_clear(ff);
tu_fifo_write_n(ff, pattern, 8);
uint32_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, &hwfifo_access_32);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(8 - n, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX32(reg_expected[n - 1], reg);
}
}
void test_read_n_fixed_addr_rw32_wrapped(void) {
uint8_t pattern[8] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87};
uint32_t reg_expected[8] = {
0x000000F0, 0x0000E1F0, 0x00D2E1F0, 0xC3D2E1F0, 0x000000B4, 0x0000A5B4, 0x0096A5B4, 0x8796A5B4};
for (uint8_t n = 1; n <= 8; n++) {
tu_fifo_clear(ff);
ff->rd_idx = FIFO_SIZE - 2;
ff->wr_idx = (uint16_t)(ff->rd_idx + n);
for (uint8_t i = 0; i < n; i++) {
uint8_t idx = (uint8_t)((ff->rd_idx + i) % FIFO_SIZE);
ff->buffer[idx] = pattern[i];
}
uint32_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, &hwfifo_access_32);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(0, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX32(reg_expected[n - 1], reg);
}
}
void test_write_n_fixed_addr_rw16_nowrap(void) {
tu_fifo_clear(ff);
volatile uint16_t reg = 0x1122;
uint8_t expected[6] = {0x22, 0x11, 0x22, 0x11, 0x22, 0x11};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, &hwfifo_access_16);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[6] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_write_n_fixed_addr_rw16_wrapped(void) {
tu_fifo_clear(ff);
volatile uint16_t reg = 0xA1B2;
uint8_t expected[6] = {0xB2, 0xA1, 0xB2, 0xA1, 0xB2, 0xA1};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
// Position the fifo near the end so writes wrap
ff->wr_idx = FIFO_SIZE - 3;
ff->rd_idx = FIFO_SIZE - 3;
uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)&reg, n, &hwfifo_access_16);
TEST_ASSERT_EQUAL(n, written);
TEST_ASSERT_EQUAL(n, tu_fifo_count(ff));
uint8_t out[6] = {0};
tu_fifo_read_n(ff, out, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n);
}
}
void test_read_n_fixed_addr_rw16_nowrap(void) {
uint8_t pattern[6] = {0x10, 0x21, 0x32, 0x43, 0x54, 0x65};
uint16_t reg_expected[6] = {0x0010, 0x2110, 0x0032, 0x4332, 0x0054, 0x6554};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
tu_fifo_write_n(ff, pattern, 6);
uint16_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, &hwfifo_access_16);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(6 - n, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg);
}
}
void test_read_n_fixed_addr_rw16_wrapped(void) {
uint8_t pattern[6] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5};
uint16_t reg_expected[6] = {0x00F0, 0xE1F0, 0x00D2, 0xC3D2, 0x00B4, 0xA5B4};
for (uint8_t n = 1; n <= 6; n++) {
tu_fifo_clear(ff);
ff->rd_idx = FIFO_SIZE - 1;
ff->wr_idx = (uint16_t)(ff->rd_idx + n);
for (uint8_t i = 0; i < n; i++) {
uint8_t idx = (uint8_t)((ff->rd_idx + i) % FIFO_SIZE);
ff->buffer[idx] = pattern[i];
}
uint16_t reg = 0;
uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, &reg, n, &hwfifo_access_16);
TEST_ASSERT_EQUAL(n, read_cnt);
TEST_ASSERT_EQUAL(0, tu_fifo_count(ff));
TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg);
}
}
void test_get_read_info_advanced_cases(void) {
tu_fifo_clear(ff);
ff->wr_idx = 20;
ff->rd_idx = 2;
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(18, info.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr);
TEST_ASSERT_NULL(info.wrapped.ptr);
ff->wr_idx = 68; // ptr = 4
ff->rd_idx = 56; // ptr = 56
tu_fifo_get_read_info(ff, &info);
TEST_ASSERT_EQUAL(8, info.linear.len);
TEST_ASSERT_EQUAL(4, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 56, info.linear.ptr);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr);
}
void test_get_write_info_advanced_cases(void) {
tu_fifo_clear(ff);
ff->wr_idx = 10;
ff->rd_idx = 104; // ptr = 40
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(30, info.linear.len);
TEST_ASSERT_EQUAL(0, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 10, info.linear.ptr);
TEST_ASSERT_NULL(info.wrapped.ptr);
ff->wr_idx = 60;
ff->rd_idx = 20;
tu_fifo_get_write_info(ff, &info);
TEST_ASSERT_EQUAL(4, info.linear.len);
TEST_ASSERT_EQUAL(20, info.wrapped.len);
TEST_ASSERT_EQUAL_PTR(ff->buffer + 60, info.linear.ptr);
TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr);
}
void test_correct_read_pointer_cases(void) {
tu_fifo_clear(ff);
// wr beyond depth: rd should be wr - depth
ff->wr_idx = FIFO_SIZE + 6; // 70
tu_fifo_correct_read_pointer(ff);
TEST_ASSERT_EQUAL(6, ff->rd_idx);
// wr exactly at depth: rd should wrap to zero
ff->wr_idx = FIFO_SIZE;
tu_fifo_correct_read_pointer(ff);
TEST_ASSERT_EQUAL(0, ff->rd_idx);
// wr below depth: rd should be wr + depth
ff->wr_idx = 10;
tu_fifo_correct_read_pointer(ff);
TEST_ASSERT_EQUAL(FIFO_SIZE + 10, ff->rd_idx);
}