// 因为main.c由STM32CubeMX自动生成
// 本模块实现用户自定义的main功能

#include "includes.h"

// 主程序位置: 16K（第2个扇区）
#define APROM_BASE				(FLASH_BASE + 0x4000)

#pragma pack(push, 1)
// 升级文件头
typedef struct // size=12
{
	unsigned short check;			// 数据有效标志, 0x55AA表示有效 
	unsigned upgrade_request : 1;	// 升级请求标志，0-无升级，1-有升级
	unsigned upgrade_result : 3;	// 升级失败标志，0-成功，1-失败，其余保留
	unsigned encrypt_flag : 2;		// 加密标志， 0-无加密，其余保留
	unsigned compress_flag : 2;		// 压缩标志， 0-无压缩，1-有压缩，其余保留
	unsigned rerseved1 : 8;			// 保留，必须为0xFF
	
	unsigned long upgrade_length; 	// 升级文件长度
	
	unsigned rerseved2 : 8;			// 保留，必须为0xFF
	unsigned rerseved3 : 8;			// 保留，必须为0xFF
	unsigned short crc;				// 对前面内容的crc校验
} TUpgradeHeader;
#pragma pack(pop)

// 实现stdout的硬件输出
int stdout_putchar(int ch)
{
	while(!(USART6->SR & LL_USART_SR_TXE));
	USART6->DR = (uint8_t) ch;

	return ch;
}

/* ************************************************************************* 
Bootloader和App跳转设计要点

1. Bootloader的程序在前面，大小为8K（0x08000000~0x08001FFF)
   App的程序在后面，从0x08002000开始，大小为0x3E000
2. Bootloader的内存在前面，大小为2K（0x20000000~0x200007FF）
   //App的内存在后面，从0x20000800开始，大小为0xF800
3. 从Bootloader跳转到App：
   1）关闭全局中断响应（__disable_irq）
   2）禁止中断允许（将各中断的允许位清零）
   3）清除中断标志（将各中断的标志位清零）
   4) 设置堆栈（MSP）为App的堆栈值
   5）跳转到App的程序入口
4. 从App跳转到Bootloader：
   直接系统复位，运行Bootloader
5. Bootloader工程的特殊处理
   1）在Target页，按照上述要求设置IROM1和IRAM1
   2）在Linker页，勾选“Use Memory Layout from Target Dialog”
6. App工程的特殊处理
   1）在Target页，按照上述要求设置IROM1和IRAM1
   2）在Linker页，勾选“Use Memory Layout from Target Dialog”
   3) 在system_stm32l4xx.c中修改以下代码：
      a. 将宏定义VECT_TAB_OFFSET改为0x2000
      b. 在SystemInit（）函数最后，重新打开全局中断响应（__enable_irq)
	  
7. 从App的任务中跳转到Bootloader，按第3条的方法试验不成功。
   具体原因未明，可能是因为要关掉的中断太多，没有一一试验；
   也可能是因为FreeRTOS将CPU的特权模式接管了，导致PSP、MSP设置无效等。
************************************************************************* */

// 实现从Bootloader跳转到App
typedef void (*pIapFun_TypeDef)(void); //定义一个函数类型的参数.
void JumpToEntry(uint32_t addr)
{
	__disable_irq();
	SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
	__set_MSP(*(volatile uint32_t *) addr);
	((pIapFun_TypeDef) (*(volatile uint32_t *) (addr + 4)))();
	
	// 不应该到这里来
	while(1)
		PRINTF(".");
}

// 最小匹配块大小
#define MIN_MATCH_LEN 3
#define MAX_MATCH_LEN (7 + MIN_MATCH_LEN)     // =match_len+MIN_MATCH_LEN

// 从Flash读出数据
long Get_Bits(unsigned long baseAddr, long *byte_index, unsigned char *bit_index, unsigned char bit_count)
{
    long word = 0;
    unsigned char total_bits = 0;
	unsigned short mask;
	unsigned char bits;
	static unsigned char buf;

    while(total_bits < bit_count)
    {
		// 读入一个字符
		if(*bit_index == 0)
			SFlash_BufferRead(baseAddr + *byte_index, &buf, 1);
		
        bits = 8 - *bit_index;
        if(bits > bit_count - total_bits)
            bits = bit_count - total_bits;

        mask = (1 << bits) - 1;
        word |= ((buf >> (*bit_index)) & mask) << total_bits;
        *bit_index += bits;
        if(*bit_index == 8)
        {
            (*byte_index)++;
            *bit_index = 0;
        }
        total_bits += bits;
    }

    return word;
}

// 写入数据到缓冲或Flash
void Put_Bytes(unsigned long *baseAddr, long *byte_index2, unsigned char *data_buf, unsigned char data_len)
{
    unsigned char i = 0;

    while(i < data_len)
    {
		Flash_rdBuf[*byte_index2] = data_buf[i];
		(*byte_index2)++;
		i++;
		
		// 够一页了就写入Flash
		if((*baseAddr + *byte_index2) % FLASH_ERASE_SIZE == 0)
		{
			Flash_BufferWrite(*baseAddr, Flash_rdBuf, *byte_index2);
			if(!Flash_BufferVerify(*baseAddr, Flash_rdBuf, *byte_index2))
				PRINTF("*** Verify flash failed at %08X, %d bytes\r\n", *baseAddr, *byte_index2);
			*baseAddr += *byte_index2;
			*byte_index2 = 0;
		}
    }
}

void Flash_Copy(uint32_t toAddr, uint32_t fromAddr, uint32_t nbytes)
{
	 uint32_t count, offset;
	
	 while(nbytes > 0)
	 {
		 // 计算页内偏移（只有第一页有可能非0）
	     offset = toAddr % FLASH_ERASE_SIZE;

		 // 计算一页内写入字节数
	     count = FLASH_ERASE_SIZE - offset;
	     if(count > nbytes)
	         count = nbytes;
	         
	     // 读本页内数据
	     SFlash_BufferRead(fromAddr, Flash_rdBuf, count);
		 // 写入本页数据
		 Flash_BufferWrite(toAddr, Flash_rdBuf, count);
		 if(!Flash_BufferVerify(toAddr, Flash_rdBuf, count))
			PRINTF("*** Verify flash failed at %08X, %d bytes\r\n", toAddr, count);
	     
	     // 计算下一页
	     fromAddr += count;
		 toAddr += count;
	     nbytes -= count;
	 }
}

// 解压升级包
void decompress_aprom(unsigned long toAddr, unsigned long fromAddr, long size)
{
	long byte_index = 0;
	unsigned char bit_index = 0;
	long byte_index2 = 0;
	unsigned char buf[MAX_MATCH_LEN];
	unsigned long baseAddr = toAddr;
	
    // 每一个记录为9/16bit，所以最后一个标志位在倒数第二字节
	while(byte_index < size - 1)
	{
        if(Get_Bits(fromAddr, &byte_index, &bit_index, 1) == 1)
        {
			// 获取一段重复字节
            long offset = Get_Bits(fromAddr, &byte_index, &bit_index, 12) + 1;
            unsigned char len = Get_Bits(fromAddr, &byte_index, &bit_index, 3) + MIN_MATCH_LEN;
			if(offset <= byte_index2)
			{
				// 全部在缓冲区内，还未写入Flash
				memmove(buf, Flash_rdBuf + byte_index2 - offset, len); 
			}
			else if(offset - byte_index2 >= len)
			{
				// 全部在Flash里
				Flash_BufferRead(baseAddr - (offset - byte_index2), buf, len);
			}
			else
			{
				// 前面部分在Flash
				Flash_BufferRead(baseAddr - (offset - byte_index2), buf, offset - byte_index2);
				// 后面部分在缓冲区
				memmove(buf + (offset - byte_index2), Flash_rdBuf, len - (offset - byte_index2)); 
			}
			Put_Bytes(&baseAddr, &byte_index2, buf, len);
        }
        else
		{
			// 获取一个原始字节
			buf[0] = Get_Bits(fromAddr, &byte_index, &bit_index, 8);
			Put_Bytes(&baseAddr, &byte_index2, buf, 1);
		}
	}
	// 最后不满一页的数据
	if(byte_index2 > 0)
	{
		Flash_BufferWrite(baseAddr, Flash_rdBuf, byte_index2);
		if(!Flash_BufferVerify(baseAddr, Flash_rdBuf, byte_index2))
			PRINTF("*** Verify flash failed at %08X, %d bytes\r\n", baseAddr, byte_index2);
	}
}

uint8_t Flash_apBuf[FLASH_ERASE_SIZE] = {0};

//#define _COPY_SFLASH_TO_APROM_

// 主循环，被main()函数调用
void main_task(void *argument)
{
	TUpgradeHeader upHeader = {0};
	uint32_t nbytes, fromAddr, toAddr;

	SFlash_Open();

#ifndef _COPY_SFLASH_TO_APROM_
	SFlash_BufferRead(SFLASH_UPGRADE_BASE, (uint8_t *) &upHeader, sizeof(upHeader));
	if(upHeader.check == 0x55AA && upHeader.upgrade_request == 1 
		&& do_crc_16(0, (uint8_t *) &upHeader, sizeof(upHeader)) == 0
		&& do_sflash_crc(SFLASH_UPGRADE_BASE + sizeof(upHeader), upHeader.upgrade_length) == 0)
#endif			
	{		
		// 解压
		PRINTF("\r\nDecompress upgrade package ...\r\n");
		
		HAL_FLASH_Unlock();
		
#ifndef _COPY_SFLASH_TO_APROM_		
		if(!upHeader.compress_flag)
			Flash_Copy(APROM_BASE, SFLASH_UPGRADE_BASE + sizeof(upHeader), upHeader.upgrade_length - 2);
		else
			decompress_aprom(APROM_BASE, SFLASH_UPGRADE_BASE + sizeof(upHeader), upHeader.upgrade_length - 2);
#else		
		nbytes = 50 * 1024;
		fromAddr = SFLASH_UPGRADE_BASE;
		toAddr = APROM_BASE;
		while(nbytes > 0)
		{
			// 读本页内数据
			SFlash_BufferRead(fromAddr, Flash_rdBuf, FLASH_ERASE_SIZE);
			// 写入本页数据
			Flash_BufferWrite(toAddr, Flash_rdBuf, FLASH_ERASE_SIZE);
			if(!Flash_BufferVerify(toAddr, Flash_rdBuf, FLASH_ERASE_SIZE))
				PRINTF("*** Verify flash failed at %08X, %d bytes\r\n", toAddr, FLASH_ERASE_SIZE);

			// 计算下一页
			fromAddr += FLASH_ERASE_SIZE;
			toAddr += FLASH_ERASE_SIZE;
			nbytes -= FLASH_ERASE_SIZE;
		}
		
		PRINTF("Verify ...\r\n");
		
		nbytes = 50 * 1024;
		fromAddr = SFLASH_UPGRADE_BASE;
		toAddr = APROM_BASE;
		while(nbytes > 0)
		{
			// 读本页内数据
			SFlash_BufferRead(fromAddr, Flash_rdBuf, FLASH_ERASE_SIZE);
			Flash_BufferRead(toAddr, Flash_apBuf, FLASH_ERASE_SIZE);
			if(memcmp(Flash_apBuf, Flash_rdBuf, FLASH_ERASE_SIZE) != 0)
			{
				PRINTF("data mismatch on 0x%x\r\n", toAddr);
				break;
			}
			// 计算下一页
			fromAddr += FLASH_ERASE_SIZE;
			toAddr += FLASH_ERASE_SIZE;
			nbytes -= FLASH_ERASE_SIZE;
		}
		
		PRINTF("OK.\r\n");
#endif
		
		HAL_FLASH_Lock();
		
#ifndef _COPY_SFLASH_TO_APROM_		
		SFlash_UnlockBPR();
		upHeader.upgrade_request = 0;
		SFlash_BufferWrite(SFLASH_UPGRADE_BASE, (uint8_t *) &upHeader, sizeof(upHeader));
		if(!SFlash_BufferVerify(SFLASH_UPGRADE_BASE, (uint8_t *) &upHeader, sizeof(upHeader)))
			PRINTF("*** Verify SFlash failed at %08X, %d bytes\r\n", SFLASH_UPGRADE_BASE, sizeof(upHeader));
			
#endif		
	}
	
	PRINTF("\r\nJump to APROM ...\r\n");
	while(!(USART6->SR & LL_USART_SR_TC));

	JumpToEntry(APROM_BASE);
	
	// 不应该到这里来
	while(1)
		PRINTF(".");
}
