diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/adf4382a_manager.c b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/adf4382a_manager.c index ee49016..128685f 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/adf4382a_manager.c +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/adf4382a_manager.c @@ -1,4 +1,5 @@ #include "adf4382a_manager.h" +#include "stm32_spi.h" #include "diag_log.h" #include "no_os_delay.h" #include @@ -12,7 +13,7 @@ extern SPI_HandleTypeDef hspi4; extern TIM_HandleTypeDef htim3; // Static function prototypes -static void set_chip_enable(uint8_t ce_pin, bool state); +static void set_chip_enable(uint16_t ce_pin, bool state); static void set_deladj_pin(uint8_t device, bool state); static void set_delstr_pin(uint8_t device, bool state); static void start_deladj_pwm(uint8_t device, uint16_t duty_cycle); @@ -24,6 +25,10 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method) struct adf4382_init_param tx_param, rx_param; int ret; uint32_t t_start = HAL_GetTick(); + + /* Platform SPI extras carry HAL handle + software CS port/pin */ + static stm32_spi_extra spi_tx_extra; + static stm32_spi_extra spi_rx_extra; DIAG_SECTION("ADF4382A LO MANAGER INIT"); DIAG("LO", "Init called with sync_method=%d (%s)", @@ -48,14 +53,23 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method) memset(&manager->spi_tx_param, 0, sizeof(manager->spi_tx_param)); memset(&manager->spi_rx_param, 0, sizeof(manager->spi_rx_param)); + // Setup platform SPI extras with software CS for each device + spi_tx_extra.hspi = &hspi4; + spi_tx_extra.cs_port = TX_CS_GPIO_Port; + spi_tx_extra.cs_pin = TX_CS_Pin; + + spi_rx_extra.hspi = &hspi4; + spi_rx_extra.cs_port = RX_CS_GPIO_Port; + spi_rx_extra.cs_pin = RX_CS_Pin; + // Setup TX SPI parameters for SPI4 manager->spi_tx_param.device_id = ADF4382A_SPI_DEVICE_ID; manager->spi_tx_param.max_speed_hz = ADF4382A_SPI_SPEED_HZ; manager->spi_tx_param.mode = NO_OS_SPI_MODE_0; manager->spi_tx_param.chip_select = TX_CS_Pin; manager->spi_tx_param.bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST; - manager->spi_tx_param.platform_ops = NULL; - manager->spi_tx_param.extra = &hspi4; + manager->spi_tx_param.platform_ops = &stm32_spi_ops; + manager->spi_tx_param.extra = &spi_tx_extra; // Setup RX SPI parameters for SPI4 manager->spi_rx_param.device_id = ADF4382A_SPI_DEVICE_ID; @@ -63,11 +77,12 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method) manager->spi_rx_param.mode = NO_OS_SPI_MODE_0; manager->spi_rx_param.chip_select = RX_CS_Pin; manager->spi_rx_param.bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST; - manager->spi_rx_param.platform_ops = NULL; - manager->spi_rx_param.extra = &hspi4; + manager->spi_rx_param.platform_ops = &stm32_spi_ops; + manager->spi_rx_param.extra = &spi_rx_extra; - DIAG("LO", "SPI4 params: TX_CS=0x%04X RX_CS=0x%04X speed=%lu Hz", - TX_CS_Pin, RX_CS_Pin, (unsigned long)ADF4382A_SPI_SPEED_HZ); + DIAG("LO", "SPI4 params: TX_CS=0x%04X RX_CS=0x%04X speed=%lu Hz platform_ops=%p", + TX_CS_Pin, RX_CS_Pin, (unsigned long)ADF4382A_SPI_SPEED_HZ, + (const void*)manager->spi_tx_param.platform_ops); // Configure TX parameters (10.5 GHz) memset(&tx_param, 0, sizeof(tx_param)); @@ -651,7 +666,7 @@ int ADF4382A_StrobePhaseShift(ADF4382A_Manager *manager, uint8_t device) // Static helper functions -static void set_chip_enable(uint8_t ce_pin, bool state) +static void set_chip_enable(uint16_t ce_pin, bool state) { GPIO_TypeDef* port = (ce_pin == TX_CE_Pin) ? TX_CE_GPIO_Port : RX_CE_GPIO_Port; HAL_GPIO_WritePin(port, ce_pin, state ? GPIO_PIN_SET : GPIO_PIN_RESET); diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/no_os_spi.h b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/no_os_spi.h index 9aa77cc..c889871 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/no_os_spi.h +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/no_os_spi.h @@ -130,8 +130,8 @@ struct no_os_spi_init_param { uint32_t device_id; /** maximum transfer speed */ uint32_t max_speed_hz; - /** SPI chip select */ - uint8_t chip_select; + /** SPI chip select (widened to uint16_t for STM32 GPIO_PIN_xx masks) */ + uint16_t chip_select; /** SPI mode */ enum no_os_spi_mode mode; /** SPI bit order */ @@ -184,8 +184,8 @@ struct no_os_spi_desc { uint32_t device_id; /** maximum transfer speed */ uint32_t max_speed_hz; - /** SPI chip select */ - uint8_t chip_select; + /** SPI chip select (widened to uint16_t for STM32 GPIO_PIN_xx masks) */ + uint16_t chip_select; /** SPI mode */ enum no_os_spi_mode mode; /** SPI bit order */ diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.c b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.c index 57c0acf..e2d26b0 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.c +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.c @@ -1,6 +1,18 @@ #include "stm32_spi.h" #include "no_os_error.h" #include +#include + +/** + * @brief Detect whether 'extra' points to a stm32_spi_extra struct (with + * cs_port != NULL) or is a bare SPI_HandleTypeDef* (legacy path). + * + * Heuristic: if cs_port is a valid-looking pointer (non-NULL, non-trivial) + * we treat extra as stm32_spi_extra. Legacy callers pass &hspi4 directly, + * whose first field (Instance) is a peripheral base address — never a small + * number, but also never a GPIO_TypeDef*. We use the struct's own cs_port + * field to discriminate: stm32_spi_extra always sets cs_port explicitly. + */ int32_t stm32_spi_init(struct no_os_spi_desc **desc, const struct no_os_spi_init_param *param) @@ -12,10 +24,30 @@ int32_t stm32_spi_init(struct no_os_spi_desc **desc, if (!*desc) return -ENOMEM; - /* store platform handle (HAL SPI_HandleTypeDef*) in extra */ - (*desc)->extra = param->extra; + /* + * If the caller provides a stm32_spi_extra with cs_port set, allocate + * a copy so the descriptor owns the data. Otherwise, store the raw + * SPI_HandleTypeDef* for backward compatibility. + */ + const stm32_spi_extra *in_extra = (const stm32_spi_extra *)param->extra; + if (in_extra && in_extra->cs_port != NULL) { + /* Caller provided full stm32_spi_extra with software CS */ + stm32_spi_extra *own = calloc(1, sizeof(stm32_spi_extra)); + if (!own) { + free(*desc); + *desc = NULL; + return -ENOMEM; + } + memcpy(own, in_extra, sizeof(stm32_spi_extra)); + (*desc)->extra = own; + } else { + /* Legacy: extra is a bare SPI_HandleTypeDef* */ + (*desc)->extra = param->extra; + } + (*desc)->max_speed_hz = param->max_speed_hz; (*desc)->mode = param->mode; + (*desc)->chip_select = param->chip_select; return 0; } @@ -27,11 +59,39 @@ int32_t stm32_spi_write_and_read(struct no_os_spi_desc *desc, if (!desc || !data || bytes_number == 0) return -EINVAL; - SPI_HandleTypeDef *hspi = (SPI_HandleTypeDef *)desc->extra; + SPI_HandleTypeDef *hspi; + GPIO_TypeDef *cs_port = NULL; + uint16_t cs_pin = 0; + + /* + * Determine HAL handle and optional CS info. + * If extra is a stm32_spi_extra with cs_port set, use its fields. + * Otherwise treat extra as a bare SPI_HandleTypeDef*. + */ + const stm32_spi_extra *sx = (const stm32_spi_extra *)desc->extra; + if (sx && sx->cs_port != NULL) { + hspi = sx->hspi; + cs_port = sx->cs_port; + cs_pin = sx->cs_pin; + } else { + hspi = (SPI_HandleTypeDef *)desc->extra; + } + if (!hspi) return -EINVAL; - if (HAL_SPI_TransmitReceive(hspi, data, data, bytes_number, 200) != HAL_OK) + /* Assert CS (active low) */ + if (cs_port) + HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); + + HAL_StatusTypeDef hal_ret; + hal_ret = HAL_SPI_TransmitReceive(hspi, data, data, bytes_number, 200); + + /* Deassert CS */ + if (cs_port) + HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); + + if (hal_ret != HAL_OK) return -EIO; return 0; @@ -41,6 +101,17 @@ int32_t stm32_spi_remove(struct no_os_spi_desc *desc) { if (!desc) return -EINVAL; + + /* + * If we allocated a stm32_spi_extra copy during init, free it. + * Detect by checking cs_port (same heuristic as init). + */ + if (desc->extra) { + const stm32_spi_extra *sx = (const stm32_spi_extra *)desc->extra; + if (sx->cs_port != NULL) + free(desc->extra); + } + free(desc); return 0; } diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.h b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.h index 7a85977..25b8656 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.h +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/stm32_spi.h @@ -4,6 +4,24 @@ #include "no_os_spi.h" #include "stm32f7xx_hal.h" +/** + * @struct stm32_spi_extra + * @brief Platform-specific SPI data for STM32. + * + * When software chip-select is needed (e.g. multiple devices sharing one SPI + * bus with GPIO-managed CS), set cs_port to the GPIO port and cs_pin to the + * GPIO pin mask. stm32_spi_write_and_read() will assert CS LOW before the + * transfer and deassert CS HIGH after. + * + * If cs_port is NULL (legacy usage where the caller passes a bare + * SPI_HandleTypeDef* as the extra pointer), CS management is skipped. + */ +typedef struct { + SPI_HandleTypeDef *hspi; /**< HAL SPI handle */ + GPIO_TypeDef *cs_port; /**< GPIO port for software CS (NULL = no SW CS) */ + uint16_t cs_pin; /**< GPIO pin mask for software CS */ +} stm32_spi_extra; + extern const struct no_os_spi_platform_ops stm32_spi_ops; #endif /* _STM32_SPI_H_ */ diff --git a/9_Firmware/9_1_Microcontroller/tests/.gitignore b/9_Firmware/9_1_Microcontroller/tests/.gitignore index efe0690..60e0313 100644 --- a/9_Firmware/9_1_Microcontroller/tests/.gitignore +++ b/9_Firmware/9_1_Microcontroller/tests/.gitignore @@ -11,3 +11,5 @@ test_bug5_fine_phase_gpio_only test_bug6_timer_variable_collision test_bug7_gpio_pin_conflict test_bug8_uart_commented_out +test_bug9_platform_ops_null +test_bug10_spi_cs_not_toggled diff --git a/9_Firmware/9_1_Microcontroller/tests/Makefile b/9_Firmware/9_1_Microcontroller/tests/Makefile index dd4a56e..4ce20e1 100644 --- a/9_Firmware/9_1_Microcontroller/tests/Makefile +++ b/9_Firmware/9_1_Microcontroller/tests/Makefile @@ -1,7 +1,7 @@ ################################################################################ # Makefile -- MCU firmware unit test harness for AERIS-10 # -# Builds and runs host-side (macOS) tests for the 8 discovered firmware bugs. +# Builds and runs host-side (macOS) tests for the 10 discovered firmware bugs. # Uses mock HAL + spy/recording pattern to test real firmware code without # hardware. # @@ -34,7 +34,9 @@ REAL_OBJ := adf4382a_manager.o TESTS_WITH_REAL := test_bug1_timed_sync_init_ordering \ test_bug3_timed_sync_noop \ test_bug4_phase_shift_before_check \ - test_bug5_fine_phase_gpio_only + test_bug5_fine_phase_gpio_only \ + test_bug9_platform_ops_null \ + test_bug10_spi_cs_not_toggled # Tests that only need mocks (extracted patterns / static analysis) TESTS_MOCK_ONLY := test_bug2_ad9523_double_setup \ @@ -44,7 +46,7 @@ TESTS_MOCK_ONLY := test_bug2_ad9523_double_setup \ ALL_TESTS := $(TESTS_WITH_REAL) $(TESTS_MOCK_ONLY) -.PHONY: all build test clean $(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8) +.PHONY: all build test clean $(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8 bug9 bug10) all: build test @@ -52,7 +54,7 @@ build: $(ALL_TESTS) test: build @echo "===============================================" - @echo " Running all 8 bug tests..." + @echo " Running all 10 bug tests..." @echo "===============================================" @pass=0; fail=0; \ for t in $(ALL_TESTS); do \ @@ -125,6 +127,12 @@ test_bug7: test_bug7_gpio_pin_conflict test_bug8: test_bug8_uart_commented_out ./test_bug8_uart_commented_out +test_bug9: test_bug9_platform_ops_null + ./test_bug9_platform_ops_null + +test_bug10: test_bug10_spi_cs_not_toggled + ./test_bug10_spi_cs_not_toggled + # --- Clean --- clean: diff --git a/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h index c56f5f7..551ee11 100644 --- a/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h +++ b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h @@ -47,7 +47,7 @@ struct no_os_spi_platform_ops { struct no_os_spi_init_param { uint32_t device_id; uint32_t max_speed_hz; - uint8_t chip_select; + uint16_t chip_select; enum no_os_spi_mode mode; enum no_os_spi_bit_order bit_order; enum no_os_spi_lanes lanes; diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/stm32_spi.h b/9_Firmware/9_1_Microcontroller/tests/shims/stm32_spi.h new file mode 100644 index 0000000..35ce1d8 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/stm32_spi.h @@ -0,0 +1,26 @@ +/* shim: stm32_spi.h -- provides stm32_spi_extra type and stm32_spi_ops mock + * + * The real stm32_spi.h includes stm32f7xx_hal.h which we can't use in tests. + * This shim provides the stm32_spi_extra struct and a mock stm32_spi_ops + * extern so that adf4382a_manager.c compiles against test infrastructure. + */ +#ifndef STM32_SPI_H_SHIM +#define STM32_SPI_H_SHIM + +#include "stm32_hal_mock.h" +#include "ad_driver_mock.h" + +/** + * @struct stm32_spi_extra + * @brief Platform-specific SPI data for STM32 (test mock version). + */ +typedef struct { + SPI_HandleTypeDef *hspi; /**< HAL SPI handle */ + GPIO_TypeDef *cs_port; /**< GPIO port for software CS (NULL = no SW CS) */ + uint16_t cs_pin; /**< GPIO pin mask for software CS */ +} stm32_spi_extra; + +/* Mock stm32_spi_ops -- declared in stm32_hal_mock.c */ +extern const struct no_os_spi_platform_ops stm32_spi_ops; + +#endif /* STM32_SPI_H_SHIM */ diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c index f0c7bcd..1cec65f 100644 --- a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c +++ b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c @@ -2,6 +2,7 @@ * stm32_hal_mock.c -- Spy/recording implementation of STM32 HAL stubs ******************************************************************************/ #include "stm32_hal_mock.h" +#include "ad_driver_mock.h" #include #include @@ -262,3 +263,14 @@ void mock_tim_set_compare(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t Co .extra = htim }); } + +/* ========================= Mock stm32_spi_ops ===================== */ + +/* Stub SPI platform ops -- real adf4382a_manager.c references &stm32_spi_ops. + * In tests, adf4382_init() is mocked so no_os_spi_init() is never called. + * We provide a non-NULL struct so tests can assert platform_ops != NULL. */ +static int mock_spi_init_stub(void) { return 0; } + +const struct no_os_spi_platform_ops stm32_spi_ops = { + .init = mock_spi_init_stub, +}; diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug10_spi_cs_not_toggled.c b/9_Firmware/9_1_Microcontroller/tests/test_bug10_spi_cs_not_toggled.c new file mode 100644 index 0000000..581c6a1 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug10_spi_cs_not_toggled.c @@ -0,0 +1,94 @@ +/******************************************************************************* + * test_bug10_spi_cs_not_toggled.c + * + * Bug #10 (FIXED): stm32_spi_write_and_read() never toggled chip select. + * Since TX and RX ADF4382A share SPI4, every register write hit BOTH PLLs + * simultaneously. + * + * Post-fix behavior: + * 1. Manager_Init creates stm32_spi_extra structs with the correct CS + * port (GPIOG) and pin for each device (TX_CS_Pin, RX_CS_Pin). + * 2. spi_param.extra points to a stm32_spi_extra with cs_port != NULL, + * so stm32_spi_write_and_read() will assert/deassert CS around + * each transfer. + * + * Note: The actual SPI CS toggling is in stm32_spi.c at the platform level. + * This test verifies that the manager correctly provisions the CS metadata + * that stm32_spi.c uses. + ******************************************************************************/ +#include "adf4382a_manager.h" +#include "stm32_spi.h" +#include +#include + +int main(void) +{ + ADF4382A_Manager mgr; + int ret; + + printf("=== Bug #10 (FIXED): SPI CS not toggled ===\n"); + + /* ---- Test A: Init succeeds ---- */ + spy_reset(); + ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED); + + printf(" Manager_Init returned: %d (expected 0=OK)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + printf(" PASS: Init returned OK\n"); + + /* ---- Test B: TX extra is non-NULL ---- */ + printf(" spi_tx_param.extra = %p (expected non-NULL)\n", mgr.spi_tx_param.extra); + assert(mgr.spi_tx_param.extra != NULL); + printf(" PASS: TX extra is non-NULL\n"); + + /* ---- Test C: TX extra has correct CS port and pin ---- */ + stm32_spi_extra *tx_extra = (stm32_spi_extra *)mgr.spi_tx_param.extra; + printf(" TX cs_port = %p (expected GPIOG = %p)\n", (void *)tx_extra->cs_port, (void *)GPIOG); + assert(tx_extra->cs_port == GPIOG); + printf(" PASS: TX cs_port == GPIOG\n"); + + printf(" TX cs_pin = 0x%04X (expected TX_CS_Pin = 0x%04X)\n", tx_extra->cs_pin, TX_CS_Pin); + assert(tx_extra->cs_pin == TX_CS_Pin); /* GPIO_PIN_14 = 0x4000 */ + printf(" PASS: TX cs_pin == TX_CS_Pin (GPIO_PIN_14)\n"); + + /* ---- Test D: TX extra has correct SPI handle ---- */ + printf(" TX hspi = %p (expected &hspi4 = %p)\n", (void *)tx_extra->hspi, (void *)&hspi4); + assert(tx_extra->hspi == &hspi4); + printf(" PASS: TX hspi == &hspi4\n"); + + /* ---- Test E: RX extra is non-NULL ---- */ + printf(" spi_rx_param.extra = %p (expected non-NULL)\n", mgr.spi_rx_param.extra); + assert(mgr.spi_rx_param.extra != NULL); + printf(" PASS: RX extra is non-NULL\n"); + + /* ---- Test F: RX extra has correct CS port and pin ---- */ + stm32_spi_extra *rx_extra = (stm32_spi_extra *)mgr.spi_rx_param.extra; + printf(" RX cs_port = %p (expected GPIOG = %p)\n", (void *)rx_extra->cs_port, (void *)GPIOG); + assert(rx_extra->cs_port == GPIOG); + printf(" PASS: RX cs_port == GPIOG\n"); + + printf(" RX cs_pin = 0x%04X (expected RX_CS_Pin = 0x%04X)\n", rx_extra->cs_pin, RX_CS_Pin); + assert(rx_extra->cs_pin == RX_CS_Pin); /* GPIO_PIN_10 = 0x0400 */ + printf(" PASS: RX cs_pin == RX_CS_Pin (GPIO_PIN_10)\n"); + + /* ---- Test G: RX extra has correct SPI handle ---- */ + printf(" RX hspi = %p (expected &hspi4 = %p)\n", (void *)rx_extra->hspi, (void *)&hspi4); + assert(rx_extra->hspi == &hspi4); + printf(" PASS: RX hspi == &hspi4\n"); + + /* ---- Test H: TX and RX extras are DIFFERENT instances ---- */ + printf(" TX extra = %p, RX extra = %p (expected different)\n", + (void *)tx_extra, (void *)rx_extra); + assert(tx_extra != rx_extra); + printf(" PASS: TX and RX have separate stm32_spi_extra instances\n"); + + /* ---- Test I: TX and RX have DIFFERENT CS pins ---- */ + assert(tx_extra->cs_pin != rx_extra->cs_pin); + printf(" PASS: TX and RX CS pins differ (individual addressing)\n"); + + /* Cleanup */ + ADF4382A_Manager_Deinit(&mgr); + + printf("=== Bug #10 (FIXED): ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug9_platform_ops_null.c b/9_Firmware/9_1_Microcontroller/tests/test_bug9_platform_ops_null.c new file mode 100644 index 0000000..fcfd936 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug9_platform_ops_null.c @@ -0,0 +1,58 @@ +/******************************************************************************* + * test_bug9_platform_ops_null.c + * + * Bug #9 (FIXED): Both TX and RX SPI init params had platform_ops = NULL. + * adf4382_init() calls no_os_spi_init() which checks + * if (!param->platform_ops) return -EINVAL; + * so SPI init always silently failed. + * + * Post-fix behavior: + * 1. Manager_Init sets platform_ops = &stm32_spi_ops for both TX and RX. + * 2. platform_ops is non-NULL and points to the STM32 SPI platform ops. + ******************************************************************************/ +#include "adf4382a_manager.h" +#include "stm32_spi.h" +#include +#include + +int main(void) +{ + ADF4382A_Manager mgr; + int ret; + + printf("=== Bug #9 (FIXED): platform_ops was NULL ===\n"); + + /* ---- Test A: Init succeeds ---- */ + spy_reset(); + ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED); + + printf(" Manager_Init returned: %d (expected 0=OK)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + printf(" PASS: Init returned OK\n"); + + /* ---- Test B: TX platform_ops is non-NULL ---- */ + printf(" spi_tx_param.platform_ops = %p (expected non-NULL)\n", + (void *)mgr.spi_tx_param.platform_ops); + assert(mgr.spi_tx_param.platform_ops != NULL); + printf(" PASS: TX platform_ops is non-NULL\n"); + + /* ---- Test C: RX platform_ops is non-NULL ---- */ + printf(" spi_rx_param.platform_ops = %p (expected non-NULL)\n", + (void *)mgr.spi_rx_param.platform_ops); + assert(mgr.spi_rx_param.platform_ops != NULL); + printf(" PASS: RX platform_ops is non-NULL\n"); + + /* ---- Test D: Both point to stm32_spi_ops ---- */ + printf(" &stm32_spi_ops = %p\n", (void *)&stm32_spi_ops); + assert(mgr.spi_tx_param.platform_ops == &stm32_spi_ops); + printf(" PASS: TX platform_ops == &stm32_spi_ops\n"); + + assert(mgr.spi_rx_param.platform_ops == &stm32_spi_ops); + printf(" PASS: RX platform_ops == &stm32_spi_ops\n"); + + /* Cleanup */ + ADF4382A_Manager_Deinit(&mgr); + + printf("=== Bug #9 (FIXED): ALL TESTS PASSED ===\n\n"); + return 0; +}