/*
 * main.c
 *
 *  Created on: Jun 23, 2023
 *      Author: true
 *
 * UART bootloader for HK32F030M MCU. Tested on HK32F030MF4P6.
 * Button or no-code activation. Otherwise instant jump to user code.
 * Should be easy to port to other platforms. Needs less than 2K RAM.
 *
 * Just a couple days before completing this, LCSC removed this part...
 * it likely won't be very common now, so what's the point? =(
 */

#include "hk32f030m.h"
#include "flash.h"
#include "usart.h"
#include "user_io.h"
#include "xmodem.h"



__attribute__((section(".noinit"))) uint32_t bootcheck;


void welcome_banner()
{
	uint8_t i;

	comm_tx_str("\r\n\r\n");

	for (i = 0; i < 31; i++) {
		comm_tx_byte('=');
	}

	comm_tx_str("\r\ntrueControl HK32F Loader v0.0.3\r\n");

	for (i = 0; i < 31; i++) {
		comm_tx_byte('=');
	}
}

int main(void)
{
	uint32_t x;
	uint32_t btn;
	uint8_t flash_empty = 0;

	x = *(volatile uint32_t *)USER_APP_START_ADDR;		// first byte of user flash
	if ((x == 0x00000000) || (x == 0xffffffff)) {
		flash_empty = 1;
	}

	// if we software reset, but have contents in flash
	if (!flash_empty && (RCC->CSR & RCC_CSR_SFTRSTF)) {
		// and have the bootload done key loaded,
		if (bootcheck == 0x1337b007) {
			// then forego all of this shit and jump to the user app
			jump_to_user_app();
		}
	}

	/*
	// set with a debugger to enable this codepath (testing)
	volatile uint8_t do_this = 0;
	// make sure bootloader is write protected before we write anything to main flash
	const uint8_t pages = (uint8_t)(OB_WRP_Pages0to3 | OB_WRP_Pages4to7);
	uint8_t pagecheck = (uint8_t)(~FLASH_OB_GetWRP());
	if (do_this && (pagecheck & pages) != pages) {
		uint32_t flash_cr;
		uint16_t wrp0;
		uint32_t wrp_addr = FLASH_OB_WRP_ADDRESS;
		uint16_t i;

		wrp0 = (uint16_t)((pages << 8) | (~pages & 0xff));

		FLASH_Unlock();
		FLASH_OB_Unlock();
		flash_cr  = FLASH->CR;

		for(i = 0; i < 2; i++) {
			FLASH->CR = flash_cr | FLASH_CR_OPTER;
			FLASH->AR = wrp_addr;
			FLASH->CR = flash_cr | FLASH_CR_OPTER | FLASH_CR_STRT;

			FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

			wrp_addr++;
		}

		FLASH->CR = flash_cr | FLASH_CR_OPTPG;
		OB->WRP0 = wrp0;
		FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);

		FLASH->CR = flash_cr;

		NVIC_SystemReset();
	}
	*/

	// start clocks used by peripherals
	RCC->AHBENR  = RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | \
					RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD;
	RCC->APB2ENR = RCC_APB2Periph_USART1;

	// set up user IO
	user_io_init();

	// get button state
	btn = BTN_PORT->IDR & (1 << BTN_PIN);
#if BTN_ACT_DIR == 0
	btn = (btn == 0) ? 1 : 0;
#endif

	// bootloader activation methods

	if (btn || flash_empty) {	// is button pushed, or flash empty or null?
		// bootloader is activated
		// this is where you can can blink an LED... at least
		// when you don't fuck up your circuit so the LED doesn't work
		// in our case we start up systick to do the blinker for us
		SysTick_Config(8000000 / 20);

		// configure serial comms
		comm_init();

		welcome_banner();

		// wait for firmware load or reset
		while (1) {
			// announce bootloader start
			comm_tx_str("\r\n\r\nsend firm.bin file using XMODEM\r\n\r\n");

			// start listening for data and letting user know to send XMODEM data
			x = xmodem_receive();

			if (x == X_OK) {
				comm_tx_str("\r\nfw updated; starting...\r\n\r\n");
				break;
			}

			// if we've spammed enough, then reboot so the banner is shown again
			if (x == X_TIMEOUT) {
				welcome_banner();
			} else {
				// if xmodem_receive exits, there was a failure. repeat the process.
				comm_tx_str("\r\nfailed to update. try again.\r\n");
			}
		}
	}

	// bootloader is either done not activated; jump to user code
	jump_to_user_app();
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(char* file , uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */    
       /* Infinite loop */
    
    while (1)
  {        
  }
}
#endif /* USE_FULL_ASSERT */
