/**
 * \file
 *
 * \brief  Microchip CryptoAuthLib hardware interface object
 *
 * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries.
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip software
 * and any derivatives exclusively with Microchip products. It is your
 * responsibility to comply with third party license terms applicable to your
 * use of third party software (including open source software) that may
 * accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
 * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
 * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT,
 * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE
 * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF
 * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE
 * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL
 * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED
 * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR
 * THIS SOFTWARE.
 */

#include <stdlib.h>
#include "atca_iface.h"
#include "hal/atca_hal.h"

/** \defgroup interface ATCAIface (atca_)
 *  \brief Abstract interface to all CryptoAuth device types.  This interface
 *  connects to the HAL implementation and abstracts the physical details of the
 *  device communication from all the upper layers of CryptoAuthLib
   @{ */


#ifndef ATCA_POST_DELAY_MSEC
/* \brief How long to wait after an initial wake failure for the POSt to
 *         complete.
 * If Power-on self test (POST) is enabled, the self test will run on waking
 * from sleep or during power-on, which delays the wake reply.
 */
#define ATCA_POST_DELAY_MSEC 25
#endif

ATCA_STATUS _atinit(ATCAIface ca_iface, ATCAHAL_t *hal);

/** \brief Initializer for ATCAIface objects
 * \param[in] cfg       Logical configuration for the interface
 * \param[in] ca_iface  Interface structure to initialize.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS initATCAIface(ATCAIfaceCfg *cfg, ATCAIface ca_iface)
{
    ATCA_STATUS status;

    if (cfg == NULL || ca_iface == NULL)
    {
        return ATCA_BAD_PARAM;
    }

    ca_iface->mType = cfg->iface_type;
    ca_iface->mIfaceCFG = cfg;

    status = atinit(ca_iface);
    if (status != ATCA_SUCCESS)
    {
        return status;
    }

    return ATCA_SUCCESS;
}

#ifndef ATCA_NO_HEAP
/** \brief Constructor for ATCAIface objects
 * \param[in] cfg  Logical configuration for the interface
 * \return New interface instance on success. NULL on failure.
 */
ATCAIface newATCAIface(ATCAIfaceCfg *cfg)
{
    ATCAIface ca_iface;
    ATCA_STATUS status;

    ca_iface = (ATCAIface)malloc(sizeof(struct atca_iface));
    status = initATCAIface(cfg, ca_iface);
    if (status != ATCA_SUCCESS)
    {
        free(ca_iface);
        ca_iface = NULL;
        return NULL;
    }

    return ca_iface;
}
#endif

/** \brief Performs the HAL initialization by calling intermediate HAL wrapper
 *         function. If using the basic API, the atcab_init() function should
 *         be called instead.
 * \param[in] ca_iface  Device to interact with.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atinit(ATCAIface ca_iface)
{
    ATCA_STATUS status = ATCA_COMM_FAIL;
    ATCAHAL_t hal;

    _atinit(ca_iface, &hal);

    status = ca_iface->atinit(&hal, ca_iface->mIfaceCFG);
    if (status == ATCA_SUCCESS)
    {
        ca_iface->hal_data = hal.hal_data;

        // Perform the post init
        status = ca_iface->atpostinit(ca_iface);
    }

    return status;
}

/** \brief Sends the data to the device by calling intermediate HAL wrapper
 *         function.
 * \param[in] ca_iface  Device to interact with.
 * \param[in] txdata    Data to be transmitted to the device.
 * \param[in] txlength  Number of bytes to be transmitted to the device.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atsend(ATCAIface ca_iface, uint8_t *txdata, int txlength)
{
    return ca_iface->atsend(ca_iface, txdata, txlength);
}

/**\brief Receives data from the device by calling intermediate HAL wrapper
 *        function.
 * \param[in]    ca_iface  Device to interact with.
 * \param[out]   rxdata    Data received will be returned here.
 * \param[inout] rxlength  As input, the size of the rxdata buffer.
 *                         As output, the number of bytes received.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atreceive(ATCAIface ca_iface, uint8_t *rxdata, uint16_t *rxlength)
{
    return ca_iface->atreceive(ca_iface, rxdata, rxlength);
}

/** \brief Wakes up the device by calling intermediate HAL wrapper function.
 *         If using the basic API, the atcab_wakeup() function should be used
 *         instead.
 * \param[in] ca_iface  Device to interact with.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atwake(ATCAIface ca_iface)
{
    ATCA_STATUS status = ca_iface->atwake(ca_iface);

    if (status == ATCA_WAKE_FAILED)
    {
        // The device might be performing a POST. Wait for it to complete
        // and try again.
        atca_delay_ms(ATCA_POST_DELAY_MSEC);

        status = ca_iface->atwake(ca_iface);
    }

    return status;
}


/** \brief Puts the device into idle state by calling intermediate HAL wrapper
 *         function. If using the basic API, the atcab_idle() function should
 *         be used instead.
 * \param[in] ca_iface  Device to interact with.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atidle(ATCAIface ca_iface)
{
    ATCA_STATUS status;

    status = ca_iface->atidle(ca_iface);
    atca_delay_ms(1);
    return status;
}

/** \brief Puts the device into sleep state by calling intermediate HAL wrapper
 *         function. If using the basic API, the atcab_sleep() function should
 *         be used instead.
 * \param[in] ca_iface  Device to interact with.
 * \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS atsleep(ATCAIface ca_iface)
{
    ATCA_STATUS status;

    status = ca_iface->atsleep(ca_iface);
    atca_delay_ms(1);
    return status;
}


/** \brief Returns the logical interface configuration for the device.
 * \param[in] ca_iface  Device interface.
 * \return Logical interface configuration.
 */
ATCAIfaceCfg * atgetifacecfg(ATCAIface ca_iface)
{
    return ca_iface->mIfaceCFG;
}


/** \brief Returns the HAL data pointer for the device.
 * \param[in] ca_iface  Device interface.
 * \return HAL data pointer.
 */
void* atgetifacehaldat(ATCAIface ca_iface)
{
    return ca_iface->hal_data;
}

/** \brief Instruct the HAL driver to release any resources associated with
 *         this interface.
 *  \param[in] ca_iface  Device interface.
 *  \return ATCA_SUCCESS on success, otherwise an error code.
 */
ATCA_STATUS releaseATCAIface(ATCAIface ca_iface)
{
    ATCA_STATUS ret = ATCA_BAD_PARAM;

    if (ca_iface)
    {
        ret = hal_iface_release(ca_iface->mType, ca_iface->hal_data);
        ca_iface->hal_data = NULL;
    }

    return ret;
}

#ifndef ATCA_NO_HEAP
/** \brief Instruct the HAL driver to release any resources associated with
 *         this interface, then delete the object.
 * \param[in] ca_iface  Device interface.
 */
void deleteATCAIface(ATCAIface *ca_iface)
{
    if (ca_iface == NULL)
    {
        return;
    }

    releaseATCAIface(*ca_iface);
    free(*ca_iface);
    *ca_iface = NULL;
}
#endif

ATCA_STATUS _atinit(ATCAIface ca_iface, ATCAHAL_t *hal)
{
    // get method mapping to HAL methods for this interface
    hal_iface_init(ca_iface->mIfaceCFG, hal);
    ca_iface->atinit     = hal->halinit;
    ca_iface->atpostinit = hal->halpostinit;
    ca_iface->atsend     = hal->halsend;
    ca_iface->atreceive  = hal->halreceive;
    ca_iface->atwake     = hal->halwake;
    ca_iface->atsleep    = hal->halsleep;
    ca_iface->atidle     = hal->halidle;
    ca_iface->hal_data   = hal->hal_data;

    return ATCA_SUCCESS;
}
/** @} */
