diff --git a/.codespell/exclude-file.txt b/.codespell/exclude-file.txt new file mode 100644 index 000000000..af8265cc7 --- /dev/null +++ b/.codespell/exclude-file.txt @@ -0,0 +1 @@ + return USB0.INTSTS1.BIT.ATTCH ? true : false; diff --git a/.codespell/ignore-words.txt b/.codespell/ignore-words.txt new file mode 100644 index 000000000..e37d97a9f --- /dev/null +++ b/.codespell/ignore-words.txt @@ -0,0 +1,8 @@ +synopsys +sie +tre +hsi +fro +dout +mot +te diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..8c32ed706 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,10 @@ +# See: https://github.com/codespell-project/codespell#using-a-config-file +[codespell] +# In the event of a false positive, add the problematic word, in all lowercase, to 'ignore-words.txt' (one word per line). +# Or copy & paste the whole problematic line to 'exclude-file.txt' +ignore-words = .codespell/ignore-words.txt +exclude-file = .codespell/exclude-file.txt +check-filenames = +check-hidden = +count = +skip = .cproject,./.git,./hw/mcu,./lib,./examples/*/*/_build,./examples/*/*/ses,./examples/*/*/ozone,./hw/mcu,./test/unit-test/vendor,./tests_obsolete,./tools/uf2 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 2958d3b12..8c39b95e1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -64,7 +64,7 @@ body: placeholder: | Attach your debug log txt file here, where the issue occurred, best with comments to explain the actual events. - Note1: Please DO NOT paste your lengthy log contents here since it hurts the readibility. + Note1: Please DO NOT paste your lengthy log contents here since it hurts the readability. Note2: To enable logging, add `LOG=3` to to the make command if building with stock examples or set `CFG_TUSB_DEBUG=3` in your tusb_config.h. More information can be found at [example's readme](https://github.com/hathach/tinyusb/blob/master/docs/getting_started.md) validations: diff --git a/.github/workflows/build_aarch64.yml b/.github/workflows/build_aarch64.yml index af26f33f2..c552a9574 100644 --- a/.github/workflows/build_aarch64.yml +++ b/.github/workflows/build_aarch64.yml @@ -1,11 +1,23 @@ name: Build AArch64 on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: # --------------------------------------- diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml index 369048fc6..c16fc0d45 100644 --- a/.github/workflows/build_arm.yml +++ b/.github/workflows/build_arm.yml @@ -1,34 +1,25 @@ name: Build ARM on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: - # --------------------------------------- - # Unit testing with Ceedling - # --------------------------------------- - unit-test: - runs-on: ubuntu-latest - steps: - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - - - name: Checkout TinyUSB - uses: actions/checkout@v3 - - - name: Unit Tests - run: | - # Install Ceedling - gem install ceedling - cd test - ceedling test:all - # --------------------------------------- # Build ARM family # --------------------------------------- diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml index 51d91de49..2e2859c95 100644 --- a/.github/workflows/build_esp.yml +++ b/.github/workflows/build_esp.yml @@ -1,11 +1,23 @@ name: Build ESP on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: build-esp: diff --git a/.github/workflows/build_msp430.yml b/.github/workflows/build_msp430.yml index 1ef25a6a0..7e1c5b128 100644 --- a/.github/workflows/build_msp430.yml +++ b/.github/workflows/build_msp430.yml @@ -1,11 +1,23 @@ name: Build MSP430 on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: build-msp430: diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml index 2e272c832..d9254dea8 100644 --- a/.github/workflows/build_renesas.yml +++ b/.github/workflows/build_renesas.yml @@ -1,11 +1,23 @@ name: Build Renesas on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: build-rx: diff --git a/.github/workflows/build_riscv.yml b/.github/workflows/build_riscv.yml index 61c1c60c5..66f4fd153 100644 --- a/.github/workflows/build_riscv.yml +++ b/.github/workflows/build_riscv.yml @@ -1,11 +1,23 @@ name: Build RISC-V on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: build-riscv: @@ -15,6 +27,7 @@ jobs: matrix: family: # Alphabetical order + - 'ch32v307' - 'fomu' - 'gd32vf103' steps: diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 000000000..7314fd9e6 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,33 @@ +name: CIFuzz +on: + pull_request: + branches: + - master + paths: + - '**.c' + - '**.cc' + - '**.cpp' + - '**.cxx' + - '**.h' +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'tinyusb' + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'tinyusb' + language: c++ + fuzz-seconds: 600 + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000..d2150d13f --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,48 @@ +name: pre-commit + +on: + push: + pull_request: + branches: [ master ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + + - name: Checkout TinyUSB + uses: actions/checkout@v3 + + - name: Run codespell + uses: codespell-project/actions-codespell@master + + - name: Run Unit Tests + run: | + # Install Ceedling + gem install ceedling + cd test/unit-test + ceedling test:all + + - name: Build Fuzzer + run: | + export CC=clang + export CXX=clang++ + fuzz_harness=$(ls -d test/fuzz/device/*/) + for h in $fuzz_harness + do + make -C $h get-deps + make -C $h all + done diff --git a/.github/workflows/test_hardware.yml b/.github/workflows/test_hardware.yml index 3ef1fdc56..601a97e99 100644 --- a/.github/workflows/test_hardware.yml +++ b/.github/workflows/test_hardware.yml @@ -1,10 +1,19 @@ name: Hardware Test + on: - pull_request: push: - release: - types: - - created + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' + pull_request: + branches: [ master ] + paths: + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' # Hardware in the loop (HIL) # Current self-hosted instance is running on an EPYC 7232 server hosted by HiFiPhile user diff --git a/.gitmodules b/.gitmodules index dcac0d9ab..14f47746b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -146,6 +146,9 @@ [submodule "hw/mcu/allwinner"] path = hw/mcu/allwinner url = https://github.com/hathach/allwinner_driver.git +[submodule "hw/mcu/wch/ch32v307"] + path = hw/mcu/wch/ch32v307 + url = https://github.com/openwch/ch32v307.git [submodule "hw/mcu/raspberry_pi/Pico-PIO-USB"] path = hw/mcu/raspberry_pi/Pico-PIO-USB url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git diff --git a/README.rst b/README.rst index 05963a655..8504b92c8 100644 --- a/README.rst +++ b/README.rst @@ -54,6 +54,7 @@ The stack supports the following MCUs: - **ST:** STM32 series: F0, F1, F2, F3, F4, F7, H7, G4, L0, L1, L4, L4+, WB - **TI:** MSP430, MSP432E4, TM4C123 - **ValentyUSB:** eptri +- **WCH:** CH32V307 Here is the list of `Supported Devices`_ that can be used with provided examples. diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index c572894ad..7ff79cb32 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -6,7 +6,7 @@ Contributing can be highly rewarding, but it can also be frustrating at times. It takes time to review patches, and as this is an open source project, that sometimes can take a while. The reviewing process depends on the availability of the maintainers, who may not be always available. Please try to be -understanding throught the process. +understanding through the process. There a few guidelines you need to keep in mind when contributing. Please have a look at them as that will make the contribution process easier for all diff --git a/docs/contributing/porting.rst b/docs/contributing/porting.rst index 7e9e462f0..710af51c3 100644 --- a/docs/contributing/porting.rst +++ b/docs/contributing/porting.rst @@ -195,7 +195,7 @@ Others (like the nRF52) may need each USB packet queued individually. To make th some state for yourself and queue up an intermediate USB packet from the interrupt handler. Once the transaction is going, the interrupt handler will notify TinyUSB of transfer completion. -During transmission, the IN data buffer is guarenteed to remain unchanged in memory until the ``dcd_xfer_complete`` function is called. +During transmission, the IN data buffer is guaranteed to remain unchanged in memory until the ``dcd_xfer_complete`` function is called. The dcd_edpt_xfer function must never add zero-length-packets (ZLP) on its own to a transfer. If a ZLP is required, then it must be explicitly sent by the stack calling dcd_edpt_xfer(), by calling dcd_edpt_xfer() a second time with len=0. @@ -238,4 +238,4 @@ Use `WireShark `_ or `a Beagle `__ + +WCH +--- + +- `CH32V307V-R1-1v0 ` diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c index c3666763b..c83299e59 100644 --- a/examples/device/cdc_msc/src/main.c +++ b/examples/device/cdc_msc/src/main.c @@ -113,7 +113,7 @@ void cdc_task(void) // connected and there are data available if ( tud_cdc_available() ) { - // read datas + // read data char buf[64]; uint32_t count = tud_cdc_read(buf, sizeof(buf)); (void) count; diff --git a/examples/device/cdc_msc_freertos/CMakeLists.txt b/examples/device/cdc_msc_freertos/CMakeLists.txt index 639dde99a..cbd75efd6 100644 --- a/examples/device/cdc_msc_freertos/CMakeLists.txt +++ b/examples/device/cdc_msc_freertos/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) # TOP is absolute path to root directory of TinyUSB git repo -# needed for esp32sx build. TOOD could be removed later on +# needed for esp32sx build. TODO could be removed later on set(TOP "../../..") get_filename_component(TOP "${TOP}" REALPATH) diff --git a/examples/device/cdc_msc_freertos/Makefile b/examples/device/cdc_msc_freertos/Makefile index 3352dd37d..ff4b41108 100644 --- a/examples/device/cdc_msc_freertos/Makefile +++ b/examples/device/cdc_msc_freertos/Makefile @@ -29,8 +29,15 @@ SRC_C += \ $(FREERTOS_SRC)/timers.c \ $(subst ../../../,,$(wildcard ../../../$(FREERTOS_SRC)/portable/GCC/$(FREERTOS_PORT)/*.c)) -# Suppress FreeRTOS warnings -CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls +# include heap manage if configSUPPORT_DYNAMIC_ALLOCATION = 1 +# SRC_C += $(FREERTOS_SRC)/portable/MemMang/heap_1.c +# CFLAGS += -Wno-error=sign-compare + +# Suppress FreeRTOSConfig.h warnings +CFLAGS += -Wno-error=redundant-decls + +# Suppress FreeRTOS source warnings +CFLAGS += -Wno-error=cast-qual # FreeRTOS (lto + Os) linker issue LDFLAGS += -Wl,--undefined=vTaskSwitchContext diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt index 1ee51a9d4..49b8ee57b 100644 --- a/examples/device/cdc_msc_freertos/skip.txt +++ b/examples/device/cdc_msc_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V307 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 @@ -8,4 +9,4 @@ mcu:SAMD11 mcu:SAMX7X mcu:VALENTYUSB_EPTRI family:broadcom_32bit -family:broadcom_64bit \ No newline at end of file +family:broadcom_64bit diff --git a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 6a3630dbc..968c59749 100644 --- a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -69,7 +69,7 @@ #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( 0*1024 ) // dynamic is not used +#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c index 755220c13..cfb14aa2b 100644 --- a/examples/device/cdc_msc_freertos/src/main.c +++ b/examples/device/cdc_msc_freertos/src/main.c @@ -51,6 +51,8 @@ #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) #endif +#define CDC_STACK_SZIE configMINIMAL_STACK_SIZE + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ @@ -66,19 +68,18 @@ enum { BLINK_SUSPENDED = 2500, }; -// static timer +// static timer & task +#if configSUPPORT_STATIC_ALLOCATION StaticTimer_t blinky_tmdef; -TimerHandle_t blinky_tm; -// static task StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; -// static task for cdc -#define CDC_STACK_SZIE configMINIMAL_STACK_SIZE StackType_t cdc_stack[CDC_STACK_SZIE]; StaticTask_t cdc_taskdef; +#endif +TimerHandle_t blinky_tm; void led_blinky_cb(TimerHandle_t xTimer); void usb_device_task(void* param); @@ -92,15 +93,22 @@ int main(void) { board_init(); +#if configSUPPORT_STATIC_ALLOCATION // soft timer for blinky blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef); - xTimerStart(blinky_tm, 0); // Create a task for tinyusb device stack - (void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); // Create CDC task - (void) xTaskCreateStatic( cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef); + xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef); +#else + blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb); + xTaskCreate( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL); + xTaskCreate( cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL); +#endif + + xTimerStart(blinky_tm, 0); // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) diff --git a/examples/device/dfu_runtime/src/main.c b/examples/device/dfu_runtime/src/main.c index 55b380353..bd9a91c27 100644 --- a/examples/device/dfu_runtime/src/main.c +++ b/examples/device/dfu_runtime/src/main.c @@ -31,7 +31,7 @@ * * $ dfu-util -e * - * This will send DETTACH command to put device into bootloader. Since this example + * This will send DETACH command to put device into bootloader. Since this example * is minimal, it doesn't actually go into DFU mode but rather change the LED blinking * pattern to fast rate as indicator. */ diff --git a/examples/device/hid_boot_interface/src/main.c b/examples/device/hid_boot_interface/src/main.c index 71afa3f46..ef072019c 100644 --- a/examples/device/hid_boot_interface/src/main.c +++ b/examples/device/hid_boot_interface/src/main.c @@ -157,11 +157,11 @@ void hid_task(void) { uint8_t const report_id = 0; uint8_t const button_mask = 0; - uint8_t const veritical = 0; + uint8_t const vertical = 0; uint8_t const horizontal = 0; int8_t const delta = 5; - tud_hid_n_mouse_report(ITF_NUM_MOUSE, report_id, button_mask, delta, delta, veritical, horizontal); + tud_hid_n_mouse_report(ITF_NUM_MOUSE, report_id, button_mask, delta, delta, vertical, horizontal); } } } @@ -175,7 +175,7 @@ void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) (void) protocol; // nothing to do since we use the same compatible boot report for both Boot and Report mode. - // TOOD set a indicator for user + // TODO set a indicator for user } // Invoked when sent REPORT successfully to host diff --git a/examples/device/hid_composite_freertos/Makefile b/examples/device/hid_composite_freertos/Makefile index 6c8c43ddd..9c66b896d 100644 --- a/examples/device/hid_composite_freertos/Makefile +++ b/examples/device/hid_composite_freertos/Makefile @@ -28,8 +28,15 @@ SRC_C += \ $(FREERTOS_SRC)/timers.c \ $(subst ../../../,,$(wildcard ../../../$(FREERTOS_SRC)/portable/GCC/$(FREERTOS_PORT)/*.c)) -# Suppress FreeRTOS warnings -CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls +# include heap manage if configSUPPORT_DYNAMIC_ALLOCATION = 1 +# SRC_C += $(FREERTOS_SRC)/portable/MemMang/heap_1.c +# CFLAGS += -Wno-error=sign-compare + +# Suppress FreeRTOSConfig.h warnings +CFLAGS += -Wno-error=redundant-decls + +# Suppress FreeRTOS source warnings +CFLAGS += -Wno-error=cast-qual # FreeRTOS (lto + Os) linker issue LDFLAGS += -Wl,--undefined=vTaskSwitchContext diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt index 1ee51a9d4..49b8ee57b 100644 --- a/examples/device/hid_composite_freertos/skip.txt +++ b/examples/device/hid_composite_freertos/skip.txt @@ -1,3 +1,4 @@ +mcu:CH32V307 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 @@ -8,4 +9,4 @@ mcu:SAMD11 mcu:SAMX7X mcu:VALENTYUSB_EPTRI family:broadcom_32bit -family:broadcom_64bit \ No newline at end of file +family:broadcom_64bit diff --git a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index bfdf1e926..968c59749 100644 --- a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -46,13 +46,14 @@ #include "bsp/board_mcu.h" #if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 -#error "ESP32-Sx should use IDF's FreeRTOSConfig.h" + #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif +// TODO fix later #if CFG_TUSB_MCU == OPT_MCU_MM32F327X - // TODO fix/remove later extern u32 SystemCoreClock; #else + // FIXME cause redundant-decls warnings extern uint32_t SystemCoreClock; #endif @@ -68,7 +69,7 @@ #define configTICK_RATE_HZ ( 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( 0*1024 ) // dynamic is not used +#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c index b67c10937..fb0d69258 100644 --- a/examples/device/hid_composite_freertos/src/main.c +++ b/examples/device/hid_composite_freertos/src/main.c @@ -53,6 +53,8 @@ #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) #endif +#define HID_STACK_SZIE configMINIMAL_STACK_SIZE + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ @@ -68,19 +70,18 @@ enum { BLINK_SUSPENDED = 2500, }; -// static timer +// static timer & task +#if configSUPPORT_STATIC_ALLOCATION StaticTimer_t blinky_tmdef; -TimerHandle_t blinky_tm; -// static task StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; -// static task for hid -#define HID_STACK_SZIE configMINIMAL_STACK_SIZE StackType_t hid_stack[HID_STACK_SZIE]; StaticTask_t hid_taskdef; +#endif +TimerHandle_t blinky_tm; void led_blinky_cb(TimerHandle_t xTimer); void usb_device_task(void* param); @@ -94,15 +95,22 @@ int main(void) { board_init(); +#if configSUPPORT_STATIC_ALLOCATION // soft timer for blinky blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef); - xTimerStart(blinky_tm, 0); // Create a task for tinyusb device stack - (void) xTaskCreateStatic( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); // Create HID task - (void) xTaskCreateStatic( hid_task, "hid", HID_STACK_SZIE, NULL, configMAX_PRIORITIES-2, hid_stack, &hid_taskdef); + xTaskCreateStatic(hid_task, "hid", HID_STACK_SZIE, NULL, configMAX_PRIORITIES-2, hid_stack, &hid_taskdef); +#else + blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb); + xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL); + xTaskCreate(hid_task, "hid", HID_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL); +#endif + + xTimerStart(blinky_tm, 0); // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) diff --git a/examples/device/hid_generic_inout/hid_test.py b/examples/device/hid_generic_inout/hid_test.py index 21fd3f421..5bdba9db0 100644 --- a/examples/device/hid_generic_inout/hid_test.py +++ b/examples/device/hid_generic_inout/hid_test.py @@ -13,8 +13,8 @@ for vid in USB_VID: if dev: while True: # Get input from console and encode to UTF8 for array of chars. - # hid generic inout is single report therefore by HIDAPI requirement - # it must be preceeded with 0x00 as dummy reportID + # hid generic in/out is single report therefore by HIDAPI requirement + # it must be preceded, with 0x00 as dummy reportID str_out = b'\x00' str_out += input("Send text to HID Device : ").encode('utf-8') dev.write(str_out) diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt index d84457bc9..7af6b738e 100644 --- a/examples/host/cdc_msc_hid/CMakeLists.txt +++ b/examples/host/cdc_msc_hid/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c ${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c diff --git a/examples/host/cdc_msc_hid/Makefile b/examples/host/cdc_msc_hid/Makefile index ad28076a0..9adccfa3a 100644 --- a/examples/host/cdc_msc_hid/Makefile +++ b/examples/host/cdc_msc_hid/Makefile @@ -7,7 +7,8 @@ INC += \ # Example source EXAMPLE_SOURCE = \ - src/hid_app.c \ + src/cdc_app.c \ + src/hid_app.c \ src/main.c \ src/msc_app.c \ diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c new file mode 100644 index 000000000..b1b137e0e --- /dev/null +++ b/examples/host/cdc_msc_hid/src/cdc_app.c @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb.h" +#include "bsp/board.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + + +//------------- IMPLEMENTATION -------------// + +size_t get_console_inputs(uint8_t* buf, size_t bufsize) +{ + size_t count = 0; + while (count < bufsize) + { + int ch = board_getchar(); + if ( ch <= 0 ) break; + + buf[count] = (uint8_t) ch; + count++; + } + + return count; +} + +void cdc_app_task(void) +{ + uint8_t buf[64+1]; // +1 for extra null character + uint32_t const bufsize = sizeof(buf)-1; + + uint32_t count = get_console_inputs(buf, bufsize); + buf[count] = 0; + + // loop over all mounted interfaces + for(uint8_t idx=0; idx cdc interfaces + if (count) + { + tuh_cdc_write(idx, buf, count); + tuh_cdc_write_flush(idx); + } + } + } +} + +// Invoked when received new data +void tuh_cdc_rx_cb(uint8_t idx) +{ + uint8_t buf[64+1]; // +1 for extra null character + uint32_t const bufsize = sizeof(buf)-1; + + // forward cdc interfaces -> console + uint32_t count = tuh_cdc_read(idx, buf, bufsize); + buf[count] = 0; + + printf((char*) buf); +} + +void tuh_cdc_mount_cb(uint8_t idx) +{ + tuh_cdc_itf_info_t itf_info = { 0 }; + tuh_cdc_itf_get_info(idx, &itf_info); + + printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.bInterfaceNumber); + +#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + // CFG_TUH_CDC_LINE_CODING_ON_ENUM must be defined for line coding is set by tinyusb in enumeration + // otherwise you need to call tuh_cdc_set_line_coding() first + cdc_line_coding_t line_coding = { 0 }; + if ( tuh_cdc_get_local_line_coding(idx, &line_coding) ) + { + printf(" Baudrate: %lu, Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits); + printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity , line_coding.data_bits); + } +#endif +} + +void tuh_cdc_umount_cb(uint8_t idx) +{ + tuh_cdc_itf_info_t itf_info = { 0 }; + tuh_cdc_itf_get_info(idx, &itf_info); + + printf("CDC Interface is unmounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.bInterfaceNumber); +} diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c index 11437c2b4..ed53c502d 100644 --- a/examples/host/cdc_msc_hid/src/hid_app.c +++ b/examples/host/cdc_msc_hid/src/hid_app.c @@ -247,7 +247,7 @@ static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t c // Composite report, 1st byte is report ID, data starts from 2nd byte uint8_t const rpt_id = report[0]; - // Find report id in the arrray + // Find report id in the array for(uint8_t i=0; i + + +#define UART_RINGBUFFER_SIZE_TX 64 +#define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX-1) + +static char tx_buf[UART_RINGBUFFER_SIZE_TX]; +static unsigned int tx_produce; +static volatile unsigned int tx_consume; + +void USART1_IRQHandler(void) __attribute__((naked)); +void USART1_IRQHandler(void) { + __asm volatile ("call USART1_IRQHandler_impl; mret"); +} + +__attribute__((used)) void USART1_IRQHandler_impl(void) +{ + if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) + { + USART_ClearITPendingBit(USART1, USART_IT_TC); + + if(tx_consume != tx_produce) { + USART_SendData(USART1, tx_buf[tx_consume]); + tx_consume = (tx_consume + 1) & UART_RINGBUFFER_MASK_TX; + } + } + +} + +void uart_write(char c) +{ + unsigned int tx_produce_next = (tx_produce + 1) & UART_RINGBUFFER_MASK_TX; + + NVIC_DisableIRQ(USART1_IRQn); + if((tx_consume != tx_produce) || (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)) { + tx_buf[tx_produce] = c; + tx_produce = tx_produce_next; + } else { + USART_SendData(USART1, c); + } + NVIC_EnableIRQ(USART1_IRQn); +} + + +void uart_sync(void) +{ + while(tx_consume != tx_produce); +} + + +void usart_printf_init(uint32_t baudrate) +{ + GPIO_InitTypeDef GPIO_InitStructure; + USART_InitTypeDef USART_InitStructure; + + tx_produce = 0; + tx_consume = 0; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + USART_InitStructure.USART_BaudRate = baudrate; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Tx; + + USART_Init(USART1, &USART_InitStructure); + USART_ITConfig(USART1, USART_IT_TC, ENABLE); + USART_Cmd(USART1, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure = { 0 }; + NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} diff --git a/hw/bsp/ch32v307/boards/ch32v307v-r1-1v0/debug_uart.h b/hw/bsp/ch32v307/boards/ch32v307v-r1-1v0/debug_uart.h new file mode 100644 index 000000000..a7e070585 --- /dev/null +++ b/hw/bsp/ch32v307/boards/ch32v307v-r1-1v0/debug_uart.h @@ -0,0 +1,31 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Greg Davill + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include + +void uart_write(char c); +void uart_sync(void); +void usart_printf_init(uint32_t baudrate); diff --git a/hw/bsp/ch32v307/ch32v307.ld b/hw/bsp/ch32v307/ch32v307.ld new file mode 100644 index 000000000..af7c06615 --- /dev/null +++ b/hw/bsp/ch32v307/ch32v307.ld @@ -0,0 +1,170 @@ +ENTRY( _start ) + +__stack_size = 4096; + +PROVIDE( _stack_size = __stack_size ); + + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K +} + + +SECTIONS +{ + + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + .vector : + { + *(.vector); + . = ALIGN(64); + } >FLASH AT>FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t.*) + . = ALIGN(4); + } >FLASH AT>FLASH + + .fini : + { + KEEP(*(SORT_NONE(.fini))) + . = ALIGN(4); + } >FLASH AT>FLASH + + PROVIDE( _etext = . ); + PROVIDE( _eitcm = . ); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH AT>FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH AT>FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH AT>FLASH + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >FLASH AT>FLASH + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >FLASH AT>FLASH + + .dalign : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + } >RAM AT>FLASH + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.sdata2.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + . = ALIGN(4); + PROVIDE( _edata = .); + } >RAM AT>FLASH + + .bss : + { + . = ALIGN(4); + PROVIDE( _sbss = .); + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON*) + . = ALIGN(4); + PROVIDE( _ebss = .); + } >RAM AT>FLASH + + PROVIDE( _end = _ebss); + PROVIDE( end = . ); + + .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : + { + PROVIDE( _heap_end = . ); + . = ALIGN(4); + PROVIDE(_susrstack = . ); + . = . + __stack_size; + PROVIDE( _eusrstack = .); + __freertos_irq_stack_top = .; + } >RAM + +} + + + diff --git a/hw/bsp/ch32v307/ch32v30x_conf.h b/hw/bsp/ch32v307/ch32v30x_conf.h new file mode 100644 index 000000000..399feaf68 --- /dev/null +++ b/hw/bsp/ch32v307/ch32v30x_conf.h @@ -0,0 +1,43 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : ch32v30x_conf.h +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : Library configuration file. +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*******************************************************************************/ +#ifndef __CH32V30x_CONF_H +#define __CH32V30x_CONF_H + +#include "ch32v30x_adc.h" +#include "ch32v30x_bkp.h" +#include "ch32v30x_can.h" +#include "ch32v30x_crc.h" +#include "ch32v30x_dac.h" +#include "ch32v30x_dbgmcu.h" +#include "ch32v30x_dma.h" +#include "ch32v30x_exti.h" +#include "ch32v30x_flash.h" +#include "ch32v30x_fsmc.h" +#include "ch32v30x_gpio.h" +#include "ch32v30x_i2c.h" +#include "ch32v30x_iwdg.h" +#include "ch32v30x_pwr.h" +#include "ch32v30x_rcc.h" +#include "ch32v30x_rtc.h" +#include "ch32v30x_sdio.h" +#include "ch32v30x_spi.h" +#include "ch32v30x_tim.h" +#include "ch32v30x_usart.h" +#include "ch32v30x_wwdg.h" +#include "ch32v30x_it.h" +#include "ch32v30x_misc.h" + + +#endif /* __CH32V30x_CONF_H */ + + + + + diff --git a/hw/bsp/ch32v307/ch32v30x_it.c b/hw/bsp/ch32v307/ch32v30x_it.c new file mode 100644 index 000000000..d348dd989 --- /dev/null +++ b/hw/bsp/ch32v307/ch32v30x_it.c @@ -0,0 +1,49 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : ch32v30x_it.c +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : Main Interrupt Service Routines. +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*******************************************************************************/ +#include "ch32v30x_it.h" + +void NMI_Handler(void) __attribute__((naked)); +void HardFault_Handler(void) __attribute__((naked)); + +/********************************************************************* + * @fn NMI_Handler + * + * @brief This function handles NMI exception. + * + * @return none + */ +void NMI_Handle(void){ + __asm volatile ("call NMI_Handler_impl; mret"); +} + +__attribute__((used)) void NMI_Handler_impl(void) +{ + +} + +/********************************************************************* + * @fn HardFault_Handler + * + * @brief This function handles Hard Fault exception. + * + * @return none + */ +void HardFault_Handler(void){ + __asm volatile ("call HardFault_Handler_impl; mret"); +} + +__attribute__((used)) void HardFault_Handler_impl(void) +{ + while (1) + { + } +} + + diff --git a/hw/bsp/ch32v307/ch32v30x_it.h b/hw/bsp/ch32v307/ch32v30x_it.h new file mode 100644 index 000000000..42f285edf --- /dev/null +++ b/hw/bsp/ch32v307/ch32v30x_it.h @@ -0,0 +1,18 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : ch32v30x_it.h +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : This file contains the headers of the interrupt handlers. +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*******************************************************************************/ +#ifndef __CH32V30x_IT_H +#define __CH32V30x_IT_H + +// #include "debug.h" + + +#endif /* __CH32V30x_IT_H */ + + diff --git a/hw/bsp/ch32v307/core_riscv.h b/hw/bsp/ch32v307/core_riscv.h new file mode 100644 index 000000000..2e94ec683 --- /dev/null +++ b/hw/bsp/ch32v307/core_riscv.h @@ -0,0 +1,384 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : core_riscv.h +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : RISC-V Core Peripheral Access Layer Header File for CH32V30x +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*******************************************************************************/ +#ifndef __CORE_RISCV_H__ +#define __CORE_RISCV_H__ + +/* IO definitions */ +#ifdef __cplusplus + #define __I volatile /* defines 'read only' permissions */ +#else + #define __I volatile const /* defines 'read only' permissions */ +#endif +#define __O volatile /* defines 'write only' permissions */ +#define __IO volatile /* defines 'read / write' permissions */ + +/* Standard Peripheral Library old types (maintained for legacy purpose) */ +typedef __I uint64_t vuc64; /* Read Only */ +typedef __I uint32_t vuc32; /* Read Only */ +typedef __I uint16_t vuc16; /* Read Only */ +typedef __I uint8_t vuc8; /* Read Only */ + +typedef const uint64_t uc64; /* Read Only */ +typedef const uint32_t uc32; /* Read Only */ +typedef const uint16_t uc16; /* Read Only */ +typedef const uint8_t uc8; /* Read Only */ + +typedef __I int64_t vsc64; /* Read Only */ +typedef __I int32_t vsc32; /* Read Only */ +typedef __I int16_t vsc16; /* Read Only */ +typedef __I int8_t vsc8; /* Read Only */ + +typedef const int64_t sc64; /* Read Only */ +typedef const int32_t sc32; /* Read Only */ +typedef const int16_t sc16; /* Read Only */ +typedef const int8_t sc8; /* Read Only */ + +typedef __IO uint64_t vu64; +typedef __IO uint32_t vu32; +typedef __IO uint16_t vu16; +typedef __IO uint8_t vu8; + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef __IO int64_t vs64; +typedef __IO int32_t vs32; +typedef __IO int16_t vs16; +typedef __IO int8_t vs8; + +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus; + +typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; + +typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; + +#define RV_STATIC_INLINE static inline + +/* memory mapped structure for Program Fast Interrupt Controller (PFIC) */ +typedef struct{ + __I uint32_t ISR[8]; + __I uint32_t IPR[8]; + __IO uint32_t ITHRESDR; + __IO uint32_t RESERVED; + __IO uint32_t CFGR; + __I uint32_t GISR; + uint8_t VTFIDR[4]; + uint8_t RESERVED0[12]; + __IO uint32_t VTFADDR[4]; + uint8_t RESERVED1[0x90]; + __O uint32_t IENR[8]; + uint8_t RESERVED2[0x60]; + __O uint32_t IRER[8]; + uint8_t RESERVED3[0x60]; + __O uint32_t IPSR[8]; + uint8_t RESERVED4[0x60]; + __O uint32_t IPRR[8]; + uint8_t RESERVED5[0x60]; + __IO uint32_t IACTR[8]; + uint8_t RESERVED6[0xE0]; + __IO uint8_t IPRIOR[256]; + uint8_t RESERVED7[0x810]; + __IO uint32_t SCTLR; +}PFIC_Type; + +/* memory mapped structure for SysTick */ +typedef struct +{ + __IO u32 CTLR; + __IO u32 SR; + __IO u64 CNT; + __IO u64 CMP; +}SysTick_Type; + + +#define PFIC ((PFIC_Type *) 0xE000E000 ) +#define NVIC PFIC +#define NVIC_KEY1 ((uint32_t)0xFA050000) +#define NVIC_KEY2 ((uint32_t)0xBCAF0000) +#define NVIC_KEY3 ((uint32_t)0xBEEF0000) + +#define SysTick ((SysTick_Type *) 0xE000F000) + + +/********************************************************************* + * @fn __enable_irq + * + * @brief Enable Global Interrupt + * + * @return none + */ +RV_STATIC_INLINE void __enable_irq(void) +{ + __asm volatile ("csrw 0x800, %0" : : "r" (0x6088) ); +} + +/********************************************************************* + * @fn __disable_irq + * + * @brief Disable Global Interrupt + * + * @return none + */ +RV_STATIC_INLINE void __disable_irq(void) +{ + __asm volatile ("csrw 0x800, %0" : : "r" (0x6000) ); +} + +/********************************************************************* + * @fn __NOP + * + * @brief nop + * + * @return none + */ +RV_STATIC_INLINE void __NOP(void) +{ + __asm volatile ("nop"); +} + +/********************************************************************* + * @fn NVIC_EnableIRQ + * + * @brief Enable Interrupt + * + * @param IRQn: Interrupt Numbers + * + * @return none + */ +RV_STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +/********************************************************************* + * @fn NVIC_DisableIRQ + * + * @brief Disable Interrupt + * + * @param IRQn: Interrupt Numbers + * + * @return none + */ +RV_STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +/********************************************************************* + * @fn NVIC_GetStatusIRQ + * + * @brief Get Interrupt Enable State + * + * @param IRQn: Interrupt Numbers + * + * @return 1 - Interrupt Enable + * 0 - Interrupt Disable + */ +RV_STATIC_INLINE uint32_t NVIC_GetStatusIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); +} + +/********************************************************************* + * @fn NVIC_GetPendingIRQ + * + * @brief Get Interrupt Pending State + * + * @param IRQn: Interrupt Numbers + * + * @return 1 - Interrupt Pending Enable + * 0 - Interrupt Pending Disable + */ +RV_STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); +} + +/********************************************************************* + * @fn NVIC_SetPendingIRQ + * + * @brief Set Interrupt Pending + * + * @param IRQn: Interrupt Numbers + * + * @return None + */ +RV_STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +/********************************************************************* + * @fn NVIC_ClearPendingIRQ + * + * @brief Clear Interrupt Pending + * + * @param IRQn: Interrupt Numbers + * + * @return None + */ +RV_STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +/********************************************************************* + * @fn NVIC_GetActive + * + * @brief Get Interrupt Active State + * + * @param IRQn: Interrupt Numbers + * + * @return 1 - Interrupt Active + * 0 - Interrupt No Active + */ +RV_STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); +} + +/********************************************************************* + * @fn NVIC_SetPriority + * + * @brief Set Interrupt Priority + * + * @param IRQn - Interrupt Numbers + * priority - + * bit7 - pre-emption priority + * bit6~bit4 - subpriority + * @return None + */ +RV_STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority) +{ + NVIC->IPRIOR[(uint32_t)(IRQn)] = priority; +} + +/********************************************************************* + * @fn __WFI + * + * @brief Wait for Interrupt + * + * @return None + */ +__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFI(void) +{ + NVIC->SCTLR &= ~(1<<3); // wfi + asm volatile ("wfi"); +} + +/********************************************************************* + * @fn __WFE + * + * @brief Wait for Events + * + * @return None + */ +__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFE(void) +{ + uint32_t t; + + t = NVIC->SCTLR; + NVIC->SCTLR |= (1<<3)|(1<<5); // (wfi->wfe)+(__sev) + NVIC->SCTLR = (NVIC->SCTLR & ~(1<<5)) | ( t & (1<<5)); + asm volatile ("wfi"); + asm volatile ("wfi"); +} + +/********************************************************************* + * @fn SetVTFIRQ + * + * @brief Set VTF Interrupt + * + * @param add - VTF interrupt service function base address. + * IRQn -Interrupt Numbers + * num - VTF Interrupt Numbers + * NewState - DISABLE or ENABLE + * @return None + */ +RV_STATIC_INLINE void SetVTFIRQ(uint32_t addr, IRQn_Type IRQn, uint8_t num, FunctionalState NewState){ + if(num > 3) return ; + + if (NewState != DISABLE) + { + NVIC->VTFIDR[num] = IRQn; + NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)|0x1); + } + else{ + NVIC->VTFIDR[num] = IRQn; + NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)&(~0x1)); + } +} + +/********************************************************************* + * @fn NVIC_SystemReset + * + * @brief Initiate a system reset request + * + * @return None + */ +RV_STATIC_INLINE void NVIC_SystemReset(void) +{ + NVIC->CFGR = NVIC_KEY3|(1<<7); +} + + +/* Core_Exported_Functions */ +extern uint32_t __get_FFLAGS(void); +extern void __set_FFLAGS(uint32_t value); +extern uint32_t __get_FRM(void); +extern void __set_FRM(uint32_t value); +extern uint32_t __get_FCSR(void); +extern void __set_FCSR(uint32_t value); +extern uint32_t __get_MSTATUS(void); +extern void __set_MSTATUS(uint32_t value); +extern uint32_t __get_MISA(void); +extern void __set_MISA(uint32_t value); +extern uint32_t __get_MIE(void); +extern void __set_MIE(uint32_t value); +extern uint32_t __get_MTVEC(void); +extern void __set_MTVEC(uint32_t value); +extern uint32_t __get_MSCRATCH(void); +extern void __set_MSCRATCH(uint32_t value); +extern uint32_t __get_MEPC(void); +extern void __set_MEPC(uint32_t value); +extern uint32_t __get_MCAUSE(void); +extern void __set_MCAUSE(uint32_t value); +extern uint32_t __get_MTVAL(void); +extern void __set_MTVAL(uint32_t value); +extern uint32_t __get_MIP(void); +extern void __set_MIP(uint32_t value); +extern uint32_t __get_MCYCLE(void); +extern void __set_MCYCLE(uint32_t value); +extern uint32_t __get_MCYCLEH(void); +extern void __set_MCYCLEH(uint32_t value); +extern uint32_t __get_MINSTRET(void); +extern void __set_MINSTRET(uint32_t value); +extern uint32_t __get_MINSTRETH(void); +extern void __set_MINSTRETH(uint32_t value); +extern uint32_t __get_MVENDORID(void); +extern uint32_t __get_MARCHID(void); +extern uint32_t __get_MIMPID(void); +extern uint32_t __get_MHARTID(void); +extern uint32_t __get_SP(void); + + +#endif + + + + + diff --git a/hw/bsp/ch32v307/family.c b/hw/bsp/ch32v307/family.c new file mode 100644 index 000000000..d5602c7b4 --- /dev/null +++ b/hw/bsp/ch32v307/family.c @@ -0,0 +1,178 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Greg Davill + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "stdio.h" +#include "debug_uart.h" +#include "ch32v30x.h" + +#include "bsp/board.h" +#include "board.h" + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ + +void USBHS_IRQHandler (void) __attribute__((naked)); +void USBHS_IRQHandler (void) +{ + __asm volatile ("call USBHS_IRQHandler_impl; mret"); +} + +__attribute__ ((used)) void USBHS_IRQHandler_impl (void) +{ + tud_int_handler(0); +} + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM +//--------------------------------------------------------------------+ + +uint32_t SysTick_Config(uint32_t ticks) +{ + NVIC_EnableIRQ(SysTicK_IRQn); + SysTick->CTLR=0; + SysTick->SR=0; + SysTick->CNT=0; + SysTick->CMP=ticks-1; + SysTick->CTLR=0xF; + return 0; +} + +void board_init(void) { + + /* Disable interrupts during init */ + __disable_irq(); + +#if CFG_TUSB_OS == OPT_OS_NONE + SysTick_Config(SystemCoreClock / 1000); +#endif + + usart_printf_init(115200); + + RCC_USBCLK48MConfig(RCC_USBCLK48MCLKSource_USBPHY); + RCC_USBHSPLLCLKConfig(RCC_HSBHSPLLCLKSource_HSE); + RCC_USBHSConfig(RCC_USBPLL_Div2); + RCC_USBHSPLLCKREFCLKConfig(RCC_USBHSPLLCKREFCLK_4M); + RCC_USBHSPHYPLLALIVEcmd(ENABLE); + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHS, ENABLE); + + GPIO_InitTypeDef GPIO_InitStructure = {0}; + + // LED + LED_CLOCK_EN(); + GPIO_InitStructure.GPIO_Pin = LED_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(LED_PORT, &GPIO_InitStructure); + + // Button + BUTTON_CLOCK_EN(); + GPIO_InitStructure.GPIO_Pin = BUTTON_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(BUTTON_PORT, &GPIO_InitStructure); + + /* Enable interrupts globally */ + __enable_irq(); + + board_delay(2); +} + +#if CFG_TUSB_OS == OPT_OS_NONE + +volatile uint32_t system_ticks = 0; + +/* Small workaround to support HW stack save/restore */ +void SysTick_Handler (void) __attribute__((naked)); +void SysTick_Handler (void) +{ + __asm volatile ("call SysTick_Handler_impl; mret"); +} + +__attribute__((used)) void SysTick_Handler_impl (void) +{ + SysTick->SR = 0; + system_ticks++; +} + +uint32_t board_millis (void) +{ + return system_ticks; +} + +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write (bool state) +{ + GPIO_WriteBit(LED_PORT, LED_PIN, state); +} + +uint32_t board_button_read (void) +{ + return BUTTON_STATE_ACTIVE == GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN); +} + +int board_uart_read (uint8_t *buf, int len) +{ + (void) buf; + (void) len; + return 0; +} + +int board_uart_write (void const *buf, int len) +{ + int txsize = len; + while ( txsize-- ) + { + uart_write(*(uint8_t const*) buf); + buf++; + } + return len; +} + + + +#ifdef USE_FULL_ASSERT +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(char* file, uint32_t line) { + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line + number, + tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) + */ + /* USER CODE END 6 */ +} +#endif /* USE_FULL_ASSERT */ diff --git a/hw/bsp/ch32v307/family.mk b/hw/bsp/ch32v307/family.mk new file mode 100644 index 000000000..15f6724a8 --- /dev/null +++ b/hw/bsp/ch32v307/family.mk @@ -0,0 +1,64 @@ +# https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable +#CROSS_COMPILE ?= riscv32-unknown-elf- + +# Toolchain from https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack +CROSS_COMPILE ?= riscv-none-embed- + +# Submodules +CH32V307_SDK = hw/mcu/wch/ch32v307 +DEPS_SUBMODULES += $(CH32V307_SDK) + +# WCH-SDK paths +CH32V307_SDK_SRC = $(CH32V307_SDK)/EVT/EXAM/SRC + +include $(TOP)/$(BOARD_PATH)/board.mk + +CFLAGS += \ + -flto \ + -march=rv32imac \ + -mabi=ilp32 \ + -msmall-data-limit=8 \ + -mno-save-restore -Os \ + -fmessage-length=0 \ + -fsigned-char \ + -ffunction-sections \ + -fdata-sections \ + -nostdlib -nostartfiles \ + -DCFG_TUSB_MCU=OPT_MCU_CH32V307 \ + -Xlinker --gc-sections \ + -DBOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED + +SRC_C += \ + src/portable/wch/ch32v307/dcd_usbhs.c \ + $(CH32V307_SDK_SRC)/Core/core_riscv.c \ + $(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_gpio.c \ + $(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_misc.c \ + $(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_rcc.c \ + $(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_usart.c + +SRC_S += \ + $(CH32V307_SDK_SRC)/Startup/startup_ch32v30x_D8C.S + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(CH32V307_SDK_SRC)/Peripheral/inc + +# For freeRTOS port source +FREERTOS_PORT = RISC-V + +# wch-link is not supported yet in official openOCD yet. We need to either use +# 1. download openocd as part of mounriver studio http://www.mounriver.com/download or +# 2. compiled from modified source https://github.com/kprasadvnsi/riscv-openocd-wch +# +# Note: For Linux, somehow openocd in mounriver studio does not seem to have wch-link enable, +# therefore we need to compile it from source as follows: +# git clone https://github.com/kprasadvnsi/riscv-openocd-wch +# cd riscv-openocd-wch +# ./bootstrap +# ./configure CFLAGS="-Wno-error" --enable-wlink +# make +# openocd binaries will be generated in riscv-openocd-wch/src + +# flash target ROM bootloader +flash: $(BUILD)/$(PROJECT).elf + openocd -f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg -c init -c halt -c "program $<" -c wlink_reset_resume -c exit diff --git a/hw/bsp/ch32v307/system_ch32v30x.c b/hw/bsp/ch32v307/system_ch32v30x.c new file mode 100644 index 000000000..12b18d7b8 --- /dev/null +++ b/hw/bsp/ch32v307/system_ch32v30x.c @@ -0,0 +1,776 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : system_ch32v30x.c +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : CH32V30x Device Peripheral Access Layer System Source File. +* For HSE = 8Mhz +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*********************************************************************************/ +#include "ch32v30x.h" + +/* +* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after +* reset the HSI is used as SYSCLK source). +* If none of the define below is enabled, the HSI is used as System clock source. +*/ +// #define SYSCLK_FREQ_HSE HSE_VALUE +/* #define SYSCLK_FREQ_24MHz 24000000 */ +//#define SYSCLK_FREQ_48MHz 48000000 +/* #define SYSCLK_FREQ_56MHz 56000000 */ +//#define SYSCLK_FREQ_72MHz 72000000 +//#define SYSCLK_FREQ_96MHz 96000000 +//#define SYSCLK_FREQ_120MHz 120000000 +#define SYSCLK_FREQ_144MHz 144000000 + +/* Clock Definitions */ +#ifdef SYSCLK_FREQ_HSE + uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_24MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_48MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_56MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_72MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /* System Clock Frequency (Core Clock) */ + +#elif defined SYSCLK_FREQ_96MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_120MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz; /* System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_144MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz; /* System Clock Frequency (Core Clock) */ + +#else /* HSI Selected as System Clock source */ + uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */ +#endif + +__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + + +/* system_private_function_proto_types */ +static void SetSysClock(void); + +#ifdef SYSCLK_FREQ_HSE + static void SetSysClockToHSE(void); +#elif defined SYSCLK_FREQ_24MHz + static void SetSysClockTo24(void); +#elif defined SYSCLK_FREQ_48MHz + static void SetSysClockTo48(void); +#elif defined SYSCLK_FREQ_56MHz + static void SetSysClockTo56(void); +#elif defined SYSCLK_FREQ_72MHz + static void SetSysClockTo72(void); + +#elif defined SYSCLK_FREQ_96MHz + static void SetSysClockTo96(void); +#elif defined SYSCLK_FREQ_120MHz + static void SetSysClockTo120(void); +#elif defined SYSCLK_FREQ_144MHz + static void SetSysClockTo144(void); + +#endif + + +/********************************************************************* + * @fn SystemInit + * + * @brief Setup the microcontroller system Initialize the Embedded Flash Interface, + * the PLL and update the SystemCoreClock variable. + * + * @return none + */ +void SystemInit (void) +{ + RCC->CTLR |= (uint32_t)0x00000001; + +#ifdef CH32V30x_D8C + RCC->CFGR0 &= (uint32_t)0xF8FF0000; +#else + RCC->CFGR0 &= (uint32_t)0xF0FF0000; +#endif + + RCC->CTLR &= (uint32_t)0xFEF6FFFF; + RCC->CTLR &= (uint32_t)0xFFFBFFFF; + RCC->CFGR0 &= (uint32_t)0xFF80FFFF; + +#ifdef CH32V30x_D8C + RCC->CTLR &= (uint32_t)0xEBFFFFFF; + RCC->INTR = 0x00FF0000; + RCC->CFGR2 = 0x00000000; +#else + RCC->INTR = 0x009F0000; +#endif + SetSysClock(); +} + +/********************************************************************* + * @fn SystemCoreClockUpdate + * + * @brief Update SystemCoreClock variable according to Clock Register Values. + * + * @return none + */ +void SystemCoreClockUpdate (void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0; + + tmp = RCC->CFGR0 & RCC_SWS; + + switch (tmp) + { + case 0x00: + SystemCoreClock = HSI_VALUE; + break; + case 0x04: + SystemCoreClock = HSE_VALUE; + break; + case 0x08: + pllmull = RCC->CFGR0 & RCC_PLLMULL; + pllsource = RCC->CFGR0 & RCC_PLLSRC; + pllmull = ( pllmull >> 18) + 2; + +#ifdef CH32V30x_D8 + if(pllmull == 17) pllmull = 18; +#else + if(pllmull == 2) pllmull = 18; + if(pllmull == 15){ + pllmull = 13; /* *6.5 */ + Pll_6_5 = 1; + } + if(pllmull == 16) pllmull = 15; + if(pllmull == 17) pllmull = 16; +#endif + + if (pllsource == 0x00) + { + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } + else + { + if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET) + { + SystemCoreClock = (HSE_VALUE >> 1) * pllmull; + } + else + { + SystemCoreClock = HSE_VALUE * pllmull; + } + } + + if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2); + + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + + tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)]; + SystemCoreClock >>= tmp; +} + +/********************************************************************* + * @fn SetSysClock + * + * @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClock(void) +{ +#ifdef SYSCLK_FREQ_HSE + SetSysClockToHSE(); +#elif defined SYSCLK_FREQ_24MHz + SetSysClockTo24(); +#elif defined SYSCLK_FREQ_48MHz + SetSysClockTo48(); +#elif defined SYSCLK_FREQ_56MHz + SetSysClockTo56(); +#elif defined SYSCLK_FREQ_72MHz + SetSysClockTo72(); +#elif defined SYSCLK_FREQ_96MHz + SetSysClockTo96(); +#elif defined SYSCLK_FREQ_120MHz + SetSysClockTo120(); +#elif defined SYSCLK_FREQ_144MHz + SetSysClockTo144(); + +#endif + + /* If none of the define above is enabled, the HSI is used as System clock + * source (default after reset) + */ +} + + +#ifdef SYSCLK_FREQ_HSE + +/********************************************************************* + * @fn SetSysClockToHSE + * + * @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockToHSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1; + + /* Select HSE as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_HSE; + + /* Wait till HSE is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04) + { + } + } + else + { + /* If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_24MHz + +/********************************************************************* + * @fn SetSysClockTo24 + * + * @brief Sets System clock frequency to 24MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo24(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1; + + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL3); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL3_EXTEN); +#endif + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_48MHz + +/********************************************************************* + * @fn SetSysClockTo48 + * + * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo48(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_56MHz + +/********************************************************************* + * @fn SetSysClockTo56 + * + * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo56(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + +#elif defined SYSCLK_FREQ_72MHz + +/********************************************************************* + * @fn SetSysClockTo72 + * + * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo72(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | + RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + + +#elif defined SYSCLK_FREQ_96MHz + +/********************************************************************* + * @fn SetSysClockTo96 + * + * @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo96(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 12 = 96 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | + RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + + +#elif defined SYSCLK_FREQ_120MHz + +/********************************************************************* + * @fn SetSysClockTo120 + * + * @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo120(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 15 = 120 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | + RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + + +#elif defined SYSCLK_FREQ_144MHz + +/********************************************************************* + * @fn SetSysClockTo144 + * + * @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers. + * + * @return none + */ +static void SetSysClockTo144(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + RCC->CTLR |= ((uint32_t)RCC_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CTLR & RCC_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CTLR & RCC_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* HCLK = SYSCLK */ + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; + /* PCLK2 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; + /* PCLK1 = HCLK */ + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + + /* PLL configuration: PLLCLK = HSE * 18 = 144 MHz */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | + RCC_PLLMULL)); + +#ifdef CH32V30x_D8 + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18); +#else + RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18_EXTEN); +#endif + + /* Enable PLL */ + RCC->CTLR |= RCC_PLLON; + /* Wait till PLL is ready */ + while((RCC->CTLR & RCC_PLLRDY) == 0) + { + } + /* Select PLL as system clock source */ + RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); + RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) + { + } + } + else + { + /* + * If HSE fails to start-up, the application will have wrong clock + * configuration. User can add here some code to deal with this error + */ + } +} + + +#endif diff --git a/hw/bsp/ch32v307/system_ch32v30x.h b/hw/bsp/ch32v307/system_ch32v30x.h new file mode 100644 index 000000000..0e0ef4e51 --- /dev/null +++ b/hw/bsp/ch32v307/system_ch32v30x.h @@ -0,0 +1,30 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : system_ch32v30x.h +* Author : WCH +* Version : V1.0.0 +* Date : 2021/06/06 +* Description : CH32V30x Device Peripheral Access Layer System Header File. +* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +* SPDX-License-Identifier: Apache-2.0 +*******************************************************************************/ +#ifndef __SYSTEM_CH32V30x_H +#define __SYSTEM_CH32V30x_H + +#ifdef __cplusplus + extern "C" { +#endif + +extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */ + +/* System_Exported_Functions */ +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); + +#ifdef __cplusplus +} +#endif + +#endif /*__CH32V30x_SYSTEM_H */ + + + diff --git a/hw/bsp/ch32v307/wch-riscv.cfg b/hw/bsp/ch32v307/wch-riscv.cfg new file mode 100644 index 000000000..0d24d16ca --- /dev/null +++ b/hw/bsp/ch32v307/wch-riscv.cfg @@ -0,0 +1,15 @@ +#interface wlink +adapter driver wlink +wlink_set +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 + +echo "Ready for Remote Connections" diff --git a/hw/bsp/esp32s2/components/led_strip/src/led_strip_rmt_ws2812.c b/hw/bsp/esp32s2/components/led_strip/src/led_strip_rmt_ws2812.c index 025d3c590..fd1746cad 100644 --- a/hw/bsp/esp32s2/components/led_strip/src/led_strip_rmt_ws2812.c +++ b/hw/bsp/esp32s2/components/led_strip/src/led_strip_rmt_ws2812.c @@ -50,7 +50,7 @@ typedef struct { } ws2812_t; /** - * @brief Conver RGB data to RMT format. + * @brief Convert RGB data to RMT format. * * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t) * diff --git a/hw/bsp/esp32s3/components/led_strip/src/led_strip_rmt_ws2812.c b/hw/bsp/esp32s3/components/led_strip/src/led_strip_rmt_ws2812.c index 025d3c590..fd1746cad 100644 --- a/hw/bsp/esp32s3/components/led_strip/src/led_strip_rmt_ws2812.c +++ b/hw/bsp/esp32s3/components/led_strip/src/led_strip_rmt_ws2812.c @@ -50,7 +50,7 @@ typedef struct { } ws2812_t; /** - * @brief Conver RGB data to RMT format. + * @brief Convert RGB data to RMT format. * * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t) * diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake index c5311b63f..601cd54f5 100644 --- a/hw/bsp/family_support.cmake +++ b/hw/bsp/family_support.cmake @@ -123,12 +123,12 @@ if (NOT TARGET _family_support_marker) # configure an executable target to link to tinyusb in device mode, and add the board implementation function(family_configure_device_example TARGET) - # default implentation is empty, the function should be redefined in the FAMILY/family.cmake + # default implementation is empty, the function should be redefined in the FAMILY/family.cmake endfunction() # configure an executable target to link to tinyusb in host mode, and add the board implementation function(family_configure_host_example TARGET) - # default implentation is empty, the function should be redefined in the FAMILY/family.cmake + # default implementation is empty, the function should be redefined in the FAMILY/family.cmake endfunction() include(${CMAKE_CURRENT_LIST_DIR}/${FAMILY}/family.cmake) diff --git a/hw/bsp/gd32vf103/family.c b/hw/bsp/gd32vf103/family.c index c20732302..60a326d27 100644 --- a/hw/bsp/gd32vf103/family.c +++ b/hw/bsp/gd32vf103/family.c @@ -112,7 +112,7 @@ void board_init(void) { otg_core_regs->GCCFG &= ~GCCFG_VBUSIG; #endif - /* Enable interrupts globaly */ + /* Enable interrupts globally */ __enable_irq(); } @@ -120,7 +120,7 @@ void gd32vf103_reset(void) { /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST * register to generate a software reset request. * BUT instead two undocumented registers in the debug peripheral - * that allow issueing a software reset. + * that allow issuing a software reset. * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */ DBG_KEY = DBG_KEY_UNLOCK; diff --git a/hw/bsp/gd32vf103/system_gd32vf103.c b/hw/bsp/gd32vf103/system_gd32vf103.c index 29518a54a..c2001a980 100644 --- a/hw/bsp/gd32vf103/system_gd32vf103.c +++ b/hw/bsp/gd32vf103/system_gd32vf103.c @@ -574,7 +574,7 @@ void ECLIC_Init(void) * \param [in] IRQn NMI interrupt handler address * \param [in] shv \ref ECLIC_NON_VECTOR_INTERRUPT means non-vector mode, and \ref ECLIC_VECTOR_INTERRUPT is vector mode * \param [in] trig_mode see \ref ECLIC_TRIGGER_Type - * \param [in] lvl interupt level + * \param [in] lvl interrupt level * \param [in] priority interrupt priority * \param [in] handler interrupt handler, if NULL, handler will not be installed * \return -1 means invalid input parameter. 0 means successful. diff --git a/hw/bsp/imxrt/boards/mimxrt1010_evk/evkmimxrt1010_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1010_evk/evkmimxrt1010_flexspi_nor_config.h index 4be2760be..bb5a64448 100644 --- a/hw/bsp/imxrt/boards/mimxrt1010_evk/evkmimxrt1010_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1010_evk/evkmimxrt1010_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -123,7 +123,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -184,7 +184,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -252,7 +252,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/mimxrt1015_evk/evkmimxrt1015_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1015_evk/evkmimxrt1015_flexspi_nor_config.h index 94af5a115..f1415aeec 100644 --- a/hw/bsp/imxrt/boards/mimxrt1015_evk/evkmimxrt1015_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1015_evk/evkmimxrt1015_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h index f5e1aca5c..ad79d5002 100644 --- a/hw/bsp/imxrt/boards/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/mimxrt1024_evk/board.h b/hw/bsp/imxrt/boards/mimxrt1024_evk/board.h new file mode 100644 index 000000000..152c9ab18 --- /dev/null +++ b/hw/bsp/imxrt/boards/mimxrt1024_evk/board.h @@ -0,0 +1,52 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef BOARD_H_ +#define BOARD_H_ + +// required since iMX RT10xx SDK include this file for board size +// RT1020-EVK #define BOARD_FLASH_SIZE (0x800000U) +#define BOARD_FLASH_SIZE (0x400000U) // builtin flash of RT1024 + +// LED - DRN updated for RT1024EVK +#define LED_PINMUX IOMUXC_GPIO_AD_B1_08_GPIO1_IO24 +#define LED_PORT GPIO1 +#define LED_PIN 24 +#define LED_STATE_ON 1 + +// SW8 button - DRN verified +#define BUTTON_PINMUX IOMUXC_SNVS_WAKEUP_GPIO5_IO00 +#define BUTTON_PORT GPIO5 +#define BUTTON_PIN 0 +#define BUTTON_STATE_ACTIVE 0 + +// UART - DRN verified +#define UART_PORT LPUART1 +#define UART_RX_PINMUX IOMUXC_GPIO_AD_B0_07_LPUART1_RX +#define UART_TX_PINMUX IOMUXC_GPIO_AD_B0_06_LPUART1_TX + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/imxrt/boards/mimxrt1024_evk/board.mk b/hw/bsp/imxrt/boards/mimxrt1024_evk/board.mk new file mode 100644 index 000000000..92209992d --- /dev/null +++ b/hw/bsp/imxrt/boards/mimxrt1024_evk/board.mk @@ -0,0 +1,14 @@ +CFLAGS += -DCPU_MIMXRT1024DAG5A +MCU_VARIANT = MIMXRT1024 + +# warnings caused by mcu driver +CFLAGS += -Wno-error=array-bounds + +# For flash-jlink target +JLINK_DEVICE = MIMXRT1024DAG5A + +# For flash-pyocd target +PYOCD_TARGET = mimxrt1024 + +# flash using pyocd +flash: flash-pyocd diff --git a/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.c b/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.c new file mode 100644 index 000000000..43dc1e823 --- /dev/null +++ b/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.c @@ -0,0 +1,48 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "evkmimxrt1024_flexspi_nor_config.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"), used)) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally, + .csHoldTime = 3u, + .csSetupTime = 3u, + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_60MHz, + .sflashA1Size = 4u * 1024u * 1024u, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 64u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.h new file mode 100644 index 000000000..5231dc034 --- /dev/null +++ b/hw/bsp/imxrt/boards/mimxrt1024_evk/evkmimxrt1024_flexspi_nor_config.h @@ -0,0 +1,266 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1024_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1024_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.1. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 1)) +/*@}*/ + +/* FLEXSPI memory config block related definitions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related definitions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_133MHz = 7, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1024_FLEXSPI_NOR_CONFIG__ */ diff --git a/hw/bsp/imxrt/boards/mimxrt1050_evkb/evkbimxrt1050_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1050_evkb/evkbimxrt1050_flexspi_nor_config.h index fe40e7ed7..7a14360e4 100644 --- a/hw/bsp/imxrt/boards/mimxrt1050_evkb/evkbimxrt1050_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1050_evkb/evkbimxrt1050_flexspi_nor_config.h @@ -19,7 +19,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -27,7 +27,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -125,7 +125,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -186,7 +186,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -254,7 +254,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/mimxrt1060_evk/evkmimxrt1060_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1060_evk/evkmimxrt1060_flexspi_nor_config.h index 28d7db57d..b4ef4ad4f 100644 --- a/hw/bsp/imxrt/boards/mimxrt1060_evk/evkmimxrt1060_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1060_evk/evkmimxrt1060_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/mimxrt1064_evk/evkmimxrt1064_flexspi_nor_config.h b/hw/bsp/imxrt/boards/mimxrt1064_evk/evkmimxrt1064_flexspi_nor_config.h index efdfe583f..995e88cb9 100644 --- a/hw/bsp/imxrt/boards/mimxrt1064_evk/evkmimxrt1064_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/mimxrt1064_evk/evkmimxrt1064_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/teensy_40/teensy40_flexspi_nor_config.h b/hw/bsp/imxrt/boards/teensy_40/teensy40_flexspi_nor_config.h index 56068ec6a..d24d4f10a 100644 --- a/hw/bsp/imxrt/boards/teensy_40/teensy40_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/teensy_40/teensy40_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/boards/teensy_41/teensy41_flexspi_nor_config.h b/hw/bsp/imxrt/boards/teensy_41/teensy41_flexspi_nor_config.h index 56068ec6a..d24d4f10a 100644 --- a/hw/bsp/imxrt/boards/teensy_41/teensy41_flexspi_nor_config.h +++ b/hw/bsp/imxrt/boards/teensy_41/teensy41_flexspi_nor_config.h @@ -18,7 +18,7 @@ #define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*@}*/ -/* FLEXSPI memory config block related defintions */ +/* FLEXSPI memory config block related definitions */ #define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian #define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 #define FLEXSPI_CFG_BLK_SIZE (512) @@ -26,7 +26,7 @@ /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 -/* Lookup table related defintions */ +/* Lookup table related definitions */ #define CMD_INDEX_READ 0 #define CMD_INDEX_READSTATUS 1 #define CMD_INDEX_WRITEENABLE 2 @@ -124,7 +124,7 @@ enum kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial RAMs }; //!@brief Flash Pad Definitions @@ -185,7 +185,7 @@ typedef struct _FlexSPIConfig //! details uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequency, device specific definitions, See System Boot //! Chapter for more details uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH @@ -253,7 +253,7 @@ typedef struct _flexspi_nor_config uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP command execution uint32_t blockSize; //!< Block size uint32_t reserve2[11]; //!< Reserved for future use } flexspi_nor_config_t; diff --git a/hw/bsp/imxrt/family.mk b/hw/bsp/imxrt/family.mk index 846ecd255..0642e52e3 100644 --- a/hw/bsp/imxrt/family.mk +++ b/hw/bsp/imxrt/family.mk @@ -30,7 +30,7 @@ MCU_DIR = $(SDK_DIR)/devices/$(MCU_VARIANT) # All source paths should be relative to the top level. LD_FILE = $(MCU_DIR)/gcc/$(MCU_VARIANT)xxxxx_flexspi_nor.ld -# TODO for net_lwip_webserver exmaple, but may not needed !! +# TODO for net_lwip_webserver example, but may not needed !! LDFLAGS += \ -Wl,--defsym,__stack_size__=0x800 \ diff --git a/hw/bsp/lpc54/family.c b/hw/bsp/lpc54/family.c index 9bb341e73..621788948 100644 --- a/hw/bsp/lpc54/family.c +++ b/hw/bsp/lpc54/family.c @@ -170,7 +170,7 @@ void board_init(void) CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1, false); CLOCK_AttachClk(kFRO_HF_to_USB0_CLK); - /*According to reference mannual, device mode setting has to be set by access usb host register */ + /*According to reference manual, device mode setting has to be set by access usb host register */ CLOCK_EnableClock(kCLOCK_Usbhsl0); /* enable usb0 host clock */ USBFSH->PORTMODE |= USBFSH_PORTMODE_DEV_ENABLE_MASK; CLOCK_DisableClock(kCLOCK_Usbhsl0); /* disable usb0 host clock */ @@ -182,7 +182,7 @@ void board_init(void) // Port1 is High Speed POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); - /*According to reference mannual, device mode setting has to be set by access usb host register */ + /*According to reference manual, device mode setting has to be set by access usb host register */ CLOCK_EnableClock(kCLOCK_Usbh1); /* enable usb1 host clock */ USBHSH->PORTMODE |= USBHSH_PORTMODE_DEV_ENABLE_MASK; CLOCK_DisableClock(kCLOCK_Usbh1); /* enable usb1 host clock */ diff --git a/hw/bsp/lpc55/family.c b/hw/bsp/lpc55/family.c index e7aed5759..5b6c56dd3 100644 --- a/hw/bsp/lpc55/family.c +++ b/hw/bsp/lpc55/family.c @@ -192,7 +192,7 @@ void board_init(void) CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1, false); CLOCK_AttachClk(kFRO_HF_to_USB0_CLK); - /*According to reference mannual, device mode setting has to be set by access usb host register */ + /*According to reference manual, device mode setting has to be set by access usb host register */ CLOCK_EnableClock(kCLOCK_Usbhsl0); // enable usb0 host clock USBFSH->PORTMODE |= USBFSH_PORTMODE_DEV_ENABLE_MASK; CLOCK_DisableClock(kCLOCK_Usbhsl0); // disable usb0 host clock @@ -213,7 +213,7 @@ void board_init(void) RESET_PeripheralReset(kUSB1_RST_SHIFT_RSTn); RESET_PeripheralReset(kUSB1RAM_RST_SHIFT_RSTn); - /* According to reference mannual, device mode setting has to be set by access usb host register */ + /* According to reference manual, device mode setting has to be set by access usb host register */ CLOCK_EnableClock(kCLOCK_Usbh1); // enable usb0 host clock USBHSH->PORTMODE = USBHSH_PORTMODE_SW_PDCOM_MASK; // Put PHY powerdown under software control diff --git a/hw/bsp/mm32/boards/mm32f327x_mb39/flash.ld b/hw/bsp/mm32/boards/mm32f327x_mb39/flash.ld index d96d6e43d..0b45ee7bd 100644 --- a/hw/bsp/mm32/boards/mm32f327x_mb39/flash.ld +++ b/hw/bsp/mm32/boards/mm32f327x_mb39/flash.ld @@ -126,7 +126,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index 26008168d..f7ee56990 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -226,6 +226,6 @@ int board_getchar(void) //--------------------------------------------------------------------+ // USB Interrupt Handler -// rp2040 implementation will install approriate handler when initializing +// rp2040 implementation will install appropriate handler when initializing // tinyusb. There is no need to forward IRQ from application //--------------------------------------------------------------------+ diff --git a/hw/bsp/sltb009a/sltb009a.c b/hw/bsp/sltb009a/sltb009a.c index b929adb13..b5eb5ed5d 100644 --- a/hw/bsp/sltb009a/sltb009a.c +++ b/hw/bsp/sltb009a/sltb009a.c @@ -397,7 +397,7 @@ void cmu_init(void) CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFXO; while((CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) != CMU_HFCLKSTATUS_SELECTED_HFXO); - // Calibrate HFRCO for 72MHz and enable tunning by PLL + // Calibrate HFRCO for 72MHz and enable tuning by PLL cmu_hfrco_calib((DEVINFO->HFRCOCAL16) | CMU_HFRCOCTRL_FINETUNINGEN); // Setup the PLL diff --git a/hw/bsp/stm32f0/boards/stm32f070rbnucleo/stm32F070rbtx_flash.ld b/hw/bsp/stm32f0/boards/stm32f070rbnucleo/stm32F070rbtx_flash.ld index 59e18f375..4a6491510 100644 --- a/hw/bsp/stm32f0/boards/stm32f070rbnucleo/stm32F070rbtx_flash.ld +++ b/hw/bsp/stm32f0/boards/stm32f070rbnucleo/stm32F070rbtx_flash.ld @@ -165,7 +165,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f0/boards/stm32f072disco/STM32F072RBTx_FLASH.ld b/hw/bsp/stm32f0/boards/stm32f072disco/STM32F072RBTx_FLASH.ld index 8d31f6a00..f0879e929 100644 --- a/hw/bsp/stm32f0/boards/stm32f072disco/STM32F072RBTx_FLASH.ld +++ b/hw/bsp/stm32f0/boards/stm32f072disco/STM32F072RBTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld b/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld index ca1804905..c434ca038 100644 --- a/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld +++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld b/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld index da40d1eb2..da637d1b0 100644 --- a/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld +++ b/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f207nucleo/STM32F207ZGTx_FLASH.ld b/hw/bsp/stm32f207nucleo/STM32F207ZGTx_FLASH.ld index 29e387f28..5010435ae 100644 --- a/hw/bsp/stm32f207nucleo/STM32F207ZGTx_FLASH.ld +++ b/hw/bsp/stm32f207nucleo/STM32F207ZGTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f303disco/STM32F303VCTx_FLASH.ld b/hw/bsp/stm32f303disco/STM32F303VCTx_FLASH.ld index ca9046d29..7f46c71b2 100644 --- a/hw/bsp/stm32f303disco/STM32F303VCTx_FLASH.ld +++ b/hw/bsp/stm32f303disco/STM32F303VCTx_FLASH.ld @@ -150,7 +150,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/feather_stm32f405/STM32F405RGTx_FLASH.ld b/hw/bsp/stm32f4/boards/feather_stm32f405/STM32F405RGTx_FLASH.ld index 57ef61e26..9eb53bcc0 100644 --- a/hw/bsp/stm32f4/boards/feather_stm32f405/STM32F405RGTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/feather_stm32f405/STM32F405RGTx_FLASH.ld @@ -150,7 +150,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/pyboardv11/STM32F405RGTx_FLASH.ld b/hw/bsp/stm32f4/boards/pyboardv11/STM32F405RGTx_FLASH.ld index 57ef61e26..9eb53bcc0 100644 --- a/hw/bsp/stm32f4/boards/pyboardv11/STM32F405RGTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/pyboardv11/STM32F405RGTx_FLASH.ld @@ -150,7 +150,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f401blackpill/STM32F401VCTx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f401blackpill/STM32F401VCTx_FLASH.ld index f51f1ab41..2bc5f6c14 100644 --- a/hw/bsp/stm32f4/boards/stm32f401blackpill/STM32F401VCTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f401blackpill/STM32F401VCTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f401blackpill/board.h b/hw/bsp/stm32f4/boards/stm32f401blackpill/board.h index 0f820512c..e1fef7277 100644 --- a/hw/bsp/stm32f4/boards/stm32f401blackpill/board.h +++ b/hw/bsp/stm32f4/boards/stm32f401blackpill/board.h @@ -93,7 +93,7 @@ static inline void board_clock_init(void) static inline void board_vbus_sense_init(void) { - // Blackpill doens't use VBUS sense (B device) explicitly disable it + // Blackpill doesn't use VBUS sense (B device) explicitly disable it USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_NOVBUSSENS; USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBUSBSEN; USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBUSASEN; diff --git a/hw/bsp/stm32f4/boards/stm32f407disco/STM32F407VGTx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f407disco/STM32F407VGTx_FLASH.ld index c41602720..aac6577ea 100644 --- a/hw/bsp/stm32f4/boards/stm32f407disco/STM32F407VGTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f407disco/STM32F407VGTx_FLASH.ld @@ -150,7 +150,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f411blackpill/STM32F411CEUx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f411blackpill/STM32F411CEUx_FLASH.ld index efea1e067..56dcea605 100644 --- a/hw/bsp/stm32f4/boards/stm32f411blackpill/STM32F411CEUx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f411blackpill/STM32F411CEUx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f411blackpill/board.h b/hw/bsp/stm32f4/boards/stm32f411blackpill/board.h index 0f820512c..e1fef7277 100644 --- a/hw/bsp/stm32f4/boards/stm32f411blackpill/board.h +++ b/hw/bsp/stm32f4/boards/stm32f411blackpill/board.h @@ -93,7 +93,7 @@ static inline void board_clock_init(void) static inline void board_vbus_sense_init(void) { - // Blackpill doens't use VBUS sense (B device) explicitly disable it + // Blackpill doesn't use VBUS sense (B device) explicitly disable it USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_NOVBUSSENS; USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBUSBSEN; USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBUSASEN; diff --git a/hw/bsp/stm32f4/boards/stm32f411disco/STM32F411VETx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f411disco/STM32F411VETx_FLASH.ld index 3a0ce526f..4477229ea 100644 --- a/hw/bsp/stm32f4/boards/stm32f411disco/STM32F411VETx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f411disco/STM32F411VETx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f412disco/STM32F412ZGTx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f412disco/STM32F412ZGTx_FLASH.ld index b00b5dbe0..2372cc1d9 100644 --- a/hw/bsp/stm32f4/boards/stm32f412disco/STM32F412ZGTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f412disco/STM32F412ZGTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f4/boards/stm32f412nucleo/STM32F412ZGTx_FLASH.ld b/hw/bsp/stm32f4/boards/stm32f412nucleo/STM32F412ZGTx_FLASH.ld index b00b5dbe0..2372cc1d9 100644 --- a/hw/bsp/stm32f4/boards/stm32f412nucleo/STM32F412ZGTx_FLASH.ld +++ b/hw/bsp/stm32f4/boards/stm32f412nucleo/STM32F412ZGTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stlinkv3mini/STM32F723xE_FLASH.ld b/hw/bsp/stm32f7/boards/stlinkv3mini/STM32F723xE_FLASH.ld index 8645ce5c5..f4be85703 100644 --- a/hw/bsp/stm32f7/boards/stlinkv3mini/STM32F723xE_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stlinkv3mini/STM32F723xE_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stm32f723disco/STM32F723xE_FLASH.ld b/hw/bsp/stm32f7/boards/stm32f723disco/STM32F723xE_FLASH.ld index 8645ce5c5..f4be85703 100644 --- a/hw/bsp/stm32f7/boards/stm32f723disco/STM32F723xE_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stm32f723disco/STM32F723xE_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stm32f746disco/STM32F746ZGTx_FLASH.ld b/hw/bsp/stm32f7/boards/stm32f746disco/STM32F746ZGTx_FLASH.ld index 045ec76f9..eeb0e29f3 100644 --- a/hw/bsp/stm32f7/boards/stm32f746disco/STM32F746ZGTx_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stm32f746disco/STM32F746ZGTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stm32f746nucleo/STM32F746ZGTx_FLASH.ld b/hw/bsp/stm32f7/boards/stm32f746nucleo/STM32F746ZGTx_FLASH.ld index b434a01b7..e1e60bc78 100644 --- a/hw/bsp/stm32f7/boards/stm32f746nucleo/STM32F746ZGTx_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stm32f746nucleo/STM32F746ZGTx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stm32f767nucleo/STM32F767ZITx_FLASH.ld b/hw/bsp/stm32f7/boards/stm32f767nucleo/STM32F767ZITx_FLASH.ld index 0b6d5a494..3785f9cbf 100644 --- a/hw/bsp/stm32f7/boards/stm32f767nucleo/STM32F767ZITx_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stm32f767nucleo/STM32F767ZITx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32f7/boards/stm32f769disco/STM32F769ZITx_FLASH.ld b/hw/bsp/stm32f7/boards/stm32f769disco/STM32F769ZITx_FLASH.ld index 378ed80dd..520a75539 100644 --- a/hw/bsp/stm32f7/boards/stm32f769disco/STM32F769ZITx_FLASH.ld +++ b/hw/bsp/stm32f7/boards/stm32f769disco/STM32F769ZITx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32g4/boards/stm32g474nucleo/STM32G474RETx_FLASH.ld b/hw/bsp/stm32g4/boards/stm32g474nucleo/STM32G474RETx_FLASH.ld index 935d97c87..9c327483b 100644 --- a/hw/bsp/stm32g4/boards/stm32g474nucleo/STM32G474RETx_FLASH.ld +++ b/hw/bsp/stm32g4/boards/stm32g474nucleo/STM32G474RETx_FLASH.ld @@ -16,7 +16,7 @@ ** ** Target : STMicroelectronics STM32 ** -** Distribution: The file is distributed as is, without any warranty +** Distribution: The file is distributed �as is,� without any warranty ** of any kind. ** ***************************************************************************** @@ -151,7 +151,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32g4/boards/stm32g474nucleo/board.h b/hw/bsp/stm32g4/boards/stm32g474nucleo/board.h index eab0bd5f0..aa2bf20bb 100644 --- a/hw/bsp/stm32g4/boards/stm32g474nucleo/board.h +++ b/hw/bsp/stm32g4/boards/stm32g474nucleo/board.h @@ -66,7 +66,7 @@ static inline void board_clock_init(void) // Configure the main internal regulator output voltage HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); - // Initializes the CPU, AHB and APB busses clocks + // Initializes the CPU, AHB and APB buses clocks RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; @@ -79,7 +79,7 @@ static inline void board_clock_init(void) RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; HAL_RCC_OscConfig(&RCC_OscInitStruct); - // Initializes the CPU, AHB and APB busses clocks + // Initializes the CPU, AHB and APB buses clocks RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; diff --git a/hw/bsp/stm32h7/boards/stm32h723nucleo/board.h b/hw/bsp/stm32h7/boards/stm32h723nucleo/board.h new file mode 100644 index 000000000..56a48ec24 --- /dev/null +++ b/hw/bsp/stm32h7/boards/stm32h723nucleo/board.h @@ -0,0 +1,131 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#define LED_PORT GPIOB +#define LED_PIN GPIO_PIN_0 +#define LED_STATE_ON 1 + +#define BUTTON_PORT GPIOC +#define BUTTON_PIN GPIO_PIN_13 +#define BUTTON_STATE_ACTIVE 1 + +#define UART_DEV USART3 +#define UART_CLK_EN __HAL_RCC_USART3_CLK_ENABLE +#define UART_GPIO_PORT GPIOD +#define UART_GPIO_AF GPIO_AF7_USART3 +#define UART_TX_PIN GPIO_PIN_8 +#define UART_RX_PIN GPIO_PIN_9 + +// VBUS Sense detection +#define OTG_FS_VBUS_SENSE 1 +#define OTG_HS_VBUS_SENSE 0 + +// STM32F723 has only one USB HS peripheral +// Nucleo board does not have ULPI so USB will operate in FS mode only +// For the rest of the synopsys driver it is FS device however there +// is only USB_OTG_HS defined. Here are required conversions to +// make peripheral FS. +#define __HAL_RCC_USB2_OTG_FS_CLK_ENABLE __HAL_RCC_USB1_OTG_HS_CLK_ENABLE +#define GPIO_AF10_OTG2_HS GPIO_AF10_OTG1_HS +#define USB_OTG_FS USB_OTG_HS + +//--------------------------------------------------------------------+ +// RCC Clock +//--------------------------------------------------------------------+ +static inline void board_stm32h7_clock_init(void) +{ + RCC_ClkInitTypeDef RCC_ClkInitStruct; + RCC_OscInitTypeDef RCC_OscInitStruct; + + /* The PWR block is always enabled on the H7 series- there is no clock + enable. For now, use the default VOS3 scale mode (lowest) and limit clock + frequencies to avoid potential current draw problems from bus + power when using the max clock speeds throughout the chip. */ + + /* Enable HSE Oscillator and activate PLL1 with HSE as source */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.HSIState = RCC_HSI_OFF; + RCC_OscInitStruct.CSIState = RCC_CSI_OFF; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = HSE_VALUE/1000000; + RCC_OscInitStruct.PLL.PLLN = 336; + RCC_OscInitStruct.PLL.PLLP = 2; + RCC_OscInitStruct.PLL.PLLQ = 7; + RCC_OscInitStruct.PLL.PLLR = 2; /* Unused */ + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_0; + RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOMEDIUM; + RCC_OscInitStruct.PLL.PLLFRACN = 0; + HAL_RCC_OscConfig(&RCC_OscInitStruct); + + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | \ + RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | \ + RCC_CLOCKTYPE_D3PCLK1); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1; + + /* Unlike on the STM32F4 family, it appears the maximum APB frequencies are + device-dependent- 120 MHz for this board according to Figure 2 of + the datasheet. Dividing by half will be safe for now. */ + RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; + RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; + RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; + RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + + /* 4 wait states required for 168MHz and VOS3. */ + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); + + /* Like on F4, on H7, USB's actual peripheral clock and bus clock are + separate. However, the main system PLL (PLL1) doesn't have a direct + connection to the USB peripheral clock to generate 48 MHz, so we do this + dance. This will connect PLL1's Q output to the USB peripheral clock. */ + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; + RCC_PeriphCLKInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL; + HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); +} + +static inline void board_stm32h7_post_init(void) +{ + // For this board does nothing +} + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/hw/bsp/stm32h7/boards/stm32h723nucleo/board.mk b/hw/bsp/stm32h7/boards/stm32h723nucleo/board.mk new file mode 100644 index 000000000..bbd0a0e58 --- /dev/null +++ b/hw/bsp/stm32h7/boards/stm32h723nucleo/board.mk @@ -0,0 +1,13 @@ +CFLAGS += -DSTM32H723xx -DHSE_VALUE=8000000 + +# Default is FulSpeed port +PORT ?= 0 + +SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32h723xx.s +LD_FILE = $(BOARD_PATH)/stm32h723xx_flash.ld + +# For flash-jlink target +JLINK_DEVICE = stm32h723zg + +# flash target using on-board stlink +flash: flash-stlink diff --git a/hw/bsp/stm32h7/boards/stm32h723nucleo/stm32h723xx_flash.ld b/hw/bsp/stm32h7/boards/stm32h723nucleo/stm32h723xx_flash.ld new file mode 100644 index 000000000..05e0d4e26 --- /dev/null +++ b/hw/bsp/stm32h7/boards/stm32h723nucleo/stm32h723xx_flash.ld @@ -0,0 +1,192 @@ +/* +****************************************************************************** +** +** File : LinkerScript.ld +** +** Author : STM32CubeIDE +** +** Abstract : Linker script for STM32H7 series +** 1024Kbytes FLASH and 560Kbytes RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +***************************************************************************** +** @attention +** +** Copyright (c) 2021 STMicroelectronics. +** All rights reserved. +** +** This software is licensed under terms that can be found in the LICENSE file +** in the root directory of this software component. +** If no LICENSE file comes with this software, it is provided AS-IS. +** +**************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200 ; /* required amount of heap */ +_Min_Stack_Size = 0x400 ; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K + DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K + RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K + RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM_D1 AT> FLASH + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM_D1 + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM_D1 + + .usbx_data 0x24027000 (NOLOAD): + { + *(.UsbHpcdSection) + + } >RAM_D1 + + .uart_bss 0x24028000 (NOLOAD): + { + *(.UsbxAppSection) + + } >RAM_D1 + + .usbx_bss 0x24029000 (NOLOAD): + { + *(.UsbxPoolSection) + + } >RAM_D1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/hw/bsp/stm32h7/boards/stm32h743eval/stm32h743xx_flash.ld b/hw/bsp/stm32h7/boards/stm32h743eval/stm32h743xx_flash.ld index 59b9ff4df..7ee40671c 100644 --- a/hw/bsp/stm32h7/boards/stm32h743eval/stm32h743xx_flash.ld +++ b/hw/bsp/stm32h7/boards/stm32h743eval/stm32h743xx_flash.ld @@ -134,7 +134,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32h7/boards/stm32h743nucleo/stm32h743xx_flash.ld b/hw/bsp/stm32h7/boards/stm32h743nucleo/stm32h743xx_flash.ld index 59b9ff4df..7ee40671c 100644 --- a/hw/bsp/stm32h7/boards/stm32h743nucleo/stm32h743xx_flash.ld +++ b/hw/bsp/stm32h7/boards/stm32h743nucleo/stm32h743xx_flash.ld @@ -134,7 +134,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32h7/boards/waveshare_openh743i/STM32H743IITX_FLASH.ld b/hw/bsp/stm32h7/boards/waveshare_openh743i/STM32H743IITX_FLASH.ld index 73d2dedb2..e99bb977b 100644 --- a/hw/bsp/stm32h7/boards/waveshare_openh743i/STM32H743IITX_FLASH.ld +++ b/hw/bsp/stm32h7/boards/waveshare_openh743i/STM32H743IITX_FLASH.ld @@ -138,7 +138,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32h7/boards/waveshare_openh743i/board.h b/hw/bsp/stm32h7/boards/waveshare_openh743i/board.h index 09442c233..2b072e07c 100644 --- a/hw/bsp/stm32h7/boards/waveshare_openh743i/board.h +++ b/hw/bsp/stm32h7/boards/waveshare_openh743i/board.h @@ -186,7 +186,7 @@ static inline void timer_board_delay(TIM_HandleTypeDef* tim_hdl, uint32_t ms) static inline void board_stm32h7_post_init(void) { - // walkaround for reseting the ULPI PHY using Timer since systick is not + // walkaround for resetting the ULPI PHY using Timer since systick is not // available when RTOS is used. // Init timer diff --git a/hw/bsp/stm32h7/family.c b/hw/bsp/stm32h7/family.c index 3964f427a..0aa8fe47f 100644 --- a/hw/bsp/stm32h7/family.c +++ b/hw/bsp/stm32h7/family.c @@ -66,10 +66,12 @@ void board_init(void) __HAL_RCC_GPIOC_CLK_ENABLE(); // USB ULPI NXT __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); - __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); // USB ULPI NXT +#ifdef __HAL_RCC_GPIOI_CLK_ENABLE __HAL_RCC_GPIOI_CLK_ENABLE(); // USB ULPI NXT +#endif __HAL_RCC_GPIOJ_CLK_ENABLE(); // Enable UART Clock @@ -84,7 +86,9 @@ void board_init(void) SysTick->CTRL &= ~1U; // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) +#ifdef USB_OTG_FS_PERIPH_BASE NVIC_SetPriority(OTG_FS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); +#endif NVIC_SetPriority(OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); #endif diff --git a/hw/bsp/stm32l0/boards/stm32l052dap52/STM32L052K8Ux_FLASH.ld b/hw/bsp/stm32l0/boards/stm32l052dap52/STM32L052K8Ux_FLASH.ld index 1bc16cc7b..dfefe59f1 100644 --- a/hw/bsp/stm32l0/boards/stm32l052dap52/STM32L052K8Ux_FLASH.ld +++ b/hw/bsp/stm32l0/boards/stm32l052dap52/STM32L052K8Ux_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32l0538disco/STM32L053C8Tx_FLASH.ld b/hw/bsp/stm32l0538disco/STM32L053C8Tx_FLASH.ld index 787418e09..79427d80b 100644 --- a/hw/bsp/stm32l0538disco/STM32L053C8Tx_FLASH.ld +++ b/hw/bsp/stm32l0538disco/STM32L053C8Tx_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32l4/boards/stm32l412nucleo/STM32L412KBUx_FLASH.ld b/hw/bsp/stm32l4/boards/stm32l412nucleo/STM32L412KBUx_FLASH.ld index 28cd96131..d4b86b3e3 100644 --- a/hw/bsp/stm32l4/boards/stm32l412nucleo/STM32L412KBUx_FLASH.ld +++ b/hw/bsp/stm32l4/boards/stm32l412nucleo/STM32L412KBUx_FLASH.ld @@ -145,7 +145,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32l4/boards/stm32l476disco/STM32L476VGTx_FLASH.ld b/hw/bsp/stm32l4/boards/stm32l476disco/STM32L476VGTx_FLASH.ld index 98f468ac0..d6865f49d 100644 --- a/hw/bsp/stm32l4/boards/stm32l476disco/STM32L476VGTx_FLASH.ld +++ b/hw/bsp/stm32l4/boards/stm32l476disco/STM32L476VGTx_FLASH.ld @@ -144,7 +144,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32l4/boards/stm32l4r5nucleo/STM32L4RXxI_FLASH.ld b/hw/bsp/stm32l4/boards/stm32l4r5nucleo/STM32L4RXxI_FLASH.ld index f93a16041..f77c72d1b 100644 --- a/hw/bsp/stm32l4/boards/stm32l4r5nucleo/STM32L4RXxI_FLASH.ld +++ b/hw/bsp/stm32l4/boards/stm32l4r5nucleo/STM32L4RXxI_FLASH.ld @@ -130,7 +130,7 @@ SECTIONS . = ALIGN(4); .bss : { - /* This is used by the startup in order to initialize the .bss secion */ + /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) diff --git a/hw/bsp/stm32u5/boards/stm32u575eval/board.h b/hw/bsp/stm32u5/boards/stm32u575eval/board.h index 5c348b812..5562b95a8 100644 --- a/hw/bsp/stm32u5/boards/stm32u575eval/board.h +++ b/hw/bsp/stm32u5/boards/stm32u575eval/board.h @@ -69,7 +69,7 @@ static inline void board_clock_init(void) */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); - /** Initializes the CPU, AHB and APB busses clocks + /** Initializes the CPU, AHB and APB buses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; @@ -92,7 +92,7 @@ static inline void board_clock_init(void) HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); - /** Initializes the CPU, AHB and APB busses clocks + /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_PCLK3; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; diff --git a/hw/bsp/stm32wb/boards/stm32wb55nucleo/board.h b/hw/bsp/stm32wb/boards/stm32wb55nucleo/board.h index f42c7d6dd..ea975df03 100644 --- a/hw/bsp/stm32wb/boards/stm32wb55nucleo/board.h +++ b/hw/bsp/stm32wb/boards/stm32wb55nucleo/board.h @@ -59,7 +59,7 @@ static inline void board_clock_init(void) RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; - // Initializes the CPU, AHB and APB busses clocks + // Initializes the CPU, AHB and APB buses clocks RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; @@ -72,7 +72,7 @@ static inline void board_clock_init(void) RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV3; HAL_RCC_OscConfig(&RCC_OscInitStruct); - // Initializes the CPU, AHB and APB busses clocks + // Initializes the CPU, AHB and APB buses clocks RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; diff --git a/hw/bsp/tm4c123/family.c b/hw/bsp/tm4c123/family.c index b0947d6c4..87b94289b 100644 --- a/hw/bsp/tm4c123/family.c +++ b/hw/bsp/tm4c123/family.c @@ -39,7 +39,7 @@ static void board_uart_init (void) UART0->LCRH = (0x3 << 5); // 8-bit, no parity, 1 stop bit UART0->CC = 0x0; // Configure the UART clock source as system clock - UART0->CTL = (1 << 0) | (1 << 8) | (1 << 9); // UART0 Enable, Transmit Enable, Recieve Enable + UART0->CTL = (1 << 0) | (1 << 8) | (1 << 9); // UART0 Enable, Transmit Enable, Receive Enable } static void initialize_board_led (GPIOA_Type *port, uint8_t PinMsk, uint8_t dirmsk) diff --git a/hw/mcu/wch/ch32v307 b/hw/mcu/wch/ch32v307 new file mode 160000 index 000000000..17761f5cf --- /dev/null +++ b/hw/mcu/wch/ch32v307 @@ -0,0 +1 @@ +Subproject commit 17761f5cf9dbbf2dcf665b7c04934188add20082 diff --git a/lib/SEGGER_RTT/RTT/SEGGER_RTT.c b/lib/SEGGER_RTT/RTT/SEGGER_RTT.c index 3ad2b1a12..a7ae013dd 100644 --- a/lib/SEGGER_RTT/RTT/SEGGER_RTT.c +++ b/lib/SEGGER_RTT/RTT/SEGGER_RTT.c @@ -1404,7 +1404,7 @@ int SEGGER_RTT_GetKey(void) { * SEGGER_RTT_WaitKey * * Function description -* Waits until at least one character is avaible in the SEGGER RTT buffer. +* Waits until at least one character is available in the SEGGER RTT buffer. * Once a character is available, it is read and this function returns. * * Return value diff --git a/lib/networking/dnserver.c b/lib/networking/dnserver.c index e4e7c3492..539cc2cea 100644 --- a/lib/networking/dnserver.c +++ b/lib/networking/dnserver.c @@ -93,11 +93,11 @@ static uint16_t get_uint16(const uint8_t *pnt) static int parse_next_query(void *data, int size, dns_query_t *query) { int len; - int lables; + int labels; uint8_t *ptr; len = 0; - lables = 0; + labels = 0; ptr = (uint8_t *)data; while (true) @@ -107,7 +107,7 @@ static int parse_next_query(void *data, int size, dns_query_t *query) lable_len = *ptr++; size--; if (lable_len == 0) break; - if (lables > 0) + if (labels > 0) { if (len == DNS_MAX_HOST_NAME_LEN) return -2; query->name[len++] = '.'; @@ -118,7 +118,7 @@ static int parse_next_query(void *data, int size, dns_query_t *query) len += lable_len; ptr += lable_len; size -= lable_len; - lables++; + labels++; } if (size < 4) return -1; diff --git a/lib/networking/ndis.h b/lib/networking/ndis.h index 1c737574c..06258e6bb 100644 --- a/lib/networking/ndis.h +++ b/lib/networking/ndis.h @@ -22,7 +22,7 @@ * ntddndis.h modified by Benedikt Spranger * * Thanks to the cygwin development team, - * espacially to Casper S. Hornstrup + * especially to Casper S. Hornstrup * * THIS SOFTWARE IS NOT COPYRIGHTED * diff --git a/lib/networking/rndis_reports.c b/lib/networking/rndis_reports.c index ee611c883..d129466c8 100644 --- a/lib/networking/rndis_reports.c +++ b/lib/networking/rndis_reports.c @@ -220,7 +220,7 @@ static void rndis_handle_set_msg(void) case OID_802_3_MULTICAST_LIST: break; - /* Power Managment: fails for now */ + /* Power Management: fails for now */ case OID_PNP_ADD_WAKE_UP_PATTERN: case OID_PNP_REMOVE_WAKE_UP_PATTERN: case OID_PNP_ENABLE_WAKE_UP: diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 6f9c1a6b5..ba497906b 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -721,11 +721,13 @@ typedef struct TU_ATTR_PACKED uint8_t bLength ; ///< Size of this descriptor, in bytes: 17. uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL. + uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this terminal. uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types. uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated. uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected. uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster. uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t. + uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first logical channel. uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t. uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal. } audio_desc_input_terminal_t; diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index e345139ea..2fecde3ca 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -41,16 +41,6 @@ /** \defgroup ClassDriver_CDC_Common Common Definitions * @{ */ -// TODO remove -/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In) -typedef enum -{ - CDC_PIPE_NOTIFICATION , ///< Notification pipe - CDC_PIPE_DATA_IN , ///< Data in pipe - CDC_PIPE_DATA_OUT , ///< Data out pipe - CDC_PIPE_ERROR , ///< Invalid Pipe ID -}cdc_pipeid_t; - //--------------------------------------------------------------------+ // CDC Communication Interface Class //--------------------------------------------------------------------+ @@ -192,8 +182,30 @@ typedef enum CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60, }cdc_management_request_t; +enum +{ + CDC_CONTROL_LINE_STATE_DTR = 0x01, + CDC_CONTROL_LINE_STATE_RTS = 0x02, +}; + +enum +{ + CDC_LINE_CONDING_STOP_BITS_1 = 0, // 1 bit + CDC_LINE_CONDING_STOP_BITS_1_5 = 1, // 1.5 bits + CDC_LINE_CONDING_STOP_BITS_2 = 2, // 2 bits +}; + +enum +{ + CDC_LINE_CODING_PARITY_NONE = 0, + CDC_LINE_CODING_PARITY_ODD = 1, + CDC_LINE_CODING_PARITY_EVEN = 2, + CDC_LINE_CODING_PARITY_MARK = 3, + CDC_LINE_CODING_PARITY_SPACE = 4, +}; + //--------------------------------------------------------------------+ -// Management Elemenent Notification (Notification Endpoint) +// Management Element Notification (Notification Endpoint) //--------------------------------------------------------------------+ /// 6.3 Notification Codes @@ -390,8 +402,8 @@ TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); typedef struct TU_ATTR_PACKED { - uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR. - uint16_t half_duplex_carrier_control : 1; + uint16_t dtr : 1; + uint16_t rts : 1; uint16_t : 14; } cdc_line_control_state_t; diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index fab6f0035..8d10a416c 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -62,10 +62,8 @@ typedef struct uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; -#if CFG_FIFO_MUTEX - osal_mutex_def_t rx_ff_mutex; - osal_mutex_def_t tx_ff_mutex; -#endif + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); // Endpoint Transfer buffer CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; @@ -171,7 +169,8 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) bufsize); // flush if queue more than packet size - if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE ) + // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE + if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) ) { tud_cdc_n_write_flush(itf); } @@ -247,10 +246,8 @@ void cdcd_init(void) // In this way, the most current data is prioritized. tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true); -#if CFG_FIFO_MUTEX tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex)); tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL); -#endif } } @@ -435,7 +432,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ // Received new data if ( ep_addr == p_cdc->ep_out ) { - tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, (uint16_t) xferred_bytes); + tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes); // Check for wanted char and invoke callback if needed if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index fbc7162a3..f8a004df4 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -27,7 +27,6 @@ #ifndef _TUSB_CDC_DEVICE_H_ #define _TUSB_CDC_DEVICE_H_ -#include "common/tusb_common.h" #include "cdc.h" //--------------------------------------------------------------------+ @@ -81,7 +80,7 @@ int32_t tud_cdc_n_read_char (uint8_t itf); // Clear the received FIFO void tud_cdc_n_read_flush (uint8_t itf); -// Get a byte from FIFO at the specified position without removing it +// Get a byte from FIFO without removing it bool tud_cdc_n_peek (uint8_t itf, uint8_t* ui8); // Write bytes to TX FIFO, data may remain in the FIFO for a while @@ -135,7 +134,7 @@ TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); // Invoked when received `wanted_char` TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); -// Invoked when space becomes available in TX buffer +// Invoked when a TX is complete and therefore space becomes available in TX buffer TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf); // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index ee824cb4e..e9c3d34cb 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -33,96 +33,260 @@ #include "cdc_host.h" + +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define CDCH_DEBUG 2 + +#define TU_LOG_CDCH(...) TU_LOG(CDCH_DEBUG, __VA_ARGS__) + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -typedef struct { - uint8_t itf_num; - uint8_t itf_protocol; - uint8_t ep_notif; - uint8_t ep_in; - uint8_t ep_out; +typedef struct { + uint8_t daddr; + uint8_t bInterfaceNumber; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; cdc_acm_capability_t acm_capability; + uint8_t ep_notif; -} cdch_data_t; + cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width + uint8_t line_state; // DTR (bit0), RTS (bit1) + + tuh_xfer_cb_t user_control_cb; + + struct { + tu_edpt_stream_t tx; + tu_edpt_stream_t rx; + + uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t tx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + + uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + } stream; + +} cdch_interface_t; //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -static cdch_data_t cdch_data[CFG_TUH_DEVICE_MAX]; -static inline cdch_data_t* get_itf(uint8_t dev_addr) +CFG_TUSB_MEM_SECTION +static cdch_interface_t cdch_data[CFG_TUH_CDC]; + +static inline cdch_interface_t* get_itf(uint8_t idx) { - return &cdch_data[dev_addr-1]; + TU_ASSERT(idx < CFG_TUH_CDC, NULL); + cdch_interface_t* p_cdc = &cdch_data[idx]; + + return (p_cdc->daddr != 0) ? p_cdc : NULL; } -bool tuh_cdc_mounted(uint8_t dev_addr) +static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) { - cdch_data_t* cdc = get_itf(dev_addr); - return cdc->ep_in && cdc->ep_out; -} - -bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) -{ - if ( !tuh_cdc_mounted(dev_addr) ) return false; - - cdch_data_t const * p_cdc = get_itf(dev_addr); - - switch (pipeid) + for(uint8_t i=0; iep_notif ); - - case CDC_PIPE_DATA_IN: - return usbh_edpt_busy(dev_addr, p_cdc->ep_in ); - - case CDC_PIPE_DATA_OUT: - return usbh_edpt_busy(dev_addr, p_cdc->ep_out ); - - default: - return false; + cdch_interface_t* p_cdc = &cdch_data[i]; + if ( (p_cdc->daddr == daddr) && + (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) + { + return i; + } } + + return TUSB_INDEX_INVALID; +} + + +static cdch_interface_t* find_new_itf(void) +{ + for(uint8_t i=0; idaddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i; + } + + return TUSB_INDEX_INVALID; } -bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify) +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_cdc_itf_info_t* info) { - (void) is_notify; - TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_data != NULL && length); + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && info); - uint8_t const ep_out = cdch_data[dev_addr-1].ep_out; - if ( usbh_edpt_busy(dev_addr, ep_out) ) return false; + info->daddr = p_cdc->daddr; + info->bInterfaceNumber = p_cdc->bInterfaceNumber; + info->bInterfaceSubClass = p_cdc->bInterfaceSubClass; + info->bInterfaceProtocol = p_cdc->bInterfaceProtocol; - return usbh_edpt_xfer(dev_addr, ep_out, (void*)(uintptr_t) p_data, (uint16_t) length); + return true; } -bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify) +bool tuh_cdc_mounted(uint8_t idx) { - (void) is_notify; - TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_buffer != NULL && length ); - - uint8_t const ep_in = cdch_data[dev_addr-1].ep_in; - if ( usbh_edpt_busy(dev_addr, ep_in) ) return false; - - return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, (uint16_t) length); + cdch_interface_t* p_cdc = get_itf(idx); + return p_cdc != NULL; } -bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb) +bool tuh_cdc_get_dtr(uint8_t idx) { - cdch_data_t const * p_cdc = get_itf(dev_addr); + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false; +} + +bool tuh_cdc_get_rts(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false; +} + +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + *line_coding = p_cdc->line_coding; + + return true; +} + +//--------------------------------------------------------------------+ +// Write +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize); +} + +uint32_t tuh_cdc_write_flush(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_xfer(&p_cdc->stream.tx); +} + +bool tuh_cdc_write_clear(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_clear(&p_cdc->stream.tx); +} + +uint32_t tuh_cdc_write_available(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_available(&p_cdc->stream.tx); +} + +//--------------------------------------------------------------------+ +// Read +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize); +} + +uint32_t tuh_cdc_read_available(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read_available(&p_cdc->stream.rx); +} + +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_peek(&p_cdc->stream.rx, ch); +} + +bool tuh_cdc_read_clear (uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx); + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + return ret; +} + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + +// internal control complete to update state such as line state, encoding +static void cdch_internal_control_complete(tuh_xfer_t* xfer) +{ + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + if (xfer->result == XFER_RESULT_SUCCESS) + { + switch(xfer->setup->bRequest) + { + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + break; + + case CDC_REQUEST_SET_LINE_CODING: + { + uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); + memcpy(&p_cdc->line_coding, xfer->buffer, len); + } + break; + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + xfer->complete_cb(xfer); +} + +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->acm_capability.support_line_request); + + TU_LOG_CDCH("CDC Set Control Line State\r\n"); tusb_control_request_t const request = { @@ -133,36 +297,154 @@ bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xf .direction = TUSB_DIR_OUT }, .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = tu_htole16((uint16_t) ((dtr ? 1u : 0u) | (rts ? 2u : 0u))), - .wIndex = tu_htole16(p_cdc->itf_num), + .wValue = tu_htole16(line_state), + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), .wLength = 0 }; + p_cdc->user_control_cb = complete_cb; tuh_xfer_t xfer = { - .daddr = dev_addr, + .daddr = p_cdc->daddr, .ep_addr = 0, .setup = &request, .buffer = NULL, - .complete_cb = complete_cb, - .user_data = 0 + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->acm_capability.support_line_request); + + TU_LOG_CDCH("CDC Set Line Conding\r\n"); + + tusb_control_request_t const request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_LINE_CODING, + .wValue = 0, + .wIndex = tu_htole16(p_cdc->bInterfaceNumber), + .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + }; + + // use usbh enum buf to hold line coding since user line_coding variable may not live long enough + // for the transfer to complete + uint8_t* enum_buf = usbh_get_enum_buf(); + memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + + p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = + { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data }; return tuh_control_xfer(&xfer); } //--------------------------------------------------------------------+ -// USBH-CLASS DRIVER API +// CLASS-USBH API //--------------------------------------------------------------------+ + void cdch_init(void) { tu_memclr(cdch_data, sizeof(cdch_data)); + + for(size_t i=0; istream.tx, true, true, false, + p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE, + p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE); + + tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, + p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE, + p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE); + } } -bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +void cdch_close(uint8_t daddr) +{ + for(uint8_t idx=0; idxdaddr == daddr) + { + // Invoke application callback + if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx); + + //tu_memclr(p_cdc, sizeof(cdch_interface_t)); + p_cdc->daddr = 0; + p_cdc->bInterfaceNumber = 0; + tu_edpt_stream_close(&p_cdc->stream.tx); + tu_edpt_stream_close(&p_cdc->stream.rx); + } + } +} + +bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + // TODO handle stall response, retry failed transfer ... + TU_ASSERT(event == XFER_RESULT_SUCCESS); + + uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc); + + if ( ep_addr == p_cdc->stream.tx.ep_addr ) + { + // invoke tx complete callback to possibly refill tx fifo + if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx); + + if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) + { + // If there is no data left, a ZLP should be sent if: + // - xferred_bytes is multiple of EP Packet size and not zero + tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes); + } + } + else if ( ep_addr == p_cdc->stream.rx.ep_addr ) + { + tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); + + // invoke receive callback + if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); + + // prepare for next transfer if needed + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + }else if ( ep_addr == p_cdc->ep_notif ) + { + // TODO handle notification endpoint + }else + { + TU_ASSERT(false); + } + + return true; +} + +//--------------------------------------------------------------------+ +// Enumeration +//--------------------------------------------------------------------+ + +bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { (void) rhport; - (void) max_len; // Only support ACM subclass // Protocol 0xFF can be RNDIS device for windows XP @@ -170,17 +452,22 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass && 0xFF != itf_desc->bInterfaceProtocol); - cdch_data_t * p_cdc = get_itf(dev_addr); + uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len; - p_cdc->itf_num = itf_desc->bInterfaceNumber; - p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; + cdch_interface_t * p_cdc = find_new_itf(); + TU_VERIFY(p_cdc); - //------------- Communication Interface -------------// - uint16_t drv_len = tu_desc_len(itf_desc); + p_cdc->daddr = daddr; + p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; + p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; + p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; + p_cdc->line_state = 0; + + //------------- Control Interface -------------// uint8_t const * p_desc = tu_desc_next(itf_desc); // Communication Functional Descriptors - while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) ) { if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) { @@ -188,19 +475,18 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; } - drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); } - if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + // Open notification endpoint of control interface if any + if (itf_desc->bNumEndpoints == 1) { - // notification endpoint + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) ); + TU_ASSERT( tuh_edpt_open(daddr, desc_ep) ); p_cdc->ep_notif = desc_ep->bEndpointAddress; - drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); } @@ -209,52 +495,96 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) { // next to endpoint descriptor - drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); // data endpoints expected to be in pairs for(uint32_t i=0; i<2; i++) { tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && + TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); - TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep)); + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) { - p_cdc->ep_in = desc_ep->bEndpointAddress; + tu_edpt_stream_open(&p_cdc->stream.rx, daddr, desc_ep); }else { - p_cdc->ep_out = desc_ep->bEndpointAddress; + tu_edpt_stream_open(&p_cdc->stream.tx, daddr, desc_ep); } - drv_len += tu_desc_len(p_desc); - p_desc = tu_desc_next( p_desc ); + p_desc = tu_desc_next(p_desc); } } return true; } -bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num) +enum { - (void) dev_addr; (void) itf_num; - return true; + CONFIG_SET_CONTROL_LINE_STATE, + CONFIG_SET_LINE_CODING, + CONFIG_COMPLETE +}; + +static void process_cdc_config(tuh_xfer_t* xfer) +{ + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + TU_ASSERT(idx != TUSB_INDEX_INVALID, ); + + switch(state) + { + case CONFIG_SET_CONTROL_LINE_STATE: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT( tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cdc_config, CONFIG_SET_LINE_CODING), ); + break; + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_SET_LINE_CODING: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + { + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT( tuh_cdc_set_line_coding(idx, &line_coding, process_cdc_config, CONFIG_COMPLETE), ); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_COMPLETE: + if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); + + // Prepare for incoming data + cdch_interface_t* p_cdc = get_itf(idx); + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + + // notify usbh that driver enumeration is complete + // itf_num+1 to account for data interface as well + usbh_driver_set_config_complete(xfer->daddr, itf_num+1); + break; + + default: break; + } } -bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { - (void) ep_addr; - tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes ); + // fake transfer to kick-off process + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); + + tuh_xfer_t xfer; + xfer.daddr = daddr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + + process_cdc_config(&xfer); + return true; } -void cdch_close(uint8_t dev_addr) -{ - TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); - - cdch_data_t * p_cdc = get_itf(dev_addr); - tu_memclr(p_cdc, sizeof(cdch_data_t)); -} - #endif diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 33dbd2efb..c759527e6 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -34,89 +34,156 @@ #endif //--------------------------------------------------------------------+ -// CDC APPLICATION PUBLIC API +// Class Driver Configuration //--------------------------------------------------------------------+ -/** \ingroup ClassDriver_CDC Communication Device Class (CDC) - * \addtogroup CDC_Serial Serial - * @{ - * \defgroup CDC_Serial_Host Host - * @{ */ -bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb); +// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1) +#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM +#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0 +#endif -static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) +// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t +//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM +//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } +//#endif + +// RX FIFO size +#ifndef CFG_TUH_CDC_RX_BUFSIZE +#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// RX Endpoint size +#ifndef CFG_TUH_CDC_RX_EPSIZE +#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX FIFO size +#ifndef CFG_TUH_CDC_TX_BUFSIZE +#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX Endpoint size +#ifndef CFG_TUH_CDC_TX_EPSIZE +#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +typedef struct { - return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb); + uint8_t daddr; + uint8_t bInterfaceNumber; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; +} tuh_cdc_itf_info_t; + +// Get Interface index from device address + interface number +// return TUSB_INDEX_INVALID (0xFF) if not found +uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num); + +// Get Interface information +// return true if index is correct and interface is currently mounted +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_cdc_itf_info_t* info); + +// Check if a interface is mounted +bool tuh_cdc_mounted(uint8_t idx); + +// Get current DTR status +bool tuh_cdc_get_dtr(uint8_t idx); + +// Get current RTS status +bool tuh_cdc_get_rts(uint8_t idx); + +// Check if interface is connected (DTR active) +TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) +{ + return tuh_cdc_get_dtr(idx); } -static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb) +// Get local (saved/cached) version of line coding. +// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() +// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined. +// NOTE: This function does not make any USB transfer request to device. +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding); + +//--------------------------------------------------------------------+ +// Write API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for writing +uint32_t tuh_cdc_write_available(uint8_t idx); + +// Write to cdc interface +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize); + +// Force sending data if possible, return number of forced bytes +uint32_t tuh_cdc_write_flush(uint8_t idx); + +// Clear the transmit FIFO +bool tuh_cdc_write_clear(uint8_t idx); + +//--------------------------------------------------------------------+ +// Read API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for reading +uint32_t tuh_cdc_read_available(uint8_t idx); + +// Read from cdc interface +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize); + +// Get a byte from RX FIFO without removing it +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch); + +// Clear the received FIFO +bool tuh_cdc_read_clear (uint8_t idx); + +//--------------------------------------------------------------------+ +// Control Endpoint (Request) API +// Each Function will make a USB transfer request to/from device +//--------------------------------------------------------------------+ + +// Request to Set Control Line State: DTR (bit 0), RTS (bit 1) +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to Set Line Coding +bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to Get Line Coding +// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and +// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined +// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding); + +// Connect by set both DTR, RTS +static inline bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb); + return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data); } -/** \brief Check if device support CDC Serial interface or not - * \param[in] dev_addr device address - * \retval true if device supports - * \retval false if device does not support or is not mounted - */ -bool tuh_cdc_serial_is_mounted(uint8_t dev_addr); - -/** \brief Check if the interface is currently busy or not - * \param[in] dev_addr device address - * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe. - * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device - * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device - * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer - * can be scheduled. User needs to make sure the corresponding interface is mounted - * (by \ref tuh_cdc_serial_is_mounted) before calling this function. - */ -bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid); - -/** \brief Perform USB OUT transfer to device - * \param[in] dev_addr device address - * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] length Number of bytes to be transferred via USB bus - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the - * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. - */ -bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify); - -/** \brief Perform USB IN transfer to get data from device - * \param[in] dev_addr device address - * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] length Number of bytes to be transferred via USB bus - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the - * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. - */ -bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify); +// Disconnect by clear both DTR, RTS +static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data); +} //--------------------------------------------------------------------+ // CDC APPLICATION CALLBACKS //--------------------------------------------------------------------+ -/** \brief Callback function that is invoked when an transferring event occurred - * \param[in] dev_addr Address of device - * \param[in] event an value from \ref xfer_result_t - * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe - * \param[in] xferred_bytes Number of bytes transferred via USB bus - * \note event can be one of following - * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. - * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. - * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. - * \note - */ -void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes); +// Invoked when a device with CDC interface is mounted +// idx is index of cdc interface in the internal pool. +TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx); -/// @} // group CDC_Serial_Host -/// @} +// Invoked when a device with CDC interface is unmounted +TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx); + +// Invoked when received new data +TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx); + +// Invoked when a TX is complete and therefore space becomes available in TX buffer +TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c index 8f28f51ac..44de85b45 100644 --- a/src/class/cdc/cdc_rndis_host.c +++ b/src/class/cdc/cdc_rndis_host.c @@ -117,7 +117,7 @@ void rndish_init(void) //------------- Task creation -------------// - //------------- semaphore creation for notificaiton pipe -------------// + //------------- semaphore creation for notification pipe -------------// for(uint8_t i=0; iep_in, hid_itf->epin_buf, hid_itf->epin_size) ) { - usbh_edpt_claim(dev_addr, hid_itf->ep_in); + usbh_edpt_release(dev_addr, hid_itf->ep_in); return false; } diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index fe09b03b2..ffc601d77 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -89,7 +89,7 @@ uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance); bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol); // Set Report using control endpoint -// report_type is either Intput, Output or Feature, (value from hid_report_type_t) +// report_type is either Input, Output or Feature, (value from hid_report_type_t) bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len); //--------------------------------------------------------------------+ diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 9e88ebc30..6724e486c 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -33,6 +33,11 @@ #include "msc_host.h" +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define MSCH_DEBUG 2 + +#define TU_LOG_MSCH(...) TU_LOG(MSCH_DEBUG, __VA_ARGS__) + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ @@ -200,7 +205,7 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_ return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg); } -bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) +bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) { msc_cbw_t cbw; cbw_init(&cbw, lun); @@ -217,7 +222,7 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_ms memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len); - return tuh_msc_scsi_command(dev_addr, &cbw, resposne, complete_cb, arg); + return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg); } bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) @@ -417,7 +422,7 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) p_msc->configured = true; //------------- Get Max Lun -------------// - TU_LOG2("MSC Get Max Lun\r\n"); + TU_LOG_MSCH("MSC Get Max Lun\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = @@ -456,7 +461,7 @@ static void config_get_maxlun_complete (tuh_xfer_t* xfer) p_msc->max_lun++; // MAX LUN is minus 1 by specs // TODO multiple LUN support - TU_LOG2("SCSI Test Unit Ready\r\n"); + TU_LOG_MSCH("SCSI Test Unit Ready\r\n"); uint8_t const lun = 0; tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0); } @@ -469,14 +474,14 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d if (csw->status == 0) { // Unit is ready, read its capacity - TU_LOG2("SCSI Read Capacity\r\n"); + TU_LOG_MSCH("SCSI Read Capacity\r\n"); tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete, 0); }else { // Note: During enumeration, some device fails Test Unit Ready and require a few retries // with Request Sense to start working !! // TODO limit number of retries - TU_LOG2("SCSI Request Sense\r\n"); + TU_LOG_MSCH("SCSI Request Sense\r\n"); TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete, 0)); } diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index 416e4318b..5134b63c2 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -85,7 +85,7 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_ // Perform SCSI Request Sense 10 command // Complete callback is invoked when SCSI op is complete. -bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb, uintptr_t arg); +bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg); // Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer // Complete callback is invoked when SCSI op is complete. diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index 5f316762f..c7428bcda 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -51,7 +51,7 @@ typedef struct bool ecm_mode; - // Endpoint descriptor use to open/close when receving SetInterface + // Endpoint descriptor use to open/close when receiving SetInterface // TODO since configuration descriptor may not be long-lived memory, we should // keep a copy of endpoint attribute instead uint8_t const * ecm_desc_epdata; diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 00892b49c..1cbc0ce01 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -392,7 +392,7 @@ bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t if (NCM_GET_NTB_PARAMETERS == request->bRequest) { - tud_control_xfer(rhport, request, (void*)&ntb_parameters, sizeof(ntb_parameters)); + tud_control_xfer(rhport, request, (void*)(uintptr_t) &ntb_parameters, sizeof(ntb_parameters)); } break; diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h index e7016ae24..fd52d766e 100644 --- a/src/class/usbtmc/usbtmc.h +++ b/src/class/usbtmc/usbtmc.h @@ -158,7 +158,7 @@ enum { USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u, USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u, }; -// bult-in halt errors +// built-in halt errors enum { USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a /// Bulk-IN transfer is in progress diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c index af4a92732..0cf0743a7 100644 --- a/src/class/usbtmc/usbtmc_device.c +++ b/src/class/usbtmc/usbtmc_device.c @@ -157,12 +157,14 @@ static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t pack static uint8_t termChar; static uint8_t termCharRequested = false; -osal_mutex_def_t usbtmcLockBuffer; -static osal_mutex_t usbtmcLock; +#if OSAL_MUTEX_REQUIRED +static OSAL_MUTEX_DEF(usbtmcLockBuffer); +#endif +osal_mutex_t usbtmcLock; // Our own private lock, mostly for the state variable. -#define criticalEnter() do {osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0) -#define criticalLeave() do {osal_mutex_unlock(usbtmcLock); } while (0) +#define criticalEnter() do { (void) osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0) +#define criticalLeave() do { (void) osal_mutex_unlock(usbtmcLock); } while (0) bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState) { @@ -362,9 +364,9 @@ bool tud_usbtmc_start_bus_read() case STATE_RCV: break; default: - TU_VERIFY(false); + return false; } - TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64)); + TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, (uint16_t)usbtmc_state.ep_bulk_out_wMaxPacketSize)); return true; } @@ -464,53 +466,52 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint switch(usbtmc_state.state) { case STATE_IDLE: - TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t)); - msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf); - uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse); - TU_VERIFY(msg->header.bTag == invInvTag); - TU_VERIFY(msg->header.bTag != 0x00); + { + TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t)); + msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf); + uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse); + TU_VERIFY(msg->header.bTag == invInvTag); + TU_VERIFY(msg->header.bTag != 0x00); - switch(msg->header.MsgID) { - case USBTMC_MSGID_DEV_DEP_MSG_OUT: - if(!handle_devMsgOutStart(rhport, msg, xferred_bytes)) - { - usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out); - TU_VERIFY(false); - } - break; + switch(msg->header.MsgID) { + case USBTMC_MSGID_DEV_DEP_MSG_OUT: + if(!handle_devMsgOutStart(rhport, msg, xferred_bytes)) + { + usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out); + return false; + } + break; - case USBTMC_MSGID_DEV_DEP_MSG_IN: - TU_VERIFY(handle_devMsgIn(msg, xferred_bytes)); - break; + case USBTMC_MSGID_DEV_DEP_MSG_IN: + TU_VERIFY(handle_devMsgIn(msg, xferred_bytes)); + break; #if (CFG_TUD_USBTMC_ENABLE_488) - case USBTMC_MSGID_USB488_TRIGGER: - // Spec says we halt the EP if we didn't declare we support it. - TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger); - TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg)); + case USBTMC_MSGID_USB488_TRIGGER: + // Spec says we halt the EP if we didn't declare we support it. + TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger); + TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg)); - break; + break; #endif - case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT: - case USBTMC_MSGID_VENDOR_SPECIFIC_IN: - default: - usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out); - TU_VERIFY(false); - return false; + case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT: + case USBTMC_MSGID_VENDOR_SPECIFIC_IN: + default: + usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out); + return false; + } + return true; } - return true; - case STATE_RCV: if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes)) { usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out); - TU_VERIFY(false); + return false; } return true; case STATE_ABORTING_BULK_OUT: - TU_VERIFY(false); - return false; // Should be stalled by now, shouldn't have received a packet. + return false; case STATE_TX_REQUESTED: case STATE_TX_INITIATED: @@ -518,7 +519,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint case STATE_ABORTING_BULK_IN_SHORTED: case STATE_ABORTING_BULK_IN_ABORTED: default: - TU_VERIFY(false); + return false; } } else if(ep_addr == usbtmc_state.ep_bulk_in) @@ -567,7 +568,6 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint default: TU_ASSERT(false); - return false; } } else if (ep_addr == usbtmc_state.ep_int_in) { @@ -599,7 +599,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request // At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT, // the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if - // the EP is not halted. The only USBD API interface to do this is to stall and then unstall the EP. + // the EP is not halted. The only USBD API interface to do this is to stall and then un-stall the EP. if(ep_addr == usbtmc_state.ep_bulk_out) { criticalEnter(); @@ -871,16 +871,13 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request case USB488_bREQUEST_LOCAL_LOCKOUT: { TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface - TU_VERIFY(false); return false; } #endif default: - TU_VERIFY(false); return false; } - TU_VERIFY(false); } #endif /* CFG_TUD_TSMC */ diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h index 144b3315d..c1298ddb8 100644 --- a/src/class/usbtmc/usbtmc_device.h +++ b/src/class/usbtmc/usbtmc_device.h @@ -36,7 +36,7 @@ #endif /*********************************************** - * Functions to be implemeted by the class implementation + * Functions to be implemented by the class implementation */ // In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after: diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index a6d2724c1..e17d0a90f 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -917,7 +917,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, switch (request->bRequest) { case VIDEO_REQUEST_SET_CUR: if (stage == CONTROL_STAGE_SETUP) { - TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN); + TU_VERIFY(sizeof(video_probe_and_commit_control_t) >= request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN); } else if (stage == CONTROL_STAGE_DATA) { @@ -973,7 +973,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, switch (request->bRequest) { case VIDEO_REQUEST_SET_CUR: if (stage == CONTROL_STAGE_SETUP) { - TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN); + TU_VERIFY(sizeof(video_probe_and_commit_control_t) >= request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN); } else if (stage == CONTROL_STAGE_DATA) { TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE); diff --git a/src/common/tusb_debug.h b/src/common/tusb_debug.h index ac5bee6ec..65fd1920d 100644 --- a/src/common/tusb_debug.h +++ b/src/common/tusb_debug.h @@ -66,7 +66,7 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize) #define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__) #define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__) #define TU_LOG_ARR(n, ...) TU_XSTRCAT3(TU_LOG, n, _ARR)(__VA_ARGS__) -#define TU_LOG_VAR(n, ...) TU_XSTRCAT3(TU_LOG, n, _VAR)(__VA_ARGS__) +#define TU_LOG_PTR(n, ...) TU_XSTRCAT3(TU_LOG, n, _PTR)(__VA_ARGS__) #define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__) #define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__) #define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__) @@ -76,7 +76,7 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize) #define TU_LOG1 tu_printf #define TU_LOG1_MEM tu_print_mem #define TU_LOG1_ARR(_x, _n) tu_print_arr((uint8_t const*)(_x), _n) -#define TU_LOG1_VAR(_x) tu_print_arr((uint8_t const*)(_x), sizeof(*(_x))) +#define TU_LOG1_PTR(_x) tu_print_arr((uint8_t const*)(_x), sizeof(*(_x))) #define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) ) #define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (unsigned long) (_x) ) @@ -85,7 +85,7 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize) #define TU_LOG2 TU_LOG1 #define TU_LOG2_MEM TU_LOG1_MEM #define TU_LOG2_ARR TU_LOG1_ARR - #define TU_LOG2_VAR TU_LOG1_VAR + #define TU_LOG2_PTR TU_LOG1_PTR #define TU_LOG2_INT TU_LOG1_INT #define TU_LOG2_HEX TU_LOG1_HEX #endif @@ -95,7 +95,7 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize) #define TU_LOG3 TU_LOG1 #define TU_LOG3_MEM TU_LOG1_MEM #define TU_LOG3_ARR TU_LOG1_ARR - #define TU_LOG3_VAR TU_LOG1_VAR + #define TU_LOG3_PTR TU_LOG1_PTR #define TU_LOG3_INT TU_LOG1_INT #define TU_LOG3_HEX TU_LOG1_HEX #endif @@ -132,7 +132,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 #ifndef TU_LOG #define TU_LOG(n, ...) #define TU_LOG_MEM(n, ...) - #define TU_LOG_VAR(n, ...) + #define TU_LOG_PTR(n, ...) #define TU_LOG_INT(n, ...) #define TU_LOG_HEX(n, ...) #define TU_LOG_LOCATION() @@ -143,14 +143,14 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 #define TU_LOG0(...) #define TU_LOG0_MEM(...) -#define TU_LOG0_VAR(...) +#define TU_LOG0_PTR(...) #define TU_LOG0_INT(...) #define TU_LOG0_HEX(...) #ifndef TU_LOG1 #define TU_LOG1(...) #define TU_LOG1_MEM(...) - #define TU_LOG1_VAR(...) + #define TU_LOG1_PTR(...) #define TU_LOG1_INT(...) #define TU_LOG1_HEX(...) #endif @@ -158,7 +158,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 #ifndef TU_LOG2 #define TU_LOG2(...) #define TU_LOG2_MEM(...) - #define TU_LOG2_VAR(...) + #define TU_LOG2_PTR(...) #define TU_LOG2_INT(...) #define TU_LOG2_HEX(...) #endif @@ -166,7 +166,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3 #ifndef TU_LOG3 #define TU_LOG3(...) #define TU_LOG3_MEM(...) - #define TU_LOG3_VAR(...) + #define TU_LOG3_PTR(...) #define TU_LOG3_INT(...) #define TU_LOG3_HEX(...) #endif diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 895b9208b..a52c92267 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -28,21 +28,22 @@ #include "osal/osal.h" #include "tusb_fifo.h" -// Supress IAR warning +#define TU_FIFO_DBG 0 + +// Suppress IAR warning // Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement #if defined(__ICCARM__) #pragma diag_suppress = Pa082 #endif -// implement mutex lock and unlock -#if CFG_FIFO_MUTEX +#if OSAL_MUTEX_REQUIRED -static inline void _ff_lock(tu_fifo_mutex_t mutex) +TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) { if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); } -static inline void _ff_unlock(tu_fifo_mutex_t mutex) +TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) { if (mutex) osal_mutex_unlock(mutex); } @@ -66,23 +67,20 @@ typedef enum bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) { - if (depth > 0x8000) return false; // Maximum depth is 2^15 items + // Limit index space to 2*depth - this allows for a fast "modulo" calculation + // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable + // only if overflow happens once (important for unsupervised DMA applications) + if (depth > 0x8000) return false; _ff_lock(f->mutex_wr); _ff_lock(f->mutex_rd); - f->buffer = (uint8_t*) buffer; - f->depth = depth; - f->item_size = item_size; + f->buffer = (uint8_t*) buffer; + f->depth = depth; + f->item_size = (uint16_t) (item_size & 0x7FFF); f->overwritable = overwritable; - - // Limit index space to 2*depth - this allows for a fast "modulo" calculation - // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable - // only if overflow happens once (important for unsupervised DMA applications) - f->max_pointer_idx = (uint16_t) (2*depth - 1); - f->non_used_index_space = UINT16_MAX - f->max_pointer_idx; - - f->rd_idx = f->wr_idx = 0; + f->rd_idx = 0; + f->wr_idx = 0; _ff_unlock(f->mutex_wr); _ff_unlock(f->mutex_rd); @@ -90,25 +88,22 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si return true; } -// Static functions are intended to work on local variables -static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth) -{ - while ( idx >= depth) idx -= depth; - return idx; -} +//--------------------------------------------------------------------+ +// Pull & Push +//--------------------------------------------------------------------+ // 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_synopsis.c +// 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) { - volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; + volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf; // Reading full available 32 bit words from const app address uint16_t full_words = len >> 2; while(full_words--) { - tu_unaligned_write32(ff_buf, *rx_fifo); + tu_unaligned_write32(ff_buf, *reg_rx); ff_buf += 4; } @@ -116,7 +111,7 @@ static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t uint8_t const bytes_rem = len & 0x03; if ( bytes_rem ) { - uint32_t tmp32 = *rx_fifo; + uint32_t tmp32 = *reg_rx; memcpy(ff_buf, &tmp32, bytes_rem); } } @@ -125,49 +120,49 @@ static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t // 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 * tx_fifo = (volatile uint32_t *) app_buf; + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; - // Pushing full available 32 bit words to const app address + // Write full available 32 bit words to const address uint16_t full_words = len >> 2; while(full_words--) { - *tx_fifo = tu_unaligned_read32(ff_buf); + *reg_tx = tu_unaligned_read32(ff_buf); ff_buf += 4; } - // Write the remaining 1-3 bytes into const app address + // Write the remaining 1-3 bytes into const address uint8_t const bytes_rem = len & 0x03; if ( bytes_rem ) { uint32_t tmp32 = 0; memcpy(&tmp32, ff_buf, bytes_rem); - *tx_fifo = tmp32; + *reg_tx = tmp32; } } -// send one item to FIFO WITHOUT updating write pointer +// send one item to fifo WITHOUT updating write pointer static inline void _ff_push(tu_fifo_t* f, void const * 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, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode) +// send n items to fifo WITHOUT updating write pointer +static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) { - uint16_t const nLin = f->depth - rel; - uint16_t const nWrap = n - nLin; + uint16_t const lin_count = f->depth - wr_ptr; + uint16_t const wrap_count = n - lin_count; - uint16_t nLin_bytes = nLin * f->item_size; - uint16_t nWrap_bytes = nWrap * f->item_size; + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; // current buffer of fifo - uint8_t* ff_buf = f->buffer + (rel * f->item_size); + uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size); switch (copy_mode) { case TU_FIFO_COPY_INC: - if(n <= nLin) + if(n <= lin_count) { // Linear only memcpy(ff_buf, app_buf, n*f->item_size); @@ -177,16 +172,17 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t // Wrap around // Write data to linear part of buffer - memcpy(ff_buf, app_buf, nLin_bytes); + memcpy(ff_buf, app_buf, lin_bytes); // Write data wrapped around - memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes); + // TU_ASSERT(nWrap_bytes <= f->depth, ); + memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes); } break; case TU_FIFO_COPY_CST_FULL_WORDS: // Intended for hardware buffers from which it can be read word by word only - if(n <= nLin) + if(n <= lin_count) { // Linear only _ff_push_const_addr(ff_buf, app_buf, n*f->item_size); @@ -196,17 +192,18 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t // Wrap around case // Write full words to linear part of buffer - uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC; + uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC; _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes); ff_buf += nLin_4n_bytes; // There could be odd 1-3 bytes before the wrap-around boundary - volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; - uint8_t rem = nLin_bytes & 0x03; + uint8_t rem = lin_bytes & 0x03; if (rem > 0) { - uint8_t remrem = (uint8_t) tu_min16(nWrap_bytes, 4-rem); - nWrap_bytes -= remrem; + volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf; + + 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); @@ -224,34 +221,34 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t } // Write data wrapped part - if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes); + if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes); } break; } } -// get one item from FIFO WITHOUT updating read pointer +// 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 rel, tu_fifo_copy_mode_t copy_mode) +// 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) { - uint16_t const nLin = f->depth - rel; - uint16_t const nWrap = n - nLin; // only used if wrapped + uint16_t const lin_count = f->depth - rd_ptr; + uint16_t const wrap_count = n - lin_count; // only used if wrapped - uint16_t nLin_bytes = nLin * f->item_size; - uint16_t nWrap_bytes = nWrap * f->item_size; + uint16_t lin_bytes = lin_count * f->item_size; + uint16_t wrap_bytes = wrap_count * f->item_size; // current buffer of fifo - uint8_t* ff_buf = f->buffer + (rel * f->item_size); + uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size); switch (copy_mode) { case TU_FIFO_COPY_INC: - if ( n <= nLin ) + if ( n <= lin_count ) { // Linear only memcpy(app_buf, ff_buf, n*f->item_size); @@ -261,15 +258,15 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu // Wrap around // Read data from linear part of buffer - memcpy(app_buf, ff_buf, nLin_bytes); + memcpy(app_buf, ff_buf, lin_bytes); // Read data wrapped part - memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes); + memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes); } break; case TU_FIFO_COPY_CST_FULL_WORDS: - if ( n <= nLin ) + if ( n <= lin_count ) { // Linear only _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size); @@ -279,17 +276,18 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu // Wrap around case // Read full words from linear part of buffer - uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC; - _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes); - ff_buf += nLin_4n_bytes; + uint16_t lin_4n_bytes = lin_bytes & 0xFFFC; + _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes); + ff_buf += lin_4n_bytes; // There could be odd 1-3 bytes before the wrap-around boundary - volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf; - uint8_t rem = nLin_bytes & 0x03; + uint8_t rem = lin_bytes & 0x03; if (rem > 0) { - uint8_t remrem = (uint8_t) tu_min16(nWrap_bytes, 4-rem); - nWrap_bytes -= remrem; + volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf; + + uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem); + wrap_bytes -= remrem; uint32_t tmp32=0; uint8_t * dst_u8 = (uint8_t *)&tmp32; @@ -301,7 +299,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu ff_buf = f->buffer; while(remrem--) *dst_u8++ = *ff_buf++; - *tx_fifo = tmp32; + *reg_tx = tmp32; } else { @@ -309,7 +307,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu } // Read data wrapped part - if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes); + if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes); } break; @@ -317,178 +315,232 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu } } -// Advance an absolute pointer -static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, 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 - if ((p > (uint16_t)(p + offset)) || ((uint16_t)(p + offset) > f->max_pointer_idx)) - { - p = (uint16_t) ((p + offset) + f->non_used_index_space); - } - else - { - p += offset; - } - return p; -} +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ -// Backward an absolute pointer -static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) +// 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) { - // 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 - if ((p < (uint16_t)(p - offset)) || ((uint16_t)(p - offset) > f->max_pointer_idx)) - { - p = (uint16_t) ((p - offset) - f->non_used_index_space); - } - else - { - p -= offset; - } - return p; -} - -// get relative from absolute pointer -static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p) -{ - return _ff_mod(p, f->depth); -} - -// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow -static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs) -{ - uint16_t cnt = wAbs-rAbs; - // In case we have non-power of two depth we need a further modification - if (rAbs > wAbs) cnt -= f->non_used_index_space; - - return cnt; + if (wr_idx >= rd_idx) + { + return (uint16_t) (wr_idx - rd_idx); + } else + { + return (uint16_t) (2*depth - (rd_idx - wr_idx)); + } } -// Works on local copies of w and r -static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs) +// 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) { - return wAbs == rAbs; + uint16_t const count = _ff_count(depth, wr_idx, rd_idx); + return (depth > count) ? (depth - count) : 0; } -// Works on local copies of w and r -static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs) +//--------------------------------------------------------------------+ +// 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) { - return (_tu_fifo_count(f, wAbs, rAbs) == f->depth); + // 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; } -// Works on local copies of w and r -// 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 you pointer states are messed up. This can only happen if you -// use DMAs, write functions do not allow such an error. -static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs) +#if 0 // not used but +// Backward an absolute index +static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) { - return (_tu_fifo_count(f, wAbs, rAbs) > f->depth); + // 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. +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 ) idx -= depth; + return idx; } // Works on local copies of w -// For more details see _tu_fifo_overflow()! -static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs) +// 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) { - f->rd_idx = backward_pointer(f, wAbs, f->depth); + uint16_t rd_idx; + if ( wr_idx >= f->depth ) + { + rd_idx = wr_idx - f->depth; + }else + { + rd_idx = wr_idx + f->depth; + } + + 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 wAbs, uint16_t rAbs) +static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx) { - uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs); + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return false; // Check overflow and correct if required - if (cnt > f->depth) + if ( cnt > f->depth ) { - _tu_fifo_correct_read_pointer(f, wAbs); + rd_idx = _ff_correct_read_index(f, wr_idx); cnt = f->depth; } - // Skip beginning of buffer - if (cnt == 0) return false; - - uint16_t rRel = get_relative_pointer(f, rAbs); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Peek data - _ff_pull(f, p_buffer, rRel); + _ff_pull(f, p_buffer, rd_ptr); return true; } // 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 wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode) +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 = _tu_fifo_count(f, wAbs, rAbs); + uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx); + + // nothing to peek + if ( cnt == 0 ) return 0; // Check overflow and correct if required - if (cnt > f->depth) + if ( cnt > f->depth ) { - _tu_fifo_correct_read_pointer(f, wAbs); - rAbs = f->rd_idx; + rd_idx = _ff_correct_read_index(f, wr_idx); cnt = f->depth; } - // Skip beginning of buffer - if (cnt == 0) return 0; - // 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 ( cnt < n ) n = cnt; - uint16_t rRel = get_relative_pointer(f, rAbs); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Peek data - _ff_pull_n(f, p_buffer, n, rRel, copy_mode); + _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode); return n; } -// Works on local copies of w and r -static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs) -{ - return f->depth - _tu_fifo_count(f, wAbs, rAbs); -} - static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode) { if ( n == 0 ) return 0; _ff_lock(f->mutex_wr); - uint16_t w = f->wr_idx, r = f->rd_idx; + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; + uint8_t const* buf8 = (uint8_t const*) data; - if (!f->overwritable) - { - // Not overwritable limit up to full - n = tu_min16(n, _tu_fifo_remaining(f, w, r)); - } - else if (n >= f->depth) - { - // Only copy last part - buf8 = buf8 + (n - f->depth) * f->item_size; - n = f->depth; + 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); - // We start writing at the read pointer's position since we fill the complete - // buffer and we do not want to modify the read pointer within a write function! - // This would end up in a race condition with read functions! - w = r; + if ( !f->overwritable ) + { + // limit up to full + uint16_t const remain = _ff_remaining(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! + if ( n >= f->depth ) + { + // Only copy last part + if ( copy_mode == TU_FIFO_COPY_INC ) + { + buf8 += (n - f->depth) * f->item_size; + }else + { + // TODO should read from hw fifo to discard data, however reading an odd number could + // accidentally discard data. + } + + n = f->depth; + + // We start writing at the read pointer's position since we fill the whole buffer + wr_idx = rd_idx; + } + else + { + uint16_t const overflowable_count = _ff_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) + // re-position write index to have a full fifo after pushed + wr_idx = advance_index(f->depth, rd_idx, f->depth - n); + + // TODO we should also shift out n bytes from read index since we avoid changing rd index !! + // However memmove() is expensive due to actual copying + wrapping consideration. + // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory + // currently deliberately not implemented --> result in incorrect data read back + }else + { + // normal + single overflowed: + // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read() + // Therefore we just increase write index + // we will correct (re-position) read index later on in fifo_read() function + } + } } - uint16_t wRel = get_relative_pointer(f, w); + if (n) + { + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); - // Write data - _ff_push_n(f, buf8, n, wRel, copy_mode); + TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); - // Advance pointer - f->wr_idx = advance_pointer(f, w, n); + // Write data + _ff_push_n(f, buf8, n, wr_ptr, copy_mode); + + // Advance index + f->wr_idx = advance_index(f->depth, wr_idx, n); + + TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\n", f->wr_idx); + } _ff_unlock(f->mutex_wr); @@ -504,12 +556,16 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // Advance read pointer - f->rd_idx = advance_pointer(f, f->rd_idx, n); + f->rd_idx = advance_index(f->depth, f->rd_idx, n); _ff_unlock(f->mutex_rd); return n; } +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + /******************************************************************************/ /*! @brief Get number of items in FIFO. @@ -527,7 +583,7 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo /******************************************************************************/ uint16_t tu_fifo_count(tu_fifo_t* f) { - return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth); + return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth); } /******************************************************************************/ @@ -545,7 +601,7 @@ uint16_t tu_fifo_count(tu_fifo_t* f) /******************************************************************************/ bool tu_fifo_empty(tu_fifo_t* f) { - return _tu_fifo_empty(f->wr_idx, f->rd_idx); + return f->wr_idx == f->rd_idx; } /******************************************************************************/ @@ -563,7 +619,7 @@ bool tu_fifo_empty(tu_fifo_t* f) /******************************************************************************/ bool tu_fifo_full(tu_fifo_t* f) { - return _tu_fifo_full(f, f->wr_idx, f->rd_idx); + return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth; } /******************************************************************************/ @@ -581,7 +637,7 @@ bool tu_fifo_full(tu_fifo_t* f) /******************************************************************************/ uint16_t tu_fifo_remaining(tu_fifo_t* f) { - return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx); + return _ff_remaining(f->depth, f->wr_idx, f->rd_idx); } /******************************************************************************/ @@ -607,14 +663,14 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f) /******************************************************************************/ bool tu_fifo_overflowed(tu_fifo_t* f) { - return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx); + 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); - _tu_fifo_correct_read_pointer(f, f->wr_idx); + _ff_correct_read_index(f, f->wr_idx); _ff_unlock(f->mutex_rd); } @@ -643,7 +699,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer) bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); // Advance pointer - f->rd_idx = advance_pointer(f, f->rd_idx, ret); + f->rd_idx = advance_index(f->depth, f->rd_idx, ret); _ff_unlock(f->mutex_rd); return ret; @@ -682,8 +738,6 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint1 @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] offset - Position to read from in the FIFO buffer with respect to read pointer @param[in] p_buffer Pointer to the place holder for data read from the buffer @@ -742,20 +796,20 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data) _ff_lock(f->mutex_wr); bool ret; - uint16_t const w = f->wr_idx; + uint16_t const wr_idx = f->wr_idx; - if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) + if ( tu_fifo_full(f) && !f->overwritable ) { ret = false; }else { - uint16_t wRel = get_relative_pointer(f, w); + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); // Write data - _ff_push(f, data, wRel); + _ff_push(f, data, wr_ptr); // Advance pointer - f->wr_idx = advance_pointer(f, w, 1); + f->wr_idx = advance_index(f->depth, wr_idx, 1); ret = true; } @@ -817,9 +871,8 @@ bool tu_fifo_clear(tu_fifo_t *f) _ff_lock(f->mutex_wr); _ff_lock(f->mutex_rd); - f->rd_idx = f->wr_idx = 0; - f->max_pointer_idx = (uint16_t) (2*f->depth-1); - f->non_used_index_space = UINT16_MAX - f->max_pointer_idx; + f->rd_idx = 0; + f->wr_idx = 0; _ff_unlock(f->mutex_wr); _ff_unlock(f->mutex_rd); @@ -857,7 +910,7 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) As long as the DMA is the only process writing into the FIFO this is safe to use. - USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE! + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! @param[in] f Pointer to the FIFO buffer to manipulate @@ -867,7 +920,7 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) /******************************************************************************/ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) { - f->wr_idx = advance_pointer(f, f->wr_idx, n); + f->wr_idx = advance_index(f->depth, f->wr_idx, n); } /******************************************************************************/ @@ -878,7 +931,7 @@ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) FIFO. As long as the DMA is the only process reading from the FIFO this is safe to use. - USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE! + USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! @param[in] f Pointer to the FIFO buffer to manipulate @@ -888,7 +941,7 @@ 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) { - f->rd_idx = advance_pointer(f, f->rd_idx, n); + f->rd_idx = advance_index(f->depth, f->rd_idx, n); } /******************************************************************************/ @@ -909,17 +962,18 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { // Operate on temporary values in case they change in between - uint16_t w = f->wr_idx, r = f->rd_idx; + uint16_t wr_idx = f->wr_idx; + uint16_t rd_idx = f->rd_idx; - uint16_t cnt = _tu_fifo_count(f, w, r); + uint16_t cnt = _ff_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); - _tu_fifo_correct_read_pointer(f, w); + rd_idx = _ff_correct_read_index(f, wr_idx); _ff_unlock(f->mutex_rd); - r = f->rd_idx; + cnt = f->depth; } @@ -934,22 +988,25 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) } // Get relative pointers - w = get_relative_pointer(f, w); - r = get_relative_pointer(f, r); + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Copy pointer to buffer to start reading from - info->ptr_lin = &f->buffer[r]; + info->ptr_lin = &f->buffer[rd_ptr]; // Check if there is a wrap around necessary - if (w > r) { + if (wr_ptr > rd_ptr) + { // Non wrapping case info->len_lin = cnt; + info->len_wrap = 0; info->ptr_wrap = NULL; } else { - info->len_lin = f->depth - r; // Also the case if FIFO was full + info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full + info->len_wrap = cnt - info->len_lin; info->ptr_wrap = f->buffer; } @@ -972,36 +1029,37 @@ 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 w = f->wr_idx, r = f->rd_idx; - uint16_t free = _tu_fifo_remaining(f, w, r); + 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); - if (free == 0) + if (remain == 0) { - info->len_lin = 0; + info->len_lin = 0; info->len_wrap = 0; - info->ptr_lin = NULL; + info->ptr_lin = NULL; info->ptr_wrap = NULL; return; } // Get relative pointers - w = get_relative_pointer(f, w); - r = get_relative_pointer(f, r); + uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); + uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); // Copy pointer to buffer to start writing to - info->ptr_lin = &f->buffer[w]; + info->ptr_lin = &f->buffer[wr_ptr]; - if (w < r) + if (wr_ptr < rd_ptr) { // Non wrapping case - info->len_lin = r-w; + info->len_lin = rd_ptr-wr_ptr; info->len_wrap = 0; info->ptr_wrap = NULL; } else { - info->len_lin = f->depth - w; - info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth - info->ptr_wrap = f->buffer; // Always start of buffer + 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 } } diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index 18db289a1..2f60ec2f4 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -32,7 +32,7 @@ extern "C" { #endif -// Due to the use of unmasked pointers, this FIFO does not suffer from loosing +// Due to the use of unmasked pointers, this FIFO does not suffer from losing // one item slice. Furthermore, write and read operations are completely // decoupled as write and read functions do not modify a common state. Henceforth, // writing or reading from the FIFO within an ISR is safe as long as no other @@ -42,32 +42,84 @@ extern "C" { // within a certain number (see tu_fifo_overflow()). #include "common/tusb_common.h" +#include "osal/osal.h" // mutex is only needed for RTOS // for OS None, we don't get preempted -#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE) +#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED -#if CFG_FIFO_MUTEX -#include "osal/osal.h" -#define tu_fifo_mutex_t osal_mutex_t -#endif +/* 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 + * + * - empty: W = R + * | + * ------------------------- + * | 0 | RW| 2 | 3 | 4 | 5 | + * + * - full 1: W > R + * | + * ------------------------- + * | 0 | R | 2 | 3 | W | 5 | + * + * - full 2: W < R + * | + * ------------------------- + * | 0 | 1 | W | 3 | 4 | R | + * + * - Number of items in the fifo can be determined in either cases: + * - case W >= R: Count = W - R + * - case W < R: Count = 2*depth - (R - W) + * + * In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth. + * However, in over-writable mode, write index can be repeatedly increased and count can be + * temporarily larger than depth (overflowed condition) e.g + * + * - Overflowed 1: write(3), write(1) + * In this case we will adjust Read index when read()/peek() is called so that count = depth. + * | + * ------------------------- + * | R | 1 | 2 | 3 | W | 5 | + * + * - Double Overflowed i.e index is out of allowed range [0,2*depth) + * This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g: + * write(3), write(1), write(2) + * This must be prevented since it will cause unrecoverable state, in above example + * if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify + * read index in write() function, which cause race condition. We will re-position write index so that + * after data is written it is a full fifo i.e W = depth - R + * + * re-position W = 1 before write(2) + * Note: we should also move data from mem[3] to read index as well, but deliberately skipped here + * since it is an expensive operation !!! + * | + * ------------------------- + * | R | W | 2 | 3 | 4 | 5 | + * + * perform write(2), result is still a full fifo. + * + * | + * ------------------------- + * | R | 1 | 2 | W | 4 | 5 | + */ typedef struct { - uint8_t* buffer ; ///< buffer pointer - uint16_t depth ; ///< max items - uint16_t item_size ; ///< size of each item - bool overwritable ; + uint8_t* buffer ; // buffer pointer + uint16_t depth ; // max items - uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length - uint16_t max_pointer_idx ; ///< maximum absolute pointer index + struct TU_ATTR_PACKED { + uint16_t item_size : 15; // size of each item + bool overwritable : 1 ; // ovwerwritable when full + }; - volatile uint16_t wr_idx ; ///< write pointer - volatile uint16_t rd_idx ; ///< read pointer + volatile uint16_t wr_idx ; // write index + volatile uint16_t rd_idx ; // read index -#if CFG_FIFO_MUTEX - tu_fifo_mutex_t mutex_wr; - tu_fifo_mutex_t mutex_rd; +#if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_wr; + osal_mutex_t mutex_rd; #endif } tu_fifo_t; @@ -86,8 +138,6 @@ typedef struct .depth = _depth, \ .item_size = sizeof(_type), \ .overwritable = _overwritable, \ - .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \ - .max_pointer_idx = 2*(_depth)-1, \ } #define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ @@ -99,13 +149,18 @@ 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); -#if CFG_FIFO_MUTEX +#if OSAL_MUTEX_REQUIRED TU_ATTR_ALWAYS_INLINE static inline -void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl) +void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) { - f->mutex_wr = write_mutex_hdl; - f->mutex_rd = read_mutex_hdl; + f->mutex_wr = wr_mutex; + f->mutex_rd = rd_mutex; } + +#else + +#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex) + #endif bool tu_fifo_write (tu_fifo_t* f, void const * p_data); @@ -133,7 +188,7 @@ uint16_t tu_fifo_depth(tu_fifo_t* f) } // Pointer modifications intended to be used in combinations with DMAs. -// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! +// 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); diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 86c68baf8..0b10c5118 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -275,12 +275,20 @@ #elif TU_CHECK_MCU(OPT_MCU_F1C100S) #define TUP_DCD_ENDPOINT_MAX 4 +//------------- WCH -------------// +#elif TU_CHECK_MCU(OPT_MCU_CH32V307) + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 #endif //--------------------------------------------------------------------+ // Default Values //--------------------------------------------------------------------+ +#ifndef TUP_MCU_MULTIPLE_CORE +#define TUP_MCU_MULTIPLE_CORE 0 +#endif + #ifndef TUP_DCD_ENDPOINT_MAX #warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8" #define TUP_DCD_ENDPOINT_MAX 8 diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index b34506f65..d5541856c 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -28,6 +28,8 @@ #ifndef _TUSB_PRIVATE_H_ #define _TUSB_PRIVATE_H_ +// Internal Helper used by Host and Device Stack + #ifdef __cplusplus extern "C" { #endif @@ -39,8 +41,31 @@ typedef struct TU_ATTR_PACKED volatile uint8_t claimed : 1; }tu_edpt_state_t; +typedef struct { + bool is_host; // host or device most + union { + uint8_t daddr; + uint8_t rhport; + uint8_t hwid; + }; + uint8_t ep_addr; + uint8_t ep_speed; + + uint16_t ep_packetsize; + uint16_t ep_bufsize; + + // TODO xfer_fifo can skip this buffer + uint8_t* ep_buf; + + tu_fifo_t ff; + + // mutex: read if ep rx, write if e tx + OSAL_MUTEX_DEF(ff_mutex); + +}tu_edpt_stream_t; + //--------------------------------------------------------------------+ -// Internal Helper used by Host and Device Stack +// Endpoint //--------------------------------------------------------------------+ // Check if endpoint descriptor is valid per USB specs @@ -58,6 +83,89 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex); // Release an endpoint with provided mutex bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); +//--------------------------------------------------------------------+ +// Endpoint Stream +//--------------------------------------------------------------------+ + +// Init an stream, should only be called once +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize); + +// Open an stream for an endpoint +// hwid is either device address (host mode) or rhport (device mode) +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep) +{ + tu_fifo_clear(&s->ff); + s->hwid = hwid; + s->ep_addr = desc_ep->bEndpointAddress; + s->ep_packetsize = tu_edpt_packet_size(desc_ep); +} + +TU_ATTR_ALWAYS_INLINE static inline +void tu_edpt_stream_close(tu_edpt_stream_t* s) +{ + s->hwid = 0; + s->ep_addr = 0; +} + +// Clear fifo +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_clear(tu_edpt_stream_t* s) +{ + return tu_fifo_clear(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +// Write to stream +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s); + +// Start an zero-length packet if needed +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes); + +// Get the number of bytes available for writing +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s) +{ + return (uint32_t) tu_fifo_remaining(&s->ff); +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +// Read from stream +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize); + +// Start an usb transfer if endpoint is not busy +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s); + +// 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) +{ + tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes); +} + +// Get the number of bytes available for reading +TU_ATTR_ALWAYS_INLINE static inline +uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) +{ + return (uint32_t) tu_fifo_count(&s->ff); +} + +TU_ATTR_ALWAYS_INLINE static inline +bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) +{ + return tu_fifo_peek(&s->ff, ch); +} + #ifdef __cplusplus } #endif diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 1bfa7c7d1..32cdba450 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -69,6 +69,15 @@ typedef enum TUSB_DIR_IN_MASK = 0x80 }tusb_dir_t; +enum +{ + TUSB_EPSIZE_BULK_FS = 64, + TUSB_EPSIZE_BULK_HS= 512, + + TUSB_EPSIZE_ISO_FS_MAX = 1023, + TUSB_EPSIZE_ISO_HS_MAX = 1024, +}; + /// Isochronous End Point Attributes typedef enum { @@ -243,7 +252,6 @@ enum INTERFACE_INVALID_NUMBER = 0xff }; - typedef enum { MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, @@ -265,6 +273,11 @@ enum CONTROL_STAGE_ACK }; +enum +{ + TUSB_INDEX_INVALID = 0xff +}; + //--------------------------------------------------------------------+ // USB Descriptors //--------------------------------------------------------------------+ diff --git a/src/device/usbd.c b/src/device/usbd.c index c199e647e..6e0c6710d 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -39,13 +39,13 @@ // USBD Configuration //--------------------------------------------------------------------+ -// Debug level of USBD -#define USBD_DBG 2 - #ifndef CFG_TUD_TASK_QUEUE_SZ #define CFG_TUD_TASK_QUEUE_SZ 16 #endif +// Debug level of USBD +#define USBD_DBG 2 + //--------------------------------------------------------------------+ // Device Data //--------------------------------------------------------------------+ @@ -272,10 +272,12 @@ static uint8_t _usbd_rhport = RHPORT_INVALID; OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); static osal_queue_t _usbd_q; -// Mutex for claiming endpoint, only needed when using with preempted RTOS -#if CFG_TUSB_OS != OPT_OS_NONE -static osal_mutex_def_t _ubsd_mutexdef; -static osal_mutex_t _usbd_mutex; +// Mutex for claiming endpoint +#if OSAL_MUTEX_REQUIRED + static osal_mutex_def_t _ubsd_mutexdef; + static osal_mutex_t _usbd_mutex; +#else + #define _usbd_mutex NULL #endif @@ -386,10 +388,12 @@ bool tud_init (uint8_t rhport) TU_LOG(USBD_DBG, "USBD init on controller %u\r\n", rhport); TU_LOG_INT(USBD_DBG, sizeof(usbd_device_t)); + TU_LOG_INT(USBD_DBG, sizeof(tu_fifo_t)); + TU_LOG_INT(USBD_DBG, sizeof(tu_edpt_stream_t)); tu_varclr(&_usbd_dev); -#if CFG_TUSB_OS != OPT_OS_NONE +#if OSAL_MUTEX_REQUIRED // Init device mutex _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef); TU_ASSERT(_usbd_mutex); @@ -504,7 +508,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) break; case DCD_EVENT_SETUP_RECEIVED: - TU_LOG_VAR(USBD_DBG, &event.setup_received); + TU_LOG_PTR(USBD_DBG, &event.setup_received); TU_LOG(USBD_DBG, "\r\n"); // Mark as connected after receiving 1st setup packet. @@ -1209,11 +1213,7 @@ bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; -#if TUSB_OPT_MUTEX return tu_edpt_claim(ep_state, _usbd_mutex); -#else - return tu_edpt_claim(ep_state, NULL); -#endif } bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) @@ -1224,11 +1224,7 @@ bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; -#if TUSB_OPT_MUTEX return tu_edpt_release(ep_state, _usbd_mutex); -#else - return tu_edpt_release(ep_state, NULL); -#endif } bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) diff --git a/src/device/usbd.h b/src/device/usbd.h index 731c57aad..ad19d1045 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -293,7 +293,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* MIDI Streaming (MS) Interface */\ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\ /* MS Header */\ - 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN) + 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN + 2 * TUD_MIDI_DESC_EP_LEN(_numcables)) #define TUD_MIDI_JACKID_IN_EMB(_cablenum) \ (uint8_t)(((_cablenum) - 1) * 4 + 1) @@ -317,6 +317,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, _stridx,\ /* MS Out Jack (External), connected to In Jack Embedded */\ 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, _stridx + #define TUD_MIDI_DESC_JACK(_cablenum) TUD_MIDI_DESC_JACK_DESC(_cablenum, 0) #define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables)) @@ -603,7 +604,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* optional interrupt endpoint */ \ // _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? #define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ - 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16 + 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), _int_pollingInterval #define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u) @@ -648,7 +649,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb #define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9) // Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size -// Note: Alternate count must be numberic or macro, string index is increased by one for each Alt interface +// Note: Alternate count must be numeric or macro, string index is increased by one for each Alt interface #define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \ TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \ /* Function */ \ diff --git a/src/host/usbh.c b/src/host/usbh.c index d97b160c9..5ac9e9cca 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -30,7 +30,6 @@ #include "host/hcd.h" #include "tusb.h" -#include "common/tusb_private.h" #include "host/usbh_classdriver.h" #include "hub.h" @@ -46,8 +45,10 @@ #define CFG_TUH_INTERFACE_MAX 8 #endif -// Debug level of USBD -#define USBH_DBG_LVL 2 +// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message +#define USBH_DEBUG 2 + +#define TU_LOG_USBH(...) TU_LOG(USBH_DEBUG, __VA_ARGS__) //--------------------------------------------------------------------+ // USBH-HCD common data structure @@ -212,28 +213,12 @@ static usbh_dev0_t _dev0; // TODO: hub can has its own simpler struct to save memory CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[TOTAL_DEVICES]; -// Mutex for claiming endpoint, only needed when using with preempted RTOS -#if TUSB_OPT_MUTEX -static osal_mutex_def_t _usbh_mutexdef; -static osal_mutex_t _usbh_mutex; - -TU_ATTR_ALWAYS_INLINE static inline void usbh_lock(void) -{ - osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); -} - -TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void) -{ - osal_mutex_unlock(_usbh_mutex); -} - +// Mutex for claiming endpoint +#if OSAL_MUTEX_REQUIRED + static osal_mutex_def_t _usbh_mutexdef; + static osal_mutex_t _usbh_mutex; #else - -#define _usbh_mutex NULL - -#define usbh_lock() -#define usbh_unlock() - + #define _usbh_mutex NULL #endif // Event queue @@ -244,10 +229,10 @@ static osal_queue_t _usbh_q; CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; -// Control transfer: since most controller does not support multiple control transfer -// on multiple devices concurrently. And control transfer is not used much except enumeration -// We will only execute control transfer one at a time. -struct +// Control transfers: since most controllers do not support multiple control transfers +// on multiple devices concurrently and control transfers are not used much except for +// enumeration, we will only execute control transfers one at a time. +CFG_TUSB_MEM_SECTION struct { tusb_control_request_t request TU_ATTR_ALIGNED(4); uint8_t* buffer; @@ -277,8 +262,6 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t // TODO rework time-related function later void osal_task_delay(uint32_t msec) { - (void) msec; - const uint32_t start = hcd_frame_number(_usbh_controller); while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {} } @@ -342,18 +325,20 @@ bool tuh_init(uint8_t controller_id) // skip if already initialized if ( tuh_inited() ) return true; - TU_LOG2("USBH init on controller %u\r\n", controller_id); - TU_LOG2_INT(sizeof(usbh_device_t)); - TU_LOG2_INT(sizeof(hcd_event_t)); - TU_LOG2_INT(sizeof(_ctrl_xfer)); - TU_LOG2_INT(sizeof(tuh_xfer_t)); + TU_LOG_USBH("USBH init on controller %u\r\n", controller_id); + TU_LOG_INT(USBH_DEBUG, sizeof(usbh_device_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(hcd_event_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(_ctrl_xfer)); + TU_LOG_INT(USBH_DEBUG, sizeof(tuh_xfer_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(tu_fifo_t)); + TU_LOG_INT(USBH_DEBUG, sizeof(tu_edpt_stream_t)); // Event queue _usbh_q = osal_queue_create( &_usbh_qdef ); TU_ASSERT(_usbh_q != NULL); -#if TUSB_OPT_MUTEX - // Mutex +#if OSAL_MUTEX_REQUIRED + // Init mutex _usbh_mutex = osal_mutex_create(&_usbh_mutexdef); TU_ASSERT(_usbh_mutex); #endif @@ -371,7 +356,7 @@ bool tuh_init(uint8_t controller_id) // Class drivers for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { - TU_LOG2("%s init\r\n", usbh_class_drivers[drv_id].name); + TU_LOG_USBH("%s init\r\n", usbh_class_drivers[drv_id].name); usbh_class_drivers[drv_id].init(); } @@ -419,12 +404,12 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) case HCD_EVENT_DEVICE_ATTACH: // TODO due to the shared _usbh_ctrl_buf, we must complete enumerating // one device before enumerating another one. - TU_LOG2("[%u:] USBH DEVICE ATTACH\r\n", event.rhport); + TU_LOG_USBH("[%u:] USBH DEVICE ATTACH\r\n", event.rhport); enum_new_device(&event); break; case HCD_EVENT_DEVICE_REMOVE: - TU_LOG2("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port); + TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port); process_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port); #if CFG_TUH_HUB @@ -443,7 +428,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const ep_dir = tu_edpt_dir(ep_addr); - TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); + TU_LOG_USBH("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); if (event.dev_addr == 0) { @@ -467,7 +452,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; if(drv_id < USBH_CLASS_DRIVER_COUNT) { - TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); + TU_LOG_USBH("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); } else @@ -537,8 +522,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) uint8_t const daddr = xfer->daddr; - // TODO probably better to use semaphore as resource management than mutex - usbh_lock(); + (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE); if (is_idle) @@ -553,14 +537,16 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) _ctrl_xfer.user_data = xfer->user_data; } - usbh_unlock(); + (void) osal_mutex_unlock(_usbh_mutex); TU_VERIFY(is_idle); const uint8_t rhport = usbh_get_rhport(daddr); - TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->setup->bRequest] : "Unknown Request"); - TU_LOG2_VAR(xfer->setup); - TU_LOG2("\r\n"); + TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr, + (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ? + tu_str_std_request[xfer->setup->bRequest] : "Class Request"); + TU_LOG_PTR(USBH_DEBUG, xfer->setup); + TU_LOG_USBH("\r\n"); if (xfer->complete_cb) { @@ -597,14 +583,14 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) { - usbh_lock(); + (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER); _ctrl_xfer.stage = stage; - usbh_unlock(); + (void) osal_mutex_unlock(_usbh_mutex); } static void _xfer_complete(uint8_t daddr, xfer_result_t result) { - TU_LOG2("\r\n"); + TU_LOG_USBH("\r\n"); // duplicate xfer since user can execute control transfer within callback tusb_control_request_t const request = _ctrl_xfer.request; @@ -637,7 +623,11 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result if (XFER_RESULT_SUCCESS != result) { - TU_LOG1("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED"); + TU_LOG1("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes); + #if CFG_TUSB_DEBUG == 1 + TU_LOG1_PTR(request); + TU_LOG1("\r\n"); + #endif // terminate transfer if any stage failed _xfer_complete(dev_addr, result); @@ -658,8 +648,8 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result case CONTROL_STAGE_DATA: if (request->wLength) { - TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr); - TU_LOG2_MEM(_ctrl_xfer.buffer, xferred_bytes, 2); + TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, dev_addr); + TU_LOG_MEM(USBH_DEBUG, _ctrl_xfer.buffer, xferred_bytes, 2); } _ctrl_xfer.actual_len = (uint16_t) xferred_bytes; @@ -775,7 +765,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b uint8_t const dir = tu_edpt_dir(ep_addr); tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; - TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + TU_LOG_USBH(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); // Attempt to transfer on a busy endpoint, sound like an race condition ! TU_ASSERT(ep_state->busy == 0); @@ -791,7 +781,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) { - TU_LOG2("OK\r\n"); + TU_LOG_USBH("OK\r\n"); return true; }else { @@ -806,7 +796,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) { - TU_LOG2("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size); + TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size); tusb_desc_endpoint_t ep0_desc = { @@ -975,7 +965,7 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG2("HID Get Report Descriptor\r\n"); + TU_LOG_USBH("HID Get Report Descriptor\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = @@ -1014,7 +1004,7 @@ bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG2("Set Configuration = %d\r\n", config_num); + TU_LOG_USBH("Set Configuration = %d\r\n", config_num); tusb_control_request_t const request = { @@ -1118,11 +1108,11 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub dev->connected) { - TU_LOG2(" Address = %u\r\n", dev_addr); + TU_LOG_USBH(" Address = %u\r\n", dev_addr); if (is_hub_addr(dev_addr)) { - TU_LOG(USBH_DBG_LVL, "HUB address = %u is unmounted\r\n", dev_addr); + TU_LOG(USBH_DEBUG, "HUB address = %u is unmounted\r\n", dev_addr); // If the device itself is a usb hub, unplug downstream devices. // FIXME un-roll recursive calls to prevent potential stack overflow process_device_unplugged(rhport, dev_addr, 0); @@ -1135,7 +1125,7 @@ static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t h // Close class driver for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { - TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name); + TU_LOG_USBH("%s close\r\n", usbh_class_drivers[drv_id].name); usbh_class_drivers[drv_id].close(dev_addr); } @@ -1260,7 +1250,7 @@ static void process_enumeration(tuh_xfer_t* xfer) TU_ASSERT( usbh_edpt_control_open(addr0, 8), ); // Get first 8 bytes of device descriptor for Control Endpoint size - TU_LOG2("Get 8 byte of Device Descriptor\r\n"); + TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n"); TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), ); } break; @@ -1269,13 +1259,13 @@ static void process_enumeration(tuh_xfer_t* xfer) case ENUM_RESET_2: // TODO not used by now, but may be needed for some devices !? // Reset device again before Set Address - TU_LOG2("Port reset2 \r\n"); + TU_LOG_USBH("Port reset2 \r\n"); if (_dev0.hub_addr == 0) { // connected directly to roothub hcd_port_reset( _dev0.rhport ); osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since - // sof of controller may not running while reseting + // sof of controller may not running while resetting hcd_port_reset_end(_dev0.rhport); // TODO: fall through to SET ADDRESS, refactor later } @@ -1287,7 +1277,7 @@ static void process_enumeration(tuh_xfer_t* xfer) break; } #endif - __attribute__((fallthrough)); + TU_ATTR_FALLTHROUGH; #endif case ENUM_SET_ADDR: @@ -1309,7 +1299,7 @@ static void process_enumeration(tuh_xfer_t* xfer) TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), ); // Get full device descriptor - TU_LOG2("Get Device Descriptor\r\n"); + TU_LOG_USBH("Get Device Descriptor\r\n"); TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), ); } break; @@ -1330,7 +1320,7 @@ static void process_enumeration(tuh_xfer_t* xfer) // Get 9-byte for total length uint8_t const config_idx = CONFIG_NUM - 1; - TU_LOG2("Get Configuration[0] Descriptor (9 bytes)\r\n"); + TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n"); TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), ); } break; @@ -1347,7 +1337,7 @@ static void process_enumeration(tuh_xfer_t* xfer) // Get full configuration descriptor uint8_t const config_idx = CONFIG_NUM - 1; - TU_LOG2("Get Configuration[0] Descriptor\r\n"); + TU_LOG_USBH("Get Configuration[0] Descriptor\r\n"); TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), ); } break; @@ -1362,7 +1352,7 @@ static void process_enumeration(tuh_xfer_t* xfer) case ENUM_CONFIG_DRIVER: { - TU_LOG2("Device configured\r\n"); + TU_LOG_USBH("Device configured\r\n"); usbh_device_t* dev = get_device(daddr); TU_ASSERT(dev, ); @@ -1395,14 +1385,14 @@ static bool enum_new_device(hcd_event_t* event) // wait until device is stable TODO non blocking hcd_port_reset(_dev0.rhport); osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since - // sof of controller may not running while reseting + // sof of controller may not running while resetting hcd_port_reset_end( _dev0.rhport); // device unplugged while delaying if ( !hcd_port_connect_status(_dev0.rhport) ) return true; _dev0.speed = hcd_port_speed_get(_dev0.rhport ); - TU_LOG2("%s Speed\r\n", tu_str_speed[_dev0.speed]); + TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]); // fake transfer to kick-off the enumeration process tuh_xfer_t xfer; @@ -1459,7 +1449,7 @@ static bool enum_request_set_addr(void) uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB); TU_ASSERT(new_addr != 0); - TU_LOG2("Set Address = %d\r\n", new_addr); + TU_LOG_USBH("Set Address = %d\r\n", new_addr); usbh_device_t* new_dev = get_device(new_addr); @@ -1503,9 +1493,12 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur { usbh_device_t* dev = get_device(dev_addr); - uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); + uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength); + uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len; uint8_t const* p_desc = tu_desc_next(desc_cfg); + TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len); + // parse each interfaces while( p_desc < desc_end ) { @@ -1543,15 +1536,14 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t)); // Find driver for this interface - uint8_t drv_id; - for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) ) { // open successfully - TU_LOG2(" %s opened\r\n", driver->name); + TU_LOG_USBH(" %s opened\r\n", driver->name); // bind (associated) interfaces to found driver for(uint8_t i=0; i= USBH_CLASS_DRIVER_COUNT ) { - TU_LOG(USBH_DBG_LVL, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n", + TU_LOG(USBH_DEBUG, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n", desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol); } } @@ -1590,12 +1582,13 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) { // continue with next valid interface - // TODO skip IAD binding interface such as CDCs + // IAD binding interface such as CDCs should return itf_num + 1 when complete + // with usbh_driver_set_config_complete() uint8_t const drv_id = dev->itf2drv[itf_num]; if (drv_id != DRVID_INVALID) { usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; - TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num); + TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num); driver->set_config(dev_addr, itf_num); break; } @@ -1608,7 +1601,7 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) if (is_hub_addr(dev_addr)) { - TU_LOG(USBH_DBG_LVL, "HUB address = %u is mounted\r\n", dev_addr); + TU_LOG(USBH_DEBUG, "HUB address = %u is mounted\r\n", dev_addr); }else { // Invoke callback if available diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h index c156afea0..be9811641 100644 --- a/src/host/usbh_classdriver.h +++ b/src/host/usbh_classdriver.h @@ -29,11 +29,16 @@ #include "osal/osal.h" #include "common/tusb_fifo.h" +#include "common/tusb_private.h" #ifdef __cplusplus extern "C" { #endif +enum { + USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS) +}; + //--------------------------------------------------------------------+ // Class Driver API //--------------------------------------------------------------------+ diff --git a/src/osal/osal.h b/src/osal/osal.h index 9d11866df..afa3826fc 100644 --- a/src/osal/osal.h +++ b/src/osal/osal.h @@ -33,17 +33,24 @@ #include "common/tusb_common.h" -// Return immediately -#define OSAL_TIMEOUT_NOTIMEOUT (0) -// Default timeout -#define OSAL_TIMEOUT_NORMAL (10) -// Wait forever -#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) - -#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER - typedef void (*osal_task_func_t)( void * ); +// Timeout +#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately +#define OSAL_TIMEOUT_NORMAL (10) // Default timeout +#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever +#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER + +// Mutex is required when using a preempted RTOS or MCU has multiple cores +#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE + #define OSAL_MUTEX_REQUIRED 0 + #define OSAL_MUTEX_DEF(_name) uint8_t :0 +#else + #define OSAL_MUTEX_REQUIRED 1 + #define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name +#endif + +// OS thin implementation #if CFG_TUSB_OS == OPT_OS_NONE #include "osal_none.h" #elif CFG_TUSB_OS == OPT_OS_FREERTOS diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h index 52db336f5..9393d1f26 100644 --- a/src/osal/osal_freertos.h +++ b/src/osal/osal_freertos.h @@ -37,6 +37,43 @@ extern "C" { #endif +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +#if configSUPPORT_STATIC_ALLOCATION + typedef StaticSemaphore_t osal_semaphore_def_t; + typedef StaticSemaphore_t osal_mutex_def_t; +#else + // not used therefore defined to smallest possible type to save space + typedef uint8_t osal_semaphore_def_t; + typedef uint8_t osal_mutex_def_t; +#endif + +typedef SemaphoreHandle_t osal_semaphore_t; +typedef SemaphoreHandle_t osal_mutex_t; + +// _int_set is not used with an RTOS +#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ + static _type _name##_##buf[_depth];\ + osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf }; + +typedef struct +{ + uint16_t depth; + uint16_t item_sz; + void* buf; +#if configSUPPORT_STATIC_ALLOCATION + StaticQueue_t sq; +#endif +}osal_queue_def_t; + +typedef QueueHandle_t osal_queue_t; + +//--------------------------------------------------------------------+ +// TASK API +//--------------------------------------------------------------------+ + TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) { if (msec == OSAL_TIMEOUT_WAIT_FOREVER) return portMAX_DELAY; @@ -51,9 +88,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) return ticks; } -//--------------------------------------------------------------------+ -// TASK API -//--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { vTaskDelay( pdMS_TO_TICKS(msec) ); @@ -62,12 +96,15 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) //--------------------------------------------------------------------+ // Semaphore API //--------------------------------------------------------------------+ -typedef StaticSemaphore_t osal_semaphore_def_t; -typedef SemaphoreHandle_t osal_semaphore_t; TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) { +#if configSUPPORT_STATIC_ALLOCATION return xSemaphoreCreateBinaryStatic(semdef); +#else + (void) semdef; + return xSemaphoreCreateBinary(); +#endif } TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) @@ -78,7 +115,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t se } else { - BaseType_t xHigherPriorityTaskWoken; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken); #if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 @@ -92,7 +129,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t se } } -TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec) +TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) { return xSemaphoreTake(sem_hdl, _osal_ms2tick(msec)); } @@ -105,15 +142,18 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t c //--------------------------------------------------------------------+ // MUTEX API (priority inheritance) //--------------------------------------------------------------------+ -typedef StaticSemaphore_t osal_mutex_def_t; -typedef SemaphoreHandle_t osal_mutex_t; TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) { +#if configSUPPORT_STATIC_ALLOCATION return xSemaphoreCreateMutexStatic(mdef); +#else + (void) mdef; + return xSemaphoreCreateMutex(); +#endif } -TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) +TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) { return osal_semaphore_wait(mutex_hdl, msec); } @@ -127,25 +167,13 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd // QUEUE API //--------------------------------------------------------------------+ -// _int_set is not used with an RTOS -#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \ - static _type _name##_##buf[_depth];\ - osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf }; - -typedef struct -{ - uint16_t depth; - uint16_t item_sz; - void* buf; - - StaticQueue_t sq; -}osal_queue_def_t; - -typedef QueueHandle_t osal_queue_t; - TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) { - return xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq); +#if configSUPPORT_STATIC_ALLOCATION + return xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq); +#else + return xQueueCreate(qdef->depth, qdef->item_sz); +#endif } TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) @@ -161,7 +189,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void } else { - BaseType_t xHigherPriorityTaskWoken; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken); #if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index 9c80e4548..1ad130557 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -82,6 +82,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t s typedef osal_semaphore_def_t osal_mutex_def_t; typedef osal_semaphore_t osal_mutex_t; +#if OSAL_MUTEX_REQUIRED +// Note: multiple cores MCUs usually do provide IPC API for mutex +// or we can use std atomic function + TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) { mdef->count = 1; @@ -98,6 +102,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd return osal_semaphore_post(mutex_hdl, false); } +#else + +#define osal_mutex_create(_mdef) (NULL) +#define osal_mutex_lock(_mutex_hdl, _ms) (true) +#define osal_mutex_unlock(_mutex_hdl) (true) + +#endif + //--------------------------------------------------------------------+ // QUEUE API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h index f8452bfb2..18eb9c693 100644 --- a/src/osal/osal_rtthread.h +++ b/src/osal/osal_rtthread.h @@ -63,7 +63,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t se } TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) { - // TODO: implement + rt_sem_control(sem_hdl, RT_IPC_CMD_RESET, 0); } //--------------------------------------------------------------------+ diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c index fabe7910b..f4c9b2b27 100644 --- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c +++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c @@ -595,7 +595,7 @@ void dcd_remote_wakeup(uint8_t rhport) SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP; - // Atleast 2 ms of delay needed for RESUME Data K state. + // At least 2 ms of delay needed for RESUME Data K state. delayms(2); SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP; diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 0bb8b6e41..961da81d6 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -243,7 +243,7 @@ static struct // Converts xfer pointer to epnum (0,1,2,3) regardless of xfer direction #define XFER_EPNUM(xfer) ((xfer - &_dcd.xfer_status[0][0]) >> 1) -// Converts xfer pinter to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number) +// Converts xfer pointer to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number) #define XFER_REGS(xfer) ep_regs[XFER_EPNUM(xfer)] // Converts epnum (0,1,2,3) to EPx_REGS pointer #define EPNUM_REGS(epnum) ep_regs[epnum] diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 76ba2a921..7140897a1 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -188,7 +188,7 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) { for(ehci_link_t* prev = list_head; - !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head); + !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head) && prev != NULL; prev = list_next(prev) ) { // TODO check type for ISO iTD and siTD @@ -199,7 +199,7 @@ static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) #pragma GCC diagnostic pop if ( qhd->dev_addr == dev_addr ) { - // TODO deactive all TD, wait for QHD to inactive before removal + // TODO deactivate all TD, wait for QHD to inactive before removal prev->address = qhd->next.address; // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller) @@ -839,7 +839,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c if (TUSB_SPEED_HIGH == p_qhd->ep_speed) { TU_ASSERT( interval <= 16, ); - if ( interval < 4) // sub milisecond interval + if ( interval < 4) // sub millisecond interval { p_qhd->interval_ms = 0; p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) : diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index ff9ae12e7..36f8649be 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -114,7 +114,7 @@ typedef struct volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list uint32_t int_on_complete : 1 ; ///< Interrupt on complete volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction - volatile uint32_t data_toggle : 1 ; ///< Data Toogle bit + volatile uint32_t data_toggle : 1 ; ///< Data Toggle bit /// Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page @@ -160,7 +160,7 @@ typedef struct TU_ATTR_ALIGNED(32) uint8_t used; uint8_t removing; // removed from asyn list, waiting for async advance uint8_t pid; - uint8_t interval_ms; // polling interval in frames (or milisecond) + uint8_t interval_ms; // polling interval in frames (or millisecond) uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set uint8_t reserved2[2]; @@ -225,7 +225,7 @@ typedef struct TU_ATTR_ALIGNED(32) uint16_t reserved ; ///< reserved // Word 3: siTD Transfer Status and Control - // Status [7:0] TODO indentical to qTD Token'status --> refractor later + // Status [7:0] TODO identical to qTD Token'status --> refactor later volatile uint32_t : 1 ; // reserved volatile uint32_t split_state : 1 ; volatile uint32_t missed_uframe : 1 ; @@ -350,8 +350,8 @@ typedef volatile struct uint32_t periodic_status : 1 ; ///< Periodic schedule status uint32_t async_status : 1 ; ///< Async schedule status uint32_t : 2 ; - uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule. - uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule. + uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected and the packet is on the asynchronous schedule. + uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the periodic schedule. uint32_t : 12 ; }status_bm; }; diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c index 0b75af627..41240f737 100644 --- a/src/portable/espressif/esp32sx/dcd_esp32sx.c +++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c @@ -741,7 +741,7 @@ static void handle_epin_ints(void) // XFER Timeout if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) { - // Clear interrupt or enpoint will hang. + // Clear interrupt or endpoint will hang. USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M; // Maybe retry? } diff --git a/src/portable/microchip/pic32mz/usbhs_registers.h b/src/portable/microchip/pic32mz/usbhs_registers.h index 757e3f083..93b552322 100644 --- a/src/portable/microchip/pic32mz/usbhs_registers.h +++ b/src/portable/microchip/pic32mz/usbhs_registers.h @@ -21,16 +21,16 @@ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. *******************************************************************************/ /******************************************************************************* - USBHS Peripheral Library Register Defintions + USBHS Peripheral Library Register Definitions File Name: usbhs_registers.h Summary: - USBHS PLIB Register Defintions + USBHS PLIB Register Definitions Description: - This file contains the constants and defintions which are required by the + This file contains the constants and definitions which are required by the the USBHS library. *******************************************************************************/ diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c index 7507c0f69..24657872b 100644 --- a/src/portable/microchip/samx7x/dcd_samx7x.c +++ b/src/portable/microchip/samx7x/dcd_samx7x.c @@ -42,7 +42,7 @@ # define USE_SOF 0 #endif -// Dual bank can imporve performance, but need 2 times bigger packet buffer +// Dual bank can improve performance, but need 2 times bigger packet buffer // As SAM7x has only 4KB packet buffer, use with caution ! // Enable in FS mode as packets are smaller #ifndef USE_DUAL_BANK @@ -644,7 +644,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t } __set_PRIMASK(irq_state); - // Here a ZLP has been recieved + // Here a ZLP has been received // and the DMA transfer must be not started. // It is the end of transfer return false; @@ -734,7 +734,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 } __set_PRIMASK(irq_state); - // Here a ZLP has been recieved + // Here a ZLP has been received // and the DMA transfer must be not started. // It is the end of transfer return false; diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index ca37d799f..afc14b010 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -170,7 +170,7 @@ static void xact_out_dma(uint8_t epnum) uint32_t xact_len; // DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock, - // If already running deffer call regardless if it was called from ISR or task, + // If already running defer call regardless if it was called from ISR or task, if ( atomic_flag_test_and_set(&_dcd.dma_running) ) { usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr()); diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index 886720e33..3fa7c1ec1 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -181,7 +181,7 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) ep->EPINTEN = USBD_EPINTEN_TXPKIEN_Msk; } - /* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */ + /* 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) { diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c index 9b490c48c..124656307 100644 --- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c +++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c @@ -139,7 +139,7 @@ typedef union TU_ATTR_PACKED uint32_t stall : 1 ; uint32_t disable : 1 ; uint32_t active : 1 ; - }; + } cmd_sts; }ep_cmd_sts_t; TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" ); @@ -329,7 +329,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) // TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around uint8_t const ep_id = ep_addr2id(ep_addr); - _dcd.ep[ep_id][0].stall = 1; + _dcd.ep[ep_id][0].cmd_sts.stall = 1; } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -338,9 +338,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const ep_id = ep_addr2id(ep_addr); - _dcd.ep[ep_id][0].stall = 0; - _dcd.ep[ep_id][0].toggle_reset = 1; - _dcd.ep[ep_id][0].toggle_mode = 0; + _dcd.ep[ep_id][0].cmd_sts.stall = 0; + _dcd.ep[ep_id][0].cmd_sts.toggle_reset = 1; + _dcd.ep[ep_id][0].cmd_sts.toggle_mode = 0; } bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) @@ -349,10 +349,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress); // Check if endpoint is available - TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable ); + TU_ASSERT( _dcd.ep[ep_id][0].cmd_sts.disable && _dcd.ep[ep_id][1].cmd_sts.disable ); edpt_reset(rhport, ep_id); - _dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS); + _dcd.ep[ep_id][0].cmd_sts.is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS); // Enable EP interrupt dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; @@ -365,8 +365,8 @@ void dcd_edpt_close_all (uint8_t rhport) { for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id) { - _dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual) - _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1; + _dcd.ep[ep_id][0].cmd_sts.active = _dcd.ep[ep_id][0].cmd_sts.active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual) + _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1; } } @@ -375,8 +375,8 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) (void) rhport; uint8_t ep_id = ep_addr2id(ep_addr); - _dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual) - _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1; + _dcd.ep[ep_id][0].cmd_sts.active = _dcd.ep[ep_id][0].cmd_sts.active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual) + _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1; } static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) @@ -385,7 +385,7 @@ static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL ) { - nbytes = tu_min16(total_bytes, _dcd.ep[ep_id][0].is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX); + nbytes = tu_min16(total_bytes, _dcd.ep[ep_id][0].cmd_sts.is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX); _dcd.ep[ep_id][0].buffer_fs.offset = buf_offset; _dcd.ep[ep_id][0].buffer_fs.nbytes = nbytes; }else @@ -397,7 +397,7 @@ static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, _dcd.dma[ep_id].nbytes = nbytes; - _dcd.ep[ep_id][0].active = 1; + _dcd.ep[ep_id][0].cmd_sts.active = 1; } bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) @@ -431,7 +431,7 @@ static void bus_reset(uint8_t rhport) // disable all endpoints as specified by LPC55S69 UM Table 778 for(uint8_t ep_id = 0; ep_id < 2*MAX_EP_PAIRS; ep_id++) { - _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1; + _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1; } dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; @@ -459,7 +459,7 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status) if ( ep_id == 0 || ep_id == 1) { // For control endpoint, we need to manually clear Active bit - ep_cs->active = 0; + ep_cs->cmd_sts.active = 0; } uint16_t buf_offset; @@ -556,8 +556,8 @@ void dcd_int_handler(uint8_t rhport) if ( tu_bit_test(int_status, 0) && (cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) ) { // Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints - _dcd.ep[0][0].active = _dcd.ep[1][0].active = 0; - _dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 0; + _dcd.ep[0][0].cmd_sts.active = _dcd.ep[1][0].cmd_sts.active = 0; + _dcd.ep[0][0].cmd_sts.stall = _dcd.ep[1][0].cmd_sts.stall = 0; dcd_reg->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c index 3e523ebc2..228da6ae0 100644 --- a/src/portable/ohci/ohci.c +++ b/src/portable/ohci/ohci.c @@ -165,7 +165,7 @@ bool hcd_init(uint8_t rhport) //------------- Data Structure init -------------// tu_memclr(&ohci_data, sizeof(ohci_data_t)); for(uint8_t i=0; i<32; i++) - { // assign all interrupt pointes to period head ed + { // assign all interrupt pointers to period head ed ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed; } diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h index 9fc954c8f..f40ae24cc 100644 --- a/src/portable/ohci/ohci.h +++ b/src/portable/ohci/ohci.h @@ -34,7 +34,7 @@ //--------------------------------------------------------------------+ // OHCI CONFIGURATION & CONSTANTS //--------------------------------------------------------------------+ -#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed +#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enabled #define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS) // TODO merge OHCI with EHCI diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 10237d1f9..9e6bdad44 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -81,89 +81,90 @@ static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) { - return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; + return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; } -static bool need_pre(uint8_t dev_addr) +TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { - // If this device is different to the speed of the root device - // (i.e. is a low speed device on a full speed hub) then need pre - return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); + // If this device is different to the speed of the root device + // (i.e. is a low speed device on a full speed hub) then need pre + return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result) { - // Mark transfer as done before we tell the tinyusb stack - uint8_t dev_addr = ep->dev_addr; - uint8_t ep_addr = ep->ep_addr; - uint xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); - hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); + // Mark transfer as done before we tell the tinyusb stack + uint8_t dev_addr = ep->dev_addr; + uint8_t ep_addr = ep->ep_addr; + uint xferred_len = ep->xferred_len; + hw_endpoint_reset_transfer(ep); + hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep) { - usb_hw_clear->buf_status = bit; - // EP may have been stalled? - assert(ep->active); - bool done = hw_endpoint_xfer_continue(ep); - if (done) - { - hw_xfer_complete(ep, XFER_RESULT_SUCCESS); - } + usb_hw_clear->buf_status = bit; + // EP may have been stalled? + assert(ep->active); + bool done = hw_endpoint_xfer_continue(ep); + if ( done ) + { + hw_xfer_complete(ep, XFER_RESULT_SUCCESS); + } } static void __tusb_irq_path_func(hw_handle_buff_status)(void) { - uint32_t remaining_buffers = usb_hw->buf_status; - pico_trace("buf_status 0x%08x\n", remaining_buffers); + uint32_t remaining_buffers = usb_hw->buf_status; + pico_trace("buf_status 0x%08x\n", remaining_buffers); - // Check EPX first - uint bit = 0b1; - if (remaining_buffers & bit) + // Check EPX first + uint bit = 0b1; + if ( remaining_buffers & bit ) + { + remaining_buffers &= ~bit; + struct hw_endpoint * ep = &epx; + + uint32_t ep_ctrl = *ep->endpoint_control; + if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS ) { + TU_LOG(3, "Double Buffered: "); + } + else + { + TU_LOG(3, "Single Buffered: "); + } + TU_LOG_HEX(3, ep_ctrl); + + _handle_buff_status_bit(bit, ep); + } + + // Check "interrupt" (asynchronous) endpoints for both IN and OUT + for ( uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++ ) + { + // EPX is bit 0 & 1 + // IEP1 IN is bit 2 + // IEP1 OUT is bit 3 + // IEP2 IN is bit 4 + // IEP2 OUT is bit 5 + // IEP3 IN is bit 6 + // IEP3 OUT is bit 7 + // etc + for ( uint j = 0; j < 2; j++ ) + { + bit = 1 << (i * 2 + j); + if ( remaining_buffers & bit ) + { remaining_buffers &= ~bit; - struct hw_endpoint *ep = &epx; - - uint32_t ep_ctrl = *ep->endpoint_control; - if (ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS) - { - TU_LOG(3, "Double Buffered: "); - }else - { - TU_LOG(3, "Single Buffered: "); - } - TU_LOG_HEX(3, ep_ctrl); - - _handle_buff_status_bit(bit, ep); + _handle_buff_status_bit(bit, &ep_pool[i]); + } } + } - // Check "interrupt" (asynchronous) endpoints for both IN and OUT - for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++) - { - // EPX is bit 0 & 1 - // IEP1 IN is bit 2 - // IEP1 OUT is bit 3 - // IEP2 IN is bit 4 - // IEP2 OUT is bit 5 - // IEP3 IN is bit 6 - // IEP3 OUT is bit 7 - // etc - for(uint j = 0; j < 2; j++) - { - bit = 1 << (i*2+j); - if (remaining_buffers & bit) - { - remaining_buffers &= ~bit; - _handle_buff_status_bit(bit, &ep_pool[i]); - } - } - } - - if (remaining_buffers) - { - panic("Unhandled buffer %d\n", remaining_buffers); - } + if ( remaining_buffers ) + { + panic("Unhandled buffer %d\n", remaining_buffers); + } } static void __tusb_irq_path_func(hw_trans_complete)(void) @@ -186,70 +187,72 @@ static void __tusb_irq_path_func(hw_trans_complete)(void) static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { - uint32_t status = usb_hw->ints; - uint32_t handled = 0; + uint32_t status = usb_hw->ints; + uint32_t handled = 0; - if (status & USB_INTS_HOST_CONN_DIS_BITS) + if ( status & USB_INTS_HOST_CONN_DIS_BITS ) + { + handled |= USB_INTS_HOST_CONN_DIS_BITS; + + if ( dev_speed() ) { - handled |= USB_INTS_HOST_CONN_DIS_BITS; - - if (dev_speed()) - { - hcd_event_device_attach(RHPORT_NATIVE, true); - } - else - { - hcd_event_device_remove(RHPORT_NATIVE, true); - } - - // Clear speed change interrupt - usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; + hcd_event_device_attach(RHPORT_NATIVE, true); + } + else + { + hcd_event_device_remove(RHPORT_NATIVE, true); } - if (status & USB_INTS_STALL_BITS) - { - // We have rx'd a stall from the device - // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS - // AND TRANS_COMPLETE as the stall is an alternative response - // to one of those events - pico_trace("Stall REC\n"); - handled |= USB_INTS_STALL_BITS; - usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; - hw_xfer_complete(&epx, XFER_RESULT_STALLED); - } + // Clear speed change interrupt + usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; + } - if (status & USB_INTS_BUFF_STATUS_BITS) - { - handled |= USB_INTS_BUFF_STATUS_BITS; - TU_LOG(2, "Buffer complete\n"); - hw_handle_buff_status(); - } + if ( status & USB_INTS_STALL_BITS ) + { + // We have rx'd a stall from the device + // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS + // AND TRANS_COMPLETE as the stall is an alternative response + // to one of those events + pico_trace("Stall REC\n"); + handled |= USB_INTS_STALL_BITS; + usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; + hw_xfer_complete(&epx, XFER_RESULT_STALLED); + } - if (status & USB_INTS_TRANS_COMPLETE_BITS) - { - handled |= USB_INTS_TRANS_COMPLETE_BITS; - usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; - TU_LOG(2, "Transfer complete\n"); - hw_trans_complete(); - } + if ( status & USB_INTS_BUFF_STATUS_BITS ) + { + handled |= USB_INTS_BUFF_STATUS_BITS; + TU_LOG(2, "Buffer complete\n"); + hw_handle_buff_status(); + } - if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) - { - handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS; - usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; - } + if ( status & USB_INTS_TRANS_COMPLETE_BITS ) + { + handled |= USB_INTS_TRANS_COMPLETE_BITS; + usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; + TU_LOG(2, "Transfer complete\n"); + hw_trans_complete(); + } - if (status & USB_INTS_ERROR_DATA_SEQ_BITS) - { - usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; - TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*epx.buffer_control), tu_u32_high16(*epx.buffer_control)); - panic("Data Seq Error \n"); - } + if ( status & USB_INTS_ERROR_RX_TIMEOUT_BITS ) + { + handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS; + usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; + } - if (status ^ handled) - { - panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); - } + if ( status & USB_INTS_ERROR_DATA_SEQ_BITS ) + { + usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; + TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", + tu_u32_low16(*epx.buffer_control), + tu_u32_high16(*epx.buffer_control)); + panic("Data Seq Error \n"); + } + + if ( status ^ handled ) + { + panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); + } } void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport) @@ -260,116 +263,118 @@ void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport) static struct hw_endpoint *_next_free_interrupt_ep(void) { - struct hw_endpoint *ep = NULL; - for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) + struct hw_endpoint * ep = NULL; + for ( uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ ) + { + ep = &ep_pool[i]; + if ( !ep->configured ) { - ep = &ep_pool[i]; - if (!ep->configured) - { - // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate - ep->interrupt_num = (uint8_t) (i - 1); - return ep; - } + // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate + ep->interrupt_num = (uint8_t) (i - 1); + return ep; } - return ep; + } + return ep; } static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) { - struct hw_endpoint *ep = NULL; + struct hw_endpoint * ep = NULL; - if (transfer_type != TUSB_XFER_CONTROL) - { - // Note: even though datasheet name these "Interrupt" endpoints. These are actually - // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation) - ep = _next_free_interrupt_ep(); - pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num); - assert(ep); - ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; - ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; - // 0 for epx (double buffered): TODO increase to 1024 for ISO - // 2x64 for intep0 - // 3x64 for intep1 - // etc - ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)]; - } - else - { - ep = &epx; - ep->buffer_control = &usbh_dpram->epx_buf_ctrl; - ep->endpoint_control = &usbh_dpram->epx_ctrl; - ep->hw_data_buf = &usbh_dpram->epx_data[0]; - } + if ( transfer_type != TUSB_XFER_CONTROL ) + { + // Note: even though datasheet name these "Interrupt" endpoints. These are actually + // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation) + ep = _next_free_interrupt_ep(); + pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num); + assert(ep); + ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; + ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; + // 0 for epx (double buffered): TODO increase to 1024 for ISO + // 2x64 for intep0 + // 3x64 for intep1 + // etc + ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)]; + } + else + { + ep = &epx; + ep->buffer_control = &usbh_dpram->epx_buf_ctrl; + ep->endpoint_control = &usbh_dpram->epx_ctrl; + ep->hw_data_buf = &usbh_dpram->epx_data[0]; + } - return ep; + return ep; } static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval) { - // Already has data buffer, endpoint control, and buffer control allocated at this point - assert(ep->endpoint_control); - assert(ep->buffer_control); - assert(ep->hw_data_buf); + // Already has data buffer, endpoint control, and buffer control allocated at this point + assert(ep->endpoint_control); + assert(ep->buffer_control); + assert(ep->hw_data_buf); - uint8_t const num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); + uint8_t const num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); - ep->ep_addr = ep_addr; - ep->dev_addr = dev_addr; + ep->ep_addr = ep_addr; + ep->dev_addr = dev_addr; - // For host, IN to host == RX, anything else rx == false - ep->rx = (dir == TUSB_DIR_IN); + // For host, IN to host == RX, anything else rx == false + ep->rx = (dir == TUSB_DIR_IN); - // Response to a setup packet on EP0 starts with pid of 1 - ep->next_pid = (num == 0 ? 1u : 0u); - ep->wMaxPacketSize = wMaxPacketSize; - ep->transfer_type = transfer_type; + // Response to a setup packet on EP0 starts with pid of 1 + ep->next_pid = (num == 0 ? 1u : 0u); + ep->wMaxPacketSize = wMaxPacketSize; + ep->transfer_type = transfer_type; - pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type); - pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf); - uint dpram_offset = hw_data_offset(ep->hw_data_buf); - // Bits 0-5 should be 0 - assert(!(dpram_offset & 0b111111)); + pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), + ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type); + pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), + ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf); + uint dpram_offset = hw_data_offset(ep->hw_data_buf); + // Bits 0-5 should be 0 + assert(!(dpram_offset & 0b111111)); - // Fill in endpoint control register with buffer offset - uint32_t ep_reg = EP_CTRL_ENABLE_BITS - | EP_CTRL_INTERRUPT_PER_BUFFER - | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) - | dpram_offset; - if (bmInterval) + // Fill in endpoint control register with buffer offset + uint32_t ep_reg = EP_CTRL_ENABLE_BITS + | EP_CTRL_INTERRUPT_PER_BUFFER + | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) + | dpram_offset; + if ( bmInterval ) + { + ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + } + *ep->endpoint_control = ep_reg; + pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg); + ep->configured = true; + + if ( ep != &epx ) + { + // Endpoint has its own addr_endp and interrupt bits to be setup! + // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with: + // - device address + // - endpoint number / direction + // - preamble + uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB)); + + if ( dir == TUSB_DIR_OUT ) { - ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS; } - *ep->endpoint_control = ep_reg; - pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg); - ep->configured = true; - if (ep != &epx) + if ( need_pre(dev_addr) ) { - // Endpoint has its own addr_endp and interrupt bits to be setup! - // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with: - // - device address - // - endpoint number / direction - // - preamble - uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB)); - - if (dir == TUSB_DIR_OUT) - { - reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS; - } - - if (need_pre(dev_addr)) - { - reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; - } - usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg; - - // Finally, enable interrupt that endpoint - usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1); - - // If it's an interrupt endpoint we need to set up the buffer control - // register + reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; } + usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg; + + // Finally, enable interrupt that endpoint + usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1); + + // If it's an interrupt endpoint we need to set up the buffer control + // register + } } //--------------------------------------------------------------------+ @@ -434,16 +439,17 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) { (void) rhport; assert(rhport == 0); + // TODO: Should enumval this register - switch (dev_speed()) + switch ( dev_speed() ) { - case 1: - return TUSB_SPEED_LOW; - case 2: - return TUSB_SPEED_FULL; - default: - panic("Invalid speed\n"); - return TUSB_SPEED_INVALID; + case 1: + return TUSB_SPEED_LOW; + case 2: + return TUSB_SPEED_FULL; + default: + panic("Invalid speed\n"); + return TUSB_SPEED_INVALID; } } @@ -476,8 +482,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) uint32_t hcd_frame_number(uint8_t rhport) { - (void) rhport; - return usb_hw->sof_rd; + (void) rhport; + return usb_hw->sof_rd; } void hcd_int_enable(uint8_t rhport) @@ -501,117 +507,116 @@ void hcd_int_disable(uint8_t rhport) bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { - (void) rhport; + (void) rhport; - pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); + pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - // Allocated differently based on if it's an interrupt endpoint or not - struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer); - TU_ASSERT(ep); + // Allocated differently based on if it's an interrupt endpoint or not + struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer); + TU_ASSERT(ep); - _hw_endpoint_init(ep, - dev_addr, - ep_desc->bEndpointAddress, - tu_edpt_packet_size(ep_desc), - ep_desc->bmAttributes.xfer, - ep_desc->bInterval); + _hw_endpoint_init(ep, + dev_addr, + ep_desc->bEndpointAddress, + tu_edpt_packet_size(ep_desc), + ep_desc->bmAttributes.xfer, + ep_desc->bInterval); - return true; + return true; } bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { - (void) rhport; + (void) rhport; - pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); - - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); + pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); - // Get appropriate ep. Either EPX or interrupt endpoint - struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr); + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); - TU_ASSERT(ep); + // Get appropriate ep. Either EPX or interrupt endpoint + struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr); - // EP should be inactive - assert(!ep->active); + TU_ASSERT(ep); - // Control endpoint can change direction 0x00 <-> 0x80 - if ( ep_addr != ep->ep_addr ) - { - assert(ep_num == 0); + // EP should be inactive + assert(!ep->active); - // Direction has flipped on endpoint control so re init it but with same properties - _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0); - } + // Control endpoint can change direction 0x00 <-> 0x80 + if ( ep_addr != ep->ep_addr ) + { + assert(ep_num == 0); - // If a normal transfer (non-interrupt) then initiate using - // sie ctrl registers. Otherwise interrupt ep registers should - // already be configured - if (ep == &epx) { - hw_endpoint_xfer_start(ep, buffer, buflen); + // Direction has flipped on endpoint control so re init it but with same properties + _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0); + } - // That has set up buffer control, endpoint control etc - // for host we have to initiate the transfer - usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); + // If a normal transfer (non-interrupt) then initiate using + // sie ctrl registers. Otherwise interrupt ep registers should + // already be configured + if ( ep == &epx ) + { + hw_endpoint_xfer_start(ep, buffer, buflen); - uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | - (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); - // Set pre if we are a low speed device on full speed hub - flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0; + // That has set up buffer control, endpoint control etc + // for host we have to initiate the transfer + usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - usb_hw->sie_ctrl = flags; - }else - { - hw_endpoint_xfer_start(ep, buffer, buflen); - } + uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | + (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + usb_hw->sie_ctrl = flags; + }else + { + hw_endpoint_xfer_start(ep, buffer, buflen); + } - return true; + return true; } bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { - (void) rhport; + (void) rhport; - // Copy data into setup packet buffer - for(uint8_t i=0; i<8; i++) - { - usbh_dpram->setup_packet[i] = setup_packet[i]; - } + // Copy data into setup packet buffer + for ( uint8_t i = 0; i < 8; i++ ) + { + usbh_dpram->setup_packet[i] = setup_packet[i]; + } - // Configure EP0 struct with setup info for the trans complete - struct hw_endpoint *ep = _hw_endpoint_allocate(0); - TU_ASSERT(ep); + // Configure EP0 struct with setup info for the trans complete + struct hw_endpoint * ep = _hw_endpoint_allocate(0); + TU_ASSERT(ep); - // EPX should be inactive - assert(!ep->active); + // EPX should be inactive + assert(!ep->active); - // EP0 out - _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); - assert(ep->configured); + // EP0 out + _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); + assert(ep->configured); - ep->remaining_len = 8; - ep->active = true; + ep->remaining_len = 8; + ep->active = true; - // Set device address - usb_hw->dev_addr_ctrl = dev_addr; + // Set device address + usb_hw->dev_addr_ctrl = dev_addr; - // Set pre if we are a low speed device on full speed hub - uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS | - (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + // Set pre if we are a low speed device on full speed hub + uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS | + (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - usb_hw->sie_ctrl = flags; + usb_hw->sie_ctrl = flags; - return true; + return true; } bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) { - (void) dev_addr; - (void) ep_addr; + (void) dev_addr; + (void) ep_addr; - panic("hcd_clear_stall"); - return true; + panic("hcd_clear_stall"); + return true; } #endif diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index d7895175e..5c7645902 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -47,6 +47,12 @@ TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_lock_update(__unused struc static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep); static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep); +// if usb hardware is in host mode +TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) +{ + return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -69,6 +75,8 @@ void rp2040_usb_init(void) // Mux the controller to the onboard usb phy usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; + + TU_LOG2_INT(sizeof(hw_endpoint_t)); } void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep) @@ -80,19 +88,23 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep) } void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) { - uint32_t value = 0; - if (and_mask) { - value = *ep->buffer_control & and_mask; - } - if (or_mask) { - value |= or_mask; - if (or_mask & USB_BUF_CTRL_AVAIL) { - if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) { - panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - } - *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL; - // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz) - // Don't need delay in host mode as host is in charge + uint32_t value = 0; + if ( and_mask ) + { + value = *ep->buffer_control & and_mask; + } + if ( or_mask ) + { + value |= or_mask; + if ( or_mask & USB_BUF_CTRL_AVAIL ) + { + if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL ) + { + panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + } + *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL; + // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz) + // Don't need delay in host mode as host is in charge #if !CFG_TUH_ENABLED __asm volatile ( "b 1f\n" @@ -104,9 +116,9 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi "1:\n" : : : "memory"); #endif - } } - *ep->buffer_control = value; + } + *ep->buffer_control = value; } // prepare buffer, return buffer control @@ -152,12 +164,14 @@ static void __tusb_irq_path_func(_hw_endpoint_start_next_buffer)(struct hw_endpo // always compute and start with buffer 0 uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL; - // For now: skip double buffered for Device mode, OUT endpoint since + // For now: skip double buffered for OUT endpoint in Device mode, since // host could send < 64 bytes and cause short packet on buffer0 - // NOTE this could happen to Host mode IN endpoint - // Also, Host mode interrupt endpoint hardware is only single buffered - bool const force_single = (!(usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) && !tu_edpt_dir(ep->ep_addr)) || - ((usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) && tu_edpt_number(ep->ep_addr) != 0); + // NOTE: this could happen to Host mode IN endpoint + // Also, Host mode "interrupt" endpoint hardware is only single buffered, + // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint + bool const is_host = is_host_mode(); + bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) || + (is_host && tu_edpt_number(ep->ep_addr) != 0); if(ep->remaining_len && !force_single) { diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index c72dae64c..b65d32fd4 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -82,26 +82,30 @@ void hw_endpoint_reset_transfer(struct hw_endpoint *ep); void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); -TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) { - return *ep->buffer_control; -} - -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) { - return _hw_endpoint_buffer_control_update32(ep, 0, value); -} - -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) { - return _hw_endpoint_buffer_control_update32(ep, ~value, value); -} - -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) { - return _hw_endpoint_buffer_control_update32(ep, ~value, 0); -} - -static inline uintptr_t hw_data_offset(uint8_t *buf) +TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep) { - // Remove usb base from buffer pointer - return (uintptr_t)buf ^ (uintptr_t)usb_dpram; + return *ep->buffer_control; +} + +TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value) +{ + return _hw_endpoint_buffer_control_update32(ep, 0, value); +} + +TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value) +{ + return _hw_endpoint_buffer_control_update32(ep, ~value, value); +} + +TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value) +{ + return _hw_endpoint_buffer_control_update32(ep, ~value, 0); +} + +static inline uintptr_t hw_data_offset (uint8_t *buf) +{ + // Remove usb base from buffer pointer + return (uintptr_t) buf ^ (uintptr_t) usb_dpram; } extern const char *ep_dir_string[]; diff --git a/src/portable/renesas/usba/hcd_usba.c b/src/portable/renesas/usba/hcd_usba.c index 5246ecb94..18cd5f148 100644 --- a/src/portable/renesas/usba/hcd_usba.c +++ b/src/portable/renesas/usba/hcd_usba.c @@ -600,7 +600,7 @@ uint32_t hcd_frame_number(uint8_t rhport) bool hcd_port_connect_status(uint8_t rhport) { (void)rhport; - return USB0.INTSTS1.BIT.ATTCH ? true: false; + return USB0.INTSTS1.BIT.ATTCH ? true : false; } void hcd_port_reset(uint8_t rhport) diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index c88a8a3a2..54c3c95e7 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -122,7 +122,7 @@ (TU_CHECK_MCU(OPT_MCU_STM32L4) && defined(STM32L4_FSDEV)) \ ) -// In order to reduce the dependance on HAL, we undefine this. +// In order to reduce the dependence on HAL, we undefine this. // Some definitions are copied to our private include file. #undef USE_HAL_DRIVER diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index a0be846a5..2d0e379ad 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -243,7 +243,7 @@ static void USBC_Dev_SetAddress(u8 address) static void __USBC_Dev_Tx_SendStall(void) { - //send stall, and fifo is flushed automaticly + //send stall, and fifo is flushed automatically USBC_REG_set_bit_w(USBC_BP_TXCSR_D_SEND_STALL, USBC_REG_TXCSR(USBC0_BASE)); } static u32 __USBC_Dev_Tx_IsEpStall(void) diff --git a/src/portable/sunxi/musb_def.h b/src/portable/sunxi/musb_def.h index 602b4f113..53da5ded2 100644 --- a/src/portable/sunxi/musb_def.h +++ b/src/portable/sunxi/musb_def.h @@ -93,7 +93,7 @@ #define USBC1_BASE 0x01c14000 #define USBC2_BASE 0x01c1E000 -//Some reg whithin musb +//Some reg within musb #define USBPHY_CLK_REG 0x01c200CC #define USBPHY_CLK_RST_BIT 0 #define USBPHY_CLK_GAT_BIT 1 diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index f5ecdfe4f..c6132a1f5 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -529,8 +529,10 @@ void dcd_init (uint8_t rhport) dwc2->dcfg |= DCFG_NZLSOHSK; // Clear all interrupts - dwc2->gintsts |= dwc2->gintsts; - dwc2->gotgint |= dwc2->gotgint; + uint32_t int_mask = dwc2->gintsts; + dwc2->gintsts |= int_mask; + int_mask = dwc2->gotgint; + dwc2->gotgint |= int_mask; // Required as part of core initialization. // TODO: How should mode mismatch be handled? It will cause @@ -1003,7 +1005,7 @@ static void handle_rxflvl_irq(uint8_t rhport) switch ( pktsts ) { - // Global OUT NAK: do nothign + // Global OUT NAK: do nothing case GRXSTS_PKTSTS_GLOBALOUTNAK: break; case GRXSTS_PKTSTS_SETUPRX: @@ -1219,7 +1221,8 @@ void dcd_int_handler(uint8_t rhport) { dwc2_regs_t *dwc2 = DWC2_REG(rhport); - uint32_t const int_status = dwc2->gintsts & dwc2->gintmsk; + uint32_t const int_mask = dwc2->gintmsk; + uint32_t const int_status = dwc2->gintsts & int_mask; if(int_status & GINTSTS_USBRST) { diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index ea786362e..cb455bd90 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -116,19 +116,19 @@ static const dwc2_controller_t _dwc2_controller[] = // //--------------------------------------------------------------------+ -// SystemCoreClock is alrady included by family header +// SystemCoreClock is already included by family header // extern uint32_t SystemCoreClock; TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { - NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum); + NVIC_EnableIRQ((IRQn_Type)_dwc2_controller[rhport].irqnum); } TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable (uint8_t rhport) { - NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum); + NVIC_DisableIRQ((IRQn_Type)_dwc2_controller[rhport].irqnum); } TU_ATTR_ALWAYS_INLINE diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index e368b5f39..a3f228dd9 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -465,7 +465,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) enable = 1; usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); } - // IN endpoints will get unstalled when more data is written. + // IN endpoints will get un-stalled when more data is written. } bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) diff --git a/src/portable/wch/ch32v307/ch32_usbhs_reg.h b/src/portable/wch/ch32v307/ch32_usbhs_reg.h new file mode 100644 index 000000000..5a2c1fbc9 --- /dev/null +++ b/src/portable/wch/ch32v307/ch32_usbhs_reg.h @@ -0,0 +1,345 @@ +#ifndef _USB_CH32_USBHS_REG_H +#define _USB_CH32_USBHS_REG_H + +#include + +/******************* GLOBAL ******************/ + +// USB CONTROL +#define USBHS_CONTROL_OFFSET 0x00 +#define USBHS_DMA_EN (1 << 0) +#define USBHS_ALL_CLR (1 << 1) +#define USBHS_FORCE_RST (1 << 2) +#define USBHS_INT_BUSY_EN (1 << 3) +#define USBHS_DEV_PU_EN (1 << 4) +#define USBHS_SPEED_MASK (3 << 5) +#define USBHS_FULL_SPEED (0 << 5) +#define USBHS_HIGH_SPEED (1 << 5) +#define USBHS_LOW_SPEED (2 << 5) +#define USBHS_HOST_MODE (1 << 7) + +// USB_INT_EN +#define USBHS_INT_EN_OFFSET 0x02 +#define USBHS_BUS_RST_EN (1 << 0) +#define USBHS_DETECT_EN (1 << 0) +#define USBHS_TRANSFER_EN (1 << 1) +#define USBHS_SUSPEND_EN (1 << 2) +#define USBHS_SOF_ACT_EN (1 << 3) +#define USBHS_FIFO_OV_EN (1 << 4) +#define USBHS_SETUP_ACT_EN (1 << 5) +#define USBHS_ISO_ACT_EN (1 << 6) +#define USBHS_DEV_NAK_EN (1 << 7) + +// USB DEV AD +#define USBHS_DEV_AD_OFFSET 0x03 +// USB FRAME_NO +#define USBHS_FRAME_NO_OFFSET 0x04 +// USB SUSPEND +#define USBHS_SUSPEND_OFFSET 0x06 +#define USBHS_DEV_REMOTE_WAKEUP (1 << 2) +#define USBHS_LINESTATE_MASK (2 << 4) /* Read Only */ + +// RESERVED0 + +// USB SPEED TYPE +#define USBHS_SPEED_TYPE_OFFSET 0x08 +#define USBSPEED_MASK (0x03) + +// USB_MIS_ST +#define USBHS_MIS_ST_OFFSET 0x09 +#define USBHS_SPLIT_CAN (1 << 0) +#define USBHS_ATTACH (1 << 1) +#define USBHS_SUSPEND (1 << 2) +#define USBHS_BUS_RESET (1 << 3) +#define USBHS_R_FIFO_RDY (1 << 4) +#define USBHS_SIE_FREE (1 << 5) +#define USBHS_SOF_ACT (1 << 6) +#define USBHS_SOF_PRES (1 << 7) + +// INT_FLAG +#define USBHS_INT_FLAG_OFFSET 0x0A +#define USBHS_BUS_RST_FLAG (1 << 0) +#define USBHS_DETECT_FLAG (1 << 0) +#define USBHS_TRANSFER_FLAG (1 << 1) +#define USBHS_SUSPEND_FLAG (1 << 2) +#define USBHS_HST_SOF_FLAG (1 << 3) +#define USBHS_FIFO_OV_FLAG (1 << 4) +#define USBHS_SETUP_FLAG (1 << 5) +#define USBHS_ISO_ACT_FLAG (1 << 6) + +// INT_ST +#define USBHS_INT_ST_OFFSET 0x0B +#define USBHS_DEV_UIS_IS_NAK (1 << 7) +#define USBHS_DEV_UIS_TOG_OK (1 << 6) +#define MASK_UIS_TOKEN (3 << 4) +#define MASK_UIS_ENDP (0x0F) +#define MASK_UIS_H_RES (0x0F) + +#define USBHS_TOGGLE_OK (0x40) +#define USBHS_HOST_RES (0x0f) + +//USB_RX_LEN +#define USBHS_RX_LEN_OFFSET 0x0C +/******************* DEVICE ******************/ + +//UEP_CONFIG +#define USBHS_UEP_CONFIG_OFFSET 0x10 +#define USBHS_EP0_T_EN (1 << 0) +#define USBHS_EP0_R_EN (1 << 16) + +#define USBHS_EP1_T_EN (1 << 1) +#define USBHS_EP1_R_EN (1 << 17) + +#define USBHS_EP2_T_EN (1 << 2) +#define USBHS_EP2_R_EN (1 << 18) + +#define USBHS_EP3_T_EN (1 << 3) +#define USBHS_EP3_R_EN (1 << 19) + +#define USBHS_EP4_T_EN (1 << 4) +#define USBHS_EP4_R_EN (1 << 20) + +#define USBHS_EP5_T_EN (1 << 5) +#define USBHS_EP5_R_EN (1 << 21) + +#define USBHS_EP6_T_EN (1 << 6) +#define USBHS_EP6_R_EN (1 << 22) + +#define USBHS_EP7_T_EN (1 << 7) +#define USBHS_EP7_R_EN (1 << 23) + +#define USBHS_EP8_T_EN (1 << 8) +#define USBHS_EP8_R_EN (1 << 24) + +#define USBHS_EP9_T_EN (1 << 9) +#define USBHS_EP9_R_EN (1 << 25) + +#define USBHS_EP10_T_EN (1 << 10) +#define USBHS_EP10_R_EN (1 << 26) + +#define USBHS_EP11_T_EN (1 << 11) +#define USBHS_EP11_R_EN (1 << 27) + +#define USBHS_EP12_T_EN (1 << 12) +#define USBHS_EP12_R_EN (1 << 28) + +#define USBHS_EP13_T_EN (1 << 13) +#define USBHS_EP13_R_EN (1 << 29) + +#define USBHS_EP14_T_EN (1 << 14) +#define USBHS_EP14_R_EN (1 << 30) + +#define USBHS_EP15_T_EN (1 << 15) +#define USBHS_EP15_R_EN (1 << 31) + +//UEP_TYPE +#define USBHS_UEP_TYPE_OFFSET 0x14 +#define USBHS_EP0_T_TYP (1 << 0) +#define USBHS_EP0_R_TYP (1 << 16) + +#define USBHS_EP1_T_TYP (1 << 1) +#define USBHS_EP1_R_TYP (1 << 17) + +#define USBHS_EP2_T_TYP (1 << 2) +#define USBHS_EP2_R_TYP (1 << 18) + +#define USBHS_EP3_T_TYP (1 << 3) +#define USBHS_EP3_R_TYP (1 << 19) + +#define USBHS_EP4_T_TYP (1 << 4) +#define USBHS_EP4_R_TYP (1 << 20) + +#define USBHS_EP5_T_TYP (1 << 5) +#define USBHS_EP5_R_TYP (1 << 21) + +#define USBHS_EP6_T_TYP (1 << 6) +#define USBHS_EP6_R_TYP (1 << 22) + +#define USBHS_EP7_T_TYP (1 << 7) +#define USBHS_EP7_R_TYP (1 << 23) + +#define USBHS_EP8_T_TYP (1 << 8) +#define USBHS_EP8_R_TYP (1 << 24) + +#define USBHS_EP9_T_TYP (1 << 8) +#define USBHS_EP9_R_TYP (1 << 25) + +#define USBHS_EP10_T_TYP (1 << 10) +#define USBHS_EP10_R_TYP (1 << 26) + +#define USBHS_EP11_T_TYP (1 << 11) +#define USBHS_EP11_R_TYP (1 << 27) + +#define USBHS_EP12_T_TYP (1 << 12) +#define USBHS_EP12_R_TYP (1 << 28) + +#define USBHS_EP13_T_TYP (1 << 13) +#define USBHS_EP13_R_TYP (1 << 29) + +#define USBHS_EP14_T_TYP (1 << 14) +#define USBHS_EP14_R_TYP (1 << 30) + +#define USBHS_EP15_T_TYP (1 << 15) +#define USBHS_EP15_R_TYP (1 << 31) + +/* BUF_MOD UEP1~15 */ +#define USBHS_BUF_MOD_OFFSET 0x18 +#define USBHS_EP0_BUF_MOD (1 << 0) +#define USBHS_EP0_ISO_BUF_MOD (1 << 16) + +#define USBHS_EP1_BUF_MOD (1 << 1) +#define USBHS_EP1_ISO_BUF_MOD (1 << 17) + +#define USBHS_EP2_BUF_MOD (1 << 2) +#define USBHS_EP2_ISO_BUF_MOD (1 << 18) + +#define USBHS_EP3_BUF_MOD (1 << 3) +#define USBHS_EP3_ISO_BUF_MOD (1 << 19) + +#define USBHS_EP4_BUF_MOD (1 << 4) +#define USBHS_EP4_ISO_BUF_MOD (1 << 20) + +#define USBHS_EP5_BUF_MOD (1 << 5) +#define USBHS_EP5_ISO_BUF_MOD (1 << 21) + +#define USBHS_EP6_BUF_MOD (1 << 6) +#define USBHS_EP6_ISO_BUF_MOD (1 << 22) + +#define USBHS_EP7_BUF_MOD (1 << 7) +#define USBHS_EP7_ISO_BUF_MOD (1 << 23) + +#define USBHS_EP8_BUF_MOD (1 << 8) +#define USBHS_EP8_ISO_BUF_MOD (1 << 24) + +#define USBHS_EP9_BUF_MOD (1 << 9) +#define USBHS_EP9_ISO_BUF_MOD (1 << 25) + +#define USBHS_EP10_BUF_MOD (1 << 10) +#define USBHS_EP10_ISO_BUF_MOD (1 << 26) + +#define USBHS_EP11_BUF_MOD (1 << 11) +#define USBHS_EP11_ISO_BUF_MOD (1 << 27) + +#define USBHS_EP12_BUF_MOD (1 << 12) +#define USBHS_EP12_ISO_BUF_MOD (1 << 28) + +#define USBHS_EP13_BUF_MOD (1 << 13) +#define USBHS_EP13_ISO_BUF_MOD (1 << 29) + +#define USBHS_EP14_BUF_MOD (1 << 14) +#define USBHS_EP14_ISO_BUF_MOD (1 << 30) + +#define USBHS_EP15_BUF_MOD (1 << 15) +#define USBHS_EP15_ISO_BUF_MOD (1 << 31) +//USBHS_EPn_T_EN USBHS_EPn_R_EN USBHS_EPn_BUF_MOD Description: Arrange from low to high with UEPn_DMA as the starting address +// 0 0 x The endpoint is disabled and the UEPn_*_DMA buffers are not used. +// 1 0 0 The first address of the receive (OUT) buffer is UEPn_RX_DMA +// 1 0 1 RB_UEPn_RX_TOG[0]=0, use buffer UEPn_RX_DMA RB_UEPn_RX_TOG[0]=1, use buffer UEPn_TX_DMA +// 0 1 0 The first address of the transmit (IN) buffer is UEPn_TX_DMA. +// 0 1 1 RB_UEPn_TX_TOG[0]=0, use buffer UEPn_TX_DMA RB_UEPn_TX_TOG[0]=1, use buffer UEPn_RX_DMA + +/* USB0_DMA */ +#define USBHS_UEP0_DMA_OFFSET(n) (0x1C) // endpoint 0 DMA buffer address + +/* USBX_RX_DMA */ +#define USBHS_UEPx_RX_DMA_OFFSET(n) (0x1C + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_TX_DMA_OFFSET(n) (0x58 + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_MAX_LEN_OFFSET(n) (0x98 + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_T_LEN_OFFSET(n) (0xD8 + 4 * (n)) // endpoint x DMA buffer address +#define USBHS_UEPx_TX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 2) // endpoint x DMA buffer address +#define USBHS_UEPx_RX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 3) // endpoint x DMA buffer address + +// UEPn_T_LEN +#define USBHS_EP_T_LEN_MASK (0x7FF) + +//UEPn_TX_CTRL +#define USBHS_EP_T_RES_MASK (3 << 0) +#define USBHS_EP_T_RES_ACK (0 << 0) +#define USBHS_EP_T_RES_NYET (1 << 0) +#define USBHS_EP_T_RES_NAK (2 << 0) +#define USBHS_EP_T_RES_STALL (3 << 0) + +#define USBHS_EP_T_TOG_MASK (3 << 3) +#define USBHS_EP_T_TOG_0 (0 << 3) +#define USBHS_EP_T_TOG_1 (1 << 3) +#define USBHS_EP_T_TOG_2 (2 << 3) +#define USBHS_EP_T_TOG_M (3 << 3) + +#define USBHS_EP_T_AUTOTOG (1 << 5) + +//UEPn_RX_CTRL +#define USBHS_EP_R_RES_MASK (3 << 0) +#define USBHS_EP_R_RES_ACK (0 << 0) +#define USBHS_EP_R_RES_NYET (1 << 0) +#define USBHS_EP_R_RES_NAK (2 << 0) +#define USBHS_EP_R_RES_STALL (3 << 0) + +#define USBHS_EP_R_TOG_MASK (3 << 3) +#define USBHS_EP_R_TOG_0 (0 << 3) +#define USBHS_EP_R_TOG_1 (1 << 3) +#define USBHS_EP_R_TOG_2 (2 << 3) +#define USBHS_EP_R_TOG_M (3 << 3) + +#define USBHS_EP_R_AUTOTOG (1 << 5) + +#define USBHS_TOG_MATCH (1 << 6) + +/******************* HOST ******************/ +// USB HOST_CTRL +#define USBHS_SEND_BUS_RESET (1 << 0) +#define USBHS_SEND_BUS_SUSPEND (1 << 1) +#define USBHS_SEND_BUS_RESUME (1 << 2) +#define USBHS_REMOTE_WAKE (1 << 3) +#define USBHS_PHY_SUSPENDM (1 << 4) +#define USBHS_UH_SOFT_FREE (1 << 6) +#define USBHS_SEND_SOF_EN (1 << 7) + +//UH_CONFIG +#define USBHS_HOST_TX_EN (1 << 3) +#define USBHS_HOST_RX_EN (1 << 18) + +// HOST_EP_TYPE +#define USBHS_ENDP_TX_ISO (1 << 3) +#define USBHS_ENDP_RX_ISO (1 << (16 + 2)) + +// R32_UH_EP_PID +#define USBHS_HOST_MASK_TOKEN (0x0f) +#define USBHS_HOST_MASK_ENDP (0x0f << 4) + +//R8_UH_RX_CTRL +#define USBHS_EP_R_RES_MASK (3 << 0) +#define USBHS_EP_R_RES_ACK (0 << 0) +#define USBHS_EP_R_RES_NYET (1 << 0) +#define USBHS_EP_R_RES_NAK (2 << 0) +#define USBHS_EP_R_RES_STALL (3 << 0) + +#define USBHS_UH_R_RES_NO (1 << 2) +#define USBHS_UH_R_TOG_1 (1 << 3) +#define USBHS_UH_R_TOG_2 (2 << 3) +#define USBHS_UH_R_TOG_3 (3 << 3) +#define USBHS_UH_R_TOG_AUTO (1 << 5) +#define USBHS_UH_R_DATA_NO (1 << 6) +//R8_UH_TX_CTRL +#define USBHS_UH_T_RES_MASK (3 << 0) +#define USBHS_UH_T_RES_ACK (0 << 0) +#define USBHS_UH_T_RES_NYET (1 << 0) +#define USBHS_UH_T_RES_NAK (2 << 0) +#define USBHS_UH_T_RES_STALL (3 << 0) + +#define USBHS_UH_T_RES_NO (1 << 2) +#define USBHS_UH_T_TOG_1 (1 << 3) +#define USBHS_UH_T_TOG_2 (2 << 3) +#define USBHS_UH_T_TOG_3 (3 << 3) +#define USBHS_UH_T_TOG_AUTO (1 << 5) +#define USBHS_UH_T_DATA_NO (1 << 6) + +// 00: OUT, 01:SOF, 10:IN, 11:SETUP +#define PID_OUT 0 +#define PID_SOF 1 +#define PID_IN 2 +#define PID_SETUP 3 + +#endif diff --git a/src/portable/wch/ch32v307/dcd_usbhs.c b/src/portable/wch/ch32v307/dcd_usbhs.c new file mode 100644 index 000000000..b7a79e166 --- /dev/null +++ b/src/portable/wch/ch32v307/dcd_usbhs.c @@ -0,0 +1,391 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Greg Davill + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_CH32V307) +#include "device/dcd.h" + +#include "ch32_usbhs_reg.h" +#include "core_riscv.h" + +// Max number of bi-directional endpoints including EP0 +#define EP_MAX 16 + +typedef struct { + uint8_t *buffer; + // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API + uint16_t total_len; + uint16_t queued_len; + uint16_t max_size; + bool short_packet; +} xfer_ctl_t; + +#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] +static xfer_ctl_t xfer_status[EP_MAX][2]; + +#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep)*2) +#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep)*4) +#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep)*4) +#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep)*2) + +#define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1)) +#define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1)) + +/* Endpoint Buffer */ +TU_ATTR_ALIGNED(4) uint8_t EP0_DatabufHD[64]; // ep0(64) + +volatile uint8_t USBHS_Dev_Endp0_Tog = 0x01; + +void dcd_init(uint8_t rhport) { + (void)rhport; + + memset(&xfer_status, 0, sizeof(xfer_status)); + + USBHSD->HOST_CTRL = 0x00; + USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM; + + USBHSD->CONTROL = 0; + +#if TUD_OPT_HIGH_SPEED + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; +#else + #error OPT_MODE_FULL_SPEED not currently supported on CH32V307 + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; +#endif + + USBHSD->INT_EN = 0; + USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_DETECT_EN | USBHS_SUSPEND_EN; + + /* ALL endpoint enable */ + USBHSD->ENDP_CONFIG = 0xffffffff; + + USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; + USBHSD->ENDP_TYPE = 0x00; + USBHSD->BUF_MODE = 0x00; + + USBHSD->UEP0_MAX_LEN = 64; + + USBHSD->UEP0_DMA = (uint32_t)EP0_DatabufHD; + + USBHSD->UEP0_TX_LEN = 0; + USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_NAK; + USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK; + + for (int ep = 1; ep < EP_MAX; ep++) { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + + EP_RX_MAX_LEN(ep) = 512; + } + + USBHSD->DEV_AD = 0; + USBHSD->CONTROL |= USBHS_DEV_PU_EN; +} + +void dcd_int_enable(uint8_t rhport) { + (void)rhport; + + NVIC_EnableIRQ(USBHS_IRQn); +} + +void dcd_int_disable(uint8_t rhport) { + (void)rhport; + + NVIC_DisableIRQ(USBHS_IRQn); +} + +void dcd_edpt_close_all(uint8_t rhport) { + (void)rhport; +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void)dev_addr; + + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; +} + +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { + (void)rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + USBHSD->DEV_AD = (uint8_t)request->wValue; + } + + EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) { + (void)rhport; + + uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); + + TU_ASSERT(epnum < EP_MAX); + + xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); + xfer->max_size = tu_edpt_packet_size(desc_edpt); + + if (epnum != 0) { + if (tu_edpt_dir(desc_edpt->bEndpointAddress) == TUSB_DIR_OUT) { + EP_RX_CTRL(epnum) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_ACK; + } else { + EP_TX_LEN(epnum) = 0; + EP_TX_CTRL(epnum) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + } + } + + return true; +} + +int usbd_ep_close(const uint8_t ep) { + (void)ep; + + return 0; +} +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (epnum == 0) { + if (dir == TUSB_DIR_OUT) { + USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_STALL; + } else { + USBHSD->UEP0_TX_LEN = 0; + USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_STALL; + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~USBHS_EP_R_RES_MASK) | USBHS_EP_R_RES_STALL; + + } else { + EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~USBHS_EP_T_RES_MASK) | USBHS_EP_T_RES_STALL; + } + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (epnum == 0) { + if (dir == TUSB_DIR_OUT) { + USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK; + } else { + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~(USBHS_EP_R_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_ACK; + + } else { + EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_NAK; + } + } +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { + (void)rhport; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); + xfer->buffer = buffer; + // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API + xfer->total_len = total_bytes; + xfer->queued_len = 0; + xfer->short_packet = false; + + // uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t short_packet_size = total_bytes % (xfer->max_size + 1); + + // Zero-size packet is special case. + if (short_packet_size == 0 || (total_bytes == 0)) { + xfer->short_packet = true; + } + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + if (!total_bytes) { + xfer->short_packet = true; + if (epnum == 0) { + USBHSD->UEP0_TX_LEN = 0; + USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0); + USBHS_Dev_Endp0_Tog ^= 1; + } else { + EP_TX_LEN(epnum) = 0; + EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK; + } + } else { + if (epnum == 0) { + xfer->queued_len += short_packet_size; + memcpy(&EP0_DatabufHD[0], buffer, short_packet_size); + + USBHSD->UEP0_TX_LEN = short_packet_size; + USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0); + USBHS_Dev_Endp0_Tog ^= 1; + } else { + xfer->queued_len += short_packet_size; + + EP_TX_DMA_ADDR(epnum) = (uint32_t)buffer; + USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << epnum); + EP_TX_LEN(epnum) = short_packet_size; + EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK; + } + } + } else { /* TUSB_DIR_OUT */ + if (epnum == 0) { + uint32_t read_count = USBHSD->RX_LEN; + read_count = TU_MIN(read_count, total_bytes); + + if ((total_bytes == 8)) { + read_count = 8; + memcpy(buffer, &EP0_DatabufHD[0], 8); + } else { + memcpy(buffer, &EP0_DatabufHD[0], read_count); + } + } else { + EP_RX_DMA_ADDR(epnum) = (uint32_t)xfer->buffer; + USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << epnum); + } + + // usbd_ep_read(ep_addr, buffer, total_bytes, &ret_bytes); + } + return true; +} + + +static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size) { + // xfer->queued_len = xfer->total_len - remaining; + + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t to_recv_size; + + if (remaining <= xfer->max_size) { + // Avoid buffer overflow. + to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; + } else { + // Room for full packet, choose recv_size based on what the microcontroller + // claims. + to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; + } + + if (to_recv_size) { + } + + xfer->queued_len += xfer_size; + + // Per USB spec, a short OUT packet (including length 0) is always + // indicative of the end of a transfer (at least for ctl, bulk, int). + xfer->short_packet = (xfer_size < xfer->max_size); +} + +void dcd_int_handler(uint8_t rhport) { + (void)rhport; + + uint32_t end_num, rx_token; + uint8_t intflag = 0; + + intflag = USBHSD->INT_FG; + + if (intflag & USBHS_TRANSFER_FLAG) { + + end_num = (USBHSD->INT_ST) & MASK_UIS_ENDP; + rx_token = (((USBHSD->INT_ST) & MASK_UIS_TOKEN) >> 4) & 0x03; + + uint8_t endp = end_num | (rx_token == PID_IN ? TUSB_DIR_IN_MASK : 0); + + xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp)); + + if (rx_token == PID_OUT) { + uint16_t rx_len = USBHSD->RX_LEN; + + receive_packet(xfer, rx_len); + + if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { + xfer->short_packet = false; + + dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } + + if (end_num == 0) { + USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; + } + + } else if (rx_token == PID_IN) { + if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { + xfer->short_packet = false; + xfer->total_len = 0; + dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true); + + EP_TX_CTRL(end_num) = (EP_TX_CTRL(end_num) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_NAK; + + if (end_num == 0) { + } + } else { + dcd_edpt_xfer(0, endp, xfer->buffer + xfer->queued_len, xfer->total_len - xfer->queued_len); + } + } + + USBHSD->INT_FG = USBHS_TRANSFER_FLAG; /* Clear flag */ + } else if (intflag & USBHS_SETUP_FLAG) { + USBHS_Dev_Endp0_Tog = 1; + dcd_event_setup_received(0, EP0_DatabufHD, true); + + USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */ + } else if (intflag & USBHS_DETECT_FLAG) { + USBHS_Dev_Endp0_Tog = 1; + + xfer_status[0][TUSB_DIR_OUT].max_size = 64; + xfer_status[0][TUSB_DIR_IN].max_size = 64; + + dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true); + + USBHSD->DEV_AD = 0; + USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; + + USBHSD->INT_FG = USBHS_DETECT_FLAG; /* Clear flag */ + } else if (intflag & USBHS_SUSPEND_FLAG) { + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPEND }; + dcd_event_handler(&event, true); + + USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */ + } +} + +#endif diff --git a/src/tusb.c b/src/tusb.c index c3787ff8f..a5c820b8d 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -31,11 +31,18 @@ #include "tusb.h" #include "common/tusb_private.h" -// TODO clean up #if CFG_TUD_ENABLED #include "device/usbd_pvt.h" #endif +#if CFG_TUH_ENABLED +#include "host/usbh_classdriver.h" +#endif + +//--------------------------------------------------------------------+ +// Public API +//--------------------------------------------------------------------+ + bool tusb_init(void) { #if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT) @@ -67,18 +74,16 @@ bool tusb_inited(void) } //--------------------------------------------------------------------+ -// Internal Helper for both Host and Device stack +// Endpoint Helper for both Host and Device stack //--------------------------------------------------------------------+ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { (void) mutex; -#if TUSB_OPT_MUTEX // pre-check to help reducing mutex lock TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0)); - osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); // can only claim the endpoint if it is not busy and not claimed yet. bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); @@ -87,9 +92,7 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) ep_state->claimed = 1; } -#if TUSB_OPT_MUTEX - osal_mutex_unlock(mutex); -#endif + (void) osal_mutex_unlock(mutex); return available; } @@ -98,9 +101,7 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { (void) mutex; -#if TUSB_OPT_MUTEX - osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif + (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); // can only release the endpoint if it is claimed and not busy bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); @@ -109,9 +110,7 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) ep_state->claimed = 0; } -#if TUSB_OPT_MUTEX - osal_mutex_unlock(mutex); -#endif + (void) osal_mutex_unlock(mutex); return ret; } @@ -204,9 +203,184 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, return len; } -/*------------------------------------------------------------------*/ -/* Debug - *------------------------------------------------------------------*/ +//--------------------------------------------------------------------+ +// Endpoint Stream Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable, + void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) +{ + osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutex); + (void) new_mutex; + (void) is_tx; + + s->is_host = is_host; + tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable); + tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); + + s->ep_buf = ep_buf; + s->ep_bufsize = ep_bufsize; + + return true; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_claim(tu_edpt_stream_t* s) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_claim(s->daddr, s->ep_addr); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_claim(s->rhport, s->ep_addr); + #endif + } + + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_xfer(tu_edpt_stream_t* s, uint16_t count) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count); + #endif + } + + return false; +} + +TU_ATTR_ALWAYS_INLINE static inline +bool stream_release(tu_edpt_stream_t* s) +{ + if (s->is_host) + { + #if CFG_TUH_ENABLED + return usbh_edpt_release(s->daddr, s->ep_addr); + #endif + }else + { + #if CFG_TUD_ENABLED + return usbd_edpt_release(s->rhport, s->ep_addr); + #endif + } + + return false; +} + +//--------------------------------------------------------------------+ +// Stream Write +//--------------------------------------------------------------------+ + +bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes) +{ + // ZLP condition: no pending data, last transferred bytes is multiple of packet size + TU_VERIFY( !tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize-1))) ); + + TU_VERIFY( stream_claim(s) ); + TU_ASSERT( stream_xfer(s, 0) ); + + return true; +} + +uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s) +{ + // skip if no data + TU_VERIFY( tu_fifo_count(&s->ff), 0 ); + + // Claim the endpoint + TU_VERIFY( stream_claim(s), 0 ); + + // Pull data from FIFO -> EP buf + uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize); + + if ( count ) + { + TU_ASSERT( stream_xfer(s, count), 0 ); + return count; + }else + { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize) +{ + TU_VERIFY(bufsize); // TODO support ZLP + + uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize); + + // flush if fifo has more than packet size or + // in rare case: fifo depth is configured too small (which never reach packet size) + if ( (tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize) ) + { + tu_edpt_stream_write_xfer(s); + } + + return ret; +} + +//--------------------------------------------------------------------+ +// Stream Read +//--------------------------------------------------------------------+ + +uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s) +{ + uint16_t available = tu_fifo_remaining(&s->ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= s->ep_packetsize); + + // claim endpoint + TU_VERIFY(stream_claim(s), 0); + + // get available again since fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&s->ff); + + if ( available >= s->ep_packetsize ) + { + // multiple of packet size limit by ep bufsize + uint16_t count = (uint16_t) (available & ~(s->ep_packetsize -1)); + count = tu_min16(count, s->ep_bufsize); + + TU_ASSERT( stream_xfer(s, count), 0 ); + + return count; + }else + { + // Release endpoint since we don't make any transfer + stream_release(s); + return 0; + } +} + +uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) +{ + uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize); + tu_edpt_stream_read_xfer(s); + return num_read; +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ + #if CFG_TUSB_DEBUG #include diff --git a/src/tusb_option.h b/src/tusb_option.h index f95ae6273..67377b7ec 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -160,6 +160,9 @@ typedef int make_iso_compilers_happy; // Allwinner #define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family +// WCH +#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307 + // Helper to check if configured MCU is one of listed // Apply _TU_CHECK_MCU with || as separator to list of input #define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m) @@ -256,6 +259,10 @@ typedef int make_iso_compilers_happy; // For backward compatible #define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED +// highspeed support indicator +#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED) + + //--------------------------------------------------------------------+ // TODO move later //--------------------------------------------------------------------+ @@ -299,9 +306,6 @@ typedef int make_iso_compilers_happy; #define CFG_TUSB_OS_INC_PATH #endif -// mutex is only needed for RTOS TODO also required with multiple core MCUs -#define TUSB_OPT_MUTEX (CFG_TUSB_OS != OPT_OS_NONE) - //-------------------------------------------------------------------- // Device Options (Default) //-------------------------------------------------------------------- diff --git a/test/fuzz/dcd_fuzz.cc b/test/fuzz/dcd_fuzz.cc new file mode 100644 index 000000000..7153e20f0 --- /dev/null +++ b/test/fuzz/dcd_fuzz.cc @@ -0,0 +1,208 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#include "device/dcd.h" +#include "fuzz/fuzz_private.h" +#include +#include +#include + +#define UNUSED(x) (void)(x) + +//--------------------------------------------------------------------+ +// State tracker +//--------------------------------------------------------------------+ +struct State { + bool interrupts_enabled; + bool sof_enabled; + uint8_t address; +}; + +static State state = {false, 0, 0}; + +//--------------------------------------------------------------------+ +// Controller API +// All no-ops as we are fuzzing. +//--------------------------------------------------------------------+ +extern "C" { +void dcd_init(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_int_handler(uint8_t rhport) { + assert(_fuzz_data_provider.has_value()); + + if (!state.interrupts_enabled) { + return; + } + + // Choose if we want to generate a signal based on the fuzzed data. + if (_fuzz_data_provider->ConsumeBool()) { + dcd_event_bus_signal( + rhport, + // Choose a random event based on the fuzz data. + (dcd_eventid_t)_fuzz_data_provider->ConsumeIntegralInRange( + DCD_EVENT_INVALID + 1, DCD_EVENT_COUNT - 1), + // Identify trigger as either an interrupt or a syncrhonous call + // depending on fuzz data. + _fuzz_data_provider->ConsumeBool()); + } + + if (_fuzz_data_provider->ConsumeBool()) { + constexpr size_t kSetupFrameLength = 8; + std::vector setup = + _fuzz_data_provider->ConsumeBytes(kSetupFrameLength); + // Fuzz consumer may return less than requested. If this is the case + // we want to make sure that at least that length is allocated and available + // to the signal handler. + if (setup.size() != kSetupFrameLength) { + setup.resize(kSetupFrameLength); + } + dcd_event_setup_received(rhport, setup.data(), + // Identify trigger as either an interrupt or a + // syncrhonous call depending on fuzz data. + _fuzz_data_provider->ConsumeBool()); + } +} + +void dcd_int_enable(uint8_t rhport) { + state.interrupts_enabled = true; + UNUSED(rhport); + return; +} + +void dcd_int_disable(uint8_t rhport) { + state.interrupts_enabled = false; + UNUSED(rhport); + return; +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + UNUSED(rhport); + state.address = dev_addr; + // Respond with status. + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + return; +} + +void dcd_remote_wakeup(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_connect(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_disconnect(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_sof_enable(uint8_t rhport, bool en) { + state.sof_enabled = en; + UNUSED(rhport); + return; +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { + UNUSED(rhport); + UNUSED(desc_ep); + return _fuzz_data_provider->ConsumeBool(); +} + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE +// therefore required for multiple configuration support. +void dcd_edpt_close_all(uint8_t rhport) { + UNUSED(rhport); + return; +} + +// Close an endpoint. +// Since it is weak, caller must TU_ASSERT this function's existence before +// calling it. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + UNUSED(rhport); + UNUSED(ep_addr); + return; +} + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to +// notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, + uint16_t total_bytes) { + UNUSED(rhport); + UNUSED(buffer); + UNUSED(total_bytes); + + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + std::vector temp = + _fuzz_data_provider->ConsumeBytes(total_bytes); + std::copy(temp.begin(), temp.end(), buffer); + } + // Ignore output data as it's not useful for fuzzing without a more + // complex fuzzed backend. But we need to make sure it's not + // optimised out. + volatile uint8_t *dont_optimise0 = buffer; + volatile uint16_t dont_optimise1 = total_bytes; + UNUSED(dont_optimise0); + UNUSED(dont_optimise1); + + + return _fuzz_data_provider->ConsumeBool(); +} + +/* TODO: implement a fuzzed version of this. +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, + uint16_t total_bytes) {} +*/ + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + + UNUSED(rhport); + UNUSED(ep_addr); + return; +} + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when +// receiving setup packet +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + + UNUSED(rhport); + UNUSED(ep_addr); + return; +} +} \ No newline at end of file diff --git a/test/fuzz/device/cdc/CMakeLists.txt b/test/fuzz/device/cdc/CMakeLists.txt new file mode 100644 index 000000000..fa6e83b7e --- /dev/null +++ b/test/fuzz/device/cdc/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/cdc/Makefile b/test/fuzz/device/cdc/Makefile new file mode 100644 index 000000000..ee51936b5 --- /dev/null +++ b/test/fuzz/device/cdc/Makefile @@ -0,0 +1,12 @@ +include ../../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +include ../../rules.mk diff --git a/test/fuzz/device/cdc/cdc_seed_corpus.zip b/test/fuzz/device/cdc/cdc_seed_corpus.zip new file mode 100644 index 000000000..81bc2240d Binary files /dev/null and b/test/fuzz/device/cdc/cdc_seed_corpus.zip differ diff --git a/test/fuzz/device/cdc/src/fuzz.cc b/test/fuzz/device/cdc/src/fuzz.cc new file mode 100644 index 000000000..d40483ca6 --- /dev/null +++ b/test/fuzz/device/cdc/src/fuzz.cc @@ -0,0 +1,174 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +extern "C" { + +#define FUZZ_ITERATIONS 500 + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +void cdc_task(FuzzedDataProvider *provider); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + cdc_task(&provider); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// USB CDC +//--------------------------------------------------------------------+ +enum CdcApiFuncs { + kCdcNConnected, + kCdcNGetLineState, + kCdcNGetLineCoding, + kCdcNSetWantedChar, + kCdcNAvailable, + kCdcNRead, + kCdcNReadChar, + kCdcNReadFlush, + kCdcNPeek, + kCdcNWrite, + kCdcNWriteChar, + kCdcNWriteStr, + kCdcNWriteFlush, + kCdcNWriteAvailable, + kCdcNWriteClear, + // We don't need to fuzz tud_cdc_* as they are just wrappers + // calling with n==0. + kMaxValue, +}; + +void cdc_task(FuzzedDataProvider *provider) { + + assert(provider != NULL); + const int kMaxBufferSize = 4096; + switch (provider->ConsumeEnum()) { + case kCdcNConnected: + // TODO: Fuzz interface number + (void)tud_cdc_n_connected(0); + break; + case kCdcNGetLineState: + // TODO: Fuzz interface number + (void)tud_cdc_n_get_line_state(0); + break; + case kCdcNGetLineCoding: { + cdc_line_coding_t coding; + // TODO: Fuzz interface number + (void)tud_cdc_n_get_line_coding(0, &coding); + } break; + case kCdcNSetWantedChar: + // TODO: Fuzz interface number + (void)tud_cdc_n_set_wanted_char(0, provider->ConsumeIntegral()); + break; + case kCdcNAvailable: + // TODO: Fuzz interface number + (void)tud_cdc_n_available(0); + break; + case kCdcNRead: { + std::vector buffer; + buffer.resize(provider->ConsumeIntegralInRange(0, kMaxBufferSize)); + // TODO: Fuzz interface number + (void)tud_cdc_n_read(0, buffer.data(), buffer.size()); + break; + } + case kCdcNReadChar: + // TODO: Fuzz interface number + tud_cdc_n_read_char(0); + break; + case kCdcNReadFlush: + // TODO: Fuzz interface number + tud_cdc_n_read_flush(0); + break; + case kCdcNPeek: { + uint8_t peak = 0; + tud_cdc_n_peek(0, &peak); + break; + } + case kCdcNWrite: { + std::vector buffer = provider->ConsumeBytes( + provider->ConsumeIntegralInRange(0, kMaxBufferSize)); + + // TODO: Fuzz interface number + (void)tud_cdc_n_write(0, buffer.data(), buffer.size()); + } break; + +case kCdcNWriteChar: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_char(0, provider->ConsumeIntegral()); + break; +case kCdcNWriteStr: { + std::string str = provider->ConsumeRandomLengthString(kMaxBufferSize); + // TODO: Fuzz interface number + (void)tud_cdc_n_write_str(0, str.c_str()); + break; +} +case kCdcNWriteFlush: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_flush(0); + break; +case kCdcNWriteAvailable: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_available(0); + break; +case kCdcNWriteClear: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_clear(0); + break; +case kMaxValue: + // Noop. + break; +} +} +} \ No newline at end of file diff --git a/test/fuzz/device/cdc/src/tusb_config.h b/test/fuzz/device/cdc/src/tusb_config.h new file mode 100644 index 000000000..10a8a825a --- /dev/null +++ b/test/fuzz/device/cdc/src/tusb_config.h @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/cdc/src/usb_descriptors.cc b/test/fuzz/device/cdc/src/usb_descriptors.cc new file mode 100644 index 000000000..0f636f05d --- /dev/null +++ b/test/fuzz/device/cdc/src/usb_descriptors.cc @@ -0,0 +1,229 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \ + _PID_MAP(VENDOR, 4)) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; + +#define EPNUM_CDC_NOTIF 0x81 +#define EPNUM_CDC_OUT 0x02 +#define EPNUM_CDC_IN 0x82 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/device/msc/CMakeLists.txt b/test/fuzz/device/msc/CMakeLists.txt new file mode 100644 index 000000000..fa6e83b7e --- /dev/null +++ b/test/fuzz/device/msc/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/msc/Makefile b/test/fuzz/device/msc/Makefile new file mode 100644 index 000000000..ee51936b5 --- /dev/null +++ b/test/fuzz/device/msc/Makefile @@ -0,0 +1,12 @@ +include ../../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +include ../../rules.mk diff --git a/test/fuzz/device/msc/msc_seed_corpus.zip b/test/fuzz/device/msc/msc_seed_corpus.zip new file mode 100644 index 000000000..81bc2240d Binary files /dev/null and b/test/fuzz/device/msc/msc_seed_corpus.zip differ diff --git a/test/fuzz/device/msc/src/fuzz.cc b/test/fuzz/device/msc/src/fuzz.cc new file mode 100644 index 000000000..568900452 --- /dev/null +++ b/test/fuzz/device/msc/src/fuzz.cc @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +#define FUZZ_ITERATIONS 500 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + } + + return 0; +} + diff --git a/test/fuzz/device/msc/src/tusb_config.h b/test/fuzz/device/msc/src/tusb_config.h new file mode 100644 index 000000000..ca39c6b0a --- /dev/null +++ b/test/fuzz/device/msc/src/tusb_config.h @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 1 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/msc/src/usb_descriptors.cc b/test/fuzz/device/msc/src/usb_descriptors.cc new file mode 100644 index 000000000..ded401fc9 --- /dev/null +++ b/test/fuzz/device/msc/src/usb_descriptors.cc @@ -0,0 +1,224 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(MSC, 0) | _PID_MAP(HID, 1) | _PID_MAP(MIDI, 2) | \ + _PID_MAP(VENDOR, 3)) +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_MSC = 0, ITF_NUM_TOTAL }; + +#define EPNUM_MSC_OUT 0x05 +#define EPNUM_MSC_IN 0x85 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB MSC", // 4: MSC Interface + +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/device/net/CMakeLists.txt b/test/fuzz/device/net/CMakeLists.txt new file mode 100644 index 000000000..fa6e83b7e --- /dev/null +++ b/test/fuzz/device/net/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/net/Makefile b/test/fuzz/device/net/Makefile new file mode 100644 index 000000000..22241fcdc --- /dev/null +++ b/test/fuzz/device/net/Makefile @@ -0,0 +1,71 @@ +DEPS_SUBMODULES += lib/lwip + +include ../../../../tools/top.mk +include ../../make.mk + +# suppress warning caused by lwip +CFLAGS += \ + -Wno-error=null-dereference \ + -Wno-error=unused-parameter \ + -Wno-error=unused-variable + +INC += \ + src \ + $(TOP)/hw \ + $(TOP)/lib/lwip/src/include \ + $(TOP)/lib/lwip/src/include/ipv4 \ + $(TOP)/lib/lwip/src/include/lwip/apps \ + $(TOP)/lib/networking + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +# lwip sources +SRC_C += \ + lib/lwip/src/core/altcp.c \ + lib/lwip/src/core/altcp_alloc.c \ + lib/lwip/src/core/altcp_tcp.c \ + lib/lwip/src/core/def.c \ + lib/lwip/src/core/dns.c \ + lib/lwip/src/core/inet_chksum.c \ + lib/lwip/src/core/init.c \ + lib/lwip/src/core/ip.c \ + lib/lwip/src/core/mem.c \ + lib/lwip/src/core/memp.c \ + lib/lwip/src/core/netif.c \ + lib/lwip/src/core/pbuf.c \ + lib/lwip/src/core/raw.c \ + lib/lwip/src/core/stats.c \ + lib/lwip/src/core/sys.c \ + lib/lwip/src/core/tcp.c \ + lib/lwip/src/core/tcp_in.c \ + lib/lwip/src/core/tcp_out.c \ + lib/lwip/src/core/timeouts.c \ + lib/lwip/src/core/udp.c \ + lib/lwip/src/core/ipv4/autoip.c \ + lib/lwip/src/core/ipv4/dhcp.c \ + lib/lwip/src/core/ipv4/etharp.c \ + lib/lwip/src/core/ipv4/icmp.c \ + lib/lwip/src/core/ipv4/igmp.c \ + lib/lwip/src/core/ipv4/ip4.c \ + lib/lwip/src/core/ipv4/ip4_addr.c \ + lib/lwip/src/core/ipv4/ip4_frag.c \ + lib/lwip/src/core/ipv6/dhcp6.c \ + lib/lwip/src/core/ipv6/ethip6.c \ + lib/lwip/src/core/ipv6/icmp6.c \ + lib/lwip/src/core/ipv6/inet6.c \ + lib/lwip/src/core/ipv6/ip6.c \ + lib/lwip/src/core/ipv6/ip6_addr.c \ + lib/lwip/src/core/ipv6/ip6_frag.c \ + lib/lwip/src/core/ipv6/mld6.c \ + lib/lwip/src/core/ipv6/nd6.c \ + lib/lwip/src/netif/ethernet.c \ + lib/lwip/src/netif/slipif.c \ + lib/lwip/src/apps/http/httpd.c \ + lib/lwip/src/apps/http/fs.c \ + lib/networking/dhserver.c \ + lib/networking/dnserver.c \ + lib/networking/rndis_reports.c + +include ../../rules.mk diff --git a/test/fuzz/device/net/src/arch/cc.h b/test/fuzz/device/net/src/arch/cc.h new file mode 100644 index 000000000..56a0cacf7 --- /dev/null +++ b/test/fuzz/device/net/src/arch/cc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __CC_H__ +#define __CC_H__ + +//#include "cpu.h" + +typedef int sys_prot_t; + + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/test/fuzz/device/net/src/fuzz.cc b/test/fuzz/device/net/src/fuzz.cc new file mode 100644 index 000000000..ea4943665 --- /dev/null +++ b/test/fuzz/device/net/src/fuzz.cc @@ -0,0 +1,99 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "class/net/net_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +extern "C" { + +#define FUZZ_ITERATIONS 500 + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +void net_task(FuzzedDataProvider *provider); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + net_task(&provider); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// USB CDC +//--------------------------------------------------------------------+ +enum NetApiFuncs { + kNetworkRecvRenew, + kNetworkCanXmit, + kNetworkXmit, + kMaxValue, +}; + +void net_task(FuzzedDataProvider *provider) { + + assert(provider != NULL); + switch (provider->ConsumeEnum()) { + + case kNetworkRecvRenew: + tud_network_recv_renew(); + break; + case kNetworkCanXmit: + (void)tud_network_can_xmit(provider->ConsumeIntegral()); + case kNetworkXmit: + // TODO: Actually pass real values here later. + tud_network_xmit(NULL, 0); + + case kMaxValue: + // Noop. + break; + } +} +} diff --git a/test/fuzz/device/net/src/lwipopts.h b/test/fuzz/device/net/src/lwipopts.h new file mode 100644 index 000000000..a215017c7 --- /dev/null +++ b/test/fuzz/device/net/src/lwipopts.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 +#define LWIP_RAW 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_DHCP 0 +#define LWIP_ICMP 1 +#define LWIP_UDP 1 +#define LWIP_TCP 1 +#define LWIP_IPV4 1 +#define LWIP_IPV6 0 +#define ETH_PAD_SIZE 0 +#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) + +#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) +#define TCP_SND_BUF (2 * TCP_MSS) +#define TCP_WND (TCP_MSS) + +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#define LWIP_HTTPD_CGI 0 +#define LWIP_HTTPD_SSI 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +#define LWIP_SINGLE_NETIF 1 + +#define PBUF_POOL_SIZE 2 + +#define HTTPD_USE_CUSTOM_FSDATA 0 + +#define LWIP_MULTICAST_PING 1 +#define LWIP_BROADCAST_PING 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 0 + +#endif /* __LWIPOPTS_H__ */ diff --git a/test/fuzz/device/net/src/tusb_config.h b/test/fuzz/device/net/src/tusb_config.h new file mode 100644 index 000000000..6ad859337 --- /dev/null +++ b/test/fuzz/device/net/src/tusb_config.h @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// Network class has 2 drivers: ECM/RNDIS and NCM. +// Only one of the drivers can be enabled +#define CFG_TUD_ECM_RNDIS 1 +#define CFG_TUD_NCM (1-CFG_TUD_ECM_RNDIS) + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + + + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/net/src/usb_descriptors.cc b/test/fuzz/device/net/src/usb_descriptors.cc new file mode 100644 index 000000000..c600bd801 --- /dev/null +++ b/test/fuzz/device/net/src/usb_descriptors.cc @@ -0,0 +1,229 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \ + _PID_MAP(VENDOR, 4)) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; + +#define EPNUM_CDC_NOTIF 0x81 +#define EPNUM_CDC_OUT 0x02 +#define EPNUM_CDC_IN 0x82 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/dicts/cdc.dict b/test/fuzz/dicts/cdc.dict new file mode 100644 index 000000000..138775cdc --- /dev/null +++ b/test/fuzz/dicts/cdc.dict @@ -0,0 +1,74 @@ +# List of supported OIDs +RNDIS_OID_GEN_SUPPORTED_LIST="\x00\x01\x01\x01" +# Hardware status +RNDIS_OID_GEN_HARDWARE_STATUS="\x00\x01\x01\x02" +# Media types supported (encoded) +RNDIS_OID_GEN_MEDIA_SUPPORTED="\x00\x01\x01\x03" +# Media types in use (encoded) +RNDIS_OID_GEN_MEDIA_IN_USE="\x00\x01\x01\x04" +RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD="\x00\x01\x01\x05" +# Maximum frame size in bytes +RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE="\x00\x01\x01\x06" +# Link speed in units of 100 bps +RNDIS_OID_GEN_LINK_SPEED="\x00\x01\x01\x07" +# Transmit buffer space +RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE="\x00\x01\x01\x08" +# Receive buffer space +RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE="\x00\x01\x01\x09" +# NDIS version number used by the driver +RNDIS_OID_GEN_DRIVER_VERSION="\x00\x01\x01\x10" +# Maximum total packet length in bytes +RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE="\x00\x01\x01\x11" +# Optional protocol flags (encoded) +RNDIS_OID_GEN_PROTOCOL_OPTIONS="\x00\x01\x01\x12" +# Optional NIC flags (encoded) +RNDIS_OID_GEN_MAC_OPTIONS="\x00\x01\x01\x13" +# Whether the NIC is connected to the network +RNDIS_OID_GEN_MEDIA_CONNECT_STATUS="\x00\x01\x01\x14" +# The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction +RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS="\x00\x01\x01\x15" +# Vendor-assigned version number of the driver +RNDIS_OID_GEN_VENDOR_DRIVER_VERSION="\x00\x01\x01\x16" +# The custom GUIDs (Globally Unique Identifier) supported by the miniport driver +RNDIS_OID_GEN_SUPPORTED_GUIDS="\x00\x01\x01\x17" +# List of network-layer addresses associated with the binding between a transport and the driver +RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES="\x00\x01\x01\x18" +# Size of packets' additional headers +RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET="\x00\x01\x01\x19" +RNDIS_OID_GEN_MEDIA_CAPABILITIES="\x00\x01\x02\x01" +# Physical media supported by the miniport driver (encoded) +RNDIS_OID_GEN_PHYSICAL_MEDIUM="\x00\x01\x02\x02" +# Permanent station address +RNDIS_OID_802_3_PERMANENT_ADDRESS="\x01\x01\x01\x01" +# Current station address +RNDIS_OID_802_3_CURRENT_ADDRESS="\x01\x01\x01\x02" +# Current multicast address list +RNDIS_OID_802_3_MULTICAST_LIST="\x01\x01\x01\x03" +# Maximum size of multicast address list +RNDIS_OID_802_3_MAXIMUM_LIST_SIZE="\x01\x01\x01\x04" +# Directed packets. Directed packets contain a destination address equal to the station address of the NIC. +RNDIS_PACKET_TYPE_DIRECTED="\x00\x00\x00\x01" +# Multicast address packets sent to addresses in the multicast address list. +RNDIS_PACKET_TYPE_MULTICAST="\x00\x00\x00\x02" +# All multicast address packets, not just the ones enumerated in the multicast address list. +RNDIS_PACKET_TYPE_ALL_MULTICAST="\x00\x00\x00\x04" +# Broadcast packets. +RNDIS_PACKET_TYPE_BROADCAST="\x00\x00\x00\x08" +# All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge. +RNDIS_PACKET_TYPE_SOURCE_ROUTING="\x00\x00\x00\x10" +# Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not. +RNDIS_PACKET_TYPE_PROMISCUOUS="\x00\x00\x00\x20" +# SMT packets that an FDDI NIC receives. +RNDIS_PACKET_TYPE_SMT="\x00\x00\x00\x40" +# All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle. +RNDIS_PACKET_TYPE_ALL_LOCAL="\x00\x00\x00\x80" +# Packets sent to the current group address. +RNDIS_PACKET_TYPE_GROUP="\x00\x00\x10\x00" +# All functional address packets, not just the ones in the current functional address. +RNDIS_PACKET_TYPE_ALL_FUNCTIONAL="\x00\x00\x20\x00" +# Functional address packets sent to addresses included in the current functional address. +RNDIS_PACKET_TYPE_FUNCTIONAL="\x00\x00\x40\x00" +# NIC driver frames that a Token Ring NIC receives. +RNDIS_PACKET_TYPE_MAC_FRAME="\x00\x00\x80\x00" +RNDIS_PACKET_TYPE_NO_LOCAL="\x00\x01\x00\x00" + diff --git a/test/fuzz/fuzz.cc b/test/fuzz/fuzz.cc new file mode 100644 index 000000000..2e4186f1a --- /dev/null +++ b/test/fuzz/fuzz.cc @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "fuzzer/FuzzedDataProvider.h" +#include + +std::optional _fuzz_data_provider; + +extern "C" int fuzz_init(const uint8_t *data, size_t size) { + _fuzz_data_provider.emplace(data, size); + return 0; +} diff --git a/test/fuzz/fuzz.h b/test/fuzz/fuzz.h new file mode 100644 index 000000000..6aa4949a1 --- /dev/null +++ b/test/fuzz/fuzz.h @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int fuzz_init(const uint8_t *data, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/test/fuzz/fuzz_private.h b/test/fuzz/fuzz_private.h new file mode 100644 index 000000000..0d791fcdc --- /dev/null +++ b/test/fuzz/fuzz_private.h @@ -0,0 +1,30 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once +#include "fuzzer/FuzzedDataProvider.h" +#include + +extern std::optional _fuzz_data_provider; diff --git a/test/fuzz/make.mk b/test/fuzz/make.mk new file mode 100644 index 000000000..6717ebc80 --- /dev/null +++ b/test/fuzz/make.mk @@ -0,0 +1,108 @@ +# --------------------------------------- +# Common make definition for all examples +# --------------------------------------- + +# Build directory +BUILD := _build +PROJECT := $(notdir $(CURDIR)) + +# Handy check parameter function +check_defined = \ + $(strip $(foreach 1,$1, \ + $(call __check_defined,$1,$(strip $(value 2))))) +__check_defined = \ + $(if $(value $1),, \ + $(error Undefined make flag: $1$(if $2, ($2)))) + +#-------------- Fuzz harness compiler ------------ + +CC ?= clang +CXX ?= clang++ +GDB ?= gdb +OBJCOPY = objcopy +SIZE = size +MKDIR = mkdir + +ifeq ($(CMDEXE),1) + CP = copy + RM = del + PYTHON = python +else + SED = sed + CP = cp + RM = rm + PYTHON = python3 +endif + +#-------------- Fuzz harness flags ------------ +COVERAGE_FLAGS ?= -fsanitize-coverage=trace-pc-guard +SANITIZER_FLAGS ?= -fsanitize=fuzzer \ + -fsanitize=address + +CFLAGS += $(COVERAGE_FLAGS) $(SANITIZER_FLAGS) + +#-------------- Source files and compiler flags -------------- + + +INC += $(TOP)/test + +# Compiler Flags +CFLAGS += \ + -ggdb \ + -fdata-sections \ + -ffunction-sections \ + -fno-strict-aliasing \ + -Wall \ + -Wextra \ + -Werror \ + -Wfatal-errors \ + -Wdouble-promotion \ + -Wstrict-prototypes \ + -Wstrict-overflow \ + -Werror-implicit-function-declaration \ + -Wfloat-equal \ + -Wundef \ + -Wshadow \ + -Wwrite-strings \ + -Wsign-compare \ + -Wmissing-format-attribute \ + -Wunreachable-code \ + -Wcast-align \ + -Wcast-qual \ + -Wnull-dereference \ + -Wuninitialized \ + -Wunused \ + -Wredundant-decls \ + -O1 + +CFLAGS += \ + -Wno-error=unreachable-code \ + -DOPT_MCU_FUZZ=1 \ + -DCFG_TUSB_MCU=OPT_MCU_FUZZ + +CXXFLAGS += \ + -xc++ \ + -Wno-c++11-narrowing \ + -fno-implicit-templates + +# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion +# -Wconversion + +# Debugging/Optimization +ifeq ($(DEBUG), 1) + CFLAGS += -Og +else + CFLAGS += $(CFLAGS_OPTIMIZED) +endif + +# Log level is mapped to TUSB DEBUG option +ifneq ($(LOG),) + CMAKE_DEFSYM += -DLOG=$(LOG) + CFLAGS += -DCFG_TUSB_DEBUG=$(LOG) +endif + +# Logger: default is uart, can be set to rtt or swo +ifneq ($(LOGGER),) + CMAKE_DEFSYM += -DLOGGER=$(LOGGER) +endif + diff --git a/test/fuzz/msc_fuzz.cc b/test/fuzz/msc_fuzz.cc new file mode 100644 index 000000000..e906ca971 --- /dev/null +++ b/test/fuzz/msc_fuzz.cc @@ -0,0 +1,162 @@ +#include "fuzz/fuzz_private.h" +#include "tusb.h" +#include +#include +#include + +#if CFG_TUD_MSC==1 + +// Whether host does safe eject. +// tud_msc_get_maxlun_cb returns a uint8_t so the max logical units that are +// allowed is 255, so we need to keep track of 255 fuzzed logical units. +static std::array::max()> ejected = {false}; + +extern "C" { +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, +// 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], + uint8_t product_id[16], uint8_t product_rev[4]) { + (void)lun; + assert(_fuzz_data_provider.has_value()); + + std::string vid = _fuzz_data_provider->ConsumeBytesAsString(8); + std::string pid = _fuzz_data_provider->ConsumeBytesAsString(16); + std::string rev = _fuzz_data_provider->ConsumeBytesAsString(4); + + memcpy(vendor_id, vid.c_str(), strlen(vid.c_str())); + memcpy(product_id, pid.c_str(), strlen(pid.c_str())); + memcpy(product_rev, rev.c_str(), strlen(rev.c_str())); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + // RAM disk is ready until ejected + if (ejected[lun]) { + // Additional Sense 3A-00 is NOT_FOUND + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + + return _fuzz_data_provider->ConsumeBool(); +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and +// SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size Application update +// block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, + uint16_t *block_size) { + (void)lun; + *block_count = _fuzz_data_provider->ConsumeIntegral(); + *block_size = _fuzz_data_provider->ConsumeIntegral(); +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, + bool load_eject) { + (void)power_condition; + assert(_fuzz_data_provider.has_value()); + + if (load_eject) { + if (start) { + // load disk storage + } else { + // unload disk storage + ejected[lun] = true; + } + } + + return _fuzz_data_provider->ConsumeBool(); +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, + void *buffer, uint32_t bufsize) { + assert(_fuzz_data_provider.has_value()); + (void)lun; + (void)lba; + (void)offset; + + std::vector consumed_buffer = _fuzz_data_provider->ConsumeBytes( + _fuzz_data_provider->ConsumeIntegralInRange(0, bufsize)); + memcpy(buffer, consumed_buffer.data(), consumed_buffer.size()); + + // Sometimes return an error code; + if (_fuzz_data_provider->ConsumeBool()) { + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), -1); + } + + return consumed_buffer.size(); +} + +bool tud_msc_is_writable_cb(uint8_t lun) { + assert(_fuzz_data_provider.has_value()); + (void)lun; + return _fuzz_data_provider->ConsumeBool(); +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, + uint8_t *buffer, uint32_t bufsize) { + // Ignore these as they are outputs and don't affect the return value. + (void)lun; + (void)lba; + (void)offset; + (void)buffer; + assert(_fuzz_data_provider.has_value()); + + // -ve error codes -> bufsize. + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), bufsize); +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, + uint16_t bufsize) { + (void)buffer; + (void)bufsize; + assert(_fuzz_data_provider.has_value()); + + switch (scsi_cmd[0]) { + case SCSI_CMD_TEST_UNIT_READY: + break; + case SCSI_CMD_INQUIRY: + break; + case SCSI_CMD_MODE_SELECT_6: + break; + case SCSI_CMD_MODE_SENSE_6: + break; + case SCSI_CMD_START_STOP_UNIT: + break; + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + break; + case SCSI_CMD_READ_CAPACITY_10: + break; + case SCSI_CMD_REQUEST_SENSE: + break; + case SCSI_CMD_READ_FORMAT_CAPACITY: + break; + case SCSI_CMD_READ_10: + break; + case SCSI_CMD_WRITE_10: + break; + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), -1); + } + + return 0; +} +} + +#endif \ No newline at end of file diff --git a/test/fuzz/net_fuzz.cc b/test/fuzz/net_fuzz.cc new file mode 100644 index 000000000..8467d6d8e --- /dev/null +++ b/test/fuzz/net_fuzz.cc @@ -0,0 +1,82 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb_config.h" +#if defined(CFG_TUD_ECM_RNDIS) || defined(CFG_TUD_NCM) + +#include "class/net/net_device.h" +#include "fuzz_private.h" +#include +#include +#include +#include "lwip/sys.h" + +extern "C" { +bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + assert(_fuzz_data_provider.has_value()); + (void)src; + (void)size; + return _fuzz_data_provider->ConsumeBool(); +} + +// client must provide this: copy from network stack packet pointer to dst +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + (void)ref; + (void)arg; + + assert(_fuzz_data_provider.has_value()); + + uint16_t size = _fuzz_data_provider->ConsumeIntegral(); + std::vector temp = _fuzz_data_provider->ConsumeBytes(size); + memcpy(dst, temp.data(), temp.size()); + return size; +} + +/* lwip has provision for using a mutex, when applicable */ +sys_prot_t sys_arch_protect(void) { return 0; } +void sys_arch_unprotect(sys_prot_t pval) { (void)pval; } + +//------------- ECM/RNDIS -------------// + +// client must provide this: initialize any network state back to the beginning +void tud_network_init_cb(void) { + // NoOp. +} + +// client must provide this: 48-bit MAC address +// TODO removed later since it is not part of tinyusb stack +const uint8_t tud_network_mac_address[6] = {0}; + +//------------- NCM -------------// + +// callback to client providing optional indication of internal state of network +// driver +void tud_network_link_state_cb(bool state) { + (void)state; + // NoOp. +} +} + +#endif \ No newline at end of file diff --git a/test/fuzz/rules.mk b/test/fuzz/rules.mk new file mode 100644 index 000000000..d9c2a1a77 --- /dev/null +++ b/test/fuzz/rules.mk @@ -0,0 +1,161 @@ +# --------------------------------------- +# Common make rules for all examples +# --------------------------------------- + +# Set all as default goal +.DEFAULT_GOAL := all + +# --------------------------------------- +# Compiler Flags +# --------------------------------------- + +LIBS_GCC ?= -lm + +# libc +LIBS += $(LIBS_GCC) + +ifneq ($(BOARD), spresense) +LIBS += -lc -Wl,-Bstatic -lc++ -Wl,-Bdynamic +endif + +# TinyUSB Stack source +SRC_C += \ + src/tusb.c \ + src/common/tusb_fifo.c \ + src/device/usbd.c \ + src/device/usbd_control.c \ + src/class/audio/audio_device.c \ + src/class/cdc/cdc_device.c \ + src/class/dfu/dfu_device.c \ + src/class/dfu/dfu_rt_device.c \ + src/class/hid/hid_device.c \ + src/class/midi/midi_device.c \ + src/class/msc/msc_device.c \ + src/class/net/ecm_rndis_device.c \ + src/class/net/ncm_device.c \ + src/class/usbtmc/usbtmc_device.c \ + src/class/video/video_device.c \ + src/class/vendor/vendor_device.c + + +# Fuzzers are c++ +SRC_CXX += \ + test/fuzz/dcd_fuzz.cc \ + test/fuzz/fuzz.cc \ + test/fuzz/msc_fuzz.cc \ + test/fuzz/net_fuzz.cc \ + test/fuzz/usbd_fuzz.cc + +# TinyUSB stack include +INC += $(TOP)/src + +CFLAGS += $(addprefix -I,$(INC)) +CXXFLAGS += -std=c++17 + +# LTO makes it difficult to analyze map file for optimizing size purpose +# We will run this option in ci +ifeq ($(NO_LTO),1) +CFLAGS := $(filter-out -flto,$(CFLAGS)) +endif + +ifneq ($(LD_FILE),) +LDFLAGS_LD_FILE ?= -Wl,-T,$(TOP)/$(LD_FILE) +endif + +LDFLAGS += $(CFLAGS) $(LDFLAGS_LD_FILE) -fuse-ld=lld -Wl,-Map=$@.map -Wl,--cref -Wl,-gc-sections +ifneq ($(SKIP_NANOLIB), 1) +endif + +ASFLAGS += $(CFLAGS) + +# Assembly files can be name with upper case .S, convert it to .s +SRC_S := $(SRC_S:.S=.s) + +# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966 +# assembly file should be placed first in linking order +# '_asm' suffix is added to object of assembly file +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o)) +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_CXX:.cc=_cxx.o)) + +# Verbose mode +ifeq ("$(V)","1") +$(info CFLAGS $(CFLAGS) ) $(info ) +$(info LDFLAGS $(LDFLAGS)) $(info ) +$(info ASFLAGS $(ASFLAGS)) $(info ) +endif + +# --------------------------------------- +# Rules +# --------------------------------------- + +all: $(BUILD)/$(PROJECT) + +OBJ_DIRS = $(sort $(dir $(OBJ))) +$(OBJ): | $(OBJ_DIRS) +$(OBJ_DIRS): +ifeq ($(CMDEXE),1) + @$(MKDIR) $(subst /,\,$@) +else + @$(MKDIR) -p $@ +endif + +$(BUILD)/$(PROJECT): $(OBJ) + @echo LINK $@ + @ $(CXX) -o $@ $(LIB_FUZZING_ENGINE) $^ $(LIBS) $(LDFLAGS) + +# We set vpath to point to the top of the tree so that the source files +# can be located. By following this scheme, it allows a single build rule +# to be used to compile all .c files. +vpath %.c . $(TOP) +$(BUILD)/obj/%.o: %.c + @echo CC $(notdir $@) + @$(CC) $(CFLAGS) -c -MD -o $@ $< + +# All cpp srcs +vpath %.cc . $(TOP) +$(BUILD)/obj/%_cxx.o: %.cc + @echo CXX $(notdir $@) + @$(CXX) $(CFLAGS) $(CXXFLAGS) -c -MD -o $@ $< + +# ASM sources lower case .s +vpath %.s . $(TOP) +$(BUILD)/obj/%_asm.o: %.s + @echo AS $(notdir $@) + @$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $< + +# ASM sources upper case .S +vpath %.S . $(TOP) +$(BUILD)/obj/%_asm.o: %.S + @echo AS $(notdir $@) + @$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $< + +.PHONY: clean +clean: +ifeq ($(CMDEXE),1) + rd /S /Q $(subst /,\,$(BUILD)) +else + $(RM) -rf $(BUILD) +endif +# ---------------- GNU Make End ----------------------- + +# get depenecies +.PHONY: get-deps +get-deps: + ifdef DEPS_SUBMODULES + git -C $(TOP) submodule update --init $(DEPS_SUBMODULES) + endif + +size: $(BUILD)/$(PROJECT) + -@echo '' + @$(SIZE) $< + -@echo '' + +# linkermap must be install previously at https://github.com/hathach/linkermap +linkermap: $(BUILD)/$(PROJECT) + @linkermap -v $<.map + +# Print out the value of a make variable. +# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile +print-%: + @echo $* = $($*) \ No newline at end of file diff --git a/test/fuzz/usbd_fuzz.cc b/test/fuzz/usbd_fuzz.cc new file mode 100644 index 000000000..45c5fd7ea --- /dev/null +++ b/test/fuzz/usbd_fuzz.cc @@ -0,0 +1,73 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "fuzz/fuzz_private.h" +#include "tusb.h" +// #include "usb_descriptors.h" + +#ifndef CFG_FUZZ_MAX_STRING_LEN +#define CFG_FUZZ_MAX_STRING_LEN 1000 +#endif + +extern "C" { + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_bos_cb(void) { } +*/ + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_device_qualifier_cb(void) {} +*/ + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {} +*/ + +void tud_mount_cb(void) { + // NOOP +} + +void tud_umount_cb(void) { + // NOOP +} + +void tud_suspend_cb(bool remote_wakeup_en) { + (void)remote_wakeup_en; + // NOOP +} + +void tud_resume_cb(void) { + // NOOP +} + +/* TODO: Implement a fuzzed version of this. +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, + tusb_control_request_t const *request) {} +*/ + +/* TODO: Implement a fuzzed version of this. +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {} +*/ +} diff --git a/test/ceedling b/test/unit-test/ceedling similarity index 100% rename from test/ceedling rename to test/unit-test/ceedling diff --git a/test/project.yml b/test/unit-test/project.yml similarity index 71% rename from test/project.yml rename to test/unit-test/project.yml index dc4a9cb28..562dbca09 100644 --- a/test/project.yml +++ b/test/unit-test/project.yml @@ -15,6 +15,7 @@ # :release_build: TRUE :test_file_prefix: test_ :which_ceedling: vendor/ceedling + :ceedling_version: 0.31.1 :default_tasks: - test:all @@ -35,7 +36,7 @@ - +:test/** - -:test/support :source: - - ../src/** + - ../../src/** :support: - test/support @@ -77,10 +78,24 @@ :html_high_threshold: 90 :xml_report: FALSE -#:tools: -# Ceedling defaults to using gcc for compiling, linking, etc. -# As [:tools] is blank, gcc will be used (so long as it's in your system path) -# See documentation to configure a given toolchain for use +:tools: + :test_compiler: + :executable: clang + :name: 'clang compiler' + :arguments: + - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE #expands to -I search paths + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR #expands to -I search paths + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR #expands to all -D defined symbols + - -fsanitize=address + - -c ${1} #source code input file (Ruby method call param list sub) + - -o ${2} #object file output (Ruby method call param list sub) + :test_linker: + :executable: clang + :name: 'clang linker' + :arguments: + - -fsanitize=address + - ${1} #list of object files to link (Ruby method call param list sub) + - -o ${2} #executable file output (Ruby method call param list sub) # LIBRARIES # These libraries are automatically injected into the build process. Those specified as diff --git a/test/test/device/msc/test_msc_device.c b/test/unit-test/test/device/msc/test_msc_device.c similarity index 99% rename from test/test/device/msc/test_msc_device.c rename to test/unit-test/test/device/msc/test_msc_device.c index 00bb86ccf..63684e76a 100644 --- a/test/test/device/msc/test_msc_device.c +++ b/test/unit-test/test/device/msc/test_msc_device.c @@ -28,6 +28,7 @@ #include "unity.h" // Files to test +#include "osal/osal.h" #include "tusb_fifo.h" #include "tusb.h" #include "usbd.h" diff --git a/test/test/device/usbd/test_usbd.c b/test/unit-test/test/device/usbd/test_usbd.c similarity index 99% rename from test/test/device/usbd/test_usbd.c rename to test/unit-test/test/device/usbd/test_usbd.c index c90383b57..ad95eb47a 100644 --- a/test/test/device/usbd/test_usbd.c +++ b/test/unit-test/test/device/usbd/test_usbd.c @@ -25,6 +25,7 @@ #include "unity.h" // Files to test +#include "osal/osal.h" #include "tusb_fifo.h" #include "tusb.h" #include "usbd.h" diff --git a/test/test/support/tusb_config.h b/test/unit-test/test/support/tusb_config.h similarity index 98% rename from test/test/support/tusb_config.h rename to test/unit-test/test/support/tusb_config.h index 0455f933b..00818fae5 100644 --- a/test/test/support/tusb_config.h +++ b/test/unit-test/test/support/tusb_config.h @@ -51,7 +51,7 @@ // CFG_TUSB_DEBUG is defined by compiler in DEBUG build #ifndef CFG_TUSB_DEBUG -#define CFG_TUSB_DEBUG 0 +#define CFG_TUSB_DEBUG 1 #endif /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. diff --git a/test/test/test_fifo.c b/test/unit-test/test/test_fifo.c similarity index 62% rename from test/test/test_fifo.c rename to test/unit-test/test/test_fifo.c index 0a5f4d3b9..28be6d8fc 100644 --- a/test/test/test_fifo.c +++ b/test/unit-test/test/test_fifo.c @@ -26,17 +26,27 @@ #include #include "unity.h" + +#include "osal/osal.h" #include "tusb_fifo.h" -#define FIFO_SIZE 10 -TU_FIFO_DEF(tu_ff, FIFO_SIZE, uint8_t, false); +#define FIFO_SIZE 64 +uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)]; +tu_fifo_t tu_ff = TU_FIFO_INIT(tu_ff_buf, FIFO_SIZE, uint8_t, false); + tu_fifo_t* ff = &tu_ff; tu_fifo_buffer_info_t info; +uint8_t test_data[4096]; +uint8_t rd_buf[FIFO_SIZE]; + void setUp(void) { tu_fifo_clear(ff); memset(&info, 0, sizeof(tu_fifo_buffer_info_t)); + + for(int i=0; i 4 - rd_count = tu_fifo_read_n(&ff4, rd, 5); + rd_count = tu_fifo_read_n(&ff4, rd_buf4, 5); TEST_ASSERT_EQUAL( 5, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data, rd, rd_count ); // 0 -> 4 + TEST_ASSERT_EQUAL_UINT32_ARRAY( data4, rd_buf4, rd_count ); // 0 -> 4 - tu_fifo_write_n(&ff4, data+10, 5); + tu_fifo_write_n(&ff4, data4+FIFO_SIZE, 5); - // read 5 -> 14 - rd_count = tu_fifo_read_n(&ff4, rd, 10); - TEST_ASSERT_EQUAL( 10, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data+5, rd, rd_count ); // 5 -> 14 + // read all 5 -> 68 + rd_count = tu_fifo_read_n(&ff4, rd_buf4, FIFO_SIZE); + TEST_ASSERT_EQUAL( FIFO_SIZE, rd_count ); + TEST_ASSERT_EQUAL_UINT32_ARRAY( data4+5, rd_buf4, rd_count ); // 5 -> 68 } void test_read_n(void) { - // prepare data - uint8_t data[20]; - for(int i=0; i 4 - rd_count = tu_fifo_read_n(ff, rd, 5); + rd_count = tu_fifo_read_n(ff, rd_buf, 5); TEST_ASSERT_EQUAL( 5, rd_count ); - TEST_ASSERT_EQUAL_MEMORY( data, rd, rd_count ); // 0 -> 4 + 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, data+10); - tu_fifo_write(ff, data+11); - tu_fifo_write(ff, data+12); + tu_fifo_write(ff, test_data+FIFO_SIZE); + tu_fifo_write(ff, test_data+FIFO_SIZE+1); + tu_fifo_write(ff, test_data+FIFO_SIZE+2); - rd_count = tu_fifo_read_n(ff, rd, 7); + rd_count = tu_fifo_read_n(ff, rd_buf, 7); TEST_ASSERT_EQUAL( 7, rd_count ); - TEST_ASSERT_EQUAL_MEMORY( data+5, rd, 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( 1, tu_fifo_read_n(ff, rd, 100) ); + TEST_ASSERT_EQUAL( FIFO_SIZE-5+3-7, tu_fifo_read_n(ff, rd_buf, 100) ); } void test_write_n(void) { - // prepare data - uint8_t data[20]; - for(int i=0; i 4 + rd_count = tu_fifo_read_n(ff, rd_buf, 16); // wr = 32, count = 16 + TEST_ASSERT_EQUAL( 16, rd_count ); + TEST_ASSERT_EQUAL_MEMORY( test_data, rd_buf, rd_count ); // case 2: wr + count > depth - tu_fifo_write_n(ff, data+8, 6); // wr = 3, count = 9 + tu_fifo_write_n(ff, test_data+32, 40); // wr = 72 -> 8, count = 56 - for(rd_count=0; rd_count<7; rd_count++) tu_fifo_read(ff, rd+rd_count); // wr = 3, count = 2 + tu_fifo_read_n(ff, rd_buf, 32); // count = 24 + TEST_ASSERT_EQUAL_MEMORY( test_data+16, rd_buf, rd_count); - TEST_ASSERT_EQUAL_MEMORY( data+5, rd, rd_count); // 5 -> 11 + TEST_ASSERT_EQUAL(24, tu_fifo_count(ff)); +} - TEST_ASSERT_EQUAL(2, tu_fifo_count(ff)); +void test_write_double_overflowed(void) +{ + tu_fifo_set_overwritable(ff, true); + + uint8_t rd_buf[FIFO_SIZE] = { 0 }; + uint8_t* buf = test_data; + + // full + buf += tu_fifo_write_n(ff, buf, FIFO_SIZE); + TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); + + // write more, should still full + buf += tu_fifo_write_n(ff, buf, FIFO_SIZE-8); + TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); + + // double overflowed: in total, write more than > 2*FIFO_SIZE + buf += tu_fifo_write_n(ff, buf, 16); + TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); + + // reading back should give back data from last FIFO_SIZE write + tu_fifo_read_n(ff, rd_buf, FIFO_SIZE); + + TEST_ASSERT_EQUAL_MEMORY(buf-16, rd_buf+FIFO_SIZE-16, 16); + + // TODO whole buffer should match, but we deliberately not implement it + // TEST_ASSERT_EQUAL_MEMORY(buf-FIFO_SIZE, rd_buf, FIFO_SIZE); +} + +static uint16_t help_write(uint16_t total, uint16_t n) +{ + tu_fifo_write_n(ff, test_data, n); + total = tu_min16(FIFO_SIZE, total + n); + + TEST_ASSERT_EQUAL(total, tu_fifo_count(ff)); + TEST_ASSERT_EQUAL(FIFO_SIZE - total, tu_fifo_remaining(ff)); + + return total; +} + +void test_write_overwritable2(void) +{ + tu_fifo_set_overwritable(ff, true); + + // based on actual crash tests detected by fuzzing + uint16_t total = 0; + + total = help_write(total, 12); + total = help_write(total, 55); + total = help_write(total, 73); + total = help_write(total, 55); + total = help_write(total, 75); + total = help_write(total, 84); + total = help_write(total, 1); + total = help_write(total, 10); + total = help_write(total, 12); + total = help_write(total, 25); + total = help_write(total, 192); } void test_peek(void) @@ -315,4 +375,4 @@ void test_rd_idx_wrap() n = tu_fifo_read_n(&ff10, dst, 4); TEST_ASSERT_EQUAL(n, 2); TEST_ASSERT_EQUAL(ff10.rd_idx, 6); -} \ No newline at end of file +} diff --git a/test/vendor/ceedling/bin/ceedling b/test/unit-test/vendor/ceedling/bin/ceedling similarity index 81% rename from test/vendor/ceedling/bin/ceedling rename to test/unit-test/vendor/ceedling/bin/ceedling index fa099590a..d110f3d4c 100644 --- a/test/vendor/ceedling/bin/ceedling +++ b/test/unit-test/vendor/ceedling/bin/ceedling @@ -49,16 +49,16 @@ unless (project_found) end desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)" - method_option :docs, :type => :boolean, :default => false, :desc => "Add docs in project vendor directory" - method_option :local, :type => :boolean, :default => false, :desc => "Create a copy of Ceedling in the project vendor directory" - method_option :no_configs, :type => :boolean, :default => false, :desc => "Don't install starter configuration files" - method_option :noconfigs, :type => :boolean, :default => false - - #deprecated: - method_option :no_docs, :type => :boolean, :default => false - method_option :nodocs, :type => :boolean, :default => false def upgrade(name, silent = false) - copy_assets_and_create_structure(name, silent, true, options || {:upgrade => true}) + as_local = true + begin + require "yaml" + as_local = (YAML.load_file(File.join(name, "project.yml"))[:project][:which_ceedling] != 'gem') + rescue + raise "ERROR: Could not find valid project file '#{yaml_path}'" + end + found_docs = File.exists?( File.join(name, "docs", "CeedlingPacket.md") ) + copy_assets_and_create_structure(name, silent, true, {:upgrade => true, :no_configs => true, :local => as_local, :docs => found_docs}) end no_commands do @@ -90,26 +90,30 @@ unless (project_found) FileUtils.touch(File.join(test_support_path, '.gitkeep')) # If documentation requested, create a place to dump them and do so + doc_path = "" if use_docs - doc_path = File.join(ceedling_path, 'docs') + doc_path = use_gem ? File.join(name, 'docs') : File.join(ceedling_path, 'docs') FileUtils.mkdir_p doc_path in_doc_path = lambda {|f| File.join(doc_path, f)} - doc_files = [ - 'docs/CeedlingPacket.md', - 'vendor/c_exception/docs/CException.md', - 'vendor/cmock/docs/CMock_Summary.md', - 'vendor/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf', - 'vendor/unity/docs/UnityAssertionsReference.md', - 'vendor/unity/docs/UnityConfigurationGuide.md', - 'vendor/unity/docs/UnityGettingStartedGuide.md', - 'vendor/unity/docs/UnityHelperScriptsGuide.md', - 'vendor/unity/docs/ThrowTheSwitchCodingStandard.md', - ] + # Add documentation from main projects to list + doc_files = {} + ['docs','vendor/unity/docs','vendor/cmock/docs','vendor/cexception/docs'].each do |p| + Dir[ File.expand_path(File.join(here, p, '*.md')) ].each do |f| + doc_files[ File.basename(f) ] = f unless(doc_files.include? f) + end + end - doc_files.each do |f| - copy_file(f, in_doc_path.call(File.basename(f)), :force => force) + # Add documentation from plugins to list + Dir[ File.join(here, 'plugins/**/README.md') ].each do |plugin_path| + k = "plugin_" + plugin_path.split(/\\|\//)[-2] + ".md" + doc_files[ k ] = File.expand_path(plugin_path) + end + + # Copy all documentation + doc_files.each_pair do |k, v| + copy_file(v, in_doc_path.call(k), :force => force) end end @@ -133,7 +137,6 @@ unless (project_found) {:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'}, {:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'}, {:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'}, - {:src => 'vendor/deep_merge/lib/', :dst => 'vendor/deep_merge/lib'}, {:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'}, {:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'}, {:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'}, @@ -146,16 +149,24 @@ unless (project_found) # We're copying in a configuration file if we haven't said not to if (use_configs) - if use_gem - copy_file(File.join('assets', 'project_as_gem.yml'), File.join(name, 'project.yml'), :force => force) + dst_yaml = File.join(name, 'project.yml') + src_yaml = if use_gem + File.join(here, 'assets', 'project_as_gem.yml') else - copy_file(File.join('assets', 'project_with_guts.yml'), File.join(name, 'project.yml'), :force => force) if is_windows? copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force) else copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force) File.chmod(0755, File.join(name, 'ceedling')) end + File.join(here, 'assets', 'project_with_guts.yml') + end + + # Perform the actual clone of the config file, while updating the version + File.open(dst_yaml,'w') do |dst| + require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb")) + dst << File.read(src_yaml).gsub(":ceedling_version: '?'",":ceedling_version: #{Ceedling::Version::CEEDLING}") + puts " create #{dst_yaml}" end end @@ -167,8 +178,8 @@ unless (project_found) unless silent puts "\n" puts "Project '#{name}' #{force ? "upgraded" : "created"}!" - puts " - Tool documentation is located in vendor/ceedling/docs" if use_docs - puts " - Execute 'ceedling help' to view available test & build tasks" + puts " - Tool documentation is located in #{doc_path}" if use_docs + puts " - Execute 'ceedling help' from #{name} to view available test & build tasks" puts '' end end @@ -206,10 +217,10 @@ unless (project_found) desc "version", "return the version of the tools installed" def version() - require 'ceedling/version.rb' - puts " Ceedling:: #{Ceedling::Version::CEEDLING}" - puts " CMock:: #{Ceedling::Version::CMOCK}" - puts " Unity:: #{Ceedling::Version::UNITY}" + require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb")) + puts " Ceedling:: #{Ceedling::Version::CEEDLING}" + puts " CMock:: #{Ceedling::Version::CMOCK}" + puts " Unity:: #{Ceedling::Version::UNITY}" puts " CException:: #{Ceedling::Version::CEXCEPTION}" end end @@ -287,6 +298,8 @@ else options[:list_tasks] = true when /^-T$/ options[:list_tasks] = true + when /^--tasks$/ + options[:list_tasks] = true when /^project:(\w+)/ ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml" else diff --git a/test/vendor/ceedling/lib/ceedling.rb b/test/unit-test/vendor/ceedling/lib/ceedling.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling.rb rename to test/unit-test/vendor/ceedling/lib/ceedling.rb diff --git a/test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb b/test/unit-test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb similarity index 97% rename from test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb index b7d57f8f4..5727bcabc 100644 --- a/test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/build_invoker_utils.rb @@ -9,7 +9,7 @@ class BuildInvokerUtils ## # Processes exceptions and tries to display a useful message for the user. # - # ==== Attriboops...utes + # ==== Attributes # # * _exception_: The exception given by a rescue statement. # * _context_: A symbol representing where in the build the exception diff --git a/test/vendor/ceedling/lib/ceedling/cacheinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/cacheinator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/cacheinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/cacheinator.rb diff --git a/test/unit-test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb new file mode 100644 index 000000000..14e8a6ef0 --- /dev/null +++ b/test/unit-test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb @@ -0,0 +1,35 @@ + +class CacheinatorHelper + + constructor :file_wrapper, :yaml_wrapper + + def diff_cached_config?(cached_filepath, hash) + return false if ( not @file_wrapper.exist?(cached_filepath) ) + return true if (@yaml_wrapper.load(cached_filepath) != hash) + return false + end + + def diff_cached_defines?(cached_filepath, files) + changed_defines = false + current_defines = COLLECTION_DEFINES_TEST_AND_VENDOR.reject(&:empty?) + + current_dependencies = Hash[files.collect { |source| [source, current_defines.dup] }] + if not @file_wrapper.exist?(cached_filepath) + @yaml_wrapper.dump(cached_filepath, current_dependencies) + return changed_defines + end + + dependencies = @yaml_wrapper.load(cached_filepath) + common_dependencies = current_dependencies.select { |file, defines| dependencies.has_key?(file) } + + if dependencies.values_at(*common_dependencies.keys) != common_dependencies.values + changed_defines = true + end + + dependencies.merge!(current_dependencies) + @yaml_wrapper.dump(cached_filepath, dependencies) + + return changed_defines + end + +end diff --git a/test/vendor/ceedling/lib/ceedling/cmock_builder.rb b/test/unit-test/vendor/ceedling/lib/ceedling/cmock_builder.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/cmock_builder.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/cmock_builder.rb diff --git a/test/vendor/ceedling/lib/ceedling/configurator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/configurator.rb similarity index 91% rename from test/vendor/ceedling/lib/ceedling/configurator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/configurator.rb index b5ad8982e..0ae4d04a8 100644 --- a/test/vendor/ceedling/lib/ceedling/configurator.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/configurator.rb @@ -54,6 +54,7 @@ class Configurator :test_fixture, :test_includes_preprocessor, :test_file_preprocessor, + :test_file_preprocessor_directives, :test_dependencies_generator, :release_compiler, :release_assembler, @@ -183,17 +184,22 @@ class Configurator @rake_plugins = @configurator_plugins.find_rake_plugins(config, paths_hash) @script_plugins = @configurator_plugins.find_script_plugins(config, paths_hash) config_plugins = @configurator_plugins.find_config_plugins(config, paths_hash) - plugin_defaults = @configurator_plugins.find_plugin_defaults(config, paths_hash) + plugin_yml_defaults = @configurator_plugins.find_plugin_yml_defaults(config, paths_hash) + plugin_hash_defaults = @configurator_plugins.find_plugin_hash_defaults(config, paths_hash) config_plugins.each do |plugin| plugin_config = @yaml_wrapper.load(plugin) config.deep_merge(plugin_config) end - plugin_defaults.each do |defaults| + plugin_yml_defaults.each do |defaults| @configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) ) end + plugin_hash_defaults.each do |defaults| + @configurator_builder.populate_defaults( config, defaults ) + end + # special plugin setting for results printing config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?) @@ -203,10 +209,19 @@ class Configurator def merge_imports(config) if config[:import] - until config[:import].empty? - path = config[:import].shift - path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) - config.deep_merge!(@yaml_wrapper.load(path)) + if config[:import].is_a? Array + until config[:import].empty? + path = config[:import].shift + path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + config.deep_merge!(@yaml_wrapper.load(path)) + end + else + config[:import].each_value do |path| + if !path.nil? + path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) + config.deep_merge!(@yaml_wrapper.load(path)) + end + end end end config.delete(:import) @@ -222,7 +237,11 @@ class Configurator interstitial = ((key == :path) ? File::PATH_SEPARATOR : '') items = ((value.class == Array) ? hash[key] : [value]) - items.each { |item| item.replace( @system_wrapper.module_eval( item ) ) if (item =~ RUBY_STRING_REPLACEMENT_PATTERN) } + items.each do |item| + if item.is_a? String and item =~ RUBY_STRING_REPLACEMENT_PATTERN + item.replace( @system_wrapper.module_eval( item ) ) + end + end hash[key] = items.join( interstitial ) @system_wrapper.env_set( key.to_s.upcase, hash[key] ) diff --git a/test/vendor/ceedling/lib/ceedling/configurator_builder.rb b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_builder.rb similarity index 93% rename from test/vendor/ceedling/lib/ceedling/configurator_builder.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/configurator_builder.rb index da8a816f5..f202d8a65 100644 --- a/test/vendor/ceedling/lib/ceedling/configurator_builder.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_builder.rb @@ -250,8 +250,8 @@ class ConfiguratorBuilder def collect_test_support_source_include_vendor_paths(in_hash) return { :collection_paths_test_support_source_include_vendor => - in_hash[:collection_paths_test_support_source_include] + - get_vendor_paths(in_hash) + get_vendor_paths(in_hash) + + in_hash[:collection_paths_test_support_source_include] } end @@ -384,14 +384,26 @@ class ConfiguratorBuilder end + def get_vendor_defines(in_hash) + defines = in_hash[:unity_defines].clone + defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks]) + defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions]) + + return defines + end + + + def collect_vendor_defines(in_hash) + return {:collection_defines_vendor => get_vendor_defines(in_hash)} + end + + def collect_test_and_vendor_defines(in_hash) - test_defines = in_hash[:defines_test].clone + defines = in_hash[:defines_test].clone + vendor_defines = get_vendor_defines(in_hash) + defines.concat(vendor_defines) if vendor_defines - test_defines.concat(in_hash[:unity_defines]) - test_defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks]) - test_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions]) - - return {:collection_defines_test_and_vendor => test_defines} + return {:collection_defines_test_and_vendor => defines} end @@ -418,28 +430,33 @@ class ConfiguratorBuilder # Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration; # we also handle those dependencies elsewhere in compilation dependencies - objects = [UNITY_C_FILE] + sources = [UNITY_C_FILE] - in_hash[:files_support].each { |file| objects << File.basename(file) } + in_hash[:files_support].each { |file| sources << file } # we don't include paths here because use of plugins or mixing different compilers may require different build paths - objects << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions]) - objects << CMOCK_C_FILE if (in_hash[:project_use_mocks]) + sources << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions]) + sources << CMOCK_C_FILE if (in_hash[:project_use_mocks]) # if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros), # then link in the unity_helper object file too if ( in_hash[:project_use_mocks] and in_hash[:cmock_unity_helper] ) in_hash[:cmock_unity_helper].each do |helper| if @file_wrapper.exist?(helper.ext(in_hash[:extension_source])) - objects << File.basename(helper) + sources << helper end end end + # create object files from all the sources + objects = sources.map { |file| File.basename(file) } + # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime) objects.map! { |object| object.ext(in_hash[:extension_object]) } - return { :collection_test_fixture_extra_link_objects => objects } + return { :collection_all_support => sources, + :collection_test_fixture_extra_link_objects => objects + } end diff --git a/test/vendor/ceedling/lib/ceedling/configurator_plugins.rb b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_plugins.rb similarity index 81% rename from test/vendor/ceedling/lib/ceedling/configurator_plugins.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/configurator_plugins.rb index 70ca884a2..75bcd982d 100644 --- a/test/vendor/ceedling/lib/ceedling/configurator_plugins.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_plugins.rb @@ -26,6 +26,7 @@ class ConfiguratorPlugins if is_script_plugin @system_wrapper.add_load_path( File.join( path, 'lib') ) + @system_wrapper.add_load_path( File.join( path, 'config') ) end break end @@ -92,7 +93,7 @@ class ConfiguratorPlugins # gather up and return default .yml filepaths that exist on-disk - def find_plugin_defaults(config, plugin_paths) + def find_plugin_yml_defaults(config, plugin_paths) defaults_with_path = [] config[:plugins][:enabled].each do |plugin| @@ -108,4 +109,23 @@ class ConfiguratorPlugins return defaults_with_path end + # gather up and return + def find_plugin_hash_defaults(config, plugin_paths) + defaults_hash= [] + + config[:plugins][:enabled].each do |plugin| + if path = plugin_paths[(plugin + '_path').to_sym] + default_path = File.join(path, "config", "defaults_#{plugin}.rb") + if @file_wrapper.exist?(default_path) + @system_wrapper.require_file( "defaults_#{plugin}.rb") + + object = eval("get_default_config()") + defaults_hash << object + end + end + end + + return defaults_hash + end + end diff --git a/test/vendor/ceedling/lib/ceedling/configurator_setup.rb b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_setup.rb similarity index 98% rename from test/vendor/ceedling/lib/ceedling/configurator_setup.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/configurator_setup.rb index 1d029b065..c43bb5c12 100644 --- a/test/vendor/ceedling/lib/ceedling/configurator_setup.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_setup.rb @@ -39,6 +39,7 @@ class ConfiguratorSetup flattened_config.merge!(@configurator_builder.collect_headers(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_existing_compilation_input(flattened_config)) flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config)) + flattened_config.merge!(@configurator_builder.collect_vendor_defines(flattened_config)) flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_artifact_extra_link_objects(flattened_config)) diff --git a/test/vendor/ceedling/lib/ceedling/configurator_validator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/configurator_validator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/configurator_validator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/configurator_validator.rb diff --git a/test/vendor/ceedling/lib/ceedling/constants.rb b/test/unit-test/vendor/ceedling/lib/ceedling/constants.rb similarity index 98% rename from test/vendor/ceedling/lib/ceedling/constants.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/constants.rb index 993ce8db2..19484f063 100644 --- a/test/vendor/ceedling/lib/ceedling/constants.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/constants.rb @@ -95,3 +95,5 @@ NULL_FILE_PATH = '/dev/null' TESTS_BASE_PATH = TEST_ROOT_NAME RELEASE_BASE_PATH = RELEASE_ROOT_NAME + +VENDORS_FILES = %w(unity UnityHelper cmock CException).freeze diff --git a/test/vendor/ceedling/lib/ceedling/defaults.rb b/test/unit-test/vendor/ceedling/lib/ceedling/defaults.rb similarity index 79% rename from test/vendor/ceedling/lib/ceedling/defaults.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/defaults.rb index 9e391fa09..1300a1aab 100644 --- a/test/vendor/ceedling/lib/ceedling/defaults.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/defaults.rb @@ -7,17 +7,20 @@ CEEDLING_VENDOR = File.expand_path(File.dirname(__FILE__) + '/../../vendor') unl CEEDLING_PLUGINS = [] unless defined? CEEDLING_PLUGINS DEFAULT_TEST_COMPILER_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_test_compiler'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, "-DGNU_COMPILER".freeze, "-g".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, "-c \"${1}\"".freeze, "-o \"${2}\"".freeze, # gcc's list file output options are complex; no use of ${3} parameter in default config @@ -27,16 +30,21 @@ DEFAULT_TEST_COMPILER_TOOL = { } DEFAULT_TEST_LINKER_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], :name => 'default_test_linker'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, "\"${1}\"".freeze, + "${5}".freeze, "-o \"${2}\"".freeze, "".freeze, - "${4}".freeze + "${4}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split ].freeze } @@ -50,12 +58,14 @@ DEFAULT_TEST_FIXTURE_TOOL = { } DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_test_includes_preprocessor'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, '-E'.freeze, # OSX clang '-MM'.freeze, '-MG'.freeze, @@ -67,18 +77,38 @@ DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = { {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, "-DGNU_COMPILER".freeze, # OSX clang - '-w'.freeze, # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX "\"${1}\"".freeze ].freeze } DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_test_file_preprocessor'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, + :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + '-E'.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, + "-DGNU_COMPILER".freeze, + # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX + "\"${1}\"".freeze, + "-o \"${2}\"".freeze + ].freeze + } + +DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL = { + :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :name => 'default_test_file_preprocessor_directives'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, :arguments => [ '-E'.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, @@ -86,6 +116,7 @@ DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = { {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, "-DGNU_COMPILER".freeze, + '-fdirectives-only'.freeze, # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX "\"${1}\"".freeze, "-o \"${2}\"".freeze @@ -100,12 +131,14 @@ else end DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_test_dependencies_generator'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, '-E'.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, @@ -123,12 +156,14 @@ DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = { } DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_release_dependencies_generator'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, '-E'.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, @@ -147,16 +182,19 @@ DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = { DEFAULT_RELEASE_COMPILER_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], :name => 'default_release_compiler'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze, "-DGNU_COMPILER".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, "-c \"${1}\"".freeze, "-o \"${2}\"".freeze, # gcc's list file output options are complex; no use of ${3} parameter in default config @@ -166,12 +204,14 @@ DEFAULT_RELEASE_COMPILER_TOOL = { } DEFAULT_RELEASE_ASSEMBLER_TOOL = { - :executable => FilePathUtils.os_executable_ext('as').freeze, + :executable => ENV['AS'].nil? ? FilePathUtils.os_executable_ext('as').freeze : ENV['AS'].split[0], :name => 'default_release_assembler'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['AS'].nil? ? "" : ENV['AS'].split[1..-1], + ENV['ASFLAGS'].nil? ? "" : ENV['ASFLAGS'].split, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_AND_INCLUDE'}.freeze, "\"${1}\"".freeze, "-o \"${2}\"".freeze, @@ -179,16 +219,21 @@ DEFAULT_RELEASE_ASSEMBLER_TOOL = { } DEFAULT_RELEASE_LINKER_TOOL = { - :executable => FilePathUtils.os_executable_ext('gcc').freeze, + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], :name => 'default_release_linker'.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze, :optional => false.freeze, :arguments => [ + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, "\"${1}\"".freeze, + "${5}".freeze, "-o \"${2}\"".freeze, "".freeze, - "${4}".freeze + "${4}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split ].freeze } @@ -205,6 +250,7 @@ DEFAULT_TOOLS_TEST_PREPROCESSORS = { :tools => { :test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL, :test_file_preprocessor => DEFAULT_TEST_FILE_PREPROCESSOR_TOOL, + :test_file_preprocessor_directives => DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL, } } @@ -245,8 +291,10 @@ DEFAULT_CEEDLING_CONFIG = { :compile_threads => 1, :test_threads => 1, :use_test_preprocessor => false, + :use_preprocessor_directives => false, :use_deep_dependencies => false, :generate_deep_dependencies => true, # only applicable if use_deep_dependencies is true + :auto_link_deep_dependencies => false, :test_file_prefix => 'test_', :options_paths => [], :release_build => false, @@ -263,6 +311,7 @@ DEFAULT_CEEDLING_CONFIG = { :source => [], # must be populated by user :support => [], :include => [], + :libraries => [], :test_toolchain_include => [], :release_toolchain_include => [], }, @@ -290,6 +339,8 @@ DEFAULT_CEEDLING_CONFIG = { }, :libraries => { + :flag => '-l${1}', + :path_flag => '-L ${1}', :test => [], :test_preprocess => [], :release => [], @@ -303,6 +354,7 @@ DEFAULT_CEEDLING_CONFIG = { :source => '.c', :assembly => '.s', :object => '.o', + :libraries => ['.a','.so'], :executable => ( SystemWrapper.windows? ? EXTENSION_WIN_EXE : EXTENSION_NONWIN_EXE ), :map => '.map', :list => '.lst', @@ -345,6 +397,7 @@ DEFAULT_CEEDLING_CONFIG = { }, :test_includes_preprocessor => { :arguments => [] }, :test_file_preprocessor => { :arguments => [] }, + :test_file_preprocessor_directives => { :arguments => [] }, :test_dependencies_generator => { :arguments => [] }, :release_compiler => { :arguments => [] }, :release_linker => { :arguments => [] }, diff --git a/test/vendor/ceedling/lib/ceedling/dependinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/dependinator.rb similarity index 92% rename from test/vendor/ceedling/lib/ceedling/dependinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/dependinator.rb index ebd123772..accfe80c9 100644 --- a/test/vendor/ceedling/lib/ceedling/dependinator.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/dependinator.rb @@ -86,13 +86,12 @@ class Dependinator def enhance_results_dependencies(result_filepath) - @rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || - @project_config_manager.test_defines_changed) + @rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if @project_config_manager.test_config_changed end - def setup_test_executable_dependencies(test, objects) - @rake_wrapper.create_file_task( @file_path_utils.form_test_executable_filepath(test), objects ) + def enhance_test_executable_dependencies(test, objects) + @rake_wrapper[ @file_path_utils.form_test_executable_filepath(test) ].enhance( objects ) end end diff --git a/test/vendor/ceedling/lib/ceedling/erb_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/erb_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/erb_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/erb_wrapper.rb diff --git a/test/vendor/ceedling/lib/ceedling/file_finder.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_finder.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/file_finder.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_finder.rb diff --git a/test/vendor/ceedling/lib/ceedling/file_finder_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_finder_helper.rb similarity index 85% rename from test/vendor/ceedling/lib/ceedling/file_finder_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_finder_helper.rb index 0a31b44bd..a168e5cb5 100644 --- a/test/vendor/ceedling/lib/ceedling/file_finder_helper.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/file_finder_helper.rb @@ -25,10 +25,12 @@ class FileFinderHelper end - case (complain) - when :error then blow_up(file_name, extra_message) if (file_to_find.nil?) - when :warn then gripe(file_name, extra_message) if (file_to_find.nil?) - #when :ignore then + if file_to_find.nil? + case (complain) + when :error then blow_up(file_name, extra_message) + when :warn then gripe(file_name, extra_message) + #when :ignore then + end end return file_to_find diff --git a/test/vendor/ceedling/lib/ceedling/file_path_utils.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_path_utils.rb similarity index 98% rename from test/vendor/ceedling/lib/ceedling/file_path_utils.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_path_utils.rb index 607039fd5..89a28ba7f 100644 --- a/test/vendor/ceedling/lib/ceedling/file_path_utils.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/file_path_utils.rb @@ -21,9 +21,11 @@ class FilePathUtils # standardize path to use '/' path separator & have no trailing path separator def self.standardize(path) - path.strip! - path.gsub!(/\\/, '/') - path.chomp!('/') + if path.is_a? String + path.strip! + path.gsub!(/\\/, '/') + path.chomp!('/') + end return path end diff --git a/test/vendor/ceedling/lib/ceedling/file_system_utils.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_system_utils.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/file_system_utils.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_system_utils.rb diff --git a/test/vendor/ceedling/lib/ceedling/file_system_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_system_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/file_system_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_system_wrapper.rb diff --git a/test/vendor/ceedling/lib/ceedling/file_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/file_wrapper.rb similarity index 88% rename from test/vendor/ceedling/lib/ceedling/file_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/file_wrapper.rb index 1680ca520..9e5a909b4 100644 --- a/test/vendor/ceedling/lib/ceedling/file_wrapper.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/file_wrapper.rb @@ -33,15 +33,15 @@ class FileWrapper end def rm_f(filepath, options={}) - FileUtils.rm_f(filepath, options) + FileUtils.rm_f(filepath, **options) end def rm_r(filepath, options={}) - FileUtils.rm_r(filepath, options={}) + FileUtils.rm_r(filepath, **options={}) end def cp(source, destination, options={}) - FileUtils.cp(source, destination, options) + FileUtils.cp(source, destination, **options) end def compare(from, to) @@ -59,7 +59,7 @@ class FileWrapper end def touch(filepath, options={}) - FileUtils.touch(filepath, options) + FileUtils.touch(filepath, **options) end def write(filepath, contents, flags='w') diff --git a/test/vendor/ceedling/lib/ceedling/flaginator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/flaginator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/flaginator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/flaginator.rb diff --git a/test/vendor/ceedling/lib/ceedling/generator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/generator.rb similarity index 96% rename from test/vendor/ceedling/lib/ceedling/generator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/generator.rb index 828a0c025..0b8902475 100644 --- a/test/vendor/ceedling/lib/ceedling/generator.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/generator.rb @@ -101,19 +101,21 @@ class Generator shell_result = ex.shell_result raise ex ensure + arg_hash[:shell_command] = command[:line] arg_hash[:shell_result] = shell_result @plugin_manager.post_compile_execute(arg_hash) end end - def generate_executable_file(tool, context, objects, executable, map='', libraries=[]) + def generate_executable_file(tool, context, objects, executable, map='', libraries=[], libpaths=[]) shell_result = {} arg_hash = { :tool => tool, :context => context, :objects => objects, :executable => executable, :map => map, - :libraries => libraries + :libraries => libraries, + :libpaths => libpaths } @plugin_manager.pre_link_execute(arg_hash) @@ -125,7 +127,8 @@ class Generator arg_hash[:objects], arg_hash[:executable], arg_hash[:map], - arg_hash[:libraries] + arg_hash[:libraries], + arg_hash[:libpaths] ) @streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG) diff --git a/test/vendor/ceedling/lib/ceedling/generator_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/generator_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/generator_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/generator_helper.rb diff --git a/test/vendor/ceedling/lib/ceedling/generator_test_results.rb b/test/unit-test/vendor/ceedling/lib/ceedling/generator_test_results.rb similarity index 81% rename from test/vendor/ceedling/lib/ceedling/generator_test_results.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/generator_test_results.rb index 1d0c5201f..3af2d720a 100644 --- a/test/vendor/ceedling/lib/ceedling/generator_test_results.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/generator_test_results.rb @@ -37,6 +37,10 @@ class GeneratorTestResults elements = extract_line_elements(line, results[:source][:file]) results[:successes] << elements[0] results[:stdout] << elements[1] if (!elements[1].nil?) + when /(:PASS \(.* ms\)$)/ + elements = extract_line_elements(line, results[:source][:file]) + results[:successes] << elements[0] + results[:stdout] << elements[1] if (!elements[1].nil?) when /(:FAIL)/ elements = extract_line_elements(line, results[:source][:file]) results[:failures] << elements[0] @@ -73,6 +77,7 @@ class GeneratorTestResults # handle anything preceding filename in line as extra output to be collected stdout = nil stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i + unity_test_time = 0 if (line =~ stdout_regex) stdout = $1.clone @@ -82,8 +87,14 @@ class GeneratorTestResults # collect up test results minus and extra output elements = (line.strip.split(':'))[1..-1] - return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip}, stdout if elements.size >= 3 - return {:test => '???', :line => -1, :message => nil} #fallback safe option. TODO better handling + # find timestamp if available + if (elements[-1] =~ / \((\d*(?:\.\d*)?) ms\)/) + unity_test_time = $1.to_f / 1000 + elements[-1].sub!(/ \((\d*(?:\.\d*)?) ms\)/, '') + end + + return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip, :unity_test_time => unity_test_time}, stdout if elements.size >= 3 + return {:test => '???', :line => -1, :message => nil, :unity_test_time => unity_test_time} #fallback safe option. TODO better handling end end diff --git a/test/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb b/test/unit-test/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/generator_test_results_sanity_checker.rb diff --git a/test/vendor/ceedling/lib/ceedling/generator_test_runner.rb b/test/unit-test/vendor/ceedling/lib/ceedling/generator_test_runner.rb similarity index 89% rename from test/vendor/ceedling/lib/ceedling/generator_test_runner.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/generator_test_runner.rb index 6999faf96..79ed7140f 100644 --- a/test/vendor/ceedling/lib/ceedling/generator_test_runner.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/generator_test_runner.rb @@ -44,13 +44,15 @@ class GeneratorTestRunner def generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes=[]) require 'generate_test_runner.rb' + header_extension = @configurator.extension_header + #actually build the test runner using Unity's test runner generator #(there is no need to use preprocessor here because we've already looked up test cases and are passing them in here) @test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config ) @test_runner_generator.generate( module_name, runner_filepath, test_cases, - mock_list, - test_file_includes) + mock_list.map{|f| File.basename(f,'.*')+header_extension}, + test_file_includes.map{|f| File.basename(f,'.*')+header_extension}) end end diff --git a/test/vendor/ceedling/lib/ceedling/loginator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/loginator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/loginator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/loginator.rb diff --git a/test/vendor/ceedling/lib/ceedling/makefile.rb b/test/unit-test/vendor/ceedling/lib/ceedling/makefile.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/makefile.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/makefile.rb diff --git a/test/vendor/ceedling/lib/ceedling/objects.yml b/test/unit-test/vendor/ceedling/lib/ceedling/objects.yml similarity index 88% rename from test/vendor/ceedling/lib/ceedling/objects.yml rename to test/unit-test/vendor/ceedling/lib/ceedling/objects.yml index 2e2e9b9d2..43bbc066c 100644 --- a/test/vendor/ceedling/lib/ceedling/objects.yml +++ b/test/unit-test/vendor/ceedling/lib/ceedling/objects.yml @@ -17,11 +17,11 @@ reportinator: rake_utils: compose: - - rake_wrapper + - rake_wrapper system_utils: compose: - - system_wrapper + - system_wrapper file_path_utils: compose: @@ -203,13 +203,13 @@ generator_helper: generator_test_results: compose: - - configurator + - configurator - generator_test_results_sanity_checker - - yaml_wrapper + - yaml_wrapper generator_test_results_sanity_checker: compose: - - configurator + - configurator - streaminator generator_test_runner: @@ -223,43 +223,46 @@ dependinator: - configurator - project_config_manager - test_includes_extractor - - file_path_utils + - file_path_utils - rake_wrapper - file_wrapper preprocessinator: compose: - - preprocessinator_helper + - preprocessinator_helper - preprocessinator_includes_handler - preprocessinator_file_handler - - task_invoker + - task_invoker - file_path_utils - yaml_wrapper + - project_config_manager + - configurator preprocessinator_helper: - compose: - - configurator - - test_includes_extractor - - task_invoker - - file_finder - - file_path_utils + compose: + - configurator + - test_includes_extractor + - task_invoker + - file_finder + - file_path_utils preprocessinator_includes_handler: compose: - - configurator - - tool_executor - - task_invoker - - file_path_utils - - yaml_wrapper - - file_wrapper + - configurator + - tool_executor + - task_invoker + - file_path_utils + - yaml_wrapper + - file_wrapper + - file_finder preprocessinator_file_handler: compose: - preprocessinator_extractor - - configurator - - tool_executor + - configurator + - tool_executor - file_path_utils - - file_wrapper + - file_wrapper preprocessinator_extractor: diff --git a/test/vendor/ceedling/lib/ceedling/par_map.rb b/test/unit-test/vendor/ceedling/lib/ceedling/par_map.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/par_map.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/par_map.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin_builder.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin_builder.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin_builder.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin_builder.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin_manager.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin_manager.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin_manager.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin_manager.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin_manager_helper.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin_reportinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin_reportinator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin_reportinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin_reportinator.rb diff --git a/test/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/plugin_reportinator_helper.rb diff --git a/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator.rb new file mode 100644 index 000000000..52d82ca29 --- /dev/null +++ b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator.rb @@ -0,0 +1,56 @@ + +class Preprocessinator + + constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper, :project_config_manager, :configurator + + + def setup + # fashion ourselves callbacks @preprocessinator_helper can use + @preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) } + @preprocess_mock_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } + @preprocess_test_file_directives_proc = Proc.new { |filepath| self.preprocess_file_directives(filepath) } + @preprocess_test_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } + end + + def preprocess_shallow_source_includes(test) + @preprocessinator_helper.preprocess_source_includes(test) + end + + def preprocess_test_and_invoke_test_mocks(test) + @preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc) + + mocks_list = @preprocessinator_helper.assemble_mocks_list(test) + + @project_config_manager.process_test_defines_change(mocks_list) + + @preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_mock_file_proc) + + @task_invoker.invoke_test_mocks(mocks_list) + + if (@configurator.project_use_preprocessor_directives) + @preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_directives_proc) + else + @preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_proc) + end + + return mocks_list + end + + def preprocess_shallow_includes(filepath) + includes = @preprocessinator_includes_handler.extract_includes(filepath) + + @preprocessinator_includes_handler.write_shallow_includes_list( + @file_path_utils.form_preprocessed_includes_list_filepath(filepath), includes) + end + + def preprocess_file(filepath) + @preprocessinator_includes_handler.invoke_shallow_includes_list(filepath) + @preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) ) + end + + def preprocess_file_directives(filepath) + @preprocessinator_includes_handler.invoke_shallow_includes_list( filepath ) + @preprocessinator_file_handler.preprocess_file_directives( filepath, + @yaml_wrapper.load( @file_path_utils.form_preprocessed_includes_list_filepath( filepath ) ) ) + end +end diff --git a/test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb similarity index 56% rename from test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb index 49509a8b0..62026e15a 100644 --- a/test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_extractor.rb @@ -16,6 +16,7 @@ class PreprocessinatorExtractor lines = [] File.readlines(filepath).each do |line| + line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') if found_file and not line =~ not_pragma lines << line else @@ -27,4 +28,28 @@ class PreprocessinatorExtractor return lines end + + def extract_base_file_from_preprocessed_directives(filepath) + # preprocessing by way of toolchain preprocessor eliminates directives only + # like #ifdef's and leave other code + + # iterate through all lines and only get last chunk of file after a last + # '#'line containing file name of our filepath + + base_name = File.basename(filepath) + pattern = /^#.*(\s|\/|\\|\")#{Regexp.escape(base_name)}/ + found_file = false # have we found the file we care about? + + lines = [] + File.readlines(filepath).each do |line| + line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') + lines << line + + if line =~ pattern + lines = [] + end + end + + return lines + end end diff --git a/test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb similarity index 54% rename from test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb index b6b5efbc6..978fa0d05 100644 --- a/test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_file_handler.rb @@ -18,4 +18,17 @@ class PreprocessinatorFileHandler @file_wrapper.write(preprocessed_filepath, contents.join("\n")) end + def preprocess_file_directives(filepath, includes) + preprocessed_filepath = @file_path_utils.form_preprocessed_file_filepath(filepath) + + command = @tool_executor.build_command_line(@configurator.tools_test_file_preprocessor_directives, [], filepath, preprocessed_filepath) + @tool_executor.exec(command[:line], command[:options]) + + contents = @preprocessinator_extractor.extract_base_file_from_preprocessed_directives(preprocessed_filepath) + + includes.each{|include| contents.unshift("#include \"#{include}\"")} + + @file_wrapper.write(preprocessed_filepath, contents.join("\n")) + end + end diff --git a/test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb similarity index 93% rename from test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb index 1419a5616..4bbda67fc 100644 --- a/test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_helper.rb @@ -15,6 +15,10 @@ class PreprocessinatorHelper end end + def preprocess_source_includes(test) + @test_includes_extractor.parse_test_file_source_include(test) + end + def assemble_mocks_list(test) return @file_path_utils.form_mocks_source_filelist( @test_includes_extractor.lookup_raw_mock_list(test) ) end diff --git a/test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb similarity index 54% rename from test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb index 703c84f3c..8b89c0b3a 100644 --- a/test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/preprocessinator_includes_handler.rb @@ -2,7 +2,7 @@ class PreprocessinatorIncludesHandler - constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper + constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper, :file_finder @@makefile_cache = {} # shallow includes: only those headers a source file explicitly includes @@ -65,6 +65,7 @@ class PreprocessinatorIncludesHandler to_process = [filepath] ignore_list = [] list = [] + all_mocks = [] include_paths = @configurator.project_config_hash[:collection_paths_include] include_paths = [] if include_paths.nil? @@ -73,12 +74,10 @@ class PreprocessinatorIncludesHandler while to_process.length > 0 target = to_process.shift() ignore_list << target - # puts "[HELL] Processing: \t\t#{target}" - new_deps, new_to_process = extract_includes_helper(target, include_paths, ignore_list) + new_deps, new_to_process, all_mocks = extract_includes_helper(target, include_paths, ignore_list, all_mocks) list += new_deps to_process += new_to_process - if (!@configurator.project_config_hash.has_key?(:project_auto_link_deep_dependencies) or - !@configurator.project_config_hash[:project_auto_link_deep_dependencies]) + if !@configurator.project_config_hash[:project_auto_link_deep_dependencies] break else list = list.uniq() @@ -89,93 +88,102 @@ class PreprocessinatorIncludesHandler return list end - def extract_includes_helper(filepath, include_paths, ignore_list) + def extract_includes_helper(filepath, include_paths, ignore_list, mocks) # Extract the dependencies from the make rule - hdr_ext = @configurator.extension_header make_rule = self.form_shallow_dependencies_rule(filepath) - dependencies = make_rule.split.find_all {|path| path.end_with?(hdr_ext) }.uniq - dependencies.map! {|hdr| hdr.gsub('\\','/') } + target_file = make_rule.split[0].gsub(':', '').gsub('\\','/') + base = File.basename(target_file, File.extname(target_file)) + make_rule_dependencies = make_rule.gsub(/.*\b#{Regexp.escape(base)}\S*/, '').gsub(/\\$/, '') + + # Extract the headers dependencies from the make rule + hdr_ext = @configurator.extension_header + headers_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(hdr_ext) }.uniq + headers_dependencies.map! {|hdr| hdr.gsub('\\','/') } + full_path_headers_dependencies = extract_full_path_dependencies(headers_dependencies) - # Separate the real files form the annotated ones and remove the '@@@@' - annotated_headers, real_headers = dependencies.partition {|hdr| hdr =~ /^@@@@/ } - annotated_headers.map! {|hdr| hdr.gsub('@@@@','') } - # Matching annotated_headers values against real_headers to ensure that - # annotated_headers contain full path entries (as returned by make rule) - annotated_headers.map! {|hdr| real_headers.find {|real_hdr| !real_hdr.match(/(.*\/)?#{Regexp.escape(hdr)}/).nil? } } - annotated_headers = annotated_headers.compact + # Extract the sources dependencies from the make rule + src_ext = @configurator.extension_source + sources_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(src_ext) }.uniq + sources_dependencies.map! {|src| src.gsub('\\','/') } + full_path_sources_dependencies = extract_full_path_dependencies(sources_dependencies) - # Find which of our annotated headers are "real" dependencies. This is - # intended to weed out dependencies that have been removed due to build - # options defined in the project yaml and/or in the headers themselves. - list = annotated_headers.find_all do |annotated_header| - # find the index of the "real" include that matches the annotated one. - idx = real_headers.find_index do |real_header| - real_header =~ /^(.*\/)?#{Regexp.escape(annotated_header)}$/ - end - # If we found a real include, delete it from the array and return it, - # otherwise return nil. Since nil is falsy this has the effect of making - # find_all return only the annotated headers for which a real include was - # found/deleted - idx ? real_headers.delete_at(idx) : nil + list = full_path_headers_dependencies + full_path_sources_dependencies + + mock_prefix = @configurator.project_config_hash[:cmock_mock_prefix] + # Creating list of mocks + mocks += full_path_headers_dependencies.find_all do |header| + File.basename(header) =~ /^#{mock_prefix}.*$/ end.compact - # Extract direct dependencies that were also added - src_ext = @configurator.extension_source - sdependencies = make_rule.split.find_all {|path| path.end_with?(src_ext) }.uniq - sdependencies.map! {|hdr| hdr.gsub('\\','/') } - list += sdependencies + # ignore real file when both mock and real file exist + mocks.each do |mock| + list.each do |filename| + if File.basename(filename) == File.basename(mock).sub(mock_prefix, '') + ignore_list << filename + end + end + end.compact + + # Filtering list of final includes to only include mocks and anything that is NOT in the ignore_list + list = list.select do |item| + mocks.include? item or !(ignore_list.any? { |ignore_item| !item.match(/^(.*\/)?#{Regexp.escape(ignore_item)}$/).nil? }) + end to_process = [] - if @configurator.project_config_hash.has_key?(:project_auto_link_deep_dependencies) && @configurator.project_config_hash[:project_auto_link_deep_dependencies] - # Creating list of mocks - mocks = annotated_headers.find_all do |annotated_header| - File.basename(annotated_header) =~ /^#{@configurator.project_config_hash[:cmock_mock_prefix]}.*$/ - end.compact - + if @configurator.project_config_hash[:project_auto_link_deep_dependencies] # Creating list of headers that should be recursively pre-processed - # Skipping mocks and unity.h - headers_to_deep_link = annotated_headers.select do |annotated_header| - !(mocks.include? annotated_header) and (annotated_header.match(/^(.*\/)?unity\.h$/).nil?) - end - headers_to_deep_link.map! {|hdr| File.expand_path(hdr)} - - mocks.each do |mock| - dirname = File.dirname(mock) - #basename = File.basename(mock).delete_prefix(@configurator.project_config_hash[:cmock_mock_prefix]) - basename = File.basename(mock).sub(@configurator.project_config_hash[:cmock_mock_prefix], '') - if dirname != "." - ignore_list << File.join(dirname, basename) - else - ignore_list << basename - end - end.compact - - # Filtering list of final includes to only include mocks and anything that is NOT in the ignore_list - list = list.select do |item| - mocks.include? item or !(ignore_list.any? { |ignore_item| !item.match(/^(.*\/)?#{Regexp.escape(ignore_item)}$/).nil? }) + # Skipping mocks and vendor headers + headers_to_deep_link = full_path_headers_dependencies.select do |hdr| + !(mocks.include? hdr) and (hdr.match(/^(.*\/)(#{VENDORS_FILES.join('|')}) + #{Regexp.escape(hdr_ext)}$/).nil?) end + headers_to_deep_link.map! {|hdr| File.expand_path(hdr) } + headers_to_deep_link.compact! headers_to_deep_link.each do |hdr| if (ignore_list.none? {|ignore_header| hdr.match(/^(.*\/)?#{Regexp.escape(ignore_header)}$/)} and include_paths.none? {|include_path| hdr =~ /^#{include_path}\.*/}) if File.exist?(hdr) to_process << hdr - #source_file = hdr.delete_suffix(hdr_ext) + src_ext - source_file = hdr.chomp(hdr_ext) + src_ext - if source_file != hdr and File.exist?(source_file) - to_process << source_file - end + src = @file_finder.find_compilation_input_file(hdr, :ignore) + to_process << src if src end end end end - return list, to_process + return list, to_process, mocks end def write_shallow_includes_list(filepath, list) @yaml_wrapper.dump(filepath, list) end + + private + + def extract_full_path_dependencies(dependencies) + # Separate the real files form the annotated ones and remove the '@@@@' + annotated_files, real_files = dependencies.partition {|file| file =~ /^@@@@/} + annotated_files.map! {|file| file.gsub('@@@@','') } + # Matching annotated_files values against real_files to ensure that + # annotated_files contain full path entries (as returned by make rule) + annotated_files.map! {|file| real_files.find {|real| !real.match(/^(.*\/)?#{Regexp.escape(file)}$/).nil?}} + annotated_files = annotated_files.compact + + # Find which of our annotated files are "real" dependencies. This is + # intended to weed out dependencies that have been removed due to build + # options defined in the project yaml and/or in the files themselves. + return annotated_files.find_all do |annotated_file| + # find the index of the "real" file that matches the annotated one. + idx = real_files.find_index do |real_file| + real_file =~ /^(.*\/)?#{Regexp.escape(annotated_file)}$/ + end + # If we found a real file, delete it from the array and return it, + # otherwise return nil. Since nil is falsy this has the effect of making + # find_all return only the annotated filess for which a real file was + # found/deleted + idx ? real_files.delete_at(idx) : nil + end.compact + end end diff --git a/test/vendor/ceedling/lib/ceedling/project_config_manager.rb b/test/unit-test/vendor/ceedling/lib/ceedling/project_config_manager.rb similarity index 75% rename from test/vendor/ceedling/lib/ceedling/project_config_manager.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/project_config_manager.rb index 31f7e3a68..ed7a73b8c 100644 --- a/test/vendor/ceedling/lib/ceedling/project_config_manager.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/project_config_manager.rb @@ -21,9 +21,15 @@ class ProjectConfigManager @options_files << File.basename( option_filepath ) config_hash.deep_merge!( @yaml_wrapper.load( option_filepath ) ) end - - + + def filter_internal_sources(sources) + filtered_sources = sources.clone + filtered_sources.delete_if { |item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{Regexp.escape(EXTENSION_SOURCE)}$/ } + filtered_sources.delete_if { |item| item =~ /#{VENDORS_FILES.map{|source| '\b' + Regexp.escape(source.ext(EXTENSION_SOURCE)) + '\b'}.join('|')}$/ } + return filtered_sources + end + def process_release_config_change # has project configuration changed since last release build @release_config_changed = @cacheinator.diff_cached_release_config?( @config_hash ) @@ -40,7 +46,7 @@ class ProjectConfigManager @test_defines_changed = @cacheinator.diff_cached_test_defines?( files ) if @test_defines_changed # update timestamp for rake task prerequisites - @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath ) + @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath, :mtime => Time.now + 10 ) end end end diff --git a/test/vendor/ceedling/lib/ceedling/project_file_loader.rb b/test/unit-test/vendor/ceedling/lib/ceedling/project_file_loader.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/project_file_loader.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/project_file_loader.rb diff --git a/test/vendor/ceedling/lib/ceedling/rake_utils.rb b/test/unit-test/vendor/ceedling/lib/ceedling/rake_utils.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rake_utils.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/rake_utils.rb diff --git a/test/vendor/ceedling/lib/ceedling/rake_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/rake_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rake_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/rake_wrapper.rb diff --git a/test/vendor/ceedling/lib/ceedling/rakefile.rb b/test/unit-test/vendor/ceedling/lib/ceedling/rakefile.rb similarity index 97% rename from test/vendor/ceedling/lib/ceedling/rakefile.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/rakefile.rb index 37001bacc..1bcb82491 100644 --- a/test/vendor/ceedling/lib/ceedling/rakefile.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/rakefile.rb @@ -10,7 +10,6 @@ $LOAD_PATH.unshift( CEEDLING_LIB ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'unity/auto') ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'diy/lib') ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'cmock/lib') ) -$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'deep_merge/lib') ) require 'rake' diff --git a/test/vendor/ceedling/lib/ceedling/release_invoker.rb b/test/unit-test/vendor/ceedling/lib/ceedling/release_invoker.rb similarity index 66% rename from test/vendor/ceedling/lib/ceedling/release_invoker.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/release_invoker.rb index 5bfb6cd70..19bbca727 100644 --- a/test/vendor/ceedling/lib/ceedling/release_invoker.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/release_invoker.rb @@ -56,15 +56,40 @@ class ReleaseInvoker end def convert_libraries_to_arguments(libraries) - args = (libraries || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : []) + args = ((libraries || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten if (defined? LIBRARIES_FLAG) args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) } end return args end + def get_library_paths_to_arguments() + paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : [] + if (defined? LIBRARIES_PATH_FLAG) + paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) } + end + return paths + end + def sort_objects_and_libraries(both) - extension = "\\" + ((defined? EXTENSION_SUBPROJECTS) ? EXTENSION_SUBPROJECTS : ".LIBRARY") + extension = if ((defined? EXTENSION_SUBPROJECTS) && (defined? EXTENSION_LIBRARIES)) + extension_libraries = if (EXTENSION_LIBRARIES.class == Array) + EXTENSION_LIBRARIES.join(")|(?:\\") + else + EXTENSION_LIBRARIES + end + "(?:\\#{EXTENSION_SUBPROJECTS})|(?:\\#{extension_libraries})" + elsif (defined? EXTENSION_SUBPROJECTS) + "\\#{EXTENSION_SUBPROJECTS}" + elsif (defined? EXTENSION_LIBRARIES) + if (EXTENSION_LIBRARIES.class == Array) + "(?:\\#{EXTENSION_LIBRARIES.join(")|(?:\\")})" + else + "\\#{EXTENSION_LIBRARIES}" + end + else + "\\.LIBRARY" + end sorted_objects = both.group_by {|v| v.match(/.+#{extension}$/) ? :libraries : :objects } libraries = sorted_objects[:libraries] || [] objects = sorted_objects[:objects] || [] diff --git a/test/vendor/ceedling/lib/ceedling/release_invoker_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/release_invoker_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/release_invoker_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/release_invoker_helper.rb diff --git a/test/vendor/ceedling/lib/ceedling/reportinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/reportinator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/reportinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/reportinator.rb diff --git a/test/vendor/ceedling/lib/ceedling/rules_cmock.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_cmock.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rules_cmock.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_cmock.rake diff --git a/test/vendor/ceedling/lib/ceedling/rules_preprocess.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_preprocess.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rules_preprocess.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_preprocess.rake diff --git a/test/vendor/ceedling/lib/ceedling/rules_release.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_release.rake similarity index 73% rename from test/vendor/ceedling/lib/ceedling/rules_release.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_release.rake index ae39a76e3..4a583bd6c 100644 --- a/test/vendor/ceedling/lib/ceedling/rules_release.rake +++ b/test/unit-test/vendor/ceedling/lib/ceedling/rules_release.rake @@ -2,6 +2,17 @@ RELEASE_COMPILE_TASK_ROOT = RELEASE_TASK_ROOT + 'compile:' unless defined?(RELEASE_COMPILE_TASK_ROOT) RELEASE_ASSEMBLE_TASK_ROOT = RELEASE_TASK_ROOT + 'assemble:' unless defined?(RELEASE_ASSEMBLE_TASK_ROOT) +# If GCC and Releasing a Library, Update Tools to Automatically Have Necessary Tags +if (TOOLS_RELEASE_COMPILER[:executable] == DEFAULT_RELEASE_COMPILER_TOOL[:executable]) + if (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.so') + TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC") + TOOLS_RELEASE_LINKER[:arguments] << "-shared" unless TOOLS_RELEASE_LINKER[:arguments].include?("-shared") + elsif (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.a') + TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC") + TOOLS_RELEASE_LINKER[:executable] = 'ar' + TOOLS_RELEASE_LINKER[:arguments] = ['rcs', '${2}', '${1}'].compact + end +end if (RELEASE_BUILD_USE_ASSEMBLY) rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_ASM_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ @@ -37,16 +48,18 @@ end rule(/#{PROJECT_RELEASE_BUILD_TARGET}/) do |bin_file| objects, libraries = @ceedling[:release_invoker].sort_objects_and_libraries(bin_file.prerequisites) - tool = TOOLS_RELEASE_LINKER.clone - lib_args = @ceedling[:release_invoker].convert_libraries_to_arguments(libraries) - map_file = @ceedling[:configurator].project_release_build_map + tool = TOOLS_RELEASE_LINKER.clone + lib_args = @ceedling[:release_invoker].convert_libraries_to_arguments(libraries) + lib_paths = @ceedling[:release_invoker].get_library_paths_to_arguments() + map_file = @ceedling[:configurator].project_release_build_map @ceedling[:generator].generate_executable_file( tool, RELEASE_SYM, objects, bin_file.name, map_file, - lib_args ) + lib_args, + lib_paths ) @ceedling[:release_invoker].artifactinate( bin_file.name, map_file, @ceedling[:configurator].release_build_artifacts ) end diff --git a/test/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_release_deep_dependencies.rake diff --git a/test/vendor/ceedling/lib/ceedling/rules_tests.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_tests.rake similarity index 91% rename from test/vendor/ceedling/lib/ceedling/rules_tests.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_tests.rake index 2b8f7af5b..61e15e2cc 100644 --- a/test/vendor/ceedling/lib/ceedling/rules_tests.rake +++ b/test/unit-test/vendor/ceedling/lib/ceedling/rules_tests.rake @@ -34,16 +34,16 @@ end rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| - lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() - + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() @ceedling[:generator].generate_executable_file( TOOLS_TEST_LINKER, TEST_SYM, bin_file.prerequisites, bin_file.name, @ceedling[:file_path_utils].form_test_build_map_filepath( bin_file.name ), - lib_args ) + lib_args, + lib_paths ) end @@ -66,8 +66,7 @@ namespace TEST_SYM do @ceedling[:file_finder].find_test_from_file_path(test) end ]) do |test| - @ceedling[:rake_wrapper][:directories].reenable if @ceedling[:task_invoker].first_run == false && @ceedling[:project_config_manager].test_defines_changed - @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:rake_wrapper][:test_deps].invoke @ceedling[:test_invoker].setup_and_invoke([test.source]) end end diff --git a/test/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake b/test/unit-test/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/rules_tests_deep_dependencies.rake diff --git a/test/vendor/ceedling/lib/ceedling/setupinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/setupinator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/setupinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/setupinator.rb index 8347b42ab..ea78fd97e 100644 --- a/test/vendor/ceedling/lib/ceedling/setupinator.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/setupinator.rb @@ -25,8 +25,8 @@ class Setupinator @ceedling[:configurator].populate_cmock_defaults( config_hash ) @ceedling[:configurator].find_and_merge_plugins( config_hash ) @ceedling[:configurator].merge_imports( config_hash ) - @ceedling[:configurator].tools_setup( config_hash ) @ceedling[:configurator].eval_environment_variables( config_hash ) + @ceedling[:configurator].tools_setup( config_hash ) @ceedling[:configurator].eval_paths( config_hash ) @ceedling[:configurator].standardize_paths( config_hash ) @ceedling[:configurator].validate( config_hash ) diff --git a/test/vendor/ceedling/lib/ceedling/stream_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/stream_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/stream_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/stream_wrapper.rb diff --git a/test/vendor/ceedling/lib/ceedling/streaminator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/streaminator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/streaminator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/streaminator.rb diff --git a/test/vendor/ceedling/lib/ceedling/streaminator_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/streaminator_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/streaminator_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/streaminator_helper.rb diff --git a/test/vendor/ceedling/lib/ceedling/system_utils.rb b/test/unit-test/vendor/ceedling/lib/ceedling/system_utils.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/system_utils.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/system_utils.rb diff --git a/test/vendor/ceedling/lib/ceedling/system_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/system_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/system_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/system_wrapper.rb diff --git a/test/vendor/ceedling/lib/ceedling/target_loader.rb b/test/unit-test/vendor/ceedling/lib/ceedling/target_loader.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/target_loader.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/target_loader.rb diff --git a/test/vendor/ceedling/lib/ceedling/task_invoker.rb b/test/unit-test/vendor/ceedling/lib/ceedling/task_invoker.rb similarity index 80% rename from test/vendor/ceedling/lib/ceedling/task_invoker.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/task_invoker.rb index 642695c46..7bfabbb12 100644 --- a/test/vendor/ceedling/lib/ceedling/task_invoker.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/task_invoker.rb @@ -46,25 +46,31 @@ class TaskInvoker return @rake_utils.task_invoked?(regex) end - + def reset_rake_task_for_changed_defines(file) + if !(file =~ /#{VENDORS_FILES.map{|ignore| '\b' + ignore.ext(File.extname(file)) + '\b'}.join('|')}$/) + @rake_wrapper[file].clear_actions if @first_run == false && @project_config_manager.test_defines_changed + @rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed + end + end + def invoke_test_mocks(mocks) @dependinator.enhance_mock_dependencies( mocks ) mocks.each { |mock| - @rake_wrapper[mock].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( mock ) @rake_wrapper[mock].invoke } end def invoke_test_runner(runner) @dependinator.enhance_runner_dependencies( runner ) - @rake_wrapper[runner].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( runner ) @rake_wrapper[runner].invoke end def invoke_test_shallow_include_lists(files) @dependinator.enhance_shallow_include_lists_dependencies( files ) par_map(PROJECT_COMPILE_THREADS, files) do |file| - @rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( file ) @rake_wrapper[file].invoke end end @@ -72,7 +78,7 @@ class TaskInvoker def invoke_test_preprocessed_files(files) @dependinator.enhance_preprocesed_file_dependencies( files ) par_map(PROJECT_COMPILE_THREADS, files) do |file| - @rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( file ) @rake_wrapper[file].invoke end end @@ -80,14 +86,14 @@ class TaskInvoker def invoke_test_dependencies_files(files) @dependinator.enhance_dependencies_dependencies( files ) par_map(PROJECT_COMPILE_THREADS, files) do |file| - @rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( file ) @rake_wrapper[file].invoke end end def invoke_test_objects(objects) par_map(PROJECT_COMPILE_THREADS, objects) do |object| - @rake_wrapper[object].reenable if @first_run == false && @project_config_manager.test_defines_changed + reset_rake_task_for_changed_defines( object ) @rake_wrapper[object].invoke end end @@ -98,7 +104,6 @@ class TaskInvoker def invoke_test_results(result) @dependinator.enhance_results_dependencies( result ) - @rake_wrapper[result].reenable if @first_run == false && @project_config_manager.test_defines_changed @rake_wrapper[result].invoke end diff --git a/test/vendor/ceedling/lib/ceedling/tasks_base.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_base.rake similarity index 66% rename from test/vendor/ceedling/lib/ceedling/tasks_base.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_base.rake index 8c8253099..a35cde75e 100644 --- a/test/vendor/ceedling/lib/ceedling/tasks_base.rake +++ b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_base.rake @@ -4,28 +4,10 @@ require 'ceedling/version' desc "Display build environment version info." task :version do - puts " Ceedling:: #{Ceedling::Version::CEEDLING}" - - [ - ['CException', File.join( CEEDLING_VENDOR, CEXCEPTION_ROOT_PATH)], - [' CMock', File.join( CEEDLING_VENDOR, CMOCK_ROOT_PATH)], - [' Unity', File.join( CEEDLING_VENDOR, UNITY_ROOT_PATH)], - ].each do |tool| - name = tool[0] - base_path = tool[1] - - version_string = begin - @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip - rescue - "UNKNOWN" - end - build_string = begin - @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip - rescue - "UNKNOWN" - end - puts "#{name}:: #{version_string.empty? ? '#.#.' : (version_string + '.')}#{build_string.empty? ? '?' : build_string}" - end + puts " Ceedling:: #{Ceedling::Version::CEEDLING}" + puts " Unity:: #{Ceedling::Version::UNITY}" + puts " CMock:: #{Ceedling::Version::CMOCK}" + puts " CException:: #{Ceedling::Version::CEXCEPTION}" end desc "Set verbose output (silent:[#{Verbosity::SILENT}] - obnoxious:[#{Verbosity::OBNOXIOUS}])." @@ -65,6 +47,12 @@ task :sanity_checks, :level do |t, args| @ceedling[:configurator].sanity_checks = check_level end +# non advertised catch for calling upgrade in the wrong place +task :upgrade do + puts "WARNING: You're currently IN your project directory. Take a step out and try" + puts "again if you'd like to perform an upgrade." +end + # list expanded environment variables if (not ENVIRONMENT.empty?) desc "List all configured environment variables." @@ -73,7 +61,7 @@ task :environment do ENVIRONMENT.each do |env| env.each_key do |key| name = key.to_s.upcase - env_list.push(" - #{name}: \"#{env[key]}\"") + env_list.push(" - #{name}: \"#{env[key]}\"") end end env_list.sort.each do |env_line| @@ -88,7 +76,7 @@ namespace :options do option = File.basename(option_path, '.yml') desc "Merge #{option} project options." - task option.downcase.to_sym do + task option.to_sym do hash = @ceedling[:project_config_manager].merge_options( @ceedling[:setupinator].config_hash, option_path ) @ceedling[:setupinator].do_setup( hash ) if @ceedling[:configurator].project_release_build @@ -97,6 +85,23 @@ namespace :options do end end + # This is to give nice errors when typing options + rule /^options:.*/ do |t, args| + filename = t.to_s.split(':')[-1] + '.yml' + filelist = COLLECTION_PROJECT_OPTIONS.map{|s| File.basename(s) } + @ceedling[:file_finder].find_file_from_list(filename, filelist, :error) + end + + # This will output the fully-merged tools options to their own project.yml file + desc "Export tools options to a new project file" + task :export, :filename do |t, args| + outfile = args.filename || 'tools.yml' + toolcfg = {} + @ceedling[:configurator].project_config_hash.each_pair do |k,v| + toolcfg[k] = v if (k.to_s[0..5] == 'tools_') + end + File.open(outfile,'w') {|f| f << toolcfg.to_yaml({:indentation => 2})} + end end diff --git a/test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake similarity index 74% rename from test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake index 58fa6511f..7b950ca0b 100644 --- a/test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake +++ b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_filesystem.rake @@ -45,26 +45,35 @@ task(:clobber => [:clean]) do @ceedling[:streaminator].stdout_puts("\nClobbering all generated files...\n(For large projects, this task may take a long time to complete)\n\n") begin CLOBBER.each { |fn| REMOVE_FILE_PROC.call(fn) } + @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:dependinator].touch_force_rebuild_files rescue end end - +# create a directory task for each of the paths, so we know how to build them PROJECT_BUILD_PATHS.each { |path| directory(path) } -# create directories that hold build output and generated files & touching rebuild dependency sources -task(:directories => PROJECT_BUILD_PATHS) { @ceedling[:dependinator].touch_force_rebuild_files } +# create a single directory task which verifies all the others get built +task :directories => PROJECT_BUILD_PATHS +# when the force file doesn't exist, it probably means we clobbered or are on a fresh +# install. In either case, stuff was deleted, so assume we want to rebuild it all +file @ceedling[:configurator].project_test_force_rebuild_filepath do + unless File.exists?(@ceedling[:configurator].project_test_force_rebuild_filepath) + @ceedling[:dependinator].touch_force_rebuild_files + end +end # list paths discovered at load time namespace :paths do - - paths = @ceedling[:setupinator].config_hash[:paths] - paths.each_key do |section| - name = section.to_s.downcase + standard_paths = ['test','source','include'] + paths = @ceedling[:setupinator].config_hash[:paths].keys.map{|n| n.to_s.downcase} + paths = (paths + standard_paths).uniq + paths.each do |name| path_list = Object.const_get("COLLECTION_PATHS_#{name.upcase}") - if (path_list.size != 0) + if (path_list.size != 0) || (standard_paths.include?(name)) desc "List all collected #{name} paths." task(name.to_sym) { puts "#{name} paths:"; path_list.sort.each {|path| puts " - #{path}" } } end @@ -77,10 +86,11 @@ end namespace :files do categories = [ - ['test', COLLECTION_ALL_TESTS], - ['source', COLLECTION_ALL_SOURCE], - ['header', COLLECTION_ALL_HEADERS] - ] + ['test', COLLECTION_ALL_TESTS], + ['source', COLLECTION_ALL_SOURCE], + ['include', COLLECTION_ALL_HEADERS], + ['support', COLLECTION_ALL_SUPPORT] + ] using_assembly = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) || (defined?(RELEASE_BUILD_USE_ASSEMBLY) && RELEASE_BUILD_USE_ASSEMBLY) diff --git a/test/vendor/ceedling/lib/ceedling/tasks_release.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_release.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tasks_release.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_release.rake diff --git a/test/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_release_deep_dependencies.rake diff --git a/test/vendor/ceedling/lib/ceedling/tasks_tests.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_tests.rake similarity index 86% rename from test/vendor/ceedling/lib/ceedling/tasks_tests.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_tests.rake index 5d09c1aff..6c51ebcc9 100644 --- a/test/vendor/ceedling/lib/ceedling/tasks_tests.rake +++ b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_tests.rake @@ -1,13 +1,15 @@ require 'ceedling/constants' -task :test => [:directories] do +task :test_deps => [:directories] + +task :test => [:test_deps] do Rake.application['test:all'].invoke end namespace TEST_SYM do desc "Run all unit tests (also just 'test' works)." - task :all => [:directories] do + task :all => [:test_deps] do @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS) end @@ -21,17 +23,17 @@ namespace TEST_SYM do end desc "Run tests for changed files." - task :delta => [:directories] do + task :delta => [:test_deps] do @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:force_run => false}) end desc "Just build tests without running." - task :build_only => [:directories] do + task :build_only => [:test_deps] do @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:build_only => true}) end desc "Run tests by matching regular expression pattern." - task :pattern, [:regex] => [:directories] do |t, args| + task :pattern, [:regex] => [:test_deps] do |t, args| matches = [] COLLECTION_ALL_TESTS.each { |test| matches << test if (test =~ /#{args.regex}/) } @@ -44,7 +46,7 @@ namespace TEST_SYM do end desc "Run tests whose test path contains [dir] or [dir] substring." - task :path, [:dir] => [:directories] do |t, args| + task :path, [:dir] => [:test_deps] do |t, args| matches = [] COLLECTION_ALL_TESTS.each { |test| matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/')) } diff --git a/test/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_tests_deep_dependencies.rake diff --git a/test/vendor/ceedling/lib/ceedling/tasks_vendor.rake b/test/unit-test/vendor/ceedling/lib/ceedling/tasks_vendor.rake similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tasks_vendor.rake rename to test/unit-test/vendor/ceedling/lib/ceedling/tasks_vendor.rake diff --git a/test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb b/test/unit-test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb similarity index 75% rename from test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb index 50cc7c041..393b0be8b 100644 --- a/test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/test_includes_extractor.rb @@ -19,6 +19,11 @@ class TestIncludesExtractor gather_and_store_includes( test, extract_from_file(test) ) end + # open, scan for, and sort & store includes of test file + def parse_test_file_source_include(test) + return extract_source_include_from_file(test) + end + # mocks with no file extension def lookup_raw_mock_list(test) file_key = form_file_key(test) @@ -65,6 +70,27 @@ class TestIncludesExtractor return includes.uniq end + def extract_source_include_from_file(file) + source_includes = [] + source_extension = @configurator.extension_source + + contents = @file_wrapper.read(file) + + # remove line comments + contents = contents.gsub(/\/\/.*$/, '') + # remove block comments + contents = contents.gsub(/\/\*.*?\*\//m, '') + + contents.split("\n").each do |line| + # look for include statement + scan_results = line.scan(/#include\s+\"\s*(.+#{'\\'+source_extension})\s*\"/) + + source_includes << scan_results[0][0] if (scan_results.size > 0) + end + + return source_includes.uniq + end + def gather_and_store_includes(file, includes) mock_prefix = @configurator.cmock_mock_prefix header_extension = @configurator.extension_header diff --git a/test/vendor/ceedling/lib/ceedling/test_invoker.rb b/test/unit-test/vendor/ceedling/lib/ceedling/test_invoker.rb similarity index 60% rename from test/vendor/ceedling/lib/ceedling/test_invoker.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/test_invoker.rb index 652cb318b..ae686a110 100644 --- a/test/vendor/ceedling/lib/ceedling/test_invoker.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/test_invoker.rb @@ -23,49 +23,24 @@ class TestInvoker @mocks = [] end - def get_test_definition_str(test) - return "-D" + File.basename(test, File.extname(test)).upcase.sub(/@.*$/, "") - end - - def get_tools_compilers - tools_compilers = Hash.new - tools_compilers["for unit test"] = TOOLS_TEST_COMPILER if defined? TOOLS_TEST_COMPILER - tools_compilers["for gcov"] = TOOLS_GCOV_COMPILER if defined? TOOLS_GCOV_COMPILER - return tools_compilers - end - - def add_test_definition(test) - test_definition_str = get_test_definition_str(test) - get_tools_compilers.each do |tools_compiler_key, tools_compiler_value| - tools_compiler_value[:arguments].push("-D#{File.basename(test, ".*").strip.upcase.sub(/@.*$/, "")}") - @streaminator.stdout_puts("Add the definition value in the build option #{tools_compiler_value[:arguments][-1]} #{tools_compiler_key}", Verbosity::OBNOXIOUS) - end - end - - def delete_test_definition(test) - test_definition_str = get_test_definition_str(test) - get_tools_compilers.each do |tools_compiler_key, tools_compiler_value| - num_options = tools_compiler_value[:arguments].size - @streaminator.stdout_puts("Delete the definition value in the build option #{tools_compiler_value[:arguments][-1]} #{tools_compiler_key}", Verbosity::OBNOXIOUS) - tools_compiler_value[:arguments].delete_if{|i| i == test_definition_str} - if num_options > tools_compiler_value[:arguments].size + 1 - @streaminator.stderr_puts("WARNING: duplicated test definition.") - end - end - end # Convert libraries configuration form YAML configuration # into a string that can be given to the compiler. def convert_libraries_to_arguments() - if @configurator.project_config_hash.has_key?(:libraries_test) - lib_args = @configurator.project_config_hash[:libraries_test] - lib_args.flatten! - lib_flag = @configurator.project_config_hash[:libraries_flag] - lib_args.map! {|v| lib_flag.gsub(/\$\{1\}/, v) } if (defined? lib_flag) - return lib_args + args = ((@configurator.project_config_hash[:libraries_test] || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten + if (defined? LIBRARIES_FLAG) + args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) } end + return args end + def get_library_paths_to_arguments() + paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : [] + if (defined? LIBRARIES_PATH_FLAG) + paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) } + end + return paths + end def setup_and_invoke(tests, context=TEST_SYM, options={:force_run => true, :build_only => false}) @@ -83,18 +58,25 @@ class TestInvoker test_name ="#{File.basename(test)}".chomp('.c') def_test_key="defines_#{test_name.downcase}" - # Re-define the project out path and pre-processor defines. - if @configurator.project_config_hash.has_key?(def_test_key.to_sym) - @project_config_manager.test_config_changed + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition defs_bkp = Array.new(COLLECTION_DEFINES_TEST_AND_VENDOR) - printf " ************** Specific test definitions for #{test_name} !!! \n" - tst_defs_cfg = @configurator.project_config_hash[def_test_key.to_sym] + tst_defs_cfg = Array.new(defs_bkp) + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + tst_defs_cfg.replace(@configurator.project_config_hash[def_test_key.to_sym]) + tst_defs_cfg .concat(COLLECTION_DEFINES_VENDOR) if COLLECTION_DEFINES_VENDOR + end + if @configurator.defines_use_test_definition + tst_defs_cfg << File.basename(test, ".*").strip.upcase.sub(/@.*$/, "") + end + COLLECTION_DEFINES_TEST_AND_VENDOR.replace(tst_defs_cfg) + end + # redefine the project out path and preprocessor defines + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + @streaminator.stdout_puts("Updating test definitions for #{test_name}", Verbosity::NORMAL) orig_path = @configurator.project_test_build_output_path @configurator.project_config_hash[:project_test_build_output_path] = File.join(@configurator.project_test_build_output_path, test_name) @file_wrapper.mkdir(@configurator.project_test_build_output_path) - COLLECTION_DEFINES_TEST_AND_VENDOR.replace(tst_defs_cfg) - # printf " * new defines = #{COLLECTION_DEFINES_TEST_AND_VENDOR}\n" end # collect up test fixture pieces & parts @@ -103,16 +85,15 @@ class TestInvoker sources = @test_invoker_helper.extract_sources( test ) extras = @configurator.collection_test_fixture_extra_link_objects core = [test] + mock_list + sources - objects = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras ) + objects = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras ).uniq results_pass = @file_path_utils.form_pass_results_filepath( test ) results_fail = @file_path_utils.form_fail_results_filepath( test ) - @project_config_manager.process_test_defines_change(sources) + # identify all the objects shall not be linked and then remove them from objects list. + no_link_objects = @file_path_utils.form_test_build_objects_filelist(@preprocessinator.preprocess_shallow_source_includes( test )) + objects = objects.uniq - no_link_objects - # add the definition value in the build option for the unit test - if @configurator.defines_use_test_definition - add_test_definition(test) - end + @project_config_manager.process_test_defines_change(@project_config_manager.filter_internal_sources(sources)) # clean results files so we have a missing file with which to kick off rake's dependency rules @test_invoker_helper.clean_results( {:pass => results_pass, :fail => results_fail}, options ) @@ -129,7 +110,7 @@ class TestInvoker @dependinator.enhance_test_build_object_dependencies( objects ) # associate object files with executable - @dependinator.setup_test_executable_dependencies( test, objects ) + @dependinator.enhance_test_executable_dependencies( test, objects ) # build test objects @task_invoker.invoke_test_objects( objects ) @@ -146,18 +127,14 @@ class TestInvoker rescue => e @build_invoker_utils.process_exception( e, context ) ensure - # delete the definition value in the build option for the unit test - if @configurator.defines_use_test_definition - delete_test_definition(test) - end @plugin_manager.post_test( test ) # restore the project test defines - if @configurator.project_config_hash.has_key?(def_test_key.to_sym) - # @configurator.project_config_hash[:defines_test] = + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition COLLECTION_DEFINES_TEST_AND_VENDOR.replace(defs_bkp) - # printf " ---- Restored defines at #{defs_bkp}" - @configurator.project_config_hash[:project_test_build_output_path] = orig_path - printf " ************** Restored defines and build path\n" + if @configurator.project_config_hash.has_key?(def_test_key.to_sym) + @configurator.project_config_hash[:project_test_build_output_path] = orig_path + @streaminator.stdout_puts("Restored defines and build path to standard", Verbosity::NORMAL) + end end end diff --git a/test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb similarity index 97% rename from test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb index b0a223f56..403d93e3e 100644 --- a/test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb +++ b/test/unit-test/vendor/ceedling/lib/ceedling/test_invoker_helper.rb @@ -11,7 +11,7 @@ class TestInvokerHelper def process_deep_dependencies(files) return if (not @configurator.project_use_deep_dependencies) - dependencies_list = @file_path_utils.form_test_dependencies_filelist( files ) + dependencies_list = @file_path_utils.form_test_dependencies_filelist( files ).uniq if @configurator.project_generate_deep_dependencies @task_invoker.invoke_test_dependencies_files( dependencies_list ) diff --git a/test/vendor/ceedling/lib/ceedling/tool_executor.rb b/test/unit-test/vendor/ceedling/lib/ceedling/tool_executor.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tool_executor.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/tool_executor.rb diff --git a/test/vendor/ceedling/lib/ceedling/tool_executor_helper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/tool_executor_helper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/tool_executor_helper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/tool_executor_helper.rb diff --git a/test/vendor/ceedling/lib/ceedling/verbosinator.rb b/test/unit-test/vendor/ceedling/lib/ceedling/verbosinator.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/verbosinator.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/verbosinator.rb diff --git a/test/unit-test/vendor/ceedling/lib/ceedling/version.rb b/test/unit-test/vendor/ceedling/lib/ceedling/version.rb new file mode 100644 index 000000000..ebda10b78 --- /dev/null +++ b/test/unit-test/vendor/ceedling/lib/ceedling/version.rb @@ -0,0 +1,54 @@ + +# @private +module Ceedling + module Version + { "UNITY" => File.join("unity","src","unity.h"), + "CMOCK" => File.join("cmock","src","cmock.h"), + "CEXCEPTION" => File.join("c_exception","lib","CException.h") + }.each_pair do |name, path| + # Check for local or global version of vendor directory in order to look up versions + path1 = File.expand_path( File.join("..","..","vendor",path) ) + path2 = File.expand_path( File.join(File.dirname(__FILE__),"..","..","vendor",path) ) + filename = if (File.exists?(path1)) + path1 + elsif (File.exists?(path2)) + path2 + elsif File.exists?(CEEDLING_VENDOR) + path3 = File.expand_path( File.join(CEEDLING_VENDOR,path) ) + if (File.exists?(path3)) + path3 + else + basepath = File.join( CEEDLING_VENDOR, path.split(/\\\//)[0], 'release') + begin + [ @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip, + @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip ].join('.') + rescue + "#{name}" + end + end + else + module_eval("#{name} = 'unknown'") + continue + end + + # Actually look up the versions + a = [0,0,0] + begin + File.readlines(filename).each do |line| + ["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i| + m = line.match(/#{name}_#{field}\s+(\d+)/) + a[i] = m[1] unless (m.nil?) + end + end + rescue + abort("Can't collect data for vendor component: \"#{filename}\" . \nPlease check your setup.") + end + + # splat it to return the final value + eval("#{name} = '#{a.join(".")}'") + end + + GEM = "0.31.1" + CEEDLING = GEM + end +end diff --git a/test/vendor/ceedling/lib/ceedling/yaml_wrapper.rb b/test/unit-test/vendor/ceedling/lib/ceedling/yaml_wrapper.rb similarity index 100% rename from test/vendor/ceedling/lib/ceedling/yaml_wrapper.rb rename to test/unit-test/vendor/ceedling/lib/ceedling/yaml_wrapper.rb diff --git a/test/vendor/ceedling/plugins/beep/README.md b/test/unit-test/vendor/ceedling/plugins/beep/README.md similarity index 100% rename from test/vendor/ceedling/plugins/beep/README.md rename to test/unit-test/vendor/ceedling/plugins/beep/README.md diff --git a/test/vendor/ceedling/plugins/beep/lib/beep.rb b/test/unit-test/vendor/ceedling/plugins/beep/lib/beep.rb similarity index 100% rename from test/vendor/ceedling/plugins/beep/lib/beep.rb rename to test/unit-test/vendor/ceedling/plugins/beep/lib/beep.rb diff --git a/test/unit-test/vendor/ceedling/plugins/bullseye/README.md b/test/unit-test/vendor/ceedling/plugins/bullseye/README.md new file mode 100644 index 000000000..ab0b53b45 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/bullseye/README.md @@ -0,0 +1,76 @@ +ceedling-bullseye +================= + +# Plugin Overview + +Plugin for integrating Bullseye code coverage tool into Ceedling projects. +This plugin requires a working license to Bullseye code coverage tools. The tools +must be within the path or the path should be added to the environment in the +`project.yml file`. + +## Configuration + +The bullseye plugin supports configuration options via your `project.yml` provided +by Ceedling. The following is a typical configuration example: + +``` +:bullseye: + :auto_license: TRUE +:plugins: + :bullseye_lib_path: [] +:paths: + :bullseye_toolchain_include: [] + +:tools: + :bullseye_instrumentation: + :executable: covc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - ${1} + :bullseye_compiler: + :executable: gcc + :arguments: + - -g + - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR + - -I"$": COLLECTION_PATHS_BULLSEYE_TOOLCHAIN_INCLUDE + - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR + - -DBULLSEYE_COMPILER + - -c "${1}" + - -o "${2}" + :bullseye_linker: + :executable: gcc + :arguments: + - ${1} + - -o ${2} + - -L$: PLUGINS_BULLSEYE_LIB_PATH + - -lcov + :bullseye_fixture: + :executable: ${1} + :bullseye_report_covsrc: + :executable: covsrc + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - -q + - -w140 + :bullseye_report_covfn: + :executable: covfn + :stderr_redirect: :auto + :arguments: + - '--file $': ENVIRONMENT_COVFILE + - --width 120 + - --no-source + - '"${1}"' + :bullseye_browser: + :executable: CoverageBrowser + :background_exec: :auto + :optional: TRUE + :arguments: + - '"$"': ENVIRONMENT_COVFILE +``` + +## Example Usage + +```sh +ceedling bullseye:all utils:bullseye +``` diff --git a/test/vendor/ceedling/plugins/bullseye/assets/template.erb b/test/unit-test/vendor/ceedling/plugins/bullseye/assets/template.erb similarity index 100% rename from test/vendor/ceedling/plugins/bullseye/assets/template.erb rename to test/unit-test/vendor/ceedling/plugins/bullseye/assets/template.erb diff --git a/test/vendor/ceedling/plugins/bullseye/bullseye.rake b/test/unit-test/vendor/ceedling/plugins/bullseye/bullseye.rake similarity index 93% rename from test/vendor/ceedling/plugins/bullseye/bullseye.rake rename to test/unit-test/vendor/ceedling/plugins/bullseye/bullseye.rake index a7bbebe3a..11073e786 100644 --- a/test/vendor/ceedling/plugins/bullseye/bullseye.rake +++ b/test/unit-test/vendor/ceedling/plugins/bullseye/bullseye.rake @@ -32,12 +32,16 @@ rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ end rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| + lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() @ceedling[:generator].generate_executable_file( TOOLS_BULLSEYE_LINKER, BULLSEYE_SYM, bin_file.prerequisites, bin_file.name, - @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name) + @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name), + lib_args, + lib_paths ) end @@ -69,7 +73,7 @@ namespace BULLSEYE_SYM do task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{BULLSEYE_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") desc 'Run code coverage for all tests' - task all: [:directories] do + task all: [:test_deps] do @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM) @@ -81,18 +85,18 @@ namespace BULLSEYE_SYM do message = "\nOops! '#{BULLSEYE_ROOT_NAME}:*' isn't a real task. " + "Use a real test or source file name (no path) in place of the wildcard.\n" + "Example: rake #{BULLSEYE_ROOT_NAME}:foo.c\n\n" - + @ceedling[:streaminator].stdout_puts( message ) end - + desc 'Run tests by matching regular expression pattern.' - task :pattern, [:regex] => [:directories] do |_t, args| + task :pattern, [:regex] => [:test_deps] do |_t, args| matches = [] - + COLLECTION_ALL_TESTS.each do |test| matches << test if test =~ /#{args.regex}/ end - + if !matches.empty? @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[BULLSEYE_SYM].enableBullseye(true) @@ -104,13 +108,13 @@ namespace BULLSEYE_SYM do end desc 'Run tests whose test path contains [dir] or [dir] substring.' - task :path, [:dir] => [:directories] do |_t, args| + task :path, [:dir] => [:test_deps] do |_t, args| matches = [] - + COLLECTION_ALL_TESTS.each do |test| matches << test if File.dirname(test).include?(args.dir.tr('\\', '/')) end - + if !matches.empty? @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[BULLSEYE_SYM].enableBullseye(true) @@ -122,13 +126,13 @@ namespace BULLSEYE_SYM do end desc 'Run code coverage for changed files' - task delta: [:directories] do + task delta: [:test_deps] do @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM, {:force_run => false}) @ceedling[:configurator].restore_config end - + # use a rule to increase efficiency for large projects # bullseye test tasks by regex rule(/^#{BULLSEYE_TASK_ROOT}\S+$/ => [ @@ -138,7 +142,7 @@ namespace BULLSEYE_SYM do @ceedling[:file_finder].find_test_from_file_path(test) end ]) do |test| - @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:rake_wrapper][:test_deps].invoke @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[:test_invoker].setup_and_invoke([test.source], BULLSEYE_SYM) @@ -159,11 +163,11 @@ end end namespace UTILS_SYM do - + desc "Open Bullseye code coverage browser" task BULLSEYE_SYM do command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BROWSER, []) @ceedling[:tool_executor].exec(command[:line], command[:options]) end - + end diff --git a/test/vendor/ceedling/plugins/bullseye/config/defaults.yml b/test/unit-test/vendor/ceedling/plugins/bullseye/config/defaults.yml similarity index 100% rename from test/vendor/ceedling/plugins/bullseye/config/defaults.yml rename to test/unit-test/vendor/ceedling/plugins/bullseye/config/defaults.yml diff --git a/test/vendor/ceedling/plugins/bullseye/lib/bullseye.rb b/test/unit-test/vendor/ceedling/plugins/bullseye/lib/bullseye.rb similarity index 100% rename from test/vendor/ceedling/plugins/bullseye/lib/bullseye.rb rename to test/unit-test/vendor/ceedling/plugins/bullseye/lib/bullseye.rb diff --git a/test/unit-test/vendor/ceedling/plugins/colour_report/README.md b/test/unit-test/vendor/ceedling/plugins/colour_report/README.md new file mode 100644 index 000000000..4e0fcd45e --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/colour_report/README.md @@ -0,0 +1,20 @@ +ceedling-colour-report +====================== + +## Overview + +The colour_report replaces the normal ceedling "pretty" output with +a colorized variant, in order to make the results easier to read from +a standard command line. This is very useful on developer machines, but +can occasionally cause problems with parsing on CI servers. + +## Setup + +Enable the plugin in your project.yml by adding `colour_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - colour_report +``` diff --git a/test/vendor/ceedling/plugins/colour_report/lib/colour_report.rb b/test/unit-test/vendor/ceedling/plugins/colour_report/lib/colour_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/colour_report/lib/colour_report.rb rename to test/unit-test/vendor/ceedling/plugins/colour_report/lib/colour_report.rb diff --git a/test/vendor/ceedling/plugins/command_hooks/README.md b/test/unit-test/vendor/ceedling/plugins/command_hooks/README.md similarity index 100% rename from test/vendor/ceedling/plugins/command_hooks/README.md rename to test/unit-test/vendor/ceedling/plugins/command_hooks/README.md diff --git a/test/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb b/test/unit-test/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb similarity index 100% rename from test/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb rename to test/unit-test/vendor/ceedling/plugins/command_hooks/lib/command_hooks.rb diff --git a/test/unit-test/vendor/ceedling/plugins/compile_commands_json/README.md b/test/unit-test/vendor/ceedling/plugins/compile_commands_json/README.md new file mode 100644 index 000000000..ea80b7397 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/compile_commands_json/README.md @@ -0,0 +1,29 @@ +compile_commands_json +===================== + +## Overview + +Syntax highlighting and code completion are hard. Historically each editor or IDE has implemented their own and then competed amongst themselves to offer the best experience for developers. Often developers would still to an IDE that felt cumbersome and slow just because it had the best syntax highlighting on the market. If doing it for one language is hard (and it is) imagine doing it for dozens of them. Imagine a full stack developer who has to work with CSS, HTML, JavaScript and some Ruby - they need excellent support in all those languages which just made things even harder. + +In June of 2016, Microsoft with Red Hat and Codenvy got together to create a standard called the Language Server Protocol (LSP). The idea was simple, by standardising on one protocol, all the IDEs and editors out there would only have to support LSP, and not have custom plugins for each language. In turn, the backend code that actually does the highlighting can be written once and used by any IDE that supports LSP. Many editors already support it such as Sublime Text, vim and emacs. This means that if you're using a crufty old IDE or worse, you're using a shiny new editor without code completion, then this could be just the upgrade you're looking for! + +For C and C++ projects, many people use the `clangd` backend. So that it can do things like "go to definition", `clangd` needs to know how to build the project so that it can figure out all the pieces to the puzzle. There are manual tools such as `bear` which can be run with `gcc` or `clang` to extract this information it has a big limitation in that if run with `ceedling release` you won't get any auto completion for Unity and you'll also get error messages reported by your IDE because of what it perceives as missing headers. If you do the same with `ceedling test` now you get Unity but you might miss things that are only seen in the release build. + +This plugin resolves that issue. As it is run by Ceedling, it has access to all the build information it needs to create the perfect `compile_commands.json`. Once enabled, this plugin will generate that file and place it in `./build/artifacts/compile_commands.json`. `clangd` will search your project for this file, but it is easier to symlink it into the root directory (for example `ln -s ./build/artifacts/compile_commands.json`. + +For more information on LSP and to find out if your editor supports it, check out https://langserver.org/ + +## Setup + +Enable the plugin in your project.yml by adding `compile_commands_json` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - compile_commands_json +``` + +## Configuration + +There is no additional configuration necessary to run this plugin. diff --git a/test/unit-test/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb b/test/unit-test/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb new file mode 100644 index 000000000..269cea4de --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/compile_commands_json/lib/compile_commands_json.rb @@ -0,0 +1,35 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'json' + +class CompileCommandsJson < Plugin + def setup + @fullpath = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, "compile_commands.json") + @database = if (File.exists?(@fullpath)) + JSON.parse( File.read(@fullpath) ) + else + [] + end + end + + def post_compile_execute(arg_hash) + + # Create the new Entry + value = { + "directory" => Dir.pwd, + "command" => arg_hash[:shell_command], + "file" => arg_hash[:source] + } + + # Determine if we're updating an existing file description or adding a new one + index = @database.index {|h| h["file"] == arg_hash[:source]} + if index + @database[index] = value + else + @database << value + end + + # Update the Actual compile_commands.json file + File.open(@fullpath,'w') {|f| f << JSON.pretty_generate(@database)} + end +end diff --git a/test/unit-test/vendor/ceedling/plugins/dependencies/README.md b/test/unit-test/vendor/ceedling/plugins/dependencies/README.md new file mode 100644 index 000000000..256467dfa --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/dependencies/README.md @@ -0,0 +1,254 @@ +ceedling-dependencies +===================== + +Plugin for supporting release dependencies. It's rare for an embedded project to +be built completely free of other libraries and modules. Some of these may be +standard internal libraries. Some of these may be 3rd party libraries. In either +case, they become part of the project's ecosystem. + +This plugin is intended to make that relationship easier. It allows you to specify +a source for dependencies. If required, it will automatically grab the appropriate +version of that dependency. + +Most 3rd party libraries have a method of building already in place. While we'd +love to convert the world to a place where everything downloads with a test suite +in Ceedling, that's not likely to happen anytime soon. Until then, this plugin +will allow the developer to specify what calls Ceedling should make to oversee +the build process of those third party utilities. Are they using Make? CMake? A +custom series of scripts that only a mad scientist could possibly understand? No +matter. Ceedling has you covered. Just specify what should be called, and Ceedling +will make it happen whenever it notices that the output artifacts are missing. + +Output artifacts? Sure! Things like static and dynamic libraries, or folders +containing header files that might want to be included by your release project. + +So how does all this magic work? + +First, you need to add the `:dependencies` plugin to your list. Then, we'll add a new +section called :dependencies. There, you can list as many dependencies as you desire. Each +has a series of fields which help Ceedling to understand your needs. Many of them are +optional. If you don't need that feature, just don't include it! In the end, it'll look +something like this: + +``` +:dependencies: + :libraries: + - :name: WolfSSL + :source_path: third_party/wolfssl/source + :build_path: third_party/wolfssl/build + :artifact_path: third_party/wolfssl/install + :fetch: + :method: :zip + :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip + :environment: + - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE + :build: + - "autoreconf -i" + - "./configure --enable-tls13 --enable-singlethreaded" + - make + - make install + :artifacts: + :static_libraries: + - lib/wolfssl.a + :dynamic_libraries: + - lib/wolfssl.so + :includes: + - include/** +``` + +Let's take a deeper look at each of these features. + +The Starting Dash & Name +------------------------ + +Yes, that opening dash tells the dependencies plugin that the rest of these fields +belong to our first dependency. If we had a second dependency, we'd have another +dash, lined up with the first, and followed by all the fields indented again. + +By convention, we use the `:name` field as the first field for each tool. Ceedling +honestly doesn't care which order the fields are given... but as humans, it makes +it easier for us to see the name of each dependency with starting dash. + +The name field is only used to print progress while we're running Ceedling. You may +call the name of the field whatever you wish. + +Working Folders +--------------- + +The `:source_path` field allows us to specify where the source code for each of our +dependencies is stored. If fetching the dependency from elsewhere, it will be fetched +to this location. All commands to build this dependency will be executed from +this location (override this by specifying a `:build_path`). Finally, the output +artifacts will be referenced to this location (override this by specifying a `:artifact_path`) + +If unspecified, the `:source_path` will be `dependencies\dep_name` where `dep_name` +is the name specified in `:name` above (with special characters removed). It's best, +though, if you specify exactly where you want your dependencies to live. + +If the dependency is directly included in your project (you've specified `:none` as the +`:method` for fetching), then `:source_path` should be where your Ceedling can find the +source for your dependency in you repo. + +All artifacts are relative to the `:artifact_path` (which defaults to be the same as +`:source_path`) + +Fetching Dependencies +--------------------- + +The `:dependencies` plugin supports the ability to automatically fetch your dependencies +for you... using some common methods of fetching source. This section contains only a +couple of fields: + +- `:method` -- This is the method that this dependency is fetched. + - `:none` -- This tells Ceedling that the code is already included in the project. + - `:zip` -- This tells Ceedling that we want to unpack a zip file to our source path. + - `:git` -- This tells Ceedling that we want to clone a git repo to our source path. + - `:svn` -- This tells Ceedling that we want to checkout a subversion repo to our source path. + - `:custom` -- This tells Ceedling that we want to use a custom command or commands to fetch the code. +- `:source` -- This is the path or url to fetch code when using the zip or git method. +- `:tag`/`:branch` -- This is the specific tag or branch that you wish to retrieve (git only. optional). +- `:hash` -- This is the specific SHA1 hash you want to fetch (git only. optional, requires a deep clone). +- `:revision` -- This is the specific revision you want to fetch (svn only. optional). +- `:executable` -- This is a list of commands to execute when using the `:custom` method + + +Environment Variables +--------------------- + +Many build systems support customization through environment variables. By specifying +an array of environment variables, Ceedling will customize the shell environment before +calling the build process. + +Environment variables may be specified in three ways. Let's look at one of each: + +``` + :environment: + - ARCHITECTURE=ARM9 + - CFLAGS+=-DADD_AWESOMENESS + - CFLAGS-=-DWASTE +``` + +In the first example, you see the most straightforward method. The environment variable +`ARCHITECTURE` is set to the value `ARM9`. That's it. Simple. + +The next two options modify an existing symbol. In the first one, we use `+=`, which tells +Ceedling to add the define `ADD_AWESOMENESS` to the environment variable `CFLAGS`. The second +tells Ceedling to remove the define `WASTE` from the same environment variable. + +There are a couple of things to note here. + +First, when adding to a variable, Ceedling has no way of knowing +what delimiter you are expecting. In this example you can see we manually added some whitespace. +If we had been modifying `PATH` instead, we might have had to use a `:` on a unux or `;` on +Windows. + +Second, removing an argument will have no effect on the argument if that argument isn't found +precisely. It's case sensitive and the entire string must match. If symbol doesn't already exist, +it WILL after executing this command... however it will be assigned to nothing. + +Building Dependencies +--------------------- + +The heart of the `:dependencies` plugin is the ability for you, the developer, to specify the +build process for each of your dependencies. You will need to have any required tools installed +before using this feature. + +The steps are specified as an array of strings. Ceedling will execute those steps in the order +specified, moving from step to step unless an error is encountered. By the end of the process, +the artifacts should have been created by your process... otherwise an error will be produced. + +Artifacts +--------- + +These are the outputs of the build process. There are there types of artifacts. Any dependency +may have none or some of these. Calling out these files tells Ceedling that they are important. +Your dependency's build process may produce many other files... but these are the files that +Ceedling understands it needs to act on. + +### `static_libraries` + +Specifying one or more static libraries will tell Ceedling where it should find static libraries +output by your build process. These libraries are automatically added to the list of dependencies +and will be linked with the rest of your code to produce the final release. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `dynamic_libraries` + +Specifying one or more dynamic libraries will tell Ceedling where it should find dynamic libraries +output by your build process. These libraries are automatically copied to the same folder as your +final release binary. + +If any of these libraries don't exist, Ceedling will trigger your build process in order for it +to produce them. + +### `includes` + +Often when libraries are built, the same process will output a collection of includes so that +your release code knows how to interact with that library. It's the public API for that library. +By specifying the directories that will contain these includes (don't specify the files themselves, +Ceedling only needs the directories), Ceedling is able to automatically add these to its internal +include list. This allows these files to be used while building your release code, as well we making +them mockable during unit testing. + +### `source` + +It's possible that your external dependency will just produce additional C files as its output. +In this case, Ceedling is able to automatically add these to its internal source list. This allows +these files to be used while building your release code. + +Tasks +----- + +Once configured correctly, the `:dependencies` plugin should integrate seamlessly into your +workflow and you shouldn't have to think about it. In the real world, that doesn't always happen. +Here are a number of tasks that are added or modified by this plugin. + +### `ceedling dependencies:clean` + +This can be issued in order to completely remove the dependency from its source path. On the +next build, it will be refetched and rebuilt from scratch. This can also apply to a particular +dependency. For example, by specifying `dependencies:clean:DepName`. + +### `ceedling dependencies:fetch` + +This can be issued in order to fetch each dependency from its origin. This will have no effect on +dependencies that don't have fetch instructions specified. This can also apply to a particular +dependency. For example, by specifying `dependencies:fetch:DepName`. + +### `ceedling dependencies:make` + +This will force the dependencies to all build. This should happen automatically when a release +has been triggered... but if you're just getting your dependency configured at this moment, you +may want to just use this feature instead. A single dependency can also be built by specifying its +name, like `dependencies:make:MyTunaBoat`. + +### `ceedling dependencies:deploy` + +This will force any dynamic libraries produced by your dependencies to be copied to your release +build directory... just in case you clobbered them. + +### `paths:include` + +Maybe you want to verify that all the include paths are correct. If you query Ceedling with this +request, it will list all the header file paths that it's found, including those produced by +dependencies. + +### `files:include` + +Maybe you want to take that query further and actually get a list of ALL the header files +Ceedling has found, including those belonging to your dependencies. + +Testing +======= + +Hopefully all your dependencies are fully tested... but we can't always depend on that. +In the event that they are tested with Ceedling, you'll probably want to consider using +the `:subprojects` plugin instead of this one. The purpose of this plugin is to pull in +third party code for release... and to provide a mockable interface for Ceedling to use +during its tests of other modules. + +If that's what you're after... you've found the right plugin! + +Happy Testing! diff --git a/test/unit-test/vendor/ceedling/plugins/dependencies/config/defaults.yml b/test/unit-test/vendor/ceedling/plugins/dependencies/config/defaults.yml new file mode 100644 index 000000000..0415f8ea1 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/dependencies/config/defaults.yml @@ -0,0 +1,5 @@ +--- +:dependencies: + :libraries: [] + +... diff --git a/test/unit-test/vendor/ceedling/plugins/dependencies/dependencies.rake b/test/unit-test/vendor/ceedling/plugins/dependencies/dependencies.rake new file mode 100644 index 000000000..08a1a48eb --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/dependencies/dependencies.rake @@ -0,0 +1,147 @@ + +DEPENDENCIES_LIBRARIES.each do |deplib| + + # Look up the name of this dependency library + deplib_name = @ceedling[DEPENDENCIES_SYM].get_name(deplib) + + # Make sure the required working directories exists + # (don't worry about the subdirectories. That's the job of the dep's build tool) + paths = @ceedling[DEPENDENCIES_SYM].get_working_paths(deplib) + paths.each {|path| directory(path) } + task :directories => paths + + all_deps = @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + + # Add a rule for building the actual libraries from dependency list + (@ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + ).each do |libpath| + file libpath do |filetask| + path = filetask.name + + # We double-check that it doesn't already exist, because this process sometimes + # produces multiple files, but they may have already been flagged as invoked + unless (File.exists?(path)) + + # Set Environment Variables, Fetch, and Build + @ceedling[DEPENDENCIES_SYM].set_env_if_required(path) + @ceedling[DEPENDENCIES_SYM].fetch_if_required(path) + @ceedling[DEPENDENCIES_SYM].build_if_required(path) + end + end + end + + # Add a rule for building the source and includes from dependency list + (@ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + + @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + ).each do |libpath| + task libpath do |filetask| + path = filetask.name + + unless (File.file?(path) || File.directory?(path)) + + # Set Environment Variables, Fetch, and Build + @ceedling[DEPENDENCIES_SYM].set_env_if_required(path) + @ceedling[DEPENDENCIES_SYM].fetch_if_required(path) + @ceedling[DEPENDENCIES_SYM].build_if_required(path) + end + end + end + + # Give ourselves a way to trigger individual dependencies + namespace DEPENDENCIES_SYM do + namespace :deploy do + # Add task to directly just build this dependency + task(deplib_name => @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib)) do |t,args| + @ceedling[DEPENDENCIES_SYM].deploy_if_required(deplib_name) + end + end + + namespace :make do + # Add task to directly just build this dependency + task(deplib_name => all_deps) + end + + namespace :clean do + # Add task to directly clobber this dependency + task(deplib_name) do + @ceedling[DEPENDENCIES_SYM].clean_if_required(deplib_name) + end + end + + namespace :fetch do + # Add task to directly clobber this dependency + task(deplib_name) do + @ceedling[DEPENDENCIES_SYM].fetch_if_required(deplib_name) + end + end + end + + # Add source files to our list of things to build during release + source_files = @ceedling[DEPENDENCIES_SYM].get_source_files_for_dependency(deplib) + task PROJECT_RELEASE_BUILD_TARGET => source_files + + # Finally, add the static libraries to our RELEASE build dependency list + static_libs = @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + task RELEASE_SYM => static_libs + + # Add the dynamic libraries to our RELEASE task dependency list so that they will be copied automatically + dynamic_libs = @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + task RELEASE_SYM => dynamic_libs + + # Add the include dirs / files to our list of dependencies for release + headers = @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib) + task RELEASE_SYM => headers + + # Paths to Libraries need to be Added to the Lib Path List + all_libs = static_libs + dynamic_libs + PATHS_LIBRARIES ||= [] + all_libs.each {|lib| PATHS_LIBRARIES << File.dirname(lib) } + PATHS_LIBRARIES.uniq! + PATHS_LIBRARIES.reject!{|s| s.empty?} + + # Libraries Need to be Added to the Library List + LIBRARIES_SYSTEM ||= [] + all_libs.each {|lib| LIBRARIES_SYSTEM << File.basename(lib,'.*').sub(/^lib/,'') } + LIBRARIES_SYSTEM.uniq! + LIBRARIES_SYSTEM.reject!{|s| s.empty?} +end + +# Add any artifact:include or :source folders to our release & test includes paths so linking and mocking work. +@ceedling[DEPENDENCIES_SYM].add_headers_and_sources() + +# Add tasks for building or cleaning ALL depencies +namespace DEPENDENCIES_SYM do + desc "Deploy missing dependencies." + task :deploy => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:deploy:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Build any missing dependencies." + task :make => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:make:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Clean all dependencies." + task :clean => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:clean:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} + + desc "Fetch all dependencies." + task :fetch => DEPENDENCIES_LIBRARIES.map{|deplib| "#{DEPENDENCIES_SYM}:fetch:#{@ceedling[DEPENDENCIES_SYM].get_name(deplib)}"} +end + +namespace :files do + desc "List all collected dependency libraries." + task :dependencies do + puts "dependency files:" + deps = [] + DEPENDENCIES_LIBRARIES.each do |deplib| + deps << @ceedling[DEPENDENCIES_SYM].get_static_libraries_for_dependency(deplib) + deps << @ceedling[DEPENDENCIES_SYM].get_dynamic_libraries_for_dependency(deplib) + end + deps.flatten! + deps.sort.each {|dep| puts " - #{dep}"} + puts "file count: #{deps.size}" + end +end + +# Make sure that we build dependencies before attempting to tackle any of the unit tests +Rake::Task[:test_deps].enhance ['dependencies:make'] diff --git a/test/unit-test/vendor/ceedling/plugins/dependencies/lib/dependencies.rb b/test/unit-test/vendor/ceedling/plugins/dependencies/lib/dependencies.rb new file mode 100644 index 000000000..fc8ae9968 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/dependencies/lib/dependencies.rb @@ -0,0 +1,237 @@ +require 'ceedling/plugin' +require 'ceedling/constants' + +DEPENDENCIES_ROOT_NAME = 'dependencies' +DEPENDENCIES_TASK_ROOT = DEPENDENCIES_ROOT_NAME + ':' +DEPENDENCIES_SYM = DEPENDENCIES_ROOT_NAME.to_sym + +class Dependencies < Plugin + + def setup + @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Set up a fast way to look up dependencies by name or static lib path + @dependencies = {} + @dynamic_libraries = [] + DEPENDENCIES_LIBRARIES.each do |deplib| + + @dependencies[ deplib[:name] ] = deplib.clone + all_deps = get_static_libraries_for_dependency(deplib) + + get_dynamic_libraries_for_dependency(deplib) + + get_include_directories_for_dependency(deplib) + + get_source_files_for_dependency(deplib) + all_deps.each do |key| + @dependencies[key] = @dependencies[ deplib[:name] ] + end + + @dynamic_libraries += get_dynamic_libraries_for_dependency(deplib) + end + end + + def config + updates = { + :collection_paths_include => COLLECTION_PATHS_INCLUDE, + :collection_all_headers => COLLECTION_ALL_HEADERS, + } + + @ceedling[DEPENDENCIES_SYM].get_include_directories_for_dependency(deplib).each do |incpath| + updates[:collection_paths_include] << incpath + Dir[ File.join(incpath, "*#{EXTENSION_HEADER}") ].each do |f| + updates[:collection_all_headers] << f + end + end + + return updates + end + + def get_name(deplib) + raise "Each dependency must have a name!" if deplib[:name].nil? + return deplib[:name].gsub(/\W*/,'') + end + + def get_source_path(deplib) + return deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_build_path(deplib) + return deplib[:build_path] || deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_artifact_path(deplib) + return deplib[:artifact_path] || deplib[:source_path] || File.join('dependencies', get_name(deplib)) + end + + def get_working_paths(deplib) + paths = [deplib[:source_path], deplib[:build_path], deplib[:artifact_paths]].compact.uniq + paths = [ File.join('dependencies', get_name(deplib)) ] if (paths.empty?) + return paths + end + + def get_static_libraries_for_dependency(deplib) + (deplib[:artifacts][:static_libraries] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_dynamic_libraries_for_dependency(deplib) + (deplib[:artifacts][:dynamic_libraries] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_source_files_for_dependency(deplib) + (deplib[:artifacts][:source] || []).map {|path| File.join(get_artifact_path(deplib), path)} + end + + def get_include_directories_for_dependency(deplib) + paths = (deplib[:artifacts][:includes] || []).map {|path| File.join(get_artifact_path(deplib), path)} + @ceedling[:file_system_utils].collect_paths(paths) + end + + def set_env_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + return if (blob[:environment].nil?) + return if (blob[:environment].empty?) + + blob[:environment].each do |e| + m = e.match(/^(\w+)\s*(\+?\-?=)\s*(.*)$/) + unless m.nil? + case m[2] + when "+=" + ENV[m[1]] = (ENV[m[1]] || "") + m[3] + when "-=" + ENV[m[1]] = (ENV[m[1]] || "").gsub(m[3],'') + else + ENV[m[1]] = m[3] + end + end + end + end + + def fetch_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + return if (blob[:fetch].nil?) + return if (blob[:fetch][:method].nil?) + return if (directory(blob[:source_path]) && !Dir.empty?(blob[:source_path])) + + steps = case blob[:fetch][:method] + when :none + return + when :zip + [ "gzip -d #{blob[:fetch][:source]}" ] + when :git + branch = blob[:fetch][:tag] || blob[:fetch][:branch] || '' + branch = ("-b " + branch) unless branch.empty? + unless blob[:fetch][:hash].nil? + # Do a deep clone to ensure the commit we want is available + retval = [ "git clone #{branch} #{blob[:fetch][:source]} ." ] + # Checkout the specified commit + retval << "git checkout #{blob[:fetch][:hash]}" + else + # Do a thin clone + retval = [ "git clone #{branch} --depth 1 #{blob[:fetch][:source]} ." ] + end + when :svn + revision = blob[:fetch][:revision] || '' + revision = ("--revision " + branch) unless branch.empty? + retval = [ "svn checkout #{revision} #{blob[:fetch][:source]} ." ] + retval + when :custom + blob[:fetch][:executable] + else + raise "Unknown fetch method '#{blob[:fetch][:method].to_s}' for dependency '#{blob[:name]}'" + end + + # Perform the actual fetching + @ceedling[:streaminator].stdout_puts("Fetching dependency #{blob[:name]}...", Verbosity::NORMAL) + Dir.chdir(get_source_path(blob)) do + steps.each do |step| + @ceedling[:tool_executor].exec( step ) + end + end + end + + def build_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't clean anything unless we know how to fetch a new copy + if (blob[:build].nil? || blob[:build].empty?) + @ceedling[:streaminator].stdout_puts("Nothing to build for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the build + @ceedling[:streaminator].stdout_puts("Building dependency #{blob[:name]}...", Verbosity::NORMAL) + Dir.chdir(get_build_path(blob)) do + blob[:build].each do |step| + @ceedling[:tool_executor].exec( step ) + end + end + end + + def clean_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't clean anything unless we know how to fetch a new copy + if (blob[:fetch].nil? || blob[:fetch][:method].nil? || (blob[:fetch][:method] == :none)) + @ceedling[:streaminator].stdout_puts("Nothing to clean for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the actual Cleaning + @ceedling[:streaminator].stdout_puts("Cleaning dependency #{blob[:name]}...", Verbosity::NORMAL) + get_working_paths(blob).each do |path| + FileUtils.rm_rf(path) if File.directory?(path) + end + end + + def deploy_if_required(lib_path) + blob = @dependencies[lib_path] + raise "Could not find dependency '#{lib_path}'" if blob.nil? + + # We don't need to deploy anything if there isn't anything to deploy + if (blob[:artifacts].nil? || blob[:artifacts][:dynamic_libraries].nil? || blob[:artifacts][:dynamic_libraries].empty?) + @ceedling[:streaminator].stdout_puts("Nothing to deploy for dependency #{blob[:name]}", Verbosity::NORMAL) + return + end + + # Perform the actual Deploying + @ceedling[:streaminator].stdout_puts("Deploying dependency #{blob[:name]}...", Verbosity::NORMAL) + FileUtils.cp( lib_path, File.dirname(PROJECT_RELEASE_BUILD_TARGET) ) + end + + def add_headers_and_sources() + # Search for header file paths and files to add to our collections + DEPENDENCIES_LIBRARIES.each do |deplib| + get_include_directories_for_dependency(deplib).each do |header| + cfg = @ceedling[:configurator].project_config_hash + cfg[:collection_paths_include] << header + cfg[:collection_paths_source_and_include] << header + cfg[:collection_paths_test_support_source_include] << header + cfg[:collection_paths_test_support_source_include_vendor] << header + cfg[:collection_paths_release_toolchain_include] << header + Dir[ File.join(header, "*#{EXTENSION_HEADER}") ].each do |f| + cfg[:collection_all_headers] << f + end + end + + get_source_files_for_dependency(deplib).each do |source| + cfg = @ceedling[:configurator].project_config_hash + cfg[:collection_paths_source_and_include] << source + cfg[:collection_paths_test_support_source_include] << source + cfg[:collection_paths_test_support_source_include_vendor] << source + cfg[:collection_paths_release_toolchain_include] << source + Dir[ File.join(source, "*#{EXTENSION_SOURCE}") ].each do |f| + cfg[:collection_all_source] << f + end + end + end + + # Make all these updated files findable by Ceedling + @ceedling[:file_finder].prepare_search_sources() + end +end + +# end blocks always executed following rake run +END { +} diff --git a/test/vendor/ceedling/plugins/fake_function_framework/README.md b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/README.md similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/README.md rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/README.md diff --git a/test/vendor/ceedling/plugins/fake_function_framework/Rakefile b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/Rakefile similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/Rakefile rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/Rakefile diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/project.yml diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/rakefile.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/bar.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/custom_types.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/display.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/event_processor.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/foo.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/src/subfolder/zzz.h diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_event_processor.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/examples/fff_example/test/test_foo.c diff --git a/test/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/lib/fake_function_framework.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/lib/fff_mock_generator.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_header_generator_spec.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/fff_mock_source_generator_spec.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/header_generator.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/spec/spec_helper.rb diff --git a/test/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h b/test/unit-test/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h similarity index 100% rename from test/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h rename to test/unit-test/vendor/ceedling/plugins/fake_function_framework/src/fff_unity_helper.h diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/README.md b/test/unit-test/vendor/ceedling/plugins/gcov/README.md new file mode 100644 index 000000000..b144e3b74 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/README.md @@ -0,0 +1,433 @@ +ceedling-gcov +============= + +# Plugin Overview + +Plugin for integrating GNU GCov code coverage tool into Ceedling projects. +Currently only designed for the gcov command (like LCOV for example). In the +future we could configure this to work with other code coverage tools. + +This plugin currently uses [gcovr](https://www.gcovr.com/) and / or +[ReportGenerator](https://danielpalme.github.io/ReportGenerator/) +as utilities to generate HTML, XML, JSON, or Text reports. The normal gcov +plugin _must_ be run first for these reports to generate. + +## Installation + +gcovr can be installed via pip like so: + +```sh +pip install gcovr +``` + +ReportGenerator can be installed via .NET Core like so: + +```sh +dotnet tool install -g dotnet-reportgenerator-globaltool +``` + +It is not required to install both `gcovr` and `ReportGenerator`. Either utility +may be installed to create reports. + +## Configuration + +The gcov plugin supports configuration options via your `project.yml` provided +by Ceedling. + +### Utilities + +Gcovr and / or ReportGenerator may be enabled to create coverage reports. + +```yaml +:gcov: + :utilities: + - gcovr # Use gcovr to create the specified reports (default). + - ReportGenerator # Use ReportGenerator to create the specified reports. +``` + +### Reports + +Various reports are available and may be enabled with the following +configuration item. See the specific report sections in this README +for additional options and information. All generated reports will be found in `build/artifacts/gcov`. + +```yaml +:gcov: + # Specify one or more reports to generate. + # Defaults to HtmlBasic. + :reports: + # Make an HTML summary report. + # Supported utilities: gcovr, ReportGenerator + - HtmlBasic + + # Make an HTML report with line by line coverage of each source file. + # Supported utilities: gcovr, ReportGenerator + - HtmlDetailed + + # Make a Text report, which may be output to the console with gcovr or a file in both gcovr and ReportGenerator. + # Supported utilities: gcovr, ReportGenerator + - Text + + # Make a Cobertura XML report. + # Supported utilities: gcovr, ReportGenerator + - Cobertura + + # Make a SonarQube XML report. + # Supported utilities: gcovr, ReportGenerator + - SonarQube + + # Make a JSON report. + # Supported utilities: gcovr + - JSON + + # Make a detailed HTML report with CSS and JavaScript included in every HTML page. Useful for build servers. + # Supported utilities: ReportGenerator + - HtmlInline + + # Make a detailed HTML report with a light theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzure + + # Make a detailed HTML report with a dark theme and CSS and JavaScript included in every HTML page for Azure DevOps. + # Supported utilities: ReportGenerator + - HtmlInlineAzureDark + + # Make a single HTML file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - HtmlChart + + # Make a detailed HTML report in a single file. + # Supported utilities: ReportGenerator + - MHtml + + # Make SVG and PNG files that show line and / or branch coverage information. + # Supported utilities: ReportGenerator + - Badges + + # Make a single CSV file containing coverage information per file. + # Supported utilities: ReportGenerator + - CsvSummary + + # Make a single TEX file containing a summary for all files and detailed reports for each files. + # Supported utilities: ReportGenerator + - Latex + + # Make a single TEX file containing a summary for all files. + # Supported utilities: ReportGenerator + - LatexSummary + + # Make a single PNG file containing a chart with historic coverage information. + # Supported utilities: ReportGenerator + - PngChart + + # Command line output interpreted by TeamCity. + # Supported utilities: ReportGenerator + - TeamCitySummary + + # Make a text file in lcov format. + # Supported utilities: ReportGenerator + - lcov + + # Make a XML file containing a summary for all classes and detailed reports for each class. + # Supported utilities: ReportGenerator + - Xml + + # Make a single XML file containing a summary for all files. + # Supported utilities: ReportGenerator + - XmlSummary +``` + +### Gcovr HTML Reports + +Generation of Gcovr HTML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable HTML reports or set to 'false' to disable. + # Defaults to enabled. (gcovr --html) + # Deprecated - See the :reports: configuration option. + :html_report: [true|false] + + # Gcovr supports generating two types of HTML reports. Use 'basic' to create + # an HTML report with only the overall file information. Use 'detailed' to create + # an HTML report with line by line coverage of each source file. + # Defaults to 'basic'. Set to 'detailed' for (gcovr --html-details). + # Deprecated - See the :reports: configuration option. + :html_report_type: [basic|detailed] + + + :gcovr: + # HTML report filename. + :html_artifact_filename: + + # Use 'title' as title for the HTML report. + # Default is 'Head'. (gcovr --html-title) + :html_title: + + # If the coverage is below MEDIUM, the value is marked as low coverage in the HTML report. + # MEDIUM has to be lower than or equal to value of html_high_threshold. + # If MEDIUM is equal to value of html_high_threshold the report has only high and low coverage. + # Default is 75.0. (gcovr --html-medium-threshold) + :html_medium_threshold: 75 + + # If the coverage is below HIGH, the value is marked as medium coverage in the HTML report. + # HIGH has to be greater than or equal to value of html_medium_threshold. + # If HIGH is equal to value of html_medium_threshold the report has only high and low coverage. + # Default is 90.0. (gcovr -html-high-threshold) + :html_high_threshold: 90 + + # Set to 'true' to use absolute paths to link the 'detailed' reports. + # Defaults to relative links. (gcovr --html-absolute-paths) + :html_absolute_paths: [true|false] + + # Override the declared HTML report encoding. Defaults to UTF-8. (gcovr --html-encoding) + :html_encoding: <html_encoding> +``` + +### Cobertura XML Reports + +Generation of Cobertura XML reports may be modified with the following configuration items. + +```yaml +:gcov: + # Set to 'true' to enable Cobertura XML reports or set to 'false' to disable. + # Defaults to disabled. (gcovr --xml) + # Deprecated - See the :reports: configuration option. + :xml_report: [true|false] + + + :gcovr: + # Set to 'true' to pretty-print the Cobertura XML report, otherwise set to 'false'. + # Defaults to disabled. (gcovr --xml-pretty) + :xml_pretty: [true|false] + :cobertura_pretty: [true|false] + + # Cobertura XML report filename. + :xml_artifact_filename: <output> + :cobertura_artifact_filename: <output> +``` + +### SonarQube XML Reports + +Generation of SonarQube XML reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # SonarQube XML report filename. + :sonarqube_artifact_filename: <output> +``` + +### JSON Reports + +Generation of JSON reports may be modified with the following configuration items. + +```yaml +:gcov: + :gcovr: + # Set to 'true' to pretty-print the JSON report, otherwise set 'false'. + # Defaults to disabled. (gcovr --json-pretty) + :json_pretty: [true|false] + + # JSON report filename. + :json_artifact_filename: <output> +``` + +### Text Reports + +Generation of text reports may be modified with the following configuration items. +Text reports may be printed to the console or output to a file. + +```yaml +:gcov: + :gcovr: + # Text report filename. + # The text report is printed to the console when no filename is provided. + :text_artifact_filename: <output> +``` + +### Common Report Options + +There are a number of options to control which files are considered part of +the coverage report. Most often, we only care about coverage on our source code, and not +on tests or automatically generated mocks, runners, etc. However, there are times +where this isn't true... or there are times where we've moved ceedling's directory +structure so that the project file isn't at the root of the project anymore. In these +cases, you may need to tweak `report_include`, `report_exclude`, and `exclude_directories`. + +One important note about `report_root`: gcovr will take only a single root folder, unlike +Ceedling's ability to take as many as you like. So you will need to choose a folder which is +a superset of ALL the folders you want, and then use the include or exclude options to set up +patterns of files to pay attention to or ignore. It's not ideal, but it works. + +Finally, there are a number of settings which can be specified to adjust the +default behaviors of gcovr: + +```yaml +:gcov: + :gcovr: + # The root directory of your source files. Defaults to ".", the current directory. + # File names are reported relative to this root. The report_root is the default report_include. + :report_root: "." + + # Load the specified configuration file. + # Defaults to gcovr.cfg in the report_root directory. (gcovr --config) + :config_file: <config_file> + + # Exit with a status of 2 if the total line coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_branch' option. (gcovr --fail-under-line) + :fail_under_line: 30 + + # Exit with a status of 4 if the total branch coverage is less than MIN. + # Can be ORed with exit status of 'fail_under_line' option. (gcovr --fail-under-branch) + :fail_under_branch: 30 + + # Select the source file encoding. + # Defaults to the system default encoding (UTF-8). (gcovr --source-encoding) + :source_encoding: <source_encoding> + + # Report the branch coverage instead of the line coverage. For text report only. (gcovr --branches). + :branches: [true|false] + + # Sort entries by increasing number of uncovered lines. + # For text and HTML report. (gcovr --sort-uncovered) + :sort_uncovered: [true|false] + + # Sort entries by increasing percentage of uncovered lines. + # For text and HTML report. (gcovr --sort-percentage) + :sort_percentage: [true|false] + + # Print a small report to stdout with line & branch percentage coverage. + # This is in addition to other reports. (gcovr --print-summary). + :print_summary: [true|false] + + # Keep only source files that match this filter. (gcovr --filter). + :report_include: "^src" + + # Exclude source files that match this filter. (gcovr --exclude). + :report_exclude: "^vendor.*|^build.*|^test.*|^lib.*" + + # Keep only gcov data files that match this filter. (gcovr --gcov-filter). + :gcov_filter: <gcov_filter> + + # Exclude gcov data files that match this filter. (gcovr --gcov-exclude). + :gcov_exclude: <gcov_exclude> + + # Exclude directories that match this regex while searching + # raw coverage files. (gcovr --exclude-directories). + :exclude_directories: <exclude_dirs> + + # Use a particular gcov executable. (gcovr --gcov-executable). + :gcov_executable: <gcov_cmd> + + # Exclude branch coverage from lines without useful + # source code. (gcovr --exclude-unreachable-branches). + :exclude_unreachable_branches: [true|false] + + # For branch coverage, exclude branches that the compiler + # generates for exception handling. (gcovr --exclude-throw-branches). + :exclude_throw_branches: [true|false] + + # Use existing gcov files for analysis. Default: False. (gcovr --use-gcov-files) + :use_gcov_files: [true|false] + + # Skip lines with parse errors in GCOV files instead of + # exiting with an error. (gcovr --gcov-ignore-parse-errors). + :gcov_ignore_parse_errors: [true|false] + + # Override normal working directory detection. (gcovr --object-directory) + :object_directory: <objdir> + + # Keep gcov files after processing. (gcovr --keep). + :keep: [true|false] + + # Delete gcda files after processing. (gcovr --delete). + :delete: [true|false] + + # Set the number of threads to use in parallel. (gcovr -j). + :num_parallel_threads: <num_threads> + + # When scanning the code coverage, if any files are found that do not have + # associated coverage data, the command will abort with an error message. + :abort_on_uncovered: true + + # When using the ``abort_on_uncovered`` option, the files in this list will not + # trigger a failure. + # Ceedling globs described in the Ceedling packet ``Path`` section can be used + # when directories are placed on the list. Globs are limited to matching directories + # and not files. + :uncovered_ignore_list: [] +``` + +### ReportGenerator Configuration + +The ReportGenerator utility may be configured with the following configuration items. +All generated reports may be found in `build/artifacts/gcov/ReportGenerator`. + +```yaml +:gcov: + :report_generator: + # Optional directory for storing persistent coverage information. + # Can be used in future reports to show coverage evolution. + :history_directory: <history_directory> + + # Optional plugin files for custom reports or custom history storage (separated by semicolon). + :plugins: CustomReports.dll + + # Optional list of assemblies that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :assembly_filters: "+Included;-Excluded" + + # Optional list of classes that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :class_filters: "+Included;-Excluded" + + # Optional list of files that should be included or excluded in the report (separated by semicolon).. + # Exclusion filters take precedence over inclusion filters. + # Wildcards are allowed, but not regular expressions. + :file_filters: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*" + + # The verbosity level of the log messages. + # Values: Verbose, Info, Warning, Error, Off + :verbosity: Warning + + # Optional tag or build version. + :tag: <tag> + + # Optional list of one or more regular expressions to exclude gcov notes files that match these filters. + :gcov_exclude: + - <exclude_regex1> + - <exclude_regex2> + + # Optionally use a particular gcov executable. Defaults to gcov. + :gcov_executable: <gcov_cmd> + + # Optionally set the number of threads to use in parallel. Defaults to 1. + :num_parallel_threads: <num_threads> + + # Optional list of one or more command line arguments to pass to Report Generator. + # Useful for configuring Risk Hotspots and Other Settings. + # https://github.com/danielpalme/ReportGenerator/wiki/Settings + :custom_args: + - <custom_arg1> + - <custom_arg2> +``` + +## Example Usage + +```sh +ceedling gcov:all utils:gcov +``` + +## To-Do list + +- Generate overall report (combined statistics from all files with coverage) + +## Citations + +Most of the comment text which describes the options was taken from the +[Gcovr User Guide](https://www.gcovr.com/en/stable/guide.html) and the +[ReportGenerator Wiki](https://github.com/danielpalme/ReportGenerator/wiki). +The text is repeated here to provide the most accurate option functionality. diff --git a/test/vendor/ceedling/plugins/gcov/assets/template.erb b/test/unit-test/vendor/ceedling/plugins/gcov/assets/template.erb similarity index 100% rename from test/vendor/ceedling/plugins/gcov/assets/template.erb rename to test/unit-test/vendor/ceedling/plugins/gcov/assets/template.erb diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb b/test/unit-test/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb new file mode 100644 index 000000000..e3ce340dd --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/config/defaults_gcov.rb @@ -0,0 +1,118 @@ + +DEFAULT_GCOV_COMPILER_TOOL = { + :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0], + :name => 'default_gcov_compiler'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-g".freeze, + "-fprofile-arcs".freeze, + "-ftest-coverage".freeze, + ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1], + ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, + {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, + {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, + "-DGCOV_COMPILER".freeze, + "-DCODE_COVERAGE".freeze, + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + "-c \"${1}\"".freeze, + "-o \"${2}\"".freeze + ].freeze + } + + +DEFAULT_GCOV_LINKER_TOOL = { + :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0], + :name => 'default_gcov_linker'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-g".freeze, + "-fprofile-arcs".freeze, + "-ftest-coverage".freeze, + ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1], + ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split, + ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split, + "\"${1}\"".freeze, + "-o \"${2}\"".freeze, + "${4}".freeze, + "${5}".freeze, + ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split + ].freeze + } + +DEFAULT_GCOV_FIXTURE_TOOL = { + :executable => '${1}'.freeze, + :name => 'default_gcov_fixture'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [].freeze + } + +DEFAULT_GCOV_REPORT_TOOL = { + :executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0], + :name => 'default_gcov_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => false.freeze, + :arguments => [ + "-n".freeze, + "-p".freeze, + "-b".freeze, + {"-o \"$\"" => 'GCOV_BUILD_OUTPUT_PATH'}.freeze, + "\"${1}\"".freeze + ].freeze + } + +DEFAULT_GCOV_GCOV_POST_REPORT_TOOL = { + :executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0], + :name => 'default_gcov_gcov_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "-b".freeze, + "-c".freeze, + "-r".freeze, + "-x".freeze, + "${1}".freeze + ].freeze + } + +DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL = { + :executable => 'gcovr'.freeze, + :name => 'default_gcov_gcovr_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "${1}".freeze + ].freeze + } + +DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT = { + :executable => 'reportgenerator'.freeze, + :name => 'default_gcov_reportgenerator_post_report'.freeze, + :stderr_redirect => StdErrRedirect::NONE.freeze, + :background_exec => BackgroundExec::NONE.freeze, + :optional => true.freeze, + :arguments => [ + "${1}".freeze + ].freeze + } + +def get_default_config + return :tools => { + :gcov_compiler => DEFAULT_GCOV_COMPILER_TOOL, + :gcov_linker => DEFAULT_GCOV_LINKER_TOOL, + :gcov_fixture => DEFAULT_GCOV_FIXTURE_TOOL, + :gcov_report => DEFAULT_GCOV_REPORT_TOOL, + :gcov_gcov_post_report => DEFAULT_GCOV_GCOV_POST_REPORT_TOOL, + :gcov_gcovr_post_report => DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL, + :gcov_reportgenerator_post_report => DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT + } +end diff --git a/test/vendor/ceedling/plugins/gcov/gcov.rake b/test/unit-test/vendor/ceedling/plugins/gcov/gcov.rake similarity index 63% rename from test/vendor/ceedling/plugins/gcov/gcov.rake rename to test/unit-test/vendor/ceedling/plugins/gcov/gcov.rake index 3acab8566..1467564a8 100644 --- a/test/vendor/ceedling/plugins/gcov/gcov.rake +++ b/test/unit-test/vendor/ceedling/plugins/gcov/gcov.rake @@ -1,3 +1,6 @@ +require 'reportgenerator_reportinator' +require 'gcovr_reportinator' + directory(GCOV_BUILD_OUTPUT_PATH) directory(GCOV_RESULTS_PATH) directory(GCOV_ARTIFACTS_PATH) @@ -16,7 +19,7 @@ rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_OBJECT}$/ => [ end ]) do |object| - if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX}|#{GCOV_IGNORE_SOURCES.join('|')})/i + if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX})|(#{VENDORS_FILES.map{|source| '\b' + source + '\b'}.join('|')})/ @ceedling[:generator].generate_object_file( TOOLS_GCOV_COMPILER, OPERATION_COMPILE_SYM, @@ -32,14 +35,15 @@ end rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_EXECUTABLE}$/) do |bin_file| lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() - + lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments() @ceedling[:generator].generate_executable_file( TOOLS_GCOV_LINKER, GCOV_SYM, bin_file.prerequisites, bin_file.name, + @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name), lib_args, - @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name) + lib_paths ) end @@ -71,7 +75,7 @@ namespace GCOV_SYM do task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") desc 'Run code coverage for all tests' - task all: [:directories] do + task all: [:test_deps] do @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM) @ceedling[:configurator].restore_config @@ -87,7 +91,7 @@ namespace GCOV_SYM do end desc 'Run tests by matching regular expression pattern.' - task :pattern, [:regex] => [:directories] do |_t, args| + task :pattern, [:regex] => [:test_deps] do |_t, args| matches = [] COLLECTION_ALL_TESTS.each do |test| @@ -104,7 +108,7 @@ namespace GCOV_SYM do end desc 'Run tests whose test path contains [dir] or [dir] substring.' - task :path, [:dir] => [:directories] do |_t, args| + task :path, [:dir] => [:test_deps] do |_t, args| matches = [] COLLECTION_ALL_TESTS.each do |test| @@ -121,7 +125,7 @@ namespace GCOV_SYM do end desc 'Run code coverage for changed files' - task delta: [:directories] do + task delta: [:test_deps] do @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, force_run: false) @ceedling[:configurator].restore_config @@ -136,7 +140,7 @@ namespace GCOV_SYM do @ceedling[:file_finder].find_test_from_file_path(test) end ]) do |test| - @ceedling[:rake_wrapper][:directories].invoke + @ceedling[:rake_wrapper][:test_deps].invoke @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM) @ceedling[:configurator].restore_config @@ -154,67 +158,52 @@ if PROJECT_USE_DEEP_DEPENDENCIES end namespace UTILS_SYM do - def gcov_args_builder(opts) - args = "" - args += "-r \"#{opts[:gcov_report_root] || '.'}\" " - args += "-f \"#{opts[:gcov_report_include]}\" " unless opts[:gcov_report_include].nil? - args += "-e \"#{opts[:gcov_report_exclude] || GCOV_FILTER_EXCLUDE}\" " - [ :gcov_fail_under_line, :gcov_fail_under_branch, :gcov_html_medium_threshold, :gcov_html_high_threshold].each do |opt| - args += "--#{opt.to_s.gsub('_','-').sub(/:?gcov-/,'')} #{opts[opt]} " unless opts[opt].nil? - end - return args + # Report Creation Utilities + UTILITY_NAME_GCOVR = "gcovr" + UTILITY_NAME_REPORT_GENERATOR = "ReportGenerator" + UTILITY_NAMES = [UTILITY_NAME_GCOVR, UTILITY_NAME_REPORT_GENERATOR] + + # Returns true is the given utility is enabled, otherwise returns false. + def is_utility_enabled(opts, utility_name) + return !(opts.nil?) && !(opts[:gcov_utilities].nil?) && (opts[:gcov_utilities].map(&:upcase).include? utility_name.upcase) end - desc 'Create gcov code coverage html report (must run ceedling gcov first)' - task GCOV_SYM do + desc "Create gcov code coverage html/xml/json/text report(s). (Note: Must run 'ceedling gcov' first)." + task GCOV_SYM do + # Get the gcov options from project.yml. + opts = @ceedling[:configurator].project_config_hash + + # Create the artifacts output directory. if !File.directory? GCOV_ARTIFACTS_PATH FileUtils.mkdir_p GCOV_ARTIFACTS_PATH end - args = gcov_args_builder(@ceedling[:configurator].project_config_hash) - - if @ceedling[:configurator].project_config_hash[:gcov_html_report].nil? - puts "In your project.yml, define: \n\n:gcov:\n :html_report:\n\n to true or false to refine this feature." - puts "For now, assumimg you want an html report generated." - html_enabled = true - else - html_enabled = @ceedling[:configurator].project_config_hash[:gcov_html_report] + # Remove unsupported reporting utilities. + if !(opts[:gcov_utilities].nil?) + opts[:gcov_utilities].reject! { |item| !(UTILITY_NAMES.map(&:upcase).include? item.upcase) } end - if @ceedling[:configurator].project_config_hash[:gcov_xml_report].nil? - puts "In your project.yml, define: \n\n:gcov:\n :xml_report:\n\n to true or false to refine this feature." - puts "For now, assumimg you do not want an xml report generated." - xml_enabled = false - else - xml_enabled = @ceedling[:configurator].project_config_hash[:gcov_xml_report] + # Default to gcovr when no reporting utilities are specified. + if opts[:gcov_utilities].nil? || opts[:gcov_utilities].empty? + opts[:gcov_utilities] = [UTILITY_NAME_GCOVR] end - if html_enabled - if @ceedling[:configurator].project_config_hash[:gcov_html_report_type] == 'basic' - puts "Creating a basic html report of gcov results in #{GCOV_ARTIFACTS_FILE}..." - command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_BASIC, [], args) - @ceedling[:tool_executor].exec(command[:line], command[:options]) - elsif @ceedling[:configurator].project_config_hash[:gcov_html_report_type] == 'detailed' - puts "Creating a detailed html report of gcov results in #{GCOV_ARTIFACTS_FILE}..." - command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_ADVANCED, [], args) - @ceedling[:tool_executor].exec(command[:line], command[:options]) - - else - puts "In your project.yml, define: \n\n:gcov:\n :html_report_type:\n\n to basic or detailed to refine this feature." - puts "For now, just creating basic." - puts "Creating a basic html report of gcov results in #{GCOV_ARTIFACTS_FILE}..." - command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_BASIC, [], args) - @ceedling[:tool_executor].exec(command[:line], command[:options]) - end + if opts[:gcov_reports].nil? + opts[:gcov_reports] = [] end - if xml_enabled - puts "Creating an xml report of gcov results in #{GCOV_ARTIFACTS_FILE_XML}..." - command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_XML, [], filter) - @ceedling[:tool_executor].exec(command[:line], command[:options]) + gcovr_reportinator = GcovrReportinator.new(@ceedling) + gcovr_reportinator.support_deprecated_options(opts) + + if is_utility_enabled(opts, UTILITY_NAME_GCOVR) + gcovr_reportinator.make_reports(opts) + end + + if is_utility_enabled(opts, UTILITY_NAME_REPORT_GENERATOR) + reportgenerator_reportinator = ReportGeneratorReportinator.new(@ceedling) + reportgenerator_reportinator.make_reports(opts) end - puts "Done." end end diff --git a/test/vendor/ceedling/plugins/gcov/lib/gcov.rb b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov.rb similarity index 76% rename from test/vendor/ceedling/plugins/gcov/lib/gcov.rb rename to test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov.rb index 15a1b4cfa..30c6326b8 100644 --- a/test/vendor/ceedling/plugins/gcov/lib/gcov.rb +++ b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov.rb @@ -82,10 +82,7 @@ class Gcov < Plugin banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" @ceedling[:streaminator].stdout_puts "\n" + banner - coverage_sources = sources.clone - coverage_sources.delete_if { |item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{EXTENSION_SOURCE}$/ } - coverage_sources.delete_if { |item| item =~ /#{GCOV_IGNORE_SOURCES.join('|')}#{EXTENSION_SOURCE}$/ } - + coverage_sources = @ceedling[:project_config_manager].filter_internal_sources(sources) coverage_sources.each do |source| basename = File.basename(source) command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, [], [basename]) @@ -98,9 +95,35 @@ class Gcov < Plugin end end + ignore_path_list = @ceedling[:file_system_utils].collect_paths(@ceedling[:configurator].project_config_hash[:gcov_uncovered_ignore_list] || []) + ignore_uncovered_list = @ceedling[:file_wrapper].instantiate_file_list + ignore_path_list.each do |path| + if File.exists?(path) and not File.directory?(path) + ignore_uncovered_list.include(path) + else + ignore_uncovered_list.include(File.join(path, "*#{EXTENSION_SOURCE}")) + end + end + + found_uncovered = false COLLECTION_ALL_SOURCE.each do |source| unless coverage_sources.include?(source) - @ceedling[:streaminator].stdout_puts("Could not find coverage results for " + source + "\n") + v = Verbosity::DEBUG + msg = "Could not find coverage results for " + source + if ignore_uncovered_list.include?(source) + msg += " [IGNORED]" + else + found_uncovered = true + v = Verbosity::NORMAL + end + msg += "\n" + @ceedling[:streaminator].stdout_puts(msg, v) + end + end + if found_uncovered + if @ceedling[:configurator].project_config_hash[:gcov_abort_on_uncovered] + @ceedling[:streaminator].stderr_puts("There were files with no coverage results: aborting.\n") + exit(-1) end end end diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb new file mode 100644 index 000000000..74c9bbda8 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb @@ -0,0 +1,48 @@ + +GCOV_ROOT_NAME = 'gcov'.freeze +GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':' +GCOV_SYM = GCOV_ROOT_NAME.to_sym + +GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME) +GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out") +GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results") +GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies") +GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME) +GCOV_REPORT_GENERATOR_PATH = File.join(GCOV_ARTIFACTS_PATH, "ReportGenerator") + +GCOV_ARTIFACTS_FILE_HTML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html") +GCOV_ARTIFACTS_FILE_COBERTURA = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageCobertura.xml") +GCOV_ARTIFACTS_FILE_SONARQUBE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageSonarQube.xml") +GCOV_ARTIFACTS_FILE_JSON = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverage.json") + +GCOV_FILTER_EXCLUDE_PATHS = ['vendor', 'build', 'test', 'lib'] + +# gcovr supports regular expressions. +GCOV_FILTER_EXCLUDE = GCOV_FILTER_EXCLUDE_PATHS.map{|path| '^'.concat(*path).concat('.*')}.join('|') + +# ReportGenerator supports text with wildcard characters. +GCOV_REPORT_GENERATOR_FILE_FILTERS = GCOV_FILTER_EXCLUDE_PATHS.map{|path| File.join('-.', *path, '*')}.join(';') + +# Report Types +class ReportTypes + HTML_BASIC = "HtmlBasic" + HTML_DETAILED = "HtmlDetailed" + HTML_CHART = "HtmlChart" + HTML_INLINE = "HtmlInline" + HTML_INLINE_AZURE = "HtmlInlineAzure" + HTML_INLINE_AZURE_DARK = "HtmlInlineAzureDark" + MHTML = "MHtml" + TEXT = "Text" + COBERTURA = "Cobertura" + SONARQUBE = "SonarQube" + JSON = "JSON" + BADGES = "Badges" + CSV_SUMMARY = "CsvSummary" + LATEX = "Latex" + LATEX_SUMMARY = "LatexSummary" + PNG_CHART = "PngChart" + TEAM_CITY_SUMMARY = "TeamCitySummary" + LCOV = "lcov" + XML = "Xml" + XML_SUMMARY = "XmlSummary" +end diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb new file mode 100644 index 000000000..5317c5d24 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/lib/gcovr_reportinator.rb @@ -0,0 +1,331 @@ +require 'reportinator_helper' + +class GcovrReportinator + + def initialize(system_objects) + @ceedling = system_objects + @reportinator_helper = ReportinatorHelper.new + end + + + # Generate the gcovr report(s) specified in the options. + def make_reports(opts) + # Get the gcovr version number. + gcovr_version_info = get_gcovr_version() + + # Build the common gcovr arguments. + args_common = args_builder_common(opts) + + if ((gcovr_version_info[0] == 4) && (gcovr_version_info[1] >= 2)) || (gcovr_version_info[0] > 4) + # gcovr version 4.2 and later supports generating multiple reports with a single call. + args = args_common + args += args_builder_cobertura(opts, false) + args += args_builder_sonarqube(opts, false) + args += args_builder_json(opts, true) + # As of gcovr version 4.2, the --html argument must appear last. + args += args_builder_html(opts, false) + + print "Creating gcov results report(s) in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the report(s). + run(args) + else + # gcovr version 4.1 and earlier supports HTML and Cobertura XML reports. + # It does not support SonarQube and JSON reports. + # Reports must also be generated separately. + args_cobertura = args_builder_cobertura(opts, true) + args_html = args_builder_html(opts, true) + + if args_html.length > 0 + print "Creating a gcov HTML report in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the HTML report. + run(args_common + args_html) + end + + if args_cobertura.length > 0 + print "Creating a gcov XML report in '#{GCOV_ARTIFACTS_PATH}'... " + STDOUT.flush + + # Generate the Cobertura XML report. + run(args_common + args_cobertura) + end + end + + # Determine if the gcovr text report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::TEXT) + make_text_report(opts, args_common) + end + end + + + def support_deprecated_options(opts) + # Support deprecated :html_report: and ":html_report_type: basic" options. + if !is_report_enabled(opts, ReportTypes::HTML_BASIC) && (opts[:gcov_html_report] || (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("basic") == 0)) + opts[:gcov_reports].push(ReportTypes::HTML_BASIC) + end + + # Support deprecated ":html_report_type: detailed" option. + if !is_report_enabled(opts, ReportTypes::HTML_DETAILED) && (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0) + opts[:gcov_reports].push(ReportTypes::HTML_DETAILED) + end + + # Support deprecated :xml_report: option. + if opts[:gcov_xml_report] + opts[:gcov_reports].push(ReportTypes::COBERTURA) + end + + # Default to HTML basic report when no report types are defined. + if opts[:gcov_reports].empty? && opts[:gcov_html_report_type].nil? && opts[:gcov_xml_report].nil? + opts[:gcov_reports] = [ReportTypes::HTML_BASIC] + + puts "In your project.yml, define one or more of the" + puts "following to specify which reports to generate." + puts "For now, creating only an #{ReportTypes::HTML_BASIC} report." + puts "" + puts ":gcov:" + puts " :reports:" + puts " - #{ReportTypes::HTML_BASIC}" + puts " - #{ReportTypes::HTML_DETAILED}" + puts " - #{ReportTypes::TEXT}" + puts " - #{ReportTypes::COBERTURA}" + puts " - #{ReportTypes::SONARQUBE}" + puts " - #{ReportTypes::JSON}" + puts "" + end + end + + + private + + GCOVR_SETTING_PREFIX = "gcov_gcovr" + + # Build the gcovr report generation common arguments. + def args_builder_common(opts) + gcovr_opts = get_opts(opts) + + args = "" + args += "--root \"#{gcovr_opts[:report_root] || '.'}\" " + args += "--config \"#{gcovr_opts[:config_file]}\" " unless gcovr_opts[:config_file].nil? + args += "--filter \"#{gcovr_opts[:report_include]}\" " unless gcovr_opts[:report_include].nil? + args += "--exclude \"#{gcovr_opts[:report_exclude] || GCOV_FILTER_EXCLUDE}\" " + args += "--gcov-filter \"#{gcovr_opts[:gcov_filter]}\" " unless gcovr_opts[:gcov_filter].nil? + args += "--gcov-exclude \"#{gcovr_opts[:gcov_exclude]}\" " unless gcovr_opts[:gcov_exclude].nil? + args += "--exclude-directories \"#{gcovr_opts[:exclude_directories]}\" " unless gcovr_opts[:exclude_directories].nil? + args += "--branches " if gcovr_opts[:branches].nil? || gcovr_opts[:branches] # Defaults to enabled. + args += "--sort-uncovered " if gcovr_opts[:sort_uncovered] + args += "--sort-percentage " if gcovr_opts[:sort_percentage].nil? || gcovr_opts[:sort_percentage] # Defaults to enabled. + args += "--print-summary " if gcovr_opts[:print_summary] + args += "--gcov-executable \"#{gcovr_opts[:gcov_executable]}\" " unless gcovr_opts[:gcov_executable].nil? + args += "--exclude-unreachable-branches " if gcovr_opts[:exclude_unreachable_branches] + args += "--exclude-throw-branches " if gcovr_opts[:exclude_throw_branches] + args += "--use-gcov-files " if gcovr_opts[:use_gcov_files] + args += "--gcov-ignore-parse-errors " if gcovr_opts[:gcov_ignore_parse_errors] + args += "--keep " if gcovr_opts[:keep] + args += "--delete " if gcovr_opts[:delete] + args += "-j #{gcovr_opts[:num_parallel_threads]} " if !(gcovr_opts[:num_parallel_threads].nil?) && (gcovr_opts[:num_parallel_threads].is_a? Integer) + + [:fail_under_line, :fail_under_branch, :source_encoding, :object_directory].each do |opt| + unless gcovr_opts[opt].nil? + + value = gcovr_opts[opt] + if (opt == :fail_under_line) || (opt == :fail_under_branch) + if not value.is_a? Integer + puts "Option value #{opt} has to be an integer" + value = nil + elsif (value < 0) || (value > 100) + puts "Option value #{opt} has to be a percentage from 0 to 100" + value = nil + end + end + args += "--#{opt.to_s.gsub('_','-')} #{value} " unless value.nil? + end + end + + return args + end + + + # Build the gcovr Cobertura XML report generation arguments. + def args_builder_cobertura(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the Cobertura XML report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::COBERTURA) + # Determine the Cobertura XML report file name. + artifacts_file_cobertura = GCOV_ARTIFACTS_FILE_COBERTURA + if !(gcovr_opts[:cobertura_artifact_filename].nil?) + artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:cobertura_artifact_filename]) + elsif !(gcovr_opts[:xml_artifact_filename].nil?) + artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:xml_artifact_filename]) + end + + args += "--xml-pretty " if gcovr_opts[:xml_pretty] || gcovr_opts[:cobertura_pretty] + args += "--xml #{use_output_option ? "--output " : ""} \"#{artifacts_file_cobertura}\" " + end + + return args + end + + + # Build the gcovr SonarQube report generation arguments. + def args_builder_sonarqube(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr SonarQube XML report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::SONARQUBE) + # Determine the SonarQube XML report file name. + artifacts_file_sonarqube = GCOV_ARTIFACTS_FILE_SONARQUBE + if !(gcovr_opts[:sonarqube_artifact_filename].nil?) + artifacts_file_sonarqube = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:sonarqube_artifact_filename]) + end + + args += "--sonarqube #{use_output_option ? "--output " : ""} \"#{artifacts_file_sonarqube}\" " + end + + return args + end + + + # Build the gcovr JSON report generation arguments. + def args_builder_json(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr JSON report is enabled. Defaults to disabled. + if is_report_enabled(opts, ReportTypes::JSON) + # Determine the JSON report file name. + artifacts_file_json = GCOV_ARTIFACTS_FILE_JSON + if !(gcovr_opts[:json_artifact_filename].nil?) + artifacts_file_json = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:json_artifact_filename]) + end + + args += "--json-pretty " if gcovr_opts[:json_pretty] + # Note: In gcovr 4.2, the JSON report is output only when the --output option is specified. + # Hopefully we can remove --output after a future gcovr release. + args += "--json #{use_output_option ? "--output " : ""} \"#{artifacts_file_json}\" " + end + + return args + end + + + # Build the gcovr HTML report generation arguments. + def args_builder_html(opts, use_output_option=false) + gcovr_opts = get_opts(opts) + args = "" + + # Determine if the gcovr HTML report is enabled. Defaults to enabled. + html_enabled = (opts[:gcov_html_report].nil? && opts[:gcov_reports].empty?) || + is_report_enabled(opts, ReportTypes::HTML_BASIC) || + is_report_enabled(opts, ReportTypes::HTML_DETAILED) + + if html_enabled + # Determine the HTML report file name. + artifacts_file_html = GCOV_ARTIFACTS_FILE_HTML + if !(gcovr_opts[:html_artifact_filename].nil?) + artifacts_file_html = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:html_artifact_filename]) + end + + is_html_report_type_detailed = (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0) + + args += "--html-details " if is_html_report_type_detailed || is_report_enabled(opts, ReportTypes::HTML_DETAILED) + args += "--html-title \"#{gcovr_opts[:html_title]}\" " unless gcovr_opts[:html_title].nil? + args += "--html-absolute-paths " if !(gcovr_opts[:html_absolute_paths].nil?) && gcovr_opts[:html_absolute_paths] + args += "--html-encoding \"#{gcovr_opts[:html_encoding]}\" " unless gcovr_opts[:html_encoding].nil? + + [:html_medium_threshold, :html_high_threshold].each do |opt| + args += "--#{opt.to_s.gsub('_','-')} #{gcovr_opts[opt]} " unless gcovr_opts[opt].nil? + end + + # The following option must be appended last for gcovr version <= 4.2 to properly work. + args += "--html #{use_output_option ? "--output " : ""} \"#{artifacts_file_html}\" " + end + + return args + end + + + # Generate a gcovr text report. + def make_text_report(opts, args_common) + gcovr_opts = get_opts(opts) + args_text = "" + message_text = "Creating a gcov text report" + + if !(gcovr_opts[:text_artifact_filename].nil?) + artifacts_file_txt = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:text_artifact_filename]) + args_text += "--output \"#{artifacts_file_txt}\" " + message_text += " in '#{GCOV_ARTIFACTS_PATH}'... " + else + message_text += "... " + end + + print message_text + STDOUT.flush + + # Generate the text report. + run(args_common + args_text) + end + + + # Get the gcovr options from the project options. + def get_opts(opts) + return opts[GCOVR_SETTING_PREFIX.to_sym] || {} + end + + + # Run gcovr with the given arguments. + def run(args) + begin + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], args) + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + @reportinator_helper.print_shell_result(shell_result) + rescue + # handle any unforeseen issues with called tool + exitcode = $?.exitstatus + show_gcovr_message(exitcode) + exit(exitcode) + end + end + + + # Get the gcovr version number as components. + # Returns [major, minor]. + def get_gcovr_version() + version_number_major = 0 + version_number_minor = 0 + + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], "--version") + shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options]) + version_number_match_data = shell_result[:output].match(/gcovr ([0-9]+)\.([0-9]+)/) + + if !(version_number_match_data.nil?) && !(version_number_match_data[1].nil?) && !(version_number_match_data[2].nil?) + version_number_major = version_number_match_data[1].to_i + version_number_minor = version_number_match_data[2].to_i + end + + return version_number_major, version_number_minor + end + + + # Show a more human-friendly message on gcovr return code + def show_gcovr_message(exitcode) + if ((exitcode & 2) == 2) + puts "The line coverage is less than the minimum" + end + if ((exitcode & 4) == 4) + puts "The branch coverage is less than the minimum" + end + end + + + # Returns true if the given report type is enabled, otherwise returns false. + def is_report_enabled(opts, report_type) + return !(opts.nil?) && !(opts[:gcov_reports].nil?) && (opts[:gcov_reports].map(&:upcase).include? report_type.upcase) + end + +end diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb b/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb new file mode 100644 index 000000000..d4a885c97 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportgenerator_reportinator.rb @@ -0,0 +1,195 @@ +require 'benchmark' +require 'reportinator_helper' + +class ReportGeneratorReportinator + + def initialize(system_objects) + @ceedling = system_objects + @reportinator_helper = ReportinatorHelper.new + end + + + # Generate the ReportGenerator report(s) specified in the options. + def make_reports(opts) + shell_result = nil + total_time = Benchmark.realtime do + rg_opts = get_opts(opts) + + print "Creating gcov results report(s) with ReportGenerator in '#{GCOV_REPORT_GENERATOR_PATH}'... " + STDOUT.flush + + # Cleanup any existing .gcov files to avoid reporting old coverage results. + for gcov_file in Dir.glob("*.gcov") + File.delete(gcov_file) + end + + # Use a custom gcov executable, if specified. + GCOV_TOOL_CONFIG[:executable] = rg_opts[:gcov_executable] unless rg_opts[:gcov_executable].nil? + + # Avoid running gcov on the mock, test, unity, and cexception gcov notes files to save time. + gcno_exclude_str = "#{opts[:cmock_mock_prefix]}.*" + gcno_exclude_str += "|#{opts[:project_test_file_prefix]}.*" + gcno_exclude_str += "|#{VENDORS_FILES.join('|')}" + + # Avoid running gcov on custom specified .gcno files. + if !(rg_opts.nil?) && !(rg_opts[:gcov_exclude].nil?) && !(rg_opts[:gcov_exclude].empty?) + for gcno_exclude_expression in rg_opts[:gcov_exclude] + if !(gcno_exclude_expression.nil?) && !(gcno_exclude_expression.empty?) + # We want to filter .gcno files, not .gcov files. + # We will generate .gcov files from .gcno files. + gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcov") + gcno_exclude_expression = gcno_exclude_expression.chomp(".gcov") + # The .gcno extension will be added later as we create the regex. + gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcno") + gcno_exclude_expression = gcno_exclude_expression.chomp(".gcno") + # Append the custom expression. + gcno_exclude_str += "|#{gcno_exclude_expression}" + end + end + end + + gcno_exclude_regex = /(\/|\\)(#{gcno_exclude_str})\.gcno/ + + # Generate .gcov files by running gcov on gcov notes files (*.gcno). + for gcno_filepath in Dir.glob(File.join(GCOV_BUILD_PATH, "**", "*.gcno")) + match_data = gcno_filepath.match(gcno_exclude_regex) + if match_data.nil? || (match_data[1].nil? && match_data[1].nil?) + # Ensure there is a matching gcov data file. + if File.file?(gcno_filepath.gsub(".gcno", ".gcda")) + run_gcov("\"#{gcno_filepath}\"") + end + end + end + + if Dir.glob("*.gcov").length > 0 + # Build the command line arguments. + args = args_builder(opts) + + # Generate the report(s). + shell_result = run(args) + else + puts "\nWarning: No matching .gcno coverage files found." + end + + # Cleanup .gcov files. + for gcov_file in Dir.glob("*.gcov") + File.delete(gcov_file) + end + end + + if shell_result + shell_result[:time] = total_time + @reportinator_helper.print_shell_result(shell_result) + end + end + + + private + + # A dictionary of report types defined in this plugin to ReportGenerator report types. + REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME = { + ReportTypes::HTML_BASIC.upcase => "HtmlSummary", + ReportTypes::HTML_DETAILED.upcase => "Html", + ReportTypes::HTML_CHART.upcase => "HtmlChart", + ReportTypes::HTML_INLINE.upcase => "HtmlInline", + ReportTypes::HTML_INLINE_AZURE.upcase => "HtmlInline_AzurePipelines", + ReportTypes::HTML_INLINE_AZURE_DARK.upcase => "HtmlInline_AzurePipelines_Dark", + ReportTypes::MHTML.upcase => "MHtml", + ReportTypes::TEXT.upcase => "TextSummary", + ReportTypes::COBERTURA.upcase => "Cobertura", + ReportTypes::SONARQUBE.upcase => "SonarQube", + ReportTypes::BADGES.upcase => "Badges", + ReportTypes::CSV_SUMMARY.upcase => "CsvSummary", + ReportTypes::LATEX.upcase => "Latex", + ReportTypes::LATEX_SUMMARY.upcase => "LatexSummary", + ReportTypes::PNG_CHART.upcase => "PngChart", + ReportTypes::TEAM_CITY_SUMMARY.upcase => "TeamCitySummary", + ReportTypes::LCOV.upcase => "lcov", + ReportTypes::XML.upcase => "Xml", + ReportTypes::XML_SUMMARY.upcase => "XmlSummary", + } + + REPORT_GENERATOR_SETTING_PREFIX = "gcov_report_generator" + + # Deep clone the gcov tool config, so we can modify it locally if specified via options. + GCOV_TOOL_CONFIG = Marshal.load(Marshal.dump(TOOLS_GCOV_GCOV_POST_REPORT)) + + # Build the ReportGenerator arguments. + def args_builder(opts) + rg_opts = get_opts(opts) + report_type_count = 0 + + args = "" + args += "\"-reports:*.gcov\" " + args += "\"-targetdir:\"#{GCOV_REPORT_GENERATOR_PATH}\"\" " + + # Build the report types argument. + if !(opts.nil?) && !(opts[:gcov_reports].nil?) && !(opts[:gcov_reports].empty?) + args += "\"-reporttypes:" + + for report_type in opts[:gcov_reports] + rg_report_type = REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME[report_type.upcase] + if !(rg_report_type.nil?) + args += rg_report_type + ";" + report_type_count = report_type_count + 1 + end + end + + # Removing trailing ';' after the last report type. + args = args.chomp(";") + + # Append a space seperator after the report type. + args += "\" " + end + + # Build the source directories argument. + args += "\"-sourcedirs:.;" + if !(opts[:collection_paths_source].nil?) + args += opts[:collection_paths_source].join(';') + end + args = args.chomp(";") + args += "\" " + + args += "\"-historydir:#{rg_opts[:history_directory]}\" " unless rg_opts[:history_directory].nil? + args += "\"-plugins:#{rg_opts[:plugins]}\" " unless rg_opts[:plugins].nil? + args += "\"-assemblyfilters:#{rg_opts[:assembly_filters]}\" " unless rg_opts[:assembly_filters].nil? + args += "\"-classfilters:#{rg_opts[:class_filters]}\" " unless rg_opts[:class_filters].nil? + file_filters = rg_opts[:file_filters] || @ceedling[:tool_executor_helper].osify_path_separators(GCOV_REPORT_GENERATOR_FILE_FILTERS) + args += "\"-filefilters:#{file_filters}\" " + args += "\"-verbosity:#{rg_opts[:verbosity] || "Warning"}\" " + args += "\"-tag:#{rg_opts[:tag]}\" " unless rg_opts[:tag].nil? + args += "\"settings:createSubdirectoryForAllReportTypes=true\" " unless report_type_count <= 1 + args += "\"settings:numberOfReportsParsedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil? + args += "\"settings:numberOfReportsMergedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil? + + # Append custom arguments. + if !(rg_opts[:custom_args].nil?) && !(rg_opts[:custom_args].empty?) + for custom_arg in rg_opts[:custom_args] + args += "\"#{custom_arg}\" " unless custom_arg.nil? || custom_arg.empty? + end + end + + return args + end + + + # Get the ReportGenerator options from the project options. + def get_opts(opts) + return opts[REPORT_GENERATOR_SETTING_PREFIX.to_sym] || {} + end + + + # Run ReportGenerator with the given arguments. + def run(args) + command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORTGENERATOR_POST_REPORT, [], args) + return @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + + + # Run gcov with the given arguments. + def run_gcov(args) + command = @ceedling[:tool_executor].build_command_line(GCOV_TOOL_CONFIG, [], args) + return @ceedling[:tool_executor].exec(command[:line], command[:options]) + end + +end diff --git a/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb b/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb new file mode 100644 index 000000000..92617fba5 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/gcov/lib/reportinator_helper.rb @@ -0,0 +1,15 @@ + +class ReportinatorHelper + + # Output the shell result to the console. + def print_shell_result(shell_result) + if !(shell_result.nil?) + puts "Done in %.3f seconds." % shell_result[:time] + + if !(shell_result[:output].nil?) && (shell_result[:output].length > 0) + puts shell_result[:output] + end + end + end + +end diff --git a/test/unit-test/vendor/ceedling/plugins/json_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/json_tests_report/README.md new file mode 100644 index 000000000..8e5a1e571 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/json_tests_report/README.md @@ -0,0 +1,36 @@ +json_tests_report +================= + +## Overview + +The json_tests_report plugin creates a JSON file of test results, which is +handy for Continuous Integration build servers or as input into other +reporting tools. The JSON file is output to the appropriate +`<build_root>/artifacts/` directory (e.g. `artifacts/test/` for test tasks, +`artifacts/gcov/` for gcov, or `artifacts/bullseye/` for bullseye runs). + +## Setup + +Enable the plugin in your project.yml by adding `json_tests_report` to the list +of enabled plugins. + +``` YAML +:plugins: + :enabled: + - json_tests_report +``` + +## Configuration + +Optionally configure the output / artifact filename in your project.yml with +the `artifact_filename` configuration option. The default filename is +`report.json`. + +You can also configure the path that this artifact is stored. This can be done +by setting `path`. The default is that it will be placed in a subfolder under +the `build` directory. + +``` YAML +:json_tests_report: + :artifact_filename: report_spectuluarly.json +``` diff --git a/test/unit-test/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb new file mode 100644 index 000000000..e7023db7b --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/json_tests_report/lib/json_tests_report.rb @@ -0,0 +1,83 @@ +require 'ceedling/plugin' +require 'ceedling/constants' +require 'json' + +class JsonTestsReport < Plugin + def setup + @results_list = {} + @test_counter = 0 + end + + def post_test_fixture_execute(arg_hash) + context = arg_hash[:context] + + @results_list[context] = [] if @results_list[context].nil? + + @results_list[context] << arg_hash[:result_file] + end + + def post_build + @results_list.each_key do |context| + results = @ceedling[:plugin_reportinator].assemble_test_results(@results_list[context]) + + artifact_filename = @ceedling[:configurator].project_config_hash[:json_tests_report_artifact_filename] || 'report.json' + artifact_fullpath = @ceedling[:configurator].project_config_hash[:json_tests_report_path] || File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s) + file_path = File.join(artifact_fullpath, artifact_filename) + + @ceedling[:file_wrapper].open(file_path, 'w') do |f| + @test_counter = 1 + + json = { + "FailedTests" => write_failures(results[:failures]), + "PassedTests" => write_tests(results[:successes]), + "IgnoredTests" => write_tests(results[:ignores]), + "Summary" => write_statistics(results[:counts]) + } + + f << JSON.pretty_generate(json) + end + end + end + + private + + def write_failures(results) + retval = [] + results.each do |result| + result[:collection].each do |item| + @test_counter += 1 + retval << { + "file" => File.join(result[:source][:path], result[:source][:file]), + "test" => item[:test], + "line" => item[:line], + "message" => item[:message] + } + end + end + return retval.uniq + end + + def write_tests(results) + retval = [] + results.each do |result| + result[:collection].each do |item| + @test_counter += 1 + retval << { + "file" => File.join(result[:source][:path], result[:source][:file]), + "test" => item[:test] + } + end + end + return retval + end + + def write_statistics(counts) + return { + "total_tests" => counts[:total], + "passed" => (counts[:total] - counts[:ignored] - counts[:failed]), + "ignored" => counts[:ignored], + "failures" => counts[:failed] + } + end + +end diff --git a/test/vendor/ceedling/plugins/junit_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/junit_tests_report/README.md similarity index 100% rename from test/vendor/ceedling/plugins/junit_tests_report/README.md rename to test/unit-test/vendor/ceedling/plugins/junit_tests_report/README.md diff --git a/test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb similarity index 86% rename from test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb index a777d07d7..310439380 100644 --- a/test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb +++ b/test/unit-test/vendor/ceedling/plugins/junit_tests_report/lib/junit_tests_report.rb @@ -44,7 +44,7 @@ class JunitTestsReport < Plugin def write_header( results, stream ) results[:counts][:time] = @time_result.reduce(0, :+) stream.puts '<?xml version="1.0" encoding="utf-8" ?>' - stream.puts('<testsuites tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" time="%<time>f">' % results[:counts]) + stream.puts('<testsuites tests="%<total>d" failures="%<failed>d" time="%<time>.3f">' % results[:counts]) end def write_footer( stream ) @@ -53,7 +53,7 @@ class JunitTestsReport < Plugin def reorganise_results( results ) # Reorganise the output by test suite instead of by result - suites = Hash.new{ |h,k| h[k] = {collection: [], total: 0, success: 0, failed: 0, ignored: 0, stdout: []} } + suites = Hash.new{ |h,k| h[k] = {collection: [], total: 0, success: 0, failed: 0, ignored: 0, errors: 0, stdout: []} } results[:successes].each do |result| source = result[:source] name = source[:file].sub(/\..{1,4}$/, "") @@ -85,7 +85,7 @@ class JunitTestsReport < Plugin def write_suite( suite, stream ) suite[:time] = @time_result.shift - stream.puts(' <testsuite name="%<name>s" tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" time="%<time>f">' % suite) + stream.puts(' <testsuite name="%<name>s" tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" errors="%<errors>d" time="%<time>.3f">' % suite) suite[:collection].each do |test| write_test( test, stream ) @@ -108,12 +108,17 @@ class JunitTestsReport < Plugin end def write_test( test, stream ) - test[:test].gsub!('"', '"') + test[:test].gsub!(/&/, '&') + test[:test].gsub!(/</, '<') + test[:test].gsub!(/>/, '>') + test[:test].gsub!(/"/, '"') + test[:test].gsub!(/'/, ''') + case test[:result] when :success - stream.puts(' <testcase name="%<test>s" />' % test) + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f"/>' % test) when :failed - stream.puts(' <testcase name="%<test>s">' % test) + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test) if test[:message].empty? stream.puts(' <failure />') else @@ -121,7 +126,7 @@ class JunitTestsReport < Plugin end stream.puts(' </testcase>') when :ignored - stream.puts(' <testcase name="%<test>s">' % test) + stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test) stream.puts(' <skipped />') stream.puts(' </testcase>') end diff --git a/test/unit-test/vendor/ceedling/plugins/module_generator/README.md b/test/unit-test/vendor/ceedling/plugins/module_generator/README.md new file mode 100644 index 000000000..a3c2c7ad9 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/module_generator/README.md @@ -0,0 +1,119 @@ +ceedling-module-generator +========================= + +## Overview + +The module_generator plugin adds a pair of new commands to Ceedling, allowing +you to make or remove modules according to predefined templates. WIth a single call, +Ceedling can generate a source, header, and test file for a new module. If given a +pattern, it can even create a series of submodules to support specific design patterns. +Finally, it can just as easily remove related modules, avoiding the need to delete +each individually. + +Let's say, for example, that you want to create a single module named `MadScience`. + +``` +ceedling module:create[MadScience] +``` + +It says we're speaking to the module plugin, and we want to create a new module. The +name of that module is between the brackets. It will keep this case, unless you have +specified a different default (see configuration). It will create three files: +`MadScience.c`, `MadScience.h`, and `TestMadScience.c`. *NOTE* that it is important that +there are no spaces between the brackets. We know, it's annoying... but it's the rules. + +You can also create an entire pattern of files. To do that, just add a second argument +to the pattern ID. Something like this: + +``` +ceedling module:create[SecretLair,mch] +``` + +In this example, we'd create 9 files total: 3 headers, 3 source files, and 3 test files. These +files would be named `SecretLairModel`, `SecretLairConductor`, and `SecretLairHardware`. Isn't +that nice? + +Similarly, you can create stubs for all functions in a header file just by making a single call +to your handy `stub` feature, like this: + +``` +ceedling module:stub[SecretLair] +``` + +This call will look in SecretLair.h and will generate a file SecretLair.c that contains a stub +for each function declared in the header! Even better, if SecretLair.c already exists, it will +add only new functions, leaving your existing calls alone so that it doesn't cause any problems. + +## Configuration + +Enable the plugin in your project.yml by adding `module_generator` +to the list of enabled plugins. + +Then, like much of Ceedling, you can just run as-is with the defaults, or you can override those +defaults for your own needs. For example, new source and header files will be automatically +placed in the `src/` folder while tests will go in the `test/` folder. That's great if your project +follows the default ceedling structure... but what if you have a different structure? + +``` +:module_generator: + :project_root: ./ + :source_root: source/ + :inc_root: includes/ + :test_root: tests/ +``` + +Now I've redirected the location where modules are going to be generated. + +### Includes + +You can make it so that all of your files are generated with a standard include list. This is done +by adding to the `:includes` array. For example: + +``` +:module_generator: + :includes: + :tst: + - defs.h + - board.h + :src: + - board.h +``` + +### Boilerplates + +You can specify the actual boilerplate used for each of your files. This is the handy place to +put that corporate copyright notice (or maybe a copyleft notice, if that's your perference?) + +``` +:module_generator: + :boilerplates: | + /*************************** + * This file is Awesome. * + * That is All. * + ***************************/ +``` + +### Test Defines + +You can specify the "#ifdef TEST" at the top of the test files with a custom define. +This example will put a "#ifdef CEEDLING_TEST" at the top of the test files. + +``` +:module_generator: + :test_define: CEEDLING_TEST +``` + +### Naming Convention + +Finally, you can force a particular naming convention. Even if someone calls the generator +with something like `MyNewModule`, if they have the naming convention set to `:caps`, it will +generate files like `MY_NEW_MODULE.c`. This keeps everyone on your team behaving the same way. + +Your options are as follows: + + - `:bumpy` - BumpyFilesLooksLikeSo + - `:camel` - camelFilesAreSimilarButStartLow + - `:snake` - snake_case_is_all_lower_and_uses_underscores + - `:caps` - CAPS_FEELS_LIKE_YOU_ARE_SCREAMING + + diff --git a/test/vendor/ceedling/plugins/module_generator/config/module_generator.yml b/test/unit-test/vendor/ceedling/plugins/module_generator/config/module_generator.yml similarity index 100% rename from test/vendor/ceedling/plugins/module_generator/config/module_generator.yml rename to test/unit-test/vendor/ceedling/plugins/module_generator/config/module_generator.yml diff --git a/test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb b/test/unit-test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb similarity index 79% rename from test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb rename to test/unit-test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb index b2fac006d..d14288c36 100644 --- a/test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb +++ b/test/unit-test/vendor/ceedling/plugins/module_generator/lib/module_generator.rb @@ -18,6 +18,14 @@ class ModuleGenerator < Plugin end end + def stub_from_header(module_name, optz={}) + require "cmock.rb" #From CMock + stuboptz = divine_options(optz) + pathname = optz[:path_inc] || optz[:path_src] || "src" + filename = File.expand_path(optz[:module_root_path], File.join(pathname, module_name + ".h")) + CMock.new(stuboptz).setup_skeletons(filename) + end + private def divine_options(optz={}) @@ -37,6 +45,8 @@ class ModuleGenerator < Plugin :boilerplates => ((defined? MODULE_GENERATOR_BOILERPLATES) ? MODULE_GENERATOR_BOILERPLATES : {} ), :naming => ((defined? MODULE_GENERATOR_NAMING ) ? MODULE_GENERATOR_NAMING : nil ), :update_svn => ((defined? MODULE_GENERATOR_UPDATE_SVN ) ? MODULE_GENERATOR_UPDATE_SVN : false ), + :skeleton_path=> ((defined? MODULE_GENERATOR_SOURCE_ROOT ) ? MODULE_GENERATOR_SOURCE_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') : "src" ), + :test_define => ((defined? MODULE_GENERATOR_TEST_DEFINE ) ? MODULE_GENERATOR_TEST_DEFINE : "TEST" ), } # Read Boilerplate template file. @@ -44,15 +54,15 @@ class ModuleGenerator < Plugin bf = MODULE_GENERATOR_BOILERPLATE_FILES - if !bf[:src].nil? && File.exists?(bf[:src]) + if !bf[:src].nil? && File.exists?(bf[:src]) unity_generator_options[:boilerplates][:src] = File.read(bf[:src]) end - if !bf[:inc].nil? && File.exists?(bf[:inc]) + if !bf[:inc].nil? && File.exists?(bf[:inc]) unity_generator_options[:boilerplates][:inc] = File.read(bf[:inc]) end - if !bf[:tst].nil? && File.exists?(bf[:tst]) + if !bf[:tst].nil? && File.exists?(bf[:tst]) unity_generator_options[:boilerplates][:tst] = File.read(bf[:tst]) end end diff --git a/test/vendor/ceedling/plugins/module_generator/module_generator.rake b/test/unit-test/vendor/ceedling/plugins/module_generator/module_generator.rake similarity index 75% rename from test/vendor/ceedling/plugins/module_generator/module_generator.rake rename to test/unit-test/vendor/ceedling/plugins/module_generator/module_generator.rake index e88e346aa..f4ed9f113 100644 --- a/test/vendor/ceedling/plugins/module_generator/module_generator.rake +++ b/test/unit-test/vendor/ceedling/plugins/module_generator/module_generator.rake @@ -26,6 +26,21 @@ namespace :module do end end + desc "Generate module stubs from header" + task :stub, :module_path do |t, args| + files = [args[:module_path]] + (args.extras || []) + optz = { :module_root_path => "" } + files.each do |v| + module_root_path, module_name = v.split(module_root_separator, 2) + if module_name + optz[:module_root_path] = module_root_path + v = module_name + end + # Otherwise, go through the normal procedure + @ceedling[:module_generator].stub_from_header(v, optz) + end + end + desc "Destroy module (source, header and test files)" task :destroy, :module_path do |t, args| files = [args[:module_path]] + (args.extras || []) diff --git a/test/unit-test/vendor/ceedling/plugins/raw_output_report/README.md b/test/unit-test/vendor/ceedling/plugins/raw_output_report/README.md new file mode 100644 index 000000000..330e87d39 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/raw_output_report/README.md @@ -0,0 +1,19 @@ +ceedling-raw-output-report +========================== + +## Overview + +The raw-output-report allows you to capture all the output from the called +tools in a single document, so you can trace back through it later. This is +useful for debugging... but can eat through memory quickly if left running. + +## Setup + +Enable the plugin in your project.yml by adding `raw_output_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - raw_output_report +``` diff --git a/test/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb b/test/unit-test/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb rename to test/unit-test/vendor/ceedling/plugins/raw_output_report/lib/raw_output_report.rb diff --git a/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md new file mode 100644 index 000000000..9ab60847e --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/README.md @@ -0,0 +1,19 @@ +ceedling-stdout-gtestlike-tests-report +====================== + +## Overview + +The stdout_gtestlike_tests_report replaces the normal ceedling "pretty" output with +a variant that resembles the output of gtest. This is most helpful when trying to +integrate into an IDE or CI that is meant to work with google test. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_gtestlike_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_gtestlike_tests_report +``` diff --git a/test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb similarity index 100% rename from test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb rename to test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb diff --git a/test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy similarity index 100% rename from test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy rename to test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/assets/template.erb copy diff --git a/test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml similarity index 100% rename from test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml rename to test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/config/stdout_gtestlike_tests_report.yml diff --git a/test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/stdout_gtestlike_tests_report/lib/stdout_gtestlike_tests_report.rb diff --git a/test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/README.md new file mode 100644 index 000000000..ed6c65582 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/README.md @@ -0,0 +1,18 @@ +ceedling-stdout-ide-tests-report +================================ + +## Overview + +The stdout_ide_tests_report replaces the normal ceedling "pretty" output with +a simplified variant intended to be easily parseable. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_ide_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_ide_tests_report +``` diff --git a/test/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml b/test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml similarity index 100% rename from test/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml rename to test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/config/stdout_ide_tests_report.yml diff --git a/test/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/stdout_ide_tests_report/lib/stdout_ide_tests_report.rb diff --git a/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md new file mode 100644 index 000000000..7e1be2382 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/README.md @@ -0,0 +1,20 @@ +ceedling-pretty-tests-report +============================ + +## Overview + +The stdout_pretty_tests_report is the default output of ceedling. Instead of +showing most of the raw output of CMock, Ceedling, etc., it shows a simplified +view. It also creates a nice summary at the end of execution which groups the +results into ignored and failed tests. + +## Setup + +Enable the plugin in your project.yml by adding `stdout_pretty_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - stdout_pretty_tests_report +``` diff --git a/test/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb b/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb similarity index 100% rename from test/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb rename to test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/assets/template.erb diff --git a/test/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml b/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml similarity index 100% rename from test/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml rename to test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/config/stdout_pretty_tests_report.yml diff --git a/test/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/stdout_pretty_tests_report/lib/stdout_pretty_tests_report.rb diff --git a/test/vendor/ceedling/plugins/subprojects/README.md b/test/unit-test/vendor/ceedling/plugins/subprojects/README.md similarity index 100% rename from test/vendor/ceedling/plugins/subprojects/README.md rename to test/unit-test/vendor/ceedling/plugins/subprojects/README.md diff --git a/test/vendor/ceedling/plugins/subprojects/config/defaults.yml b/test/unit-test/vendor/ceedling/plugins/subprojects/config/defaults.yml similarity index 100% rename from test/vendor/ceedling/plugins/subprojects/config/defaults.yml rename to test/unit-test/vendor/ceedling/plugins/subprojects/config/defaults.yml diff --git a/test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb b/test/unit-test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb similarity index 98% rename from test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb rename to test/unit-test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb index 1f5d4c2f7..559251ed8 100644 --- a/test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb +++ b/test/unit-test/vendor/ceedling/plugins/subprojects/lib/subprojects.rb @@ -20,7 +20,7 @@ class Subprojects < Plugin end end - #gather information about the subprojects + # Gather information about the subprojects @subprojects = {} @subproject_lookup_by_path = {} SUBPROJECTS_PATHS.each do |subproj| @@ -84,7 +84,7 @@ class Subprojects < Plugin Object.send(:remove_const, constant.to_sym) if (Object.const_defined? constant) Object.const_set(constant, new_value) end - + end # end blocks always executed following rake run diff --git a/test/vendor/ceedling/plugins/subprojects/subprojects.rake b/test/unit-test/vendor/ceedling/plugins/subprojects/subprojects.rake similarity index 100% rename from test/vendor/ceedling/plugins/subprojects/subprojects.rake rename to test/unit-test/vendor/ceedling/plugins/subprojects/subprojects.rake diff --git a/test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/README.md new file mode 100644 index 000000000..9fcda7d5b --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/README.md @@ -0,0 +1,18 @@ +ceedling-teamcity-tests-report +============================== + +## Overview + +The teamcity_tests_report replaces the normal ceedling "pretty" output with +a version that has results tagged to be consumed with the teamcity CI server. + +## Setup + +Enable the plugin in your project.yml by adding `teamcity_tests_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - teamcity_tests_report +``` diff --git a/test/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml b/test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml similarity index 100% rename from test/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml rename to test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/config/teamcity_tests_report.yml diff --git a/test/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/teamcity_tests_report/lib/teamcity_tests_report.rb diff --git a/test/unit-test/vendor/ceedling/plugins/warnings_report/README.md b/test/unit-test/vendor/ceedling/plugins/warnings_report/README.md new file mode 100644 index 000000000..fd7fae5d9 --- /dev/null +++ b/test/unit-test/vendor/ceedling/plugins/warnings_report/README.md @@ -0,0 +1,19 @@ +warnings-report +=============== + +## Overview + +The warnings_report captures all warnings throughout the build process +and collects them into a single report at the end of execution. It places all +of this into a warnings file in the output artifact directory. + +## Setup + +Enable the plugin in your project.yml by adding `warnings_report` +to the list of enabled plugins. + +``` YAML +:plugins: + :enabled: + - warnings_report +``` diff --git a/test/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb b/test/unit-test/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb rename to test/unit-test/vendor/ceedling/plugins/warnings_report/lib/warnings_report.rb diff --git a/test/vendor/ceedling/plugins/xml_tests_report/README.md b/test/unit-test/vendor/ceedling/plugins/xml_tests_report/README.md similarity index 97% rename from test/vendor/ceedling/plugins/xml_tests_report/README.md rename to test/unit-test/vendor/ceedling/plugins/xml_tests_report/README.md index ce81eadf3..6200c7dea 100644 --- a/test/vendor/ceedling/plugins/xml_tests_report/README.md +++ b/test/unit-test/vendor/ceedling/plugins/xml_tests_report/README.md @@ -1,5 +1,5 @@ xml_tests_report -==================== +================ ## Overview diff --git a/test/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb b/test/unit-test/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb similarity index 100% rename from test/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb rename to test/unit-test/vendor/ceedling/plugins/xml_tests_report/lib/xml_tests_report.rb diff --git a/test/vendor/ceedling/vendor/c_exception/lib/CException.c b/test/unit-test/vendor/ceedling/vendor/c_exception/lib/CException.c similarity index 100% rename from test/vendor/ceedling/vendor/c_exception/lib/CException.c rename to test/unit-test/vendor/ceedling/vendor/c_exception/lib/CException.c diff --git a/test/vendor/ceedling/vendor/c_exception/lib/CException.h b/test/unit-test/vendor/ceedling/vendor/c_exception/lib/CException.h similarity index 99% rename from test/vendor/ceedling/vendor/c_exception/lib/CException.h rename to test/unit-test/vendor/ceedling/vendor/c_exception/lib/CException.h index 78f2f940c..be9e18695 100644 --- a/test/vendor/ceedling/vendor/c_exception/lib/CException.h +++ b/test/unit-test/vendor/ceedling/vendor/c_exception/lib/CException.h @@ -11,7 +11,7 @@ extern "C" #define CEXCEPTION_VERSION_MAJOR 1 #define CEXCEPTION_VERSION_MINOR 3 -#define CEXCEPTION_VERSION_BUILD 2 +#define CEXCEPTION_VERSION_BUILD 3 #define CEXCEPTION_VERSION ((CEXCEPTION_VERSION_MAJOR << 16) | (CEXCEPTION_VERSION_MINOR << 8) | CEXCEPTION_VERSION_BUILD) //To Use CException, you have a number of options: diff --git a/test/unit-test/vendor/ceedling/vendor/c_exception/lib/meson.build b/test/unit-test/vendor/ceedling/vendor/c_exception/lib/meson.build new file mode 100644 index 000000000..2770122af --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/c_exception/lib/meson.build @@ -0,0 +1,11 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mark VanderVoord. +# +# license: MIT +# +cexception_dir = include_directories('.') + +cexception_lib = static_library(meson.project_name(), + files('CException.c'), + include_directories : cexception_dir) diff --git a/test/vendor/ceedling/vendor/cmock/config/production_environment.rb b/test/unit-test/vendor/ceedling/vendor/cmock/config/production_environment.rb similarity index 65% rename from test/vendor/ceedling/vendor/cmock/config/production_environment.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/config/production_environment.rb index 915582b79..082b63fde 100644 --- a/test/vendor/ceedling/vendor/cmock/config/production_environment.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/config/production_environment.rb @@ -2,13 +2,11 @@ # CMock Project - Automatic Mock Generation for C # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams # [Released under MIT License. Please refer to license.txt for details] -# ========================================== - +# ========================================== + # Setup our load path: -[ - 'lib', +[ + 'lib' ].each do |dir| - $LOAD_PATH.unshift( File.join( File.expand_path(File.dirname(__FILE__)) + '/../', dir) ) + $:.unshift(File.join(__dir__ + '/../', dir)) end - - diff --git a/test/vendor/ceedling/vendor/cmock/config/test_environment.rb b/test/unit-test/vendor/ceedling/vendor/cmock/config/test_environment.rb similarity index 82% rename from test/vendor/ceedling/vendor/cmock/config/test_environment.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/config/test_environment.rb index fe1ed817a..aeae3a342 100644 --- a/test/vendor/ceedling/vendor/cmock/config/test_environment.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/config/test_environment.rb @@ -12,5 +12,5 @@ './vendor/unity/auto/', './test/system/' ].each do |dir| - $LOAD_PATH.unshift( File.join( File.expand_path(File.dirname(__FILE__) + "/../"), dir) ) + $:.unshift(File.join(File.expand_path(File.dirname(__FILE__) + '/../'), dir)) end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock.rb new file mode 100644 index 000000000..72f864189 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock.rb @@ -0,0 +1,111 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +['../config/production_environment', + 'cmock_header_parser', + 'cmock_generator', + 'cmock_file_writer', + 'cmock_config', + 'cmock_plugin_manager', + 'cmock_generator_utils', + 'cmock_unityhelper_parser'].each { |req| require "#{__dir__}/#{req}" } + +class CMock + def initialize(options = nil) + cm_config = CMockConfig.new(options) + cm_unityhelper = CMockUnityHelperParser.new(cm_config) + cm_writer = CMockFileWriter.new(cm_config) + cm_gen_utils = CMockGeneratorUtils.new(cm_config, + :unity_helper => cm_unityhelper) + cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils) + @cm_parser = CMockHeaderParser.new(cm_config) + @cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils, + cm_gen_plugins) + @silent = (cm_config.verbosity < 2) + end + + def setup_mocks(files, folder = nil) + [files].flatten.each do |src| + generate_mock(src, folder) + end + end + + def setup_skeletons(files) + [files].flatten.each do |src| + generate_skeleton src + end + end + + private ############################### + + def generate_mock(src, folder) + name = File.basename(src, '.*') + ext = File.extname(src) + puts "Creating mock for #{name}..." unless @silent + @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder) + end + + def generate_skeleton(src) + name = File.basename(src, '.*') + puts "Creating skeleton for #{name}..." unless @silent + @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src))) + end +end + +def option_maker(options, key, val) + options ||= {} + options[key.to_sym] = + if val.chr == ':' + val[1..-1].to_sym + elsif val.include? ';' + val.split(';') + elsif val == 'true' + true + elsif val == 'false' + false + elsif val =~ /^\d+$/ + val.to_i + else + val + end + options +end + +# Command Line Support ############################### + +if $0 == __FILE__ + usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock" + + unless ARGV[0] + puts usage + exit 1 + end + + options = {} + filelist = [] + ARGV.each do |arg| + if arg =~ /^-o\"?([a-zA-Z0-9@._\\\/:\s]+)\"?/ + options.merge! CMockConfig.load_config_file_from_yaml(arg.gsub(/^-o/, '')) + elsif arg == '--skeleton' + options[:skeleton] = true + elsif arg =~ /^--strippables=\"?(.*)\"?/ + # --strippables are dealt with separately since the user is allowed to + # enter any valid regular expression as argument + options = option_maker(options, 'strippables', Regexp.last_match(1)) + elsif arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]*)\"?/x + options = option_maker(options, Regexp.last_match(1), + Regexp.last_match(2)) + else + filelist << arg + end + end + + if options[:skeleton] + CMock.new(options).setup_skeletons(filelist) + else + CMock.new(options).setup_mocks(filelist) + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb new file mode 100644 index 000000000..716a0c55e --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb @@ -0,0 +1,174 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockConfig + CMOCK_DEFAULT_OPTIONS = + { + :framework => :unity, + :mock_path => 'mocks', + :mock_prefix => 'Mock', + :mock_suffix => '', + :skeleton_path => '', + :weak => '', + :subdir => nil, + :plugins => [], + :strippables => ['(?:__attribute__\s*\(+.*?\)+)'], + :attributes => %w[__ramfunc __irq __fiq register extern], + :c_calling_conventions => %w[__stdcall __cdecl __fastcall], + :enforce_strict_ordering => false, + :fail_on_unexpected_calls => true, + :unity_helper_path => false, + :treat_as => {}, + :treat_as_array => {}, + :treat_as_void => [], + :memcmp_if_unknown => true, + :when_no_prototypes => :warn, # the options being :ignore, :warn, or :error + :when_ptr => :compare_data, # the options being :compare_ptr, :compare_data, or :smart + :verbosity => 2, # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose + :treat_externs => :exclude, # the options being :include or :exclude + :treat_inlines => :exclude, # the options being :include or :exclude + :callback_include_count => true, + :callback_after_arg_check => false, + :includes => nil, + :includes_h_pre_orig_header => nil, + :includes_h_post_orig_header => nil, + :includes_c_pre_header => nil, + :includes_c_post_header => nil, + :orig_header_include_fmt => '#include "%s"', + :array_size_type => [], + :array_size_name => 'size|len', + :skeleton => false, + :exclude_setjmp_h => false, + + # Format to look for inline functions. + # This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static") + # There are several possibilities: + # - sometimes they appear together, sometimes individually, + # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff), + # so we check for word boundaries when searching for them + # - We first remove "static inline" combinations and boil down to single inline or static statements + :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] # Last part (\s*) is just to remove whitespaces (only to prettify the output) + }.freeze + + def initialize(options = nil) + case options + when NilClass then options = CMOCK_DEFAULT_OPTIONS.dup + when String then options = CMOCK_DEFAULT_OPTIONS.dup.merge(load_config_file_from_yaml(options)) + when Hash then options = CMOCK_DEFAULT_OPTIONS.dup.merge(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + + # do some quick type verification + %i[plugins attributes treat_as_void].each do |opt| + unless options[opt].class == Array + options[opt] = [] + puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1 + end + end + %i[includes includes_h_pre_orig_header includes_h_post_orig_header includes_c_pre_header includes_c_post_header].each do |opt| + unless options[opt].nil? || (options[opt].class == Array) + options[opt] = [] + puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1 + end + end + options[:unity_helper_path] ||= options[:unity_helper] + options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String + + if options[:unity_helper_path] + require 'pathname' + includes1 = options[:includes_c_post_header] || [] + includes2 = options[:unity_helper_path].map do |path| + Pathname(path).relative_path_from(Pathname(options[:mock_path])).to_s + end + options[:includes_c_post_header] = (includes1 + includes2).uniq + end + + options[:plugins].compact! + options[:plugins].map!(&:to_sym) + @options = options + + treat_as_map = standard_treat_as_map # .clone + treat_as_map.merge!(@options[:treat_as]) + @options[:treat_as] = treat_as_map + + @options.each_key do |key| + unless methods.include?(key) + eval("def #{key}() return @options[:#{key}] end") + end + end + end + + def load_config_file_from_yaml(yaml_filename) + self.class.load_config_file_from_yaml yaml_filename + end + + def self.load_config_file_from_yaml(yaml_filename) + require 'yaml' + require 'fileutils' + YAML.load_file(yaml_filename)[:cmock] + end + + def path(new_path) + @src_path = new_path + end + + def load_unity_helper + return nil unless @options[:unity_helper_path] + + @options[:unity_helper_path].inject('') do |unity_helper, filename| + unity_helper + "\n" + File.new(filename).read + end + end + + def standard_treat_as_map + { + 'int' => 'INT', + 'char' => 'INT8', + 'short' => 'INT16', + 'long' => 'INT', + 'int8' => 'INT8', + 'int16' => 'INT16', + 'int32' => 'INT', + 'int8_t' => 'INT8', + 'int16_t' => 'INT16', + 'int32_t' => 'INT', + 'INT8_T' => 'INT8', + 'INT16_T' => 'INT16', + 'INT32_T' => 'INT', + 'bool' => 'INT', + 'bool_t' => 'INT', + 'BOOL' => 'INT', + 'BOOL_T' => 'INT', + 'unsigned int' => 'HEX32', + 'unsigned long' => 'HEX32', + 'uint32' => 'HEX32', + 'uint32_t' => 'HEX32', + 'UINT32' => 'HEX32', + 'UINT32_T' => 'HEX32', + 'void*' => 'HEX8_ARRAY', + 'void const*' => 'HEX8_ARRAY', + 'const void*' => 'HEX8_ARRAY', + 'unsigned short' => 'HEX16', + 'uint16' => 'HEX16', + 'uint16_t' => 'HEX16', + 'UINT16' => 'HEX16', + 'UINT16_T' => 'HEX16', + 'unsigned char' => 'HEX8', + 'uint8' => 'HEX8', + 'uint8_t' => 'HEX8', + 'UINT8' => 'HEX8', + 'UINT8_T' => 'HEX8', + 'char*' => 'STRING', + 'char const*' => 'STRING', + 'const char*' => 'STRING', + 'pCHAR' => 'STRING', + 'cstring' => 'STRING', + 'CSTRING' => 'STRING', + 'float' => 'FLOAT', + 'double' => 'FLOAT' + } + end +end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb similarity index 50% rename from test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb index d2d954cbc..f30c44b24 100644 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_file_writer.rb @@ -5,7 +5,6 @@ # ========================================== class CMockFileWriter - attr_reader :config def initialize(config) @@ -13,32 +12,36 @@ class CMockFileWriter end def create_subdir(subdir) - if !Dir.exists?("#{@config.mock_path}/") - require 'fileutils' - FileUtils.mkdir_p "#{@config.mock_path}/" - end - if subdir && !Dir.exists?("#{@config.mock_path}/#{subdir+'/' if subdir}") - require 'fileutils' - FileUtils.mkdir_p "#{@config.mock_path}/#{subdir+'/' if subdir}" - end + require 'fileutils' + FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/") + FileUtils.mkdir_p "#{@config.mock_path}/#{subdir + '/' if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{subdir + '/' if subdir}") end def create_file(filename, subdir) raise "Where's the block of data to create?" unless block_given? - full_file_name_temp = "#{@config.mock_path}/#{subdir+'/' if subdir}#{filename}.new" - full_file_name_done = "#{@config.mock_path}/#{subdir+'/' if subdir}#{filename}" + + full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" + full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" File.open(full_file_name_temp, 'w') do |file| yield(file, filename) end update_file(full_file_name_done, full_file_name_temp) end + def append_file(filename, subdir) + raise "Where's the block of data to create?" unless block_given? + + full_file_name = "#{@config.skeleton_path}/#{subdir + '/' if subdir}#{filename}" + File.open(full_file_name, 'a') do |file| + yield(file, filename) + end + end + private ################################### def update_file(dest, src) require 'fileutils' - FileUtils.rm(dest) if (File.exist?(dest)) - FileUtils.cp(src, dest) - FileUtils.rm(src) + FileUtils.rm(dest, :force => true) + FileUtils.mv(src, dest) end end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb new file mode 100644 index 000000000..6ed51109f --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb @@ -0,0 +1,368 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGenerator + attr_accessor :config, :file_writer, :module_name, :module_ext, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered + + def initialize(config, file_writer, utils, plugins) + @file_writer = file_writer + @utils = utils + @plugins = plugins + @config = config + @prefix = @config.mock_prefix + @suffix = @config.mock_suffix + @weak = @config.weak + @include_inline = @config.treat_inlines + @ordered = @config.enforce_strict_ordering + @framework = @config.framework.to_s + @fail_on_unexpected_calls = @config.fail_on_unexpected_calls + @exclude_setjmp_h = @config.exclude_setjmp_h + @subdir = @config.subdir + + @includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_c_pre_header = (@config.includes_c_pre_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + @includes_c_post_header = (@config.includes_c_post_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } + + here = File.dirname __FILE__ + unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling + unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock + # path to Unity as specified by env var + unity_path_in_env = ENV.key?('UNITY_DIR') ? File.expand_path(ENV.fetch('UNITY_DIR')) : nil + + if unity_path_in_env && File.exist?(unity_path_in_env) + require "#{unity_path_in_env}/auto/type_sanitizer" + elsif File.exist? unity_path_in_ceedling + require "#{unity_path_in_ceedling}/auto/type_sanitizer" + elsif File.exist? unity_path_in_cmock + require "#{unity_path_in_cmock}/auto/type_sanitizer" + else + raise 'Failed to find an instance of Unity to pull in type_sanitizer module!' + end + end + + def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil) + # determine the name for our new mock + mock_name = @prefix + module_name + @suffix + + # determine the folder our mock will reside + mock_folder = if folder && @subdir + File.join(@subdir, folder) + elsif @subdir + @subdir + else + folder + end + + # adds a trailing slash to the folder output + mock_folder = File.join(mock_folder, '') if mock_folder + + # create out mock project from incoming data + mock_project = { + :module_name => module_name, + :module_ext => (module_ext || '.h'), + :mock_name => mock_name, + :clean_name => TypeSanitizer.sanitize_c_identifier(mock_name), + :folder => mock_folder, + :parsed_stuff => parsed_stuff, + :skeleton => false + } + + create_mock_subdir(mock_project) + create_mock_header_file(mock_project) + create_mock_source_file(mock_project) + end + + def create_skeleton(module_name, parsed_stuff) + mock_project = { + :module_name => module_name, + :module_ext => '.h', + :parsed_stuff => parsed_stuff, + :skeleton => true + } + + create_skeleton_source_file(mock_project) + end + + private if $ThisIsOnlyATest.nil? ############################## + + def create_mock_subdir(mock_project) + @file_writer.create_subdir(mock_project[:folder]) + end + + def create_using_statement(file, function) + file << "using namespace #{function[:namespace].join('::')};\n" unless function[:namespace].empty? + end + + def create_mock_header_file(mock_project) + if @include_inline == :include + @file_writer.create_file(mock_project[:module_name] + (mock_project[:module_ext]), mock_project[:folder]) do |file, _filename| + file << mock_project[:parsed_stuff][:normalized_source] + end + end + + @file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename| + create_mock_header_header(file, filename, mock_project) + create_mock_header_service_call_declarations(file, mock_project) + create_typedefs(file, mock_project) + mock_project[:parsed_stuff][:functions].each do |function| + create_using_statement(file, function) + file << @plugins.run(:mock_function_declarations, function) + end + create_mock_header_footer(file) + end + end + + def create_mock_source_file(mock_project) + @file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename| + create_source_header_section(file, filename, mock_project) + create_instance_structure(file, mock_project) + create_extern_declarations(file) + create_mock_verify_function(file, mock_project) + create_mock_init_function(file, mock_project) + create_mock_destroy_function(file, mock_project) + mock_project[:parsed_stuff][:functions].each do |function| + create_mock_implementation(file, function) + create_mock_interfaces(file, function) + end + end + end + + def create_skeleton_source_file(mock_project) + filename = "#{@config.mock_path}/#{@subdir + '/' if @subdir}#{mock_project[:module_name]}.c" + existing = File.exist?(filename) ? File.read(filename) : '' + @file_writer.append_file(mock_project[:module_name] + '.c', @subdir) do |file, fullname| + blank_project = mock_project.clone + blank_project[:parsed_stuff] = { :functions => [] } + create_source_header_section(file, fullname, blank_project) if existing.empty? + mock_project[:parsed_stuff][:functions].each do |function| + create_function_skeleton(file, function, existing) + end + end + end + + def create_mock_header_header(file, _filename, mock_project) + define_name = mock_project[:clean_name].upcase + orig_filename = (mock_project[:folder] || '') + mock_project[:module_name] + mock_project[:module_ext] + file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" + file << "#ifndef _#{define_name}_H\n" + file << "#define _#{define_name}_H\n\n" + file << "#include \"#{@framework}.h\"\n" + @includes_h_pre_orig_header.each { |inc| file << "#include #{inc}\n" } + file << @config.orig_header_include_fmt.gsub(/%s/, orig_filename.to_s) + "\n" + @includes_h_post_orig_header.each { |inc| file << "#include #{inc}\n" } + plugin_includes = @plugins.run(:include_files) + file << plugin_includes unless plugin_includes.empty? + file << "\n" + file << "/* Ignore the following warnings, since we are copying code */\n" + file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + file << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + file << "#pragma GCC diagnostic push\n" + file << "#endif\n" + file << "#if !defined(__clang__)\n" + file << "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + file << "#endif\n" + file << "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + file << "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + file << "#endif\n" + file << "\n" + end + + def create_typedefs(file, mock_project) + file << "\n" + mock_project[:parsed_stuff][:typedefs].each { |typedef| file << "#{typedef}\n" } + file << "\n\n" + end + + def create_mock_header_service_call_declarations(file, mock_project) + file << "void #{mock_project[:clean_name]}_Init(void);\n" + file << "void #{mock_project[:clean_name]}_Destroy(void);\n" + file << "void #{mock_project[:clean_name]}_Verify(void);\n\n" + end + + def create_mock_header_footer(header) + header << "\n" + header << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + header << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + header << "#pragma GCC diagnostic pop\n" + header << "#endif\n" + header << "#endif\n" + header << "\n" + header << "#endif\n" + end + + def create_source_header_section(file, filename, mock_project) + header_file = (mock_project[:folder] || '') + filename.gsub('.c', mock_project[:module_ext]) + file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty? + file << "#include <string.h>\n" + file << "#include <stdlib.h>\n" + unless @exclude_setjmp_h + file << "#include <setjmp.h>\n" + end + file << "#include \"cmock.h\"\n" + @includes_c_pre_header.each { |inc| file << "#include #{inc}\n" } + file << "#include \"#{header_file}\"\n" + @includes_c_post_header.each { |inc| file << "#include #{inc}\n" } + file << "\n" + strs = [] + mock_project[:parsed_stuff][:functions].each do |func| + strs << func[:name] + func[:args].each { |arg| strs << arg[:name] } + end + strs.uniq.sort.each do |str| + file << "static const char* CMockString_#{str} = \"#{str}\";\n" + end + file << "\n" + end + + def create_instance_structure(file, mock_project) + functions = mock_project[:parsed_stuff][:functions] + functions.each do |function| + file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n" + file << " UNITY_LINE_TYPE LineNumber;\n" + file << @plugins.run(:instance_typedefs, function) + file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n" + end + file << "static struct #{mock_project[:clean_name]}Instance\n{\n" + if functions.empty? + file << " unsigned char placeHolder;\n" + end + functions.each do |function| + file << @plugins.run(:instance_structure, function) + file << " CMOCK_MEM_INDEX_TYPE #{function[:name]}_CallInstance;\n" + end + file << "} Mock;\n\n" + end + + def create_extern_declarations(file) + unless @exclude_setjmp_h + file << "extern jmp_buf AbortFrame;\n" + end + if @ordered + file << "extern int GlobalExpectCount;\n" + file << "extern int GlobalVerifyOrder;\n" + end + file << "\n" + end + + def create_mock_verify_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Verify(void)\n{\n" + verifications = mock_project[:parsed_stuff][:functions].collect do |function| + v = @plugins.run(:mock_verify, function) + v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v] + end.join + unless verifications.empty? + file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" + file << " CMOCK_MEM_INDEX_TYPE call_instance;\n" + file << verifications + end + file << "}\n\n" + end + + def create_mock_init_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Init(void)\n{\n" + file << " #{mock_project[:clean_name]}_Destroy();\n" + file << "}\n\n" + end + + def create_mock_destroy_function(file, mock_project) + file << "void #{mock_project[:clean_name]}_Destroy(void)\n{\n" + file << " CMock_Guts_MemFreeAll();\n" + file << " memset(&Mock, 0, sizeof(Mock));\n" + file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_destroy, function) }.join + + unless @fail_on_unexpected_calls + file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_ignore, function) }.join + end + + if @ordered + file << " GlobalExpectCount = 0;\n" + file << " GlobalVerifyOrder = 0;\n" + end + file << "}\n\n" + end + + def create_mock_implementation(file, function) + # prepare return value and arguments + function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") + + (function[:return][:type]) + + (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') + args_string = function[:args_string] + args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil? + + # Encapsulate in namespace(s) if applicable + function[:namespace].each do |ns| + file << "namespace #{ns} {\n" + end + + # Determine class prefix (if any) + cls_pre = '' + unless function[:class].nil? + cls_pre = "#{function[:class]}::" + end + + # Create mock function + unless @weak.empty? + file << "#if defined (__IAR_SYSTEMS_ICC__)\n" + file << "#pragma weak #{function[:unscoped_name]}\n" + file << "#else\n" + file << "#{function_mod_and_rettype} #{function[:unscoped_name]}(#{args_string}) #{weak};\n" + file << "#endif\n\n" + end + file << "#{function_mod_and_rettype} #{cls_pre}#{function[:unscoped_name]}(#{args_string})\n" + file << "{\n" + file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" + file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n" + file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" + file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n" + file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" + file << @plugins.run(:mock_implementation_precheck, function) + file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n" + file << " cmock_line = cmock_call_instance->LineNumber;\n" + if @ordered + file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n" + file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n" + file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n" + file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate);\n" + end + file << @plugins.run(:mock_implementation, function) + file << " UNITY_CLR_DETAILS();\n" + file << " return cmock_call_instance->ReturnVal;\n" unless function[:return][:void?] + file << "}\n" + + # Close any namespace(s) opened above + function[:namespace].each do + file << "}\n" + end + + file << "\n" + end + + def create_mock_interfaces(file, function) + file << @utils.code_add_argument_loader(function) + file << @plugins.run(:mock_interfaces, function) + end + + def create_function_skeleton(file, function, existing) + # prepare return value and arguments + function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") + + (function[:return][:type]) + + (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') + args_string = function[:args_string] + args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil? + + decl = "#{function_mod_and_rettype} #{function[:name]}(#{args_string})" + + return if existing.include?(decl) + + file << "#{decl}\n" + file << "{\n" + file << " /*TODO: Implement Me!*/\n" + function[:args].each { |arg| file << " (void)#{arg[:name]};\n" } + file << " return (#{(function[:return][:type])})0;\n" unless function[:return][:void?] + file << "}\n\n" + end +end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb similarity index 76% rename from test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb index 3b73708e7..a9864ab74 100644 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_array.rb @@ -5,7 +5,6 @@ # ========================================== class CMockGeneratorPluginArray - attr_reader :priority attr_accessor :config, :utils, :unity_helper, :ordered def initialize(config, utils) @@ -18,46 +17,47 @@ class CMockGeneratorPluginArray end def instance_typedefs(function) - function[:args].inject("") do |all, arg| - (arg[:ptr?]) ? all + " int Expected_#{arg[:name]}_Depth;\n" : all + function[:args].inject('') do |all, arg| + arg[:ptr?] ? all + " int Expected_#{arg[:name]}_Depth;\n" : all end end def mock_function_declarations(function) return nil unless function[:contains_ptr?] - args_call = function[:args].map{|m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : "#{m[:name]}"}.join(', ') + + args_call = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : (m[:name]).to_s }.join(', ') args_string = function[:args].map do |m| type = @utils.arg_type_with_const(m) m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" end.join(', ') - if (function[:return][:void?]) - return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" + + if function[:return][:void?] + return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" \ "void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n" else - return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" + + return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" \ "void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n" end end def mock_interfaces(function) return nil unless function[:contains_ptr?] + lines = [] func_name = function[:name] args_string = function[:args].map do |m| type = @utils.arg_type_with_const(m) m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" end.join(', ') - call_string = function[:args].map{|m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name]}.join(', ') - if (function[:return][:void?]) - lines << "void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n" - else - lines << "void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n" - end + call_string = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name] }.join(', ') + lines << if function[:return][:void?] + "void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n" + else + "void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n" + end lines << "{\n" lines << @utils.code_add_base_expectation(func_name) lines << " CMockExpectParameters_#{func_name}(cmock_call_instance, #{call_string});\n" - lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless (function[:return][:void?]) + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless function[:return][:void?] lines << "}\n\n" end - end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb similarity index 80% rename from test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb index 564e0ac2b..6ba8e9bd9 100644 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_callback.rb @@ -5,7 +5,6 @@ # ========================================== class CMockGeneratorPluginCallback - attr_accessor :include_count attr_reader :priority attr_reader :config, :utils @@ -20,7 +19,7 @@ class CMockGeneratorPluginCallback def instance_structure(function) func_name = function[:name] - " int #{func_name}_CallbackBool;\n" \ + " char #{func_name}_CallbackBool;\n" \ " CMOCK_#{func_name}_CALLBACK #{func_name}_CallbackFunctionPointer;\n" \ " int #{func_name}_CallbackCalls;\n" end @@ -30,7 +29,7 @@ class CMockGeneratorPluginCallback return_type = function[:return][:type] action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub' style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2) - styles = [ "void", "int cmock_num_calls", function[:args_string], "#{function[:args_string]}, int cmock_num_calls" ] + styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"] "typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \ @@ -60,29 +59,30 @@ class CMockGeneratorPluginCallback " UNITY_CLR_DETAILS();\n" \ " return;\n }\n" else - " #{function[:return][:type]} ret = #{generate_call(function)};\n" \ + " #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \ " UNITY_CLR_DETAILS();\n" \ - " return ret;\n }\n" + " return cmock_cb_ret;\n }\n" end end def mock_interfaces(function) func_name = function[:name] has_ignore = @config.plugins.include? :ignore - lines = "" + lines = '' lines << "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" - lines << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if has_ignore - lines << " Mock.#{func_name}_CallbackBool = (int)1;\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore + lines << " Mock.#{func_name}_CallbackBool = (char)1;\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" lines << "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" - lines << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if has_ignore - lines << " Mock.#{func_name}_CallbackBool = (int)0;\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore + lines << " Mock.#{func_name}_CallbackBool = (char)0;\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" end def mock_verify(function) func_name = function[:name] - " if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n call_instance = CMOCK_GUTS_NONE;\n" + " if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n {\n" \ + " call_instance = CMOCK_GUTS_NONE;\n" \ + " (void)call_instance;\n }\n" end - end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb new file mode 100644 index 000000000..7e2d7b628 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginCexception + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 7 + raise 'Error: cexception is not supported without setjmp support' if @config.exclude_setjmp_h + end + + def include_files + "#include \"CException.h\"\n" + end + + def instance_typedefs(_function) + " CEXCEPTION_T ExceptionToThrow;\n" + end + + def mock_function_declarations(function) + if function[:args_string] == 'void' + "#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" \ + "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n" + else + "#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" \ + "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n" + end + end + + def mock_implementation(_function) + " if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" \ + " UNITY_CLR_DETAILS();\n" \ + " Throw(cmock_call_instance->ExceptionToThrow);\n }\n" + end + + def mock_interfaces(function) + arg_insert = function[:args_string] == 'void' ? '' : "#{function[:args_string]}, " + ["void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n", + @utils.code_add_base_expectation(function[:name]), + @utils.code_call_argument_loader(function), + " cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n", + "}\n\n"].join + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb new file mode 100644 index 000000000..3a79c1a4d --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb @@ -0,0 +1,100 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginExpect + attr_reader :priority + attr_accessor :config, :utils, :unity_helper, :ordered + + def initialize(config, utils) + @config = config + @ptr_handling = @config.when_ptr + @ordered = @config.enforce_strict_ordering + @utils = utils + @unity_helper = @utils.helpers[:unity_helper] + @priority = 5 + + if @config.plugins.include? :expect_any_args + alias :mock_implementation :mock_implementation_might_check_args + else + alias :mock_implementation :mock_implementation_always_check_args + end + end + + def instance_typedefs(function) + lines = '' + lines << " #{function[:return][:type]} ReturnVal;\n" unless function[:return][:void?] + lines << " int CallOrder;\n" if @ordered + function[:args].each do |arg| + lines << " #{arg[:type]} Expected_#{arg[:name]};\n" + end + lines + end + + def mock_function_declarations(function) + if function[:args].empty? + if function[:return][:void?] + "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" \ + "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n" + else + "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + elsif function[:return][:void?] + "#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" \ + "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n" + else + "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n" + end + end + + def mock_implementation_always_check_args(function) + lines = '' + function[:args].each do |arg| + lines << @utils.code_verify_an_arg_expectation(function, arg) + end + lines + end + + def mock_implementation_might_check_args(function) + return '' if function[:args].empty? + + lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n" + function[:args].each do |arg| + lines << @utils.code_verify_an_arg_expectation(function, arg) + end + lines << " }\n" + lines + end + + def mock_interfaces(function) + lines = '' + func_name = function[:name] + lines << if function[:return][:void?] + if function[:args_string] == 'void' + "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n" + else + "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n" + end + elsif function[:args_string] == 'void' + "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + else + "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n" + end + lines << @utils.code_add_base_expectation(func_name) + lines << @utils.code_call_argument_loader(function) + lines << @utils.code_assign_argument_quickly('cmock_call_instance->ReturnVal', function[:return]) unless function[:return][:void?] + lines << "}\n\n" + end + + def mock_verify(function) + " if (CMOCK_GUTS_NONE != call_instance)\n" \ + " {\n" \ + " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" \ + " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess);\n" \ + " }\n" + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb new file mode 100644 index 000000000..0fc88e124 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginExpectAnyArgs + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 3 + end + + def instance_typedefs(_function) + " char ExpectAnyArgsBool;\n" + end + + def mock_function_declarations(function) + if function[:args].empty? + '' + elsif function[:return][:void?] + "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" \ + "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n" + else + "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + end + + def mock_interfaces(function) + lines = '' + unless function[:args].empty? + lines << if function[:return][:void?] + "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n" + else + "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + end + lines << @utils.code_add_base_expectation(function[:name], true) + unless function[:return][:void?] + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" + end + lines << " cmock_call_instance->ExpectAnyArgsBool = (char)1;\n" + lines << "}\n\n" + end + lines + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb new file mode 100644 index 000000000..b292f3d4f --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb @@ -0,0 +1,88 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginIgnore + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 2 + end + + def instance_structure(function) + if function[:return][:void?] + " char #{function[:name]}_IgnoreBool;\n" + else + " char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" + end + end + + def mock_function_declarations(function) + lines = if function[:return][:void?] + "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \ + "void #{function[:name]}_CMockIgnore(void);\n" + else + "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" \ + "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" + end + + # Add stop ignore function. it does not matter if there are any args + lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \ + "void #{function[:name]}_CMockStopIgnore(void);\n" + lines + end + + def mock_implementation_precheck(function) + lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" + lines << " UNITY_CLR_DETAILS();\n" + if function[:return][:void?] + lines << " return;\n }\n" + else + retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal') + lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" + lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?] + lines << " return cmock_call_instance->ReturnVal;\n }\n" + end + lines + end + + def mock_interfaces(function) + lines = '' + lines << if function[:return][:void?] + "void #{function[:name]}_CMockIgnore(void)\n{\n" + else + "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" + end + unless function[:return][:void?] + lines << @utils.code_add_base_expectation(function[:name], false) + end + unless function[:return][:void?] + lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + lines << "}\n\n" + + # Add stop ignore function. it does not matter if there are any args + lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" + unless function[:return][:void?] + lines << " if(Mock.#{function[:name]}_IgnoreBool)\n" + lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n" + lines << "}\n\n" + end + + def mock_ignore(function) + " Mock.#{function[:name]}_IgnoreBool = (char) 1;\n" + end + + def mock_verify(function) + func_name = function[:name] + " if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n" + end +end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb similarity index 80% rename from test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb rename to test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb index ef40e503d..d55e84c25 100644 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_arg.rb @@ -2,21 +2,21 @@ class CMockGeneratorPluginIgnoreArg attr_reader :priority attr_accessor :utils - def initialize(config, utils) + def initialize(_config, utils) @utils = utils @priority = 10 end def instance_typedefs(function) - lines = "" + lines = '' function[:args].each do |arg| - lines << " int IgnoreArg_#{arg[:name]};\n" + lines << " char IgnoreArg_#{arg[:name]};\n" end lines end def mock_function_declarations(function) - lines = "" + lines = '' function[:args].each do |arg| lines << "#define #{function[:name]}_IgnoreArg_#{arg[:name]}()" lines << " #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(__LINE__)\n" @@ -31,8 +31,8 @@ class CMockGeneratorPluginIgnoreArg function[:args].each do |arg| lines << "void #{func_name}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line)\n" lines << "{\n" - lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " + - "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ + "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp);\n" lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 1;\n" lines << "}\n\n" diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb new file mode 100644 index 000000000..9196edea4 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore_stateless.rb @@ -0,0 +1,85 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorPluginIgnoreStateless + attr_reader :priority + attr_reader :config, :utils + + def initialize(config, utils) + @config = config + @utils = utils + @priority = 2 + end + + def instance_structure(function) + if function[:return][:void?] + " char #{function[:name]}_IgnoreBool;\n" + else + " char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" + end + end + + def mock_function_declarations(function) + lines = if function[:return][:void?] + "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \ + "void #{function[:name]}_CMockIgnore(void);\n" + else + "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(cmock_retval)\n" \ + "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]});\n" + end + + # Add stop ignore function. it does not matter if there are any args + lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \ + "void #{function[:name]}_CMockStopIgnore(void);\n" + lines + end + + def mock_implementation_precheck(function) + lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" + lines << " UNITY_CLR_DETAILS();\n" + if function[:return][:void?] + lines << " return;\n }\n" + else + retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal') + lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" + lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?] + lines << " return cmock_call_instance->ReturnVal;\n }\n" + end + lines + end + + # this function is adjusted + def mock_interfaces(function) + lines = '' + lines << if function[:return][:void?] + "void #{function[:name]}_CMockIgnore(void)\n{\n" + else + "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]})\n{\n" + end + unless function[:return][:void?] + lines << " Mock.#{function[:name]}_CallInstance = CMOCK_GUTS_NONE;\n" + lines << " Mock.#{function[:name]}_FinalReturn = cmock_to_return;\n" + end + lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + lines << "}\n\n" + + # Add stop ignore function. it does not matter if there are any args + lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" + lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n" + lines << "}\n\n" + + lines + end + + def mock_ignore(function) + " Mock.#{function[:name]}_IgnoreBool = (char)1;\n" + end + + def mock_verify(function) + func_name = function[:name] + " if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n" + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb new file mode 100644 index 000000000..96b20035e --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -0,0 +1,79 @@ +class CMockGeneratorPluginReturnThruPtr + attr_reader :priority + attr_accessor :utils + + def initialize(_config, utils) + @utils = utils + @priority = 9 + end + + def instance_typedefs(function) + lines = '' + function[:args].each do |arg| + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << " char ReturnThruPtr_#{arg[:name]}_Used;\n" + lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n" + lines << " size_t ReturnThruPtr_#{arg[:name]}_Size;\n" + end + lines + end + + def mock_function_declarations(function) + lines = '' + function[:args].each do |arg| + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})" + # If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise + # we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size + lines << if arg[:type][-1] == '*' + " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n" + else + " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" + end + lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)" + lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_len * sizeof(*#{arg[:name]}))\n" + lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)" + lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n" + lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, size_t cmock_size);\n" + end + lines + end + + def mock_interfaces(function) + lines = [] + func_name = function[:name] + function[:args].each do |arg| + arg_name = arg[:name] + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, size_t cmock_size)\n" + lines << "{\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ + "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n" + lines << "}\n\n" + end + lines + end + + def mock_implementation(function) + lines = [] + function[:args].each do |arg| + arg_name = arg[:name] + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n" + lines << " {\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" + lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" + lines << " }\n" + end + lines + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb new file mode 100644 index 000000000..ecbc37e51 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb @@ -0,0 +1,250 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockGeneratorUtils + attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception + + def initialize(config, helpers = {}) + @config = config + @ptr_handling = @config.when_ptr + @ordered = @config.enforce_strict_ordering + @arrays = @config.plugins.include? :array + @cexception = @config.plugins.include? :cexception + @expect_any = @config.plugins.include? :expect_any_args + @return_thru_ptr = @config.plugins.include? :return_thru_ptr + @ignore_arg = @config.plugins.include? :ignore_arg + @ignore = @config.plugins.include? :ignore + @ignore_stateless = @config.plugins.include? :ignore_stateless + @treat_as = @config.treat_as + @helpers = helpers + end + + def self.arg_type_with_const(arg) + # Restore any "const" that was removed in header parsing + if arg[:type].include?('*') + arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type] + else + arg[:const?] ? "const #{arg[:type]}" : arg[:type] + end + end + + def arg_type_with_const(arg) + self.class.arg_type_with_const(arg) + end + + def code_verify_an_arg_expectation(function, arg) + if @arrays + case @ptr_handling + when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg) + when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg) + when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option." + end + else + code_verify_an_arg_expectation_with_no_arrays(function, arg) + end + end + + def code_add_base_expectation(func_name, global_ordering_supported = true) + lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" + lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" + lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if @ignore || @ignore_stateless + lines << " cmock_call_instance->LineNumber = cmock_line;\n" + lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if @ordered && global_ordering_supported + lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if @cexception + lines << " cmock_call_instance->ExpectAnyArgsBool = (char)0;\n" if @expect_any + lines + end + + def code_add_an_arg_expectation(arg, depth = 1) + lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg) + lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if @arrays && (depth.class == String) + lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if @ignore_arg + lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if @return_thru_ptr && ptr_or_str?(arg[:type]) && !(arg[:const?]) + lines + end + + def code_assign_argument_quickly(dest, arg) + if arg[:ptr?] || @treat_as.include?(arg[:type]) + " #{dest} = #{arg[:name]};\n" + else + assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1" + comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */" + " memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \ + " sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n" + end + end + + def code_add_argument_loader(function) + if function[:args_string] != 'void' + if @arrays + args_string = function[:args].map do |m| + type = arg_type_with_const(m) + m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + end.join(', ') + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" \ + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" + + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1)) } + + "}\n\n" + else + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" \ + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" + + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg) } + + "}\n\n" + end + else + '' + end + end + + def code_call_argument_loader(function) + if function[:args_string] != 'void' + args = function[:args].map do |m| + if @arrays && m[:ptr?] && !(m[:array_data?]) + "#{m[:name]}, 1" + elsif @arrays && m[:array_size?] + "#{m[:name]}, #{m[:name]}" + else + m[:name] + end + end + " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" + else + '' + end + end + + def ptr_or_str?(arg_type) + (arg_type.include?('*') || + @treat_as.fetch(arg_type, '').include?('*')) + end + + # private ###################### + + def lookup_expect_type(_function, arg) + c_type = arg[:type] + arg_name = arg[:name] + expected = "cmock_call_instance->Expected_#{arg_name}" + ignore = "cmock_call_instance->IgnoreArg_#{arg_name}" + unity_func = if (arg[:ptr?]) && ((c_type =~ /\*\*/) || (@ptr_handling == :compare_ptr)) + ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] + else + @helpers.nil? || @helpers[:unity_helper].nil? ? ['UNITY_TEST_ASSERT_EQUAL', ''] : @helpers[:unity_helper].get_helper(c_type) + end + [c_type, arg_name, expected, ignore, unity_func[0], unity_func[1]] + end + + def code_verify_an_arg_expectation_with_no_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end + + def code_verify_an_arg_expectation_with_normal_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end + + def code_verify_an_arg_expectation_with_smart_arrays(function, arg) + c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + lines = '' + lines << " if (!#{ignore})\n" if @ignore_arg + lines << " {\n" + lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" + case unity_func + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + c_type_local = c_type.gsub(/\*$/, '') + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" + when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' + if pre == '&' + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '') + lines << " else\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + when /_ARRAY/ + if pre == '&' + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" + else + lines << " if (#{pre}#{expected} == NULL)\n" + lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" + lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '') + lines << " else\n" + lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" + end + else + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" + end + lines << " }\n" + lines + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb new file mode 100644 index 000000000..9730bf40b --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb @@ -0,0 +1,623 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockHeaderParser + attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns + + def initialize(cfg) + @c_strippables = cfg.strippables + @c_attr_noconst = cfg.attributes.uniq - ['const'] + @c_attributes = ['const'] + c_attr_noconst + @c_calling_conventions = cfg.c_calling_conventions.uniq + @treat_as_array = cfg.treat_as_array + @treat_as_void = (['void'] + cfg.treat_as_void).uniq + @function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)' + @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m + @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq + @array_size_name = cfg.array_size_name + @array_size_type = (%w[int size_t] + cfg.array_size_type).uniq + @when_no_prototypes = cfg.when_no_prototypes + @local_as_void = @treat_as_void + @verbosity = cfg.verbosity + @treat_externs = cfg.treat_externs + @treat_inlines = cfg.treat_inlines + @inline_function_patterns = cfg.inline_function_patterns + @c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs + @c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines + end + + def parse(name, source) + parse_project = { + :module_name => name.gsub(/\W/, ''), + :typedefs => [], + :functions => [], + :normalized_source => nil + } + + function_names = [] + + all_funcs = parse_functions(import_source(source, parse_project)).map { |item| [item] } + all_funcs += parse_cpp_functions(import_source(source, parse_project, true)) + all_funcs.map do |decl| + func = parse_declaration(parse_project, *decl) + unless function_names.include? func[:name] + parse_project[:functions] << func + function_names << func[:name] + end + end + + parse_project[:normalized_source] = if @treat_inlines == :include + transform_inline_functions(source) + else + '' + end + + { :includes => nil, + :functions => parse_project[:functions], + :typedefs => parse_project[:typedefs], + :normalized_source => parse_project[:normalized_source] } + end + + private if $ThisIsOnlyATest.nil? ################ + + # Remove C/C++ comments from a string + # +source+:: String which will have the comments removed + def remove_comments_from_source(source) + # remove comments (block and line, in three steps to ensure correct precedence) + source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source.gsub!(/\/\*.*?\*\//m, '') # remove block comments + source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) + end + + def remove_nested_pairs_of_braces(source) + # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) + if RUBY_VERSION.split('.')[0].to_i > 1 + # we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. + r = '\\{([^\\{\\}]*|\\g<0>)*\\}' + source.gsub!(/#{r}/m, '{ }') + else + while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }') + end + end + + source + end + + # Return the number of pairs of braces/square brackets in the function provided by the user + # +source+:: String containing the function to be processed + def count_number_of_pairs_of_braces_in_function(source) + is_function_start_found = false + curr_level = 0 + total_pairs = 0 + + source.each_char do |c| + if c == '{' + curr_level += 1 + total_pairs += 1 + is_function_start_found = true + elsif c == '}' + curr_level -= 1 + end + + break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body + end + + if curr_level != 0 + total_pairs = 0 # Something is fishy about this source, not enough closing braces? + end + + total_pairs + end + + # Transform inline functions to regular functions in the source by the user + # +source+:: String containing the source to be processed + def transform_inline_functions(source) + inline_function_regex_formats = [] + square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets + + # Convert user provided string patterns to regex + # Use word bounderies before and after the user regex to limit matching to actual word iso part of a word + @inline_function_patterns.each do |user_format_string| + user_regex = Regexp.new(user_format_string) + word_boundary_before_user_regex = /\b/ + cleanup_spaces_after_user_regex = /[ ]*\b/ + inline_function_regex_formats << Regexp.new(word_boundary_before_user_regex.source + user_regex.source + cleanup_spaces_after_user_regex.source) + end + + # let's clean up the encoding in case they've done anything weird with the characters we might find + source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) + + # Comments can contain words that will trigger the parser (static|inline|<user_defined_static_keyword>) + remove_comments_from_source(source) + + # smush multiline macros into single line (checking for continuation character at end of line '\') + # If the user uses a macro to declare an inline function, + # smushing the macros makes it easier to recognize them as a macro and if required, + # remove them later on in this function + source.gsub!(/\s*\\\s*/m, ' ') + + # Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter + # Instead, look for an inline pattern (f.e. "static inline") and parse it. + # Below is a small explanation on how the general mechanism works: + # - Everything before the match should just be copied, we don't want + # to touch anything but the inline functions. + # - Remove the implementation of the inline function (this is enclosed + # in square brackets) and replace it with ";" to complete the + # transformation to normal/non-inline function. + # To ensure proper removal of the function body, we count the number of square-bracket pairs + # and remove the pairs one-by-one. + # - Copy everything after the inline function implementation and start the parsing of the next inline function + # There are ofcourse some special cases (inline macro declarations, inline function declarations, ...) which are handled and explained below + inline_function_regex_formats.each do |format| + inspected_source = '' + regex_matched = false + loop do + inline_function_match = source.match(/#{format}/) # Search for inline function declaration + + if inline_function_match.nil? # No inline functions so nothing to do + # Join pre and post match stripped parts for the next inline function detection regex + source = inspected_source + source if regex_matched == true + break + end + + regex_matched = true + # 1. Determine if we are dealing with a user defined macro to declare inline functions + # If the end of the pre-match string is a macro-declaration-like string, + # we are dealing with a user defined macro to declare inline functions + if /(#define\s*)\z/ =~ inline_function_match.pre_match + # Remove the macro from the source + stripped_pre_match = inline_function_match.pre_match.sub(/(#define\s*)\z/, '') + stripped_post_match = inline_function_match.post_match.sub(/\A(.*[\n]?)/, '') + inspected_source += stripped_pre_match + source = stripped_post_match + next + end + + # 2. Determine if we are dealing with an inline function declaration iso function definition + # If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments), + # we are dealing with a inline function declaration + if /\A#{@function_declaration_parse_base_match}\s*;/m =~ inline_function_match.post_match + # Only remove the inline part from the function declaration, leaving the function declaration won't do any harm + inspected_source += inline_function_match.pre_match + source = inline_function_match.post_match + next + end + + # 3. If we get here, we found an inline function declaration AND inline function body. + # Remove the function body to transform it into a 'normal' function declaration. + if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match + total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match) + + break if total_pairs_to_remove == 0 # Bad source? + + inline_function_stripped = inline_function_match.post_match + + total_pairs_to_remove.times do + inline_function_stripped.sub!(/\s*#{square_bracket_pair_regex_format}/, ';') # Remove inline implementation (+ some whitespace because it's prettier) + end + inspected_source += inline_function_match.pre_match + source = inline_function_stripped + next + end + + # 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header) + # Leave this code as it is. + inspected_source += inline_function_match.pre_match + inline_function_match[0] + source = inline_function_match.post_match + end + end + + source + end + + def import_source(source, parse_project, cpp = false) + # let's clean up the encoding in case they've done anything weird with the characters we might find + source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) + + # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void + # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void + @local_as_void = @treat_as_void + void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/) + if void_types + @local_as_void += void_types.flatten.uniq.compact + end + + # If user wants to mock inline functions, + # remove the (user specific) inline keywords before removing anything else to avoid missing an inline function + if @treat_inlines == :include + @inline_function_patterns.each do |user_format_string| + source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns + end + end + + # smush multiline macros into single line (checking for continuation character at end of line '\') + source.gsub!(/\s*\\\s*/m, ' ') + + remove_comments_from_source(source) + + # remove assembler pragma sections + source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') + + # remove gcc's __attribute__ tags + source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + + # remove preprocessor statements and extern "C" + source.gsub!(/^\s*#.*/, '') + source.gsub!(/extern\s+\"C\"\s*\{/, '') + + # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them + # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes + source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs + source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces + # remove problem keywords + source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2') + source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp + + source.gsub!(/\s*=\s*['"a-zA-Z0-9_\.]+\s*/, '') # remove default value statements from argument lists + source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements + source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric + source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped + + # scan standalone function pointers and remove them, because they can just be ignored + source.gsub!(/\w+\s*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/, ';') + + # scan for functions which return function pointers, because they are a pain + source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + unless cpp # only collect once + parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});" + "#{functype} #{Regexp.last_match(2).strip}(#{Regexp.last_match(3)});" + end + end + + source = remove_nested_pairs_of_braces(source) unless cpp + + if @treat_inlines == :include + # Functions having "{ }" at this point are/were inline functions, + # User wants them in so 'disguise' them as normal functions with the ";" + source.gsub!('{ }', ';') + end + + # remove function definitions by stripping off the arguments right now + source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ';') + + # drop extra white space to make the rest go faster + source.gsub!(/^\s+/, '') # remove extra white space from beginning of line + source.gsub!(/\s+$/, '') # remove extra white space from end of line + source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens + source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens + source.gsub!(/\s+/, ' ') # remove remaining extra white space + + # split lines on semicolons and remove things that are obviously not what we are looking for + src_lines = source.split(/\s*;\s*/) + src_lines = src_lines.uniq unless cpp # must retain closing braces for class/namespace + src_lines.delete_if { |line| line.strip.empty? } # remove blank lines + src_lines.delete_if { |line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil? } # remove function pointer arrays + + unless @treat_externs == :include + src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil? } # remove extern functions + end + + unless @treat_inlines == :include + src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil? } # remove inline functions + end + + src_lines.delete_if(&:empty?) # drop empty lines + end + + # Rudimentary C++ parser - does not handle all situations - e.g.: + # * A namespace function appears after a class with private members (should be parsed) + # * Anonymous namespace (shouldn't parse anything - no matter how nested - within it) + # * A class nested within another class + def parse_cpp_functions(source) + funcs = [] + + ns = [] + pub = false + source.each do |line| + # Search for namespace, class, opening and closing braces + line.scan(/(?:(?:\b(?:namespace|class)\s+(?:\S+)\s*)?{)|}/).each do |item| + if item == '}' + ns.pop + else + token = item.strip.sub(/\s+/, ' ') + ns << token + + pub = false if token.start_with? 'class' + pub = true if token.start_with? 'namespace' + end + end + + pub = true if line =~ /public:/ + pub = false if line =~ /private:/ || line =~ /protected:/ + + # ignore non-public and non-static + next unless pub + next unless line =~ /\bstatic\b/ + + line.sub!(/^.*static/, '') + next unless line =~ @declaration_parse_matcher + + tmp = ns.reject { |item| item == '{' } + + # Identify class name, if any + cls = nil + if tmp[-1].start_with? 'class ' + cls = tmp.pop.sub(/class (\S+) {/, '\1') + end + + # Assemble list of namespaces + tmp.each { |item| item.sub!(/(?:namespace|class) (\S+) {/, '\1') } + + funcs << [line.strip.gsub(/\s+/, ' '), tmp, cls] + end + funcs + end + + def parse_functions(source) + funcs = [] + source.each { |line| funcs << line.strip.gsub(/\s+/, ' ') if line =~ @declaration_parse_matcher } + if funcs.empty? + case @when_no_prototypes + when :error + raise 'ERROR: No function prototypes found!' + when :warn + puts 'WARNING: No function prototypes found!' unless @verbosity < 1 + end + end + funcs + end + + def parse_type_and_name(arg) + # Split up words and remove known attributes. For pointer types, make sure + # to remove 'const' only when it applies to the pointer itself, not when it + # applies to the type pointed to. For non-pointer types, remove any + # occurrence of 'const'. + arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word + arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word + arg_array = arg.split + arg_info = divine_ptr_and_const(arg) + arg_info[:name] = arg_array[-1] + + attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes + attr_array = [] + type_array = [] + + arg_array[0..-2].each do |word| + if attributes.include?(word) + attr_array << word + elsif @c_calling_conventions.include?(word) + arg_info[:c_calling_convention] = word + else + type_array << word + end + end + + if arg_info[:const_ptr?] + attr_array << 'const' + type_array.delete_at(type_array.rindex('const')) + end + + arg_info[:modifier] = attr_array.join(' ') + arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks + arg_info + end + + def parse_args(arg_list) + args = [] + arg_list.split(',').each do |arg| + arg.strip! + return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ... + + arg_info = parse_type_and_name(arg) + arg_info.delete(:modifier) # don't care about this + arg_info.delete(:c_calling_convention) # don't care about this + + # in C, array arguments implicitly degrade to pointers + # make the translation explicit here to simplify later logic + if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) + arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" + arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] + arg_info[:ptr?] = true + end + + args << arg_info + end + + # Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name> + args.each_with_index do |val, index| + next_index = index + 1 + next unless args.length > next_index + + if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) + val[:array_data?] = true + args[next_index][:array_size?] = true + end + end + + args + end + + def divine_ptr(arg) + return false unless arg.include? '*' + # treat "const char *" and similar as a string, not a pointer + return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg + + true + end + + def divine_const(arg) + # a non-pointer arg containing "const" is a constant + # an arg containing "const" before the last * is a pointer to a constant + if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg) + true + else + false + end + end + + def divine_ptr_and_const(arg) + divination = {} + + divination[:ptr?] = divine_ptr(arg) + divination[:const?] = divine_const(arg) + + # an arg containing "const" after the last * is a constant pointer + divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false + + divination + end + + def clean_args(arg_list, parse_project) + if @local_as_void.include?(arg_list.strip) || arg_list.empty? + 'void' + else + c = 0 + # magically turn brackets into asterisks, also match for parentheses that come from macros + arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1') + # remove space to place asterisks with type (where they belong) + arg_list.gsub!(/\s+\*/, '*') + # pull asterisks away from arg to place asterisks with type (where they belong) + arg_list.gsub!(/\*(\w)/, '* \1') + + # scan argument list for function pointers and replace them with custom types + arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + funcret = Regexp.last_match(1).strip + funcname = Regexp.last_match(2).strip + funcargs = Regexp.last_match(3).strip + funconst = '' + if funcname.include? 'const' + funcname.gsub!('const', '').strip! + funconst = 'const ' + end + parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" + funcname = "cmock_arg#{c += 1}" if funcname.empty? + "#{functype} #{funconst}#{funcname}" + end + + # scan argument list for function pointers with shorthand notation and replace them with custom types + arg_list.gsub!(/([\w\s\*]+)+\s+(\w+)\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| + functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + funcret = Regexp.last_match(1).strip + funcname = Regexp.last_match(2).strip + funcargs = Regexp.last_match(3).strip + funconst = '' + if funcname.include? 'const' + funcname.gsub!('const', '').strip! + funconst = 'const ' + end + parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" + funcname = "cmock_arg#{c += 1}" if funcname.empty? + "#{functype} #{funconst}#{funcname}" + end + + # automatically name unnamed arguments (those that only had a type) + arg_list.split(/\s*,\s*/).map do |arg| + parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) + if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1]) + "#{arg} cmock_arg#{c += 1}" + else + arg + end + end.join(', ') + end + end + + def parse_declaration(parse_project, declaration, namespace = [], classname = nil) + decl = {} + decl[:namespace] = namespace + decl[:class] = classname + + regex_match = @declaration_parse_matcher.match(declaration) + raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? + + # grab argument list + args = regex_match[2].strip + + # process function attributes, return type, and name + parsed = parse_type_and_name(regex_match[1]) + + # Record original name without scope prefix + decl[:unscoped_name] = parsed[:name] + + # Prefix name with namespace scope (if any) and then class + decl[:name] = namespace.join('_') + unless classname.nil? + decl[:name] << '_' unless decl[:name].empty? + decl[:name] << classname + end + # Add original name to complete fully scoped name + decl[:name] << '_' unless decl[:name].empty? + decl[:name] << decl[:unscoped_name] + + decl[:modifier] = parsed[:modifier] + unless parsed[:c_calling_convention].nil? + decl[:c_calling_convention] = parsed[:c_calling_convention] + end + + rettype = parsed[:type] + rettype = 'void' if @local_as_void.include?(rettype.strip) + decl[:return] = { :type => rettype, + :name => 'cmock_to_return', + :str => "#{rettype} cmock_to_return", + :void? => (rettype == 'void'), + :ptr? => parsed[:ptr?] || false, + :const? => parsed[:const?] || false, + :const_ptr? => parsed[:const_ptr?] || false } + + # remove default argument statements from mock definitions + args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') + + # check for var args + if args =~ /\.\.\./ + decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip + args = if args =~ /\,[\w\s]*\.\.\./ + args.gsub!(/\,[\w\s]*\.\.\./, '') + else + 'void' + end + else + decl[:var_arg] = nil + end + args = clean_args(args, parse_project) + decl[:args_string] = args + decl[:args] = parse_args(args) + decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') + decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } + + if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? || + decl[:return][:type].empty? || decl[:name].empty? + raise "Failed Parsing Declaration Prototype!\n" \ + " declaration: '#{declaration}'\n" \ + " modifier: '#{decl[:modifier]}'\n" \ + " return: #{prototype_inspect_hash(decl[:return])}\n" \ + " function: '#{decl[:name]}'\n" \ + " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" + end + + decl + end + + def prototype_inspect_hash(hash) + pairs = [] + hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if value.class == String}#{value}#{"'" if value.class == String}" } + "{#{pairs.join(', ')}}" + end + + def prototype_inspect_array_of_hashes(array) + hashes = [] + array.each { |hash| hashes << prototype_inspect_hash(hash) } + case array.size + when 0 + return '[]' + when 1 + return "[#{hashes[0]}]" + else + return "[\n #{hashes.join("\n ")}\n ]\n" + end + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb new file mode 100644 index 000000000..342014e22 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb @@ -0,0 +1,50 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockPluginManager + attr_accessor :plugins + + def initialize(config, utils) + @plugins = [] + plugins_to_load = [:expect, config.plugins].flatten.uniq.compact + plugins_to_load.each do |plugin| + plugin_name = plugin.to_s + object_name = 'CMockGeneratorPlugin' + camelize(plugin_name) + self.class.mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) } + end + @plugins.sort! { |a, b| a.priority <=> b.priority } + end + + def run(method, args = nil) + if args.nil? + @plugins.collect { |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join + else + @plugins.collect { |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join + end + end + + def camelize(lower_case_and_underscored_word) + lower_case_and_underscored_word.gsub(/\/(.?)/) { '::' + Regexp.last_match(1).upcase }.gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase } + end + + def self.mutex + @mutex ||= Mutex.new + end + + private + + def load_plugin(plugin_name, object_name, config, utils) + unless Object.const_defined? object_name + file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb" + require file_name + end + class_name = Object.const_get(object_name) + @plugins << class_name.new(config, utils) + rescue StandardError + file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb" + raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}" + end +end diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb new file mode 100644 index 000000000..9f4beb770 --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb @@ -0,0 +1,77 @@ +# ========================================== +# CMock Project - Automatic Mock Generation for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +class CMockUnityHelperParser + attr_accessor :c_types + + def initialize(config) + @config = config + @fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY' + @c_types = map_c_types.merge(import_source) + end + + def get_helper(ctype) + lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/, '\1\3\5\6').strip.gsub(/\s+/, '_') + return [@c_types[lookup], ''] if @c_types[lookup] + + if lookup =~ /\*$/ + lookup = lookup.gsub(/\*$/, '') + return [@c_types[lookup], '*'] if @c_types[lookup] + else + lookup += '*' + return [@c_types[lookup], '&'] if @c_types[lookup] + end + return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if ctype =~ /cmock_\w+_ptr\d+/ + raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown + + lookup =~ /\*$/ ? [@fallback, '&'] : [@fallback, ''] + end + + private ########################### + + def map_c_types + c_types = {} + @config.treat_as.each_pair do |ctype, expecttype| + c_type = ctype.gsub(/\s+/, '_') + if expecttype =~ /\*/ + c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.delete('*')}_ARRAY" + else + c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}" + c_types[c_type + '*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY" + end + end + c_types + end + + def import_source + source = @config.load_unity_helper + return {} if source.nil? + + c_types = {} + source = source.gsub(/\/\/.*$/, '') # remove line comments + source = source.gsub(/\/\*.*?\*\//m, '') # remove block comments + + # scan for comparison helpers + match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4, '\s*\w+\s*').join(',') + '\)') + pairs = source.scan(match_regex).flatten.compact + (pairs.size / 2).times do |i| + expect = pairs[i * 2] + ctype = pairs[(i * 2) + 1] + c_types[ctype] = expect unless expect.include?('_ARRAY') + end + + # scan for array variants of those helpers + match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5, '\s*\w+\s*').join(',') + '\)') + pairs = source.scan(match_regex).flatten.compact + (pairs.size / 2).times do |i| + expect = pairs[i * 2] + ctype = pairs[(i * 2) + 1] + c_types[ctype.gsub('_ARRAY', '*')] = expect + end + + c_types + end +end diff --git a/test/vendor/ceedling/vendor/cmock/src/cmock.c b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.c similarity index 67% rename from test/vendor/ceedling/vendor/cmock/src/cmock.c rename to test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.c index 5e5cb6c72..88f2c2b25 100644 --- a/test/vendor/ceedling/vendor/cmock/src/cmock.c +++ b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.c @@ -6,7 +6,7 @@ #include "cmock.h" -//public constants to be used by mocks +/* public constants to be used by mocks */ const char* CMockStringOutOfMemory = "CMock has run out of memory. Please allocate more."; const char* CMockStringCalledMore = "Called more times than expected."; const char* CMockStringCalledLess = "Called fewer times than expected."; @@ -19,7 +19,7 @@ const char* CMockStringPtrIsNULL = "Pointer is NULL."; const char* CMockStringExpNULL = "Expected NULL."; const char* CMockStringMismatch = "Function called with unexpected argument value."; -//private variables +/* private variables */ #ifdef CMOCK_MEM_DYNAMIC static unsigned char* CMock_Guts_Buffer = NULL; static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_ALIGN_SIZE; @@ -30,37 +30,37 @@ static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_SIZE + CMOCK_MEM static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; #endif -//------------------------------------------------------- -// CMock_Guts_MemNew -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemNew + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size) { CMOCK_MEM_INDEX_TYPE index; - //verify arguments valid (we must be allocating space for at least 1 byte, and the existing chain must be in memory somewhere) + /* verify arguments valid (we must be allocating space for at least 1 byte, and the existing chain must be in memory somewhere) */ if (size < 1) return CMOCK_GUTS_NONE; - //verify we have enough room + /* verify we have enough room */ size = size + CMOCK_MEM_INDEX_SIZE; if (size & CMOCK_MEM_ALIGN_MASK) size = (size + CMOCK_MEM_ALIGN_MASK) & ~CMOCK_MEM_ALIGN_MASK; if ((CMock_Guts_BufferSize - CMock_Guts_FreePtr) < size) { #ifndef CMOCK_MEM_DYNAMIC - return CMOCK_GUTS_NONE; // nothing we can do; our static buffer is out of memory + return CMOCK_GUTS_NONE; /* nothing we can do; our static buffer is out of memory */ #else - // our dynamic buffer does not have enough room; request more via realloc() + /* our dynamic buffer does not have enough room; request more via realloc() */ CMOCK_MEM_INDEX_TYPE new_buffersize = CMock_Guts_BufferSize + CMOCK_MEM_SIZE + size; unsigned char* new_buffer = realloc(CMock_Guts_Buffer, (size_t)new_buffersize); if (new_buffer == NULL) - return CMOCK_GUTS_NONE; // realloc() failed; out of memory + return CMOCK_GUTS_NONE; /* realloc() failed; out of memory */ CMock_Guts_Buffer = new_buffer; CMock_Guts_BufferSize = new_buffersize; #endif } - //determine where we're putting this new block, and init its pointer to be the end of the line + /* determine where we're putting this new block, and init its pointer to be the end of the line */ index = CMock_Guts_FreePtr + CMOCK_MEM_INDEX_SIZE; *(CMOCK_MEM_INDEX_TYPE*)(&CMock_Guts_Buffer[CMock_Guts_FreePtr]) = CMOCK_GUTS_NONE; CMock_Guts_FreePtr += size; @@ -68,9 +68,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size) return index; } -//------------------------------------------------------- -// CMock_Guts_MemChain -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemChain + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index) { CMOCK_MEM_INDEX_TYPE index; @@ -80,12 +80,12 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_ if (root_index == CMOCK_GUTS_NONE) { - //if there is no root currently, we return this object as the root of the chain + /* if there is no root currently, we return this object as the root of the chain */ return obj_index; } else { - //reject illegal nodes + /* reject illegal nodes */ if ((root_index < CMOCK_MEM_ALIGN_SIZE) || (root_index >= CMock_Guts_FreePtr)) { return CMOCK_GUTS_NONE; @@ -98,7 +98,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_ root = (void*)(&CMock_Guts_Buffer[root_index]); obj = (void*)(&CMock_Guts_Buffer[obj_index]); - //find the end of the existing chain and add us + /* find the end of the existing chain and add us */ next = root; do { index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE); @@ -112,21 +112,21 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_ } } -//------------------------------------------------------- -// CMock_Guts_MemNext -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemNext + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) { CMOCK_MEM_INDEX_TYPE index; void* previous_item; - //There is nothing "next" if the pointer isn't from our buffer + /* There is nothing "next" if the pointer isn't from our buffer */ if ((previous_item_index < CMOCK_MEM_ALIGN_SIZE) || (previous_item_index >= CMock_Guts_FreePtr)) return CMOCK_GUTS_NONE; previous_item = (void*)(&CMock_Guts_Buffer[previous_item_index]); - //if the pointer is good, then use it to look up the next index - //(we know the first element always goes in zero, so NEXT must always be > 1) + /* if the pointer is good, then use it to look up the next index + * (we know the first element always goes in zero, so NEXT must always be > 1) */ index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)previous_item - CMOCK_MEM_INDEX_SIZE); if ((index > 1) && (index < CMock_Guts_FreePtr)) return index; @@ -134,9 +134,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index return CMOCK_GUTS_NONE; } -//------------------------------------------------------- -// CMock_Guts_MemEndOfChain -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemEndOfChain + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) { CMOCK_MEM_INDEX_TYPE index = root_index; @@ -152,9 +152,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) return index; } -//------------------------------------------------------- -// CMock_GetAddressFor -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_GetAddressFor + *-------------------------------------------------------*/ void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) { if ((index >= CMOCK_MEM_ALIGN_SIZE) && (index < CMock_Guts_FreePtr)) @@ -167,41 +167,41 @@ void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) } } -//------------------------------------------------------- -// CMock_Guts_MemBytesCapacity -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemBytesCapacity + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) { return (sizeof(CMock_Guts_Buffer) - CMOCK_MEM_ALIGN_SIZE); } -//------------------------------------------------------- -// CMock_Guts_MemBytesFree -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemBytesFree + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) { return CMock_Guts_BufferSize - CMock_Guts_FreePtr; } -//------------------------------------------------------- -// CMock_Guts_MemBytesUsed -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemBytesUsed + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) { return CMock_Guts_FreePtr - CMOCK_MEM_ALIGN_SIZE; } -//------------------------------------------------------- -// CMock_Guts_MemFreeAll -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemFreeAll + *-------------------------------------------------------*/ void CMock_Guts_MemFreeAll(void) { - CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; //skip the very beginning + CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; /* skip the very beginning */ } -//------------------------------------------------------- -// CMock_Guts_MemFreeFinal -//------------------------------------------------------- +/*------------------------------------------------------- + * CMock_Guts_MemFreeFinal + *-------------------------------------------------------*/ void CMock_Guts_MemFreeFinal(void) { CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; diff --git a/test/vendor/ceedling/vendor/cmock/src/cmock.h b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.h similarity index 56% rename from test/vendor/ceedling/vendor/cmock/src/cmock.h rename to test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.h index e96546dab..45bab1829 100644 --- a/test/vendor/ceedling/vendor/cmock/src/cmock.h +++ b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock.h @@ -11,30 +11,37 @@ #define CMOCK_VERSION_MAJOR 2 #define CMOCK_VERSION_MINOR 5 -#define CMOCK_VERSION_BUILD 0 +#define CMOCK_VERSION_BUILD 4 #define CMOCK_VERSION ((CMOCK_VERSION_MAJOR << 16) | (CMOCK_VERSION_MINOR << 8) | CMOCK_VERSION_BUILD) -//should be big enough to index full range of CMOCK_MEM_MAX +/* should be big enough to index full range of CMOCK_MEM_MAX */ #ifndef CMOCK_MEM_INDEX_TYPE -#define CMOCK_MEM_INDEX_TYPE unsigned int +#include <stddef.h> +#define CMOCK_MEM_INDEX_TYPE size_t #endif #define CMOCK_GUTS_NONE (0) -//------------------------------------------------------- -// Memory API -//------------------------------------------------------- +#if defined __GNUC__ +# define CMOCK_FUNCTION_ATTR(a) __attribute__((a)) +#else +# define CMOCK_FUNCTION_ATTR(a) /* ignore */ +#endif + +/*------------------------------------------------------- + * Memory API + *-------------------------------------------------------*/ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index); -CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index); -CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) CMOCK_FUNCTION_ATTR(pure); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) CMOCK_FUNCTION_ATTR(pure); -void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index); +void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) CMOCK_FUNCTION_ATTR(pure); -CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void); -CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void); -CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) CMOCK_FUNCTION_ATTR(const); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) CMOCK_FUNCTION_ATTR(pure); +CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) CMOCK_FUNCTION_ATTR(pure); void CMock_Guts_MemFreeAll(void); void CMock_Guts_MemFreeFinal(void); -#endif //CMOCK_FRAMEWORK +#endif /* end of CMOCK_FRAMEWORK_H */ diff --git a/test/vendor/ceedling/vendor/cmock/src/cmock_internals.h b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock_internals.h similarity index 79% rename from test/vendor/ceedling/vendor/cmock/src/cmock_internals.h rename to test/unit-test/vendor/ceedling/vendor/cmock/src/cmock_internals.h index ae2e4962f..56fb33b1a 100644 --- a/test/vendor/ceedling/vendor/cmock/src/cmock_internals.h +++ b/test/unit-test/vendor/ceedling/vendor/cmock/src/cmock_internals.h @@ -9,7 +9,7 @@ #include "unity.h" -//These are constants that the generated mocks have access to +/* These are constants that the generated mocks have access to */ extern const char* CMockStringOutOfMemory; extern const char* CMockStringCalledMore; extern const char* CMockStringCalledLess; @@ -22,8 +22,8 @@ extern const char* CMockStringPtrIsNULL; extern const char* CMockStringExpNULL; extern const char* CMockStringMismatch; -//define CMOCK_MEM_DYNAMIC to grab memory as needed with malloc -//when you do that, CMOCK_MEM_SIZE is used for incremental size instead of total +/* define CMOCK_MEM_DYNAMIC to grab memory as needed with malloc + * when you do that, CMOCK_MEM_SIZE is used for incremental size instead of total */ #ifdef CMOCK_MEM_STATIC #undef CMOCK_MEM_DYNAMIC #endif @@ -32,7 +32,7 @@ extern const char* CMockStringMismatch; #include <stdlib.h> #endif -//this is used internally during pointer arithmetic. make sure this type is the same size as the target's pointer type +/* this is used internally during pointer arithmetic. make sure this type is the same size as the target's pointer type */ #ifndef CMOCK_MEM_PTR_AS_INT #ifdef UNITY_POINTER_WIDTH #ifdef UNITY_INT_WIDTH @@ -60,7 +60,7 @@ extern const char* CMockStringMismatch; #define CMOCK_MEM_PTR_AS_INT unsigned long #endif -//0 for no alignment, 1 for 16-bit, 2 for 32-bit, 3 for 64-bit +/* 0 for no alignment, 1 for 16-bit, 2 for 32-bit, 3 for 64-bit */ #ifndef CMOCK_MEM_ALIGN #ifdef UNITY_LONG_WIDTH #if (UNITY_LONG_WIDTH == 16) @@ -77,15 +77,15 @@ extern const char* CMockStringMismatch; #endif #endif -//amount of memory to allow cmock to use in its internal heap +/* amount of memory to allow cmock to use in its internal heap */ #ifndef CMOCK_MEM_SIZE #define CMOCK_MEM_SIZE (32768) #endif -//automatically calculated defs for easier reading +/* automatically calculated defs for easier reading */ #define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(1u << CMOCK_MEM_ALIGN) #define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1) #define CMOCK_MEM_INDEX_SIZE (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_PTR_AS_INT)((sizeof(CMOCK_MEM_INDEX_TYPE) > CMOCK_MEM_ALIGN_SIZE) ? sizeof(CMOCK_MEM_INDEX_TYPE) : CMOCK_MEM_ALIGN_SIZE) -#endif //CMOCK_FRAMEWORK_INTERNALS +#endif /* end of CMOCK_FRAMEWORK_INTERNALS_H */ diff --git a/test/unit-test/vendor/ceedling/vendor/cmock/src/meson.build b/test/unit-test/vendor/ceedling/vendor/cmock/src/meson.build new file mode 100644 index 000000000..c03c4e5bb --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/cmock/src/meson.build @@ -0,0 +1,12 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams. +# +# license: MIT +# +cmock_dir = include_directories('.') + +cmock_lib = static_library(meson.project_name(), + files('cmock.c'), + dependencies: [unity_dep], + include_directories: cmock_dir) diff --git a/test/vendor/ceedling/vendor/diy/lib/diy.rb b/test/unit-test/vendor/ceedling/vendor/diy/lib/diy.rb similarity index 100% rename from test/vendor/ceedling/vendor/diy/lib/diy.rb rename to test/unit-test/vendor/ceedling/vendor/diy/lib/diy.rb diff --git a/test/vendor/ceedling/vendor/diy/lib/diy/factory.rb b/test/unit-test/vendor/ceedling/vendor/diy/lib/diy/factory.rb similarity index 100% rename from test/vendor/ceedling/vendor/diy/lib/diy/factory.rb rename to test/unit-test/vendor/ceedling/vendor/diy/lib/diy/factory.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/colour_prompt.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/colour_prompt.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/colour_prompt.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/colour_prompt.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/colour_reporter.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/colour_reporter.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/colour_reporter.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/colour_reporter.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/generate_config.yml b/test/unit-test/vendor/ceedling/vendor/unity/auto/generate_config.yml similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/generate_config.yml rename to test/unit-test/vendor/ceedling/vendor/unity/auto/generate_config.yml diff --git a/test/vendor/ceedling/vendor/unity/auto/generate_module.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/generate_module.rb similarity index 92% rename from test/vendor/ceedling/vendor/unity/auto/generate_module.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/generate_module.rb index eb2cebd47..0a88becc9 100644 --- a/test/vendor/ceedling/vendor/unity/auto/generate_module.rb +++ b/test/unit-test/vendor/ceedling/vendor/unity/auto/generate_module.rb @@ -13,7 +13,9 @@ require 'fileutils' require 'pathname' # TEMPLATE_TST -TEMPLATE_TST ||= '#include "unity.h" +TEMPLATE_TST ||= '#ifdef TEST + +#include "unity.h" %2$s#include "%1$s.h" @@ -25,10 +27,12 @@ void tearDown(void) { } -void test_%1$s_NeedToImplement(void) +void test_%4$s_NeedToImplement(void) { TEST_IGNORE_MESSAGE("Need to Implement %1$s"); } + +#endif // TEST '.freeze # TEMPLATE_SRC @@ -163,24 +167,23 @@ class UnityModuleGenerator files end + ############################ + def neutralize_filename(name, start_cap = true) + return name if name.empty? + name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map { |v| v.capitalize }.join('_') + name = name[0].downcase + name[1..-1] unless start_cap + return name + end + ############################ def create_filename(part1, part2 = '') - if part2.empty? - case (@options[:naming]) - when 'bumpy' then part1 - when 'camel' then part1 - when 'snake' then part1.downcase - when 'caps' then part1.upcase - else part1 - end - else - case (@options[:naming]) - when 'bumpy' then part1 + part2 - when 'camel' then part1 + part2 - when 'snake' then part1.downcase + '_' + part2.downcase - when 'caps' then part1.upcase + '_' + part2.upcase - else part1 + '_' + part2 - end + name = part2.empty? ? part1 : part1 + '_' + part2 + case (@options[:naming]) + when 'bumpy' then neutralize_filename(name,false).delete('_') + when 'camel' then neutralize_filename(name).delete('_') + when 'snake' then neutralize_filename(name).downcase + when 'caps' then neutralize_filename(name).upcase + else name end end @@ -208,7 +211,8 @@ class UnityModuleGenerator f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil? f.write(file[:template] % [file[:name], file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join, - file[:name].upcase]) + file[:name].upcase.gsub(/-/, '_'), + file[:name].gsub(/-/, '_')]) end if @options[:update_svn] `svn add \"#{file[:path]}\"` diff --git a/test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb similarity index 91% rename from test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb index 5053210d0..d1d8f91af 100644 --- a/test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb +++ b/test/unit-test/vendor/ceedling/vendor/unity/auto/generate_test_runner.rb @@ -42,7 +42,9 @@ class UnityTestRunnerGenerator main_export_decl: '', cmdline_args: false, omit_begin_end: false, - use_param_tests: false + use_param_tests: false, + include_extensions: '(?:hpp|hh|H|h)', + source_extensions: '(?:cpp|cc|ino|C|c)' } end @@ -92,7 +94,7 @@ class UnityTestRunnerGenerator create_suite_setup(output) create_suite_teardown(output) create_reset(output) - create_run_test(output) + create_run_test(output) unless tests.empty? create_args_wrappers(output, tests) create_main(output, input_file, tests, used_mocks) end @@ -108,7 +110,7 @@ class UnityTestRunnerGenerator tests_and_line_numbers = [] # contains characters which will be substituted from within strings, doing - # this prevents these characters from interferring with scrubbers + # this prevents these characters from interfering with scrubbers # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' } substring_re = Regexp.union(substring_subs.keys) @@ -128,7 +130,7 @@ class UnityTestRunnerGenerator lines.each_with_index do |line, _index| # find tests - next unless line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m + next unless line =~ /^((?:\s*(?:TEST_CASE|TEST_RANGE)\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m arguments = Regexp.last_match(1) name = Regexp.last_match(2) @@ -139,6 +141,20 @@ class UnityTestRunnerGenerator if @options[:use_param_tests] && !arguments.empty? args = [] arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] } + + arguments.scan(/\s*TEST_RANGE\s*\((.*)\)\s*$/).flatten.each do |range_str| + args += range_str.scan(/\[\s*(-?\d+.?\d*),\s*(-?\d+.?\d*),\s*(-?\d+.?\d*)\s*\]/).map do |arg_values_str| + arg_values_str.map do |arg_value_str| + arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i + end + end.map do |arg_values| + (arg_values[0]..arg_values[1]).step(arg_values[2]).to_a + end.reduce do |result, arg_range_expanded| + result.product(arg_range_expanded) + end.map do |arg_combinations| + arg_combinations.flatten.join(', ') + end + end end tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } @@ -170,9 +186,9 @@ class UnityTestRunnerGenerator # parse out includes includes = { - local: source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten, + local: source.scan(/^\s*#include\s+\"\s*(.+\.#{@options[:include_extensions]})\s*\"/).flatten, system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, - linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+)\.[cC]\w*\s*\"/).flatten + linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+\.#{@options[:source_extensions]})\s*\"/).flatten } includes end @@ -181,7 +197,7 @@ class UnityTestRunnerGenerator mock_headers = [] includes.each do |include_path| include_file = File.basename(include_path) - mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}$/i + mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i end mock_headers end @@ -190,7 +206,7 @@ class UnityTestRunnerGenerator @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/ @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/ @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/) - @options[:has_suite_teardown] ||= (source =~ /void\s+suiteTearDown\s*\(/) + @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/) end def create_header(output, mocks, testfile_includes = []) @@ -205,14 +221,14 @@ class UnityTestRunnerGenerator output.puts("#include \"#{File.basename(@options[:header_file])}\"") else @options[:includes].flatten.uniq.compact.each do |inc| - output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") end testfile_includes.each do |inc| - output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") end end mocks.each do |mock| - output.puts("#include \"#{mock.gsub('.h', '')}.h\"") + output.puts("#include \"#{mock}\"") end output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) @@ -247,7 +263,7 @@ class UnityTestRunnerGenerator output.puts(' GlobalOrderError = NULL;') end - mocks = mock_headers.map { |mock| File.basename(mock) } + mocks = mock_headers.map { |mock| File.basename(mock, '.*') } mocks.each do |mock| mock_clean = TypeSanitizer.sanitize_c_identifier(mock) output.puts(" #{mock_clean}_Init();") @@ -325,8 +341,8 @@ class UnityTestRunnerGenerator def create_run_test(output) require 'erb' - template = ERB.new(File.read(File.join(__dir__, 'run_test.erb'))) - output.puts(template.result(binding)) + template = ERB.new(File.read(File.join(__dir__, 'run_test.erb')), nil, '<>') + output.puts("\n" + template.result(binding)) end def create_args_wrappers(output, tests) @@ -346,7 +362,7 @@ class UnityTestRunnerGenerator end def create_main(output, filename, tests, used_mocks) - output.puts("\n\n/*=======MAIN=====*/") + output.puts("\n/*=======MAIN=====*/") main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s if @options[:cmdline_args] if main_name != 'main' @@ -410,7 +426,7 @@ class UnityTestRunnerGenerator output.puts(' return suiteTearDown(UnityEnd());') end else - output.puts(' return UnityEnd();') if not @options[:omit_begin_end] + output.puts(' return UnityEnd();') unless @options[:omit_begin_end] end output.puts('}') end @@ -423,10 +439,10 @@ class UnityTestRunnerGenerator output.puts("#include \"#{@options[:framework]}.h\"") output.puts('#include "cmock.h"') unless used_mocks.empty? @options[:includes].flatten.uniq.compact.each do |inc| - output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") end testfile_includes.each do |inc| - output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") end output.puts "\n" tests.each do |test| @@ -449,13 +465,13 @@ if $0 == __FILE__ when '-cexception' options[:plugins] = [:cexception] true - when /\.*\.ya?ml/ + when /\.*\.ya?ml$/ options = UnityTestRunnerGenerator.grab_config(arg) true when /--(\w+)=\"?(.*)\"?/ options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) true - when /\.*\.h/ + when /\.*\.(?:hpp|hh|H|h)$/ options[:includes] << arg true else false diff --git a/test/vendor/ceedling/vendor/unity/auto/parse_output.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/parse_output.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/parse_output.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/parse_output.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/run_test.erb b/test/unit-test/vendor/ceedling/vendor/unity/auto/run_test.erb similarity index 83% rename from test/vendor/ceedling/vendor/unity/auto/run_test.erb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/run_test.erb index 3d3b6d1e8..f91b56691 100644 --- a/test/vendor/ceedling/vendor/unity/auto/run_test.erb +++ b/test/unit-test/vendor/ceedling/vendor/unity/auto/run_test.erb @@ -1,5 +1,5 @@ /*=======Test Runner Used To Run Each Test=====*/ -static void run_test(UnityTestFunction func, const char* name, int line_num) +static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num) { Unity.CurrentTestName = name; Unity.CurrentTestLineNumber = line_num; @@ -16,13 +16,14 @@ static void run_test(UnityTestFunction func, const char* name, int line_num) <% if @options[:plugins].include?(:cexception) %> CEXCEPTION_T e; Try { -<% end %> <%= @options[:setup_name] %>(); func(); -<% if @options[:plugins].include?(:cexception) %> } Catch(e) { TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); } +<% else %> + <%= @options[:setup_name] %>(); + func(); <% end %> } if (TEST_PROTECT()) diff --git a/test/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/stylize_as_junit.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/test_file_filter.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/test_file_filter.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/test_file_filter.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/test_file_filter.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/type_sanitizer.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/unity_test_summary.py b/test/unit-test/vendor/ceedling/vendor/unity/auto/unity_test_summary.py similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/unity_test_summary.py rename to test/unit-test/vendor/ceedling/vendor/unity/auto/unity_test_summary.py diff --git a/test/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb b/test/unit-test/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb rename to test/unit-test/vendor/ceedling/vendor/unity/auto/unity_test_summary.rb diff --git a/test/vendor/ceedling/vendor/unity/auto/unity_to_junit.py b/test/unit-test/vendor/ceedling/vendor/unity/auto/unity_to_junit.py similarity index 100% rename from test/vendor/ceedling/vendor/unity/auto/unity_to_junit.py rename to test/unit-test/vendor/ceedling/vendor/unity/auto/unity_to_junit.py diff --git a/test/unit-test/vendor/ceedling/vendor/unity/src/meson.build b/test/unit-test/vendor/ceedling/vendor/unity/src/meson.build new file mode 100644 index 000000000..1c7b426ff --- /dev/null +++ b/test/unit-test/vendor/ceedling/vendor/unity/src/meson.build @@ -0,0 +1,11 @@ +# +# build script written by : Michael Brockus. +# github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams. +# +# license: MIT +# +unity_dir = include_directories('.') + +unity_lib = static_library(meson.project_name(), + files('unity.c'), + include_directories: unity_dir) diff --git a/test/vendor/ceedling/vendor/unity/src/unity.c b/test/unit-test/vendor/ceedling/vendor/unity/src/unity.c similarity index 97% rename from test/vendor/ceedling/vendor/unity/src/unity.c rename to test/unit-test/vendor/ceedling/vendor/unity/src/unity.c index c7be02cc6..764a42b18 100644 --- a/test/vendor/ceedling/vendor/unity/src/unity.c +++ b/test/unit-test/vendor/ceedling/vendor/unity/src/unity.c @@ -1,6 +1,6 @@ /* ========================================================================= Unity Project - A Test Framework for C - Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ============================================================================ */ @@ -21,7 +21,7 @@ void UNITY_OUTPUT_CHAR(int); /* Helpful macros for us to use here in Assert functions */ #define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } #define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } -#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) return +#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) TEST_ABORT() struct UNITY_STORAGE_T Unity; @@ -43,6 +43,7 @@ static const char PROGMEM UnityStrWas[] = " Was "; static const char PROGMEM UnityStrGt[] = " to be greater than "; static const char PROGMEM UnityStrLt[] = " to be less than "; static const char PROGMEM UnityStrOrEqual[] = "or equal to "; +static const char PROGMEM UnityStrNotEqual[] = " to be not equal to "; static const char PROGMEM UnityStrElement[] = " Element "; static const char PROGMEM UnityStrByte[] = " Byte "; static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; @@ -66,9 +67,10 @@ static const char PROGMEM UnityStrBreaker[] = "------------------ static const char PROGMEM UnityStrResultsTests[] = " Tests "; static const char PROGMEM UnityStrResultsFailures[] = " Failures "; static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; +#ifndef UNITY_EXCLUDE_DETAILS static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; - +#endif /*----------------------------------------------- * Pretty Printers & Test Result Output Handlers *-----------------------------------------------*/ @@ -146,121 +148,6 @@ void UnityPrint(const char* string) } } } - -/*-----------------------------------------------*/ -#ifdef UNITY_INCLUDE_PRINT_FORMATTED -void UnityPrintFormatted(const char* format, ...) -{ - const char* pch = format; - va_list va; - va_start(va, format); - - if (pch != NULL) - { - while (*pch) - { - /* format identification character */ - if (*pch == '%') - { - pch++; - - if (pch != NULL) - { - switch (*pch) - { - case 'd': - case 'i': - { - const int number = va_arg(va, int); - UnityPrintNumber((UNITY_INT)number); - break; - } -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - case 'f': - case 'g': - { - const double number = va_arg(va, double); - UnityPrintFloat((UNITY_DOUBLE)number); - break; - } -#endif - case 'u': - { - const unsigned int number = va_arg(va, unsigned int); - UnityPrintNumberUnsigned((UNITY_UINT)number); - break; - } - case 'b': - { - const unsigned int number = va_arg(va, unsigned int); - const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; - UNITY_OUTPUT_CHAR('0'); - UNITY_OUTPUT_CHAR('b'); - UnityPrintMask(mask, (UNITY_UINT)number); - break; - } - case 'x': - case 'X': - case 'p': - { - const unsigned int number = va_arg(va, unsigned int); - UNITY_OUTPUT_CHAR('0'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)number, 8); - break; - } - case 'c': - { - const int ch = va_arg(va, int); - UnityPrintChar((const char *)&ch); - break; - } - case 's': - { - const char * string = va_arg(va, const char *); - UnityPrint(string); - break; - } - case '%': - { - UnityPrintChar(pch); - break; - } - default: - { - /* print the unknown format character */ - UNITY_OUTPUT_CHAR('%'); - UnityPrintChar(pch); - break; - } - } - } - } -#ifdef UNITY_OUTPUT_COLOR - /* print ANSI escape code */ - else if ((*pch == 27) && (*(pch + 1) == '[')) - { - pch += UnityPrintAnsiEscapeString(pch); - continue; - } -#endif - else if (*pch == '\n') - { - UNITY_PRINT_EOL(); - } - else - { - UnityPrintChar(pch); - } - - pch++; - } - } - - va_end(va); -} -#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ - /*-----------------------------------------------*/ void UnityPrintLen(const char* string, const UNITY_UINT32 length) { @@ -558,7 +445,7 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number) /* build up buffer in reverse order */ digits = 0; - while ((n != 0) || (digits < (decimals + 1))) + while ((n != 0) || (digits <= decimals)) { buf[digits++] = (char)('0' + n % 10); n /= 10; @@ -680,6 +567,10 @@ static void UnityAddMsgIfSpecified(const char* msg) if (msg) { UnityPrint(UnityStrSpacer); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif #ifndef UNITY_EXCLUDE_DETAILS if (Unity.CurrentDetail1) { @@ -863,9 +754,10 @@ void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(actual, style); - if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } - if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } - if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } UnityPrintNumberByStyle(threshold, style); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; @@ -1110,7 +1002,7 @@ void UnityAssertFloatSpecial(const UNITY_FLOAT actual, is_trait = !isinf(actual) && !isnan(actual); break; - default: + default: /* including UNITY_FLOAT_INVALID_TRAIT */ trait_index = 0; trait_names[0] = UnityStrInvalidFloatTrait; break; @@ -1250,7 +1142,7 @@ void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, is_trait = !isinf(actual) && !isnan(actual); break; - default: + default: /* including UNITY_FLOAT_INVALID_TRAIT */ trait_index = 0; trait_names[0] = UnityStrInvalidFloatTrait; break; @@ -1731,6 +1623,133 @@ UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) } #endif +/*----------------------------------------------- + * printf helper function + *-----------------------------------------------*/ +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +static void UnityPrintFVA(const char* format, va_list va) +{ + const char* pch = format; + if (pch != NULL) + { + while (*pch) + { + /* format identification character */ + if (*pch == '%') + { + pch++; + + if (pch != NULL) + { + switch (*pch) + { + case 'd': + case 'i': + { + const int number = va_arg(va, int); + UnityPrintNumber((UNITY_INT)number); + break; + } +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + case 'f': + case 'g': + { + const double number = va_arg(va, double); + UnityPrintFloat((UNITY_DOUBLE)number); + break; + } +#endif + case 'u': + { + const unsigned int number = va_arg(va, unsigned int); + UnityPrintNumberUnsigned((UNITY_UINT)number); + break; + } + case 'b': + { + const unsigned int number = va_arg(va, unsigned int); + const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('b'); + UnityPrintMask(mask, (UNITY_UINT)number); + break; + } + case 'x': + case 'X': + case 'p': + { + const unsigned int number = va_arg(va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 8); + break; + } + case 'c': + { + const int ch = va_arg(va, int); + UnityPrintChar((const char *)&ch); + break; + } + case 's': + { + const char * string = va_arg(va, const char *); + UnityPrint(string); + break; + } + case '%': + { + UnityPrintChar(pch); + break; + } + default: + { + /* print the unknown format character */ + UNITY_OUTPUT_CHAR('%'); + UnityPrintChar(pch); + break; + } + } + } + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + else if (*pch == '\n') + { + UNITY_PRINT_EOL(); + } + else + { + UnityPrintChar(pch); + } + + pch++; + } + } +} + +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if(format != NULL) + { + UnityPrint(": "); + va_list va; + va_start(va, format); + UnityPrintFVA(format, va); + va_end(va); + } + UNITY_PRINT_EOL(); +} +#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ + + /*----------------------------------------------- * Control Functions *-----------------------------------------------*/ @@ -1746,6 +1765,9 @@ void UnityFail(const char* msg, const UNITY_LINE_TYPE line) { UNITY_OUTPUT_CHAR(':'); +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif #ifndef UNITY_EXCLUDE_DETAILS if (Unity.CurrentDetail1) { @@ -1800,6 +1822,8 @@ void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) } /*-----------------------------------------------*/ +/* If we have not defined our own test runner, then include our default test runner to make life easier */ +#ifndef UNITY_SKIP_DEFAULT_RUNNER void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) { Unity.CurrentTestName = FuncName; @@ -1819,6 +1843,7 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int UNITY_EXEC_TIME_STOP(); UnityConcludeTest(); } +#endif /*-----------------------------------------------*/ void UnitySetTestFile(const char* filename) diff --git a/test/vendor/ceedling/vendor/unity/src/unity.h b/test/unit-test/vendor/ceedling/vendor/unity/src/unity.h similarity index 92% rename from test/vendor/ceedling/vendor/unity/src/unity.h rename to test/unit-test/vendor/ceedling/vendor/unity/src/unity.h index 34d7f93aa..338df0b55 100644 --- a/test/vendor/ceedling/vendor/unity/src/unity.h +++ b/test/unit-test/vendor/ceedling/vendor/unity/src/unity.h @@ -1,6 +1,6 @@ /* ========================================== Unity Project - A Test Framework for C - Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ========================================== */ @@ -10,7 +10,7 @@ #define UNITY_VERSION_MAJOR 2 #define UNITY_VERSION_MINOR 5 -#define UNITY_VERSION_BUILD 0 +#define UNITY_VERSION_BUILD 4 #define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) #ifdef __cplusplus @@ -104,6 +104,9 @@ void verifyTest(void); #define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) #define TEST_MESSAGE(message) UnityMessage((message), __LINE__) #define TEST_ONLY() +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__) +#endif /* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ @@ -125,6 +128,8 @@ void verifyTest(void); #define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") #define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") #define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") +#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") +#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") /* Integers (of all sizes) */ #define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) @@ -145,10 +150,28 @@ void verifyTest(void); #define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) -#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, NULL) -#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) -#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) /* Integer Greater Than/ Less Than (of all sizes) */ #define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) @@ -373,6 +396,8 @@ void verifyTest(void); #define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) #define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) #define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) +#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) /* Integers (of all sizes) */ #define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) @@ -398,6 +423,25 @@ void verifyTest(void); #define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + + /* Integer Greater Than/ Less Than (of all sizes) */ #define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) diff --git a/test/vendor/ceedling/vendor/unity/src/unity_internals.h b/test/unit-test/vendor/ceedling/vendor/unity/src/unity_internals.h similarity index 93% rename from test/vendor/ceedling/vendor/unity/src/unity_internals.h rename to test/unit-test/vendor/ceedling/vendor/unity/src/unity_internals.h index a9a7ea237..2c91b6db1 100644 --- a/test/vendor/ceedling/vendor/unity/src/unity_internals.h +++ b/test/unit-test/vendor/ceedling/vendor/unity/src/unity_internals.h @@ -1,6 +1,6 @@ /* ========================================== Unity Project - A Test Framework for C - Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ========================================== */ @@ -40,6 +40,26 @@ #include <limits.h> #endif +#if defined(__GNUC__) || defined(__clang__) + #define UNITY_FUNCTION_ATTR(a) __attribute__((a)) +#else + #define UNITY_FUNCTION_ATTR(a) /* ignore */ +#endif + +#ifndef UNITY_NORETURN + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define UNITY_NORETURN [[ noreturn ]] + #endif + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #include <stdnoreturn.h> + #define UNITY_NORETURN noreturn + #endif +#endif +#ifndef UNITY_NORETURN + #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn) +#endif + /*------------------------------------------------------- * Guess Widths If Not Specified *-------------------------------------------------------*/ @@ -327,7 +347,7 @@ typedef UNITY_FLOAT_TYPE UNITY_FLOAT; UnityPrintNumberUnsigned(execTimeMs); \ UnityPrint(" ms)"); \ } - #elif defined(__unix__) + #elif defined(__unix__) || defined(__APPLE__) #include <time.h> #define UNITY_TIME_TYPE struct timespec #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) @@ -421,6 +441,7 @@ typedef enum UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, UNITY_SMALLER_THAN = 0x4, UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, + UNITY_NOT_EQUAL = 0x0, UNITY_UNKNOWN } UNITY_COMPARISON_T; @@ -479,7 +500,12 @@ void UnityBegin(const char* filename); int UnityEnd(void); void UnitySetTestFile(const char* filename); void UnityConcludeTest(void); + +#ifndef RUN_TEST void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); +#else +#define UNITY_SKIP_DEFAULT_RUNNER +#endif /*------------------------------------------------------- * Details Support @@ -503,6 +529,10 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int #endif #endif +#ifdef UNITY_PRINT_TEST_CONTEXT +void UNITY_PRINT_TEST_CONTEXT(void); +#endif + /*------------------------------------------------------- * Test Output *-------------------------------------------------------*/ @@ -510,7 +540,7 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int void UnityPrint(const char* string); #ifdef UNITY_INCLUDE_PRINT_FORMATTED -void UnityPrintFormatted(const char* format, ...); +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...); #endif void UnityPrintLen(const char* string, const UNITY_UINT32 length); @@ -601,8 +631,14 @@ void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, const UNITY_DISPLAY_STYLE_T style, const UNITY_FLAGS_T flags); +#ifndef UNITY_EXCLUDE_SETJMP_H +UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line); +UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#else void UnityFail(const char* message, const UNITY_LINE_TYPE line); void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#endif + void UnityMessage(const char* message, const UNITY_LINE_TYPE line); #ifndef UNITY_EXCLUDE_FLOAT @@ -691,11 +727,8 @@ extern const char UnityStrErrShorthand[]; #endif #endif #ifdef UNITY_SUPPORT_VARIADIC_MACROS -#define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) -#define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) -#define RUN_TEST_FIRST_HELPER(first, ...) (first), #first -#define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway) -#define RUN_TEST_SECOND_HELPER(first, second, ...) (second) +#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) #endif #endif @@ -753,9 +786,11 @@ int UnityTestMatches(void); * Test Asserts *-------------------------------------------------------*/ -#define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} +#define UNITY_TEST_ASSERT(condition, line, message) do {if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));}} while(0) #define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) @@ -771,6 +806,19 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) #define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + #define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) @@ -841,7 +889,7 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) @@ -900,6 +948,9 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) diff --git a/test/vendor/ceedling/docs/CException.md b/test/vendor/ceedling/docs/CException.md deleted file mode 100644 index c3718191f..000000000 --- a/test/vendor/ceedling/docs/CException.md +++ /dev/null @@ -1,292 +0,0 @@ - -CException -========== - -CException is a basic exception framework for C, suitable for use in -embedded applications. It provides an exception framework similar in -use to C++, but with much less overhead. - - -CException uses C standard library functions `setjmp` and `longjmp` to -operate. As long as the target system has these two functions defined, -this library should be useable with very little configuration. It -even supports environments where multiple program flows are in use, -such as real-time operating systems. - - -There are about a gabillion exception frameworks using a similar -setjmp/longjmp method out there... and there will probably be more -in the future. Unfortunately, when we started our last embedded -project, all those that existed either (a) did not support multiple -tasks (therefore multiple stacks) or (b) were way more complex than -we really wanted. CException was born. - - -*Why use CException?* - - -0. It's ANSI C, and it beats passing error codes around. -1. You want something simple... CException throws a single id. You can - define those ID's to be whatever you like. You might even choose which - type that number is for your project. But that's as far as it goes. - We weren't interested in passing objects or structs or strings... - just simple error codes. -2. Performance... CException can be configured for single tasking or - multitasking. In single tasking, there is very little overhead past - the setjmp/longjmp calls (which are already fast). In multitasking, - your only additional overhead is the time it takes you to determine - a unique task id 0 - num_tasks. - - -For the latest version, go to [ThrowTheSwitch.org](http://throwtheswitch.org) - - -CONTENTS OF THIS DOCUMENT -========================= - -* Usage -* Limitations -*API -* Configuration -* Testing -* License - - -Usage ------ - -Code that is to be protected are wrapped in `Try { } Catch { }` blocks. -The code directly following the Try call is "protected", meaning that -if any Throws occur, program control is directly transferred to the -start of the Catch block. - - -A numerical exception ID is included with Throw, and is made accessible -from the Catch block. - - -Throws can occur from within function calls (nested as deeply as you -like) or directly from within the function itself. - - - -Limitations ------------ - - -This library was made to be as fast as possible, and provide basic -exception handling. It is not a full-blown exception library. Because -of this, there are a few limitations that should be observed in order -to successfully utilize this library: - -1. Do not directly "return" from within a `Try` block, nor `goto` - into or out of a `Try` block. - - *Why?* - - The `Try` macro allocates some local memory and alters a global - pointer. These are cleaned up at the top of the `Catch` macro. - Gotos and returns would bypass some of these steps, resulting in - memory leaks or unpredictable behavior. - - -2. If (a) you change local (stack) variables within your `Try` block, - AND (b) wish to make use of the updated values after an exception - is thrown, those variables should be made `volatile`. Note that this - is ONLY for locals and ONLY when you need access to them after a - `Throw`. - - *Why?* - - Compilers optimize. There is no way to guarantee that the actual - memory location was updated and not just a register unless the - variable is marked volatile. - - -3. Memory which is `malloc`'d or `new`'d is not automatically released - when an error is thrown. This will sometimes be desirable, and - othertimes may not. It will be the responsibility of the `Catch` - block to perform this kind of cleanup. - - *Why?* - - There's just no easy way to track `malloc`'d memory, etc., without - replacing or wrapping malloc calls or something like that. This - is a light framework, so these options were not desirable. - - - -API ---- - -###Try - -`Try` is a macro which starts a protected block. It MUST be followed by -a pair of braces or a single protected line (similar to an 'if'), -enclosing the data that is to be protected. It **must** be followed by a -`Catch` block (don't worry, you'll get compiler errors to let you know if -you mess any of that up). - - -###Catch(e) - -`Catch` is a macro which ends the `Try` block and starts the error handling -block. The `Catch` block is called if and only if an exception was thrown -while within the `Try` block. This error was thrown by a `Throw` call -somewhere within `Try` (or within a function called within `Try`, or a function -called by a function called within `Try`, etc). - -The single parameter `e` is filled with the error code which was thrown. -This can be used for reporting, conditional cleanup, etc. (or you can just -ignore it if you really want... people ignore return codes all the time, -right?). `e` should be of type `EXCEPTION_T` - - -###Throw(e) - -This is the method of throwing an error. A `Throw` should only occur from within a -protected (`Try` ... `Catch`) block, though it may easily be nested many function -calls deep without an impact on performance or functionality. `Throw` takes -a single argument, which is an exception id which will be passed to `Catch` -as the reason for the error. - -If you wish to rethrow an error, this can be done by calling `Throw(e)` with -the error code you just caught. It **is** valid to throw from a catch block. - - -###ExitTry() - -On rare occasion, you might want to immediately exit your current `Try` block -but **not** treat this as an error. Don't run the `Catch`. Just start executing -from after the `Catch` as if nothing had happened... That's what `ExitTry` is -for. - - -CONFIGURATION -------------- - -CException is a mostly portable library. It has one universal -dependency, and some macros which are required if working in a -multi-tasking environment. - -1. The standard C library setjmp must be available. Since this is part - of the standard library, chances are good that you'll be fine. - -2. If working in a multitasking environment, methods for obtaining an - index into an array of frames and to get the overall number of - id's are required. If the OS supports a method to retrieve Task - ID's, and those Tasks are number 0, 1, 2... you are in an ideal - situation. Otherwise, a more creative mapping function may be - required. Note that this function is likely to be called twice - for each protected block and once during a throw. This is the - only overhead in the system. - - -Exception.h ------------ - -By convention, most projects include `Exception.h` which defines any -further requirements, then calls `CException.h` to do the gruntwork. All -of these are optional. You could directly include `CException.h` if -you wanted and just use the defaults provided. - -* `EXCEPTION_T` - * Set this to the type you want your exception id's to be. Defaults to 'unsigned int'. - -* `EXCEPTION_NONE` - * Set this to a number which will never be an exception id in your system. Defaults to `0x5a5a5a5a`. - -* `EXCEPTION_GET_ID` - * If in a multi-tasking environment, this should be - set to be a call to the function described in #2 above. - Defaults to just return `0` all the time (good for - single tasking environments) - -* `EXCEPTION_NUM_ID` - * If in a multi-tasking environment, this should be set - to the number of ID's required (usually the number of - tasks in the system). Defaults to `1` (for single - tasking environments). - -* `CEXCEPTION_NO_CATCH_HANDLER(id)` - * This macro can be optionally specified. - It allows you to specify code to be called when a Throw - is made outside of `Try` ... `Catch` protection. Consider - this the emergency fallback plan for when something has - gone terribly wrong. - - -You may also want to include any header files which will commonly be -needed by the rest of your application where it uses exception handling -here. For example, OS header files or exception codes would be useful. - - -Finally, there are some hook macros which you can implement to inject -your own target-specific code in particular places. It is a rare instance -where you will need these, but they are here if you need them: - - -* `CEXCEPTION_HOOK_START_TRY` - * called immediately before the Try block - -* `CEXCEPTION_HOOK_HAPPY_TRY` - * called immediately after the Try block if no exception was thrown - -* `CEXCEPTION_HOOK_AFTER_TRY` - * called immediately after the Try block OR before an exception is caught - -* `CEXCEPTION_HOOK_START_CATCH` - * called immediately before the catch - - - -TESTING -------- - - -If you want to validate that CException works with your tools or that -it works with your custom configuration, you may want to run the test -suite. - - -The test suite included makes use of the `Unity` Test Framework. It will -require a native C compiler. The example makefile uses MinGW's gcc. -Modify the makefile to include the proper paths to tools, then run `make` -to compile and run the test application. - -* `C_COMPILER` - * The C compiler to use to perform the tests - -* `C_LIBS` - * The path to the C libraries (including setjmp) - -* `UNITY_DIR` - * The path to the Unity framework (required to run tests) - (get it at [ThrowTheSwitch.org](http://throwtheswitch.org)) - - - -LICENSE -------- - -This software is licensed under the MIT License - -Copyright (c) 2007-2017 Mark VanderVoord - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/test/vendor/ceedling/docs/CMock_Summary.md b/test/vendor/ceedling/docs/CMock_Summary.md deleted file mode 100644 index 87f9c00b8..000000000 --- a/test/vendor/ceedling/docs/CMock_Summary.md +++ /dev/null @@ -1,603 +0,0 @@ -CMock: A Summary -================ - -*[ThrowTheSwitch.org](http://throwtheswitch.org)* - -*This documentation is released under a Creative Commons 3.0 Attribution Share-Alike License* - - -What Exactly Are We Talking About Here? ---------------------------------------- - -CMock is a nice little tool which takes your header files and creates -a Mock interface for it so that you can more easily unit test modules -that touch other modules. For each function prototype in your -header, like this one: - - int DoesSomething(int a, int b); - - -...you get an automatically generated DoesSomething function -that you can link to instead of your real DoesSomething function. -By using this Mocked version, you can then verify that it receives -the data you want, and make it return whatever data you desire, -make it throw errors when you want, and more... Create these for -everything your latest real module touches, and you're suddenly -in a position of power: You can control and verify every detail -of your latest creation. - -To make that easier, CMock also gives you a bunch of functions -like the ones below, so you can tell that generated DoesSomething -function how to behave for each test: - - void DoesSomething_ExpectAndReturn(int a, int b, int toReturn); - void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error); - void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback); - void DoesSomething_IgnoreAndReturn(int toReturn); - - -You can pile a bunch of these back to back, and it remembers what -you wanted to pass when, like so: - - test_CallsDoesSomething_ShouldDoJustThat(void) - { - DoesSomething_ExpectAndReturn(1,2,3); - DoesSomething_ExpectAndReturn(4,5,6); - DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS); - - CallsDoesSomething( ); - } - - -This test will call CallsDoesSomething, which is the function -we are testing. We are expecting that function to call DoesSomething -three times. The first time, we check to make sure it's called -as DoesSomething(1, 2) and we'll magically return a 3. The second -time we check for DoesSomething(4, 5) and we'll return a 6. The -third time we verify DoesSomething(7, 8) and we'll throw an error -instead of returning anything. If CallsDoesSomething gets -any of this wrong, it fails the test. It will fail if you didn't -call DoesSomething enough, or too much, or with the wrong arguments, -or in the wrong order. - -CMock is based on Unity, which it uses for all internal testing. -It uses Ruby to do all the main work (versions 2.0.0 and above). - - -Installing -========== - -The first thing you need to do to install CMock is to get yourself -a copy of Ruby. If you're on linux or osx, you probably already -have it. You can prove it by typing the following: - - ruby --version - - -If it replied in a way that implies ignorance, then you're going to -need to install it. You can go to [ruby-lang](https://ruby-lang.org) -to get the latest version. You're also going to need to do that if it -replied with a version that is older than 2.0.0. Go ahead. We'll wait. - -Once you have Ruby, you have three options: - -* Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/) -* Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/) -* Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`. - - -Generated Mock Module Summary -============================= - -In addition to the mocks themselves, CMock will generate the -following functions for use in your tests. The expect functions -are always generated. The other functions are only generated -if those plugins are enabled: - - -Expect: -------- - -Your basic staple Expects which will be used for most of your day -to day CMock work. By calling this, you are telling CMock that you -expect that function to be called during your test. It also specifies -which arguments you expect it to be called with, and what return -value you want returned when that happens. You can call this function -multiple times back to back in order to queue up multiple calls. - -* `void func(void)` => `void func_Expect(void)` -* `void func(params)` => `void func_Expect(expected_params)` -* `retval func(void)` => `void func_ExpectAndReturn(retval_to_return)` -* `retval func(params)` => `void func_ExpectAndReturn(expected_params, retval_to_return)` - - -ExpectAnyArgs: --------------- - -This behaves just like the Expects calls, except that it doesn't really -care what the arguments are that the mock gets called with. It still counts -the number of times the mock is called and it still handles return values -if there are some. - -* `void func(void)` => `void func_ExpectAnyArgs(void)` -* `void func(params)` => `void func_ExpectAnyArgs(void)` -* `retval func(void)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)` -* `retval func(params)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)` - - -Array: ------- - -An ExpectWithArray is another variant of Expect. Like expect, it cares about -the number of times a mock is called, the arguments it is called with, and the -values it is to return. This variant has another feature, though. For anything -that resembles a pointer or array, it breaks the argument into TWO arguments. -The first is the original pointer. The second specify the number of elements -it is to verify of that array. If you specify 1, it'll check one object. If 2, -it'll assume your pointer is pointing at the first of two elements in an array. -If you specify zero elements, it will check just the pointer if -`:smart` mode is configured or fail if `:compare_data` is set. - -* `void func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers) -* `void func(ptr * param, other)` => `void func_ExpectWithArray(ptr* param, int param_depth, other)` -* `retval func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers) -* `retval func(other, ptr* param)` => `void func_ExpectWithArrayAndReturn(other, ptr* param, int param_depth, retval_to_return)` - - -Ignore: -------- - -Maybe you don't care about the number of times a particular function is called or -the actual arguments it is called with. In that case, you want to use Ignore. Ignore -only needs to be called once per test. It will then ignore any further calls to that -particular mock. The IgnoreAndReturn works similarly, except that it has the added -benefit of knowing what to return when that call happens. If the mock is called more -times than IgnoreAndReturn was called, it will keep returning the last value without -complaint. If it's called less times, it will also ignore that. You SAID you didn't -care how many times it was called, right? - -* `void func(void)` => `void func_Ignore(void)` -* `void func(params)` => `void func_Ignore(void)` -* `retval func(void)` => `void func_IgnoreAndReturn(retval_to_return)` -* `retval func(params)` => `void func_IgnoreAndReturn(retval_to_return)` - - -Ignore Arg: ------------- - -Maybe you overall want to use Expect and its similar variations, but you don't care -what is passed to a particular argument. This is particularly useful when that argument -is a pointer to a value that is supposed to be filled in by the function. You don't want -to use ExpectAnyArgs, because you still care about the other arguments. Instead, before -any of your Expect calls are made, you can call this function. It tells CMock to ignore -a particular argument for the rest of this test, for this mock function. - -* `void func(params)` => `void func_IgnoreArg_paramName(void)` - - -ReturnThruPtr: --------------- - -Another option which operates on a particular argument of a function is the ReturnThruPtr -plugin. For every argument that resembles a pointer or reference, CMock generates an -instance of this function. Just as the AndReturn functions support injecting one or more -return values into a queue, this function lets you specify one or more return values which -are queued up and copied into the space being pointed at each time the mock is called. - -* `void func(param1)` => `void func_ReturnThruPtr_paramName(val_to_return)` -* => `void func_ReturnArrayThruPtr_paramName(cal_to_return, len)` -* => `void func_ReturnMemThruPtr_paramName(val_to_return, size)` - - -Callback: ---------- - -If all those other options don't work, and you really need to do something custom, you -still have a choice. As soon as you stub a callback in a test, it will call the callback -whenever the mock is encountered and return the retval returned from the callback (if any) -instead of performing the usual expect checks. It can be configured to check the arguments -first (like expects) or just jump directly to the callback. - -* `void func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)` -where `CMOCK_func_CALLBACK` looks like: `void func(int NumCalls)` -* `void func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)` -where `CMOCK_func_CALLBACK` looks like: `void func(params, int NumCalls)` -* `retval func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)` -where `CMOCK_func_CALLBACK` looks like: `retval func(int NumCalls)` -* `retval func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)` -where `CMOCK_func_CALLBACK` looks like: `retval func(params, int NumCalls)` - - -Cexception: ------------ - -Finally, if you are using Cexception for error handling, you can use this to throw errors -from inside mocks. Like Expects, it remembers which call was supposed to throw the error, -and it still checks parameters first. - -* `void func(void)` => `void func_ExpectAndThrow(value_to_throw)` -* `void func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)` -* `retval func(void)` => `void func_ExpectAndThrow(value_to_throw)` -* `retval func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)` - - - -Running CMock -============= - -CMock is a Ruby script and class. You can therefore use it directly -from the command line, or include it in your own scripts or rakefiles. - - -Mocking from the Command Line ------------------------------ - -After unpacking CMock, you will find cmock.rb in the 'lib' directory. -This is the file that you want to run. It takes a list of header files -to be mocked, as well as an optional yaml file for a more detailed -configuration (see config options below). - -For example, this will create three mocks using the configuration -specified in MyConfig.yml: - - ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h - -And this will create two mocks using the default configuration: - - ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h - - -Mocking From Scripts or Rake ----------------------------- - -CMock can be used directly from your own scripts or from a rakefile. -Start by including cmock.rb, then create an instance of CMock. -When you create your instance, you may initialize it in one of -three ways. - -You may specify nothing, allowing it to run with default settings: - - require 'cmock.rb' - cmock = CMock.new - -You may specify a YAML file containing the configuration options -you desire: - - cmock = CMock.new('../MyConfig.yml') - -You may specify the options explicitly: - - cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/') - - -Config Options: ---------------- - -The following configuration options can be specified in the -yaml file or directly when instantiating. - -Passed as Ruby, they look like this: - - { :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare } - -Defined in the yaml file, they look more like this: - - :cmock: - :attributes: - - __funky - - __intrinsic - :when_ptr: :compare - -In all cases, you can just include the things that you want to override -from the defaults. We've tried to specify what the defaults are below. - -* `:attributes`: - These are attributes that CMock should ignore for you for testing - purposes. Custom compiler extensions and externs are handy things to - put here. If your compiler is choking on some extended syntax, this - is often a good place to look. - - * defaults: ['__ramfunc', '__irq', '__fiq', 'register', 'extern'] - * **note:** this option will reinsert these attributes onto the mock's calls. - If that isn't what you are looking for, check out :strippables. - -* `:c_calling_conventions`: - Similarly, CMock may need to understand which C calling conventions - might show up in your codebase. If it encounters something it doesn't - recognize, it's not going to mock it. We have the most common covered, - but there are many compilers out there, and therefore many other options. - - * defaults: ['__stdcall', '__cdecl', '__fastcall'] - * **note:** this option will reinsert these attributes onto the mock's calls. - If that isn't what you are looking for, check out :strippables. - -* `:callback_after_arg_check`: - Tell `:callback` plugin to do the normal argument checking **before** it - calls the callback function by setting this to true. When false, the - callback function is called **instead** of the argument verification. - - * default: false - -* `:callback_include_count`: - Tell `:callback` plugin to include an extra parameter to specify the - number of times the callback has been called. If set to false, the - callback has the same interface as the mocked function. This can be - handy when you're wanting to use callback as a stub. - - * default: true - -* `:cexception_include`: - Tell `:cexception` plugin where to find CException.h... You only need to - define this if it's not in your build path already... which it usually - will be for the purpose of your builds. - - * default: *nil* - -* `:enforce_strict_ordering`: - CMock always enforces the order that you call a particular function, - so if you expect GrabNabber(int size) to be called three times, it - will verify that the sizes are in the order you specified. You might - *also* want to make sure that all different functions are called in a - particular order. If so, set this to true. - - * default: false - -* `:framework`: - Currently the only option is `:unity.` Eventually if we support other - unity test frameworks (or if you write one for us), they'll get added - here. - - : default: :unity - -* `:includes`: - An array of additional include files which should be added to the - mocks. Useful for global types and definitions used in your project. - There are more specific versions if you care WHERE in the mock files - the includes get placed. You can define any or all of these options. - - * `:includes` - * `:includes_h_pre_orig_header` - * `:includes_h_post_orig_header` - * `:includes_c_pre_header` - * `:includes_c_post_header` - * default: nil #for all 5 options - -* `:memcmp_if_unknown`: - C developers create a lot of types, either through typedef or preprocessor - macros. CMock isn't going to automatically know what you were thinking all - the time (though it tries its best). If it comes across a type it doesn't - recognize, you have a choice on how you want it to handle it. It can either - perform a raw memory comparison and report any differences, or it can fail - with a meaningful message. Either way, this feature will only happen after - all other mechanisms have failed (The thing encountered isn't a standard - type. It isn't in the :treat_as list. It isn't in a custom unity_helper). - - * default: true - -* `:mock_path`: - The directory where you would like the mock files generated to be - placed. - - * default: mocks - -* `:mock_prefix`: - The prefix to prepend to your mock files. For example, if it's “Mock”, a file - “USART.h” will get a mock called “MockUSART.c”. This CAN be used with a suffix - at the same time. - - * default: Mock - -* `:mock_suffix`: - The suffix to append to your mock files. For example, it it's "_Mock", a file - "USART.h" will get a mock called "USART_Mock.h". This CAN be used with a prefix - at the same time. - - * default: "" - -* `:plugins`: - An array of which plugins to enable. ':expect' is always active. Also - available currently: - - * `:ignore` - * `:ignore_arg` - * `:expect_any_args` - * `:array` - * `:cexception` - * `:callback` - * `:return_thru_ptr` - -* `:strippables`: - An array containing a list of items to remove from the header - before deciding what should be mocked. This can be something simple - like a compiler extension CMock wouldn't recognize, or could be a - regex to reject certain function name patterns. This is a great way to - get rid of compiler extensions when your test compiler doesn't support - them. For example, use `:strippables: ['(?:functionName\s*\(+.*?\)+)']` - to prevent a function `functionName` from being mocked. By default, it - is ignoring all gcc attribute extensions. - - * default: ['(?:__attribute__\s*\(+.*?\)+)'] - -* `:subdir`: - This is a relative subdirectory for your mocks. Set this to e.g. "sys" in - order to create a mock for `sys/types.h` in `(:mock_path)/sys/`. - - * default: "" - -* `:treat_as`: - The `:treat_as` list is a shortcut for when you have created typedefs - of standard types. Why create a custom unity helper for UINT16 when - the unity function TEST_ASSERT_EQUAL_HEX16 will work just perfectly? - Just add 'UINT16' => 'HEX16' to your list (actually, don't. We already - did that one for you). Maybe you have a type that is a pointer to an - array of unsigned characters? No problem, just add 'UINT8_T*' => - 'HEX8*' - - * NOTE: unlike the other options, your specifications MERGE with the - default list. Therefore, if you want to override something, you must - reassign it to something else (or to *nil* if you don't want it) - - * default: - * 'int': 'INT' - * 'char': 'INT8' - * 'short': 'INT16' - * 'long': 'INT' - * 'int8': 'INT8' - * 'int16': 'INT16' - * 'int32': 'INT' - * 'int8_t': 'INT8' - * 'int16_t': 'INT16' - * 'int32_t': 'INT' - * 'INT8_T': 'INT8' - * 'INT16_T': 'INT16' - * 'INT32_T': 'INT' - * 'bool': 'INT' - * 'bool_t': 'INT' - * 'BOOL': 'INT' - * 'BOOL_T': 'INT' - * 'unsigned int': 'HEX32' - * 'unsigned long': 'HEX32' - * 'uint32': 'HEX32' - * 'uint32_t': 'HEX32' - * 'UINT32': 'HEX32' - * 'UINT32_T': 'HEX32' - * 'void*': 'HEX8_ARRAY' - * 'unsigned short': 'HEX16' - * 'uint16': 'HEX16' - * 'uint16_t': 'HEX16' - * 'UINT16': 'HEX16' - * 'UINT16_T': 'HEX16' - * 'unsigned char': 'HEX8' - * 'uint8': 'HEX8' - * 'uint8_t': 'HEX8' - * 'UINT8': 'HEX8' - * 'UINT8_T': 'HEX8' - * 'char*': 'STRING' - * 'pCHAR': 'STRING' - * 'cstring': 'STRING' - * 'CSTRING': 'STRING' - * 'float': 'FLOAT' - * 'double': 'FLOAT' - -* `:treat_as_void`: - We've seen "fun" legacy systems typedef 'void' with a custom type, - like MY_VOID. Add any instances of those to this list to help CMock - understand how to deal with your code. - - * default: [] - -* `:treat_externs`: - This specifies how you want CMock to handle functions that have been - marked as extern in the header file. Should it mock them? - - * `:include` will mock externed functions - * `:exclude` will ignore externed functions (default). - -* `:unity_helper_path`: - If you have created a header with your own extensions to unity to - handle your own types, you can set this argument to that path. CMock - will then automagically pull in your helpers and use them. The only - trick is that you make sure you follow the naming convention: - `UNITY_TEST_ASSERT_EQUAL_YourType`. If it finds macros of the right - shape that match that pattern, it'll use them. - - * default: [] - -* `:verbosity`: - How loud should CMock be? - - * 0 for errors only - * 1 for errors and warnings - * 2 for normal (default) - * 3 for verbose - -* `:weak`: - When set this to some value, the generated mocks are defined as weak - symbols using the configured format. This allows them to be overridden - in particular tests. - - * Set to '__attribute ((weak))' for weak mocks when using GCC. - * Set to any non-empty string for weak mocks when using IAR. - * default: "" - -* `:when_no_prototypes`: - When you give CMock a header file and ask it to create a mock out of - it, it usually contains function prototypes (otherwise what was the - point?). You can control what happens when this isn't true. You can - set this to `:warn,` `:ignore,` or `:error` - - * default: :warn - -* `:when_ptr`: - You can customize how CMock deals with pointers (c strings result in - string comparisons... we're talking about **other** pointers here). Your - options are `:compare_ptr` to just verify the pointers are the same, - `:compare_data` or `:smart` to verify that the data is the same. - `:compare_data` and `:smart` behaviors will change slightly based on - if you have the array plugin enabled. By default, they compare a - single element of what is being pointed to. So if you have a pointer - to a struct called ORGAN_T, it will compare one ORGAN_T (whatever that - is). - - * default: :smart - -* `:fail_on_unexpected_calls`: - By default, CMock will fail a test if a mock is called without _Expect and _Ignore - called first. While this forces test writers to be more explicit in their expectations, - it can clutter tests with _Expect or _Ignore calls for functions which are not the focus - of the test. While this is a good indicator that this module should be refactored, some - users are not fans of the additional noise. - - Therefore, :fail_on_unexpected_calls can be set to false to force all mocks to start with - the assumption that they are operating as _Ignore unless otherwise specified. - - * default: true - * **note:** - If this option is disabled, the mocked functions will return - a default value (0) when called (and only if they have to return something of course). - - -Compiled Options: ------------------ - -A number of #defines also exist for customizing the cmock experience. -Feel free to pass these into your compiler or whatever is most -convenient. CMock will otherwise do its best to guess what you want -based on other settings, particularly Unity's settings. - -* `CMOCK_MEM_STATIC` or `CMOCK_MEM_DYNAMIC` - Define one of these to determine if you want to dynamically add - memory during tests as required from the heap. If static, you - can control the total footprint of Cmock. If dynamic, you will - need to make sure you make some heap space available for Cmock. - -* `CMOCK_MEM_SIZE` - In static mode this is the total amount of memory you are allocating - to Cmock. In Dynamic mode this is the size of each chunk allocated - at once (larger numbers grab more memory but require less mallocs). - -* `CMOCK_MEM_ALIGN` - The way to align your data to. Not everything is as flexible as - a PC, as most embedded designers know. This defaults to 2, meaning - align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment - by setting 0, force alignment to the closest uint16 with 1 or even - to the closest uint64 with 3. - -* `CMOCK_MEM_PTR_AS_INT` - This is used internally to hold pointers... it needs to be big - enough. On most processors a pointer is the same as an unsigned - long... but maybe that's not true for yours? - -* `CMOCK_MEM_INDEX_TYPE` - This needs to be something big enough to point anywhere in Cmock's - memory space... usually it's an unsigned int. - -Examples -======== - -You can look in the [examples directory](/examples/) for a couple of examples on how -you might tool CMock into your build process. You may also want to consider -using [Ceedling](https://throwtheswitch.org/ceedling). Please note that -these examples are meant to show how the build process works. They have -failing tests ON PURPOSE to show what that would look like. Don't be alarmed. ;) - diff --git a/test/vendor/ceedling/docs/CeedlingPacket.md b/test/vendor/ceedling/docs/CeedlingPacket.md deleted file mode 100644 index 88cd02025..000000000 --- a/test/vendor/ceedling/docs/CeedlingPacket.md +++ /dev/null @@ -1,2060 +0,0 @@ -[All code is copyright © 2010-2012 Ceedling Project -by Mike Karlesky, Mark VanderVoord, and Greg Williams. - -This Documentation Is Released Under a -Creative Commons 3.0 Attribution Share-Alike License] - -What the What? - -Assembling build environments for C projects - especially with -automated unit tests - is a pain. Whether it's Make or Rake or Premake -or what-have-you, set up with an all-purpose build environment -tool is tedious and requires considerable glue code to pull together -the necessary tools and libraries. Ceedling allows you to generate -an entire test and build environment for a C project from a single -YAML configuration file. Ceedling is written in Ruby and works -with the Rake build tool plus other goodness like Unity and CMock -- the unit testing and mocking frameworks for C. Ceedling and -its complementary tools can support the tiniest of embedded -processors, the beefiest 64 bit power houses available, and -everything in between. - -For a build project including unit tests and using the default -toolchain gcc, the configuration file could be as simple as this: - -```yaml -:project: - :build_root: project/build/ - :release_build: TRUE - -:paths: - :test: - - tests/** - :source: - - source/** -``` - -From the command line, to build the release version of your project, -you would simply run `ceedling release`. To run all your unit tests, -you would run `ceedling test:all`. That's it! - -Of course, many more advanced options allow you to configure -your project with a variety of features to meet a variety of needs. -Ceedling can work with practically any command line toolchain -and directory structure – all by way of the configuration file. -Further, because Ceedling piggy backs on Rake, you can add your -own Rake tasks to accomplish project tasks outside of testing -and release builds. A facility for plugins also allows you to -extend Ceedling's capabilities for needs such as custom code -metrics reporting and coverage testing. - -What's with this Name? - -Glad you asked. Ceedling is tailored for unit tested C projects -and is built upon / around Rake (Rake is a Make replacement implemented -in the Ruby scripting language). So, we've got C, our Rake, and -the fertile soil of a build environment in which to grow and tend -your project and its unit tests. Ta da - _Ceedling_. - -What Do You Mean "tailored for unit tested C projects"? - -Well, we like to write unit tests for our C code to make it lean and -mean (that whole [Test-Driven Development][tdd] -thing). Along the way, this style of writing C code spawned two -tools to make the job easier: a unit test framework for C called -_Unity_ and a mocking library called _CMock_. And, though it's -not directly related to testing, a C framework for exception -handling called _CException_ also came along. - -[tdd]: http://en.wikipedia.org/wiki/Test-driven_development - -These tools and frameworks are great, but they require quite -a bit of environment support to pull them all together in a convenient, -usable fashion. We started off with Rakefiles to assemble everything. -These ended up being quite complicated and had to be hand-edited -or created anew for each new project. Ceedling replaces all that -tedium and rework with a configuration file that ties everything -together. - -Though Ceedling is tailored for unit testing, it can also go right ahead -and build your final binary release artifact for you as well. Or, -Ceedling and your tests can live alongside your existing release build -setup. That said, Ceedling is more powerful as a unit test build -environment than it is a general purpose release build environment; -complicated projects including separate bootloaders or multiple library -builds, etc. are not its strong suit. - -Hold on. Back up. Ruby? Rake? YAML? Unity? CMock? CException? - -Seem overwhelming? It's not bad at all, and for the benefits tests -bring us, it's all worth it. - -[Ruby][] is a handy scripting -language like Perl or Python. It's a modern, full featured language -that happens to be quite handy for accomplishing tasks like code -generation or automating one's workflow while developing in -a compiled language such as C. - -[Ruby]: http://www.ruby-lang.org/en/ - -[Rake][] is a utility written in Ruby -for accomplishing dependency tracking and task automation -common to building software. It's a modern, more flexible replacement -for [Make][]). -Rakefiles are Ruby files, but they contain build targets similar -in nature to that of Makefiles (but you can also run Ruby code in -your Rakefile). - -[Rake]: http://rubyrake.org/ -[Make]: http://en.wikipedia.org/wiki/Make_(software) - -[YAML][] is a "human friendly data serialization standard for all -programming languages." It's kinda like a markup language, but don't -call it that. With a YAML library, you can [serialize][] data structures -to and from the file system in a textual, human readable form. Ceedling -uses a serialized data structure as its configuration input. - -[YAML]: http://en.wikipedia.org/wiki/Yaml -[serialize]: http://en.wikipedia.org/wiki/Serialization - -[Unity] is a [unit test framework][test] for C. It provides facilities -for test assertions, executing tests, and collecting / reporting test -results. Unity derives its name from its implementation in a single C -source file (plus two C header files) and from the nature of its -implementation - Unity will build in any C toolchain and is configurable -for even the very minimalist of processors. - -[Unity]: http://github.com/ThrowTheSwitch/Unity -[test]: http://en.wikipedia.org/wiki/Unit_testing - -[CMock] is a tool written in Ruby able to generate entire -[mock functions][mock] in C code from a given C header file. Mock -functions are invaluable in [interaction-based unit testing][ut]. -CMock's generated C code uses Unity. - -[CMock]: http://github.com/ThrowTheSwitch/CMock -[mock]: http://en.wikipedia.org/wiki/Mock_object -[ut]: http://martinfowler.com/articles/mocksArentStubs.html - -[CException] is a C source and header file that provide a simple -[exception mechanism][exn] for C by way of wrapping up the -[setjmp / longjmp][setjmp] standard library calls. Exceptions are a much -cleaner and preferable alternative to managing and passing error codes -up your return call trace. - -[CException]: http://github.com/ThrowTheSwitch/CException -[exn]: http://en.wikipedia.org/wiki/Exception_handling -[setjmp]: http://en.wikipedia.org/wiki/Setjmp.h - -Notes ------ - -* YAML support is included with Ruby - requires no special installation - or configuration. - -* Unity, CMock, and CException are bundled with Ceedling, and - Ceedling is designed to glue them all together for your project - as seamlessly as possible. - - -Installation & Setup: What Exactly Do I Need to Get Started? ------------------------------------------------------------- - -As a [Ruby gem](http://docs.rubygems.org/read/chapter/1): - -1. [Download and install Ruby](http://www.ruby-lang.org/en/downloads/) - -2. Use Ruby's command line gem package manager to install Ceedling: - `gem install ceedling` - (Unity, CMock, and CException come along with Ceedling for free) - -3. Execute Ceedling at command line to create example project - or an empty Ceedling project in your filesystem (executing - `ceedling help` first is, well, helpful). - -Gem install notes: - -1. Steps 1-2 are a one time affair for your local environment. - When steps 1-2 are completed once, only step 3 is needed for - each new project. - - - -General notes: - -1. Certain advanced features of Ceedling rely on gcc and cpp - as preprocessing tools. In most *nix systems, these tools - are already available. For Windows environments, we recommend - the [mingw project](http://www.mingw.org/) (Minimalist - GNU for Windows). This represents an optional, additional - setup / installation step to complement the list above. Upon - installing mingw ensure your system path is updated or set - [:environment][:path] in your `project.yml` file (see - environment section later in this document). - -2. To use a project file name other than the default `project.yml` - or place the project file in a directory other than the one - in which you'll run Rake, create an environment variable - `CEEDLING_MAIN_PROJECT_FILE` with your desired project - file path. - -3. To better understand Rake conventions, Rake execution, - and Rakefiles, consult the [Rake tutorial, examples, and - user guide](http://rubyrake.org/). - -4. When using Ceedling in Windows environments, a test file name may - not include the sequences “patch” or “setup”. The Windows Installer - Detection Technology (part of UAC), requires administrator - privileges to execute file names with these strings. - - - -Now What? How Do I Make It GO? ------------------------------- - -We're getting a little ahead of ourselves here, but it's good -context on how to drive this bus. Everything is done via the command -line. We'll cover conventions and how to actually configure -your project in later sections. - -To run tests, build your release artifact, etc., you will be interacting -with Rake on the command line. Ceedling works with Rake to present -you with named tasks that coordinate the file generation and -build steps needed to accomplish something useful. You can also -add your own independent Rake tasks or create plugins to extend -Ceedling (more on this later). - - -* `ceedling [no arguments]`: - - Run the default Rake task (conveniently recognized by the name default - by Rake). Neither Rake nor Ceedling provide a default task. Rake will - abort if run without arguments when no default task is defined. You can - conveniently define a default task in the Rakefile discussed in the - preceding setup & installation section of this document. - -* `ceedling -T`: - - List all available Rake tasks with descriptions (Rake tasks without - descriptions are not listed). -T is a command line switch for Rake and - not the same as tasks that follow. - -* `ceedling <tasks...> --trace`: - - For advanced users troubleshooting a confusing build error, debug - Ceedling or a plugin, --trace provides a stack trace of dependencies - walked during task execution and any Ruby failures along the way. Note - that --trace is a command line switch for Rake and is not the same as - tasks that follow. - -* `ceedling environment`: - - List all configured environment variable names and string values. This - task is helpful in verifying the evaluatio of any Ruby expressions in - the [:environment] section of your config file.`: Note: Ceedling may - set some convenience environment variables by default. - -* `ceedling paths:*`: - - List all paths collected from [:paths] entries in your YAML config - file where * is the name of any section contained in [:paths]. This - task is helpful in verifying the expansion of path wildcards / globs - specified in the [:paths] section of your config file. - -* `ceedling files:assembly` -* `ceedling files:header` -* `ceedling files:source` -* `ceedling files:test` - - List all files and file counts collected from the relevant search - paths specified by the [:paths] entries of your YAML config file. The - files:assembly task will only be available if assembly support is - enabled in the [:release_build] section of your configuration file. - -* `ceedling options:*`: - - Load and merge configuration settings into the main project - configuration. Each task is named after a *.yml file found in the - configured options directory. See documentation for the configuration - setting [:project][:options_path] and for options files in advanced - topics. - -* `ceedling test:all`: - - Run all unit tests (rebuilding anything that's changed along the way). - -* `ceedling test:delta`: - - Run only those unit tests for which the source or test files have - changed (i.e. incremental build). Note: with the - [:project][:use_test_preprocessor] configuration file option set, - runner files are always regenerated limiting the total efficiency this - text execution option can afford. - -* `ceedling test:*`: - - Execute the named test file or the named source file that has an - accompanying test. No path. Examples: ceedling test:foo.c or ceed - test:test_foo.c - -* `ceedling test:pattern[*]`: - - Execute any tests whose name and/or path match the regular expression - pattern (case sensitive). Example: ceedling "test:pattern[(I|i)nit]" will - execute all tests named for initialization testing. Note: quotes may - be necessary around the ceedling parameter to distinguish regex characters - from command line operators. - -* `ceedling test:path[*]`: - - Execute any tests whose path contains the given string (case - sensitive). Example: ceedling test:path[foo/bar] will execute all tests - whose path contains foo/bar. Note: both directory separator characters - / and \ are valid. - -* `ceedling release`: - - Build all source into a release artifact (if the release build option - is configured). - -* `ceedling release:compile:*`: - - Sometimes you just need to compile a single file dagnabit. Example: - ceedling release:compile:foo.c - -* `ceedling release:assemble:*`: - - Sometimes you just need to assemble a single file doggonit. Example: - ceedling release:assemble:foo.s - -* `ceedling module:create[Filename]`: -* `ceedling module:create[<Path:>Filename]`: - - It's often helpful to create a file automatically. What's better than - that? Creating a source file, a header file, and a corresponding test - file all in one step! - - There are also patterns which can be specified to automatically generate - a bunch of files. Try `ceedling module:create[Poodles,mch]` for example! - - The module generator has several options you can configure. - F.e. Generating the source/header/test file in a subdirectory (by adding <Path> when calling module:create). - For more info, refer to the [Module Generator](https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md#module-generator) section. - -* `ceedling logging <tasks...>`: - - Enable logging to <build path>/logs. Must come before test and release - tasks to log their steps and output. Log names are a concatenation of - project, user, and option files loaded. User and option files are - documented in the advanced topics section of this document. - -* `ceedling verbosity[x] <tasks...>`: - - Change the default verbosity level. [x] ranges from 0 (quiet) to 4 - (obnoxious). Level [3] is the default. The verbosity task must precede - all tasks in the command line list for which output is desired to be - seen. Verbosity settings are generally most meaningful in conjunction - with test and release tasks. - -* `ceedling summary`: - - If plugins are enabled, this task will execute the summary method of - any plugins supporting it. This task is intended to provide a quick - roundup of build artifact metrics without re-running any part of the - build. - -* `ceedling clean`: - - Deletes all toolchain binary artifacts (object files, executables), - test results, and any temporary files. Clean produces no output at the - command line unless verbosity has been set to an appreciable level. - -* `ceedling clobber`: - - Extends clean task's behavior to also remove generated files: test - runners, mocks, preprocessor output. Clobber produces no output at the - command line unless verbosity has been set to an appreciable level. - -To better understand Rake conventions, Rake execution, and -Rakefiles, consult the [Rake tutorial, examples, and user guide][guide]. - -[guide]: http://rubyrake.org/ - -At present, none of Ceedling's commands provide persistence. -That is, they must each be specified at the command line each time -they are needed. For instance, Ceedling's verbosity command -only affects output at the time it's run. - -Individual test and release file tasks -are not listed in `-T` output. Because so many files may be present -it's unwieldy to list them all. - -Multiple rake tasks can be executed at the command line (order -is executed as provided). For example, `ceed -clobber test:all release` will removed all generated files; -build and run all tests; and then build all source - in that order. -If any Rake task fails along the way, execution halts before the -next task. - -The `clobber` task removes certain build directories in the -course of deleting generated files. In general, it's best not -to add to source control any Ceedling generated directories -below the root of your top-level build directory. That is, leave -anything Ceedling & its accompanying tools generate out of source -control (but go ahead and add the top-level build directory that -holds all that stuff). Also, since Ceedling is pretty smart about -what it rebuilds and regenerates, you needn't clobber often. - -Important Conventions -===================== - -Directory Structure, Filenames & Extensions -------------------------------------------- - -Much of Ceedling's functionality is driven by collecting files -matching certain patterns inside the paths it's configured -to search. See the documentation for the [:extensions] section -of your configuration file (found later in this document) to -configure the file extensions Ceedling uses to match and collect -files. Test file naming is covered later in this section. - -Test files and source files must be segregated by directories. -Any directory structure will do. Tests can be held in subdirectories -within source directories, or tests and source directories -can be wholly separated at the top of your project's directory -tree. - -Search Path Order ------------------ - -When Ceedling searches for files (e.g. looking for header files -to mock) or when it provides search paths to any of the default -gcc toolchain executables, it organizes / prioritizes its search -paths. The order is always: test paths, support paths, source -paths, and then include paths. This can be useful, for instance, -in certain testing scenarios where we desire Ceedling or a compiler -to find a stand-in header file in our support directory before -the actual source header file of the same name. - -This convention only holds when Ceedling is using its default -tool configurations and / or when tests are involved. If you define -your own tools in the configuration file (see the [:tools] section -documented later in this here document), you have complete control -over what directories are searched and in what order. Further, -test and support directories are only searched when appropriate. -That is, when running a release build, test and support directories -are not used at all. - -Source Files & Binary Release Artifacts ---------------------------------------- - -Your binary release artifact results from the compilation and -linking of all source files Ceedling finds in the specified source -directories. At present only source files with a single (configurable) -extension are recognized. That is, *.c and *.cc files will not -both be recognized - only one or the other. See the configuration -options and defaults in the documentation for the [:extensions] -sections of your configuration file (found later in this document). - -Test Files & Executable Test Fixtures -------------------------------------- - -Ceedling builds each individual test file with its accompanying -source file(s) into a single, monolithic test fixture executable. -Test files are recognized by a naming convention: a (configurable) -prefix such as "`test_`" in the file name with the same file extension -as used by your C source files. See the configuration options -and defaults in the documentation for the [:project] and [:extensions] -sections of your configuration file (found later in this document). -Depending on your configuration options, Ceedling can recognize -a variety of test file naming patterns in your test search paths. -For example: `test_some_super_functionality.c`, `TestYourSourceFile.cc`, -or `testing_MyAwesomeCode.C` could each be valid test file -names. Note, however, that Ceedling can recognize only one test -file naming convention per project. - -Ceedling knows what files to compile and link into each individual -test executable by way of the #include list contained in each -test file. Any C source files in the configured search directories -that correspond to the header files included in a test file will -be compiled and linked into the resulting test fixture executable. -From this same #include list, Ceedling knows which files to mock -and compile and link into the test executable (if you use mocks -in your tests). That was a lot of clauses and information in a very -few sentences; the example that follows in a bit will make it clearer. - -By naming your test functions according to convention, Ceedling -will extract and collect into a runner C file calls to all your -test case functions. This runner file handles all the execution -minutiae so that your test file can be quite simple and so that -you never forget to wire up a test function to be executed. In this -generated runner lives the `main()` entry point for the resulting -test executable. There are no configuration options for the -naming convention of your test case functions. A test case function -signature must have these three elements: void return, void -parameter list, and the function name prepended with lowercase -"`test`". In other words, a test function signature should look -like this: `void test``[any name you like]``(void)`. - -A commented sample test file follows on the next page. Also, see -the sample project contained in the Ceedling documentation -bundle. - -```c -// test_foo.c ----------------------------------------------- -#include "unity.h" // compile/link in Unity test framework -#include "types.h" // header file with no *.c file -- no compilation/linking -#include "foo.h" // source file foo.c under test -#include "mock_bar.h" // bar.h will be found and mocked as mock_bar.c + compiled/linked in; - // foo.c includes bar.h and uses functions declared in it -#include "mock_baz.h" // baz.h will be found and mocked as mock_baz.c + compiled/linked in - // foo.c includes baz.h and uses functions declared in it - - -void setUp(void) {} // every test file requires this function; - // setUp() is called by the generated runner before each test case function - -void tearDown(void) {} // every test file requires this function; - // tearDown() is called by the generated runner before each test case function - -// a test case function -void test_Foo_Function1_should_Call_Bar_AndGrill(void) -{ - Bar_AndGrill_Expect(); // setup function from mock_bar.c that instructs our - // framework to expect Bar_AndGrill() to be called once - TEST_ASSERT_EQUAL(0xFF, Foo_Function1()); // assertion provided by Unity - // Foo_Function1() calls Bar_AndGrill() & returns a byte -} - -// another test case function -void test_Foo_Function2_should_Call_Baz_Tec(void) -{ - Baz_Tec_ExpectAnd_Return(1); // setup function provided by mock_baz.c that instructs our - // framework to expect Baz_Tec() to be called once and return 1 - TEST_ASSERT_TRUE(Foo_Function2()); // assertion provided by Unity -} - -// end of test_foo.c ---------------------------------------- -``` - -From the test file specified above Ceedling will generate `test_foo_runner.c`; -this runner file will contain `main()` and call both of the example -test case functions. - -The final test executable will be `test_foo.exe` (for Windows -machines or `test_foo.out` for *nix systems - depending on default -or configured file extensions). Based on the #include list above, -the test executable will be the output of the linker having processed -`unity.o`, `foo.o`, `mock_bar.o`, `mock_baz.o`, `test_foo.o`, -and `test_foo_runner.o`. Ceedling finds the files, generates -mocks, generates a runner, compiles all the files, and links -everything into the test executable. Ceedling will then run -the test executable and collect test results from it to be reported -to the developer at the command line. - -For more on the assertions and mocks shown, consult the documentation -for Unity and CMock. - -The Magic of Dependency Tracking --------------------------------- - -Ceedling is pretty smart in using Rake to build up your project's -dependencies. This means that Ceedling automagically rebuilds -all the appropriate files in your project when necessary: when -your configuration changes, Ceedling or any of the other tools -are updated, or your source or test files change. For instance, -if you modify a header file that is mocked, Ceedling will ensure -that the mock is regenerated and all tests that use that mock are -rebuilt and re-run when you initiate a relevant testing task. -When you see things rebuilding, it's for a good reason. Ceedling -attempts to regenerate and rebuild only what's needed for a given -execution of a task. In the case of large projects, assembling -dependencies and acting upon them can cause some delay in executing -tasks. - -With one exception, the trigger to rebuild or regenerate a file -is always a disparity in timestamps between a target file and -its source - if an input file is newer than its target dependency, -the target is rebuilt or regenerated. For example, if the C source -file from which an object file is compiled is newer than that object -file on disk, recompilation will occur (of course, if no object -file exists on disk, compilation will always occur). The one -exception to this dependency behavior is specific to your input -configuration. Only if your logical configuration changes -will a system-wide rebuild occur. Reorganizing your input configuration -or otherwise updating its file timestamp without modifying -the values within the file will not trigger a rebuild. This behavior -handles the various ways in which your input configuration can -change (discussed later in this document) without having changed -your actual project YAML file. - -Ceedling needs a bit of help to accomplish its magic with deep -dependencies. Shallow dependencies are straightforward: -a mock is dependent on the header file from which it's generated, -a test file is dependent upon the source files it includes (see -the preceding conventions section), etc. Ceedling handles -these "out of the box." Deep dependencies are specifically a -C-related phenomenon and occur as a consequence of include statements -within C source files. Say a source file includes a header file -and that header file in turn includes another header file which -includes still another header file. A change to the deepest header -file should trigger a recompilation of the source file, a relinking -of all the object files comprising a test fixture, and a new execution -of that test fixture. - -Ceedling can handle deep dependencies but only with the help -of a C preprocessor. Ceedling is quite capable, but a full C preprocessor -it ain't. Your project can be configured to use a C preprocessor -or not. Simple projects or large projects constructed so as to -be quite flat in their include structure generally don't need -deep dependency preprocessing - and can enjoy the benefits of -faster execution. Legacy code, on the other hand, will almost -always want to be tested with deep preprocessing enabled. Set -up of the C preprocessor is covered in the documentation for the -[:project] and [:tools] section of the configuration file (later -in this document). Ceedling contains all the configuration -necessary to use the gcc preprocessor by default. That is, as -long as gcc is in your system search path, deep preprocessing -of deep dependencies is available to you by simply enabling it -in your project configuration file. - -Ceedling's Build Output ------------------------ - -Ceedling requires a top-level build directory for all the stuff -that it, the accompanying test tools, and your toolchain generate. -That build directory's location is configured in the [:project] -section of your configuration file (discussed later). There -can be a ton of generated files. By and large, you can live a full -and meaningful life knowing absolutely nothing at all about -the files and directories generated below the root build directory. - -As noted already, it's good practice to add your top-level build -directory to source control but nothing generated beneath it. -You'll spare yourself headache if you let Ceedling delete and -regenerate files and directories in a non-versioned corner -of your project's filesystem beneath the top-level build directory. - -The `artifacts` directory is the one and only directory you may -want to know about beneath the top-level build directory. The -subdirectories beneath `artifacts` will hold your binary release -target output (if your project is configured for release builds) -and will serve as the conventional location for plugin output. -This directory structure was chosen specifically because it -tends to work nicely with Continuous Integration setups that -recognize and list build artifacts for retrieval / download. - -The Almighty Project Configuration File (in Glorious YAML) ----------------------------------------------------------- - -Please consult YAML documentation for the finer points of format -and to understand details of our YAML-based configuration file. -We recommend [Wikipedia's entry on YAML](http://en.wikipedia.org/wiki/Yaml) -for this. A few highlights from that reference page: - -* YAML streams are encoded using the set of printable Unicode - characters, either in UTF-8 or UTF-16 - -* Whitespace indentation is used to denote structure; however - tab characters are never allowed as indentation - -* Comments begin with the number sign ( # ), can start anywhere - on a line, and continue until the end of the line unless enclosed - by quotes - -* List members are denoted by a leading hyphen ( - ) with one member - per line, or enclosed in square brackets ( [ ] ) and separated - by comma space ( , ) - -* Hashes are represented using the colon space ( : ) in the form - key: value, either one per line or enclosed in curly braces - ( { } ) and separated by comma space ( , ) - -* Strings (scalars) are ordinarily unquoted, but may be enclosed - in double-quotes ( " ), or single-quotes ( ' ) - -* YAML requires that colons and commas used as list separators - be followed by a space so that scalar values containing embedded - punctuation can generally be represented without needing - to be enclosed in quotes - -* Repeated nodes are initially denoted by an ampersand ( & ) and - thereafter referenced with an asterisk ( * ) - - -Notes on what follows: - -* Each of the following sections represent top-level entries - in the YAML configuration file. - -* Unless explicitly specified in the configuration file, default - values are used by Ceedling. - -* These three settings, at minimum, must be specified: - * [:project][:build_root] - * [:paths][:source] - * [:paths][:test] - -* As much as is possible, Ceedling validates your settings in - properly formed YAML. - -* Improperly formed YAML will cause a Ruby error when the YAML - is parsed. This is usually accompanied by a complaint with - line and column number pointing into the project file. - -* Certain advanced features rely on gcc and cpp as preprocessing - tools. In most *nix systems, these tools are already available. - For Windows environments, we recommend the [mingw project](http://www.mingw.org/) - (Minimalist GNU for Windows). - -* Ceedling is primarily meant as a build tool to support automated - unit testing. All the heavy lifting is involved there. Creating - a simple binary release build artifact is quite trivial in - comparison. Consequently, most default options and the construction - of Ceedling itself is skewed towards supporting testing though - Ceedling can, of course, build your binary release artifact - as well. Note that complex binary release artifacts (e.g. - application + bootloader or multiple libraries) are beyond - Ceedling's release build ability. - - -Conventions / features of Ceedling-specific YAML: - -* Any second tier setting keys anywhere in YAML whose names end - in `_path` or `_paths` are automagically processed like all - Ceedling-specific paths in the YAML to have consistent directory - separators (i.e. "/") and to take advantage of inline Ruby - string expansion (see [:environment] setting below for further - explanation of string expansion). - - -**Let's Be Careful Out There:** Ceedling performs validation -on the values you set in your configuration file (this assumes -your YAML is correct and will not fail format parsing, of course). -That said, validation is limited to only those settings Ceedling -uses and those that can be reasonably validated. Ceedling does -not limit what can exist within your configuration file. In this -way, you can take full advantage of YAML as well as add sections -and values for use in your own custom plugins (documented later). -The consequence of this is simple but important. A misspelled -configuration section name or value name is unlikely to cause -Ceedling any trouble. Ceedling will happily process that section -or value and simply use the properly spelled default maintained -internally - thus leading to unexpected behavior without warning. - -project: global project settings - - -* `build_root`: - - Top level directory into which generated path structure and files are - placed. Note: this is one of the handful of configuration values that - must be set. The specified path can be absolute or relative to your - working directory. - - **Default**: (none) - -* `use_exceptions`: - - Configures the build environment to make use of CException. Note that - if you do not use exceptions, there's no harm in leaving this as its - default value. - - **Default**: TRUE - -* `use_mocks`: - - Configures the build environment to make use of CMock. Note that if - you do not use mocks, there's no harm in leaving this setting as its - default value. - - **Default**: TRUE - -* `use_test_preprocessor`: - - This option allows Ceedling to work with test files that contain - conditional compilation statements (e.g. #ifdef) and header files you - wish to mock that contain conditional preprocessor statements and/or - macros. - - Ceedling and CMock are advanced tools with sophisticated parsers. - However, they do not include entire C language preprocessors. - Consequently, with this option enabled, Ceedling will use gcc's - preprocessing mode and the cpp preprocessor tool to strip down / - expand test files and headers to their applicable content which can - then be processed by Ceedling and CMock. - - With this option enabled, the gcc & cpp tools must exist in an - accessible system search path and test runner files are always - regenerated. - - **Default**: FALSE - -* `use_deep_dependencies`: - - The base rules and tasks that Ceedling creates using Rake capture most - of the dependencies within a standard project (e.g. when the source - file accompanying a test file changes, the corresponding test fixture - executable will be rebuilt when tests are re-run). However, deep - dependencies cannot be captured this way. If a typedef or macro - changes in a header file three levels of #include statements deep, - this option allows the appropriate incremental build actions to occur - for both test execution and release builds. - - This is accomplished by using the dependencies discovery mode of gcc. - With this option enabled, gcc must exist in an accessible system - search path. - - **Default**: FALSE - -* `generate_deep_dependencies`: - - When `use_deep_dependencies` is set to TRUE, Ceedling will run a separate - build step to generate the deep dependencies. If you are using gcc as your - primary compiler, or another compiler that can generate makefile rules as - a side effect of compilation, then you can set this to FALSE to avoid the - extra build step but still use the deep dependencies data when deciding - which source files to rebuild. - - **Default**: TRUE - -* `test_file_prefix`: - - Ceedling collects test files by convention from within the test file - search paths. The convention includes a unique name prefix and a file - extension matching that of source files. - - Why not simply recognize all files in test directories as test files? - By using the given convention, we have greater flexibility in what we - do with C files in the test directories. - - **Default**: "test_" - -* `options_paths`: - - Just as you may have various build configurations for your source - codebase, you may need variations of your project configuration. - - By specifying options paths, Ceedling will search for other project - YAML files, make command line tasks available (ceedling options:variation - for a variation.yml file), and merge the project configuration of - these option files in with the main project file at runtime. See - advanced topics. - - Note these Rake tasks at the command line - like verbosity or logging - control - must come before the test or release task they are meant to - modify. - - **Default**: [] (empty) - -* `release_build`: - - When enabled, a release Rake task is exposed. This configuration - option requires a corresponding release compiler and linker to be - defined (gcc is used as the default). - - More release configuration options are available in the release_build - section. - - **Default**: FALSE - - -Example `[:project]` YAML blurb - -```yaml -:project: - :build_root: project_awesome/build - :use_exceptions: FALSE - :use_test_preprocessor: TRUE - :use_deep_dependencies: TRUE - :options_paths: - - project/options - - external/shared/options - :release_build: TRUE -``` - -Ceedling is primarily concerned with facilitating the somewhat -complicated mechanics of automating unit tests. The same mechanisms -are easily capable of building a final release binary artifact -(i.e. non test code; the thing that is your final working software -that you execute on target hardware). - - -* `output`: - - The name of your release build binary artifact to be found in <build - path>/artifacts/release. Ceedling sets the default artifact file - extension to that as is explicitly specified in the [:extensions] - section or as is system specific otherwise. - - **Default**: `project.exe` or `project.out` - -* `use_assembly`: - - If assembly code is present in the source tree, this option causes - Ceedling to create appropriate build directories and use an assembler - tool (default is the GNU tool as - override available in the [:tools] - section. - - **Default**: FALSE - -* `artifacts`: - - By default, Ceedling copies to the <build path>/artifacts/release - directory the output of the release linker and (optionally) a map - file. Many toolchains produce other important output files as well. - Adding a file path to this list will cause Ceedling to copy that file - to the artifacts directory. The artifacts directory is helpful for - organizing important build output files and provides a central place - for tools such as Continuous Integration servers to point to build - output. Selectively copying files prevents incidental build cruft from - needlessly appearing in the artifacts directory. Note that inline Ruby - string replacement is available in the artifacts paths (see discussion - in the [:environment] section). - - **Default**: [] (empty) - -Example `[:release_build]` YAML blurb - -```yaml -:release_build: - :output: top_secret.bin - :use_assembly: TRUE - :artifacts: - - build/release/out/c/top_secret.s19 -``` - -**paths**: options controlling search paths for source and header -(and assembly) files - -* `test`: - - All C files containing unit test code. Note: this is one of the - handful of configuration values that must be set. - - **Default**: [] (empty) - -* `source`: - - All C files containing release code (code to be tested). Note: this is - one of the handful of configuration values that must be set. - - **Default**: [] (empty) - -* `support`: - - Any C files you might need to aid your unit testing. For example, on - occasion, you may need to create a header file containing a subset of - function signatures matching those elsewhere in your code (e.g. a - subset of your OS functions, a portion of a library API, etc.). Why? - To provide finer grained control over mock function substitution or - limiting the size of the generated mocks. - - **Default**: [] (empty) - -* `include`: - - Any header files not already in the source search path. Note there's - no practical distinction between this search path and the source - search path; it's merely to provide options or to support any - peculiar source tree organization. - - **Default**: [] (empty) - -* `test_toolchain_include`: - - System header files needed by the test toolchain - should your - compiler be unable to find them, finds the wrong system include search - path, or you need a creative solution to a tricky technical problem. - Note that if you configure your own toolchain in the [:tools] section, - this search path is largely meaningless to you. However, this is a - convenient way to control the system include path should you rely on - the default gcc tools. - - **Default**: [] (empty) - -* `release_toolchain_include`: - - Same as preceding albeit related to the release toolchain. - - **Default**: [] (empty) - -* `<custom>` - - Any paths you specify for custom list. List is available to tool - configurations and/or plugins. Note a distinction. The preceding names - are recognized internally to Ceedling and the path lists are used to - build collections of files contained in those paths. A custom list is - just that - a custom list of paths. - -Notes on path grammar within the [:paths] section: - -* Order of search paths listed in [:paths] is preserved when used by an - entry in the [:tools] section - -* Wherever multiple path lists are combined for use Ceedling prioritizes - path groups as follows: - test paths, support paths, source paths, include paths. - - This can be useful, for instance, in certain testing scenarios where - we desire Ceedling or the compiler to find a stand-in header file before - the actual source header file of the same name. - -* Paths: - - 1. can be absolute or relative - - 2. can be singly explicit - a single fully specified path - - 3. can include a glob operator (more on this below) - - 4. can use inline Ruby string replacement (see [:environment] - section for more) - - 5. default as an addition to a specific search list (more on this - in the examples) - - 6. can act to subtract from a glob included in the path list (more - on this in the examples) - - -[Globs](http://ruby.about.com/od/beginningruby/a/dir2.htm) -as used by Ceedling are wildcards for specifying directories -without the need to list each and every required search path. -Ceedling globs operate just as Ruby globs except that they are -limited to matching directories and not files. Glob operators -include the following * ** ? [-] {,} (note: this list is space separated -and not comma separated as commas are used within the bracket -operators). - -* `*`: - - All subdirectories of depth 1 below the parent path and including the - parent path - -* `**`: - - All subdirectories recursively discovered below the parent path and - including the parent path - -* `?`: - - Single alphanumeric character wildcard - -* `[x-y]`: - - Single alphanumeric character as found in the specified range - -* `{x,y}`: - - Single alphanumeric character from the specified list - -Example [:paths] YAML blurbs - -```yaml -:paths: - :source: #together the following comprise all source search paths - - project/source/* #expansion yields all subdirectories of depth 1 plus parent directory - - project/lib #single path - :test: #all test search paths - - project/**/test? #expansion yields any subdirectory found anywhere in the project that - #begins with "test" and contains 5 characters - -:paths: - :source: #all source search paths - - +:project/source/** #all subdirectories recursively discovered plus parent directory - - -:project/source/os/generated #subtract os/generated directory from expansion of above glob - #note that '+:' notation is merely aesthetic; default is to add - - :test: #all test search paths - - project/test/bootloader #explicit, single search paths (searched in the order specified) - - project/test/application - - project/test/utilities - - :custom: #custom path list - - "#{PROJECT_ROOT}/other" #inline Ruby string expansion -``` - -Globs and inline Ruby string expansion can require trial and -error to arrive at your intended results. Use the `ceedling paths:*` -command line options (documented in preceding section) to verify -your settings. - -Ceedling relies on file collections automagically assembled -from paths, globs, and file extensions. File collections greatly -simplify project set up. However, sometimes you need to remove -from or add individual files to those collections. - - -* `test`: - - Modify the collection of unit test C files. - - **Default**: [] (empty) - -* `source`: - - Modify the collection of all source files used in unit test builds and release builds. - - **Default**: [] (empty) - -* `assembly`: - - Modify the (optional) collection of assembly files used in release builds. - - **Default**: [] (empty) - -* `include`: - - Modify the collection of all source header files used in unit test builds (e.g. for mocking) and release builds. - - **Default**: [] (empty) - -* `support`: - - Modify the collection of supporting C files available to unit tests builds. - - **Default**: [] (empty) - - -Note: All path grammar documented in [:paths] section applies -to [:files] path entries - albeit at the file path level and not -the directory level. - -Example [:files] YAML blurb - -```yaml -:files: - :source: - - callbacks/comm.c # entry defaults to file addition - - +:callbacks/comm*.c # add all comm files matching glob pattern - - -:source/board/atm134.c # not our board - :test: - - -:test/io/test_output_manager.c # remove unit tests from test build -``` - -**environment:** inserts environment variables into the shell -instance executing configured tools - -Ceedling creates environment variables from any key / value -pairs in the environment section. Keys become an environment -variable name in uppercase. The values are strings assigned -to those environment variables. These value strings are either -simple string values in YAML or the concatenation of a YAML array. - -Ceedling is able to execute inline Ruby string substitution -code to set environment variables. This evaluation occurs when -the project file is first processed for any environment pair's -value string including the Ruby string substitution pattern -`#{…}`. Note that environment value strings that _begin_ with -this pattern should always be enclosed in quotes. YAML defaults -to processing unquoted text as a string; quoting text is optional. -If an environment pair's value string begins with the Ruby string -substitution pattern, YAML will interpret the string as a Ruby -comment (because of the `#`). Enclosing each environment value -string in quotes is a safe practice. - -[:environment] entries are processed in the configured order -(later entries can reference earlier entries). - -Special case: PATH handling - -In the specific case of specifying an environment key named _path_, -an array of string values will be concatenated with the appropriate -platform-specific path separation character (e.g. ':' on *nix, -';' on Windows). All other instances of environment keys assigned -YAML arrays use simple concatenation. - -Example [:environment] YAML blurb - -```yaml -:environment: - - :license_server: gizmo.intranet #LICENSE_SERVER set with value "gizmo.intranet" - - :license: "#{`license.exe`}" #LICENSE set to string generated from shelling out to - #execute license.exe; note use of enclosing quotes - - - :path: #concatenated with path separator (see special case above) - - Tools/gizmo/bin #prepend existing PATH with gizmo path - - "#{ENV['PATH']}" #pattern #{…} triggers ruby evaluation string substitution - #note: value string must be quoted because of '#' - - - :logfile: system/logs/thingamabob.log #LOGFILE set with path for a log file -``` - -**extension**: configure file name extensions used to collect lists of files searched in [:paths] - -* `header`: - - C header files - - **Default**: .h - -* `source`: - - C code files (whether source or test files) - - **Default**: .c - -* `assembly`: - - Assembly files (contents wholly assembly instructions) - - **Default**: .s - -* `object`: - - Resulting binary output of C code compiler (and assembler) - - **Default**: .o - -* `executable`: - - Binary executable to be loaded and executed upon target hardware - - **Default**: .exe or .out (Win or *nix) - -* `testpass`: - - Test results file (not likely to ever need a new value) - - **Default**: .pass - -* `testfail`: - - Test results file (not likely to ever need a new value) - - **Default**: .fail - -* `dependencies`: - - File containing make-style dependency rules created by gcc preprocessor - - **Default**: .d - - -Example [:extension] YAML blurb - - :extension: - :source: .cc - :executable: .bin - -**defines**: command line defines used in test and release compilation by configured tools - -* `test`: - - Defines needed for testing. Useful for: - - 1. test files containing conditional compilation statements (i.e. - tests active in only certain contexts) - - 2. testing legacy source wherein the isolation of source under test - afforded by Ceedling and its complementary tools leaves certain - symbols unset when source files are compiled in isolation - - **Default**: [] (empty) - -* `test_preprocess`: - - If [:project][:use_test_preprocessor] or - [:project][:use_deep_dependencies] is set and code is structured in a - certain way, the gcc preprocessor may need symbol definitions to - properly preprocess files to extract function signatures for mocking - and extract deep dependencies for incremental builds. - - **Default**: [] (empty) - -* `release`: - - Defines needed for the release build binary artifact. - - **Default**: [] (empty) - -* `release_preprocess`: - - If [:project][:use_deep_dependencies] is set and code is structured in - a certain way, the gcc preprocessor may need symbol definitions to - properly preprocess files for incremental release builds due to deep - dependencies. - - **Default**: [] (empty) - - -Example [:defines] YAML blurb - -```yaml -:defines: - :test: - - UNIT_TESTING #for select cases in source to allow testing with a changed behavior or interface - - OFF=0 - - ON=1 - - FEATURE_X=ON - :source: - - FEATURE_X=ON -``` - - -**libraries**: command line defines used in test and release compilation by configured tools - -Ceedling allows you to pull in specific libraries for the purpose of release and test builds. -It has a few levels of support for this. Start by adding a :libraries main section in your -configuration. In this section, you can optionally have the following subsections: - -* `test`: - - Library files that should be injected into your tests when linking occurs. - These can be specified as either relative or absolute paths. These files MUST - exist when the test attempts to build. - -* `source`: - - Library files that should be injected into your release when linking occurs. These - can be specified as either relative or absolute paths. These files MUST exist when - the release attempts to build UNLESS you are using the subprojects plugin. In that - case, it will attempt to build that library for you as a dynamic dependency. - -* `system`: - - These libraries are assumed to be in the tool path somewhere and shouldn't need to be - specified. The libraries added here will be injected into releases and tests. - -* `flag`: - - This is the method of adding an argument for each library. For example, gcc really likes - it when you specify “-l${1}” - -Notes: - -* If you've specified your own link step, you are going to want to add ${4} to your argument -list in the place where library files should be added to the command call. For gcc, this is -often the very end. Other tools may vary. - - -**flags**: configure per-file compilation and linking flags - -Ceedling tools (see later [:tools] section) are used to configure -compilation and linking of test and source files. These tool -configurations are a one-size-fits-all approach. Should individual files -require special compilation or linking flags, the settings in the -[:flags] section work in conjunction with tool definitions by way of -argument substitution to achieve this. - -* `release`: - - [:compile] or [:link] flags for release build - -* `test`: - - [:compile] or [:link] flags for test build - -Notes: - -* Ceedling works with the [:release] and [:test] build contexts - as-is; plugins can add additional contexts - -* Only [:compile] and [:link] are recognized operations beneath - a context - -* File specifiers do not include a path or file extension - -* File specifiers are case sensitive (must match original file - name) - -* File specifiers do support regular expressions if encased in quotes - -* '*' is a special (optional) file specifier to provide flags - to all files not otherwise specified - - -Example [:flags] YAML blurb - -```yaml -:flags: - :release: - :compile: - :main: # add '-Wall' to compilation of main.c - - -Wall - :fan: # add '--O2' to compilation of fan.c - - --O2 - :'test_.+': # add '-pedantic' to all test-files - - -pedantic - :*: # add '-foo' to compilation of all files not main.c or fan.c - - -foo - :test: - :compile: - :main: # add '--O1' to compilation of main.c as part of test builds including main.c - - --O1 - :link: - :test_main: # add '--bar --baz' to linking of test_main.exe - - --bar - - --baz -``` - -Ceedling sets values for a subset of CMock settings. All CMock -options are available to be set, but only those options set by -Ceedling in an automated fashion are documented below. See CMock -documentation. - -**cmock**: configure CMock's code generation options and set symbols used to modify CMock's compiled features -Ceedling sets values for a subset of CMock settings. All CMock options are available to be set, but only those options set by Ceedling in an automated fashion are documented below. See CMock documentation. - -* `enforce_strict_ordering`: - - Tests fail if expected call order is not same as source order - - **Default**: TRUE - -* `mock_path`: - - Path for generated mocks - - **Default**: <build path>/tests/mocks - -* `defines`: - - List of conditional compilation symbols used to configure CMock's - compiled features. See CMock documentation to understand available - options. No symbols must be set unless defaults are inappropriate for - your specific environment. All symbols are used only by Ceedling to - compile CMock C code; contents of [:defines] are ignored by CMock's - Ruby code when instantiated. - - **Default**: [] (empty) - -* `verbosity`: - - If not set, defaults to Ceedling's verbosity level - -* `plugins`: - - If [:project][:use_exceptions] is enabled, the internal plugins list is pre-populated with 'cexception'. - - Whether or not you have included [:cmock][:plugins] in your - configuration file, Ceedling automatically adds 'cexception' to the - plugin list if exceptions are enabled. To add to the list Ceedling - provides CMock, simply add [:cmock][:plugins] to your configuration - and specify your desired additional plugins. - -* `includes`: - - If [:cmock][:unity_helper] set, pre-populated with unity_helper file - name (no path). - - The [:cmock][:includes] list works identically to the plugins list - above with regard to adding additional files to be inserted within - mocks as #include statements. - - -The last four settings above are directly tied to other Ceedling -settings; hence, why they are listed and explained here. The -first setting above, [:enforce_strict_ordering], defaults -to FALSE within CMock. It is set to TRUE by default in Ceedling -as our way of encouraging you to use strict ordering. It's a teeny -bit more expensive in terms of code generated, test execution -time, and complication in deciphering test failures. However, -it's good practice. And, of course, you can always disable it -by overriding the value in the Ceedling YAML configuration file. - - -**cexception**: configure symbols used to modify CException's compiled features - -* `defines`: - - List of conditional compilation symbols used to configure CException's - features in its source and header files. See CException documentation - to understand available options. No symbols must be set unless the - defaults are inappropriate for your specific environment. - - **Default**: [] (empty) - - -**unity**: configure symbols used to modify Unity's compiled features - -* `defines`: - - List of conditional compilation symbols used to configure Unity's - features in its source and header files. See Unity documentation to - understand available options. No symbols must be set unless the - defaults are inappropriate for your specific environment. Most Unity - defines can be easily configured through the YAML file. - - **Default**: [] (empty) - -Example [:unity] YAML blurbs -```yaml -:unity: #itty bitty processor & toolchain with limited test execution options - :defines: - - UNITY_INT_WIDTH=16 #16 bit processor without support for 32 bit instructions - - UNITY_EXCLUDE_FLOAT #no floating point unit - -:unity: #great big gorilla processor that grunts and scratches - :defines: - - UNITY_SUPPORT_64 #big memory, big counters, big registers - - UNITY_LINE_TYPE=\"unsigned int\" #apparently we're using really long test files, - - UNITY_COUNTER_TYPE=\"unsigned int\" #and we've got a ton of test cases in those test files - - UNITY_FLOAT_TYPE=\"double\" #you betcha -``` - - -Notes on Unity configuration: - -* **Verification** - Ceedling does no verification of your configuration - values. In a properly configured setup, your Unity configuration - values are processed, collected together with any test define symbols - you specify elsewhere, and then passed to your toolchain during test - compilation. Unity's conditional compilation statements, your - toolchain's preprocessor, and/or your toolchain's compiler will - complain appropriately if your specified configuration values are - incorrect, incomplete, or incompatible. - -* **Routing $stdout** - Unity defaults to using `putchar()` in C's - standard library to display test results. For more exotic environments - than a desktop with a terminal (e.g. running tests directly on a - non-PC target), you have options. For example, you could create a - routine that transmits a character via RS232 or USB. Once you have - that routine, you can replace `putchar()` calls in Unity by overriding - the function-like macro `UNITY_OUTPUT_CHAR`. Consult your toolchain - and shell documentation. Eventhough this can also be defined in the YAML file - most shell environments do not handle parentheses as command line arguments - very well. To still be able to add this functionality all necessary - options can be defined in the `unity_config.h`. Unity needs to be told to look for - the `unity_config.h` in the YAML file, though. - -Example [:unity] YAML blurbs -```yaml -:unity: - :defines: - - UNITY_INCLUDE_CONFIG_H -``` - -Example unity_config.h -``` -#ifndef UNITY_CONFIG_H -#define UNITY_CONFIG_H - -#include "uart_output.h" //Helper library for your custom environment - -#define UNITY_INT_WIDTH 16 -#define UNITY_OUTPUT_START() uart_init(F_CPU, BAUD) //Helperfunction to init UART -#define UNITY_OUTPUT_CHAR(a) uart_putchar(a) //Helperfunction to forward char via UART -#define UNITY_OUTPUT_COMPLETE() uart_complete() //Helperfunction to inform that test has ended - -#endif -``` - - -**tools**: a means for representing command line tools for use under -Ceedling's automation framework - -Ceedling requires a variety of tools to work its magic. By default, -the GNU toolchain (gcc, cpp, as) are configured and ready for -use with no additions to the project configuration YAML file. -However, as most work will require a project-specific toolchain, -Ceedling provides a generic means for specifying / overriding -tools. - -* `test_compiler`: - - Compiler for test & source-under-test code - ${1}: input source ${2}: output object ${3}: optional output list ${4}: optional output dependencies file - - **Default**: gcc - -* `test_linker`: - - Linker to generate test fixture executables - ${1}: input objects ${2}: output binary ${3}: optional output map ${4}: optional library list - - **Default**: gcc - -* `test_fixture`: - - Executable test fixture - ${1}: simulator as executable with ${1} as input binary file argument or native test executable - - **Default**: ${1} - -* `test_includes_preprocessor`: - - Extractor of #include statements - ${1}: input source file - - **Default**: cpp - -* `test_file_preprocessor`: - - Preprocessor of test files (macros, conditional compilation statements) - ${1}: input source file ${2}: preprocessed output source file - - **Default**: gcc - -* `test_dependencies_generator`: - - Discovers deep dependencies of source & test (for incremental builds) - ${1}: input source file ${2}: compiled object filepath ${3}: output dependencies file - - **Default**: gcc - -* `release_compiler`: - - Compiler for release source code - ${1}: input source ${2}: output object ${3}: optional output list ${4}: optional output dependencies file - - **Default**: gcc - -* `release_assembler`: - - Assembler for release assembly code - ${1}: input assembly source file ${2}: output object file - - **Default**: as - -* `release_linker`: - - Linker for release source code - ${1}: input objects ${2}: output binary ${3}: optional output map ${4}: optional library list - - **Default**: gcc - -* `release_dependencies_generator`: - - Discovers deep dependencies of source files (for incremental builds) - ${1}: input source file ${2}: compiled object filepath ${3}: output dependencies file - - **Default**: gcc - - -A Ceedling tool has a handful of configurable elements: - -1. [:executable] (required) - Command line executable having - the form of: - -2. [:arguments] (required) - List of command line arguments - and substitutions - -3. [:name] - Simple name (e.g. "nickname") of tool beyond its - executable name (if not explicitly set then Ceedling will - form a name from the tool's YAML entry name) - -4. [:stderr_redirect] - Control of capturing $stderr messages - {:none, :auto, :win, :unix, :tcsh}. - Defaults to :none if unspecified; create a custom entry by - specifying a simple string instead of any of the available - symbols. - -5. [:background_exec] - Control execution as background process - {:none, :auto, :win, :unix}. - Defaults to :none if unspecified. - - -Tool Element Runtime Substitution ---------------------------------- - -To accomplish useful work on multiple files, a configured tool will most -often require that some number of its arguments or even the executable -itself change for each run. Consequently, every tool's argument list and -executable field possess two means for substitution at runtime. Ceedling -provides two kinds of inline Ruby execution and a notation for -populating elements with dynamically gathered values within the build -environment. - -Tool Element Runtime Substitution: Inline Ruby Execution --------------------------------------------------------- - -In-line Ruby execution works similarly to that demonstrated for the -[:environment] section except that substitution occurs as the tool is -executed and not at the time the configuration file is first scanned. - -* `#{...}`: - - Ruby string substitution pattern wherein the containing string is - expanded to include the string generated by Ruby code between the - braces. Multiple instances of this expansion can occur within a single - tool element entry string. Note that if this string substitution - pattern occurs at the very beginning of a string in the YAML - configuration the entire string should be enclosed in quotes (see the - [:environment] section for further explanation on this point). - -* `{...} `: - - If an entire tool element string is enclosed with braces, it signifies - that Ceedling should execute the Ruby code contained within those - braces. Say you have a collection of paths on disk and some of those - paths include spaces. Further suppose that a single tool that must use - those paths requires those spaces to be escaped, but all other uses of - those paths requires the paths to remain unchanged. You could use this - Ceedling feature to insert Ruby code that iterates those paths and - escapes those spaces in the array as used by the tool of this example. - -Tool Element Runtime Substitution: Notational Substitution ----------------------------------------------------------- - -A Ceedling tool's other form of dynamic substitution relies on a '$' -notation. These '$' operators can exist anywhere in a string and can be -decorated in any way needed. To use a literal '$', escape it as '\\$'. - -* `$`: - - Simple substitution for value(s) globally available within the runtime - (most often a string or an array). - -* `${#}`: - - When a Ceedling tool's command line is expanded from its configured - representation and used within Ceedling Ruby code, certain calls to - that tool will be made with a parameter list of substitution values. - Each numbered substitution corresponds to a position in a parameter - list. Ceedling Ruby code expects that configured compiler and linker - tools will contain ${1} and ${2} replacement arguments. In the case of - a compiler ${1} will be a C code file path, and ${2} will be the file - path of the resulting object file. For a linker ${1} will be an array - of object files to link, and ${2} will be the resulting binary - executable. For an executable test fixture ${1} is either the binary - executable itself (when using a local toolchain such as gcc) or a - binary input file given to a simulator in its arguments. - - -Example [:tools] YAML blurbs - -```yaml -:tools: - :test_compiler: - :executable: compiler #exists in system search path - :name: 'acme test compiler' - :arguments: - - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE #expands to -I search paths - - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR #expands to -I search paths - - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR #expands to all -D defined symbols - - --network-license #simple command line argument - - -optimize-level 4 #simple command line argument - - "#{`args.exe -m acme.prj`}" #in-line ruby sub to shell out & build string of arguments - - -c ${1} #source code input file (Ruby method call param list sub) - - -o ${2} #object file output (Ruby method call param list sub) - :test_linker: - :executable: /programs/acme/bin/linker.exe #absolute file path - :name: 'acme test linker' - :arguments: - - ${1} #list of object files to link (Ruby method call param list sub) - - -l$-lib: #inline yaml array substitution to link in foo-lib and bar-lib - - foo - - bar - - -o ${2} #executable file output (Ruby method call param list sub) - :test_fixture: - :executable: tools/bin/acme_simulator.exe #relative file path to command line simulator - :name: 'acme test fixture' - :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use - :arguments: - - -mem large #simple command line argument - - -f "${1}" #binary executable input file to simulator (Ruby method call param list sub) -``` - -Resulting command line constructions from preceding example [:tools] YAML blurbs - - > compiler -I"/usr/include” -I”project/tests” - -I"project/tests/support” -I”project/source” -I”project/include” - -DTEST -DLONG_NAMES -network-license -optimize-level 4 arg-foo - arg-bar arg-baz -c project/source/source.c -o - build/tests/out/source.o - -[notes: (1.) "arg-foo arg-bar arg-baz" is a fabricated example -string collected from $stdout as a result of shell execution -of args.exe -(2.) the -c and -o arguments are -fabricated examples simulating a single compilation step for -a test; ${1} & ${2} are single files] - - > \programs\acme\bin\linker.exe thing.o unity.o - test_thing_runner.o test_thing.o mock_foo.o mock_bar.o -lfoo-lib - -lbar-lib -o build\tests\out\test_thing.exe - -[note: in this scenario ${1} is an array of all the object files -needed to link a test fixture executable] - - > tools\bin\acme_simulator.exe -mem large -f "build\tests\out\test_thing.bin 2>&1” - -[note: (1.) :executable could have simply been ${1} - if we were compiling -and running native executables instead of cross compiling (2.) we're using -$stderr redirection to allow us to capture simulator error messages to -$stdout for display at the run's conclusion] - - -Notes: - -* The upper case names are Ruby global constants that Ceedling - builds - -* "COLLECTION_" indicates that Ceedling did some work to assemble - the list. For instance, expanding path globs, combining multiple - path globs into a convenient summation, etc. - -* At present, $stderr redirection is primarily used to capture - errors from test fixtures so that they can be displayed at the - conclusion of a test run. For instance, if a simulator detects - a memory access violation or a divide by zero error, this notice - might go unseen in all the output scrolling past in a terminal. - -* The preprocessing tools can each be overridden with non-gcc - equivalents. However, this is an advanced feature not yet - documented and requires that the replacement toolchain conform - to the same conventions used by gcc. - -**Ceedling Collection Used in Compilation**: - -* `COLLECTION_PATHS_TEST`: - - All test paths - -* `COLLECTION_PATHS_SOURCE`: - - All source paths - -* `COLLECTION_PATHS_INCLUDE`: - - All include paths - -* `COLLECTION_PATHS_SUPPORT`: - - All test support paths - -* `COLLECTION_PATHS_SOURCE_AND_INCLUDE`: - - All source and include paths - -* `COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR`: - - All source and include paths + applicable vendor paths (e.g. - CException's source path if exceptions enabled) - -* `COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE`: - - All test toolchain include paths - -* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE`: - - All test, source, and include paths - -* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR`: - - All test, source, include, and applicable vendor paths (e.g. Unity's - source path plus CMock and CException's source paths if mocks and - exceptions are enabled) - -* `COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE`: - - All release toolchain include paths - -* `COLLECTION_DEFINES_TEST_AND_VENDOR`: - - All symbols specified in [:defines][:test] + symbols defined for - enabled vendor tools - e.g. [:unity][:defines], [:cmock][:defines], - and [:cexception][:defines] - -* `COLLECTION_DEFINES_RELEASE_AND_VENDOR`: - - All symbols specified in [:defines][:release] plus symbols defined by -[:cexception][:defines] if exceptions are ena bled - - -Notes: - -* Other collections exist within Ceedling. However, they are - only useful for advanced features not yet documented. - -* Wherever multiple path lists are combined for use Ceedling prioritizes - path groups as follows: test paths, support paths, source paths, include - paths. - This can be useful, for instance, in certain testing scenarios - where we desire Ceedling or the compiler to find a stand-in header file - before the actual source header file of the same name. - - -**plugins**: Ceedling extensions - -* `load_paths`: - - Base paths to search for plugin subdirectories or extra ruby functionalit - - **Default**: [] (empty) - -* `enabled`: - - List of plugins to be used - a plugin's name is identical to the - subdirectory that contains it (and the name of certain files within - that subdirectory) - - **Default**: [] (empty) - - -Plugins can provide a variety of added functionality to Ceedling. In -general use, it's assumed that at least one reporting plugin will be -used to format test results. However, if no reporting plugins are -specified, Ceedling will print to `$stdout` the (quite readable) raw -test results from all test fixtures executed. - -Example [:plugins] YAML blurb - -```yaml -:plugins: - :load_paths: - - project/tools/ceedling/plugins #home to your collection of plugin directories - - project/support #maybe home to some ruby code your custom plugins share - :enabled: - - stdout_pretty_tests_report #nice test results at your command line - - our_custom_code_metrics_report #maybe you needed line count and complexity metrics, so you - #created a plugin to scan all your code and collect that info -``` - -* `stdout_pretty_tests_report`: - - Prints to $stdout a well-formatted list of ignored and failed tests, - final test counts, and any extraneous output (e.g. printf statements - or simulator memory errors) collected from executing the test - fixtures. Meant to be used with runs at the command line. - -* `stdout_ide_tests_report`: - - Prints to $stdout simple test results formatted such that an IDE - executing test-related Rake tasks can recognize file paths and line - numbers in test failures, etc. Thus, you can click a test result in - your IDE's execution window and jump to the failure (or ignored test) - in your test file (obviously meant to be used with an [IDE like - Eclipse][ide], etc). - - [ide]: http://throwtheswitch.org/white-papers/using-with-ides.html - -* `xml_tests_report`: - - Creates an XML file of test results in the xUnit format (handy for - Continuous Integration build servers or as input to other reporting - tools). Produces a file report.xml in <build root>/artifacts/tests. - -* `bullseye`: - - Adds additional Rake tasks to execute tests with the commercial code - coverage tool provided by [Bullseye][]. See readme.txt inside the bullseye - plugin directory for configuration and use instructions. Note: - Bullseye only works with certain compilers and linkers (healthy list - of supported toolchains though). - - [bullseye]: http://www.bullseye.com - -* `gcov`: - - Adds additional Rake tasks to execute tests with the GNU code coverage - tool [gcov][]. See readme.txt inside the gcov directory for configuration - and use instructions. Only works with GNU compiler and linker. - - [gcov]: http://gcc.gnu.org/onlinedocs/gcc/Gcov.html - -* `warnings_report`: - - Scans compiler and linker `$stdout / $stderr` output for the word - 'warning' (case insensitive). All code warnings (or tool warnings) are - logged to a file warnings.log in the appropriate `<build - root>/artifacts` directory (e.g. test/ for test tasks, `release/` for a - release build, or even `bullseye/` for bullseye runs). - -Module Generator -======================== -Ceedling includes a plugin called module_generator that will create a source, header and test file for you. -There are several possibilities to configure this plugin through your project.yml to suit your project's needs. - -Directory Structure -------------------------------------------- - -The default configuration for directory/project structure is: -```yaml -:module_generator: - :project_root: ./ - :source_root: src/ - :test_root: test/ -``` -You can change these variables in your project.yml file to comply with your project's directory structure. - -If you call `ceedling module:create`, it will create three files: -1. A source file in the source_root -2. A header file in the source_root -3. A test file in the test_root - -If you want your header file to be in another location, -you can specify the ':inc_root:" in your project.yml file: -```yaml -:module_generator: - :inc_root: inc/ -``` -The module_generator will then create the header file in your defined ':inc_root:'. -By default, ':inc_root:' is not defined so the module_generator will use the source_root. - -Sometimes, your project can't be divided into a single src, inc, and test folder. You have several directories -with sources/..., something like this for example: -<project_root> - - myDriver - - src - - inc - - test - - myOtherDriver - - src - - inc - - test - - ... - -Don't worry, you don't have to manually create the source/header/test files. -The module_generator can accept a path to create a source_root/inc_root/test_root folder with your files: -`ceedling module:create[<module_root_path>:<module_name>]` - -F.e., applied to the above project structure: -`ceedling module:create[myOtherDriver:driver]` -This will make the module_generator run in the subdirectory 'myOtherDriver' and generate the module files -for you in that directory. So, this command will generate the following files: -1. A source file 'driver.c' in <project_root>/myOtherDriver/<source_root> -2. A header file 'driver.h' in <project_root>/myOtherDriver/<source_root> (or <inc_root> if specified) -3. A test file 'test_driver.c' in <project_root>/myOtherDriver/<test_root> - -Naming -------------------------------------------- -By default, the module_generator will generate your files in lowercase. -`ceedling module:create[mydriver]` and `ceedling module:create[myDriver]`(note the uppercase) will generate the same files: -1. mydriver.c -2. mydriver.h -3. test_mydriver.c - -You can configure the module_generator to use a differect naming mechanism through the project.yml: -```yaml -:module_generator: - :naming: "camel" -``` -There are other possibilities as well (bumpy, camel, snake, caps). -Refer to the unity module generator for more info (the unity module generator is used under the hood by module_generator). - -Advanced Topics (Coming) -======================== - -Modifying Your Configuration without Modifying Your Project File: Option Files & User Files -------------------------------------------------------------------------------------------- - -Modifying your project file without modifying your project file - -Debugging and/or printf() -------------------------- - -When you gotta get your hands dirty... - -Ceedling Plays Nice with Others - Using Ceedling for Tests Alongside Another Release Build Setup ------------------------------------------------------------------------------------------------- - -You've got options. - -Adding Handy Rake Tasks for Your Project (without Fancy Pants Custom Plugins) ------------------------------------------------------------------------------ - -Simple as snot. - -Working with Non-Desktop Testing Environments ---------------------------------------------- - -For those crazy platforms lacking command line simulators and for which -cross-compiling on the desktop just ain't gonna get it done. - -Creating Custom Plugins ------------------------ - -Oh boy. This is going to take some explaining. diff --git a/test/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md b/test/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md deleted file mode 100644 index bf4c099bb..000000000 --- a/test/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md +++ /dev/null @@ -1,206 +0,0 @@ -# ThrowTheSwitch.org Coding Standard - -Hi. Welcome to the coding standard for ThrowTheSwitch.org. For the most part, -we try to follow these standards to unify our contributors' code into a cohesive -unit (puns intended). You might find places where these standards aren't -followed. We're not perfect. Please be polite where you notice these discrepancies -and we'll try to be polite when we notice yours. - -;) - - -## Why Have A Coding Standard? - -Being consistent makes code easier to understand. We've tried to keep -our standard simple because we also believe that we can only expect someone to -follow something that is understandable. Please do your best. - - -## Our Philosophy - -Before we get into details on syntax, let's take a moment to talk about our -vision for these tools. We're C developers and embedded software developers. -These tools are great to test any C code, but catering to embedded software has -made us more tolerant of compiler quirks. There are a LOT of quirky compilers -out there. By quirky I mean "doesn't follow standards because they feel like -they have a license to do as they wish." - -Our philosophy is "support every compiler we can". Most often, this means that -we aim for writing C code that is standards compliant (often C89... that seems -to be a sweet spot that is almost always compatible). But it also means these -tools are tolerant of things that aren't common. Some that aren't even -compliant. There are configuration options to override the size of standard -types. There are configuration options to force Unity to not use certain -standard library functions. A lot of Unity is configurable and we have worked -hard to make it not TOO ugly in the process. - -Similarly, our tools that parse C do their best. They aren't full C parsers -(yet) and, even if they were, they would still have to accept non-standard -additions like gcc extensions or specifying `@0x1000` to force a variable to -compile to a particular location. It's just what we do, because we like -everything to Just Work™. - -Speaking of having things Just Work™, that's our second philosophy. By that, we -mean that we do our best to have EVERY configuration option have a logical -default. We believe that if you're working with a simple compiler and target, -you shouldn't need to configure very much... we try to make the tools guess as -much as they can, but give the user the power to override it when it's wrong. - - -## Naming Things - -Let's talk about naming things. Programming is all about naming things. We name -files, functions, variables, and so much more. While we're not always going to -find the best name for something, we actually put a bit of effort into -finding *What Something WANTS to be Called*™. - -When naming things, we follow this hierarchy, the first being the -most important to us (but we do all four when possible): -1. Readable -2. Descriptive -3. Consistent -4. Memorable - - -#### Readable - -We want to read our code. This means we like names and flow that are more -naturally read. We try to avoid double negatives. We try to avoid cryptic -abbreviations (sticking to ones we feel are common). - - -#### Descriptive - -We like descriptive names for things, especially functions and variables. -Finding the right name for something is an important endeavor. You might notice -from poking around our code that this often results in names that are a little -longer than the average. Guilty. We're okay with a bit more typing if it -means our code is easier to understand. - -There are two exceptions to this rule that we also stick to as religiously as -possible: - -First, while we realize hungarian notation (and similar systems for encoding -type information into variable names) is providing a more descriptive name, we -feel that (for the average developer) it takes away from readability and is to be avoided. - -Second, loop counters and other local throw-away variables often have a purpose -which is obvious. There's no need, therefore, to get carried away with complex -naming. We find i, j, and k are better loop counters than loopCounterVar or -whatnot. We only break this rule when we see that more description could improve -understanding of an algorithm. - - -#### Consistent - -We like consistency, but we're not really obsessed with it. We try to name our -configuration macros in a consistent fashion... you'll notice a repeated use of -UNITY_EXCLUDE_BLAH or UNITY_USES_BLAH macros. This helps users avoid having to -remember each macro's details. - - -#### Memorable - -Where ever it doesn't violate the above principles, we try to apply memorable -names. Sometimes this means using something that is simply descriptive, but -often we strive for descriptive AND unique... we like quirky names that stand -out in our memory and are easier to search for. Take a look through the file -names in Ceedling and you'll get a good idea of what we are talking about here. -Why use preprocess when you can use preprocessinator? Or what better describes a -module in charge of invoking tasks during releases than release_invoker? Don't -get carried away. The names are still descriptive and fulfill the above -requirements, but they don't feel stale. - - -## C and C++ Details - -We don't really want to add to the style battles out there. Tabs or spaces? -How many spaces? Where do the braces go? These are age-old questions that will -never be answered... or at least not answered in a way that will make everyone -happy. - -We've decided on our own style preferences. If you'd like to contribute to these -projects (and we hope that you do), then we ask if you do your best to follow -the same. It will only hurt a little. We promise. - - -#### Whitespace - -Our C-style is to use spaces and to use 4 of them per indent level. It's a nice -power-of-2 number that looks decent on a wide-screen. We have no more reason -than that. We break that rule when we have lines that wrap (macros or function -arguments or whatnot). When that happens, we like to indent further to line -things up in nice tidy columns. - -```C - if (stuff_happened) - { - do_something(); - } -``` - - -#### Case - -- Files - all lower case with underscores. -- Variables - all lower case with underscores -- Macros - all caps with underscores. -- Typedefs - all caps with underscores. (also ends with _T). -- Functions - camel cased. Usually named ModuleName_FuncName -- Constants and Globals - camel cased. - - -#### Braces - -The left brace is on the next line after the declaration. The right brace is -directly below that. Everything in between in indented one level. If you're -catching an error and you have a one-line, go ahead and to it on the same line. - -```C - while (blah) - { - //Like so. Even if only one line, we use braces. - } -``` - - -#### Comments - -Do you know what we hate? Old-school C block comments. BUT, we're using them -anyway. As we mentioned, our goal is to support every compiler we can, -especially embedded compilers. There are STILL C compilers out there that only -support old-school block comments. So that is what we're using. We apologize. We -think they are ugly too. - - -## Ruby Details - -Is there really such thing as a Ruby coding standard? Ruby is such a free form -language, it seems almost sacrilegious to suggest that people should comply to -one method! We'll keep it really brief! - - -#### Whitespace - -Our Ruby style is to use spaces and to use 2 of them per indent level. It's a -nice power-of-2 number that really grooves with Ruby's compact style. We have no -more reason than that. We break that rule when we have lines that wrap. When -that happens, we like to indent further to line things up in nice tidy columns. - - -#### Case - -- Files - all lower case with underscores. -- Variables - all lower case with underscores -- Classes, Modules, etc - Camel cased. -- Functions - all lower case with underscores -- Constants - all upper case with underscores - - -## Documentation - -Egad. Really? We use mark down and we like pdf files because they can be made to -look nice while still being portable. Good enough? - - -*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/test/vendor/ceedling/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf b/test/vendor/ceedling/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf deleted file mode 100644 index 28f0c3214..000000000 Binary files a/test/vendor/ceedling/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf and /dev/null differ diff --git a/test/vendor/ceedling/docs/UnityAssertionsReference.md b/test/vendor/ceedling/docs/UnityAssertionsReference.md deleted file mode 100644 index eb855f3c0..000000000 --- a/test/vendor/ceedling/docs/UnityAssertionsReference.md +++ /dev/null @@ -1,779 +0,0 @@ -# Unity Assertions Reference - -## Background and Overview - -### Super Condensed Version - -- An assertion establishes truth (i.e. boolean True) for a single condition. -Upon boolean False, an assertion stops execution and reports the failure. -- Unity is mainly a rich collection of assertions and the support to gather up -and easily execute those assertions. -- The structure of Unity allows you to easily separate test assertions from -source code in, well, test code. -- Unity's assertions: -- Come in many, many flavors to handle different C types and assertion cases. -- Use context to provide detailed and helpful failure messages. -- Document types, expected values, and basic behavior in your source code for -free. - - -### Unity Is Several Things But Mainly It's Assertions - -One way to think of Unity is simply as a rich collection of assertions you can -use to establish whether your source code behaves the way you think it does. -Unity provides a framework to easily organize and execute those assertions in -test code separate from your source code. - - -### What's an Assertion? - -At their core, assertions are an establishment of truth - boolean truth. Was this -thing equal to that thing? Does that code doohickey have such-and-such property -or not? You get the idea. Assertions are executable code (to appreciate the big -picture on this read up on the difference between -[link:Dynamic Verification and Static Analysis]). A failing assertion stops -execution and reports an error through some appropriate I/O channel (e.g. -stdout, GUI, file, blinky light). - -Fundamentally, for dynamic verification all you need is a single assertion -mechanism. In fact, that's what the [assert() macro in C's standard library](http://en.wikipedia.org/en/wiki/Assert.h) -is for. So why not just use it? Well, we can do far better in the reporting -department. C's `assert()` is pretty dumb as-is and is particularly poor for -handling common data types like arrays, structs, etc. And, without some other -support, it's far too tempting to litter source code with C's `assert()`'s. It's -generally much cleaner, manageable, and more useful to separate test and source -code in the way Unity facilitates. - - -### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation - -Asserting a simple truth condition is valuable, but using the context of the -assertion is even more valuable. For instance, if you know you're comparing bit -flags and not just integers, then why not use that context to give explicit, -readable, bit-level feedback when an assertion fails? - -That's what Unity's collection of assertions do - capture context to give you -helpful, meaningful assertion failure messages. In fact, the assertions -themselves also serve as executable documentation about types and values in your -source code. So long as your tests remain current with your source and all those -tests pass, you have a detailed, up-to-date view of the intent and mechanisms in -your source code. And due to a wondrous mystery, well-tested code usually tends -to be well designed code. - - -## Assertion Conventions and Configurations - -### Naming and Parameter Conventions - -The convention of assertion parameters generally follows this order: - - TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) - -The very simplest assertion possible uses only a single "actual" parameter (e.g. -a simple null check). - -"Actual" is the value being tested and unlike the other parameters in an -assertion construction is the only parameter present in all assertion variants. -"Modifiers" are masks, ranges, bit flag specifiers, floating point deltas. -"Expected" is your expected value (duh) to compare to an "actual" value; it's -marked as an optional parameter because some assertions only need a single -"actual" parameter (e.g. null check). -"Size/count" refers to string lengths, number of array elements, etc. - -Many of Unity's assertions are clear duplications in that the same data type -is handled by several assertions. The differences among these are in how failure -messages are presented. For instance, a `_HEX` variant of an assertion prints -the expected and actual values of that assertion formatted as hexadecimal. - - -#### TEST_ASSERT_X_MESSAGE Variants - -_All_ assertions are complemented with a variant that includes a simple string -message as a final parameter. The string you specify is appended to an assertion -failure message in Unity output. - -For brevity, the assertion variants with a message parameter are not listed -below. Just tack on `_MESSAGE` as the final component to any assertion name in -the reference list below and add a string as the final parameter. - -_Example:_ - - TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) - -becomes messageified like thus... - - TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message ) - -Notes: -- The `_MESSAGE` variants intentionally do not support `printf` style formatting - since many embedded projects don't support or avoid `printf` for various reasons. - It is possible to use `sprintf` before the assertion to assemble a complex fail - message, if necessary. -- If you want to output a counter value within an assertion fail message (e.g. from - a loop) , building up an array of results and then using one of the `_ARRAY` - assertions (see below) might be a handy alternative to `sprintf`. - - -#### TEST_ASSERT_X_ARRAY Variants - -Unity provides a collection of assertions for arrays containing a variety of -types. These are documented in the Array section below. These are almost on par -with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity -type assertion you can tack on `_ARRAY` and run assertions on an entire block of -memory. - - TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} ) - -"Expected" is an array itself. -"Size/count" is one or two parameters necessary to establish the number of array -elements and perhaps the length of elements within the array. - -Notes: -- The `_MESSAGE` variant convention still applies here to array assertions. The -`_MESSAGE` variants of the `_ARRAY` assertions have names ending with -`_ARRAY_MESSAGE`. -- Assertions for handling arrays of floating point values are grouped with float -and double assertions (see immediately following section). - - -### TEST_ASSERT_EACH_EQUAL_X Variants - -Unity provides a collection of assertions for arrays containing a variety of -types which can be compared to a single value as well. These are documented in -the Each Equal section below. these are almost on par with the `_MESSAGE` -variants of Unity's Asserts in that for pretty much any Unity type assertion you -can inject _EACH_EQUAL and run assertions on an entire block of memory. - - TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} ) - -"Expected" is a single value to compare to. -"Actual" is an array where each element will be compared to the expected value. -"Size/count" is one of two parameters necessary to establish the number of array -elements and perhaps the length of elements within the array. - -Notes: -- The `_MESSAGE` variant convention still applies here to Each Equal assertions. -- Assertions for handling Each Equal of floating point values are grouped with -float and double assertions (see immediately following section). - - -### Configuration - -#### Floating Point Support Is Optional - -Support for floating point types is configurable. That is, by defining the -appropriate preprocessor symbols, floats and doubles can be individually enabled -or disabled in Unity code. This is useful for embedded targets with no floating -point math support (i.e. Unity compiles free of errors for fixed point only -platforms). See Unity documentation for specifics. - - -#### Maximum Data Type Width Is Configurable - -Not all targets support 64 bit wide types or even 32 bit wide types. Define the -appropriate preprocessor symbols and Unity will omit all operations from -compilation that exceed the maximum width of your target. See Unity -documentation for specifics. - - -## The Assertions in All Their Blessed Glory - -### Basic Fail and Ignore - -##### `TEST_FAIL()` - -This fella is most often used in special conditions where your test code is -performing logic beyond a simple assertion. That is, in practice, `TEST_FAIL()` -will always be found inside a conditional code block. - -_Examples:_ -- Executing a state machine multiple times that increments a counter your test -code then verifies as a final step. -- Triggering an exception and verifying it (as in Try / Catch / Throw - see the -[CException](https://github.com/ThrowTheSwitch/CException) project). - -##### `TEST_IGNORE()` - -Marks a test case (i.e. function meant to contain test assertions) as ignored. -Usually this is employed as a breadcrumb to come back and implement a test case. -An ignored test case has effects if other assertions are in the enclosing test -case (see Unity documentation for more). - -### Boolean - -##### `TEST_ASSERT (condition)` - -##### `TEST_ASSERT_TRUE (condition)` - -##### `TEST_ASSERT_FALSE (condition)` - -##### `TEST_ASSERT_UNLESS (condition)` - -A simple wording variation on `TEST_ASSERT_FALSE`.The semantics of -`TEST_ASSERT_UNLESS` aid readability in certain test constructions or -conditional statements. - -##### `TEST_ASSERT_NULL (pointer)` - -##### `TEST_ASSERT_NOT_NULL (pointer)` - - -### Signed and Unsigned Integers (of all sizes) - -Large integer sizes can be disabled for build targets that do not support them. -For example, if your target only supports up to 16 bit types, by defining the -appropriate symbols Unity can be configured to omit 32 and 64 bit operations -that would break compilation (see Unity documentation for more). Refer to -Advanced Asserting later in this document for advice on dealing with other word -sizes. - -##### `TEST_ASSERT_EQUAL_INT (expected, actual)` - -##### `TEST_ASSERT_EQUAL_INT8 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_INT16 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_INT32 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_INT64 (expected, actual)` - -##### `TEST_ASSERT_EQUAL (expected, actual)` - -##### `TEST_ASSERT_NOT_EQUAL (expected, actual)` - -##### `TEST_ASSERT_EQUAL_UINT (expected, actual)` - -##### `TEST_ASSERT_EQUAL_UINT8 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_UINT16 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_UINT32 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_UINT64 (expected, actual)` - - -### Unsigned Integers (of all sizes) in Hexadecimal - -All `_HEX` assertions are identical in function to unsigned integer assertions -but produce failure messages with the `expected` and `actual` values formatted -in hexadecimal. Unity output is big endian. - -##### `TEST_ASSERT_EQUAL_HEX (expected, actual)` - -##### `TEST_ASSERT_EQUAL_HEX8 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_HEX16 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_HEX32 (expected, actual)` - -##### `TEST_ASSERT_EQUAL_HEX64 (expected, actual)` - - -### Masked and Bit-level Assertions - -Masked and bit-level assertions produce output formatted in hexadecimal. Unity -output is big endian. - - -##### `TEST_ASSERT_BITS (mask, expected, actual)` - -Only compares the masked (i.e. high) bits of `expected` and `actual` parameters. - - -##### `TEST_ASSERT_BITS_HIGH (mask, actual)` - -Asserts the masked bits of the `actual` parameter are high. - - -##### `TEST_ASSERT_BITS_LOW (mask, actual)` - -Asserts the masked bits of the `actual` parameter are low. - - -##### `TEST_ASSERT_BIT_HIGH (bit, actual)` - -Asserts the specified bit of the `actual` parameter is high. - - -##### `TEST_ASSERT_BIT_LOW (bit, actual)` - -Asserts the specified bit of the `actual` parameter is low. - -### Integer Less Than / Greater Than - -These assertions verify that the `actual` parameter is less than or greater -than `threshold` (exclusive). For example, if the threshold value is 0 for the -greater than assertion will fail if it is 0 or less. - -##### `TEST_ASSERT_GREATER_THAN (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_INT (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_INT16 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_INT32 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_UINT (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_UINT8 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_UINT16 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_UINT32 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_HEX8 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_HEX16 (threshold, actual)` - -##### `TEST_ASSERT_GREATER_THAN_HEX32 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_INT (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_INT8 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_INT16 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_UINT (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_UINT8 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_UINT16 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_UINT32 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_HEX8 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_HEX16 (threshold, actual)` - -##### `TEST_ASSERT_LESS_THAN_HEX32 (threshold, actual)` - - -### Integer Ranges (of all sizes) - -These assertions verify that the `expected` parameter is within +/- `delta` -(inclusive) of the `actual` parameter. For example, if the expected value is 10 -and the delta is 3 then the assertion will fail for any value outside the range -of 7 - 13. - -##### `TEST_ASSERT_INT_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_INT8_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_INT16_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_INT32_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_INT64_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_UINT_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_UINT8_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_UINT16_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_UINT32_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_UINT64_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_HEX_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_HEX8_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_HEX16_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_HEX32_WITHIN (delta, expected, actual)` - -##### `TEST_ASSERT_HEX64_WITHIN (delta, expected, actual)` - - -### Structs and Strings - -##### `TEST_ASSERT_EQUAL_PTR (expected, actual)` - -Asserts that the pointers point to the same memory location. - - -##### `TEST_ASSERT_EQUAL_STRING (expected, actual)` - -Asserts that the null terminated (`'\0'`)strings are identical. If strings are -of different lengths or any portion of the strings before their terminators -differ, the assertion fails. Two NULL strings (i.e. zero length) are considered -equivalent. - - -##### `TEST_ASSERT_EQUAL_MEMORY (expected, actual, len)` - -Asserts that the contents of the memory specified by the `expected` and `actual` -pointers is identical. The size of the memory blocks in bytes is specified by -the `len` parameter. - - -### Arrays - -`expected` and `actual` parameters are both arrays. `num_elements` specifies the -number of elements in the arrays to compare. - -`_HEX` assertions produce failure messages with expected and actual array -contents formatted in hexadecimal. - -For array of strings comparison behavior, see comments for -`TEST_ASSERT_EQUAL_STRING` in the preceding section. - -Assertions fail upon the first element in the compared arrays found not to -match. Failure messages specify the array index of the failed comparison. - -##### `TEST_ASSERT_EQUAL_INT_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_INT8_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_INT16_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_INT32_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_INT64_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_UINT_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_UINT8_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_UINT16_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_UINT32_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_UINT64_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_HEX_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_HEX8_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_HEX16_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_HEX32_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_HEX64_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_PTR_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_STRING_ARRAY (expected, actual, num_elements)` - -##### `TEST_ASSERT_EQUAL_MEMORY_ARRAY (expected, actual, len, num_elements)` - -`len` is the memory in bytes to be compared at each array element. - - -### Each Equal (Arrays to Single Value) - -`expected` are single values and `actual` are arrays. `num_elements` specifies -the number of elements in the arrays to compare. - -`_HEX` assertions produce failure messages with expected and actual array -contents formatted in hexadecimal. - -Assertions fail upon the first element in the compared arrays found not to -match. Failure messages specify the array index of the failed comparison. - -#### `TEST_ASSERT_EACH_EQUAL_INT (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_INT8 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_INT16 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_INT32 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_INT64 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_UINT (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_UINT8 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_UINT16 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_UINT32 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_UINT64 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_HEX (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_HEX8 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_HEX16 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_HEX32 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_HEX64 (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_PTR (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_STRING (expected, actual, num_elements)` - -#### `TEST_ASSERT_EACH_EQUAL_MEMORY (expected, actual, len, num_elements)` - -`len` is the memory in bytes to be compared at each array element. - - -### Floating Point (If enabled) - -##### `TEST_ASSERT_FLOAT_WITHIN (delta, expected, actual)` - -Asserts that the `actual` value is within +/- `delta` of the `expected` value. -The nature of floating point representation is such that exact evaluations of -equality are not guaranteed. - - -##### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)` - -Asserts that the ?actual?value is "close enough to be considered equal" to the -`expected` value. If you are curious about the details, refer to the Advanced -Asserting section for more details on this. Omitting a user-specified delta in a -floating point assertion is both a shorthand convenience and a requirement of -code generation conventions for CMock. - - -##### `TEST_ASSERT_EQUAL_FLOAT_ARRAY (expected, actual, num_elements)` - -See Array assertion section for details. Note that individual array element -float comparisons are executed using T?EST_ASSERT_EQUAL_FLOAT?.That is, user -specified delta comparison values requires a custom-implemented floating point -array assertion. - - -##### `TEST_ASSERT_FLOAT_IS_INF (actual)` - -Asserts that `actual` parameter is equivalent to positive infinity floating -point representation. - - -##### `TEST_ASSERT_FLOAT_IS_NEG_INF (actual)` - -Asserts that `actual` parameter is equivalent to negative infinity floating -point representation. - - -##### `TEST_ASSERT_FLOAT_IS_NAN (actual)` - -Asserts that `actual` parameter is a Not A Number floating point representation. - - -##### `TEST_ASSERT_FLOAT_IS_DETERMINATE (actual)` - -Asserts that ?actual?parameter is a floating point representation usable for -mathematical operations. That is, the `actual` parameter is neither positive -infinity nor negative infinity nor Not A Number floating point representations. - - -##### `TEST_ASSERT_FLOAT_IS_NOT_INF (actual)` - -Asserts that `actual` parameter is a value other than positive infinity floating -point representation. - - -##### `TEST_ASSERT_FLOAT_IS_NOT_NEG_INF (actual)` - -Asserts that `actual` parameter is a value other than negative infinity floating -point representation. - - -##### `TEST_ASSERT_FLOAT_IS_NOT_NAN (actual)` - -Asserts that `actual` parameter is a value other than Not A Number floating -point representation. - - -##### `TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE (actual)` - -Asserts that `actual` parameter is not usable for mathematical operations. That -is, the `actual` parameter is either positive infinity or negative infinity or -Not A Number floating point representations. - - -### Double (If enabled) - -##### `TEST_ASSERT_DOUBLE_WITHIN (delta, expected, actual)` - -Asserts that the `actual` value is within +/- `delta` of the `expected` value. -The nature of floating point representation is such that exact evaluations of -equality are not guaranteed. - - -##### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)` - -Asserts that the `actual` value is "close enough to be considered equal" to the -`expected` value. If you are curious about the details, refer to the Advanced -Asserting section for more details. Omitting a user-specified delta in a -floating point assertion is both a shorthand convenience and a requirement of -code generation conventions for CMock. - - -##### `TEST_ASSERT_EQUAL_DOUBLE_ARRAY (expected, actual, num_elements)` - -See Array assertion section for details. Note that individual array element -double comparisons are executed using `TEST_ASSERT_EQUAL_DOUBLE`.That is, user -specified delta comparison values requires a custom implemented double array -assertion. - - -##### `TEST_ASSERT_DOUBLE_IS_INF (actual)` - -Asserts that `actual` parameter is equivalent to positive infinity floating -point representation. - - -##### `TEST_ASSERT_DOUBLE_IS_NEG_INF (actual)` - -Asserts that `actual` parameter is equivalent to negative infinity floating point -representation. - - -##### `TEST_ASSERT_DOUBLE_IS_NAN (actual)` - -Asserts that `actual` parameter is a Not A Number floating point representation. - - -##### `TEST_ASSERT_DOUBLE_IS_DETERMINATE (actual)` - -Asserts that `actual` parameter is a floating point representation usable for -mathematical operations. That is, the ?actual?parameter is neither positive -infinity nor negative infinity nor Not A Number floating point representations. - - -##### `TEST_ASSERT_DOUBLE_IS_NOT_INF (actual)` - -Asserts that `actual` parameter is a value other than positive infinity floating -point representation. - - -##### `TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF (actual)` - -Asserts that `actual` parameter is a value other than negative infinity floating -point representation. - - -##### `TEST_ASSERT_DOUBLE_IS_NOT_NAN (actual)` - -Asserts that `actual` parameter is a value other than Not A Number floating -point representation. - - -##### `TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE (actual)` - -Asserts that `actual` parameter is not usable for mathematical operations. That -is, the `actual` parameter is either positive infinity or negative infinity or -Not A Number floating point representations. - - -## Advanced Asserting: Details On Tricky Assertions - -This section helps you understand how to deal with some of the trickier -assertion situations you may run into. It will give you a glimpse into some of -the under-the-hood details of Unity's assertion mechanisms. If you're one of -those people who likes to know what is going on in the background, read on. If -not, feel free to ignore the rest of this document until you need it. - - -### How do the EQUAL assertions work for FLOAT and DOUBLE? - -As you may know, directly checking for equality between a pair of floats or a -pair of doubles is sloppy at best and an outright no-no at worst. Floating point -values can often be represented in multiple ways, particularly after a series of -operations on a value. Initializing a variable to the value of 2.0 is likely to -result in a floating point representation of 2 x 20,but a series of -mathematical operations might result in a representation of 8 x 2-2 -that also evaluates to a value of 2. At some point repeated operations cause -equality checks to fail. - -So Unity doesn't do direct floating point comparisons for equality. Instead, it -checks if two floating point values are "really close." If you leave Unity -running with defaults, "really close" means "within a significant bit or two." -Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN` -with the `delta` parameter calculated on the fly. For single precision, delta is -the expected value multiplied by 0.00001, producing a very small proportional -range around the expected value. - -If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So -any value between 19,999.8 and 20,000.2 will satisfy the equality check. This -works out to be roughly a single bit of range for a single-precision number, and -that's just about as tight a tolerance as you can reasonably get from a floating -point value. - -So what happens when it's zero? Zero - even more than other floating point -values - can be represented many different ways. It doesn't matter if you have -0 x 20 or 0 x 263.It's still zero, right? Luckily, if you -subtract these values from each other, they will always produce a difference of -zero, which will still fall between 0 plus or minus a delta of 0. So it still -works! - -Double precision floating point numbers use a much smaller multiplier, again -approximating a single bit of error. - -If you don't like these ranges and you want to make your floating point equality -assertions less strict, you can change these multipliers to whatever you like by -defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity -documentation for more. - - -### How do we deal with targets with non-standard int sizes? - -It's "fun" that C is a standard where something as fundamental as an integer -varies by target. According to the C standard, an `int` is to be the target's -natural register size, and it should be at least 16-bits and a multiple of a -byte. It also guarantees an order of sizes: - -```C -char <= short <= int <= long <= long long -``` - -Most often, `int` is 32-bits. In many cases in the embedded world, `int` is -16-bits. There are rare microcontrollers out there that have 24-bit integers, -and this remains perfectly standard C. - -To make things even more interesting, there are compilers and targets out there -that have a hard choice to make. What if their natural register size is 10-bits -or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least -16-bits AND the requirement to match the natural register size. In these -situations, they often choose the natural register size, leaving us with -something like this: - -```C -char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit) -``` - -Um... yikes. It's obviously breaking a rule or two... but they had to break SOME -rules, so they made a choice. - -When the C99 standard rolled around, it introduced alternate standard-size types. -It also introduced macros for pulling in MIN/MAX values for your integer types. -It's glorious! Unfortunately, many embedded compilers can't be relied upon to -use the C99 types (Sometimes because they have weird register sizes as described -above. Sometimes because they don't feel like it?). - -A goal of Unity from the beginning was to support every combination of -microcontroller or microprocessor and C compiler. Over time, we've gotten really -close to this. There are a few tricks that you should be aware of, though, if -you're going to do this effectively on some of these more idiosyncratic targets. - -First, when setting up Unity for a new target, you're going to want to pay -special attention to the macros for automatically detecting types -(where available) or manually configuring them yourself. You can get information -on both of these in Unity's documentation. - -What about the times where you suddenly need to deal with something odd, like a -24-bit `int`? The simplest solution is to use the next size up. If you have a -24-bit `int`, configure Unity to use 32-bit integers. If you have a 12-bit -`int`, configure Unity to use 16 bits. There are two ways this is going to -affect you: - -1. When Unity displays errors for you, it's going to pad the upper unused bits -with zeros. -2. You're going to have to be careful of assertions that perform signed -operations, particularly `TEST_ASSERT_INT_WITHIN`.Such assertions might wrap -your `int` in the wrong place, and you could experience false failures. You can -always back down to a simple `TEST_ASSERT` and do the operations yourself. - - -*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/test/vendor/ceedling/docs/UnityConfigurationGuide.md b/test/vendor/ceedling/docs/UnityConfigurationGuide.md deleted file mode 100644 index dace20c54..000000000 --- a/test/vendor/ceedling/docs/UnityConfigurationGuide.md +++ /dev/null @@ -1,433 +0,0 @@ -# Unity Configuration Guide - -## C Standards, Compilers and Microcontrollers - -The embedded software world contains its challenges. Compilers support different -revisions of the C Standard. They ignore requirements in places, sometimes to -make the language more usable in some special regard. Sometimes it's to simplify -their support. Sometimes it's due to specific quirks of the microcontroller they -are targeting. Simulators add another dimension to this menagerie. - -Unity is designed to run on almost anything that is targeted by a C compiler. It -would be awesome if this could be done with zero configuration. While there are -some targets that come close to this dream, it is sadly not universal. It is -likely that you are going to need at least a couple of the configuration options -described in this document. - -All of Unity's configuration options are `#defines`. Most of these are simple -definitions. A couple are macros with arguments. They live inside the -unity_internals.h header file. We don't necessarily recommend opening that file -unless you really need to. That file is proof that a cross-platform library is -challenging to build. From a more positive perspective, it is also proof that a -great deal of complexity can be centralized primarily to one place to -provide a more consistent and simple experience elsewhere. - - -### Using These Options - -It doesn't matter if you're using a target-specific compiler and a simulator or -a native compiler. In either case, you've got a couple choices for configuring -these options: - -1. Because these options are specified via C defines, you can pass most of these -options to your compiler through command line compiler flags. Even if you're -using an embedded target that forces you to use their overbearing IDE for all -configuration, there will be a place somewhere in your project to configure -defines for your compiler. -2. You can create a custom `unity_config.h` configuration file (present in your -toolchain's search paths). In this file, you will list definitions and macros -specific to your target. All you must do is define `UNITY_INCLUDE_CONFIG_H` and -Unity will rely on `unity_config.h` for any further definitions it may need. - - -## The Options - -### Integer Types - -If you've been a C developer for long, you probably already know that C's -concept of an integer varies from target to target. The C Standard has rules -about the `int` matching the register size of the target microprocessor. It has -rules about the `int` and how its size relates to other integer types. An `int` -on one target might be 16 bits while on another target it might be 64. There are -more specific types in compilers compliant with C99 or later, but that's -certainly not every compiler you are likely to encounter. Therefore, Unity has a -number of features for helping to adjust itself to match your required integer -sizes. It starts off by trying to do it automatically. - - -##### `UNITY_EXCLUDE_STDINT_H` - -The first thing that Unity does to guess your types is check `stdint.h`. -This file includes defines like `UINT_MAX` that Unity can use to -learn a lot about your system. It's possible you don't want it to do this -(um. why not?) or (more likely) it's possible that your system doesn't -support `stdint.h`. If that's the case, you're going to want to define this. -That way, Unity will know to skip the inclusion of this file and you won't -be left with a compiler error. - -_Example:_ - #define UNITY_EXCLUDE_STDINT_H - - -##### `UNITY_EXCLUDE_LIMITS_H` - -The second attempt to guess your types is to check `limits.h`. Some compilers -that don't support `stdint.h` could include `limits.h` instead. If you don't -want Unity to check this file either, define this to make it skip the inclusion. - -_Example:_ - #define UNITY_EXCLUDE_LIMITS_H - - -If you've disabled both of the automatic options above, you're going to have to -do the configuration yourself. Don't worry. Even this isn't too bad... there are -just a handful of defines that you are going to specify if you don't like the -defaults. - - -##### `UNITY_INT_WIDTH` - -Define this to be the number of bits an `int` takes up on your system. The -default, if not autodetected, is 32 bits. - -_Example:_ - #define UNITY_INT_WIDTH 16 - - -##### `UNITY_LONG_WIDTH` - -Define this to be the number of bits a `long` takes up on your system. The -default, if not autodetected, is 32 bits. This is used to figure out what kind -of 64-bit support your system can handle. Does it need to specify a `long` or a -`long long` to get a 64-bit value. On 16-bit systems, this option is going to be -ignored. - -_Example:_ - #define UNITY_LONG_WIDTH 16 - - -##### `UNITY_POINTER_WIDTH` - -Define this to be the number of bits a pointer takes up on your system. The -default, if not autodetected, is 32-bits. If you're getting ugly compiler -warnings about casting from pointers, this is the one to look at. - -_Example:_ - #define UNITY_POINTER_WIDTH 64 - - -##### `UNITY_SUPPORT_64` - -Unity will automatically include 64-bit support if it auto-detects it, or if -your `int`, `long`, or pointer widths are greater than 32-bits. Define this to -enable 64-bit support if none of the other options already did it for you. There -can be a significant size and speed impact to enabling 64-bit support on small -targets, so don't define it if you don't need it. - -_Example:_ - #define UNITY_SUPPORT_64 - - -### Floating Point Types - -In the embedded world, it's not uncommon for targets to have no support for -floating point operations at all or to have support that is limited to only -single precision. We are able to guess integer sizes on the fly because integers -are always available in at least one size. Floating point, on the other hand, is -sometimes not available at all. Trying to include `float.h` on these platforms -would result in an error. This leaves manual configuration as the only option. - - -##### `UNITY_INCLUDE_FLOAT` - -##### `UNITY_EXCLUDE_FLOAT` - -##### `UNITY_INCLUDE_DOUBLE` - -##### `UNITY_EXCLUDE_DOUBLE` - -By default, Unity guesses that you will want single precision floating point -support, but not double precision. It's easy to change either of these using the -include and exclude options here. You may include neither, either, or both, as -suits your needs. For features that are enabled, the following floating point -options also become available. - -_Example:_ - - //what manner of strange processor is this? - #define UNITY_EXCLUDE_FLOAT - #define UNITY_INCLUDE_DOUBLE - - -##### `UNITY_EXCLUDE_FLOAT_PRINT` - -Unity aims for as small of a footprint as possible and avoids most standard -library calls (some embedded platforms don’t have a standard library!). Because -of this, its routines for printing integer values are minimalist and hand-coded. -Therefore, the display of floating point values during a failure are optional. -By default, Unity will print the actual results of floating point assertion -failure (e.g. ”Expected 4.56 Was 4.68”). To not include this extra support, you -can use this define to instead respond to a failed assertion with a message like -”Values Not Within Delta”. If you would like verbose failure messages for floating -point assertions, use these options to give more explicit failure messages. - -_Example:_ - #define UNITY_EXCLUDE_FLOAT_PRINT - - -##### `UNITY_FLOAT_TYPE` - -If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C -floats. If your compiler supports a specialty floating point type, you can -always override this behavior by using this definition. - -_Example:_ - #define UNITY_FLOAT_TYPE float16_t - - -##### `UNITY_DOUBLE_TYPE` - -If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard C -doubles. If you would like to change this, you can specify something else by -using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long double` -could enable gargantuan floating point types on your 64-bit processor instead of -the standard `double`. - -_Example:_ - #define UNITY_DOUBLE_TYPE long double - - -##### `UNITY_FLOAT_PRECISION` - -##### `UNITY_DOUBLE_PRECISION` - -If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as -documented in the big daddy Unity Assertion Guide, you will learn that they are -not really asserting that two values are equal but rather that two values are -"close enough" to equal. "Close enough" is controlled by these precision -configuration options. If you are working with 32-bit floats and/or 64-bit -doubles (the normal on most processors), you should have no need to change these -options. They are both set to give you approximately 1 significant bit in either -direction. The float precision is 0.00001 while the double is 10-12. -For further details on how this works, see the appendix of the Unity Assertion -Guide. - -_Example:_ - #define UNITY_FLOAT_PRECISION 0.001f - - -### Toolset Customization - -In addition to the options listed above, there are a number of other options -which will come in handy to customize Unity's behavior for your specific -toolchain. It is possible that you may not need to touch any of these... but -certain platforms, particularly those running in simulators, may need to jump -through extra hoops to run properly. These macros will help in those -situations. - - -##### `UNITY_OUTPUT_CHAR(a)` - -##### `UNITY_OUTPUT_FLUSH()` - -##### `UNITY_OUTPUT_START()` - -##### `UNITY_OUTPUT_COMPLETE()` - -By default, Unity prints its results to `stdout` as it runs. This works -perfectly fine in most situations where you are using a native compiler for -testing. It works on some simulators as well so long as they have `stdout` -routed back to the command line. There are times, however, where the simulator -will lack support for dumping results or you will want to route results -elsewhere for other reasons. In these cases, you should define the -`UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time (as -an `int`, since this is the parameter type of the standard C `putchar` function -most commonly used). You may replace this with whatever function call you like. - -_Example:_ -Say you are forced to run your test suite on an embedded processor with no -`stdout` option. You decide to route your test result output to a custom serial -`RS232_putc()` function you wrote like thus: - #include "RS232_header.h" - ... - #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) - #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) - #define UNITY_OUTPUT_FLUSH() RS232_flush() - #define UNITY_OUTPUT_COMPLETE() RS232_close() - -_Note:_ -`UNITY_OUTPUT_FLUSH()` can be set to the standard out flush function simply by -specifying `UNITY_USE_FLUSH_STDOUT`. No other defines are required. - - -##### `UNITY_WEAK_ATTRIBUTE` - -##### `UNITY_WEAK_PRAGMA` - -##### `UNITY_NO_WEAK` - -For some targets, Unity can make the otherwise required setUp() and tearDown() -functions optional. This is a nice convenience for test writers since setUp and -tearDown don’t often actually do anything. If you’re using gcc or clang, this -option is automatically defined for you. Other compilers can also support this -behavior, if they support a C feature called weak functions. A weak function is -a function that is compiled into your executable unless a non-weak version of -the same function is defined elsewhere. If a non-weak version is found, the weak -version is ignored as if it never existed. If your compiler supports this feature, -you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as -the function attributes that would need to be applied to identify a function as -weak. If your compiler lacks support for weak functions, you will always need to -define setUp and tearDown functions (though they can be and often will be just -empty). You can also force Unity to NOT use weak functions by defining -UNITY_NO_WEAK. The most common options for this feature are: - -_Example:_ - #define UNITY_WEAK_ATTRIBUTE weak - #define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) - #define UNITY_WEAK_PRAGMA - #define UNITY_NO_WEAK - - -##### `UNITY_PTR_ATTRIBUTE` - -Some compilers require a custom attribute to be assigned to pointers, like -`near` or `far`. In these cases, you can give Unity a safe default for these by -defining this option with the attribute you would like. - -_Example:_ - #define UNITY_PTR_ATTRIBUTE __attribute__((far)) - #define UNITY_PTR_ATTRIBUTE near - - -##### `UNITY_PRINT_EOL` - -By default, Unity outputs \n at the end of each line of output. This is easy -to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR -system. Feel free to override this and to make it whatever you wish. - -_Example:_ - #define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') } - - - -##### `UNITY_EXCLUDE_DETAILS` - -This is an option for if you absolutely must squeeze every byte of memory out of -your system. Unity stores a set of internal scratchpads which are used to pass -extra detail information around. It's used by systems like CMock in order to -report which function or argument flagged an error. If you're not using CMock and -you're not using these details for other things, then you can exclude them. - -_Example:_ - #define UNITY_EXCLUDE_DETAILS - - - -##### `UNITY_EXCLUDE_SETJMP` - -If your embedded system doesn't support the standard library setjmp, you can -exclude Unity's reliance on this by using this define. This dropped dependence -comes at a price, though. You will be unable to use custom helper functions for -your tests, and you will be unable to use tools like CMock. Very likely, if your -compiler doesn't support setjmp, you wouldn't have had the memory space for those -things anyway, though... so this option exists for those situations. - -_Example:_ - #define UNITY_EXCLUDE_SETJMP - -##### `UNITY_OUTPUT_COLOR` - -If you want to add color using ANSI escape codes you can use this define. -t -_Example:_ - #define UNITY_OUTPUT_COLOR - - - -## Getting Into The Guts - -There will be cases where the options above aren't quite going to get everything -perfect. They are likely sufficient for any situation where you are compiling -and executing your tests with a native toolchain (e.g. clang on Mac). These -options may even get you through the majority of cases encountered in working -with a target simulator run from your local command line. But especially if you -must run your test suite on your target hardware, your Unity configuration will -require special help. This special help will usually reside in one of two -places: the `main()` function or the `RUN_TEST` macro. Let's look at how these -work. - - -##### `main()` - -Each test module is compiled and run on its own, separate from the other test -files in your project. Each test file, therefore, has a `main` function. This -`main` function will need to contain whatever code is necessary to initialize -your system to a workable state. This is particularly true for situations where -you must set up a memory map or initialize a communication channel for the -output of your test results. - -A simple main function looks something like this: - - int main(void) { - UNITY_BEGIN(); - RUN_TEST(test_TheFirst); - RUN_TEST(test_TheSecond); - RUN_TEST(test_TheThird); - return UNITY_END(); - } - -You can see that our main function doesn't bother taking any arguments. For our -most barebones case, we'll never have arguments because we just run all the -tests each time. Instead, we start by calling `UNITY_BEGIN`. We run each test -(in whatever order we wish). Finally, we call `UNITY_END`, returning its return -value (which is the total number of failures). - -It should be easy to see that you can add code before any test cases are run or -after all the test cases have completed. This allows you to do any needed -system-wide setup or teardown that might be required for your special -circumstances. - - -##### `RUN_TEST` - -The `RUN_TEST` macro is called with each test case function. Its job is to -perform whatever setup and teardown is necessary for executing a single test -case function. This includes catching failures, calling the test module's -`setUp()` and `tearDown()` functions, and calling `UnityConcludeTest()`. If -using CMock or test coverage, there will be additional stubs in use here. A -simple minimalist RUN_TEST macro looks something like this: - - #define RUN_TEST(testfunc) \ - UNITY_NEW_TEST(#testfunc) \ - if (TEST_PROTECT()) { \ - setUp(); \ - testfunc(); \ - } \ - if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \ - tearDown(); \ - UnityConcludeTest(); - -So that's quite a macro, huh? It gives you a glimpse of what kind of stuff Unity -has to deal with for every single test case. For each test case, we declare that -it is a new test. Then we run `setUp` and our test function. These are run -within a `TEST_PROTECT` block, the function of which is to handle failures that -occur during the test. Then, assuming our test is still running and hasn't been -ignored, we run `tearDown`. No matter what, our last step is to conclude this -test before moving on to the next. - -Let's say you need to add a call to `fsync` to force all of your output data to -flush to a file after each test. You could easily insert this after your -`UnityConcludeTest` call. Maybe you want to write an xml tag before and after -each result set. Again, you could do this by adding lines to this macro. Updates -to this macro are for the occasions when you need an action before or after -every single test case throughout your entire suite of tests. - - -## Happy Porting - -The defines and macros in this guide should help you port Unity to just about -any C target we can imagine. If you run into a snag or two, don't be afraid of -asking for help on the forums. We love a good challenge! - - -*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/test/vendor/ceedling/docs/UnityGettingStartedGuide.md b/test/vendor/ceedling/docs/UnityGettingStartedGuide.md deleted file mode 100644 index 5e4427ce9..000000000 --- a/test/vendor/ceedling/docs/UnityGettingStartedGuide.md +++ /dev/null @@ -1,192 +0,0 @@ -# Unity - Getting Started - -## Welcome - -Congratulations. You're now the proud owner of your very own pile of bits! What -are you going to do with all these ones and zeros? This document should be able -to help you decide just that. - -Unity is a unit test framework. The goal has been to keep it small and -functional. The core Unity test framework is three files: a single C file and a -couple header files. These team up to provide functions and macros to make -testing easier. - -Unity was designed to be cross-platform. It works hard to stick with C standards -while still providing support for the many embedded C compilers that bend the -rules. Unity has been used with many compilers, including GCC, IAR, Clang, -Green Hills, Microchip, and MS Visual Studio. It's not much work to get it to -work with a new target. - - -### Overview of the Documents - -#### Unity Assertions reference - -This document will guide you through all the assertion options provided by -Unity. This is going to be your unit testing bread and butter. You'll spend more -time with assertions than any other part of Unity. - - -#### Unity Assertions Cheat Sheet - -This document contains an abridged summary of the assertions described in the -previous document. It's perfect for printing and referencing while you -familiarize yourself with Unity's options. - - -#### Unity Configuration Guide - -This document is the one to reference when you are going to use Unity with a new -target or compiler. It'll guide you through the configuration options and will -help you customize your testing experience to meet your needs. - - -#### Unity Helper Scripts - -This document describes the helper scripts that are available for simplifying -your testing workflow. It describes the collection of optional Ruby scripts -included in the auto directory of your Unity installation. Neither Ruby nor -these scripts are necessary for using Unity. They are provided as a convenience -for those who wish to use them. - - -#### Unity License - -What's an open source project without a license file? This brief document -describes the terms you're agreeing to when you use this software. Basically, we -want it to be useful to you in whatever context you want to use it, but please -don't blame us if you run into problems. - - -### Overview of the Folders - -If you have obtained Unity through Github or something similar, you might be -surprised by just how much stuff you suddenly have staring you in the face. -Don't worry, Unity itself is very small. The rest of it is just there to make -your life easier. You can ignore it or use it at your convenience. Here's an -overview of everything in the project. - -- `src` - This is the code you care about! This folder contains a C file and two -header files. These three files _are_ Unity. -- `docs` - You're reading this document, so it's possible you have found your way -into this folder already. This is where all the handy documentation can be -found. -- `examples` - This contains a few examples of using Unity. -- `extras` - These are optional add ons to Unity that are not part of the core -project. If you've reached us through James Grenning's book, you're going to -want to look here. -- `test` - This is how Unity and its scripts are all tested. If you're just using -Unity, you'll likely never need to go in here. If you are the lucky team member -who gets to port Unity to a new toolchain, this is a good place to verify -everything is configured properly. -- `auto` - Here you will find helpful Ruby scripts for simplifying your test -workflow. They are purely optional and are not required to make use of Unity. - - -## How to Create A Test File - -Test files are C files. Most often you will create a single test file for each C -module that you want to test. The test file should include unity.h and the -header for your C module to be tested. - -Next, a test file will include a `setUp()` and `tearDown()` function. The setUp -function can contain anything you would like to run before each test. The -tearDown function can contain anything you would like to run after each test. -Both functions accept no arguments and return nothing. You may leave either or -both of these blank if you have no need for them. If you're using a compiler -that is configured to make these functions optional, you may leave them off -completely. Not sure? Give it a try. If you compiler complains that it can't -find setUp or tearDown when it links, you'll know you need to at least include -an empty function for these. - -The majority of the file will be a series of test functions. Test functions -follow the convention of starting with the word "test_" or "spec_". You don't HAVE -to name them this way, but it makes it clear what functions are tests for other -developers. Also, the automated scripts that come with Unity or Ceedling will default -to looking for test functions to be prefixed this way. Test functions take no arguments -and return nothing. All test accounting is handled internally in Unity. - -Finally, at the bottom of your test file, you will write a `main()` function. -This function will call `UNITY_BEGIN()`, then `RUN_TEST` for each test, and -finally `UNITY_END()`.This is what will actually trigger each of those test -functions to run, so it is important that each function gets its own `RUN_TEST` -call. - -Remembering to add each test to the main function can get to be tedious. If you -enjoy using helper scripts in your build process, you might consider making use -of our handy generate_test_runner.rb script. This will create the main function -and all the calls for you, assuming that you have followed the suggested naming -conventions. In this case, there is no need for you to include the main function -in your test file at all. - -When you're done, your test file will look something like this: - -```C -#include "unity.h" -#include "file_to_test.h" - -void setUp(void) { - // set stuff up here -} - -void tearDown(void) { - // clean stuff up here -} - -void test_function_should_doBlahAndBlah(void) { - //test stuff -} - -void test_function_should_doAlsoDoBlah(void) { - //more test stuff -} - -int main(void) { - UNITY_BEGIN(); - RUN_TEST(test_function_should_doBlahAndBlah); - RUN_TEST(test_function_should_doAlsoDoBlah); - return UNITY_END(); -} -``` - -It's possible that you will need more customization than this, eventually. -For that sort of thing, you're going to want to look at the configuration guide. -This should be enough to get you going, though. - - -## How to Build and Run A Test File - -This is the single biggest challenge to picking up a new unit testing framework, -at least in a language like C or C++. These languages are REALLY good at getting -you "close to the metal" (why is the phrase metal? Wouldn't it be more accurate -to say "close to the silicon"?). While this feature is usually a good thing, it -can make testing more challenging. - -You have two really good options for toolchains. Depending on where you're -coming from, it might surprise you that neither of these options is running the -unit tests on your hardware. -There are many reasons for this, but here's a short version: -- On hardware, you have too many constraints (processing power, memory, etc), -- On hardware, you don't have complete control over all registers, -- On hardware, unit testing is more challenging, -- Unit testing isn't System testing. Keep them separate. - -Instead of running your tests on your actual hardware, most developers choose to -develop them as native applications (using gcc or MSVC for example) or as -applications running on a simulator. Either is a good option. Native apps have -the advantages of being faster and easier to set up. Simulator apps have the -advantage of working with the same compiler as your target application. The -options for configuring these are discussed in the configuration guide. - -To get either to work, you might need to make a few changes to the file -containing your register set (discussed later). - -In either case, a test is built by linking unity, the test file, and the C -file(s) being tested. These files create an executable which can be run as the -test set for that module. Then, this process is repeated for the next test file. -This flexibility of separating tests into individual executables allows us to -much more thoroughly unit test our system and it keeps all the test code out of -our final release! - - -*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/test/vendor/ceedling/docs/UnityHelperScriptsGuide.md b/test/vendor/ceedling/docs/UnityHelperScriptsGuide.md deleted file mode 100644 index 12d68d30e..000000000 --- a/test/vendor/ceedling/docs/UnityHelperScriptsGuide.md +++ /dev/null @@ -1,260 +0,0 @@ -# Unity Helper Scripts - -## With a Little Help From Our Friends - -Sometimes what it takes to be a really efficient C programmer is a little non-C. -The Unity project includes a couple of Ruby scripts for making your life just a tad -easier. They are completely optional. If you choose to use them, you'll need a -copy of Ruby, of course. Just install whatever the latest version is, and it is -likely to work. You can find Ruby at [ruby-lang.org](https://ruby-labg.org/). - - -### `generate_test_runner.rb` - -Are you tired of creating your own `main` function in your test file? Do you -keep forgetting to add a `RUN_TEST` call when you add a new test case to your -suite? Do you want to use CMock or other fancy add-ons but don't want to figure -out how to create your own `RUN_TEST` macro? - -Well then we have the perfect script for you! - -The `generate_test_runner` script processes a given test file and automatically -creates a separate test runner file that includes ?main?to execute the test -cases within the scanned test file. All you do then is add the generated runner -to your list of files to be compiled and linked, and presto you're done! - -This script searches your test file for void function signatures having a -function name beginning with "test" or "spec". It treats each of these -functions as a test case and builds up a test suite of them. For example, the -following includes three test cases: - -```C -void testVerifyThatUnityIsAwesomeAndWillMakeYourLifeEasier(void) -{ - ASSERT_TRUE(1); -} -void test_FunctionName_should_WorkProperlyAndReturn8(void) { - ASSERT_EQUAL_INT(8, FunctionName()); -} -void spec_Function_should_DoWhatItIsSupposedToDo(void) { - ASSERT_NOT_NULL(Function(5)); -} -``` - -You can run this script a couple of ways. The first is from the command line: - -```Shell -ruby generate_test_runner.rb TestFile.c NameOfRunner.c -``` - -Alternatively, if you include only the test file parameter, the script will copy -the name of the test file and automatically append "_Runner" to the name of the -generated file. The example immediately below will create TestFile_Runner.c. - -```Shell -ruby generate_test_runner.rb TestFile.c -``` - -You can also add a [YAML](http://www.yaml.org/) file to configure extra options. -Conveniently, this YAML file is of the same format as that used by Unity and -CMock. So if you are using YAML files already, you can simply pass the very same -file into the generator script. - -```Shell -ruby generate_test_runner.rb TestFile.c my_config.yml -``` - -The contents of the YAML file `my_config.yml` could look something like the -example below. If you're wondering what some of these options do, you're going -to love the next section of this document. - -```YAML -:unity: - :includes: - - stdio.h - - microdefs.h - :cexception: 1 - :suit_setup: "blah = malloc(1024);" - :suite_teardown: "free(blah);" -``` - -If you would like to force your generated test runner to include one or more -header files, you can just include those at the command line too. Just make sure -these are _after_ the YAML file, if you are using one: - -```Shell -ruby generate_test_runner.rb TestFile.c my_config.yml extras.h -``` - -Another option, particularly if you are already using Ruby to orchestrate your -builds - or more likely the Ruby-based build tool Rake - is requiring this -script directly. Anything that you would have specified in a YAML file can be -passed to the script as part of a hash. Let's push the exact same requirement -set as we did above but this time through Ruby code directly: - -```Ruby -require "generate_test_runner.rb" -options = { - :includes => ["stdio.h", "microdefs.h"], - :cexception => 1, - :suite_setup => "blah = malloc(1024);", - :suite_teardown => "free(blah);" -} -UnityTestRunnerGenerator.new.run(testfile, runner_name, options) -``` - -If you have multiple files to generate in a build script (such as a Rakefile), -you might want to instantiate a generator object with your options and call it -to generate each runner afterwards. Like thus: - -```Ruby -gen = UnityTestRunnerGenerator.new(options) -test_files.each do |f| - gen.run(f, File.basename(f,'.c')+"Runner.c" -end -``` - -#### Options accepted by generate_test_runner.rb: - -The following options are available when executing `generate_test_runner`. You -may pass these as a Ruby hash directly or specify them in a YAML file, both of -which are described above. In the `examples` directory, Example 3's Rakefile -demonstrates using a Ruby hash. - - -##### `:includes` - -This option specifies an array of file names to be `#include`'d at the top of -your runner C file. You might use it to reference custom types or anything else -universally needed in your generated runners. - - -##### `:suite_setup` - -Define this option with C code to be executed _before any_ test cases are run. - -Alternatively, if your C compiler supports weak symbols, you can leave this -option unset and instead provide a `void suiteSetUp(void)` function in your test -suite. The linker will look for this symbol and fall back to a Unity-provided -stub if it is not found. - - -##### `:suite_teardown` - -Define this option with C code to be executed _after all_ test cases have -finished. An integer variable `num_failures` is available for diagnostics. -The code should end with a `return` statement; the value returned will become -the exit code of `main`. You can normally just return `num_failures`. - -Alternatively, if your C compiler supports weak symbols, you can leave this -option unset and instead provide a `int suiteTearDown(int num_failures)` -function in your test suite. The linker will look for this symbol and fall -back to a Unity-provided stub if it is not found. - - -##### `:enforce_strict_ordering` - -This option should be defined if you have the strict order feature enabled in -CMock (see CMock documentation). This generates extra variables required for -everything to run smoothly. If you provide the same YAML to the generator as -used in CMock's configuration, you've already configured the generator properly. - -##### `:mock_prefix` and `:mock_suffix` - -Unity automatically generates calls to Init, Verify and Destroy for every file -included in the main test file that starts with the given mock prefix and ends -with the given mock suffix, file extension not included. By default, Unity -assumes a `Mock` prefix and no suffix. - -##### `:plugins` - -This option specifies an array of plugins to be used (of course, the array can -contain only a single plugin). This is your opportunity to enable support for -CException support, which will add a check for unhandled exceptions in each -test, reporting a failure if one is detected. To enable this feature using Ruby: - -```Ruby -:plugins => [ :cexception ] -``` - -Or as a yaml file: - -```YAML -:plugins: - -:cexception -``` - -If you are using CMock, it is very likely that you are already passing an array -of plugins to CMock. You can just use the same array here. This script will just -ignore the plugins that don't require additional support. - - -### `unity_test_summary.rb` - -A Unity test file contains one or more test case functions. Each test case can -pass, fail, or be ignored. Each test file is run individually producing results -for its collection of test cases. A given project will almost certainly be -composed of multiple test files. Therefore, the suite of tests is comprised of -one or more test cases spread across one or more test files. This script -aggregates individual test file results to generate a summary of all executed -test cases. The output includes how many tests were run, how many were ignored, -and how many failed. In addition, the output includes a listing of which -specific tests were ignored and failed. A good example of the breadth and -details of these results can be found in the `examples` directory. Intentionally -ignored and failing tests in this project generate corresponding entries in the -summary report. - -If you're interested in other (prettier?) output formats, check into the -Ceedling build tool project (ceedling.sourceforge.net) that works with Unity and -CMock and supports xunit-style xml as well as other goodies. - -This script assumes the existence of files ending with the extensions -`.testpass` and `.testfail`.The contents of these files includes the test -results summary corresponding to each test file executed with the extension set -according to the presence or absence of failures for that test file. The script -searches a specified path for these files, opens each one it finds, parses the -results, and aggregates and prints a summary. Calling it from the command line -looks like this: - -```Shell -ruby unity_test_summary.rb build/test/ -``` - -You can optionally specify a root path as well. This is really helpful when you -are using relative paths in your tools' setup, but you want to pull the summary -into an IDE like Eclipse for clickable shortcuts. - -```Shell -ruby unity_test_summary.rb build/test/ ~/projects/myproject/ -``` - -Or, if you're more of a Windows sort of person: - -```Shell -ruby unity_test_summary.rb build\teat\ C:\projects\myproject\ -``` - -When configured correctly, you'll see a final summary, like so: - -```Shell --------------------------- -UNITY IGNORED TEST SUMMARY --------------------------- -blah.c:22:test_sandwiches_should_HaveBreadOnTwoSides:IGNORE - -------------------------- -UNITY FAILED TEST SUMMARY -------------------------- -blah.c:87:test_sandwiches_should_HaveCondiments:FAIL:Expected 1 was 0 -meh.c:38:test_soda_should_BeCalledPop:FAIL:Expected "pop" was "coke" - --------------------------- -OVERALL UNITY TEST SUMMARY --------------------------- -45 TOTAL TESTS 2 TOTAL FAILURES 1 IGNORED -``` - -How convenient is that? - - -*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb b/test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb deleted file mode 100644 index 2a161854b..000000000 --- a/test/vendor/ceedling/lib/ceedling/cacheinator_helper.rb +++ /dev/null @@ -1,31 +0,0 @@ - -class CacheinatorHelper - - constructor :file_wrapper, :yaml_wrapper - - def diff_cached_config?(cached_filepath, hash) - return true if ( not @file_wrapper.exist?(cached_filepath) ) - return true if ( (@file_wrapper.exist?(cached_filepath)) and (!(@yaml_wrapper.load(cached_filepath) == hash)) ) - return false - end - - def diff_cached_defines?(cached_filepath, files) - current_defines = COLLECTION_DEFINES_TEST_AND_VENDOR.reject(&:empty?) - - current_dependency = Hash[files.collect { |source| [source, current_defines.dup] }] - if not @file_wrapper.exist?(cached_filepath) - @yaml_wrapper.dump(cached_filepath, current_dependency) - return false - end - - dependencies = @yaml_wrapper.load(cached_filepath) - if dependencies.values_at(*current_dependency.keys) != current_dependency.values - dependencies.merge!(current_dependency) - @yaml_wrapper.dump(cached_filepath, dependencies) - return true - end - - return false - end - -end diff --git a/test/vendor/ceedling/lib/ceedling/preprocessinator.rb b/test/vendor/ceedling/lib/ceedling/preprocessinator.rb deleted file mode 100644 index f07750dd2..000000000 --- a/test/vendor/ceedling/lib/ceedling/preprocessinator.rb +++ /dev/null @@ -1,42 +0,0 @@ - -class Preprocessinator - - attr_reader :preprocess_file_proc - - constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper - - - def setup - # fashion ourselves callbacks @preprocessinator_helper can use - @preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) } - @preprocess_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } - end - - - def preprocess_test_and_invoke_test_mocks(test) - @preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc) - - mocks_list = @preprocessinator_helper.assemble_mocks_list(test) - - @preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_file_proc) - - @task_invoker.invoke_test_mocks(mocks_list) - - @preprocessinator_helper.preprocess_test_file(test, @preprocess_file_proc) - - return mocks_list - end - - def preprocess_shallow_includes(filepath) - includes = @preprocessinator_includes_handler.extract_includes(filepath) - - @preprocessinator_includes_handler.write_shallow_includes_list( - @file_path_utils.form_preprocessed_includes_list_filepath(filepath), includes) - end - - def preprocess_file(filepath) - @preprocessinator_includes_handler.invoke_shallow_includes_list(filepath) - @preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) ) - end - -end diff --git a/test/vendor/ceedling/lib/ceedling/version.rb b/test/vendor/ceedling/lib/ceedling/version.rb deleted file mode 100644 index ba917df5a..000000000 --- a/test/vendor/ceedling/lib/ceedling/version.rb +++ /dev/null @@ -1,36 +0,0 @@ - -# @private -module Ceedling - module Version - # Check for local or global version of vendor directory in order to look up versions - { - "CEXCEPTION" => File.join("vendor","c_exception","lib","CException.h"), - "CMOCK" => File.join("vendor","cmock","src","cmock.h"), - "UNITY" => File.join("vendor","unity","src","unity.h"), - }.each_pair do |name, path| - filename = if (File.exist?(File.join("..","..",path))) - File.join("..","..",path) - elsif (File.exist?(File.join(File.dirname(__FILE__),"..","..",path))) - File.join(File.dirname(__FILE__),"..","..",path) - else - eval "#{name} = 'unknown'" - continue - end - - # Actually look up the versions - a = [0,0,0] - File.readlines(filename) do |line| - ["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i| - m = line.match(/#{name}_#{field}\s+(\d+)/) - a[i] = m[1] unless (m.nil?) - end - end - - # Make a constant from each, so that we can use it elsewhere - eval "#{name} = '#{a.join(".")}'" - end - - GEM = "0.29.0" - CEEDLING = GEM - end -end diff --git a/test/vendor/ceedling/lib/ceedling/version.rb.erb b/test/vendor/ceedling/lib/ceedling/version.rb.erb deleted file mode 100644 index a31e3abb3..000000000 --- a/test/vendor/ceedling/lib/ceedling/version.rb.erb +++ /dev/null @@ -1,15 +0,0 @@ -# @private -module Ceedling - module Version - # @private - GEM = "0.27.0" - # @private - CEEDLING = "<%= versions["CEEDLING"] %>" - # @private - CEXCEPTION = "<%= versions["CEXCEPTION"] %>" - # @private - CMOCK = "<%= versions["CMOCK"] %>" - # @private - UNITY = "<%= versions["UNITY"] %>" - end -end diff --git a/test/vendor/ceedling/plugins/bullseye/readme.txt b/test/vendor/ceedling/plugins/bullseye/readme.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/vendor/ceedling/plugins/gcov/README.md b/test/vendor/ceedling/plugins/gcov/README.md deleted file mode 100644 index 096ffa100..000000000 --- a/test/vendor/ceedling/plugins/gcov/README.md +++ /dev/null @@ -1,101 +0,0 @@ -ceedling-gcov -============= - -# Plugin Overview - -Plugin for integrating GNU GCov code coverage tool into Ceedling projects. -Currently only designed for the gcov command (like LCOV for example). In the -future we could configure this to work with other code coverage tools. - -This plugin currently uses `gcovr` to generate HTML and/or XML reports as a -utility. The normal gcov plugin _must_ be run first for this report to generate. - -## Installation - -Gcovr can be installed via pip like so: - -``` -pip install gcovr -``` - -## Configuration - -The gcov plugin supports configuration options via your `project.yml` provided -by Ceedling. - -Generation of HTML reports may be enabled or disabled with the following -config. Set to `true` to enable or set to `false` to disable. - -``` -:gcov: - :html_report: true -``` - -Generation of XML reports may be enabled or disabled with the following -config. Set to `true` to enable or set to `false` to disable. - -``` -:gcov: - :xml_report: true -``` - -There are two types of gcovr HTML reports that can be configured in your -`project.yml`. To create a basic HTML report, with only the overall file -information, use the following config. - -``` -:gcov: - :html_report_type: basic -``` - -To create a detailed HTML report, with line by line breakdown of the -coverage, use the following config. - -``` -:gcov: - :html_report_type: detailed -``` - -There are a number of options to control which files are considered part of -the coverage report. Most often, we only care about coverage on our source code, and not -on tests or automatically generated mocks, runners, etc. However, there are times -where this isn't true... or there are times where we've moved ceedling's directory -structure so that the project file isn't at the root of the project anymore. In these -cases, you may need to tweak the following: - -``` -:gcov: - :report_root: "." - :report_exclude: "^build|^vendor|^test|^support" - :report_include: "^src" -``` - -One important note about html_report_root: gcovr will only take a single root folder, unlike -Ceedling's ability to take as many as you like. So you will need to choose a folder which is -a superset of ALL the folders you want, and then use the include or exclude options to set up -patterns of files to pay attention to or ignore. It's not ideal, but it works. - -Finally, there are a number of settings which can be specified in order to adjust the -default behaviors of gcov: - -``` -:gcov: - :html_medium_threshold: 75 - :html_high_threshold: 90 - :fail_under_line: 30 - :fail_under_branch: 30 -``` - -These HTML and XML reports will be found in `build/artifacts/gcov`. - -## Example Usage - -``` -ceedling gcov:all utils:gcov -``` - -## To-Do list - -- Generate overall report (combined statistics from all files with coverage) -- Generate coverage output files -- Easier option override for better customisation diff --git a/test/vendor/ceedling/plugins/gcov/config/defaults.yml b/test/vendor/ceedling/plugins/gcov/config/defaults.yml deleted file mode 100644 index 13bac556e..000000000 --- a/test/vendor/ceedling/plugins/gcov/config/defaults.yml +++ /dev/null @@ -1,73 +0,0 @@ ---- - -:tools: - :gcov_compiler: - :executable: gcc - :arguments: - - -g - - -fprofile-arcs - - -ftest-coverage - - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR - - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE - - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR - - -DGCOV_COMPILER - - -DCODE_COVERAGE - - -c "${1}" - - -o "${2}" - :gcov_linker: - :executable: gcc - :arguments: - - -fprofile-arcs - - -ftest-coverage - - ${1} - - -o ${2} - - ${3} - :gcov_fixture: - :executable: ${1} - :gcov_report: - :executable: gcov - :arguments: - - -n - - -p - - -b - - -o "$": GCOV_BUILD_OUTPUT_PATH - - "\"${1}\"" - :gcov_post_report: - :executable: gcovr - :optional: TRUE - :arguments: - - -p - - -b - - ${1} - - --html - - -o GcovCoverageResults.html - :gcov_post_report_basic: - :executable: gcovr - :optional: TRUE - :arguments: - - -p - - -b - - ${1} - - --html - - -o "$": GCOV_ARTIFACTS_FILE - :gcov_post_report_advanced: - :executable: gcovr - :optional: TRUE - :arguments: - - -p - - -b - - ${1} - - --html - - --html-details - - -o "$": GCOV_ARTIFACTS_FILE - :gcov_post_report_xml: - :executable: gcovr - :optional: TRUE - :arguments: - - -p - - -b - - ${1} - - --xml - - -o "$": GCOV_ARTIFACTS_FILE_XML - -... diff --git a/test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb b/test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb deleted file mode 100644 index 539d46f75..000000000 --- a/test/vendor/ceedling/plugins/gcov/lib/gcov_constants.rb +++ /dev/null @@ -1,19 +0,0 @@ - -GCOV_ROOT_NAME = 'gcov'.freeze -GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':' -GCOV_SYM = GCOV_ROOT_NAME.to_sym - -GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME) -GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out") -GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results") -GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies") -GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME) - -GCOV_ARTIFACTS_FILE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html") -GCOV_ARTIFACTS_FILE_XML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.xml") - -GCOV_IGNORE_SOURCES = %w(unity cmock cexception).freeze - -GCOV_FILTER_EXCLUDE = '^vendor.*|^build.*|^test.*|^lib.*' - - diff --git a/test/vendor/ceedling/vendor/c_exception/release/build.info b/test/vendor/ceedling/vendor/c_exception/release/build.info deleted file mode 100644 index 0cd525d8c..000000000 --- a/test/vendor/ceedling/vendor/c_exception/release/build.info +++ /dev/null @@ -1,2 +0,0 @@ -18 - diff --git a/test/vendor/ceedling/vendor/c_exception/release/version.info b/test/vendor/ceedling/vendor/c_exception/release/version.info deleted file mode 100644 index 1527a8de2..000000000 --- a/test/vendor/ceedling/vendor/c_exception/release/version.info +++ /dev/null @@ -1,2 +0,0 @@ -1.3.1 - diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock.rb deleted file mode 100644 index 8243ce58a..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock.rb +++ /dev/null @@ -1,86 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -[ "../config/production_environment", - "cmock_header_parser", - "cmock_generator", - "cmock_file_writer", - "cmock_config", - "cmock_plugin_manager", - "cmock_generator_utils", - "cmock_unityhelper_parser"].each {|req| require "#{File.expand_path(File.dirname(__FILE__))}/#{req}"} - -class CMock - - def initialize(options=nil) - cm_config = CMockConfig.new(options) - cm_unityhelper = CMockUnityHelperParser.new(cm_config) - cm_writer = CMockFileWriter.new(cm_config) - cm_gen_utils = CMockGeneratorUtils.new(cm_config, {:unity_helper => cm_unityhelper}) - cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils) - @cm_parser = CMockHeaderParser.new(cm_config) - @cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils, cm_gen_plugins) - @silent = (cm_config.verbosity < 2) - end - - def setup_mocks(files) - [files].flatten.each do |src| - generate_mock src - end - end - - private ############################### - - def generate_mock(src) - name = File.basename(src, '.h') - puts "Creating mock for #{name}..." unless @silent - @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src))) - end -end - -def option_maker(options, key, val) - options = options || {} - options[key.to_sym] = - if val.chr == ":" - val[1..-1].to_sym - elsif val.include? ";" - val.split(';') - elsif val == 'true' - true - elsif val == 'false' - false - elsif val =~ /^\d+$/ - val.to_i - else - val - end - options -end - - # Command Line Support ############################### - -if ($0 == __FILE__) - usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock" - - if (!ARGV[0]) - puts usage - exit 1 - end - - options = {} - filelist = [] - ARGV.each do |arg| - if (arg =~ /^-o\"?([a-zA-Z0-9._\\\/:\s]+)\"?/) - options.merge! CMockConfig.load_config_file_from_yaml( arg.gsub(/^-o/,'') ) - elsif (arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]+)\"?/) - options = option_maker(options, $1, $2) - else - filelist << arg - end - end - - CMock.new(options).setup_mocks(filelist) -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb deleted file mode 100644 index b21b61ed2..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_config.rb +++ /dev/null @@ -1,153 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockConfig - - CMockDefaultOptions = - { - :framework => :unity, - :mock_path => 'mocks', - :mock_prefix => 'Mock', - :mock_suffix => '', - :weak => '', - :subdir => nil, - :plugins => [], - :strippables => ['(?:__attribute__\s*\(+.*?\)+)'], - :attributes => ['__ramfunc', '__irq', '__fiq', 'register', 'extern'], - :c_calling_conventions => ['__stdcall', '__cdecl', '__fastcall'], - :enforce_strict_ordering => false, - :fail_on_unexpected_calls => true, - :unity_helper_path => false, - :treat_as => {}, - :treat_as_array => {}, - :treat_as_void => [], - :memcmp_if_unknown => true, - :when_no_prototypes => :warn, #the options being :ignore, :warn, or :error - :when_ptr => :compare_data, #the options being :compare_ptr, :compare_data, or :smart - :verbosity => 2, #the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose - :treat_externs => :exclude, #the options being :include or :exclude - :callback_include_count => true, - :callback_after_arg_check => false, - :includes => nil, - :includes_h_pre_orig_header => nil, - :includes_h_post_orig_header => nil, - :includes_c_pre_header => nil, - :includes_c_post_header => nil, - :orig_header_include_fmt => "#include \"%s\"", - :array_size_type => [], - :array_size_name => 'size|len', - } - - def initialize(options=nil) - case(options) - when NilClass then options = CMockDefaultOptions.clone - when String then options = CMockDefaultOptions.clone.merge(load_config_file_from_yaml(options)) - when Hash then options = CMockDefaultOptions.clone.merge(options) - else raise "If you specify arguments, it should be a filename or a hash of options" - end - - #do some quick type verification - [:plugins, :attributes, :treat_as_void].each do |opt| - unless (options[opt].class == Array) - options[opt] = [] - puts "WARNING: :#{opt.to_s} should be an array." unless (options[:verbosity] < 1) - end - end - [:includes, :includes_h_pre_orig_header, :includes_h_post_orig_header, :includes_c_pre_header, :includes_c_post_header].each do |opt| - unless (options[opt].nil? or (options[opt].class == Array)) - options[opt] = [] - puts "WARNING: :#{opt.to_s} should be an array." unless (options[:verbosity] < 1) - end - end - options[:unity_helper_path] ||= options[:unity_helper] - options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String - options[:includes_c_post_header] = ((options[:includes_c_post_header] || []) + (options[:unity_helper_path] || [])).uniq - options[:plugins].compact! - options[:plugins].map! {|p| p.to_sym} - @options = options - - treat_as_map = standard_treat_as_map()#.clone - treat_as_map.merge!(@options[:treat_as]) - @options[:treat_as] = treat_as_map - - @options.each_key do |key| - unless methods.include?(key) - eval("def #{key.to_s}() return @options[:#{key.to_s}] end") - end - end - end - - def load_config_file_from_yaml yaml_filename - self.class.load_config_file_from_yaml yaml_filename - end - - def self.load_config_file_from_yaml yaml_filename - require 'yaml' - require 'fileutils' - YAML.load_file(yaml_filename)[:cmock] - end - - def set_path(path) - @src_path = path - end - - def load_unity_helper - return nil unless (@options[:unity_helper_path]) - - return @options[:unity_helper_path].inject("") do |unity_helper, filename| - unity_helper + "\n" + File.new(filename).read - end - end - - def standard_treat_as_map - { - 'int' => 'INT', - 'char' => 'INT8', - 'short' => 'INT16', - 'long' => 'INT', - 'int8' => 'INT8', - 'int16' => 'INT16', - 'int32' => 'INT', - 'int8_t' => 'INT8', - 'int16_t' => 'INT16', - 'int32_t' => 'INT', - 'INT8_T' => 'INT8', - 'INT16_T' => 'INT16', - 'INT32_T' => 'INT', - 'bool' => 'INT', - 'bool_t' => 'INT', - 'BOOL' => 'INT', - 'BOOL_T' => 'INT', - 'unsigned int' => 'HEX32', - 'unsigned long' => 'HEX32', - 'uint32' => 'HEX32', - 'uint32_t' => 'HEX32', - 'UINT32' => 'HEX32', - 'UINT32_T' => 'HEX32', - 'void*' => 'HEX8_ARRAY', - 'void const*' => 'HEX8_ARRAY', - 'const void*' => 'HEX8_ARRAY', - 'unsigned short' => 'HEX16', - 'uint16' => 'HEX16', - 'uint16_t' => 'HEX16', - 'UINT16' => 'HEX16', - 'UINT16_T' => 'HEX16', - 'unsigned char' => 'HEX8', - 'uint8' => 'HEX8', - 'uint8_t' => 'HEX8', - 'UINT8' => 'HEX8', - 'UINT8_T' => 'HEX8', - 'char*' => 'STRING', - 'char const*' => 'STRING', - 'const char*' => 'STRING', - 'pCHAR' => 'STRING', - 'cstring' => 'STRING', - 'CSTRING' => 'STRING', - 'float' => 'FLOAT', - 'double' => 'FLOAT' - } - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb deleted file mode 100644 index 42725a60d..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator.rb +++ /dev/null @@ -1,268 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGenerator - - attr_accessor :config, :file_writer, :module_name, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered - - def initialize(config, file_writer, utils, plugins) - @file_writer = file_writer - @utils = utils - @plugins = plugins - @config = config - @prefix = @config.mock_prefix - @suffix = @config.mock_suffix - @weak = @config.weak - @ordered = @config.enforce_strict_ordering - @framework = @config.framework.to_s - @fail_on_unexpected_calls = @config.fail_on_unexpected_calls - - @subdir = @config.subdir - - @includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} - @includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} - @includes_c_pre_header = (@config.includes_c_pre_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} - @includes_c_post_header = (@config.includes_c_post_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} - - here = File.dirname __FILE__ - unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling - unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock - # path to Unity as specified by env var - unity_path_in_env = ENV.has_key?("UNITY_DIR") ? File.expand_path(ENV.fetch("UNITY_DIR")) : nil - - if unity_path_in_env and File.exist? unity_path_in_env - require "#{unity_path_in_env}/auto/type_sanitizer" - elsif File.exist? unity_path_in_ceedling - require "#{unity_path_in_ceedling}/auto/type_sanitizer" - elsif File.exist? unity_path_in_cmock - require "#{unity_path_in_cmock}/auto/type_sanitizer" - else - raise "Failed to find an instance of Unity to pull in type_sanitizer module!" - end - - end - - def create_mock(module_name, parsed_stuff) - @module_name = module_name - @mock_name = @prefix + @module_name + @suffix - @clean_mock_name = TypeSanitizer.sanitize_c_identifier(@mock_name) - create_mock_subdir() - create_mock_header_file(parsed_stuff) - create_mock_source_file(parsed_stuff) - end - - private if $ThisIsOnlyATest.nil? ############################## - - def create_mock_subdir() - @file_writer.create_subdir(@subdir) - end - - def create_mock_header_file(parsed_stuff) - @file_writer.create_file(@mock_name + ".h", @subdir) do |file, filename| - create_mock_header_header(file, filename) - create_mock_header_service_call_declarations(file) - create_typedefs(file, parsed_stuff[:typedefs]) - parsed_stuff[:functions].each do |function| - file << @plugins.run(:mock_function_declarations, function) - end - create_mock_header_footer(file) - end - end - - def create_mock_source_file(parsed_stuff) - @file_writer.create_file(@mock_name + ".c", @subdir) do |file, filename| - create_source_header_section(file, filename, parsed_stuff[:functions]) - create_instance_structure(file, parsed_stuff[:functions]) - create_extern_declarations(file) - create_mock_verify_function(file, parsed_stuff[:functions]) - create_mock_init_function(file) - create_mock_destroy_function(file, parsed_stuff[:functions]) - parsed_stuff[:functions].each do |function| - create_mock_implementation(file, function) - create_mock_interfaces(file, function) - end - end - end - - def create_mock_header_header(file, filename) - define_name = @clean_mock_name.upcase - orig_filename = (@subdir ? @subdir + "/" : "") + @module_name + ".h" - file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" - file << "#ifndef _#{define_name}_H\n" - file << "#define _#{define_name}_H\n\n" - file << "#include \"#{@framework}.h\"\n" - @includes_h_pre_orig_header.each {|inc| file << "#include #{inc}\n"} - file << @config.orig_header_include_fmt.gsub(/%s/, "#{orig_filename}") + "\n" - @includes_h_post_orig_header.each {|inc| file << "#include #{inc}\n"} - plugin_includes = @plugins.run(:include_files) - file << plugin_includes if (!plugin_includes.empty?) - file << "\n" - file << "/* Ignore the following warnings, since we are copying code */\n" - file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" - file << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" - file << "#pragma GCC diagnostic push\n" - file << "#endif\n" - file << "#if !defined(__clang__)\n" - file << "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" - file << "#endif\n" - file << "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" - file << "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" - file << "#endif\n" - file << "\n" - end - - def create_typedefs(file, typedefs) - file << "\n" - typedefs.each {|typedef| file << "#{typedef}\n" } - file << "\n\n" - end - - def create_mock_header_service_call_declarations(file) - file << "void #{@clean_mock_name}_Init(void);\n" - file << "void #{@clean_mock_name}_Destroy(void);\n" - file << "void #{@clean_mock_name}_Verify(void);\n\n" - end - - def create_mock_header_footer(header) - header << "\n" - header << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" - header << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" - header << "#pragma GCC diagnostic pop\n" - header << "#endif\n" - header << "#endif\n" - header << "\n" - header << "#endif\n" - end - - def create_source_header_section(file, filename, functions) - header_file = (@subdir ? @subdir + '/' : '') + filename.gsub(".c",".h") - file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" - file << "#include <string.h>\n" - file << "#include <stdlib.h>\n" - file << "#include <setjmp.h>\n" - file << "#include \"cmock.h\"\n" - @includes_c_pre_header.each {|inc| file << "#include #{inc}\n"} - file << "#include \"#{header_file}\"\n" - @includes_c_post_header.each {|inc| file << "#include #{inc}\n"} - file << "\n" - strs = [] - functions.each do |func| - strs << func[:name] - func[:args].each {|arg| strs << arg[:name] } - end - strs.uniq.sort.each do |str| - file << "static const char* CMockString_#{str} = \"#{str}\";\n" - end - file << "\n" - end - - def create_instance_structure(file, functions) - functions.each do |function| - file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n" - file << " UNITY_LINE_TYPE LineNumber;\n" - file << @plugins.run(:instance_typedefs, function) - file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n" - end - file << "static struct #{@clean_mock_name}Instance\n{\n" - if (functions.size == 0) - file << " unsigned char placeHolder;\n" - end - functions.each do |function| - file << @plugins.run(:instance_structure, function) - file << " CMOCK_MEM_INDEX_TYPE #{function[:name]}_CallInstance;\n" - end - file << "} Mock;\n\n" - end - - def create_extern_declarations(file) - file << "extern jmp_buf AbortFrame;\n" - if (@ordered) - file << "extern int GlobalExpectCount;\n" - file << "extern int GlobalVerifyOrder;\n" - end - file << "\n" - end - - def create_mock_verify_function(file, functions) - file << "void #{@clean_mock_name}_Verify(void)\n{\n" - verifications = functions.collect do |function| - v = @plugins.run(:mock_verify, function) - v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v] - end.join - unless verifications.empty? - file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" - file << " CMOCK_MEM_INDEX_TYPE call_instance;\n" - file << verifications - end - file << "}\n\n" - end - - def create_mock_init_function(file) - file << "void #{@clean_mock_name}_Init(void)\n{\n" - file << " #{@clean_mock_name}_Destroy();\n" - file << "}\n\n" - end - - def create_mock_destroy_function(file, functions) - file << "void #{@clean_mock_name}_Destroy(void)\n{\n" - file << " CMock_Guts_MemFreeAll();\n" - file << " memset(&Mock, 0, sizeof(Mock));\n" - file << functions.collect {|function| @plugins.run(:mock_destroy, function)}.join - - unless (@fail_on_unexpected_calls) - file << functions.collect {|function| @plugins.run(:mock_ignore, function)}.join - end - - if (@ordered) - file << " GlobalExpectCount = 0;\n" - file << " GlobalVerifyOrder = 0;\n" - end - file << "}\n\n" - end - - def create_mock_implementation(file, function) - # prepare return value and arguments - function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") + - (function[:return][:type]) + - (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') - args_string = function[:args_string] - args_string += (", " + function[:var_arg]) unless (function[:var_arg].nil?) - - # Create mock function - if (not @weak.empty?) - file << "#if defined (__IAR_SYSTEMS_ICC__)\n" - file << "#pragma weak #{function[:name]}\n" - file << "#else\n" - file << "#{function_mod_and_rettype} #{function[:name]}(#{args_string}) #{weak};\n" - file << "#endif\n\n" - end - file << "#{function_mod_and_rettype} #{function[:name]}(#{args_string})\n" - file << "{\n" - file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" - file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n" - file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" - file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n" - file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" - file << @plugins.run(:mock_implementation_precheck, function) - file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n" - file << " cmock_line = cmock_call_instance->LineNumber;\n" - if (@ordered) - file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n" - file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n" - file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n" - file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate);\n" - end - file << @plugins.run(:mock_implementation, function) - file << " UNITY_CLR_DETAILS();\n" - file << " return cmock_call_instance->ReturnVal;\n" unless (function[:return][:void?]) - file << "}\n\n" - end - - def create_mock_interfaces(file, function) - file << @utils.code_add_argument_loader(function) - file << @plugins.run(:mock_interfaces, function) - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb deleted file mode 100644 index 39d36d64c..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_cexception.rb +++ /dev/null @@ -1,51 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGeneratorPluginCexception - - attr_reader :priority - attr_reader :config, :utils - - def initialize(config, utils) - @config = config - @utils = utils - @priority = 7 - end - - def include_files - return "#include \"CException.h\"\n" - end - - def instance_typedefs(function) - " CEXCEPTION_T ExceptionToThrow;\n" - end - - def mock_function_declarations(function) - if (function[:args_string] == "void") - return "#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" + - "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n" - else - return "#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" + - "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n" - end - end - - def mock_implementation(function) - " if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" + - " UNITY_CLR_DETAILS();\n" + - " Throw(cmock_call_instance->ExceptionToThrow);\n }\n" - end - - def mock_interfaces(function) - arg_insert = (function[:args_string] == "void") ? "" : "#{function[:args_string]}, " - [ "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n", - @utils.code_add_base_expectation(function[:name]), - @utils.code_call_argument_loader(function), - " cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n", - "}\n\n" ].join - end - -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb deleted file mode 100644 index dcf96f2ed..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect.rb +++ /dev/null @@ -1,103 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGeneratorPluginExpect - - attr_reader :priority - attr_accessor :config, :utils, :unity_helper, :ordered - - def initialize(config, utils) - @config = config - @ptr_handling = @config.when_ptr - @ordered = @config.enforce_strict_ordering - @utils = utils - @unity_helper = @utils.helpers[:unity_helper] - @priority = 5 - - if (@config.plugins.include? :expect_any_args) - alias :mock_implementation :mock_implementation_might_check_args - else - alias :mock_implementation :mock_implementation_always_check_args - end - end - - def instance_typedefs(function) - lines = "" - lines << " #{function[:return][:type]} ReturnVal;\n" unless (function[:return][:void?]) - lines << " int CallOrder;\n" if (@ordered) - function[:args].each do |arg| - lines << " #{arg[:type]} Expected_#{arg[:name]};\n" - end - lines - end - - def mock_function_declarations(function) - if (function[:args].empty?) - if (function[:return][:void?]) - return "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" + - "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n" - else - return "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" + - "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" - end - else - if (function[:return][:void?]) - return "#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" + - "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n" - else - return "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" + - "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n" - end - end - end - - def mock_implementation_always_check_args(function) - lines = "" - function[:args].each do |arg| - lines << @utils.code_verify_an_arg_expectation(function, arg) - end - lines - end - - def mock_implementation_might_check_args(function) - return "" if (function[:args].empty?) - lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n" - function[:args].each do |arg| - lines << @utils.code_verify_an_arg_expectation(function, arg) - end - lines << " }\n" - lines - end - - def mock_interfaces(function) - lines = "" - func_name = function[:name] - if (function[:return][:void?]) - if (function[:args_string] == "void") - lines << "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n" - else - lines << "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n" - end - else - if (function[:args_string] == "void") - lines << "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" - else - lines << "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n" - end - end - lines << @utils.code_add_base_expectation(func_name) - lines << @utils.code_call_argument_loader(function) - lines << @utils.code_assign_argument_quickly("cmock_call_instance->ReturnVal", function[:return]) unless (function[:return][:void?]) - lines << "}\n\n" - end - - def mock_verify(function) - " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" + - " UNITY_TEST_ASSERT(CMOCK_GUTS_NONE == call_instance, cmock_line, CMockStringCalledLess);\n" + - " UNITY_CLR_DETAILS();\n" - end - -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb deleted file mode 100644 index 0c1c74e95..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_expect_any_args.rb +++ /dev/null @@ -1,53 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGeneratorPluginExpectAnyArgs - - attr_reader :priority - attr_reader :config, :utils - - def initialize(config, utils) - @config = config - @utils = utils - @priority = 3 - end - - def instance_typedefs(function) - " int ExpectAnyArgsBool;\n" - end - - def mock_function_declarations(function) - unless (function[:args].empty?) - if (function[:return][:void?]) - return "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" + - "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n" - else - return "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" + - "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" - end - else - "" - end - end - - def mock_interfaces(function) - lines = "" - unless (function[:args].empty?) - if (function[:return][:void?]) - lines << "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n" - else - lines << "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" - end - lines << @utils.code_add_base_expectation(function[:name], true) - unless (function[:return][:void?]) - lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" - end - lines << " cmock_call_instance->ExpectAnyArgsBool = (int)1;\n" - lines << "}\n\n" - end - return lines - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb deleted file mode 100644 index 8f31967a1..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_ignore.rb +++ /dev/null @@ -1,75 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGeneratorPluginIgnore - - attr_reader :priority - attr_reader :config, :utils - - def initialize(config, utils) - @config = config - @utils = utils - @priority = 2 - end - - def instance_structure(function) - if (function[:return][:void?]) - " int #{function[:name]}_IgnoreBool;\n" - else - " int #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" - end - end - - def mock_function_declarations(function) - if (function[:return][:void?]) - return "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" + - "void #{function[:name]}_CMockIgnore(void);\n" - else - return "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" + - "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" - end - end - - def mock_implementation_precheck(function) - lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" - lines << " UNITY_CLR_DETAILS();\n" - if (function[:return][:void?]) - lines << " return;\n }\n" - else - retval = function[:return].merge( { :name => "cmock_call_instance->ReturnVal"} ) - lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" - lines << " " + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless (retval[:void?]) - lines << " return cmock_call_instance->ReturnVal;\n }\n" - end - lines - end - - def mock_interfaces(function) - lines = "" - if (function[:return][:void?]) - lines << "void #{function[:name]}_CMockIgnore(void)\n{\n" - else - lines << "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" - end - if (!function[:return][:void?]) - lines << @utils.code_add_base_expectation(function[:name], false) - end - unless (function[:return][:void?]) - lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" - end - lines << " Mock.#{function[:name]}_IgnoreBool = (int)1;\n" - lines << "}\n\n" - end - - def mock_ignore(function) - " Mock.#{function[:name]}_IgnoreBool = (int) 1;\n" - end - - def mock_verify(function) - func_name = function[:name] - " if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n" - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb deleted file mode 100644 index 1c1af0610..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_plugin_return_thru_ptr.rb +++ /dev/null @@ -1,79 +0,0 @@ -class CMockGeneratorPluginReturnThruPtr - attr_reader :priority - attr_accessor :utils - - def initialize(config, utils) - @utils = utils - @priority = 9 - end - - def instance_typedefs(function) - lines = "" - function[:args].each do |arg| - if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) - lines << " int ReturnThruPtr_#{arg[:name]}_Used;\n" - lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n" - lines << " int ReturnThruPtr_#{arg[:name]}_Size;\n" - end - end - lines - end - - def mock_function_declarations(function) - lines = "" - function[:args].each do |arg| - if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) - lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})" - # If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise - # we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size - if (arg[:type][-1] == '*') - lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n" - else - lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" - end - lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)" - lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, (int)(cmock_len * (int)sizeof(*#{arg[:name]})))\n" - lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)" - lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n" - lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, int cmock_size);\n" - end - end - lines - end - - def mock_interfaces(function) - lines = [] - func_name = function[:name] - function[:args].each do |arg| - arg_name = arg[:name] - if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) - lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, int cmock_size)\n" - lines << "{\n" - lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " + - "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" - lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n" - lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n" - lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n" - lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n" - lines << "}\n\n" - end - end - lines - end - - def mock_implementation(function) - lines = [] - function[:args].each do |arg| - arg_name = arg[:name] - if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) - lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n" - lines << " {\n" - lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" - lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" - lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" - lines << " }\n" - end - end - lines - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb deleted file mode 100644 index 994e85c5d..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_generator_utils.rb +++ /dev/null @@ -1,253 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockGeneratorUtils - - attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception - - def initialize(config, helpers={}) - @config = config - @ptr_handling = @config.when_ptr - @ordered = @config.enforce_strict_ordering - @arrays = @config.plugins.include? :array - @cexception = @config.plugins.include? :cexception - @expect_any = @config.plugins.include? :expect_any_args - @return_thru_ptr = @config.plugins.include? :return_thru_ptr - @ignore_arg = @config.plugins.include? :ignore_arg - @ignore = @config.plugins.include? :ignore - @treat_as = @config.treat_as - @helpers = helpers - end - - def self.arg_type_with_const(arg) - # Restore any "const" that was removed in header parsing - if arg[:type].include?('*') - arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type] - else - arg[:const?] ? "const #{arg[:type]}" : arg[:type] - end - end - - def arg_type_with_const(arg) - self.class.arg_type_with_const(arg) - end - - def code_verify_an_arg_expectation(function, arg) - if (@arrays) - case(@ptr_handling) - when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg) - when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg) - when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option." - end - else - code_verify_an_arg_expectation_with_no_arrays(function, arg) - end - end - - def code_add_base_expectation(func_name, global_ordering_supported=true) - lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" - lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" - lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" - lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" - lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" - lines << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if (@ignore) - lines << " cmock_call_instance->LineNumber = cmock_line;\n" - lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if (@ordered and global_ordering_supported) - lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if (@cexception) - lines << " cmock_call_instance->ExpectAnyArgsBool = (int)0;\n" if (@expect_any) - lines - end - - def code_add_an_arg_expectation(arg, depth=1) - lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg) - lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if (@arrays and (depth.class == String)) - lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if (@ignore_arg) - lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if (@return_thru_ptr and ptr_or_str?(arg[:type]) and not arg[:const?]) - lines - end - - def code_assign_argument_quickly(dest, arg) - if (arg[:ptr?] or @treat_as.include?(arg[:type])) - " #{dest} = #{arg[:name]};\n" - else - assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1" - comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */" - " memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" + - " sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n" - end - end - - def code_add_argument_loader(function) - if (function[:args_string] != "void") - if (@arrays) - args_string = function[:args].map do |m| - type = arg_type_with_const(m) - m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" - end.join(', ') - "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" + - "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" + - function[:args].inject("") { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1) ) } + - "}\n\n" - else - "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" + - "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" + - function[:args].inject("") { |all, arg| all + code_add_an_arg_expectation(arg) } + - "}\n\n" - end - else - "" - end - end - - def code_call_argument_loader(function) - if (function[:args_string] != "void") - args = function[:args].map do |m| - if (@arrays and m[:ptr?] and not m[:array_data?]) - "#{m[:name]}, 1" - elsif (@arrays and m[:array_size?]) - "#{m[:name]}, #{m[:name]}" - else - m[:name] - end - end - " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" - else - "" - end - end - - def ptr_or_str?(arg_type) - return (arg_type.include? '*' or - @treat_as.fetch(arg_type, "").include? '*') - end - - #private ###################### - - def lookup_expect_type(function, arg) - c_type = arg[:type] - arg_name = arg[:name] - expected = "cmock_call_instance->Expected_#{arg_name}" - ignore = "cmock_call_instance->IgnoreArg_#{arg_name}" - unity_func = if ((arg[:ptr?]) and ((c_type =~ /\*\*/) or (@ptr_handling == :compare_ptr))) - ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] - else - (@helpers.nil? or @helpers[:unity_helper].nil?) ? ["UNITY_TEST_ASSERT_EQUAL",''] : @helpers[:unity_helper].get_helper(c_type) - end - return c_type, arg_name, expected, ignore, unity_func[0], unity_func[1] - end - - def code_verify_an_arg_expectation_with_no_arrays(function, arg) - c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) - lines = "" - lines << " if (!#{ignore})\n" if @ignore_arg - lines << " {\n" - lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" - case(unity_func) - when "UNITY_TEST_ASSERT_EQUAL_MEMORY" - c_type_local = c_type.gsub(/\*$/,'') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" - when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" - if (pre == '&') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch); }\n" - end - when /_ARRAY/ - if (pre == '&') - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << " else\n" - lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n" - end - else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" - end - lines << " }\n" - lines - end - - def code_verify_an_arg_expectation_with_normal_arrays(function, arg) - c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) - depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 - lines = "" - lines << " if (!#{ignore})\n" if @ignore_arg - lines << " {\n" - lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" - lines << " if (#{pre}#{expected} != #{pre}#{arg_name}) {\n" - case(unity_func) - when "UNITY_TEST_ASSERT_EQUAL_MEMORY" - c_type_local = c_type.gsub(/\*$/,'') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" - when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" - if (pre == '&') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" - end - when /_ARRAY/ - if (pre == '&') - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << " else\n" - lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" - end - else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" - end - lines << " }\n }\n" - lines - end - - def code_verify_an_arg_expectation_with_smart_arrays(function, arg) - c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) - depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 - lines = "" - lines << " if (!#{ignore})\n" if @ignore_arg - lines << " {\n" - lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" - lines << " if (#{pre}#{expected} != #{pre}#{arg_name}) {\n" - case(unity_func) - when "UNITY_TEST_ASSERT_EQUAL_MEMORY" - c_type_local = c_type.gsub(/\*$/,'') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" - when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" - if (pre == '&') - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << ((depth_name != 1) ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : "") - lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" - end - when /_ARRAY/ - if (pre == '&') - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n" - else - lines << " if (#{pre}#{expected} == NULL)\n" - lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" - lines << ((depth_name != 1) ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : "") - lines << " else\n" - lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" - end - else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" - end - lines << " }\n }\n" - lines - end - -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb deleted file mode 100644 index 0cf19478d..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_header_parser.rb +++ /dev/null @@ -1,364 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockHeaderParser - - attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs - - def initialize(cfg) - @funcs = [] - @c_strippables = cfg.strippables - @c_attr_noconst = cfg.attributes.uniq - ['const'] - @c_attributes = ['const'] + c_attr_noconst - @c_calling_conventions = cfg.c_calling_conventions.uniq - @treat_as_array = cfg.treat_as_array - @treat_as_void = (['void'] + cfg.treat_as_void).uniq - @declaration_parse_matcher = /([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+-]*)\)$/m - @standards = (['int','short','char','long','unsigned','signed'] + cfg.treat_as.keys).uniq - @array_size_name = cfg.array_size_name - @array_size_type = (['int', 'size_t'] + cfg.array_size_type).uniq - @when_no_prototypes = cfg.when_no_prototypes - @local_as_void = @treat_as_void - @verbosity = cfg.verbosity - @treat_externs = cfg.treat_externs - @c_strippables += ['extern'] if (@treat_externs == :include) #we'll need to remove the attribute if we're allowing externs - end - - def parse(name, source) - @module_name = name.gsub(/\W/,'') - @typedefs = [] - @funcs = [] - function_names = [] - - parse_functions( import_source(source) ).map do |decl| - func = parse_declaration(decl) - unless (function_names.include? func[:name]) - @funcs << func - function_names << func[:name] - end - end - - { :includes => nil, - :functions => @funcs, - :typedefs => @typedefs - } - end - - private if $ThisIsOnlyATest.nil? ################ - - def import_source(source) - - # let's clean up the encoding in case they've done anything weird with the characters we might find - source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) - - # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void - # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void - @local_as_void = @treat_as_void - void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/) - if void_types - @local_as_void += void_types.flatten.uniq.compact - end - - # smush multiline macros into single line (checking for continuation character at end of line '\') - source.gsub!(/\s*\\\s*/m, ' ') - - #remove comments (block and line, in three steps to ensure correct precedence) - source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks - source.gsub!(/\/\*.*?\*\//m, '') # remove block comments - source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) - - # remove assembler pragma sections - source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') - - # remove gcc's __attribute__ tags - source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') - - # remove preprocessor statements and extern "C" - source.gsub!(/^\s*#.*/, '') - source.gsub!(/extern\s+\"C\"\s*\{/, '') - - # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them - # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes - source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs - source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces - source.gsub!(/(\W)(?:register|auto|static|restrict)(\W)/, '\1\2') # remove problem keywords - source.gsub!(/\s*=\s*['"a-zA-Z0-9_\.]+\s*/, '') # remove default value statements from argument lists - source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements - source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric - source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/,'\1') unless @c_strippables.empty? # remove known attributes slated to be stripped - - #scan standalone function pointers and remove them, because they can just be ignored - source.gsub!(/\w+\s*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/,';') - - #scan for functions which return function pointers, because they are a pain - source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |m| - functype = "cmock_#{@module_name}_func_ptr#{@typedefs.size + 1}" - @typedefs << "typedef #{$1.strip}(*#{functype})(#{$4});" - "#{functype} #{$2.strip}(#{$3});" - end - - # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) - if (RUBY_VERSION.split('.')[0].to_i > 1) - #we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. - r = "\\{([^\\{\\}]*|\\g<0>)*\\}" - source.gsub!(/#{r}/m, '{ }') - else - while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }') - end - end - - # remove function definitions by stripping off the arguments right now - source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ";") - - #drop extra white space to make the rest go faster - source.gsub!(/^\s+/, '') # remove extra white space from beginning of line - source.gsub!(/\s+$/, '') # remove extra white space from end of line - source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens - source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens - source.gsub!(/\s+/, ' ') # remove remaining extra white space - - #split lines on semicolons and remove things that are obviously not what we are looking for - src_lines = source.split(/\s*;\s*/).uniq - src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines - src_lines.delete_if {|line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil?} #remove function pointer arrays - if (@treat_externs == :include) - src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions - else - src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} # remove inline and extern functions - end - src_lines.delete_if {|line| line.empty? } #drop empty lines - end - - def parse_functions(source) - funcs = [] - source.each {|line| funcs << line.strip.gsub(/\s+/, ' ') if (line =~ @declaration_parse_matcher)} - if funcs.empty? - case @when_no_prototypes - when :error - raise "ERROR: No function prototypes found!" - when :warn - puts "WARNING: No function prototypes found!" unless (@verbosity < 1) - end - end - return funcs - end - - def parse_type_and_name(arg) - # Split up words and remove known attributes. For pointer types, make sure - # to remove 'const' only when it applies to the pointer itself, not when it - # applies to the type pointed to. For non-pointer types, remove any - # occurrence of 'const'. - arg.gsub!(/(\w)\*/,'\1 *') # pull asterisks away from preceding word - arg.gsub!(/\*(\w)/,'* \1') # pull asterisks away from following word - arg_array = arg.split - arg_info = divine_ptr_and_const(arg) - arg_info[:name] = arg_array[-1] - - attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes - attr_array = [] - type_array = [] - - arg_array[0..-2].each do |word| - if attributes.include?(word) - attr_array << word - elsif @c_calling_conventions.include?(word) - arg_info[:c_calling_convention] = word - else - type_array << word - end - end - - if arg_info[:const_ptr?] - attr_array << 'const' - type_array.delete_at(type_array.rindex('const')) - end - - arg_info[:modifier] = attr_array.join(' ') - arg_info[:type] = type_array.join(' ').gsub(/\s+\*/,'*') # remove space before asterisks - return arg_info - end - - def parse_args(arg_list) - args = [] - arg_list.split(',').each do |arg| - arg.strip! - return args if (arg =~ /^\s*((\.\.\.)|(void))\s*$/) # we're done if we reach void by itself or ... - - arg_info = parse_type_and_name(arg) - arg_info.delete(:modifier) # don't care about this - arg_info.delete(:c_calling_convention) # don't care about this - - # in C, array arguments implicitly degrade to pointers - # make the translation explicit here to simplify later logic - if @treat_as_array[arg_info[:type]] and not arg_info[:ptr?] then - arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" - arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] - arg_info[:ptr?] = true - end - - args << arg_info - end - - # Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name> - args.each_with_index {|val, index| - next_index = index + 1 - if (args.length > next_index) - if (val[:ptr?] == true and args[next_index][:name].match(@array_size_name) and @array_size_type.include?(args[next_index][:type])) - val[:array_data?] = true - args[next_index][:array_size?] = true - end - end - } - - return args - end - - def divine_ptr(arg) - return false unless arg.include? '*' - # treat "const char *" and similar as a string, not a pointer - return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg - return true - end - - def divine_const(arg) - # a non-pointer arg containing "const" is a constant - # an arg containing "const" before the last * is a pointer to a constant - return ( arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) - : (/(^|\s)const(\s|$)/ =~ arg) ) ? true : false - end - - def divine_ptr_and_const(arg) - divination = {} - - divination[:ptr?] = divine_ptr(arg) - divination[:const?] = divine_const(arg) - - # an arg containing "const" after the last * is a constant pointer - divination[:const_ptr?] = (/\*(?!.*\*)\s*const(\s|$)/ =~ arg) ? true : false - - return divination - end - - def clean_args(arg_list) - if ((@local_as_void.include?(arg_list.strip)) or (arg_list.empty?)) - return 'void' - else - c=0 - arg_list.gsub!(/(\w+)(?:\s*\[\s*\(*[\s\w+-]*\)*\s*\])+/,'*\1') # magically turn brackets into asterisks, also match for parentheses that come from macros - arg_list.gsub!(/\s+\*/,'*') # remove space to place asterisks with type (where they belong) - arg_list.gsub!(/\*(\w)/,'* \1') # pull asterisks away from arg to place asterisks with type (where they belong) - - #scan argument list for function pointers and replace them with custom types - arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |m| - - functype = "cmock_#{@module_name}_func_ptr#{@typedefs.size + 1}" - funcret = $1.strip - funcname = $2.strip - funcargs = $3.strip - funconst = '' - if (funcname.include? 'const') - funcname.gsub!('const','').strip! - funconst = 'const ' - end - @typedefs << "typedef #{funcret}(*#{functype})(#{funcargs});" - funcname = "cmock_arg#{c+=1}" if (funcname.empty?) - "#{functype} #{funconst}#{funcname}" - end - - #automatically name unnamed arguments (those that only had a type) - arg_list.split(/\s*,\s*/).map { |arg| - parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) - if ((parts.size < 2) or (parts[-1][-1].chr == '*') or (@standards.include?(parts[-1]))) - "#{arg} cmock_arg#{c+=1}" - else - arg - end - }.join(', ') - end - end - - def parse_declaration(declaration) - decl = {} - - regex_match = @declaration_parse_matcher.match(declaration) - raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? - - #grab argument list - args = regex_match[2].strip - - #process function attributes, return type, and name - parsed = parse_type_and_name(regex_match[1]) - - decl[:name] = parsed[:name] - decl[:modifier] = parsed[:modifier] - unless parsed[:c_calling_convention].nil? - decl[:c_calling_convention] = parsed[:c_calling_convention] - end - - rettype = parsed[:type] - rettype = 'void' if (@local_as_void.include?(rettype.strip)) - decl[:return] = { :type => rettype, - :name => 'cmock_to_return', - :str => "#{rettype} cmock_to_return", - :void? => (rettype == 'void'), - :ptr? => parsed[:ptr?], - :const? => parsed[:const?], - :const_ptr? => parsed[:const_ptr?] - } - - #remove default argument statements from mock definitions - args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') - - #check for var args - if (args =~ /\.\.\./) - decl[:var_arg] = args.match( /[\w\s]*\.\.\./ ).to_s.strip - if (args =~ /\,[\w\s]*\.\.\./) - args = args.gsub!(/\,[\w\s]*\.\.\./,'') - else - args = 'void' - end - else - decl[:var_arg] = nil - end - args = clean_args(args) - decl[:args_string] = args - decl[:args] = parse_args(args) - decl[:args_call] = decl[:args].map{|a| a[:name]}.join(', ') - decl[:contains_ptr?] = decl[:args].inject(false) {|ptr, arg| arg[:ptr?] ? true : ptr } - - if (decl[:return][:type].nil? or decl[:name].nil? or decl[:args].nil? or - decl[:return][:type].empty? or decl[:name].empty?) - raise "Failed Parsing Declaration Prototype!\n" + - " declaration: '#{declaration}'\n" + - " modifier: '#{decl[:modifier]}'\n" + - " return: #{prototype_inspect_hash(decl[:return])}\n" + - " function: '#{decl[:name]}'\n" + - " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" - end - - return decl - end - - def prototype_inspect_hash(hash) - pairs = [] - hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if (value.class == String)}#{value}#{"'" if (value.class == String)}" } - return "{#{pairs.join(', ')}}" - end - - def prototype_inspect_array_of_hashes(array) - hashes = [] - array.each { |hash| hashes << prototype_inspect_hash(hash) } - case (array.size) - when 0 - return "[]" - when 1 - return "[#{hashes[0]}]" - else - return "[\n #{hashes.join("\n ")}\n ]\n" - end - end - -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb deleted file mode 100644 index cc5ced2ad..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_plugin_manager.rb +++ /dev/null @@ -1,55 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -require 'thread' - -class CMockPluginManager - - attr_accessor :plugins - - def initialize(config, utils) - @plugins = [] - plugins_to_load = [:expect, config.plugins].flatten.uniq.compact - plugins_to_load.each do |plugin| - plugin_name = plugin.to_s - object_name = "CMockGeneratorPlugin" + camelize(plugin_name) - self.class.plugin_require_mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) } - end - @plugins.sort! {|a,b| a.priority <=> b.priority } - end - - def run(method, args=nil) - if args.nil? - return @plugins.collect{ |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join - else - return @plugins.collect{ |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join - end - end - - def camelize(lower_case_and_underscored_word) - lower_case_and_underscored_word.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } - end - - private - - def self.plugin_require_mutex - @mutex ||= Mutex.new - end - - def load_plugin(plugin_name, object_name, config, utils) - begin - unless (Object.const_defined? object_name) - file_name = "#{File.expand_path(File.dirname(__FILE__))}/cmock_generator_plugin_#{plugin_name.downcase}.rb" - require file_name - end - class_name = Object.const_get(object_name) - @plugins << class_name.new(config, utils) - rescue - file_name = "#{File.expand_path(File.dirname(__FILE__))}/cmock_generator_plugin_#{plugin_name.downcase}.rb" - raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}" - end - end -end diff --git a/test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb b/test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb deleted file mode 100644 index c22db7aa9..000000000 --- a/test/vendor/ceedling/vendor/cmock/lib/cmock_unityhelper_parser.rb +++ /dev/null @@ -1,75 +0,0 @@ -# ========================================== -# CMock Project - Automatic Mock Generation for C -# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams -# [Released under MIT License. Please refer to license.txt for details] -# ========================================== - -class CMockUnityHelperParser - - attr_accessor :c_types - - def initialize(config) - @config = config - @fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY' - @c_types = map_C_types.merge(import_source) - end - - def get_helper(ctype) - lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/,'\1\3\5\6').strip.gsub(/\s+/,'_') - return [@c_types[lookup], ''] if (@c_types[lookup]) - if (lookup =~ /\*$/) - lookup = lookup.gsub(/\*$/,'') - return [@c_types[lookup], '*'] if (@c_types[lookup]) - else - lookup = lookup + '*' - return [@c_types[lookup], '&'] if (@c_types[lookup]) - end - return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if (ctype =~ /cmock_\w+_ptr\d+/) - raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown - return (lookup =~ /\*$/) ? [@fallback, '&'] : [@fallback, ''] - end - - private ########################### - - def map_C_types - c_types = {} - @config.treat_as.each_pair do |ctype, expecttype| - c_type = ctype.gsub(/\s+/,'_') - if (expecttype =~ /\*/) - c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.gsub(/\*/,'')}_ARRAY" - else - c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}" - c_types[c_type+'*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY" - end - end - c_types - end - - def import_source - source = @config.load_unity_helper - return {} if source.nil? - c_types = {} - source = source.gsub(/\/\/.*$/, '') #remove line comments - source = source.gsub(/\/\*.*?\*\//m, '') #remove block comments - - #scan for comparison helpers - match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4,'\s*\w+\s*').join(',') + '\)') - pairs = source.scan(match_regex).flatten.compact - (pairs.size/2).times do |i| - expect = pairs[i*2] - ctype = pairs[(i*2)+1] - c_types[ctype] = expect unless expect.include?("_ARRAY") - end - - #scan for array variants of those helpers - match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5,'\s*\w+\s*').join(',') + '\)') - pairs = source.scan(match_regex).flatten.compact - (pairs.size/2).times do |i| - expect = pairs[i*2] - ctype = pairs[(i*2)+1] - c_types[ctype.gsub('_ARRAY','*')] = expect - end - - c_types - end -end diff --git a/test/vendor/ceedling/vendor/cmock/release/build.info b/test/vendor/ceedling/vendor/cmock/release/build.info deleted file mode 100644 index a62fe1172..000000000 --- a/test/vendor/ceedling/vendor/cmock/release/build.info +++ /dev/null @@ -1,2 +0,0 @@ -217 - diff --git a/test/vendor/ceedling/vendor/cmock/release/version.info b/test/vendor/ceedling/vendor/cmock/release/version.info deleted file mode 100644 index 1b6799f80..000000000 --- a/test/vendor/ceedling/vendor/cmock/release/version.info +++ /dev/null @@ -1,2 +0,0 @@ -2.4.6 - diff --git a/test/vendor/ceedling/vendor/cmock/src/meson.build b/test/vendor/ceedling/vendor/cmock/src/meson.build deleted file mode 100644 index e375e81a1..000000000 --- a/test/vendor/ceedling/vendor/cmock/src/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -################################################################################### -# # -# NAME: meson.build # -# # -# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. # -# WRITTEN BY: Michael Brockus. # -# # -# License: MIT # -# # -################################################################################### - -cmock_dir = include_directories('.') - -cmock_lib = static_library(meson.project_name(), - sources: ['cmock.c'], - dependencies: [unity_dep], - include_directories: cmock_dir) diff --git a/test/vendor/ceedling/vendor/deep_merge/lib/deep_merge.rb b/test/vendor/ceedling/vendor/deep_merge/lib/deep_merge.rb deleted file mode 100644 index 4c4b7610b..000000000 --- a/test/vendor/ceedling/vendor/deep_merge/lib/deep_merge.rb +++ /dev/null @@ -1,211 +0,0 @@ -module DeepMerge - - MAJOR_VERSION = 0 - MINOR_VERSION = 1 - FIX_VERSION = 0 - VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}.#{FIX_VERSION}" - - class InvalidParameter < StandardError; end - - DEFAULT_FIELD_KNOCKOUT_PREFIX = '--' - - module DeepMergeHash - # ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX - def ko_deep_merge!(source, options = {}) - default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false} - DeepMerge::deep_merge!(source, self, default_opts.merge(options)) - end - - # deep_merge! will merge and overwrite any unmergeables in destination hash - def deep_merge!(source, options = {}) - default_opts = {:preserve_unmergeables => false} - DeepMerge::deep_merge!(source, self, default_opts.merge(options)) - end - - # deep_merge will merge and skip any unmergeables in destination hash - def deep_merge(source, options = {}) - default_opts = {:preserve_unmergeables => true} - DeepMerge::deep_merge!(source, self, default_opts.merge(options)) - end - - end # DeepMergeHashExt - - # Deep Merge core documentation. - # deep_merge! method permits merging of arbitrary child elements. The two top level - # elements must be hashes. These hashes can contain unlimited (to stack limit) levels - # of child elements. These child elements to not have to be of the same types. - # Where child elements are of the same type, deep_merge will attempt to merge them together. - # Where child elements are not of the same type, deep_merge will skip or optionally overwrite - # the destination element with the contents of the source element at that level. - # So if you have two hashes like this: - # source = {:x => [1,2,3], :y => 2} - # dest = {:x => [4,5,'6'], :y => [7,8,9]} - # dest.deep_merge!(source) - # Results: {:x => [1,2,3,4,5,'6'], :y => 2} - # By default, "deep_merge!" will overwrite any unmergeables and merge everything else. - # To avoid this, use "deep_merge" (no bang/exclamation mark) - # - # Options: - # Options are specified in the last parameter passed, which should be in hash format: - # hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'}) - # :preserve_unmergeables DEFAULT: false - # Set to true to skip any unmergeable elements from source - # :knockout_prefix DEFAULT: nil - # Set to string value to signify prefix which deletes elements from existing element - # :sort_merged_arrays DEFAULT: false - # Set to true to sort all arrays that are merged together - # :unpack_arrays DEFAULT: nil - # Set to string value to run "Array::join" then "String::split" against all arrays - # :merge_debug DEFAULT: false - # Set to true to get console output of merge process for debugging - # - # Selected Options Details: - # :knockout_prefix => The purpose of this is to provide a way to remove elements - # from existing Hash by specifying them in a special way in incoming hash - # source = {:x => ['--1', '2']} - # dest = {:x => ['1', '3']} - # dest.ko_deep_merge!(source) - # Results: {:x => ['2','3']} - # Additionally, if the knockout_prefix is passed alone as a string, it will cause - # the entire element to be removed: - # source = {:x => '--'} - # dest = {:x => [1,2,3]} - # dest.ko_deep_merge!(source) - # Results: {:x => ""} - # :unpack_arrays => The purpose of this is to permit compound elements to be passed - # in as strings and to be converted into discrete array elements - # irsource = {:x => ['1,2,3', '4']} - # dest = {:x => ['5','6','7,8']} - # dest.deep_merge!(source, {:unpack_arrays => ','}) - # Results: {:x => ['1','2','3','4','5','6','7','8'} - # Why: If receiving data from an HTML form, this makes it easy for a checkbox - # to pass multiple values from within a single HTML element - # - # There are many tests for this library - and you can learn more about the features - # and usages of deep_merge! by just browsing the test examples - def DeepMerge.deep_merge!(source, dest, options = {}) - # turn on this line for stdout debugging text - merge_debug = options[:merge_debug] || false - overwrite_unmergeable = !options[:preserve_unmergeables] - knockout_prefix = options[:knockout_prefix] || nil - if knockout_prefix == "" then raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!"; end - if knockout_prefix && !overwrite_unmergeable then raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!"; end - # if present: we will split and join arrays on this char before merging - array_split_char = options[:unpack_arrays] || false - # request that we sort together any arrays when they are merged - sort_merged_arrays = options[:sort_merged_arrays] || false - di = options[:debug_indent] || '' - # do nothing if source is nil - if source.nil? || (source.respond_to?(:blank?) && source.blank?) then return dest; end - # if dest doesn't exist, then simply copy source to it - if dest.nil? && overwrite_unmergeable then dest = source; return dest; end - - puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug - if source.kind_of?(Hash) - puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug - source.each do |src_key, src_value| - if dest.kind_of?(Hash) - puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug - if not dest[src_key].nil? - puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug - dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' ')) - else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!) - puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug - # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others) - begin - src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty) - rescue TypeError - src_dup = src_value - end - dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' ')) - end - else # dest isn't a hash, so we overwrite it completely (if permitted) - if overwrite_unmergeable - puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug - dest = overwrite_unmergeables(source, dest, options) - end - end - end - elsif source.kind_of?(Array) - puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug - # if we are instructed, join/split any source arrays before processing - if array_split_char - puts "#{di} split/join on source: #{source.inspect}" if merge_debug - source = source.join(array_split_char).split(array_split_char) - if dest.kind_of?(Array) then dest = dest.join(array_split_char).split(array_split_char); end - end - # if there's a naked knockout_prefix in source, that means we are to truncate dest - if source.index(knockout_prefix) then dest = clear_or_nil(dest); source.delete(knockout_prefix); end - if dest.kind_of?(Array) - if knockout_prefix - print "#{di} knocking out: " if merge_debug - # remove knockout prefix items from both source and dest - source.delete_if do |ko_item| - retval = false - item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item - if item != ko_item - print "#{ko_item} - " if merge_debug - dest.delete(item) - dest.delete(ko_item) - retval = true - end - retval - end - puts if merge_debug - end - puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug - dest = dest | source - if sort_merged_arrays then dest.sort!; end - elsif overwrite_unmergeable - puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug - dest = overwrite_unmergeables(source, dest, options) - end - else # src_hash is not an array or hash, so we'll have to overwrite dest - puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug - dest = overwrite_unmergeables(source, dest, options) - end - puts "#{di}Returning #{dest.inspect}" if merge_debug - dest - end # deep_merge! - - # allows deep_merge! to uniformly handle overwriting of unmergeable entities - def DeepMerge::overwrite_unmergeables(source, dest, options) - merge_debug = options[:merge_debug] || false - overwrite_unmergeable = !options[:preserve_unmergeables] - knockout_prefix = options[:knockout_prefix] || false - di = options[:debug_indent] || '' - if knockout_prefix && overwrite_unmergeable - if source.kind_of?(String) # remove knockout string from source before overwriting dest - src_tmp = source.gsub(%r{^#{knockout_prefix}},"") - elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest - src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) } - else - src_tmp = source - end - if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest - puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug - dest = src_tmp - else # if we do find a knockout_prefix, then we just delete dest - puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug - dest = "" - end - elsif overwrite_unmergeable - dest = source - end - dest - end - - def DeepMerge::clear_or_nil(obj) - if obj.respond_to?(:clear) - obj.clear - else - obj = nil - end - obj - end - -end # module DeepMerge - -class Hash - include DeepMerge::DeepMergeHash -end diff --git a/test/vendor/ceedling/vendor/unity/release/build.info b/test/vendor/ceedling/vendor/unity/release/build.info deleted file mode 100644 index 56d591286..000000000 --- a/test/vendor/ceedling/vendor/unity/release/build.info +++ /dev/null @@ -1,2 +0,0 @@ -122 - diff --git a/test/vendor/ceedling/vendor/unity/release/version.info b/test/vendor/ceedling/vendor/unity/release/version.info deleted file mode 100644 index cf12b30d2..000000000 --- a/test/vendor/ceedling/vendor/unity/release/version.info +++ /dev/null @@ -1,2 +0,0 @@ -2.4.3 - diff --git a/test/vendor/ceedling/vendor/unity/src/CMakeLists.txt b/test/vendor/ceedling/vendor/unity/src/CMakeLists.txt deleted file mode 100644 index c747cb0ae..000000000 --- a/test/vendor/ceedling/vendor/unity/src/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -################################################################################### -# # -# NAME: CMakeLists.txt # -# # -# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. # -# WRITTEN BY: Michael Brockus. # -# # -# License: MIT # -# # -################################################################################### -cmake_minimum_required(VERSION 3.0 FATAL_ERROR) - - -add_library(unity STATIC "unity.c") - -install(TARGETS unity EXPORT unityConfig - ARCHIVE DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_LIBDIR}" - LIBRARY DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_LIBDIR}") - - diff --git a/test/vendor/ceedling/vendor/unity/src/meson.build b/test/vendor/ceedling/vendor/unity/src/meson.build deleted file mode 100644 index f5e014653..000000000 --- a/test/vendor/ceedling/vendor/unity/src/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -################################################################################### -# # -# NAME: meson.build # -# # -# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. # -# WRITTEN BY: Michael Brockus. # -# # -# License: MIT # -# # -################################################################################### - -unity_dir = include_directories('.') - -unity_lib = static_library(meson.project_name(), - sources: ['unity.c'], - include_directories: unity_dir) diff --git a/tools/pcapng_to_corpus.py b/tools/pcapng_to_corpus.py new file mode 100755 index 000000000..78b1f77a2 --- /dev/null +++ b/tools/pcapng_to_corpus.py @@ -0,0 +1,46 @@ +#!/bin/python3 +import argparse +import pcapng +import zipfile +import hashlib + +def extract_packets(pcap_file): + """Reads a wireshark packet capture and extracts the binary packets""" + packets = [] + with open(pcap_file, 'rb') as fp: + scanner = pcapng.FileScanner(fp) + for block in scanner: + if isinstance(block, pcapng.blocks.EnhancedPacket): + packets.append(block.packet_data) + return packets + +def build_corpus_zip(zip_file_output, packets): + """Builds a zip file with a file per packet + + The structure of this zip corpus is a simple content addressable storage + i.e. seed_file_name == sha256_digest(packet). + """ + with zipfile.ZipFile(zip_file_output, 'a') as out: + for packet in packets: + hash = hashlib.sha256(packet).hexdigest() + if hash not in out.namelist(): + out.writestr(hash, packet) + + +def main(pcap_file, output_zip_file): + packets = extract_packets(pcap_file) + build_corpus_zip(output_zip_file, packets) + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog = "pcapng_to_corpus.py", + description="""Converts a wireshark capture to a zip of binary packet + files suitable for an oss-fuzz corpus. In the case the + zip corpus already exists, this script will modify + the zip file in place adding seed entries.""") + parser.add_argument('pcapng_capture_file') + parser.add_argument('oss_fuzz_corpus_zip') + args = parser.parse_args() + main(args.pcapng_capture_file, args.oss_fuzz_corpus_zip) + +