
#include "stiap.h"

#include "stapp.h"

#include "crc32.h"
#include "trigger.h"
#include "shell.h"
#include "delay.h"

#if (0)	// 临时措施,后续需做兼容
#include "can.h"
#include "fmc.h"
#endif

//===================================================================
// IAP代码
// stfaddr: 应用程序的起始地址
// saveaddr: 应用程序存放在 NORFLASH 或 RAM 的起始地址.
// size: 应用程序大小 （字节）

int WriteAppBinData(u32 stfaddr, u32 saveaddr, u32 size)
{
	int rslt = 0;
	u32 faddr = stfaddr;
	u32 saddr = saveaddr;
	u32 wrsize = size;
	u16 thissize;
#ifdef STM32_FLASH_SEC_SIZE		// 每扇区字节数
#define STM32_RW_BUFF_SIZE	(STM32_FLASH_SEC_SIZE)
#else
#define STM32_RW_BUFF_SIZE	(STM32_FLASH_RW_SIZE)
#endif


#if (APP_SAVE_TO == APP_SAVE_NORFLASH || APP_SAVE_TO == APP_SAVE_INFLASH)
	u8 iapbuf[STM32_RW_BUFF_SIZE];
#else
	u8 * iapbuf;
#endif

#if (NO_BUFF_WHEN_WT == 1)	// 20230928 ljs 无缓写入时需提前擦除对应扇区,避免写入失败
	u32 secIdx = GetFlashSectorIdx(faddr);
	u32 secnums = GetFlashSectorIdx(faddr+wrsize);
	secnums = secnums - secIdx + 1;
	STMFlashErase(secIdx, secnums);
#endif

	printf("Write data to addr = 0x%x begin\r\n", (unsigned int)faddr);
	while (wrsize > 0)
	{
		if (wrsize < STM32_RW_BUFF_SIZE)
		{
			thissize = wrsize;
		}
		else
		{
			thissize = STM32_RW_BUFF_SIZE;
		}

#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
		DatFlashRdData(saddr, iapbuf, thissize);		// 从 norflash 读数据
#elif (APP_SAVE_TO == APP_SAVE_EXRAM)
		iapbuf = (u8*)saddr;							// 从 ram 读数据
#elif (APP_SAVE_TO == APP_SAVE_INFLASH)
		STMFlashRead(saddr, (u16*)iapbuf, thissize/2);	// 从st flash 读数据
#else
		break;
#endif

		STMFlashWrite(faddr, (u16*)iapbuf, thissize/2, 1);		// 写数据到stm32的flash中

		rslt = STMFlashCheck(faddr, (u16*)iapbuf, thissize/2);	// 检查数据正确性
		if (rslt != 0)
		{
			printf("\r\nWriteAppBinData error. addr=0x%x, size=%d\r\n", (unsigned int)faddr, thissize);
			break;
		}
		PutChar('.');
		faddr += thissize;		// 指针偏移
		saddr += thissize;		// 指针偏移
		wrsize -= thissize;
	}

	printf("write data finish\r\n");

#if (APP_SAVE_TO == APP_SAVE_EXRAM)
	if (rslt == 0)
	{
		// 校验所有数据
		printf("WriteAppBinData finish, check all data\r\n");
		rslt = STMFlashCheck(stfaddr, (u16*)saveaddr, size/2);		// 检查数据正确性
		if (rslt == 0)
		{
			printf("WriteAppBinData ok\r\n");
		}
	}
#endif

	if (rslt != 0)
	{
		printf("WriteAppBinData failed\r\n");
	}

	return rslt;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// 20230523 完善使用外部RAM进行升级
#if (APP_SAVE_TO == APP_SAVE_EXRAM)
char g_appEnFlag[16] = {0xff};
void SetAppEnFlag(int flag)
{
	if (flag == 0)
	{
		memset(g_appEnFlag, 0xff, 16);
	}
	else
	{
		strcpy(g_appEnFlag, APP_RIGHT);
	}
}

int IsAppRight(void)
{
	if (strcmp((void*)APP_RIGHT, g_appEnFlag) != 0)
	{
		return -1;
	}
	else
	{
		return 0;
	}
}

#endif


//////////////////////////////////////////////////////////////////////////////////////////////////////

// bootloader 接口函数
// 升级主控文件（从存储器到APP区域）
int UpdateApp(void)
{
	int rslt = 0;
	u32 saveaddr;		// 数据读取地址
	u32 stfaddr;		// 数据写入地址
	u32 appsize, wsize;
	u32 crc;
	IapHead iaphd;
	u32 resetaddr, stacktop;

	memset(&iaphd, 0, sizeof(IapHead));

#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
	u32 taddr = (u32)(&iaphd);
	DatFlashRdData(APP_MARK_ADDR, (u8 *)(taddr), sizeof(IapHead)/sizeof(u8));				// 读数据
#elif (APP_SAVE_TO == APP_SAVE_EXRAM)
	memcpy((void*)(&iaphd), (void*)APP_MARK_ADDR, sizeof(IapHead));				// 读入iaphead 数据
	SetAppEnFlag(1);
#elif (APP_SAVE_TO == APP_SAVE_INFLASH)
	u32 taddr = (u32)(&iaphd);
	STMFlashRead(APP_MARK_ADDR, (u16*)(taddr), sizeof(IapHead)/sizeof(u16));
#endif

	crc = CalcCrc32((u8*)&(iaphd), sizeof(IapHead)-sizeof(iaphd.checkcrc));		// 校验之前所有字段

	printf("UpdateApp begin\r\n");
#if (0)
	printf("iaphd.upmask=%s\r\n", iaphd.upmask);
	printf("iaphd.rdaddr=0x%x\r\n", iaphd.rdaddr);
	printf("iaphd.wraddr=0x%x\r\n", iaphd.wraddr);
	printf("iaphd.size=0x%x\r\n", iaphd.size);
	printf("iaphd.wsize=0x%x\r\n", iaphd.wsize);
	printf("iaphd.appcheck=0x%x\r\n", iaphd.appcheck);
	printf("calc iaphd crc = 0x%x, save iaphd.checkcrc = 0x%x\r\n", crc, iaphd.checkcrc);
#endif

	// 校验数据
	if (crc != iaphd.checkcrc)
	{
		printf("updatedata checkcrc err\r\n");
		return -1;		// 校验错
	}

	if (strcmp((void*)STM32_MARK, iaphd.upmask) != 0)		// 比较
	{
		printf("updatedata upmask err\r\n");
		return 0;		// 不一致, 返回
	}

	saveaddr = iaphd.rdaddr + APP_SAVE_BASE;	// 读取地址
	stfaddr = iaphd.wraddr + STM32_FLASH_BASE;	// 写入地址
	appsize = iaphd.size;
	wsize = iaphd.wsize;

	if ((stfaddr < STM32_APP_ADDR)	||
		(stfaddr >= (STM32_FLASH_BASE + STM32_FLASH_SIZE)) ||
		(saveaddr < APP_ADDR_BEG)	||
		0	)	// 地址不符合要求
	{
		printf("flash addr error\r\n");
		return -2;
	}

	crc = GetAppSaveDataCrc(saveaddr, appsize);

	if (crc != iaphd.appcheck)
	{
		printf("save data crc check error, crc=0x%x, appcheck=0x%x\r\n", crc, iaphd.appcheck);
		return -2;
	}

	// 从存储区中读取复位地址和栈顶地址
#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
	{
		u8 buff[8];
		DatFlashRdData(APP_ADDR_BEG, buff, 8);
		stacktop  = *(__IO uint32_t*)(buff);	// 栈顶地址
		resetaddr = *(__IO uint32_t*)(buff+4);	// 复位地址
	}
#elif (APP_SAVE_TO == APP_SAVE_EXRAM || APP_SAVE_TO == APP_SAVE_INFLASH)
	stacktop  = *(__IO uint32_t*)(APP_ADDR_BEG);	// 栈顶地址
	resetaddr = *(__IO uint32_t*)(APP_ADDR_BEG+4);	// 复位地址
#else
	return -2;
#endif

	printf("save file stacktop = 0x%x, resetaddr=0x%x\r\n", (unsigned int)stacktop, (unsigned int)resetaddr);
	if ((resetaddr >= STM32_APP_ADDR) &&
		((resetaddr & 0xFF000000) == 0x08000000) &&		// 判断是否为0X08XXXXXX
		((stacktop  & 0x2FFC0000) == 0x20000000) &&		// 检查栈顶地址是否合法
		1	)
	{
		printf("save data ok\r\n");
	}
	else
	{
		printf("save data not right\r\n");
		return -2;
	}
#if (APP_SAVE_TO == APP_SAVE_EXRAM)
	SetAppEnFlag(0);		// 清除app正确标志....
#endif
	printf("UpdateApp: from add=0x%x to stadd=0x%x, size=%d, wsize=%d\r\n", saveaddr, stfaddr, appsize, wsize);

	rslt = WriteAppBinData(stfaddr, saveaddr, wsize);		// 更新FLASH代码

#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
	DatFlash4kErase(APP_MARK_ADDR, 1);						// 更新完成, 擦除掉标志, 参数等
#elif (APP_SAVE_TO == APP_SAVE_EXRAM)
	memset((void*)APP_MARK_ADDR, 0xff, IAPHEAD_LEN);		// 全部写为0xff
#elif (APP_SAVE_TO == APP_SAVE_INFLASH)
	STMFlashErase(GetFlashSectorIdx(APP_MARK_ADDR), 1);
#endif

#if (APP_SAVE_TO == APP_SAVE_EXRAM)
	if (rslt == 0)
	{
		SetAppEnFlag(1);
	}
#endif
	return rslt;
}

// 关闭所有
__WEAK void CloseAllPeripheral()
{
	__disable_irq();			// 关闭中断

#if (0)	// 临时措施,后续需做兼容
	HAL_DeInit();

	LL_GPIO_DeInit(GPIOA);
	LL_GPIO_DeInit(GPIOB);
	LL_GPIO_DeInit(GPIOC);
	LL_GPIO_DeInit(GPIOD);
	LL_GPIO_DeInit(GPIOE);
	LL_GPIO_DeInit(GPIOF);
	LL_GPIO_DeInit(GPIOG);

	LL_EXTI_DeInit();

	HAL_ADC_DeInit(&hadc3);

	HAL_CAN_DeInit(&HCAN);

	HAL_SRAM_DeInit(&hsram1);
	HAL_SRAM_DeInit(&hsram2);
	HAL_SRAM_DeInit(&hsram3);
	HAL_SRAM_DeInit(&hsram4);

	LL_SPI_DeInit(SPI1);
	LL_SPI_DeInit(SPI2);

	LL_TIM_DeInit(TIM1);
	LL_TIM_DeInit(TIM2);
	LL_TIM_DeInit(TIM3);
	LL_TIM_DeInit(TIM4);
	LL_TIM_DeInit(TIM5);
	LL_TIM_DeInit(TIM6);
	LL_TIM_DeInit(TIM7);
	LL_TIM_DeInit(TIM8);

	LL_USART_DeInit(USART1);
	LL_USART_DeInit(USART2);
	LL_USART_DeInit(USART3);
	LL_USART_DeInit(UART4);
	LL_USART_DeInit(UART5);
#else
	LL_EXTI_DeInit();
#endif

	__disable_irq();			// 关闭中断
}

//===================================================================

// 跳转到应用程序段
int JumpToApp(void)
{
	typedef  void (*iapfun)(void);				// 定义一个函数类型的参数.
	int rslt;
	u32 resetaddr, stacktop;

	printf("Begin run app\r\n");

	// 从flash中读取复位地址和栈顶地址
	stacktop  = *(__IO uint32_t*)(STM32_APP_ADDR);		// 栈顶地址
	resetaddr = *(__IO uint32_t*)(STM32_APP_ADDR+4);	// 复位地址
	rslt = 0;

#if (APP_SAVE_TO == APP_SAVE_EXRAM)
	rslt = IsAppRight();
#endif

	printf("stacktop = 0x%x, resetaddr=0x%x\r\n", (unsigned int)stacktop, (unsigned int)resetaddr);

	DelayMs(100);
	
	if (	(rslt == 0) &&
			(resetaddr >= STM32_APP_ADDR) &&
			((resetaddr & 0xFF000000) == 0x08000000) &&		// 判断是否为0X08XXXXXX
			((stacktop  & 0x2FFC0000) == 0x20000000) &&		// 检查栈顶地址是否合法
			1	)
	{
		iapfun jump;

		printf("Jump to app address\r\n");

		DelayMs(100);

		CloseAllPeripheral();
		DelayMs(100);

		jump = (iapfun)(resetaddr);	// 用户代码区第二个字为程序开始地址(复位地址)
		__set_MSP(stacktop);  		// 初始化 APP 堆栈指针 (用户代码区的第一个字用于存放栈顶地址)
		jump();						// 跳转到APP.
	}

	printf("run app failed\r\n");

	return -1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// app 调用iap模块功能接口函数

void IAPCleanUpdateArea(void)
{
#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
	// 擦除升级区域 norflash 的块
	u32 addr;
	int erflag;
	int count = 0;

	printf("IAPCleanUpdateArea, erase begin\r\n");

	addr = NOR_SAVE_BASE;
	while(addr < NOR_SAVE_END)
	{
		erflag = IsDatFlashEmpty(addr, SNF_BYTES_PER_SEC);	// 判断一个扇区是否为空

		if (erflag != 0)
		{
			DatFlash4kErase(addr, 1);					// 擦除一个扇区
			PutChar('.');
			count++;
			//erflag = IsDatFlashEmpty(addr, SNF_BYTES_PER_SEC);	// 判断一个扇区是否为空
			//printf("erase failed, addr=%d, idx=%d, flag=%d\r\n", addr, (addr-NOR_SAVE_BASE)/SNF_BYTES_PER_SEC, erflag);
		}
		addr += SNF_BYTES_PER_SEC;						// 地址增加
	}

	printf("IAPCleanUpdateArea finish, count=%d\r\n", count);
#elif (APP_SAVE_TO == APP_SAVE_EXRAM)
	memset((void*)EXRAM_SAVE_BASE, 0xff, STM32_FLASH_SIZE);		// 全部写为0xff
#elif (APP_SAVE_TO == APP_SAVE_INFLASH)
	STMFlashErase(GetFlashSectorIdx(APP_SAVE_BASE), (STF_UPDATE_SIZE+MARK_SIZE)/STM32_FLASH_SEC_SIZE);
#endif
}

// 写入升级起始地址, 长度和升级标志
void IapWritePara(u32 rdAddr, u32 wrAddr, u32 len, u32 wlen, u32 check)
{
	// 数据长度必须为扇区(STM32_FLASH_SEC_SIZE)的整数倍
	IapHead iaphd;
	memset(&iaphd, 0xff, sizeof(IapHead));

	strcpy(iaphd.upmask, STM32_MARK);
	iaphd.rdaddr = rdAddr;
	iaphd.wraddr = wrAddr;
	iaphd.size = len;
	iaphd.wsize = wlen;
	iaphd.appcheck = check;
	iaphd.checkcrc = CalcCrc32((u8*)&(iaphd.upmask[0]), sizeof(IapHead)-sizeof(iaphd.checkcrc));	// 校验之前所有字段

	printf("IapWritePara begin\r\n");

#if (0)
	printf("iaphd.upmask %s\r\n", iaphd.upmask);
	printf("iaphd.rdaddr=0x%x\r\n", iaphd.rdaddr);
	printf("iaphd.wraddr=0x%x\r\n", iaphd.wraddr);
	printf("iaphd.size=0x%x\r\n", iaphd.size);
	printf("iaphd.wsize=0x%x\r\n", iaphd.wsize);
	printf("iaphd.appcheck=0x%x\r\n", iaphd.appcheck);
	printf("iaphd.checkcrc = 0x%x\r\n", iaphd.checkcrc);
#endif

#if (APP_SAVE_TO == APP_SAVE_NORFLASH)
	DatFlash4kErase(NOR_MARK_ADDR, 1);		// 4k擦除
	u32 taddr = (u32)(&iaphd);
	DatFlashWrData(NOR_MARK_ADDR, (u8*)(taddr), sizeof(IapHead)/sizeof(u8));		// 写入
#elif (APP_SAVE_TO == APP_SAVE_EXRAM)
	memcpy((void*)EXRAM_MARK_ADDR, (void*)(&(iaphd)), sizeof(IapHead));
#elif (APP_SAVE_TO == APP_SAVE_INFLASH)
	STMFlashErase(GetFlashSectorIdx(APP_MARK_ADDR), 1);
	u32 taddr = (u32)(&iaphd);
	STMFlashWrite(APP_MARK_ADDR, (u16*)(taddr), sizeof(IapHead)/sizeof(u16), 3);		// 写入
#endif

	printf("IapWritePara end\r\n");

	DelayMs(100);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////




//////////////////////////////////////////////////////////////////////////////////////////////////////

