/*
 * FreeRTOS Common IO V0.1.3
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * 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.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/**
 * @file iot_spi.h
 * @brief File for the APIs of SPI called by application layer.
 */
#ifndef _IOT_SPI_H_
#define _IOT_SPI_H_

/* Standard includes. */
#include <stdint.h>
#include <stddef.h>

/**
 * @defgroup iot_spi SPI Abstraction APIs.
 * @{
 */

/**
 * @brief The return codes for the methods in SPI.
 */
#define IOT_SPI_SUCCESS                   ( 0 ) /*!< SPI operation completed successfully. */
#define IOT_SPI_INVALID_VALUE             ( 1 ) /*!< At least one parameter is invalid. */
#define IOT_SPI_WRITE_FAILED              ( 2 ) /*!< SPI driver returns error when performing write operation. */
#define IOT_SPI_READ_FAILED               ( 3 ) /*!< SPI driver returns error when performing read operation. */
#define IOT_SPI_TRANSFER_ERROR            ( 4 ) /*!< SPI driver returns error when performing transfer. */
#define IOT_SPI_BUS_BUSY                  ( 5 ) /*!< SPI bus is busy at current time. */
#define IOT_SPI_NOTHING_TO_CANCEL         ( 6 ) /*!< No ongoing operation when cancel operation is performed. */
#define IOT_SPI_FUNCTION_NOT_SUPPORTED    ( 7 ) /*!< SPI operation is not supported. */


/**
 * @brief The SPI return status from Async operations.
 */
typedef enum
{
    eSPISuccess = IOT_SPI_SUCCESS,              /*!< SPI operation completed successfully. */
    eSPIWriteError = IOT_SPI_WRITE_FAILED,      /*!< SPI driver returns error when performing write operation. */
    eSPIReadError = IOT_SPI_READ_FAILED,        /*!< SPI driver returns error when performing read operation. */
    eSPITransferError = IOT_SPI_TRANSFER_ERROR, /*!< SPI driver returns error when performing transfer. */
} IotSPITransactionStatus_t;

/**
 * @brief The SPI Modes denoting the clock polarity
 * and clock phase.
 */
typedef enum
{
    eSPIMode0, /*!< CPOL = 0 and CPHA = 0 */
    eSPIMode1, /*!< CPOL = 0 and CPHA = 1 */
    eSPIMode2, /*!< CPOL = 1 and CPHA = 0 */
    eSPIMode3, /*!<CPOL = 1 and CPHA = 1 */
} IotSPIMode_t;

/**
 * @brief The bit order of the data transmission.
 */
typedef enum
{
    eSPIMSBFirst, /*!< The master sends the most-significant bit (MSB) first */
    eSPILSBFirst  /*!< The master sends the least-significant bit (LSB) first */
} IotSPIBitOrder_t;

/**
 * @brief Ioctl request for SPI HAL.
 */
typedef enum
{
    eSPISetMasterConfig, /*!< Sets the configuration of the SPI master and the data type is IotSPIMasterConfig_t. */
    eSPIGetMasterConfig, /*!< Gets the configuration of the SPI master and the data type is IotSPIMasterConfig_t. */
    eSPIGetTxNoOfbytes,  /*!< Get the number of bytes sent in write operation and the data type is uint16_t. */
    eSPIGetRxNoOfbytes,  /*!< Get the number of bytes received in read operation and the data type is uint16_t. */
} IotSPIIoctlRequest_t;

/**
 * @brief The configuration parameters for SPI Master.
 *
 * @details The application will set the SPI master interface using the Ioctl
 * eSPISetMasterConfig and send this structure.
 */
typedef struct IotSPIMasterConfig
{
    uint32_t ulFreq;               /*!< SPI frequency set for data transmission in Hz. */
    IotSPIMode_t eMode;            /*!< Mode selected as per enum IotSPIMode_t. */
    IotSPIBitOrder_t eSetBitOrder; /*!< Bit Order selected as per enum IotSPIBitOrder_t. */
    uint8_t ucDummyValue;          /*!< The dummy value sent by master for SPI Read. */
} IotSPIMasterConfig_t;

/**
 * @brief The SPI descriptor type defined in the source file.
 */
struct                      IotSPIDescriptor;

/**
 * @brief IotSPIHandle_t is the handle type returned by calling iot_spi_open().
 *        This is initialized in open and returned to caller. The caller must pass
 *        this pointer to the rest of APIs.
 */
typedef struct IotSPIDescriptor * IotSPIHandle_t;

/**
 * @brief The callback function for completion of SPI operation.
 */
typedef void (* IotSPICallback_t) ( IotSPITransactionStatus_t xStatus,
                                    void * pvSPIparam );

/**
 * @brief Initializes SPI peripheral with default configuration.
 *
 * @warning Once opened, the same SPI instance must be closed before calling open again.
 *
 * @param[in] lSPIInstance The instance of the SPI driver to initialize.
 *
 * @return
 * - 'the handle to the SPI port (not NULL)', on success.
 * - 'NULL', if
 *     - invalid instance number
 *     - open same instance more than once before closing it
 */
IotSPIHandle_t iot_spi_open( int32_t lSPIInstance );

/**
 * @brief Sets the application callback to be called on completion of an operation.
 *
 * The callback is guaranteed to be invoked when the current asynchronous operation completes, either successful or failed.
 * This simply provides a notification mechanism to user's application. It has no impact if the callback is not set.
 *
 * @note This callback will not be invoked when synchronous operation completes.
 * @note This callback is per handle. Each instance has its own callback.
 * @note Single callback is used for both read_async and write_async. Newly set callback overrides the one previously set.
 * @warning If the input handle is invalid, this function silently takes no action.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in the open() call.
 * @param[in] xCallback The callback function to be called on completion of operation.
 * @param[in] pvUserContext The user context to be passed back when callback is called.
 */
void iot_spi_set_callback( IotSPIHandle_t const pxSPIPeripheral,
                           IotSPICallback_t xCallback,
                           void * pvUserContext );

/**
 * @brief Configures the SPI port with user configuration.
 *
 *
 * @note eSPISetMasterConfig sets the configurations for master.
 * This request expects the buffer with size of IotSPIMasterConfig_t.
 *
 * @note eSPIGetMasterConfig gets the current configuration for SPI master.
 * This request expects the buffer with size of IotSPIMasterConfig_t.
 *
 * @note eSPIGetTxNoOfbytes returns the number of written bytes in last operation.
 * This is supposed to be called in the caller task or application callback, right after last operation completes.
 * This request expects 2 bytes buffer (uint16_t).
 *
 * - If the last operation only did write, this returns the actual number of written bytes which might be smaller than the requested number (partial write).
 * - If the last operation only did read, this returns 0.
 * - If the last operation did both write and read, this returns the number of written bytes.
 *
 * @note eSPIGetRxNoOfbytes returns the number of read bytes in last operation.
 * This is supposed to be called in the caller task or application callback, right after last operation completes.
 * This request expects 2 bytes buffer (uint16_t).
 *
 * - If the last operation only did read, this returns the actual number of read bytes which might be smaller than the requested number (partial read).
 * - If the last operation only did write, this returns 0.
 * - If the last operation did both write and read, this returns the number of read bytes.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[in] xSPIRequest The configuration request from one of the
 * IotSPIIoctlRequest_t.
 * @param[in,out] pvBuffer The configuration values for the SPI port.
 *
 *
 * @return
 * - IOT_SPI_SUCCESS, on success
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL with requests which needs buffer
 * - IOT_SPI_BUS_BUSY, if the bus is busy for only following requests:
 *     - eSPISetMasterConfig
 */
int32_t iot_spi_ioctl( IotSPIHandle_t const pxSPIPeripheral,
                       IotSPIIoctlRequest_t xSPIRequest,
                       void * const pvBuffer );

/**
 * @brief The SPI master starts reading from the slave synchronously.
 *
 * This function attempts to read certain number of bytes from slave device to a pre-allocated buffer, in synchronous way.
 * This function does not return on paritial read, unless there is an error.
 * And the number of bytes that have been actually read can be obtained by calling iot_spi_ioctl.
 *
 * @note Dummy data will be written to slave while reading. The dummy data value can be configured with iot_spi_ioctl.
 *
 * @warning None of other read or write functions shall be called during this function.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[out] pvBuffer The buffer to store the received data.
 * @param[in] xBytes The number of bytes to read.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_READ_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing operation.
 */
int32_t iot_spi_read_sync( IotSPIHandle_t const pxSPIPeripheral,
                           uint8_t * const pvBuffer,
                           size_t xBytes );

/**
 * @brief The SPI master starts reading from the slave asynchronously.
 *
 * This function attempts to read certain number of bytes from a pre-allocated buffer, in asynchronous way.
 * It returns immediately when the operation is started and the status can be check by calling iot_spi_ioctl.
 *
 * Once the operation completes successfully, the user callback will be invoked.
 * If the operation encounters an error, the user callback will be invoked.
 * The callback is not invoked on paritial read, unless there is an error.
 * And the number of bytes that have been actually read can be obtained by calling iot_spi_ioctl.
 *
 * @note Dummy data will be written to slave while reading. The dummy data value can be configured with iot_spi_ioctl.
 * @note In order to get notification when the asynchronous call is completed, iot_spi_set_callback must be called prior to this.
 *
 * @warning pvBuffer must be valid before callback is invoked.
 * @warning None of other read or write functions shall be called during this function or before user callback.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[out] pvBuffer The buffer to store the received data.
 * @param[in] xBytes The number of bytes to read.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_READ_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing operation.
 */
int32_t iot_spi_read_async( IotSPIHandle_t const pxSPIPeripheral,
                            uint8_t * const pvBuffer,
                            size_t xBytes );

/**
 * @brief The SPI master starts transmission of data to the slave synchronously.
 *
 * This function attempts to write certain number of bytes from a pre-allocated buffer to a slave device, in synchronous way.
 * This function does not return on paritial write, unless there is an error.
 * And the number of bytes that have been actually written can be obtained by calling iot_spi_ioctl.
 *
 *
 * @warning None of other read or write functions shall be called during this function.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[in] pvBuffer The buffer with data to transmit.
 * @param[in] xBytes The number of bytes to write.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_WRITE_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing operation.
 */
int32_t iot_spi_write_sync( IotSPIHandle_t const pxSPIPeripheral,
                            uint8_t * const pvBuffer,
                            size_t xBytes );

/**
 * @brief The SPI master starts transmission of data to the slave asynchronously.
 *
 * This function attempts to read certain number of bytes from a pre-allocated buffer, in asynchronous way.
 * It returns immediately when the operation is started and the status can be check by calling iot_spi_ioctl.
 *
 * Once the operation completes successfully, the user callback will be invoked.
 * If the operation encounters an error, the user callback will be invoked.
 * The callback is not invoked on paritial write, unless there is an error.
 * And the number of bytes that have been actually written can be obtained by calling iot_spi_ioctl.
 *
 * @note In order to get notification when the asynchronous call is completed, iot_spi_set_callback must be called prior to this.
 *
 * @warning pvBuffer must be valid before callback is invoked.
 * @warning None of other read or write functions shall be called during this function or before user callback.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[in] pvBuffer The buffer with data to transmit.
 * @param[in] xBytes The number of bytes to write.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_WRITE_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing operation.
 */
int32_t iot_spi_write_async( IotSPIHandle_t const pxSPIPeripheral,
                             uint8_t * const pvBuffer,
                             size_t xBytes );

/**
 * @brief The SPI master starts a synchronous transfer between master and the slave.
 *
 * This function attempts to read/write certain number of bytes from/to two pre-allocated buffers at the same time, in synchronous way.
 * This function does not return on paritial read/write, unless there is an error.
 * And the number of bytes that have been actually read or written can be obtained by calling iot_spi_ioctl.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[in] pvTxBuffer The buffer to store the received data.
 * @param[out] pvRxBuffer The buffer with data to transmit.
 * @param[in] xBytes The number of bytes to transfer.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read/written)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_TRANSFER_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing operation.
 */
int32_t iot_spi_transfer_sync( IotSPIHandle_t const pxSPIPeripheral,
                               uint8_t * const pvTxBuffer,
                               uint8_t * const pvRxBuffer,
                               size_t xBytes );

/**
 * @brief The SPI master starts a asynchronous transfer between master and the slave.
 *
 * This function attempts to read/write certain number of bytes from/to two pre-allocated buffers at the same time, in asynchronous way.
 * It returns immediately when the operation is started and the status can be check by calling iot_spi_ioctl.
 *
 * Once the operation completes successfully, the user callback will be invoked.
 * If the operation encounters an error, the user callback will be invoked.
 * The callback is not invoked on paritial read/write, unless there is an error.
 * And the number of bytes that have been actually read/write can be obtained by calling iot_spi_ioctl.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 * @param[in] pvTxBuffer The buffer to store the received data.
 * @param[out] pvRxBuffer The buffer with data to transmit.
 * @param[in] xBytes The number of bytes to transfer.
 *
 * - IOT_SPI_SUCCESS, on success (all the requested bytes have been read/written)
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 *     - pucBuffer is NULL
 *     - xBytes is 0
 * - IOT_SPI_TRANSFER_ERROR, if there is some unknown driver error.
 * - IOT_SPI_BUSY, if the bus is busy which means there is an ongoing opeartion.
 */
int32_t iot_spi_transfer_async( IotSPIHandle_t const pxSPIPeripheral,
                                uint8_t * const pvTxBuffer,
                                uint8_t * const pvRxBuffer,
                                size_t xBytes );

/**
 * @brief Stops the ongoing operation on SPI bus and de-initializes the SPI peripheral.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 */
int32_t iot_spi_close( IotSPIHandle_t const pxSPIPeripheral );

/**
 * @brief This function is used to cancel the current operation in progress, if the underlying driver
 * allows the cancellation.
 *
 * @param[in] pxSPIPeripheral The SPI peripheral handle returned in open() call.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success
 * - IOT_SPI_INVALID_VALUE, if
 *     - pxSPIPeripheral is NULL
 *     - pxSPIPeripheral is not opened yet
 * - IOT_SPI_NOTHING_TO_CANCEL, if there is no on-going operation.
 * - IOT_SPI_FUNCTION_NOT_SUPPORTED, if this board doesn't support this operation.
 */
int32_t iot_spi_cancel( IotSPIHandle_t const pxSPIPeripheral );

/**
 * @brief This function is used to select spi slave.
 *
 * @param[in] lSPIInstance The instance of the SPI driver to initialize.
 * @param[in] lSPISlave Slave select number.
 *
 * @return
 * - IOT_SPI_SUCCESS, on success
 * - IOT_SPI_INVALID_VALUE, if
 *     - lSPISlave is invalid
 */
int32_t iot_spi_select_slave( int32_t lSPIInstance,
                              int32_t lSPISlave );

/**
 * @}
 */
/* end of group iot_spi */

#endif /* _IOT_SPI_H_ */