diff --git a/.clang-format b/.clang-format index f15c26c9ed..2cd0e1554e 100644 --- a/.clang-format +++ b/.clang-format @@ -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 diff --git a/AGENTS.md b/AGENTS.md index a6163dd424..73bf1f5993 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -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 @@ -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` @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 6c6baa2461..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,78 +0,0 @@ -# TinyUSB Development Guide - -## Build Commands - -### CMake Build System (Preferred) -CMake with Ninja is the preferred build method for TinyUSB development. - -- Build example with Ninja: - ```bash - cd examples/device/cdc_msc - mkdir build && cd build - cmake -G Ninja -DBOARD=raspberry_pi_pico .. - ninja - ``` -- Debug build: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DCMAKE_BUILD_TYPE=Debug ..` -- With logging: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DLOG=2 ..` -- With RTT logger: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DLOG=2 -DLOGGER=rtt ..` -- Flash with JLink: `ninja cdc_msc-jlink` -- Flash with OpenOCD: `ninja cdc_msc-openocd` -- Generate UF2: `ninja cdc_msc-uf2` -- List all targets: `ninja -t targets` - -### Make Build System (Alternative) -- Build example: `cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico all` -- For specific example: `cd examples/{device|host|dual}/{example_name} && make BOARD=raspberry_pi_pico all` -- Flash with JLink: `make BOARD=raspberry_pi_pico flash-jlink` -- Flash with OpenOCD: `make BOARD=raspberry_pi_pico flash-openocd` -- Debug build: `make BOARD=raspberry_pi_pico DEBUG=1 all` -- With logging: `make BOARD=raspberry_pi_pico LOG=2 all` -- With RTT logger: `make BOARD=raspberry_pi_pico LOG=2 LOGGER=rtt all` -- Generate UF2: `make BOARD=raspberry_pi_pico all uf2` - -### Additional Options -- Select RootHub port: `RHPORT_DEVICE=1` (make) or `-DRHPORT_DEVICE=1` (cmake) -- Set port speed: `RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED` (make) or `-DRHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED` (cmake) - -### Dependencies -- Get dependencies: `python tools/get_deps.py rp2040` -- Or from example: `cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico get-deps` - -### Testing -- Run unit tests: `cd test/unit-test && ceedling test:all` -- Run specific test: `cd test/unit-test && ceedling test:test_fifo` - -### Pre-commit Hooks -Before building, it's recommended to run pre-commit to ensure code quality: -- Run pre-commit on all files: `pre-commit run --all-files` -- Run pre-commit on staged files: `pre-commit run` -- Install pre-commit hook: `pre-commit install` - -## Code Style Guidelines -- Use C99 standard -- Memory-safe: no dynamic allocation -- Thread-safe: defer all interrupt events to non-ISR task functions -- 2-space indentation, no tabs -- Use snake_case for variables/functions -- Use UPPER_CASE for macros and constants -- Follow existing variable naming patterns in files you're modifying -- Include proper header comments with MIT license -- Add descriptive comments for non-obvious functions -- When including headers, group in order: C stdlib, tusb common, drivers, classes -- Always check return values from functions that can fail -- Use TU_ASSERT() for error checking with return statements - -## Project Structure -- src/: Core TinyUSB stack code -- hw/: Board support packages and MCU drivers -- examples/: Reference examples for device/host/dual -- test/: Unit tests and hardware integration tests - -## Release Process -To prepare a new release: -1. Update the `version` variable in `tools/make_release.py` to the new version number -2. Run the release script: `python tools/make_release.py` - - This will update version numbers in `src/tusb_option.h`, `repository.yml`, and `library.json` - - It will also regenerate documentation -3. Update `docs/info/changelog.rst` with release notes -4. Commit changes and create release tag diff --git a/docs/info/changelog.rst b/docs/info/changelog.rst index df23ce7d81..35c8515ccf 100644 --- a/docs/info/changelog.rst +++ b/docs/info/changelog.rst @@ -5,7 +5,7 @@ Changelog 0.20.0 ====== -*November 19, 2024* +*November 19, 2025* General ------- diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 9e62c65099..7ef8aa7385 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -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); @@ -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 //--------------------------------------------------------------------+ @@ -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) { @@ -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 @@ -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 @@ -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); } } @@ -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--; + } } } } diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 6f21af4f32..0809b578fb 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -36,6 +36,14 @@ #define CFG_TUD_CDC_NOTIFY 0 #endif +#ifndef CFG_TUD_CDC_TX_BUFSIZE + #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#endif + +#ifndef CFG_TUD_CDC_RX_BUFSIZE + #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#endif + #if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE) #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE @@ -133,11 +141,37 @@ bool tud_cdc_n_write_clear(uint8_t itf); #if CFG_TUD_CDC_NOTIFY +bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg); + // Send UART status notification: DCD, DSR etc .. -bool tud_cdc_n_notify_uart_state(uint8_t itf, const cdc_notify_uart_state_t *state); +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_n_notify_uart_state(uint8_t itf, + const cdc_notify_uart_state_t *state) { + cdc_notify_msg_t notify_msg; + 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 = 0; // filled later + notify_msg.request.wLength = sizeof(cdc_notify_uart_state_t); + notify_msg.serial_state = *state; + return tud_cdc_n_notify_msg(itf, ¬ify_msg); +} // Send connection speed change notification -bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change); +TU_ATTR_ALWAYS_INLINE static inline bool +tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t *conn_speed_change) { + cdc_notify_msg_t notify_msg; + 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 = 0; // filled later + notify_msg.request.wLength = sizeof(cdc_notify_conn_speed_change_t); + notify_msg.conn_speed_change = *conn_speed_change; + return tud_cdc_n_notify_msg(itf, ¬ify_msg); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_msg(cdc_notify_msg_t *msg) { + return tud_cdc_n_notify_msg(0, msg); +} TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_uart_state(const cdc_notify_uart_state_t* state) { return tud_cdc_n_notify_uart_state(0, state); diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index b20903d68f..8d1dc7d4a9 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (CFG_TUD_ENABLED && CFG_TUD_MIDI) +#if CFG_TUD_ENABLED && CFG_TUD_MIDI //--------------------------------------------------------------------+ // INCLUDE @@ -71,20 +71,21 @@ typedef struct { static midid_interface_t _midid_itf[CFG_TUD_MIDI]; -// Endpoint Transfer buffer + #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 +// Endpoint Transfer buffer: not used if dedicated hw FIFO is available typedef struct { TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE); TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE); } midid_epbuf_t; CFG_TUD_MEM_SECTION static midid_epbuf_t _midid_epbuf[CFG_TUD_MIDI]; + #endif //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ bool tud_midi_n_mounted (uint8_t itf) { midid_interface_t *p_midi = &_midid_itf[itf]; - const bool tx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.tx); const bool rx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.rx); return tx_opened && rx_opened; @@ -313,18 +314,23 @@ uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_ //--------------------------------------------------------------------+ void midid_init(void) { tu_memclr(_midid_itf, sizeof(_midid_itf)); - for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) { midid_interface_t *p_midi = &_midid_itf[i]; + + #if CFG_TUD_EDPT_DEDICATED_HWFIFO + uint8_t *epout_buf = NULL; + uint8_t *epin_buf = NULL; + #else midid_epbuf_t *p_epbuf = &_midid_epbuf[i]; + uint8_t *epout_buf = p_epbuf->epout; + uint8_t *epin_buf = p_epbuf->epin; + #endif - tu_edpt_stream_init( - &p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, - p_epbuf->epout, CFG_TUD_MIDI_EP_BUFSIZE); + tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf, + CFG_TUD_MIDI_RX_BUFSIZE, epout_buf, CFG_TUD_MIDI_EP_BUFSIZE); - tu_edpt_stream_init( - &p_midi->ep_stream.tx, false, true, false, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, p_epbuf->epin, - CFG_TUD_MIDI_EP_BUFSIZE); + tu_edpt_stream_init(&p_midi->ep_stream.tx, false, true, false, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, + epin_buf, CFG_TUD_MIDI_EP_BUFSIZE); } } diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index 7da4d22398..c7903375d1 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -37,6 +37,7 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ typedef struct { + uint8_t rhport; uint8_t itf_num; /*------------- From this point, data is not cleared by bus reset -------------*/ @@ -97,32 +98,26 @@ bool tud_vendor_n_mounted(uint8_t itf) { uint32_t tud_vendor_n_available(uint8_t itf) { TU_VERIFY(itf < CFG_TUD_VENDOR, 0); vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_read_available(&p_itf->rx.stream); } bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) { TU_VERIFY(itf < CFG_TUD_VENDOR); vendord_interface_t* p_itf = &_vendord_itf[itf]; - return tu_edpt_stream_peek(&p_itf->rx.stream, u8); } uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) { TU_VERIFY(itf < CFG_TUD_VENDOR, 0); vendord_interface_t* p_itf = &_vendord_itf[itf]; - const uint8_t rhport = 0; - - return tu_edpt_stream_read(rhport, &p_itf->rx.stream, buffer, bufsize); + return tu_edpt_stream_read(p_itf->rhport, &p_itf->rx.stream, buffer, bufsize); } void tud_vendor_n_read_flush (uint8_t itf) { TU_VERIFY(itf < CFG_TUD_VENDOR, ); vendord_interface_t* p_itf = &_vendord_itf[itf]; - const uint8_t rhport = 0; - tu_edpt_stream_clear(&p_itf->rx.stream); - tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream); + tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->rx.stream); } //--------------------------------------------------------------------+ @@ -131,25 +126,19 @@ void tud_vendor_n_read_flush (uint8_t itf) { uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) { TU_VERIFY(itf < CFG_TUD_VENDOR, 0); vendord_interface_t* p_itf = &_vendord_itf[itf]; - const uint8_t rhport = 0; - - return tu_edpt_stream_write(rhport, &p_itf->tx.stream, buffer, (uint16_t) bufsize); + return tu_edpt_stream_write(p_itf->rhport, &p_itf->tx.stream, buffer, (uint16_t)bufsize); } uint32_t tud_vendor_n_write_flush (uint8_t itf) { TU_VERIFY(itf < CFG_TUD_VENDOR, 0); vendord_interface_t* p_itf = &_vendord_itf[itf]; - const uint8_t rhport = 0; - - return tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream); + return tu_edpt_stream_write_xfer(p_itf->rhport, &p_itf->tx.stream); } uint32_t tud_vendor_n_write_available (uint8_t itf) { TU_VERIFY(itf < CFG_TUD_VENDOR, 0); vendord_interface_t* p_itf = &_vendord_itf[itf]; - const uint8_t rhport = 0; - - return tu_edpt_stream_write_available(rhport, &p_itf->tx.stream); + return tu_edpt_stream_write_available(p_itf->rhport, &p_itf->tx.stream); } //--------------------------------------------------------------------+ @@ -162,23 +151,21 @@ void vendord_init(void) { vendord_interface_t* p_itf = &_vendord_itf[i]; vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i]; - uint8_t* rx_ff_buf = - #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 - p_itf->rx.ff_buf; - #else - NULL; - #endif + #if CFG_TUD_VENDOR_RX_BUFSIZE > 0 + uint8_t *rx_ff_buf = p_itf->rx.ff_buf; + #else + uint8_t *rx_ff_buf = NULL; + #endif tu_edpt_stream_init(&p_itf->rx.stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE); - uint8_t* tx_ff_buf = - #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 - p_itf->tx.ff_buf; - #else - NULL; - #endif + #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 + uint8_t *tx_ff_buf = p_itf->tx.ff_buf; + #else + uint8_t* tx_ff_buf = NULL; + #endif tu_edpt_stream_init(&p_itf->tx.stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, @@ -225,7 +212,9 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin } TU_VERIFY(p_vendor, 0); + p_vendor->rhport = rhport; p_vendor->itf_num = desc_itf->bInterfaceNumber; + while (tu_desc_in_bounds(p_desc, desc_end)) { const uint8_t desc_type = tu_desc_type(p_desc); if (desc_type == TUSB_DESC_INTERFACE || desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) { diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index f377d5272e..b53fa5c02a 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -329,6 +329,39 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_ #endif +// scatter read 4 bytes from two buffers. Parameter are not checked +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_scatter_read32(const uint8_t *buf1, uint8_t len1, const uint8_t *buf2, + uint8_t len2) { + uint32_t result = 0; + uint8_t shift = 0; + + for (uint8_t i = 0; i < len1; ++i) { + result |= ((uint32_t)buf1[i]) << shift; + shift += 8; + } + + for (uint8_t i = 0; i < len2; ++i) { + result |= ((uint32_t)buf2[i]) << shift; + shift += 8; + } + + return result; +} + +// scatter write 4 bytes to two buffers. Parameter are not checked +TU_ATTR_ALWAYS_INLINE static inline void tu_scatter_write32(uint32_t value, uint8_t *buf1, uint8_t len1, + uint8_t *buf2, uint8_t len2) { + for (uint8_t i = 0; i < len1; ++i) { + buf1[i] = (uint8_t)(value & 0xFF); + value >>= 8; + } + + for (uint8_t i = 0; i < len2; ++i) { + buf2[i] = (uint8_t)(value & 0xFF); + value >>= 8; + } +} + //--------------------------------------------------------------------+ // Descriptor helper //--------------------------------------------------------------------+ diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 5c9e586fb2..27b97310a7 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -38,35 +38,23 @@ #if OSAL_MUTEX_REQUIRED -TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) { +TU_ATTR_ALWAYS_INLINE static inline void ff_lock(osal_mutex_t mutex) { if (mutex != NULL) { osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); } } -TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) { +TU_ATTR_ALWAYS_INLINE static inline void ff_unlock(osal_mutex_t mutex) { if (mutex != NULL) { osal_mutex_unlock(mutex); } } #else + #define ff_lock(_mutex) + #define ff_unlock(_mutex) -#define _ff_lock(_mutex) -#define _ff_unlock(_mutex) - -#endif - -/** \enum tu_fifo_copy_mode_t - * \brief Write modes intended to allow special read and write functions to be able to - * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others - */ -typedef enum { - TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode -#ifdef TUP_MEM_CONST_ADDR - TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO #endif -} tu_fifo_copy_mode_t; bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_size, bool overwritable) { // Limit index space to 2*depth - this allows for a fast "modulo" calculation @@ -76,78 +64,67 @@ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_si return false; } - _ff_lock(f->mutex_wr); - _ff_lock(f->mutex_rd); + ff_lock(f->mutex_wr); + ff_lock(f->mutex_rd); f->buffer = (uint8_t *)buffer; f->depth = depth; - f->item_size = (uint16_t)(item_size & 0x7FFF); + f->item_size = (uint16_t)(item_size & 0x7FFFu); f->overwritable = overwritable; - f->rd_idx = 0; - f->wr_idx = 0; + f->rd_idx = 0u; + f->wr_idx = 0u; - _ff_unlock(f->mutex_wr); - _ff_unlock(f->mutex_rd); + ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_rd); return true; } //--------------------------------------------------------------------+ // Pull & Push +// copy data to/from fifo without updating read/write pointers //--------------------------------------------------------------------+ - -#ifdef TUP_MEM_CONST_ADDR -// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address -// Code adapted from dcd_synopsys.c -// TODO generalize with configurable 1 byte or 4 byte each read -static void _ff_push_const_addr(uint8_t *ff_buf, const void *app_buf, uint16_t len) { - const volatile uint32_t *reg_rx = (volatile const uint32_t *)app_buf; - +#ifdef CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32 +// Copy to fifo from fixed address buffer (usually a rx register) with TU_FIFO_FIXED_ADDR_RW32 mode +static void ff_push_fixed_addr_rw32(uint8_t *ff_buf, const volatile uint32_t *reg_rx, uint16_t len) { // Reading full available 32 bit words from const app address uint16_t full_words = len >> 2; while (full_words--) { - tu_unaligned_write32(ff_buf, *reg_rx); + const uint32_t tmp32 = *reg_rx; + tu_unaligned_write32(ff_buf, tmp32); ff_buf += 4; } // Read the remaining 1-3 bytes from const app address const uint8_t bytes_rem = len & 0x03; if (bytes_rem) { - uint32_t tmp32 = *reg_rx; + const uint32_t tmp32 = *reg_rx; memcpy(ff_buf, &tmp32, bytes_rem); } } -// Intended to be used to write to hardware USB FIFO in e.g. STM32 -// where all data is written to a constant address in full word copies -static void _ff_pull_const_addr(void *app_buf, const uint8_t *ff_buf, uint16_t len) { - volatile uint32_t *reg_tx = (volatile uint32_t *)app_buf; - +// Copy from fifo to fixed address buffer (usually a tx register) with TU_FIFO_FIXED_ADDR_RW32 mode +static void ff_pull_fixed_addr_rw32(volatile uint32_t *reg_tx, const uint8_t *ff_buf, uint16_t len) { // Write full available 32 bit words to const address - uint16_t full_words = len >> 2; + uint16_t full_words = len >> 2u; while (full_words--) { *reg_tx = tu_unaligned_read32(ff_buf); - ff_buf += 4; + ff_buf += 4u; } - // Write the remaining 1-3 bytes into const address + // Write the remaining 1-3 bytes const uint8_t bytes_rem = len & 0x03; if (bytes_rem) { - uint32_t tmp32 = 0; + uint32_t tmp32 = 0u; memcpy(&tmp32, ff_buf, bytes_rem); - *reg_tx = tmp32; } } #endif -// send one item to fifo WITHOUT updating write pointer -static inline void _ff_push(tu_fifo_t *f, const void *app_buf, uint16_t rel) { - memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size); -} - // send n items to fifo WITHOUT updating write pointer -static void _ff_push_n(tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) { +static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, + tu_fifo_access_mode_t copy_mode) { const uint16_t lin_count = f->depth - wr_ptr; const uint16_t wrap_count = n - lin_count; @@ -158,67 +135,51 @@ static void _ff_push_n(tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t w uint8_t *ff_buf = f->buffer + (wr_ptr * f->item_size); switch (copy_mode) { - case TU_FIFO_COPY_INC: + case TU_FIFO_INC_ADDR_RW8: if (n <= lin_count) { // Linear only memcpy(ff_buf, app_buf, n * f->item_size); } else { // Wrap around - - // Write data to linear part of buffer - memcpy(ff_buf, app_buf, lin_bytes); - - // Write data wrapped around - // TU_ASSERT(nWrap_bytes <= f->depth, ); - memcpy(f->buffer, ((const uint8_t *)app_buf) + lin_bytes, wrap_bytes); + memcpy(ff_buf, app_buf, lin_bytes); // linear part + memcpy(f->buffer, ((const uint8_t *)app_buf) + lin_bytes, wrap_bytes); // wrapped part } break; -#ifdef TUP_MEM_CONST_ADDR - case TU_FIFO_COPY_CST_FULL_WORDS: - // Intended for hardware buffers from which it can be read word by word only +#ifdef CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32 + case TU_FIFO_FIXED_ADDR_RW32: { + const volatile uint32_t *reg_rx = (volatile const uint32_t *)app_buf; if (n <= lin_count) { // Linear only - _ff_push_const_addr(ff_buf, app_buf, n * f->item_size); + ff_push_fixed_addr_rw32(ff_buf, reg_rx, n * f->item_size); } else { - // Wrap around case + // Wrap around // Write full words to linear part of buffer - uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC; - _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes); - ff_buf += nLin_4n_bytes; + uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; + ff_push_fixed_addr_rw32(ff_buf, reg_rx, lin_4n_bytes); + ff_buf += lin_4n_bytes; // There could be odd 1-3 bytes before the wrap-around boundary - uint8_t rem = lin_bytes & 0x03; + const uint8_t rem = lin_bytes & 0x03; if (rem > 0) { - const volatile uint32_t *rx_fifo = (volatile const uint32_t *)app_buf; + const uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem); + const uint32_t tmp32 = *reg_rx; + tu_scatter_write32(tmp32, ff_buf, rem, f->buffer, remrem); - uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem); wrap_bytes -= remrem; - - uint32_t tmp32 = *rx_fifo; - uint8_t *src_u8 = ((uint8_t *)&tmp32); - - // Write 1-3 bytes before wrapped boundary - while (rem--) { - *ff_buf++ = *src_u8++; - } - - // Read more bytes to beginning to complete a word - ff_buf = f->buffer; - while (remrem--) { - *ff_buf++ = *src_u8++; - } + ff_buf = f->buffer + remrem; // wrap around } else { ff_buf = f->buffer; // wrap around to beginning } // Write data wrapped part if (wrap_bytes > 0) { - _ff_push_const_addr(ff_buf, app_buf, wrap_bytes); + ff_push_fixed_addr_rw32(ff_buf, reg_rx, wrap_bytes); } } break; + } #endif default: @@ -226,13 +187,8 @@ static void _ff_push_n(tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t w } } -// get one item from fifo WITHOUT updating read pointer -static inline void _ff_pull(tu_fifo_t *f, void *app_buf, uint16_t rel) { - memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size); -} - // get n items from fifo WITHOUT updating read pointer -static void _ff_pull_n(tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode) { +static void ff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_access_mode_t copy_mode) { const uint16_t lin_count = f->depth - rd_ptr; const uint16_t wrap_count = n - lin_count; // only used if wrapped @@ -240,70 +196,56 @@ static void _ff_pull_n(tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, uint16_t wrap_bytes = wrap_count * f->item_size; // current buffer of fifo - uint8_t *ff_buf = f->buffer + (rd_ptr * f->item_size); + const uint8_t *ff_buf = f->buffer + (rd_ptr * f->item_size); switch (copy_mode) { - case TU_FIFO_COPY_INC: + case TU_FIFO_INC_ADDR_RW8: if (n <= lin_count) { // Linear only memcpy(app_buf, ff_buf, n * f->item_size); } else { // Wrap around - - // Read data from linear part of buffer - memcpy(app_buf, ff_buf, lin_bytes); - - // Read data wrapped part - memcpy((uint8_t *)app_buf + lin_bytes, f->buffer, wrap_bytes); + memcpy(app_buf, ff_buf, lin_bytes); // linear part + memcpy((uint8_t *)app_buf + lin_bytes, f->buffer, wrap_bytes); // wrapped part } break; -#ifdef TUP_MEM_CONST_ADDR - case TU_FIFO_COPY_CST_FULL_WORDS: +#ifdef CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32 + case TU_FIFO_FIXED_ADDR_RW32: { + volatile uint32_t *reg_tx = (volatile uint32_t *)app_buf; + if (n <= lin_count) { // Linear only - _ff_pull_const_addr(app_buf, ff_buf, n * f->item_size); + ff_pull_fixed_addr_rw32(reg_tx, ff_buf, n * f->item_size); } else { // Wrap around case - // Read full words from linear part of buffer + // Read full words from linear part uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; - _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes); + ff_pull_fixed_addr_rw32(reg_tx, ff_buf, lin_4n_bytes); ff_buf += lin_4n_bytes; // There could be odd 1-3 bytes before the wrap-around boundary - uint8_t rem = lin_bytes & 0x03; + const uint8_t rem = lin_bytes & 0x03; if (rem > 0) { - volatile uint32_t *reg_tx = (volatile uint32_t *)app_buf; + const uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem); + const uint32_t scatter32 = tu_scatter_read32(ff_buf, rem, f->buffer, remrem); - uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem); - wrap_bytes -= remrem; + *reg_tx = scatter32; - uint32_t tmp32 = 0; - uint8_t *dst_u8 = (uint8_t *)&tmp32; - - // Read 1-3 bytes before wrapped boundary - while (rem--) { - *dst_u8++ = *ff_buf++; - } - - // Read more bytes from beginning to complete a word - ff_buf = f->buffer; - while (remrem--) { - *dst_u8++ = *ff_buf++; - } - - *reg_tx = tmp32; + wrap_bytes -= remrem; + ff_buf = f->buffer + remrem; // wrap around } else { - ff_buf = f->buffer; // wrap around to beginning + ff_buf = f->buffer; // wrap around to beginning } // Read data wrapped part if (wrap_bytes > 0) { - _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes); + ff_pull_fixed_addr_rw32(reg_tx, ff_buf, wrap_bytes); } } break; + } #endif default: @@ -311,33 +253,13 @@ static void _ff_pull_n(tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, } } -//--------------------------------------------------------------------+ -// Helper -//--------------------------------------------------------------------+ - -// return only the index difference and as such can be used to determine an overflow i.e overflowable count -TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) { - // In case we have non-power of two depth we need a further modification - if (wr_idx >= rd_idx) { - return (uint16_t)(wr_idx - rd_idx); - } else { - return (uint16_t)(2 * depth - (rd_idx - wr_idx)); - } -} - -// return remaining slot in fifo -TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) { - const uint16_t count = _ff_count(depth, wr_idx, rd_idx); - return (depth > count) ? (depth - count) : 0; -} - //--------------------------------------------------------------------+ // Index Helper //--------------------------------------------------------------------+ // Advance an absolute index // "absolute" index is only in the range of [0..2*depth) -static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) { +TU_ATTR_ALWAYS_INLINE static inline uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) { // We limit the index space of p such that a correct wrap around happens // Check for a wrap around or if we are in unused index space - This has to be checked first!! // We are exploiting the wrap around to the correct index @@ -350,23 +272,7 @@ static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) { return new_idx; } -#if 0 // not used but -// Backward an absolute index -static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) { - // We limit the index space of p such that a correct wrap around happens - // Check for a wrap around or if we are in unused index space - This has to be checked first!! - // We are exploiting the wrap around to the correct index - uint16_t new_idx = (uint16_t) (idx - offset); - if ( (idx < new_idx) || (new_idx >= 2*depth) ) { - uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1)); - new_idx = (uint16_t) (new_idx - non_used_index_space); - } - - return new_idx; -} -#endif - -// index to pointer, simply an modulo with minus. +// index to pointer (0..depth-1), simply a modulo with minus. TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t idx) { // Only run at most 3 times since index is limit in the range of [0..2*depth) while (idx >= depth) { @@ -376,9 +282,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t id } // Works on local copies of w -// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms -// an full fifo i.e _ff_count() = depth -TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_correct_read_index(tu_fifo_t *f, uint16_t wr_idx) { +// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms a full fifo +TU_ATTR_ALWAYS_INLINE static inline uint16_t correct_read_index(tu_fifo_t *f, uint16_t wr_idx) { uint16_t rd_idx; if (wr_idx >= f->depth) { rd_idx = wr_idx - f->depth; @@ -387,91 +292,87 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_correct_read_index(tu_fifo_t *f } f->rd_idx = rd_idx; - return rd_idx; } -// Works on local copies of w and r -// Must be protected by mutexes since in case of an overflow read pointer gets modified -static bool _tu_fifo_peek(tu_fifo_t *f, void *p_buffer, uint16_t wr_idx, uint16_t rd_idx) { - uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); - - // nothing to peek - if (cnt == 0) { - return false; +// peek() using local write/read index. Be careful, caller must not lock mutex, since this Will also try to lock mutex +// in case of overflowed to correct read index +static bool ff_peek_local(tu_fifo_t *f, void *buf, uint16_t wr_idx, uint16_t rd_idx) { + const uint16_t ovf_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); + if (ovf_count == 0) { + return false; // nothing to peek } - // Check overflow and correct if required - if (cnt > f->depth) { - rd_idx = _ff_correct_read_index(f, wr_idx); + // Correct read index if overflow + if (ovf_count > f->depth) { + ff_lock(f->mutex_rd); + rd_idx = correct_read_index(f, wr_idx); + ff_unlock(f->mutex_rd); } - uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); - - // Peek data - _ff_pull(f, p_buffer, rd_ptr); + const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + memcpy(buf, f->buffer + (rd_ptr * f->item_size), f->item_size); return true; } +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + // Works on local copies of w and r // Must be protected by mutexes since in case of an overflow read pointer gets modified -static uint16_t _tu_fifo_peek_n( - tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode) { - uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); +uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, + tu_fifo_access_mode_t access_mode) { + uint16_t ovf_cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); - // nothing to peek - if (cnt == 0) { - return 0; + if (ovf_cnt == 0) { + return 0; // nothing to peek } // Check overflow and correct if required - if (cnt > f->depth) { - rd_idx = _ff_correct_read_index(f, wr_idx); - cnt = f->depth; + if (ovf_cnt > f->depth) { + rd_idx = correct_read_index(f, wr_idx); + ovf_cnt = f->depth; } - // Check if we can read something at and after offset - if too less is available we read what remains - if (cnt < n) { - n = cnt; + if (ovf_cnt < n) { + n = ovf_cnt; // limit to available count } - uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); - - // Peek data - _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode); + const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); + ff_pull_n(f, p_buffer, n, rd_ptr, access_mode); return n; } -static uint16_t _tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_copy_mode_t copy_mode) { +uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_access_mode_t access_mode) { if (n == 0) { return 0; } - _ff_lock(f->mutex_wr); + ff_lock(f->mutex_wr); uint16_t wr_idx = f->wr_idx; uint16_t rd_idx = f->rd_idx; const uint8_t *buf8 = (const uint8_t *)data; - TU_LOG( - TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx, - _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n); + TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx, + tu_ff_overflow_count(f->depth, wr_idx, rd_idx), tu_ff_remaining_local(f->depth, wr_idx, rd_idx), n); if (!f->overwritable) { // limit up to full - const uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx); + const uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx); n = tu_min16(n, remain); } else { // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case, - // oldest data in fifo i.e at read pointer data will be overwritten - // Note: we can modify read buffer contents but we must not modify the read index itself within a write function! - // Since it would end up in a race condition with read functions! + // oldest data in fifo i.e. at read pointer data will be overwritten + // Note: we can modify read buffer contents however we must not modify the read index itself within a write + // function! Since it would end up in a race condition with read functions! if (n >= f->depth) { // Only copy last part - if (copy_mode == TU_FIFO_COPY_INC) { + if (access_mode == TU_FIFO_INC_ADDR_RW8) { buf8 += (n - f->depth) * f->item_size; } else { // TODO should read from hw fifo to discard data, however reading an odd number could @@ -483,7 +384,7 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n, tu_ // We start writing at the read pointer's position since we fill the whole buffer wr_idx = rd_idx; } else { - const uint16_t overflowable_count = _ff_count(f->depth, wr_idx, rd_idx); + const uint16_t overflowable_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); if (overflowable_count + n >= 2 * f->depth) { // Double overflowed // Index is bigger than the allowed range [0,2*depth) @@ -504,121 +405,36 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n, tu_ } if (n) { - uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + const uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); - _ff_push_n(f, buf8, n, wr_ptr, copy_mode); + ff_push_n(f, buf8, n, wr_ptr, access_mode); f->wr_idx = advance_index(f->depth, wr_idx, n); TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx); } - _ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_wr); return n; } -static uint16_t _tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode) { - _ff_lock(f->mutex_rd); +uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_access_mode_t access_mode) { + ff_lock(f->mutex_rd); - // Peek the data - // f->rd_idx might get modified in case of an overflow so we can not use a local variable - n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); - - // Advance read pointer + // Peek the data: f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = tu_fifo_peek_n_access_mode(f, buffer, n, f->wr_idx, f->rd_idx, access_mode); f->rd_idx = advance_index(f->depth, f->rd_idx, n); - _ff_unlock(f->mutex_rd); + ff_unlock(f->mutex_rd); return n; } -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ - -/******************************************************************************/ -/*! - @brief Get number of items in FIFO. - - As this function only reads the read and write pointers once, this function is - reentrant and thus thread and ISR save without any mutexes. In case an - overflow occurred, this function return f.depth at maximum. Overflows are - checked and corrected for in the read functions! - - @param[in] f - Pointer to the FIFO buffer to manipulate - - @returns Number of items in FIFO - */ -/******************************************************************************/ -uint16_t tu_fifo_count(const tu_fifo_t *f) { - return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth); -} - -/******************************************************************************/ -/*! - @brief Check if FIFO is full. - - As this function only reads the read and write pointers once, this function is - reentrant and thus thread and ISR save without any mutexes. - - @param[in] f - Pointer to the FIFO buffer to manipulate - - @returns Number of items in FIFO - */ -/******************************************************************************/ -bool tu_fifo_full(const tu_fifo_t *f) { - return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; -} - -/******************************************************************************/ -/*! - @brief Get remaining space in FIFO. - - As this function only reads the read and write pointers once, this function is - reentrant and thus thread and ISR save without any mutexes. - - @param[in] f - Pointer to the FIFO buffer to manipulate - - @returns Number of items in FIFO - */ -/******************************************************************************/ -uint16_t tu_fifo_remaining(const tu_fifo_t *f) { - return _ff_remaining(f->depth, f->wr_idx, f->rd_idx); -} - -/******************************************************************************/ -/*! - @brief Check if overflow happened. - - BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS" - Only one overflow is allowed for this function to work e.g. if depth = 100, you must not - write more than 2*depth-1 items in one rush without updating write pointer. Otherwise - write pointer wraps and your pointer states are messed up. This can only happen if you - use DMAs, write functions do not allow such an error. Avoid such nasty things! - - All reading functions (read, peek) check for overflows and correct read pointer on their own such - that latest items are read. - If required (e.g. for DMA use) you can also correct the read pointer by - tu_fifo_correct_read_pointer(). - - @param[in] f - Pointer to the FIFO buffer to manipulate - - @returns True if overflow happened - */ -/******************************************************************************/ -bool tu_fifo_overflowed(const tu_fifo_t *f) { - return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth; -} - // Only use in case tu_fifo_overflow() returned true! void tu_fifo_correct_read_pointer(tu_fifo_t *f) { - _ff_lock(f->mutex_rd); - _ff_correct_read_index(f, f->wr_idx); - _ff_unlock(f->mutex_rd); + ff_lock(f->mutex_rd); + correct_read_index(f, f->wr_idx); + ff_unlock(f->mutex_rd); } /******************************************************************************/ @@ -638,62 +454,18 @@ void tu_fifo_correct_read_pointer(tu_fifo_t *f) { */ /******************************************************************************/ bool tu_fifo_read(tu_fifo_t *f, void *buffer) { - _ff_lock(f->mutex_rd); - // Peek the data // f->rd_idx might get modified in case of an overflow so we can not use a local variable - bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); - - // Advance pointer - f->rd_idx = advance_index(f->depth, f->rd_idx, ret); + const bool ret = ff_peek_local(f, buffer, f->wr_idx, f->rd_idx); + if (ret) { + ff_lock(f->mutex_rd); + f->rd_idx = advance_index(f->depth, f->rd_idx, 1); + ff_unlock(f->mutex_rd); + } - _ff_unlock(f->mutex_rd); return ret; } -/******************************************************************************/ -/*! - @brief This function will read n elements from the array index specified by - the read pointer and increment the read index. - This function checks for an overflow and corrects read pointer if required. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] buffer - The pointer to data location - @param[in] n - Number of element that buffer can afford - - @returns number of items read from the FIFO - */ -/******************************************************************************/ -uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n) { - return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC); -} - -#ifdef TUP_MEM_CONST_ADDR -/******************************************************************************/ -/*! - @brief This function will read n elements from the array index specified by - the read pointer and increment the read index. - This function checks for an overflow and corrects read pointer if required. - The dest address will not be incremented which is useful for writing to registers. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] buffer - The pointer to data location - @param[in] n - Number of element that buffer can afford - - @returns number of items read from the FIFO - */ -/******************************************************************************/ -uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t *f, void *buffer, uint16_t n) { - return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS); -} -#endif - /******************************************************************************/ /*! @brief Read one item without removing it from the FIFO. @@ -708,10 +480,7 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t *f, void *buffer, uint16 */ /******************************************************************************/ bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) { - _ff_lock(f->mutex_rd); - bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx); - _ff_unlock(f->mutex_rd); - return ret; + return ff_peek_local(f, p_buffer, f->wr_idx, f->rd_idx); } /******************************************************************************/ @@ -730,9 +499,9 @@ bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) { */ /******************************************************************************/ uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) { - _ff_lock(f->mutex_rd); - uint16_t ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC); - _ff_unlock(f->mutex_rd); + ff_lock(f->mutex_rd); + const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_INC_ADDR_RW8); + ff_unlock(f->mutex_rd); return ret; } @@ -753,64 +522,25 @@ uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) { */ /******************************************************************************/ bool tu_fifo_write(tu_fifo_t *f, const void *data) { - _ff_lock(f->mutex_wr); + bool ret; + ff_lock(f->mutex_wr); - bool ret; const uint16_t wr_idx = f->wr_idx; if (tu_fifo_full(f) && !f->overwritable) { ret = false; } else { - uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); - _ff_push(f, data, wr_ptr); + const uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + memcpy(f->buffer + (wr_ptr * f->item_size), data, f->item_size); f->wr_idx = advance_index(f->depth, wr_idx, 1); ret = true; } - _ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_wr); return ret; } -/******************************************************************************/ -/*! - @brief This function will write n elements into the array index specified by - the write pointer and increment the write index. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] data - The pointer to data to add to the FIFO - @param[in] count - Number of element - @return Number of written elements - */ -/******************************************************************************/ -uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n) { - return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC); -} - -#ifdef TUP_MEM_CONST_ADDR -/******************************************************************************/ -/*! - @brief This function will write n elements into the array index specified by - the write pointer and increment the write index. The source address will - not be incremented which is useful for reading from registers. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] data - The pointer to data to add to the FIFO - @param[in] count - Number of element - @return Number of written elements - */ -/******************************************************************************/ -uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t *f, const void *data, uint16_t n) { - return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS); -} -#endif - /******************************************************************************/ /*! @brief Clear the fifo read and write pointers @@ -820,14 +550,14 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t *f, const void *data, u */ /******************************************************************************/ bool tu_fifo_clear(tu_fifo_t *f) { - _ff_lock(f->mutex_wr); - _ff_lock(f->mutex_rd); + ff_lock(f->mutex_wr); + ff_lock(f->mutex_rd); f->rd_idx = 0; f->wr_idx = 0; - _ff_unlock(f->mutex_wr); - _ff_unlock(f->mutex_rd); + ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_rd); return true; } @@ -846,13 +576,13 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { return true; } - _ff_lock(f->mutex_wr); - _ff_lock(f->mutex_rd); + ff_lock(f->mutex_wr); + ff_lock(f->mutex_rd); f->overwritable = overwritable; - _ff_unlock(f->mutex_wr); - _ff_unlock(f->mutex_rd); + ff_unlock(f->mutex_wr); + ff_unlock(f->mutex_rd); return true; } @@ -917,23 +647,23 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { uint16_t wr_idx = f->wr_idx; uint16_t rd_idx = f->rd_idx; - uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + uint16_t cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); // Check overflow and correct if required - may happen in case a DMA wrote too fast if (cnt > f->depth) { - _ff_lock(f->mutex_rd); - rd_idx = _ff_correct_read_index(f, wr_idx); - _ff_unlock(f->mutex_rd); + ff_lock(f->mutex_rd); + rd_idx = correct_read_index(f, wr_idx); + ff_unlock(f->mutex_rd); cnt = f->depth; } // Check if fifo is empty if (cnt == 0) { - info->len_lin = 0; - info->len_wrap = 0; - info->ptr_lin = NULL; - info->ptr_wrap = NULL; + info->linear.len = 0; + info->wrapped.len = 0; + info->linear.ptr = NULL; + info->wrapped.ptr = NULL; return; } @@ -942,20 +672,20 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Copy pointer to buffer to start reading from - info->ptr_lin = &f->buffer[rd_ptr]; + info->linear.ptr = &f->buffer[rd_ptr]; // Check if there is a wrap around necessary if (wr_ptr > rd_ptr) { // Non wrapping case - info->len_lin = cnt; + info->linear.len = cnt; - info->len_wrap = 0; - info->ptr_wrap = NULL; + info->wrapped.len = 0; + info->wrapped.ptr = NULL; } else { - info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full + info->linear.len = f->depth - rd_ptr; // Also the case if FIFO was full - info->len_wrap = cnt - info->len_lin; - info->ptr_wrap = f->buffer; + info->wrapped.len = cnt - info->linear.len; + info->wrapped.ptr = f->buffer; } } @@ -977,13 +707,13 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { uint16_t wr_idx = f->wr_idx; uint16_t rd_idx = f->rd_idx; - uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx); + uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx); if (remain == 0) { - info->len_lin = 0; - info->len_wrap = 0; - info->ptr_lin = NULL; - info->ptr_wrap = NULL; + info->linear.len = 0; + info->wrapped.len = 0; + info->linear.ptr = NULL; + info->wrapped.ptr = NULL; return; } @@ -992,16 +722,16 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Copy pointer to buffer to start writing to - info->ptr_lin = &f->buffer[wr_ptr]; + info->linear.ptr = &f->buffer[wr_ptr]; if (wr_ptr < rd_ptr) { // Non wrapping case - info->len_lin = rd_ptr - wr_ptr; - info->len_wrap = 0; - info->ptr_wrap = NULL; + info->linear.len = rd_ptr - wr_ptr; + info->wrapped.len = 0; + info->wrapped.ptr = NULL; } else { - info->len_lin = f->depth - wr_ptr; - info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth - info->ptr_wrap = f->buffer; // Always start of buffer + info->linear.len = f->depth - wr_ptr; + info->wrapped.len = remain - info->linear.len; // Remaining length - n already was limited to remain or FIFO depth + info->wrapped.ptr = f->buffer; // Always start of buffer } } diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index 2fb4f37d4a..4d8448c44c 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -48,8 +48,13 @@ extern "C" { // for OS None, we don't get preempted #define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED -/* Write/Read index is always in the range of: - * 0 .. 2*depth-1 +#if CFG_TUD_EDPT_DEDICATED_HWFIFO || CFG_TUH_EDPT_DEDICATED_HWFIFO + #define CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32 +#endif + +/* Write/Read "pointer" is in the range of: 0 .. depth - 1, and is used to get the fifo data. + * Write/Read "index" is always in the range of: 0 .. 2*depth-1 + * * The extra window allow us to determine the fifo state of empty or full with only 2 indices * Following are examples with depth = 3 * @@ -123,10 +128,10 @@ typedef struct { } tu_fifo_t; typedef struct { - uint16_t len_lin ; ///< linear length in item size - uint16_t len_wrap ; ///< wrapped length in item size - void * ptr_lin ; ///< linear part start pointer - void * ptr_wrap ; ///< wrapped part start pointer + struct { + uint16_t len; // length + uint8_t *ptr; // buffer pointer + } linear, wrapped; } tu_fifo_buffer_info_t; #define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \ @@ -141,6 +146,16 @@ typedef struct { uint8_t _name##_buf[_depth*sizeof(_type)]; \ tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable) +// Write modes intended to allow special read and write functions to be able to +// copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others +typedef enum { + TU_FIFO_INC_ADDR_RW8, // increased address read/write by bytes - normal (default) mode + TU_FIFO_FIXED_ADDR_RW32, // fixed address read/write by 4 bytes (word). Used for STM32 access into USB hardware FIFO +} tu_fifo_access_mode_t; + +//--------------------------------------------------------------------+ +// Setup API +//--------------------------------------------------------------------+ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable); bool tu_fifo_clear(tu_fifo_t *f); bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); @@ -155,48 +170,98 @@ void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_m #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) #endif -bool tu_fifo_write(tu_fifo_t *f, void const *data); -uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n); - -bool tu_fifo_read(tu_fifo_t *f, void *buffer); -uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n); - -#ifdef TUP_MEM_CONST_ADDR -uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t *f, const void *data, uint16_t n); -uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t *f, void *buffer, uint16_t n); -#endif - +//--------------------------------------------------------------------+ +// Peek API +// peek() will correct/re-index read pointer in case of an overflowed fifo to form a full fifo +//--------------------------------------------------------------------+ +uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, + tu_fifo_access_mode_t access_mode); bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer); uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n); -uint16_t tu_fifo_count(const tu_fifo_t *f); -uint16_t tu_fifo_remaining(const tu_fifo_t *f); -bool tu_fifo_full(const tu_fifo_t *f); -bool tu_fifo_overflowed(const tu_fifo_t *f); - -TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) { - uint16_t wr_idx = f->wr_idx; - uint16_t rd_idx = f->rd_idx; - return wr_idx == rd_idx; +//--------------------------------------------------------------------+ +// Read API +// peek() + advance read index +//--------------------------------------------------------------------+ +uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_access_mode_t access_mode); +bool tu_fifo_read(tu_fifo_t *f, void *buffer); +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n) { + return tu_fifo_read_n_access_mode(f, buffer, n, TU_FIFO_INC_ADDR_RW8); } -TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_depth(const tu_fifo_t *f) { - return f->depth; +//--------------------------------------------------------------------+ +// Write API +//--------------------------------------------------------------------+ +uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_access_mode_t access_mode); +bool tu_fifo_write(tu_fifo_t *f, const void *data); +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n) { + return tu_fifo_write_n_access_mode(f, data, n, TU_FIFO_INC_ADDR_RW8); } +//--------------------------------------------------------------------+ +// Index API +//--------------------------------------------------------------------+ void tu_fifo_correct_read_pointer(tu_fifo_t *f); // Pointer modifications intended to be used in combinations with DMAs. // USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n); -void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n); +void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n); // If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies // to handle a possible wrapping part. These functions deliver a pointer to start // reading/writing from/to and a valid linear length along which no wrap occurs. -void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); +//--------------------------------------------------------------------+ +// Internal Helper Local +// work on local copies of read/write indices in order to only access them once for re-entrancy +//--------------------------------------------------------------------+ +// return overflowable count (index difference), which can be used to determine both fifo count and an overflow state +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_ff_overflow_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) { + if (wr_idx >= rd_idx) { + return (uint16_t)(wr_idx - rd_idx); + } else { + return (uint16_t)(2 * depth - (rd_idx - wr_idx)); + } +} + +// return remaining slot in fifo +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_ff_remaining_local(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) { + const uint16_t ovf_count = tu_ff_overflow_count(depth, wr_idx, rd_idx); + return (depth > ovf_count) ? (depth - ovf_count) : 0; +} + +//--------------------------------------------------------------------+ +// State API +// Following functions are reentrant since they only access read/write indices once, therefore can be used in thread and +// ISRs context without the need of mutexes +//--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_depth(const tu_fifo_t *f) { + return f->depth; +} + +TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) { + const uint16_t wr_idx = f->wr_idx; + const uint16_t rd_idx = f->rd_idx; + return wr_idx == rd_idx; +} + +// return number of items in fifo, capped to fifo's depth +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_count(const tu_fifo_t *f) { + return tu_min16(tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx), f->depth); +} + +// check if fifo is full +TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_full(const tu_fifo_t *f) { + return tu_ff_overflow_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_remaining(const tu_fifo_t *f) { + return tu_ff_remaining_local(f->depth, f->wr_idx, f->rd_idx); +} + #ifdef __cplusplus } #endif diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 002cd3a0e5..1e773bf968 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -24,8 +24,7 @@ * This file is part of the TinyUSB stack. */ -#ifndef TUSB_MCU_H_ -#define TUSB_MCU_H_ +#pragma once //--------------------------------------------------------------------+ // Port/Platform Specific @@ -524,6 +523,7 @@ //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_F1C100S) #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_EDPT_CLOSE_API //--------------------------------------------------------------------+ // WCH @@ -697,9 +697,3 @@ #ifndef TUP_DCD_EDPT_CLOSE_API #define TUP_DCD_EDPT_ISO_ALLOC #endif - -#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA_ENABLE == 0 - #define TUP_MEM_CONST_ADDR -#endif - -#endif diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index be1264a714..48fd1d6d2b 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -60,7 +60,7 @@ typedef struct { uint8_t ep_addr; uint16_t ep_bufsize; - uint8_t* ep_buf; // TODO xfer_fifo can skip this buffer + uint8_t *ep_buf; // set to NULL to use xfer_fifo when CFG_TUD_EDPT_DEDICATED_HWFIFO = 1 tu_fifo_t ff; // mutex: read if rx, otherwise write @@ -98,7 +98,7 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove // Deinit an endpoint stream bool tu_edpt_stream_deinit(tu_edpt_stream_t* s); -// Open an stream for an endpoint +// Open an endpoint stream TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_open(tu_edpt_stream_t* s, tusb_desc_endpoint_t const *desc_ep) { s->ep_addr = desc_ep->bEndpointAddress; s->is_mps512 = tu_edpt_packet_size(desc_ep) == 512; @@ -150,7 +150,7 @@ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s); // Complete read transfer by writing EP -> FIFO. Must be called in the transfer complete callback TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) { - if (0u != tu_fifo_depth(&s->ff)) { + if (0u != tu_fifo_depth(&s->ff) && s->ep_buf != NULL) { tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes); } } diff --git a/src/device/usbd.c b/src/device/usbd.c index d4dfae4b47..5e7d0ffa7d 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -140,7 +140,7 @@ typedef struct { }usbd_device_t; -tu_static usbd_device_t _usbd_dev; +static usbd_device_t _usbd_dev; static volatile uint8_t _usbd_queued_setup; //--------------------------------------------------------------------+ @@ -153,8 +153,8 @@ static volatile uint8_t _usbd_queued_setup; #endif // Built-in class drivers -tu_static usbd_class_driver_t const _usbd_driver[] = { - #if CFG_TUD_CDC +static const usbd_class_driver_t _usbd_driver[] = { + #if CFG_TUD_CDC { .name = DRIVER_NAME("CDC"), .init = cdcd_init, @@ -340,10 +340,10 @@ tu_static usbd_class_driver_t const _usbd_driver[] = { enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; // Additional class drivers implemented by application -tu_static usbd_class_driver_t const * _app_driver = NULL; -tu_static uint8_t _app_driver_count = 0; +static const usbd_class_driver_t *_app_driver = NULL; +static uint8_t _app_driver_count = 0; -#define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + BUILTIN_DRIVER_COUNT)) + #define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + BUILTIN_DRIVER_COUNT)) // virtually joins built-in and application drivers together. // Application is positioned first to allow overwriting built-in ones. @@ -365,8 +365,10 @@ TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8 //--------------------------------------------------------------------+ // DCD Event //--------------------------------------------------------------------+ -enum { RHPORT_INVALID = 0xFFu }; -tu_static uint8_t _usbd_rhport = RHPORT_INVALID; +enum { + RHPORT_INVALID = 0xFFu +}; +static uint8_t _usbd_rhport = RHPORT_INVALID; static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set); @@ -428,7 +430,7 @@ TU_ATTR_WEAK bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t // Debug //--------------------------------------------------------------------+ #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL -tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = { +static char const *const _usbd_event_str[DCD_EVENT_COUNT] = { "Invalid", "Bus Reset", "Unplugged", @@ -1472,6 +1474,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // success message. If total_bytes is too big, the FIFO will copy only what is available // into the USB buffer! bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes, bool is_isr) { + #if CFG_TUD_EDPT_DEDICATED_HWFIFO rhport = _usbd_rhport; uint8_t const epnum = tu_edpt_number(ep_addr); @@ -1479,7 +1482,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_ TU_LOG_USBD(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes); - // Attempt to transfer on a busy endpoint, sound like an race condition ! + // Attempt to transfer on a busy endpoint, sound like a race condition ! TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return @@ -1497,6 +1500,14 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_ TU_BREAKPOINT(); return false; } + #else + (void)rhport; + (void)ep_addr; + (void)ff; + (void)total_bytes; + (void)is_isr; + return false; + #endif } bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c index c6d405e986..4a5e5c91f5 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -545,19 +545,19 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ tu_fifo_get_write_info(ff, &fifo_info); } - if ( fifo_info.len_lin >= total_bytes ) + if ( fifo_info.linear.len >= total_bytes ) { // Linear length is enough for this transfer - qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes); + qtd_init(p_qtd, fifo_info.linear.ptr, total_bytes); } else { // linear part is not enough // prepare TD up to linear length - qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin); + qtd_init(p_qtd, fifo_info.linear.ptr, fifo_info.linear.len); - if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) ) + if ( !tu_offset4k((uint32_t) fifo_info.wrapped.ptr) && !tu_offset4k(tu_fifo_depth(ff)) ) { // If buffer is aligned to 4K & buffer size is multiple of 4K // We can make use of buffer page array to also combine the linear + wrapped length @@ -568,7 +568,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ // pick up buffer array where linear ends if (p_qtd->buffer[i] == 0) { - p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page; + p_qtd->buffer[i] = (uint32_t) fifo_info.wrapped.ptr + 4096 * page; page++; } } diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 1e4ec00159..ad20d64bd1 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -221,12 +221,12 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne tu_fifo_buffer_info_t info; ops[dir].tu_fifo_get_info(f, &info); unsigned total_len = len; - len = TU_MIN(total_len, info.len_lin); - ops[dir].pipe_read_write(info.ptr_lin, fifo, len); + len = TU_MIN(total_len, info.linear.len); + ops[dir].pipe_read_write(info.linear.ptr, fifo, len); unsigned rem = total_len - len; if (rem) { - len = TU_MIN(rem, info.len_wrap); - ops[dir].pipe_read_write(info.ptr_wrap, fifo, len); + len = TU_MIN(rem, info.wrapped.len); + ops[dir].pipe_read_write(info.wrapped.ptr, fifo, len); rem -= len; } ops[dir].tu_fifo_advance(f, total_len - rem); diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 149eee794f..1faac2aa8c 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -436,9 +436,8 @@ void dcd_int_handler(uint8_t rhport) { // write to EP fifo #if 0 // TODO support dcd_edpt_xfer_fifo - if (xfer->ff) - { - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len); + if (xfer->ff) { + tu_fifo_read_n_access_mode(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len, TU_FIFO_FIXED_ADDR_RW32); } else #endif @@ -471,9 +470,8 @@ void dcd_int_handler(uint8_t rhport) // Read from EP fifo #if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len); + if (xfer->ff) { + tu_fifo_write_n_access_mode(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len, TU_FIFO_FIXED_ADDR_RW32); } else #endif diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c index 4d54f90575..b0a053c012 100644 --- a/src/portable/microchip/samx7x/dcd_samx7x.c +++ b/src/portable/microchip/samx7x/dcd_samx7x.c @@ -697,7 +697,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ udd_dma_ctrl_wrap |= DEVDMACONTROL_END_TR_IT | DEVDMACONTROL_END_TR_EN; } else { tu_fifo_get_read_info(ff, &info); - if(info.len_wrap == 0) + if(info.wrapped.len == 0) { udd_dma_ctrl_lin |= DEVDMACONTROL_END_B_EN; } @@ -705,18 +705,18 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ } // Clean invalidate cache of linear part - CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_lin, 4), info.len_lin + 31); + CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.linear.ptr, 4), info.linear.len + 31); - USB_REG->DEVDMA[epnum - 1].DEVDMAADDRESS = (uint32_t)info.ptr_lin; - if (info.len_wrap) + USB_REG->DEVDMA[epnum - 1].DEVDMAADDRESS = (uint32_t)info.linear.ptr; + if (info.wrapped.len) { // Clean invalidate cache of wrapped part - CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_wrap, 4), info.len_wrap + 31); + CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.wrapped.ptr, 4), info.wrapped.len + 31); dma_desc[epnum - 1].next_desc = 0; - dma_desc[epnum - 1].buff_addr = (uint32_t)info.ptr_wrap; + dma_desc[epnum - 1].buff_addr = (uint32_t)info.wrapped.ptr; dma_desc[epnum - 1].chnl_ctrl = - udd_dma_ctrl_wrap | (info.len_wrap << DEVDMACONTROL_BUFF_LENGTH_Pos); + udd_dma_ctrl_wrap | (info.wrapped.len << DEVDMACONTROL_BUFF_LENGTH_Pos); // Clean cache of wrapped DMA descriptor CleanInValidateCache((uint32_t*)&dma_desc[epnum - 1], sizeof(dma_desc_t)); @@ -725,7 +725,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ } else { udd_dma_ctrl_lin |= DEVDMACONTROL_END_BUFFIT; } - udd_dma_ctrl_lin |= (info.len_lin << DEVDMACONTROL_BUFF_LENGTH_Pos); + udd_dma_ctrl_lin |= (info.linear.len << DEVDMACONTROL_BUFF_LENGTH_Pos); // Disable IRQs to have a short sequence // between read of EOT_STA and DMA enable uint32_t irq_state = __get_PRIMASK(); diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index 7c80f06d9e..91b8767182 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -193,9 +193,8 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) /* provided buffers are thankfully 32-bit aligned, allowing most data to be transferred as 32-bit */ #if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now); + if (xfer->ff) { + tu_fifo_read_n_access_mode(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now, TU_FIFO_FIXED_ADDR_RW32); } else #endif @@ -696,15 +695,14 @@ void dcd_int_handler(uint8_t rhport) uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk; /* copy the data from the PC to the previously provided buffer */ #if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far)); + if (xfer->ff) { + tu_fifo_write_n_access_mode(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far), TU_FIFO_FIXED_ADDR_RW32); } else #endif { - for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++) - { + for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); + count++, xfer->out_bytes_so_far++) { *xfer->data_ptr++ = ep->EPDAT_BYTE; } } diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index 7caf5d68a2..786b8d980d 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -213,13 +213,13 @@ static void pipe_write_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void tu_fifo_buffer_info_t info; tu_fifo_get_read_info(f, &info); - uint16_t count = tu_min16(total_len, info.len_lin); - pipe_write_packet(rusb, info.ptr_lin, fifo, count); + uint16_t count = tu_min16(total_len, info.linear.len); + pipe_write_packet(rusb, info.linear.ptr, fifo, count); uint16_t rem = total_len - count; if (rem) { - rem = tu_min16(rem, info.len_wrap); - pipe_write_packet(rusb, info.ptr_wrap, fifo, rem); + rem = tu_min16(rem, info.wrapped.len); + pipe_write_packet(rusb, info.wrapped.ptr, fifo, rem); count += rem; } @@ -231,13 +231,13 @@ static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void tu_fifo_buffer_info_t info; tu_fifo_get_write_info(f, &info); - uint16_t count = tu_min16(total_len, info.len_lin); - pipe_read_packet(rusb, info.ptr_lin, fifo, count); + uint16_t count = tu_min16(total_len, info.linear.len); + pipe_read_packet(rusb, info.linear.ptr, fifo, count); uint16_t rem = total_len - count; if (rem) { - rem = tu_min16(rem, info.len_wrap); - pipe_read_packet(rusb, info.ptr_wrap, fifo, rem); + rem = tu_min16(rem, info.wrapped.len); + pipe_read_packet(rusb, info.wrapped.ptr, fifo, rem); count += rem; } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 6276f0f077..64046ce173 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -927,15 +927,15 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNB tu_fifo_buffer_info_t info; tu_fifo_get_read_info(ff, &info); - uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); - uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); + uint16_t cnt_lin = tu_min16(wNBytes, info.linear.len); + uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.wrapped.len); uint16_t const cnt_total = cnt_lin + cnt_wrap; // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, // last lin byte will be combined with wrapped part To ensure PMA is always access aligned uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); - uint8_t const *src8 = (uint8_t const*) info.ptr_lin; + uint8_t const *src8 = (uint8_t const*) info.linear.ptr; // write even linear part dcd_write_packet_memory(dst, src8, lin_even); @@ -943,7 +943,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNB src8 += lin_even; if (lin_odd == 0) { - src8 = (uint8_t const*) info.ptr_wrap; + src8 = (uint8_t const*) info.wrapped.ptr; } else { // Combine last linear bytes + first wrapped bytes to form fsdev bus width data fsdev_bus_t temp = 0; @@ -952,7 +952,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNB temp |= *src8++ << (i * 8); } - src8 = (uint8_t const*) info.ptr_wrap; + src8 = (uint8_t const*) info.wrapped.ptr; for(; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { temp |= *src8++ << (i * 8); } @@ -977,8 +977,8 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBy tu_fifo_buffer_info_t info; tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO - uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); - uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); + uint16_t cnt_lin = tu_min16(wNBytes, info.linear.len); + uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.wrapped.len); uint16_t cnt_total = cnt_lin + cnt_wrap; // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, @@ -986,7 +986,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBy uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); - uint8_t *dst8 = (uint8_t *) info.ptr_lin; + uint8_t *dst8 = (uint8_t *) info.linear.ptr; // read even linear part dcd_read_packet_memory(dst8, src, lin_even); @@ -994,7 +994,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBy src += lin_even; if (lin_odd == 0) { - dst8 = (uint8_t *) info.ptr_wrap; + dst8 = (uint8_t *) info.wrapped.ptr; } else { // Combine last linear bytes + first wrapped bytes to form fsdev bus width data fsdev_bus_t temp; @@ -1007,7 +1007,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBy temp >>= 8; } - dst8 = (uint8_t *) info.ptr_wrap; + dst8 = (uint8_t *) info.wrapped.ptr; for (; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { *dst8++ = (uint8_t) (temp & 0xfful); temp >>= 8; diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index d43ea1dc3c..b413121a5c 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -535,12 +535,12 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne tu_fifo_buffer_info_t info; ops[dir].tu_fifo_get_info(f, &info); unsigned total_len = len; - len = TU_MIN(total_len, info.len_lin); - ops[dir].pipe_read_write(info.ptr_lin, fifo, len); + len = TU_MIN(total_len, info.linear.len); + ops[dir].pipe_read_write(info.linear.ptr, fifo, len); unsigned rem = total_len - len; if (rem) { - len = TU_MIN(rem, info.len_wrap); - ops[dir].pipe_read_write(info.ptr_wrap, fifo, len); + len = TU_MIN(rem, info.wrapped.len); + ops[dir].pipe_read_write(info.wrapped.ptr, fifo, len); rem -= len; } ops[dir].tu_fifo_advance(f, total_len - rem); diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index e99cd29c67..00e81217b4 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -362,7 +362,7 @@ static uint16_t epin_write_tx_fifo(uint8_t rhport, uint8_t epnum) { // Push packet to Tx-FIFO if (xfer->ff) { volatile uint32_t* tx_fifo = dwc2->fifo[epnum]; - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes); + tu_fifo_read_n_access_mode(xfer->ff, (void *)(uintptr_t)tx_fifo, xact_bytes, TU_FIFO_FIXED_ADDR_RW32); total_bytes_written += xact_bytes; } else { dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes); @@ -878,7 +878,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { if (byte_count != 0) { // Read packet off RxFIFO if (xfer->ff != NULL) { - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count); + tu_fifo_write_n_access_mode(xfer->ff, (const void *)(uintptr_t)rx_fifo, byte_count, TU_FIFO_FIXED_ADDR_RW32); } else { dfifo_read_packet(dwc2, xfer->buffer, byte_count); xfer->buffer += byte_count; diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index 9da8de41f6..516eb021b5 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -337,6 +337,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_ } TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uintptr_t addr) { + if (0 == (SCB->CCR & SCB_CCR_DC_Msk)) { + return false; // D-Cache is disabled + } for (unsigned int i = 0; i < TU_ARRAY_SIZE(uncached_regions); i++) { if (uncached_regions[i].start <= addr && addr <= uncached_regions[i].end) { return false; } } diff --git a/src/tusb.c b/src/tusb.c index 3852da76b0..c589e105e7 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -387,8 +387,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool stream_xfer(uint8_t hwid, tu_edpt_strea #endif } else { #if CFG_TUD_ENABLED - return usbd_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count, false); - #endif + if (s->ep_buf == NULL) { + return usbd_edpt_xfer_fifo(hwid, s->ep_addr, &s->ff, count, false); + } else { + return usbd_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count, false); + } + #endif } return false; } @@ -419,12 +423,17 @@ bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint3 } uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) { - // skip if no data - TU_VERIFY(tu_fifo_count(&s->ff) > 0, 0); + const uint16_t ff_count = tu_fifo_count(&s->ff); + TU_VERIFY(ff_count > 0, 0); // skip if no data TU_VERIFY(stream_claim(hwid, s), 0); // Pull data from FIFO -> EP buf - const uint16_t count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + uint16_t count; + if (s->ep_buf == NULL) { + count = ff_count; + } else { + count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + } if (count > 0) { TU_ASSERT(stream_xfer(hwid, s, count), 0); @@ -441,7 +450,8 @@ uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t *s, const void *buf TU_VERIFY(bufsize > 0); // TODO support ZLP if (0 == tu_fifo_depth(&s->ff)) { - // no fifo for buffered + // non-fifo mode, ep_buf must be valid + TU_VERIFY(s->ep_buf != NULL, 0); TU_VERIFY(stream_claim(hwid, s), 0); const uint32_t xact_len = tu_min32(bufsize, s->ep_bufsize); memcpy(s->ep_buf, buffer, xact_len); @@ -464,6 +474,7 @@ uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) { if (tu_fifo_depth(&s->ff) > 0) { return (uint32_t) tu_fifo_remaining(&s->ff); } else { + // non-fifo mode bool is_busy = true; if (s->is_host) { #if CFG_TUH_ENABLED @@ -483,7 +494,7 @@ uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) { //--------------------------------------------------------------------+ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) { if (0 == tu_fifo_depth(&s->ff)) { - // no fifo for buffered + // non-fifo mode TU_VERIFY(stream_claim(hwid, s), 0); TU_ASSERT(stream_xfer(hwid, s, s->ep_bufsize), 0); return s->ep_bufsize; @@ -517,7 +528,15 @@ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) { } uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) { - const uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); + uint32_t num_read; + if (tu_fifo_depth(&s->ff) > 0) { + num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize); + } else { + // non-fifo mode + memcpy(buffer, s->ep_buf, bufsize); + num_read = bufsize; + } + tu_edpt_stream_read_xfer(hwid, s); return num_read; } diff --git a/src/tusb_option.h b/src/tusb_option.h index c8265f8985..eb072faabf 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -24,8 +24,7 @@ * This file is part of the TinyUSB stack. */ -#ifndef TUSB_OPTION_H_ -#define TUSB_OPTION_H_ +#pragma once #include "common/tusb_compiler.h" @@ -268,15 +267,17 @@ // USBIP //--------------------------------------------------------------------+ +//------------- DWC2 -------------// +// Slave mode for device #ifndef CFG_TUD_DWC2_SLAVE_ENABLE #ifndef CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT - #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT 1 + #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT 1 #endif #define CFG_TUD_DWC2_SLAVE_ENABLE CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT #endif -// Enable DWC2 DMA for device +// DMA for device #ifndef CFG_TUD_DWC2_DMA_ENABLE #ifndef CFG_TUD_DWC2_DMA_ENABLE_DEFAULT #define CFG_TUD_DWC2_DMA_ENABLE_DEFAULT 0 @@ -285,33 +286,46 @@ #define CFG_TUD_DWC2_DMA_ENABLE CFG_TUD_DWC2_DMA_ENABLE_DEFAULT #endif -// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is insufficient). -#ifndef CFG_TUD_CI_HS_VBUS_CHARGE - #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT - #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0 - #endif - - #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT -#endif - -// Enable DWC2 Slave mode for host +// Slave mode for host #ifndef CFG_TUH_DWC2_SLAVE_ENABLE #ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT - #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT 1 + #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT 1 #endif #define CFG_TUH_DWC2_SLAVE_ENABLE CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT #endif -// Enable DWC2 DMA for host +// DMA for host #ifndef CFG_TUH_DWC2_DMA_ENABLE #ifndef CFG_TUH_DWC2_DMA_ENABLE_DEFAULT - #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 + #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 + #endif + + #define CFG_TUH_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE_DEFAULT +#endif + +#if defined(TUP_USBIP_DWC2) + #if CFG_TUD_DWC2_SLAVE_ENABLE && !CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 + #endif + + #if CFG_TUD_DWC2_SLAVE_ENABLE && !CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUH_EDPT_DEDICATED_HWFIFO 1 #endif +#endif - #define CFG_TUH_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE_DEFAULT +//------------- ChipIdea -------------// +// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is +// insufficient). +#ifndef CFG_TUD_CI_HS_VBUS_CHARGE + #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT + #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0 + #endif + + #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT #endif +//------------- pio-usb -------------// // Enable PIO-USB software host controller #ifndef CFG_TUH_RPI_PIO_USB #define CFG_TUH_RPI_PIO_USB 0 @@ -326,7 +340,6 @@ #define CFG_TUH_MAX3421 0 #endif - //-------------------------------------------------------------------- // RootHub Mode detection //-------------------------------------------------------------------- @@ -573,6 +586,10 @@ #define CFG_TUD_NCM 0 #endif +#ifndef CFG_TUD_EDPT_DEDICATED_HWFIFO + #define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 +#endif + //-------------------------------------------------------------------- // Host Options (Default) //-------------------------------------------------------------------- @@ -712,6 +729,10 @@ #define CFG_TUH_API_EDPT_XFER 0 #endif +#ifndef CFG_TUH_EDPT_DEDICATED_HWFIFO + #define CFG_TUH_EDPT_DEDICATED_HWFIFO 0 +#endif + //--------------------------------------------------------------------+ // TypeC Options (Default) //--------------------------------------------------------------------+ @@ -731,7 +752,3 @@ // To avoid GCC compiler warnings when -pedantic option is used (strict ISO C) typedef int make_iso_compilers_happy; - -#endif /* TUSB_OPTION_H_ */ - -/** @} */ diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 3a11cee13c..ba0826bd38 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -32,6 +32,13 @@ import re import sys import time +import warnings + +# Suppress pkg_resources deprecation warning from fs module +warnings.filterwarnings("ignore", message="pkg_resources is deprecated") +# Suppress pyfatfs unclean unmount warning +warnings.filterwarnings("ignore", message="Filesystem was not cleanly unmounted") + import serial import subprocess import json @@ -41,6 +48,7 @@ import hashlib import ctypes from pymtp import MTP +import string ENUM_TIMEOUT = 30 @@ -50,6 +58,7 @@ verbose = False test_only = [] +build_dir = 'cmake-build' WCH_RISCV_CONTENT = """ adapter driver wlinke @@ -395,41 +404,71 @@ def test_device_cdc_dual_ports(board): ] ser = [open_serial_dev(p) for p in port] - str_test = [ b"test_no1", b"test_no2" ] - # Echo test write to each port and read back - for i in range(len(str_test)): - s = str_test[i] - l = len(s) - ser[i].write(s) - ser[i].flush() - rd = [ ser[i].read(l) for i in range(len(ser)) ] - assert rd[0] == s.lower(), f'Port1 wrong data: expected {s.lower()} was {rd[0]}' - assert rd[1] == s.upper(), f'Port2 wrong data: expected {s.upper()} was {rd[1]}' + def rand_ascii(length): + return "".join(random.choices(string.ascii_letters + string.digits, k=length)).encode("ascii") + + sizes = [32, 64, 128, 256, 512, random.randint(2000, 5000)] + + def write_and_check(writer, payload): + size = len(payload) + for s in ser: + s.reset_input_buffer() + rd0 = b'' + rd1 = b'' + offset = 0 + # Write in chunks of random 1-64 bytes (device has 64-byte buffer) + while offset < size: + chunk_size = min(random.randint(1, 64), size - offset) + ser[writer].write(payload[offset:offset + chunk_size]) + ser[writer].flush() + rd0 += ser[0].read(chunk_size) + rd1 += ser[1].read(chunk_size) + offset += chunk_size + assert rd0 == payload.lower(), f'Port0 wrong data ({size}): expected {payload.lower()[:16]}... was {rd0[:16]}' + assert rd1 == payload.upper(), f'Port1 wrong data ({size}): expected {payload.upper()[:16]}... was {rd1[:16]}' + + for size in sizes: + payload0 = rand_ascii(size) + write_and_check(0, payload0) + + payload1 = rand_ascii(size) + write_and_check(1, payload1) ser[0].close() ser[1].close() def test_device_cdc_msc(board): uid = board['uid'] - # Echo test + # CDC Echo test port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0) ser = open_serial_dev(port) - test_str = b"test_str" - ser.write(test_str) - ser.flush() - rd_str = ser.read(len(test_str)) + def rand_ascii(length): + return "".join(random.choices(string.ascii_letters + string.digits, k=length)).encode("ascii") + + sizes = [32, 64, 128, 256, 512, random.randint(2000, 5000)] + for size in sizes: + test_str = rand_ascii(size) + rd_str = b'' + offset = 0 + # Write in chunks of random 1-64 bytes (device has 64-byte buffer) + while offset < size: + chunk_size = min(random.randint(1, 64), size - offset) + ser.write(test_str[offset:offset + chunk_size]) + ser.flush() + rd_str += ser.read(chunk_size) + offset += chunk_size + assert rd_str == test_str, f'CDC wrong data ({size} bytes):\n expected: {test_str}\n received: {rd_str}' ser.close() - assert rd_str == test_str, f'CDC wrong data: expected: {test_str} was {rd_str}' - # Block test - data = read_disk_file(uid,0,'README.TXT') + # MSC Block test + data = read_disk_file(uid, 0, 'README.TXT') readme = \ - b"This is tinyusb's MassStorage Class demo.\r\n\r\n\ + b"This is tinyusb's MassStorage Class demo.\r\n\r\n\ If you find any bugs or get any questions, feel free to file an\r\n\ issue at github.com/hathach/tinyusb" - assert data == readme, 'MSC wrong data' + assert data == readme, f'MSC wrong data in README.TXT\n expected: {readme.decode()}\n received: {data.decode()}' def test_device_cdc_msc_freertos(board): @@ -611,9 +650,7 @@ def test_example(board, f1, example): if f1 != "": f1_str = '-f1_' + f1.replace(' ', '_') - fw_dir = f'{TINYUSB_ROOT}/cmake-build/cmake-build-{name}{f1_str}/{example}' - if not os.path.exists(fw_dir): - fw_dir = f'{TINYUSB_ROOT}/examples/cmake-build-{name}{f1_str}/{example}' + fw_dir = f'{TINYUSB_ROOT}/{build_dir}/cmake-build-{name}{f1_str}/{example}' fw_name = f'{fw_dir}/{os.path.basename(example)}' print(f'{name+f1_str:40} {example:30} ...', end='') @@ -625,7 +662,7 @@ def test_example(board, f1, example): print(f'Flashing {fw_name}.elf') # flash firmware. It may fail randomly, retry a few times - max_rety = 3 + max_rety = 1 start_s = time.time() for i in range(max_rety): ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name) @@ -701,6 +738,7 @@ def main(): """ global verbose global test_only + global build_dir duration = time.time() @@ -709,6 +747,7 @@ def main(): parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified') parser.add_argument('-s', '--skip', action='append', default=[], help='Skip boards from test') parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified') + parser.add_argument('-B', '--build', default='cmake-build', help='Build folder name (default: cmake-build)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') args = parser.parse_args() @@ -717,6 +756,7 @@ def main(): skip_boards = args.skip verbose = args.verbose test_only = args.test_only + build_dir = args.build # if config file is not found, try to find it in the same directory as this script if not os.path.exists(config_file): diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt new file mode 100644 index 0000000000..7172f5575a --- /dev/null +++ b/test/unit-test/CMakeLists.txt @@ -0,0 +1,132 @@ +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//. +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 + "" + ) +target_compile_definitions(test_fifo PRIVATE CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32=1) + +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/device/usbd_control.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/device/usbd_control.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() diff --git a/test/unit-test/project.yml b/test/unit-test/project.yml index 6c86b0205e..d971d098d2 100644 --- a/test/unit-test/project.yml +++ b/test/unit-test/project.yml @@ -128,6 +128,7 @@ :defines: :test: - _UNITY_TEST_ + - CFG_TUSB_FIFO_ACCESS_FIXED_ADDR_RW32 :release: [] # Enable to inject name of a test as a unique compilation symbol into its respective executable build. diff --git a/test/unit-test/test/test_common_func.c b/test/unit-test/test/test_common_func.c index 981531dd72..8afcc5b2ba 100644 --- a/test/unit-test/test/test_common_func.c +++ b/test/unit-test/test/test_common_func.c @@ -80,3 +80,113 @@ void test_TU_ARGS_NUM(void) 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]); +} diff --git a/test/unit-test/test/test_fifo.c b/test/unit-test/test/test_fifo.c index 3b4deb33e4..35bbeaa62d 100644 --- a/test/unit-test/test/test_fifo.c +++ b/test/unit-test/test/test_fifo.c @@ -30,51 +30,52 @@ #include "osal/osal.h" #include "tusb_fifo.h" -#define FIFO_SIZE 64 -uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)]; +#define FIFO_SIZE 64 +uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)]; tu_fifo_t tu_ff = TU_FIFO_INIT(tu_ff_buf, FIFO_SIZE, uint8_t, false); -tu_fifo_t* ff = &tu_ff; +tu_fifo_t *ff = &tu_ff; tu_fifo_buffer_info_t info; uint8_t test_data[4096]; uint8_t rd_buf[FIFO_SIZE]; -void setUp(void) -{ +void setUp(void) { tu_fifo_clear(ff); memset(&info, 0, sizeof(tu_fifo_buffer_info_t)); - for(int i=0; i 4 rd_count = tu_fifo_read_n(&ff4, rd_buf4, 5); - TEST_ASSERT_EQUAL( 5, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data4, rd_buf4, rd_count ); // 0 -> 4 + TEST_ASSERT_EQUAL(5, rd_count); + TEST_ASSERT_EQUAL_UINT32_ARRAY(data4, rd_buf4, rd_count); // 0 -> 4 - tu_fifo_write_n(&ff4, data4+FIFO_SIZE, 5); + tu_fifo_write_n(&ff4, data4 + FIFO_SIZE, 5); // read all 5 -> 68 rd_count = tu_fifo_read_n(&ff4, rd_buf4, FIFO_SIZE); - TEST_ASSERT_EQUAL( FIFO_SIZE, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data4+5, rd_buf4, rd_count ); // 5 -> 68 + TEST_ASSERT_EQUAL(FIFO_SIZE, rd_count); + TEST_ASSERT_EQUAL_UINT32_ARRAY(data4 + 5, rd_buf4, rd_count); // 5 -> 68 } -void test_read_n(void) -{ +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); + 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 + 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); + 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(7, rd_count); - TEST_ASSERT_EQUAL_MEMORY( test_data+5, rd_buf, rd_count ); // 5 -> 11 + 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) ); + TEST_ASSERT_EQUAL(FIFO_SIZE - 5 + 3 - 7, tu_fifo_read_n(ff, rd_buf, 100)); } -void test_write_n(void) -{ +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 ); + 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_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); + 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) -{ +void test_write_double_overflowed(void) { tu_fifo_set_overwritable(ff, true); - uint8_t rd_buf[FIFO_SIZE] = { 0 }; - uint8_t* buf = test_data; + 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); + 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 @@ -165,14 +165,13 @@ void test_write_double_overflowed(void) // 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); + 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) -{ +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); @@ -182,8 +181,7 @@ static uint16_t help_write(uint16_t total, uint16_t n) return total; } -void test_write_overwritable2(void) -{ +void test_write_overwritable2(void) { tu_fifo_set_overwritable(ff, true); // based on actual crash tests detected by fuzzing @@ -202,13 +200,15 @@ void test_write_overwritable2(void) total = help_write(total, 192); } -void test_peek(void) -{ +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 = 10; + tu_fifo_write(ff, &temp); + temp = 20; + tu_fifo_write(ff, &temp); + temp = 30; + tu_fifo_write(ff, &temp); temp = 0; @@ -222,12 +222,13 @@ void test_peek(void) TEST_ASSERT_EQUAL(30, temp); } -void test_get_read_info_when_no_wrap() -{ +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); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_write(ff, &ch); + } // read 2 items tu_fifo_read(ff, &ch); @@ -235,22 +236,25 @@ void test_get_read_info_when_no_wrap() tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(4, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(4, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info.ptr_lin); - TEST_ASSERT_NULL(info.ptr_wrap); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr); + TEST_ASSERT_NULL(info.wrapped.ptr); } -void test_get_read_info_when_wrapped() -{ +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); + 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); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_read(ff, &ch); + } // write 2 items tu_fifo_write(ff, &ch); @@ -258,15 +262,14 @@ void test_get_read_info_when_wrapped() tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); - TEST_ASSERT_EQUAL(2, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 6, info.linear.len); + TEST_ASSERT_EQUAL(2, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info.ptr_lin); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); + 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() -{ +void test_get_write_info_when_no_wrap() { uint8_t ch = 1; // write 2 items @@ -275,20 +278,21 @@ void test_get_write_info_when_no_wrap() tu_fifo_get_write_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-2, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 2, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info .ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr); // application should check len instead of ptr. - // TEST_ASSERT_NULL(info.ptr_wrap); + // TEST_ASSERT_NULL(info.wrapped.ptr); } -void test_get_write_info_when_wrapped() -{ +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); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_write(ff, &ch); + } // read 2 items tu_fifo_read(ff, &ch); @@ -296,68 +300,67 @@ void test_get_write_info_when_wrapped() tu_fifo_get_write_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); - TEST_ASSERT_EQUAL(2, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 6, info.linear.len); + TEST_ASSERT_EQUAL(2, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info .ptr_lin); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 6, info.linear.ptr); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr); } -void test_empty(void) -{ +void test_empty(void) { uint8_t temp; TEST_ASSERT_TRUE(tu_fifo_empty(ff)); // read info tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(0, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(0, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_NULL(info.ptr_lin); - TEST_ASSERT_NULL(info.ptr_wrap); + 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.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info .ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr); // application should check len instead of ptr. - // TEST_ASSERT_NULL(info.ptr_wrap); + // 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) -{ +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); + for (uint8_t i = 0; i < FIFO_SIZE; i++) { + tu_fifo_write(ff, &i); + } TEST_ASSERT_TRUE(tu_fifo_full(ff)); // read info tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr); // skip this, application must check len instead of buffer - // TEST_ASSERT_NULL(info.ptr_wrap); + // TEST_ASSERT_NULL(info.wrapped.ptr); // write info } -void test_rd_idx_wrap() -{ +void test_rd_idx_wrap(void) { tu_fifo_t ff10; - uint8_t buf[10]; - uint8_t dst[10]; + uint8_t buf[10]; + uint8_t dst[10]; tu_fifo_config(&ff10, buf, 10, 1, 1); @@ -376,3 +379,187 @@ void test_rd_idx_wrap() 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 *)®, n, TU_FIFO_FIXED_ADDR_RW32); + 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 *)®, n, TU_FIFO_FIXED_ADDR_RW32); + 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, ®, n, TU_FIFO_FIXED_ADDR_RW32); + 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, ®, n, TU_FIFO_FIXED_ADDR_RW32); + 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_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); +}