Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
30198b2
refactor tu_fifo, add tu_fifo_write/read/peek_n_access()
hathach Nov 20, 2025
d4cdc09
add more unit tests for tu_fifo
hathach Nov 21, 2025
d0d56a5
more tu_fifo refactor
hathach Nov 21, 2025
c9b623a
edpt stream support xfer_fifo for device CFG_TUD_EDPT_DEDICATED_HWFIFO
hathach Nov 21, 2025
793d3b5
more tusb fifo refactor: ff_peek_local() lock mutex if need to correc…
hathach Nov 21, 2025
7df6e7b
implement wanted char for cdc device without ep_buf
hathach Nov 21, 2025
d9094ff
implement wanted char for cdc device without ep_buf
hathach Nov 21, 2025
b7bf1d9
hil stress test cdc
hathach Nov 21, 2025
b98127f
hil stress test cdc
hathach Nov 22, 2025
67ba8ea
focus on cdc test, write lots more data, each trunk is 64 or less sin…
hathach Nov 22, 2025
e1c218f
Merge branch 'master' into xfer-fifo
hathach Nov 26, 2025
1a51a7e
dwc2 only enable dedicated hwfifo if DMA is not enabled
hathach Nov 26, 2025
c925277
change tu_fifo_buffer_info_t layout
hathach Nov 26, 2025
f63b672
dwc2 stm32 check if dcache enabled before calling SCB DCache function
hathach Nov 26, 2025
0fa3002
omit ep buffer for midi device if device support CFG_TUD_EDPT_DEDICAT…
hathach Nov 26, 2025
26a73df
add tu_scatter_read32(), tu_scatter_write32() to simplify tu_fifo
hathach Nov 26, 2025
02e67c8
more fifo refactor
hathach Nov 26, 2025
d6c50c7
add tud_cdc_n_notify_msg()
hathach Nov 26, 2025
726497a
omit cdc epnotify for dedicated hw fifo
hathach Nov 26, 2025
4affbc1
more rename
hathach Nov 27, 2025
07dfbad
correct tu_edpt_stream_read() with non-fifo mode. Fix rhport with ven…
hathach Nov 27, 2025
8cf2c3b
re-enable other hil tests
hathach Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
PenaltyBreakBeforeFirstCallParameter: 1000000
PenaltyBreakOpenParenthesis: 1000000
PPIndentWidth: 2
QualifierAlignment: Custom
QualifierOrder: ['static', 'const', 'volatile', 'restrict', 'type']
ReflowComments: false
SpaceAfterTemplateKeyword: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
Expand Down
50 changes: 28 additions & 22 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,39 @@ information that does not match the info here.

- Install ARM GCC toolchain: `sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi`
- Fetch core dependencies: `python3 tools/get_deps.py` -- takes <1 second. NEVER CANCEL.
- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4)
- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4), or
`python3 tools/get_deps.py -b BOARD_NAME`
- Dependencies are cached in `lib/` and `hw/mcu/` directories

## Build Examples

Choose ONE of these approaches:

**Option 1: Individual Example with CMake (RECOMMENDED)**
**Option 1: Individual Example with CMake and Ninja (RECOMMENDED)**

```bash
cd examples/device/cdc_msc
mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -DCMAKE_BUILD_TYPE=MinSizeRel ..
cmake --build . -j4
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ..
cmake --build .
```

-- takes 1-2 seconds. NEVER CANCEL. Set timeout to 5+ minutes.

**CMake with Ninja (Alternative)**
**Option 2: All Examples for a Board**

different folder than Option 1

```bash
cd examples/device/cdc_msc
mkdir build && cd build
cmake -G Ninja -DBOARD=raspberry_pi_pico ..
ninja
cd examples/
mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ..
cmake --build .
```

**Option 2: Individual Example with Make**
-- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes.

**Option 3: Individual Example with Make**

```bash
cd examples/device/cdc_msc
Expand All @@ -54,13 +59,6 @@ make BOARD=raspberry_pi_pico all

-- takes 2-3 seconds. NEVER CANCEL. Set timeout to 5+ minutes.

**Option 3: All Examples for a Board**

```bash
python3 tools/build.py -b BOARD_NAME
```

-- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes.

## Build Options

Expand Down Expand Up @@ -101,6 +99,17 @@ python3 tools/build.py -b BOARD_NAME
- Run specific test: `cd test/unit-test && ceedling test:test_fifo`
- Tests use Unity framework with CMock for mocking

## Hardware-in-the-Loop (HIL) Testing

- Run tests on actual hardware, one of following ways:
- test a specific board `python test/hil/hil_test.py -b BOARD_NAME -B examples local.json`
- test all boards in config `python test/hil/hil_test.py -B examples local.json`
- In case of error, enabled verbose mode with `-v` flag for detailed logs. Also try to observe script output, and try to
modify hil_test.py (temporarily) to add more debug prints to pinpoint the issue.
- Requires pre-built (all) examples for target boards (see Build Examples section 2)

take 2-5 minutes. NEVER CANCEL. Set timeout to 20+ minutes.

## Documentation

- Install requirements: `pip install -r docs/requirements.txt`
Expand Down Expand Up @@ -145,11 +154,8 @@ python3 tools/build.py -b BOARD_NAME
- Install pre-commit: `pip install pre-commit && pre-commit install`
- Runs all quality checks, unit tests, spell checking, and formatting
- Takes 10-15 seconds. NEVER CANCEL. Set timeout to 15+ minutes.
2. **Build validation**: Build at least one example that exercises your changes
```bash
cd examples/device/cdc_msc
make BOARD=raspberry_pi_pico all
```
2. **Build validation**: Build at least one board with all example that exercises your changes, see Build Examples
section (option 2)
3. Run unit tests relevant to touched modules; add fuzz/HIL coverage when modifying parsers or protocol state machines.

### Manual Testing Scenarios
Expand Down
78 changes: 0 additions & 78 deletions CLAUDE.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/info/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Changelog
0.20.0
======

*November 19, 2024*
*November 19, 2025*

General
-------
Expand Down
91 changes: 54 additions & 37 deletions src/class/cdc/cdc_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ typedef struct {

#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding)

// Skip local EP buffer if dedicated hw FIFO is supported
#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
typedef struct {
TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
Expand All @@ -75,6 +77,9 @@ typedef struct {
#endif
} cdcd_epbuf_t;

CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
#endif

//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -115,7 +120,6 @@ TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();

TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) {
Expand Down Expand Up @@ -166,40 +170,22 @@ void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t *coding) {
}

#if CFG_TUD_CDC_NOTIFY
bool tud_cdc_n_notify_uart_state (uint8_t itf, const cdc_notify_uart_state_t *state) {
bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg) {
TU_VERIFY(itf < CFG_TUD_CDC);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[itf];
const cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));

cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
notify_msg->request.bRequest = CDC_NOTIF_SERIAL_STATE;
notify_msg->request.wValue = 0;
notify_msg->request.wIndex = p_cdc->itf_num;
notify_msg->request.wLength = sizeof(cdc_notify_uart_state_t);
notify_msg->serial_state = *state;

return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_uart_state_t), false);
}

bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change) {
TU_VERIFY(itf < CFG_TUD_CDC);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[itf];
TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));
#if CFG_TUD_EDPT_DEDICATED_HWFIFO
cdc_notify_msg_t *msg_epbuf = msg;
#else
cdc_notify_msg_t *msg_epbuf = &_cdcd_epbuf[itf].epnotify;
*msg_epbuf = *msg;
#endif

cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
notify_msg->request.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE;
notify_msg->request.wValue = 0;
notify_msg->request.wIndex = p_cdc->itf_num;
notify_msg->request.wLength = sizeof(cdc_notify_conn_speed_change_t);
notify_msg->conn_speed_change = *conn_speed_change;
msg_epbuf->request.wIndex = p_cdc->itf_num;

return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_conn_speed_change_t), false);
return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)msg_epbuf, 8 + msg_epbuf->request.wLength, false);
}
#endif

Expand Down Expand Up @@ -268,8 +254,6 @@ void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t *p_cdc = &_cdcd_itf[i];
cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[i];

p_cdc->wanted_char = (char) -1;

// default line coding is : stop bit = 1, parity = none, data bits = 8
Expand All @@ -278,14 +262,23 @@ void cdcd_init(void) {
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;

#if CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *epout_buf = NULL;
uint8_t *epin_buf = NULL;
#else
cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[i];
uint8_t *epout_buf = p_epbuf->epout;
uint8_t *epin_buf = p_epbuf->epin;
#endif

tu_edpt_stream_init(&p_cdc->stream.rx, false, false, false, p_cdc->stream.rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE,
p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
epout_buf, CFG_TUD_CDC_EP_BUFSIZE);

// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
tu_edpt_stream_init(&p_cdc->stream.tx, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected,
p_cdc->stream.tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
p_cdc->stream.tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, epin_buf, CFG_TUD_CDC_EP_BUFSIZE);
}
}

Expand Down Expand Up @@ -481,11 +474,35 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
if (ep_addr == stream_rx->ep_addr) {
tu_edpt_stream_read_xfer_complete(stream_rx, xferred_bytes);

// Check for wanted char and invoke wanted callback (multiple times if multiple wanted received)
// Check for wanted char and invoke wanted callback
if (((signed char)p_cdc->wanted_char) != -1) {
for (uint32_t i = 0; i < xferred_bytes; i++) {
if ((p_cdc->wanted_char == (char)stream_rx->ep_buf[i]) && !tu_edpt_stream_empty(stream_rx)) {
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
tu_fifo_buffer_info_t buf_info;
tu_fifo_get_read_info(&stream_rx->ff, &buf_info);

// find backward
uint8_t *ptr;
if (buf_info.wrapped.len > 0) {
ptr = buf_info.wrapped.ptr + buf_info.wrapped.len - 1; // last byte of wrap buffer
} else if (buf_info.linear.len > 0) {
ptr = buf_info.linear.ptr + buf_info.linear.len - 1; // last byte of linear buffer
} else {
ptr = NULL; // no data
}

if (ptr != NULL) {
for (uint32_t i = 0; i < xferred_bytes; i++) {
if (p_cdc->wanted_char == (char)*ptr) {
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
break; // only invoke once per transfer, even if multiple wanted chars are present
}

if (ptr == buf_info.wrapped.ptr) {
ptr = buf_info.linear.ptr + buf_info.linear.len - 1; // last byte of linear buffer
} else if (ptr == buf_info.linear.ptr) {
break; // reached the beginning
} else {
ptr--;
}
}
}
}
Expand Down
Loading