/*
 * Kneron Hardware Test: SPI Loopback
 *
 * Copyright (C) 2023 Kneron, Inc. All rights reserved.
 */

#include "cmsis_os2.h"
#include "kdrv_ssp.h"
#include "kdrv_pinmux.h"
#include "kdrv_clock.h"
#include "kdrv_gpio.h"
#include "kmdw_console.h"
#include <string.h>

#define SPI_PORT SSP_SPI_PORT0
#define SPI_TEST_DATA_SIZE 16

extern struct st_ssp_spi driver_ssp_master_ctx;

uint8_t kmdw_ssp_api_spi_init(kdrv_ssp_spi_dev_id_t handle, enum e_spi edata)
{
    if( kdrv_ssp_statemachine( handle, &driver_ssp_master_ctx, edata, NULL ) == e_spi_ret_init_done ){
        return 1;
    }
    return 0;
}
uint8_t kmdw_ssp_api_spi_transfer(kdrv_ssp_spi_dev_id_t handle)
{
    if( kdrv_ssp_statemachine( handle, &driver_ssp_master_ctx, e_spi_tx, NULL ) == e_spi_ret_txbusy ){
            return 1;		//rx data correct
    }
    else{
            return 0;		//rx data fail
    }
}

uint8_t kmdw_ssp_api_spi_transfer_checks(kdrv_ssp_spi_dev_id_t handle)
{
    if( kdrv_ssp_statemachine( handle, &driver_ssp_master_ctx, e_spi_tx_status_check, NULL ) == e_spi_ret_txbusy )
    {
            return 0;
    }
    else
    {
            return 1;
    }
}

void kmdw_ssp_api_spi_write_tx_buff( uint8_t *src, uint16_t nlen )
{
    kdrv_ssp_write_buff( &driver_ssp_master_ctx, src, nlen );
}
/**
 * @brief SPI Master thread to send data and verify the loopback data.
 */
int hw_test_spi_run(void)
{
    uint8_t tx_data[SPI_TEST_DATA_SIZE];
    uint8_t rx_data[SPI_TEST_DATA_SIZE];
    int result = -1;

    kmdw_printf("[SPI TEST] External Loopback Test Thread started.\n");
    osDelay(10);

    kmdw_printf("Configuring pins for SPI0 Loopback...\n");
    kmdw_printf("Please connect pins: LC_DATA_6 (MOSI) <-> LC_DATA_7 (MISO)\n");

    // 1. Enable SSP clocks. This is crucial for the SPI peripheral to work.
    kdrv_clock_enable(CLK_SSP0_0_SSPCLK);
    kdrv_clock_enable(CLK_SSP0_1_SSPCLK);

    kdrv_clock_enable(CLK_SSP1_0_SSPCLK);
    kdrv_clock_enable(CLK_SSP1_1_SSPCLK);

    // 2. Configure Pinmux for SSP0.
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_4, PIN_MODE_4, PIN_PULL_NONE, PIN_DRIVING_8MA); // SSP0_CLK
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_5, PIN_MODE_4, PIN_PULL_NONE, PIN_DRIVING_8MA); // SSP0_CS
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_6, PIN_MODE_4, PIN_PULL_NONE, PIN_DRIVING_8MA); // SSP0_MOSI
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_7, PIN_MODE_4, PIN_PULL_UP,   PIN_DRIVING_8MA); // SSP0_MISO

    // 1. Prepare test data
    for (int i = 0; i < SPI_TEST_DATA_SIZE; i++) {
        tx_data[i] = (uint8_t)(0xA0 + i);
    }
    memset(rx_data, 0, SPI_TEST_DATA_SIZE);

    // 2. Initialize SSP in master mode
    kmdw_printf("[SPI TEST] Initializing SSP0 in master mode...\n");
    if (kmdw_ssp_api_spi_init(SPI_PORT, e_spi_init_master) != 1) {
        kmdw_printf("[SPI TEST] SSP0 initialization failed.\n");
        return -1;
    }

    // 3. Perform transfer
    kmdw_printf("[SPI TEST] Transferring data...\n");
    kmdw_ssp_api_spi_write_tx_buff(tx_data, SPI_TEST_DATA_SIZE);

    kdrv_ssp_spi_CS_set(KDRV_PIN_LC_DATA_5, SPI_CS_LOW);
    kmdw_ssp_api_spi_transfer(SPI_PORT);

    uint32_t timeout = 1000; // 1000ms timeout
    while (!kmdw_ssp_api_spi_transfer_checks(SPI_PORT) && timeout > 0) {
        osDelay(1);
        timeout--;
    }
    if (timeout == 0) {
        kmdw_printf("[SPI TEST] Error: Transfer timed out!\n");
    }

    // Add a robust check to ensure the SPI bus is no longer busy.
    // while (kdrv_ssp_busy(SPI_PORT));

    kdrv_ssp_spi_CS_set(KDRV_PIN_LC_DATA_5, SPI_CS_HI);

    // 4. Read received data from FIFO
    uint8_t nsize = kdrv_ssp_rxfifo_valid_entries(driver_ssp_master_ctx.port_no);
    if (nsize) {
        driver_ssp_master_ctx.Rx_buffer_index = 0;
        kdrv_ssp_rx_polling_receive_all(&driver_ssp_master_ctx);
        memcpy(rx_data, (void *)driver_ssp_master_ctx.Rx_buffer, nsize);
    }

    // 5. Compare results
    if (memcmp(tx_data, rx_data, SPI_TEST_DATA_SIZE) == 0) {
        kmdw_printf("-> SPI Loopback Test PASSED\n");
        result = 0;
    } else {
        kmdw_printf("-> SPI Loopback Test FAILED\n");
        kmdw_printf("  TX Data: ");
        for(int i=0; i<SPI_TEST_DATA_SIZE; i++) kmdw_printf("0x%02X ", tx_data[i]);
        kmdw_printf("\n  RX Data: ");
        for(int i=0; i<SPI_TEST_DATA_SIZE; i++) kmdw_printf("0x%02X ", rx_data[i]);
        kmdw_printf("\n");
    }

    kmdw_printf("=========================================================\n");
    kmdw_printf("SPI Test finished. Returning to main menu...\n");
    kmdw_printf("=========================================================\n");

    return result;
}
