//*****************************************************************************
//
//! @file am_devices_da14581.c
//!
//! @brief Support functions for the Dialog Semiconductor DA14581 BTLE radio.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2019, Ambiq Micro
// 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. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 is part of revision v2.2.0-7-g63f7c2ba1 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include "am_bsp.h"
#include "am_devices_da14581.h"

//*****************************************************************************
//
// Macro definitions
//
//*****************************************************************************
#define DIALOG_BOOT_STX                     0x02
#define DIALOG_BOOT_SOH                     0x01
#define DIALOG_BOOT_ACK                     0x06
#define DIALOG_BOOT_NACK                    0x15

//*****************************************************************************
//
// BLE MAC address for the Dialog radio.
//
//*****************************************************************************
static uint8_t g_BLEMacAddress[6] =
{0x01, 0x00, 0x00, 0xCA, 0xEA, 0x80};

//*****************************************************************************
//
// Location in the Diaog binary where the MAC address is stored.
//
//*****************************************************************************
static const uint32_t g_ui32MacIndex = 1000;

//*****************************************************************************
//
// Sets the MAC address to send to the dialog radio on the next boot up.
//
//*****************************************************************************
void
am_devices_da14581_mac_set(const uint8_t *pui8MacAddress)
{
    uint32_t i;

    //
    // Copy the 6-byte MAC address into our global variable.
    //
    for ( i = 0; i < 6; i++ )
    {
        g_BLEMacAddress[i] = *pui8MacAddress++;
    }
}

//*****************************************************************************
//
//! @brief Runs a UART based boot sequence for a Dialog radio device.
//!
//! @param pui8BinData - pointer to an array of bytes containing the firmware
//! for the DA14581
//!
//! @param ui32NumBytes - length of the DA14581 firmware image.
//!
//! This function allows the Ambiq device to program a "blank" DA14581 device
//! on startup. It will handle all of the necessary UART negotiation for the
//! Dialog boot procedure, and will verify that the CRC value for the
//! downloaded firmware image is correct.
//!
//! @return true if successful.
//
//*****************************************************************************
bool
am_devices_da14581_uart_boot(const uint8_t *pui8BinData, uint32_t ui32NumBytes,
                             uint32_t ui32UartModule)
{
    uint32_t ui32Index;
    uint8_t ui8CRCValue;
    uint8_t ui8TxData;
    char ui8RxChar;

    //
    // Poll the RX lines until we get some indication that the dialog radio is
    // present and ready to receive data.
    //
    do
    {
        am_hal_uart_char_receive_polled(ui32UartModule, &ui8RxChar);
    }
    while (ui8RxChar != DIALOG_BOOT_STX);

    //
    // Send the Start-of-Header signal and the length of the data download.
    //
    am_hal_uart_char_transmit_polled(ui32UartModule, DIALOG_BOOT_SOH);
    am_hal_uart_char_transmit_polled(ui32UartModule, ui32NumBytes & 0xFF);
    am_hal_uart_char_transmit_polled(ui32UartModule, (ui32NumBytes & 0xFF00) >> 8);

    //
    // Poll for the 'ACK' from the dialog device that signifies that the header
    // was recieved correctly.
    //
    do
    {
        am_hal_uart_char_receive_polled(ui32UartModule, &ui8RxChar);
    }
    while (ui8RxChar != DIALOG_BOOT_ACK);

    //
    // Initialize the CRC value to zero.
    //
    ui8CRCValue = 0;

    //
    // Send the binary image over to the dialog device one byte at a time,
    // keeping track of the CRC as we go.
    //
    for (ui32Index = 0; ui32Index < ui32NumBytes; ui32Index++)
    {
        if ((ui32Index >= g_ui32MacIndex) && (ui32Index < g_ui32MacIndex + 6))
        {
            ui8TxData = g_BLEMacAddress[ui32Index - g_ui32MacIndex];
        }
        else
        {
            ui8TxData = pui8BinData[ui32Index];
        }

        ui8CRCValue ^= ui8TxData;
        am_hal_uart_char_transmit_polled(ui32UartModule, ui8TxData);
    }

    //
    // The Dialog device should respond back with a CRC value at the end of the
    // programming cycle. We should check here to make sure that they got the
    // same CRC result that we did. If it doesn't match, return with an error.
    //
    am_hal_uart_char_receive_polled(ui32UartModule, &ui8RxChar);

    if ( ui8RxChar != ui8CRCValue )
    {
        return 1;
    }

    //
    // If all is well, send the final 'ACK' to tell the dialog device that its
    // new image is correct. After this point, the dialog device should start
    // running the downloaded firmware.
    //
    am_hal_uart_char_transmit_polled(ui32UartModule, DIALOG_BOOT_ACK);

    //
    // Wait until the FIFO is actually empty and the UART is no-longer busy.
    //
    while (!AM_BFR(UART, FR, TXFE) || AM_BFR(UART, FR, BUSY));

    return 0;
}

