/**
 * @file main.c
 * @brief Main routine
 *
 * @section License
 *
 * Copyright (C) 2021-2026 Oryx Embedded SARL. All rights reserved.
 * 
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 *
 * This program 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-1301, USA.

 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 2.6.4
 **/

/**
 * Flashing instructions:
 *
 * Prerequisites: Make sure Dual Bank mode is DISABLED in Option Bytes. DBANK unchecked and DB256 unchecked.
 *
 * 1. Flash the 2nd stage bootloader to 0x08000000:
 *    STM32_Programmer_CLI.exe -c port=SWD index=0 -d bootloader.bin 0x08000000 -halt -rst
 *
 * 2. Flash the application image to 0x08008000 using ImageBuilder --vtor-align output:
 *    STM32_Programmer_CLI.exe -c port=SWD index=0 -d iap_demo_1_0_0.img.bin 0x08008000 -halt -rst
 *
 * Uploading a firmware update:
 *
 *  - Firmware updates performed over LPUART1 (PG7/PG8). Exact configuration in uart_user.c.
 *  - Press '1' to put the MCU in update mode.
 *  - Send the update image (iap_demo_2_0_0_update.img) via Y-Modem.
 *  - Once complete, the MCU restarts and the 2nd stage bootloader installs the update.
 **/

//Dependencies
#include <stdlib.h>
#include "stm32l5xx.h"
#include "stm32l5xx_hal.h"
#include "stm32l5xx_nucleo.h"
#include "resource_manager.h"
#include "core/mailbox.h"
#include "version.h"
#include "uart_user.h"
#include "debug.h"

//Update signature public key resource path
#define UPDT_PUBLIC_SIGN_KEY "keys/rsa_public_key.pem"

//Global variables
const uint8_t* pemUpdtSignPublicKey;
size_t pemUpdtSignPublicKeyLen;

//IAP mutex handler
OsMutex updateMutex;

/**
 * @brief System clock configuration
 **/

void SystemClock_Config(void)
{
   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

   //Configure the main internal regulator output voltage
   HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE0);

   //MSI is enabled after System reset, activate PLL with MSI as source
   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
   RCC_OscInitStruct.MSIState = RCC_MSI_ON;
   RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
   RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
   RCC_OscInitStruct.PLL.PLLM = 1;
   RCC_OscInitStruct.PLL.PLLN = 55;
   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
   RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
   RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
   HAL_RCC_OscConfig(&RCC_OscInitStruct);

   //Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
   //clocks dividers
   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
      RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}


/**
 * @brief LED task
 * @param[in] param Unused parameter
 **/

void ledTask(void *param)
{
   //Endless loop
   while(1)
   {
      BSP_LED_On(LED1);
      osDelayTask(100);
      BSP_LED_Off(LED1);
      osDelayTask(900);
   }
}


/**
 * @brief Load user RSA public key from "res.c".
 * "res.c" is a C array file system representing the demo folder "resources/keys".
 **/

error_t updateUserLoadKeys(void)
{
   error_t error;

   //Get server's private key
   error = resGetData(UPDT_PUBLIC_SIGN_KEY, (const uint8_t **) &pemUpdtSignPublicKey,
      &pemUpdtSignPublicKeyLen);
   //Any error to report?
   if(error)
      return error;

   //Successful process
   return NO_ERROR;
}


/**
 * @brief Main entry point
 * @return Unused value
 **/

int_t main(void)
{
   error_t error;
   OsTaskId taskId;
   OsTaskParameters taskParams;

   //HAL library initialization
   HAL_Init();
   //Configure the system clock
   SystemClock_Config();

   //Enable instruction cache
   HAL_ICACHE_ConfigAssociativityMode(ICACHE_1WAY);
   HAL_ICACHE_Enable();

   //Initialize kernel
   osInitKernel();
   //Configure debug UART
   debugInit(115200);

   mailBoxInit();

   //Start-up message
   TRACE_INFO("\r\n");
   TRACE_INFO("***************************************************\r\n");
   TRACE_INFO("*** CycloneBOOT Hybrid UART Y-MODEM Application ***\r\n");
   TRACE_INFO("***************************************************\r\n");
   TRACE_INFO("Version: %s\r\n", APP_VERSION_STRING);
   TRACE_INFO("Copyright: 2010-2026 Oryx Embedded SARL\r\n");
   TRACE_INFO("Compiled: %s %s\r\n", __DATE__, __TIME__);
   TRACE_INFO("Target: STM32L552\r\n");
   TRACE_INFO("\r\n");

   //LED configuration
   BSP_LED_Init(LED1);
   BSP_LED_Init(LED2);
   BSP_LED_Init(LED3);

   //Clear LEDs
   BSP_LED_Off(LED1);
   BSP_LED_Off(LED2);
   BSP_LED_Off(LED3);

   //Initialize user button
   BSP_PB_Init(BUTTON_USER, BUTTON_MODE_GPIO);

   //Load IAP public signature key
   error = updateUserLoadKeys();
   if (error)
   {
      //Debug message
      TRACE_ERROR("Failed to load IAP user keys!\r\n");
      return error;
   }

   //Create IAP mutex
   if(!osCreateMutex(&updateMutex))
   {
      //Debug message
      TRACE_ERROR("Failed to create IAP mutex!\r\n");
   }

   //Set USER task parameters
   taskParams = OS_TASK_DEFAULT_PARAMS;
   taskParams.stackSize = 500;
   taskParams.priority = OS_TASK_PRIORITY_NORMAL;

   //Create user task
   taskId = osCreateTask("UART User", uartUserTask, NULL, &taskParams);
   //Failed to create the task?
   if(taskId == OS_INVALID_TASK_ID)
   {
      //Debug message
      TRACE_ERROR("Failed to create task!\r\n");
   }

   //Set LED task parameters
   taskParams = OS_TASK_DEFAULT_PARAMS;
   taskParams.stackSize = 200;
   taskParams.priority = OS_TASK_PRIORITY_NORMAL;

   //Create a task to blink the LED
   taskId = osCreateTask("LED", ledTask, NULL, &taskParams);
   //Failed to create the task?
   if(taskId == OS_INVALID_TASK_ID)
   {
      //Debug message
      TRACE_ERROR("Failed to create task!\r\n");
   }

   //Start the execution of tasks
   osStartKernel();

   //This function should never return
   return 0;
}
