/* nrf51.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */


#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif

#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/types.h>

#if defined(WOLFSSL_NRF51) || defined(WOLFSSL_NRF5x)

#include "bsp.h"
#include "nrf_delay.h"
#include "app_uart.h"
#include "app_error.h"
#include "nrf_drv_rng.h"
#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrf_ecb.h"

#ifdef SOFTDEVICE_PRESENT
    #include "softdevice_handler.h"
    #include "nrf_soc.h"
#endif /* SOFTDEVICE_PRESENT */

/* RTC */
#ifndef NO_CRYPT_BENCHMARK
static volatile byte mRtcInitDone = 0;
static volatile int mRtcSec = 0;
const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(0); /**< Declaring an instance of nrf_drv_rtc for RTC0. */
#endif /* !NO_CRYPT_BENCHMARK */

/* AES */
#if !defined(NO_AES) && defined(WOLFSSL_NRF51_AES) && !defined(SOFTDEVICE_PRESENT)
    static volatile byte mAesInitDone = 0;
#endif

/** @brief Function for getting vector of random numbers.
 *
 * @param[out] p_buff   Pointer to uint8_t buffer for storing the bytes.
 * @param[in]  size     Number of bytes to take from pool and place in p_buff.
 *
 * @retval     0 = Success, else error
 */
int nrf51_random_generate(byte* output, word32 size)
{
    word32 remaining = size;
    word32 pos = 0;
    uint8_t length;
    uint8_t available;
    uint32_t err_code;

    /* Make sure RNG is running */
    err_code = nrf_drv_rng_init(NULL);
    if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
        return -1;
    }
    err_code = NRF_SUCCESS;

    while (remaining > 0) {
        available = 0;
        nrf_drv_rng_bytes_available(&available); /* is void */
        length = (remaining < (word32)available) ? (uint8_t)remaining :
                                                   available;
        if (length > 0) {
            err_code = nrf_drv_rng_rand(&output[pos], length);
            if (err_code != NRF_SUCCESS) {
                break;
            }
            remaining -= length;
            pos += length;
        }
        else {
            nrf_delay_us(100);
        }
    }

    return (err_code == NRF_SUCCESS) ? 0 : -1;
}

#if !defined(NO_AES) && defined(WOLFSSL_NRF51_AES)

#ifdef SOFTDEVICE_PRESENT
static const byte* nRF51AesKey = NULL;
#endif
int nrf51_aes_set_key(const byte* key)
{
#ifdef SOFTDEVICE_PRESENT
    nRF51AesKey = key;
#else
    if (!mAesInitDone) {
        nrf_ecb_init();
        mAesInitDone = 1;
    }
    nrf_ecb_set_key(key);
#endif
    return 0;
}

/* returns 0 on success and -1 on failure. */
int nrf51_aes_encrypt(const byte* in, const byte* key, word32 rounds, byte* out)
{
    int ret;
#ifdef SOFTDEVICE_PRESENT
    uint32_t err_code = 0;
    nrf_ecb_hal_data_t ecb_hal_data;
#endif

    (void)rounds;

    /* Set key */
    ret = nrf51_aes_set_key(key);
    if (ret != 0) {
        return ret;
    }

#ifdef SOFTDEVICE_PRESENT
    /* Define ECB record */
    XMEMCPY(ecb_hal_data.key, nRF51AesKey, SOC_ECB_KEY_LENGTH);
    XMEMCPY(ecb_hal_data.cleartext, in, SOC_ECB_CLEARTEXT_LENGTH);
    XMEMSET(ecb_hal_data.ciphertext, 0, SOC_ECB_CIPHERTEXT_LENGTH);

    /* Perform block encrypt */
    err_code = sd_ecb_block_encrypt(&ecb_hal_data);
    if (err_code != NRF_SUCCESS) {
        return -1;
    }

    /* Grab result */
    XMEMCPY(out, ecb_hal_data.ciphertext, SOC_ECB_CIPHERTEXT_LENGTH);
#else
    /* Returns true or false depending on operation success. */
    if (nrf_ecb_crypt(out, in))
        ret = 0;
    else
        ret = -1;
#endif

    return ret;
}

#endif /* !NO_AES && WOLFSSL_NRF51_AES */


#ifndef NO_CRYPT_BENCHMARK
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    if (int_type == NRF_DRV_RTC_INT_COMPARE0) {
        mRtcSec++;
        nrf_drv_rtc_counter_clear(&rtc);
        nrf_drv_rtc_int_enable(&rtc, RTC_CHANNEL_INT_MASK(0));

#ifdef BSP_LED_0
        nrf_gpio_pin_toggle(BSP_LED_0);
#endif
    }
}

#ifndef RTC0_CONFIG_FREQUENCY
#define RTC0_CONFIG_FREQUENCY 32768
#endif

static void rtc_config(void)
{
    uint32_t err_code;

    /* Start the internal LFCLK XTAL oscillator */
#if defined(NRF52) || defined(NRF52_SERIES)
    err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);
    nrf_drv_clock_lfclk_request(NULL);
#else
    err_code = nrf_drv_clock_init(NULL);
    APP_ERROR_CHECK(err_code);
    nrf_drv_clock_lfclk_request();
#endif

    /* Initialize RTC instance */
    err_code = nrf_drv_rtc_init(&rtc, NULL, rtc_handler);
    APP_ERROR_CHECK(err_code);

    /* Enable tick event */
    nrf_drv_rtc_tick_enable(&rtc, false);

    /* Set compare channel to trigger interrupt after 1 seconds */
    err_code = nrf_drv_rtc_cc_set(&rtc, 0, RTC0_CONFIG_FREQUENCY, true);
    APP_ERROR_CHECK(err_code);

    /* Power on RTC instance */
    nrf_drv_rtc_enable(&rtc);
}

static int rtc_get_ms(void)
{
    /* Prescaler is 12-bit for COUNTER: frequency = (32768/(PRESCALER+1)) */
    uint32_t frequency = (32768 / (rtc_prescaler_get(rtc.p_reg) + 1));
    /* Only 24-bits returned by call. */
    uint32_t counter = nrf_drv_rtc_counter_get(&rtc);

    /* Convert with rounding frequency to milliseconds */
    return (int)((((uint64_t)counter * 1000) + (frequency / 2)) / frequency);
}

double current_time(int reset)
{
    double time;
    int sec;

    (void)reset;

    if (!mRtcInitDone) {
        rtc_config();
        mRtcInitDone = 1;
    }

    do {
      sec = mRtcSec;
      time = sec + ((double)rtc_get_ms() / 1000);
    } while (sec != mRtcSec);

    return time;
}
#endif /* !NO_CRYPT_BENCHMARK */

#endif /* WOLFSSL_NRF51 || WOLFSSL_NRF5x */
