这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 企业专区 » 机智云Gizwits » 基于机智云物联网平台MCU模式基础教程及远程OTA

共2条 1/1 1 跳转至

基于机智云物联网平台MCU模式基础教程及远程OTA

助工
2021-01-26 18:22:57     打赏


编写背景:
       用户如果将开发了的产品发布上线销售,后期需要更新固件和程序,就需要用到远程OTA固件升级,就能对已发布的产品进行远程升级。

材料准备:
       1.机智云4G模组GC211

此模组采用中移模组ML302,需烧录机智云GAgent固件,支持网络,移动,联通,电信4G网络。



       2.秉火【F103开发板-指南者】




正文:
云端部署
创建新产品,可根据自己需求选择。



添加如下数据点,可根据自己需求


生成STM32F103代码下载备用



       简述STM32 启动
ARM7/ARM9 内核的控制器在复位后,CPU 会从存储空间的绝对地址0x000000 取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x000000(PC =0x000000)同时中断向量表的位置并不是固定的。然而,Cortex-M3 内核启动有3 种情况:
1、通过boot 引脚设置可以将中断向量表定位于SRAM 区,即起始地址为0x2000000,同时复位后PC 指针位于0x2000000 处;
2、通过boot 引脚设置可以将中断向量表定位于FLASH 区,即起始地址为0x8000000,同时复位后PC 指针位于0x8000000 处;
3、通过boot 引脚设置可以将中断向量表定位于内置Bootloader 区;
Cortex-M3 内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断
入口向量地址,这样在Cortex-M3 内核复位后,会自动从起始地址的下一个32 位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比ARM7/ARM9 内核,Cortex-M3 内核则是固定了中断向量表的位置而起始地址是可变化的。
总结一下STM32 的启动文件和启动过程。首先对栈和堆的大小进行定义,并在代码区的起始处建立中断向量表,其第一个表项是栈顶地址,第二个表项是复位中断服务入口地址。
然后在复位中断服务程序中跳转C/C++标准实时库的main 函数,完成用户堆栈等的初始化后,跳转.c 文件中的main 函数开始执行C 程序。假设STM32被设置为从内部FLASH 启动(这也是最常见的一种情况),中断向量表起始地位为0x8000000,则栈顶地址存放于0x8000000处,而复位中断服务入口地址存放于0x8000004 处。当STM32 遇到复位信号后,则从0x80000004 处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转main函数,最后进入mian 函数。

分析下OTA 需求,我们将建立两个工程,分别是Bootloader 还有APP,我们将Bootloader下载到FLASH 空间0x8000000 地址处,那么STM32 启动后会首先执行我们的Bootloader 程序,然后就可以按照我们意愿实现OTA 了。

       FLASH 区间划分
       根据需求,我们将STM32F103VET6 这个芯片Flash 空间划分出4 个区域:Bootloader、FLAG、APP、APP_BAK。四个区间作用描述如下:
   Bootloader:存储Bootloader 固件,MCU 上电后首先运行该固件。
   FLAG:存储有关升级的相关标志位,Bootloader 和APP 都需要操作该区域。
              升级标志位(2B)
固件大小(4B)
MD5加密数据(16B)
   APP:存储用户程序固件。
   APPBAK:临时存储云端下发的新固件,升级固件的一个过渡存储区。
STM32F103VET6分区方案如下图所示:



BOOTLOADER分区部分
Bootloader程序流程
Bootloader 的主要职能是在有升级任务的时候将 APPBAK 分区里面的固件拷贝到 APP 区域。当然,这期间需要做很多的工作,比如升级失败的容错等等。具体的流程可以参考图示。需要注意的是,在校验 MD5 正确后开始搬运固件数据期间,MCU 出现故障(包括突然断电),MCU 应发生复位操作(FLAG 区域数据未破坏),复位后重新开始执行 Bootloader,从而避免 MCU 刷成板砖。



Bootloader程序配置
为了方便构架,此处我采用cubemx构建项目,生成keil工程。
创建STM32F103VE项目



配置外部时钟



配置时钟72M



配置debug为serisl wire



配置串口4为BootLoader的日志打印口。



生成keil代码添加驱动flash.c,gagent_md5.c和app.c以及对应的组,如何添加此处不过多介绍(基础的软件操作),只提供文件路径。











驱动文件的编写以及函数介绍我此处不过多介绍,可以在福建进行下载源文件,也可以在文章末尾复制代码。代码只讲重点部分。
Main.c添加头文件#include "app.h"



主函数添加APP_Process();



根据自己分区大小设置区域,我的是18K,2k,54k,54k



Bootloader编译设置
按照 Bootloader 流程编写好代码,需要我们对 KEIL 工程做相应配置,需要注意的是编译的 Bootloader 固件大小不超过最大可允许的 18KB。Keil 编译器需要设置如下:



Flash 烧写地址设置有效


设置ST-LINK按块擦除 FLASH 区间和烧写程序




编译烧录程序,到此BootLoader编写烧录完成。



APP程序分区部分
固件接收流程
做好 BOOTLOADER 工作后,我们开始写 APP 分区的代码。APP 分区固件的编写要注意硬件版本号和软件版本号,软件版号作为升级迭代很重要的标志。需要注意的是,中断向量地址偏移的定义,这个地方需要我们尤其注意,我在开发过程中在这个地方排查了好长时间。STM32 标准库默认中断向量地址偏移为0x0,但是我们APP 实际的偏移是0x5000。如果不修改,APP 也可以正常加载运行,但是不会相应中断。所以,我们需要根据实际APP 下载的起始地址,对中断向量地址偏移做定义。



CUBEMX部署
解压云端生成的代码,由于生成的代码是STM32F103c8的,我们需要通过CUBEMX转换成STM32F103VET6的代码来适应我们的秉火开发板,创建一个新的cubeMX项目,导入自动代码的CUBEMX工程.



目录保存在解压代码所在的路径。



修改时钟树为72M,注意需要同BootLoader时钟。



根据项目需求以及我们自己创建的数据点,我们需要控制RGB灯,蜂鸣器,DHT11,在此处我们需要对引脚进行初始化。


配置RGB灯,采用定时器输出PWM控制。



有源蜂鸣器,和默认代码按键2冲突,修改按键2的GPIO为PB15



配置温湿度传感器DHT11



为每个单独的文件生成.C.H文件,方便调用头文件



生成KEIL的代码



到此我们就完成了项目的构建。
编译器设置
       因为硬件FLASH 空间限定,我们需要对APP 的固件大小做严格的限制。本方案,
针对秉火开发板 我们可允许的最大固件为54KB。需要升级的新固件同样最大可支持54KB。
1、设置FLASH 固件下载地址



2、配置中断向量偏移地址设置



3.重新构建工程,添加相关头文件。



4.添加flash.h,flash.h, gagent_md5.c, gagent_md5.h, app.c文件到项目



5. 在mian.c里面添加一下头文件



在main.c里面添加如下代码



       6.在gizwits_protocol.h里面添加如下代码

  1. /************************************

  2. * OTA

  3. ************************************/

  4. #define PIECE_MAX_LEN  256


  5. #define FILE_MD5_MAX_LEN  32

  6. //#define SSL_MAX_LEN (FILE_MD5_MAX_LEN/2)


  7. #define UPDATE_IS_HEX_FORMAT    0 // Piece Send Format 0,nohex; 1,hex


  8. typedef enum

  9. {

  10.     HEX = 0,


  11.     BIN,


  12. } otaDataType;


  13. __packed typedef struct


  14. {

  15.     uint16_t                       piecenum;


  16.     uint16_t                       piececount;


  17.     uint8_t                                piececontent[PIECE_MAX_LEN];


  18. } updataPieceData_TypeDef;


  19. typedef struct

  20. {

  21.     uint16_t rom_statue;


  22.     uint32_t rom_size;


  23.     uint8_t  ssl_data[SSL_MAX_LEN];


  24. } updateParamSave_t;


  25. typedef struct

  26. {

  27.     uint8_t otaBusyModeFlag;


  28.     uint32_t updateFileSize;       //Rom Size


  29.     MD5_CTX ctx;


  30.     updateParamSave_t update_param;//Save Update Param


  31. } mcuOTA_t;

  32. int8_t Pro_W2D_UpdateDataHandle(uint8_t *inData , uint32_t dateLen , otaDataType formatType);


  33. int8_t Pro_D2W_UpdateReady(uint8_t *md5Data , uint16_t md5Len);


  34. int8_t Pro_W2D_UpdateCmdHandle(uint8_t *inData,uint32_t dataLen);


  35. void Pro_D2W_UpdateSuspend(void);

  36. void Pro_D2W_Ask_Module_Reboot(void);

复制代码






7. 在gizwits_protocol.c里面添加相应的代码,此处不一一例举,只截图详细,详细代码请看附件代码(注意修改gizwitsHandle函数,此处不做截图)。



       8.在gizwits_prduct.h修改这个参数可改变版本号,当前版本需要大于上一个版本。



       9.将代码下载到对应的地址,前面已配置好地址。后面需要生成bin文件才能进行推送(机智云最新版本的升级界面支持hex),在配置里面修改生成bin文件。





       10.在产品的OTA界面进行固件升级,升级配置如下,手动推送需要主动更新,静默推送自动更新,其余按照要求配置即可。



       到此本教程结束,如有疑问联系机智云技术支持,15120205205(QQ,微信,电话同号)

FLASH驱动编写Flash.c1
  1.    #include "flash.h"

  2. 2.    #include <stdio.h>


  3. 3.    #include <string.h>


  4. 4.    volatile uint32_t flashWriteOffset = SYS_APP_BAK_SAVE_ADDR_BASE;


  5. 5.    volatile uint32_t flashReadOffset = SYS_APP_BAK_SAVE_ADDR_BASE;


  6. 6.    /* Exported types ------------------------------------------------------------*/


  7. 7.    /** @defgroup FLASH_Exported_Types FLASH Exported Types


  8. 8.      * @{

  9. 9.      */

  10. 10.  /* MCU OTA */

  11. 11.  void flash_erase_page(uint8_t flashPage , uint32_t addr_base)


  12. 12.  {

  13. 13.      HAL_FLASH_Unlock();

  14. 14.      FLASH_EraseInitTypeDef f;


  15. 15.      f.TypeErase = FLASH_TYPEERASE_PAGES;


  16. 16.      f.PageAddress = flashPage + (addr_base - SYS_Bootloader_SAVE_ADDR_BASE)/FLASH_PAGE_SIZE;


  17. 17.      f.NbPages = 1;


  18. 18.     

  19. 19.      uint32_t PageError = 0;


  20. 20.      HAL_FLASHEx_Erase(&f, &PageError);


  21. 21.      HAL_FLASH_Lock();

  22. 22.  }

  23. 23.  void flash_erase(uint32_t size , uint32_t addr_base)


  24. 24.  {

  25. 25.      uint32_t flashPageSum;


  26. 26.        uint32_t i;

  27. 27.      /*如果小于2048做处理*/

  28. 28.      if(size < FLASH_PAGE_SIZE)


  29. 29.          size = FLASH_PAGE_SIZE;                                                                               //


  30. 30.      /* 计算需要擦写的Flash页 */

  31. 31.      if((size % FLASH_PAGE_SIZE) == 0)


  32. 32.      {

  33. 33.          flashPageSum = size / FLASH_PAGE_SIZE;                        //小于一页擦除一页


  34. 34.      }

  35. 35.      else

  36. 36.      {

  37. 37.          flashPageSum = (size / FLASH_PAGE_SIZE) + 1; //大于一页擦除n+1页


  38. 38.      }

  39. 39.      for(i = 0;i<flashPageSum;i++)


  40. 40.      {

  41. 41.               flash_erase_page(i,addr_base);                                                      //基址累加擦除flash

  42. 42.      }

  43. 43.  }

  44. 44.   

  45. 45.  void writeFlash(uint64_t * buf_to_save , uint16_t len , uint32_t wFlashAddr)


  46. 46.  {

  47. 47.      uint16_t count=0;


  48. 48.      if(wFlashAddr >= 0x08020000)


  49. 49.      {

  50. 50.  #ifdef DEBUG

  51. 51.          printf("Waring:Flash Write Addr Error\r\n");


  52. 52.  #endif

  53. 53.          flashWriteOffset = SYS_APP_BAK_SAVE_ADDR_BASE;


  54. 54.          return;


  55. 55.      }

  56. 56.      HAL_FLASH_Unlock();

  57. 57.   

  58. 58.        while(count < len)

  59. 59.        {

  60. 60.                      HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,(wFlashAddr + count*8),buf_to_save[count]); //вflashһٶַ֘дɫѫؖè16λé


  61. 61.                      count ++;     


  62. 62.        }

  63. 63.        HAL_FLASH_Lock();

  64. 64.  }

  65. 65.   

  66. 66.  void readFlash(uint64_t * buf_to_get,uint16_t len , uint32_t readFlashAddr)


  67. 67.  {

  68. 68.      uint16_t count=0;

  69. 69.      while(count<len)

  70. 70.      {

  71. 71.        buf_to_get[count]=*(uint64_t *)(readFlashAddr + count*8);


  72. 72.        count++;

  73. 73.      }

  74. 74.  }

  75. 75.  /*写Flash,控制写长度,Flash地址偏移*/

  76. 76.  void wFlashData(uint8_t * buf_to_save , uint16_t len , uint32_t wFlashAddr)


  77. 77.  {

  78. 78.      uint8_t WriteFlashTempBuf[PIECE_MAX_LEN];//写Flash临时缓冲区


  79. 79.      uint16_t WriteFlashTempLen = 0;//写Flash长度


  80. 80.      uint8_t rem;

  81. 81.      memset(WriteFlashTempBuf,0xEE,sizeof(WriteFlashTempBuf));//写Flash临时缓冲区首先全部填充0xEE


  82. 82.      memcpy(WriteFlashTempBuf,buf_to_save,len);//临时缓冲区


  83. 83.      WriteFlashTempLen = len;


  84. 84.      if(len%8 != 0)


  85. 85.      {

  86. 86.        rem = len%8;

  87. 87.        WriteFlashTempLen = len +8 - rem;

  88. 88.      }

  89. 89.      writeFlash((uint64_t *)&WriteFlashTempBuf ,  WriteFlashTempLen/8 , wFlashAddr);


  90. 90.  }

  91. 91.  void rFlashData(uint8_t * buf_to_get , uint16_t len , uint32_t rFlashAddr)


  92. 92.  {

  93. 93.      uint8_t ReadFlashTempBuf[PIECE_MAX_LEN];//读Flash临时缓冲区


  94. 94.      uint16_t ReadFlashTempLen = 0;//读Flash长度


  95. 95.      uint8_t rem;


  96. 96.      if(len%8 == 0)


  97. 97.      {

  98. 98.          ReadFlashTempLen = len;


  99. 99.          readFlash((uint64_t *)&ReadFlashTempBuf,ReadFlashTempLen/8 , rFlashAddr);


  100. 100.            memcpy(buf_to_get,ReadFlashTempBuf,len);


  101. 101.        }

  102. 102.        else

  103. 103.        {

  104. 104.             rem = len%8;

  105. 105.            ReadFlashTempLen = len + 8 - rem;


  106. 106.            readFlash((uint64_t *)&ReadFlashTempBuf,ReadFlashTempLen/8 , rFlashAddr);


  107. 107.            memcpy(buf_to_get,ReadFlashTempBuf,len);


  108. 108.        }

  109. 109.    }

  110. 110.    /***IAP****/

  111. 111.    typedef  void (*iapfun)(void);


  112. 112.    iapfun jump2app;


  113. 113.    uint16_t iapbuf[1024];   


  114. 114.     

  115. 115.    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))


  116. 116.    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))


  117. 117.    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))


  118. 118.     

  119. 119.     

  120. 120.    /*#define iar

  121. 121.    #ifdef iar

  122. 122.    //设置栈顶地址

  123. 123.    //addr:栈顶地址

  124. 124.     

  125. 125.    void MSR_MSP(uint32_t addr)


  126. 126.    {

  127. 127.      asm("MSR MSP, r0 ");


  128. 128.      asm("BX r14");


  129. 129.    }

  130. 130.    #else

  131. 131.    __asm void MSR_MSP(uint32_t addr)


  132. 132.    {

  133. 133.        MSR MSP, r0                 //set Main Stack value


  134. 134.        BX r14


  135. 135.    }

  136. 136.    #endif

  137. 137.    */

  138. 138.    __asm void MSR_MSP(uint32_t addr)


  139. 139.    {

  140. 140.        MSR MSP, r0                 //set Main Stack value


  141. 141.        BX r14

  142. 142.    }

  143. 143.     

  144. 144.     

  145. 145.    void iap_load_app(uint32_t appxaddr)


  146. 146.    {

  147. 147.      if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)

  148. 148.      {

  149. 149.    #ifdef DEBUG

  150. 150.             printf("Stack Success!\r\n");

  151. 151.    #endif

  152. 152.             jump2app=(iapfun)*(vu32*)(appxaddr+4);

  153. 153.             MSR_MSP(*(vu32*)appxaddr);

  154. 154.             /* Relocate vector table */

  155. 155.          SCB->VTOR = (uint32_t)(appxaddr - SYS_Bootloader_SAVE_ADDR_BASE);


  156. 156.             jump2app();

  157. 157.      }

  158. 158.      else

  159. 159.      {

  160. 160.    #ifdef DEBUG

  161. 161.             printf("Stack Failed!\r\n");

  162. 162.    #endif

  163. 163.      }

  164. 164.    }

复制代码


flash.h1.
  1.    #ifndef _FLASH_

  2. 2.    #define _FLASH_

  3. 3.    #include "stm32f1xx_hal.h"


  4. 4.    //#define DEBUG

  5. 5.    //#define PROTOCOL_DEBUG

  6. 6.    typedef uint32_t  u32;


  7. 7.    typedef uint16_t u16;


  8. 8.    typedef uint8_t  u8;


  9. 9.    typedef __IO uint32_t  vu32;


  10. 10.  typedef __IO uint16_t vu16;


  11. 11.  typedef __IO uint8_t  vu8;


  12. 12.  /* BootLoader Flash首地址 */

  13. 13.  #define SYS_Bootloader_SAVE_ADDR_BASE       0x08000000//Bootloader首地址//支持Bootloader大小18KB


  14. 14.  /* 升级参数存储 */

  15. 15.  #define UPDATE_PARAM_SAVE_ADDR_BASE         0x08004800


  16. 16.  #define UPDATE_PARAM_MAX_SIZE               (2*1024)//支持参数大小2KB


  17. 17.  /* APP Flash首地址 */

  18. 18.  #define SYS_APP_SAVE_ADDR_BASE              0x08005000


  19. 19.  #define APP_DATA_MAX_SIZE                   (54*1024)//支持APP大小54KB


  20. 20.  /* APP BAK Flash首地址 */

  21. 21.  #define SYS_APP_BAK_SAVE_ADDR_BASE          0x08012800


  22. 22.  #define APP_BAK_DATA_MAX_SIZE               (54*1024)//支持APP_BAK大小54KB


  23. 23.  /* 升级参数 */

  24. 24.  #define PIECE_MAX 256


  25. 25.  #define SSL_MAX_LEN 16


  26. 26.  typedef struct


  27. 27.  {

  28. 28.      uint16_t rom_statue;


  29. 29.      uint32_t rom_size;


  30. 30.      uint8_t  ssl_data[SSL_MAX_LEN];


  31. 31.  }update_param_def;

  32. 32.  /* FLASH页大小 */

  33. 33.  //#define FLASH_PAGE_SIZE           0x400   //1KB


  34. 34.  /* 大数据分片大小 */

  35. 35.  //#define BIGDATA_PIECE_MAX 200 //支持的最大数据分片即大数据最大传输单元,200B


  36. 36.  #define PIECE_MAX_LEN  256


  37. 37.  void save_param_to_flash(uint16_t * buf_to_save,uint16_t len );


  38. 38.  void read_param_from_flash(uint16_t * buf_to_get,uint16_t len);


  39. 39.  void set_flash_flag_to_updata(uint16_t crc_code);


  40. 40.  void rFlashData(uint8_t * buf_to_get , uint16_t len , uint32_t rFlashAddr);


  41. 41.  void wFlashData(uint8_t * buf_to_save , uint16_t len , uint32_t wFlashAddr);


  42. 42.  void iap_load_app(uint32_t appxaddr);


  43. 43.  void flash_erase(uint32_t size , uint32_t addr_base);


  44. 44.  #endif

复制代码


APP驱动编写app.c1.
  1.    /**

  2. 2.      ****************************************************


  3. 3.      * @file           : main.c


  4. 4.      * @brief          : Main program body


  5. 5.      ****************************************************

  6. 6.      * @attention

  7. 7.      *

  8. 8.      * <h2><center>© Copyright (c) 2019 STMicroelectronics.


  9. 9.      * All rights reserved.</center></h2>


  10. 10.    *

  11. 11.    * This software component is licensed by ST under BSD 3-Clause license,


  12. 12.    * the "License"; You may not use this file except in compliance with the


  13. 13.    * License. You may obtain a copy of the License at:


  14. 14.    *                        opensource.org/licenses/BSD-3-Clause


  15. 15.    *

  16. 16.    ****************************************************


  17. 17.    */

  18. 18.  /* Includes ------------------------------------------------------------------*/


  19. 19.  #include "app.h"


  20. 20.  #include "../Src/md5/gagent_md5.h"


  21. 21.  #include "../Hal/flash.h"


  22. 22.  #include "usart.h"


  23. 23.   

  24. 24.  /*Global Variable*/

  25. 25.  /**

  26. 26.    * @brief  Main program.


  27. 27.    * @param  None


  28. 28.    * @retval None


  29. 29.    */

  30. 30.  update_param_def update_param;


  31. 31.  uint8_t md5_calc[SSL_MAX_LEN];


  32. 32.  MD5_CTX ctx;

  33. 33.  void mcu_restart()

  34. 34.  {

  35. 35.      //__set_FAULTMASK(1);


  36. 36.      NVIC_SystemReset();


  37. 37.  }

  38. 38.  int8_t ROM_MD5_Check(uint32_t sys_size , uint32_t FlashAddr , uint8_t *ssl)


  39. 39.  {

  40. 40.      uint8_t update_data_tmp[PIECE_MAX];


  41. 41.      uint32_t load_loop = 0;


  42. 42.      uint32_t remaind_data_len = sys_size;


  43. 43.      uint32_t valid_data_len = 0;


  44. 44.     

  45. 45.      GAgent_MD5Init(&ctx);


  46. 46.      if(0 == sys_size%PIECE_MAX)


  47. 47.      {

  48. 48.          load_loop = sys_size / PIECE_MAX;


  49. 49.      }

  50. 50.      else

  51. 51.      {

  52. 52.          load_loop = sys_size / PIECE_MAX + 1;


  53. 53.      }

  54. 54.  #ifdef DEBUG

  55. 55.      printf("Check New Sys ...loop = %d\r\n",load_loop);


  56. 56.  #endif

  57. 57.      for(uint32_t i = 0;i<load_loop;i++)


  58. 58.      {

  59. 59.          if(remaind_data_len > PIECE_MAX)


  60. 60.          {


  61. 61.              valid_data_len = PIECE_MAX;


  62. 62.          }


  63. 63.          else


  64. 64.          {


  65. 65.              valid_data_len = remaind_data_len;


  66. 66.          }


  67. 67.          memset(update_data_tmp,0,PIECE_MAX);


  68. 68.          rFlashData(update_data_tmp, valid_data_len, FlashAddr + i*PIECE_MAX);


  69. 69.          GAgent_MD5Update(&ctx, update_data_tmp, valid_data_len);


  70. 70.          remaind_data_len = remaind_data_len - valid_data_len;


  71. 71.  #ifdef DEBUG

  72. 72.          printf("*");


  73. 73.  #endif

  74. 74.      }

  75. 75.  #ifdef DEBUG

  76. 76.      printf("\r\n");

  77. 77.  #endif

  78. 78.      GAgent_MD5Final(&ctx, md5_calc);


  79. 79.  #ifdef DEBUG

  80. 80.      printf("MD5 Calculate Success \r\n ");


  81. 81.  #endif

  82. 82.      if(memcmp(ssl, md5_calc, SSL_MAX_LEN) != 0)


  83. 83.      {

  84. 84.  #ifdef DEBUG

  85. 85.          printf("Md5_Cacl Check Faild ,MCU OTA Faild\r\n ");


  86. 86.  #endif

  87. 87.  #ifdef PROTOCOL_DEBUG


  88. 88.          printf("MD5: ");


  89. 89.          for(uint16_t i=0; i<SSL_MAX_LEN; i++)


  90. 90.          {


  91. 91.              printf("%02x ", md5_calc[i]);


  92. 92.          }


  93. 93.          printf("\r\n");


  94. 94.  #endif

  95. 95.          return -1;


  96. 96.      }

  97. 97.      else

  98. 98.      {

  99. 99.  #ifdef DEBUG

  100. 100.            printf("MD5 Check Success ,MCU OTA Success\r\n ");


  101. 101.    #endif

  102. 102.            return 0;


  103. 103.        }

  104. 104.    }

  105. 105.    uint8_t update_new_system(uint32_t sys_size)


  106. 106.    {

  107. 107.        uint8_t update_data_tmp[PIECE_MAX];


  108. 108.        uint32_t load_loop = 0;


  109. 109.        uint32_t remaind_data_len = sys_size;


  110. 110.        uint32_t valid_data_len = 0;


  111. 111.     

  112. 112.        if(0 == sys_size%PIECE_MAX)


  113. 113.        {

  114. 114.            load_loop = sys_size / PIECE_MAX;


  115. 115.        }

  116. 116.        else

  117. 117.        {

  118. 118.            load_loop = sys_size / PIECE_MAX + 1;


  119. 119.        }

  120. 120.    #ifdef DEBUG

  121. 121.        printf("Copy New Sys ...loop = %d\r\n",load_loop);


  122. 122.    #endif

  123. 123.     

  124. 124.        flash_erase(update_param.rom_size , SYS_APP_SAVE_ADDR_BASE);


  125. 125.    #ifdef DEBUG

  126. 126.        printf("Copy New Sys\r\n");


  127. 127.    #endif

  128. 128.        for(uint32_t i = 0;i<load_loop;i++)


  129. 129.        {

  130. 130.            if(remaind_data_len > PIECE_MAX)


  131. 131.            {


  132. 132.                valid_data_len = PIECE_MAX;


  133. 133.            }


  134. 134.            else


  135. 135.            {


  136. 136.                valid_data_len = remaind_data_len;


  137. 137.            }


  138. 138.            memset(update_data_tmp,0,PIECE_MAX);


  139. 139.            rFlashData(update_data_tmp, valid_data_len, SYS_APP_BAK_SAVE_ADDR_BASE + i*PIECE_MAX);


  140. 140.            


  141. 141.            wFlashData(update_data_tmp , valid_data_len , SYS_APP_SAVE_ADDR_BASE + i*PIECE_MAX);


  142. 142.            remaind_data_len = remaind_data_len - valid_data_len;


  143. 143.    #ifdef DEBUG

  144. 144.            printf(".");


  145. 145.    #endif

  146. 146.        }

  147. 147.    #ifdef DEBUG

  148. 148.        printf("\r\n");


  149. 149.        printf("Copy Success , Wait to Check... \r\n");


  150. 150.    #endif

  151. 151.     

  152. 152.        if(0 == ROM_MD5_Check(update_param.rom_size , SYS_APP_SAVE_ADDR_BASE , update_param.ssl_data))


  153. 153.        {

  154. 154.    #ifdef DEBUG

  155. 155.            printf("New ROM Check Success , Wait to Load New Systerm \r\n");


  156. 156.    #endif

  157. 157.            flash_erase(sizeof(update_param_def), UPDATE_PARAM_SAVE_ADDR_BASE);


  158. 158.            mcu_restart();


  159. 159.        }

  160. 160.        else

  161. 161.        {

  162. 162.    #ifdef DEBUG

  163. 163.            printf("New ROM Check Faild , Update Faild , MCU Try To Update Again ,MCU Restart... \r\n");


  164. 164.    #endif

  165. 165.            mcu_restart();


  166. 166.        }

  167. 167.     

  168. 168.        return 0;


  169. 169.    }

  170. 170.    void APP_Process(void)


  171. 171.    {

  172. 172.      memset((uint8_t *)&update_param, 0 , sizeof(update_param_def));


  173. 173.      rFlashData((uint8_t *)&update_param, sizeof(update_param_def), UPDATE_PARAM_SAVE_ADDR_BASE);


  174. 174.        if(0xEEEE == update_param.rom_statue)


  175. 175.        {

  176. 176.    #ifdef DEBUG

  177. 177.            printf("Update Task ,Sys Will Load New Sys..Wait For A Moment \r\n");


  178. 178.            printf("Update Size [%d] \r\n",update_param.rom_size);


  179. 179.    #endif

  180. 180.            if(0 == ROM_MD5_Check(update_param.rom_size , SYS_APP_BAK_SAVE_ADDR_BASE , update_param.ssl_data))


  181. 181.            {


  182. 182.                update_new_system(update_param.rom_size);


  183. 183.            }


  184. 184.            else


  185. 185.            {


  186. 186.    #ifdef DEBUG

  187. 187.                printf("Check Faild , Go to Old Systerm\r\n");


  188. 188.    #endif

  189. 189.                flash_erase(sizeof(update_param_def), UPDATE_PARAM_SAVE_ADDR_BASE);


  190. 190.                if(((*(vu32*)(SYS_APP_SAVE_ADDR_BASE + 4)) & 0xFF000000) == 0x08000000)


  191. 191.                {


  192. 192.    #ifdef DEBUG

  193. 193.                    printf("Sys Will Load APP.....\r\n");


  194. 194.    #endif

  195. 195.                    iap_load_app(SYS_APP_SAVE_ADDR_BASE);


  196. 196.                }


  197. 197.                else


  198. 198.                {


  199. 199.    #ifdef DEBUG

  200. 200.                    printf("Start APP Failed!\r\n");


  201. 201.    #endif

  202. 202.                }


  203. 203.            }


  204. 204.            


  205. 205.        }

  206. 206.        else

  207. 207.        {

  208. 208.    #ifdef DEBUG

  209. 209.             printf("No Update Task , Go To APP ....%04X\r\n",update_param.rom_statue);


  210. 210.    #endif

  211. 211.             if(((*(vu32*)(SYS_APP_SAVE_ADDR_BASE + 4)) & 0xFF000000) == 0x08000000)


  212. 212.             {

  213. 213.    #ifdef DEBUG

  214. 214.                printf("Sys Will Load APP.....\r\n");


  215. 215.    #endif

  216. 216.                    iap_load_app(SYS_APP_SAVE_ADDR_BASE);

  217. 217.             }

  218. 218.             else

  219. 219.             {

  220. 220.    #ifdef DEBUG

  221. 221.                    printf("Start APP Failed!\r\n");


  222. 222.                  

  223. 223.    #endif

  224. 224.                  

  225. 225.             }

  226. 226.        }

  227. 227.    }

复制代码


app.h1.
  1.    /**

  2. 2.      ****************************************************

  3. 3.      * File Name          : gpio.h


  4. 4.      * Description        : This file contains all the functions prototypes for


  5. 5.      *                      the gpio  


  6. 6.      ****************************************************


  7. 7.      ** This notice applies to any and all portions of this file


  8. 8.      * that are not between comment pairs USER CODE BEGIN and


  9. 9.      * USER CODE END. Other portions of this file, whether


  10. 10.    * inserted by the user or by software development tools


  11. 11.    * are owned by their respective copyright owners.


  12. 12.    *

  13. 13.    * COPYRIGHT(c) 2019 STMicroelectronics


  14. 14.    *

  15. 15.    * Redistribution and use in source and binary forms, with or without modification,


  16. 16.    * are permitted provided that the following conditions are met:


  17. 17.    *   1. Redistributions of source code must retain the above copyright notice,


  18. 18.    *      this list of conditions and the following disclaimer.


  19. 19.    *   2. Redistributions in binary form must reproduce the above copyright notice,


  20. 20.    *      this list of conditions and the following disclaimer in the documentation


  21. 21.    *      and/or other materials provided with the distribution.


  22. 22.    *   3. Neither the name of STMicroelectronics nor the names of its contributors


  23. 23.    *      may be used to endorse or promote products derived from this software


  24. 24.    *      without specific prior written permission.


  25. 25.    *

  26. 26.    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"


  27. 27.    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE


  28. 28.    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE


  29. 29.    * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE


  30. 30.    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL


  31. 31.    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR


  32. 32.    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER


  33. 33.    * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,


  34. 34.    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE


  35. 35.    * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


  36. 36.    *

  37. 37.    ****************************************************


  38. 38.    */

  39. 39.   

  40. 40.  /* Define to prevent recursive inclusion -------------------------------------*/


  41. 41.  #ifndef __app_H

  42. 42.  #define __app_H

  43. 43.  #ifdef __cplusplus

  44. 44.   extern "C" {


  45. 45.  #endif

  46. 46.   

  47. 47.  /* Includes ------------------------------------------------------------------*/


  48. 48.  #include "main.h"


  49. 49.  #include <stdio.h>


  50. 50.  #include <string.h>


  51. 51.   

  52. 52.   

  53. 53.  void APP_Process(void);  


  54. 54.   

  55. 55.  #ifdef __cplusplus

  56. 56.  }

  57. 57.  #endif

  58. 58.  #endif /*__ pinoutConfig_H */


  59. 59.   

  60. 60.  /**

  61. 61.    * @}

  62. 62.    */

  63. 63.   

  64. 64.  /**

  65. 65.    * @}

  66. 66.    */

  67. 67.   

  68. 68.  /**************** (C) COPYRIGHT STMicroelectronics ****END OF FILE***/

复制代码



gagent_md5驱动编写gagent_md5.c1.
  1.    #include <string.h>


  2. 2.    #include "gagent_md5.h"


  3. 3.    unsigned char PADDING[] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


  4. 4.          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


  5. 5.          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


  6. 6.          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };


  7. 7.    void GAgent_MD5Init(MD5_CTX *context)


  8. 8.    {

  9. 9.        context->count[0] = 0;

  10. 10.      context->count[1] = 0;

  11. 11.      context->state[0] = 0x67452301;

  12. 12.      context->state[1] = 0xEFCDAB89;

  13. 13.      context->state[2] = 0x98BADCFE;

  14. 14.      context->state[3] = 0x10325476;

  15. 15.  }

  16. 16.  void GAgent_MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen)


  17. 17.  {

  18. 18.      unsigned int i = 0, index = 0, partlen = 0;

  19. 19.      index = (context->count[0] >> 3) & 0x3F;


  20. 20.      partlen = 64 - index;

  21. 21.      context->count[0] += inputlen << 3;

  22. 22.      if (context->count[0] < (inputlen << 3))


  23. 23.        context->count[1]++;

  24. 24.      context->count[1] += inputlen >> 29;


  25. 25.      if (inputlen >= partlen)

  26. 26.      {

  27. 27.        memcpy(&context->buffer[index], input, partlen);


  28. 28.        GAgent_MD5Transform(context->state, context->buffer);


  29. 29.        for (i = partlen; i + 64 <= inputlen; i += 64)


  30. 30.               GAgent_MD5Transform(context->state, &input[i]);


  31. 31.        index = 0;

  32. 32.      }

  33. 33.      else

  34. 34.      {

  35. 35.        i = 0;

  36. 36.      }

  37. 37.      memcpy(&context->buffer[index], &input[i], inputlen - i);


  38. 38.  }

  39. 39.  void GAgent_MD5Final(MD5_CTX *context, unsigned char digest[16])


  40. 40.  {

  41. 41.      unsigned int index = 0, padlen = 0;

  42. 42.      unsigned char bits[8];

  43. 43.      index = (context->count[0] >> 3) & 0x3F;


  44. 44.      padlen = (index < 56) ? (56 - index) : (120 - index);


  45. 45.      GAgent_MD5Encode(bits, context->count, 8);


  46. 46.      GAgent_MD5Update(context, PADDING, padlen);

  47. 47.      GAgent_MD5Update(context, bits, 8);

  48. 48.      GAgent_MD5Encode(digest, context->state, 16);


  49. 49.  }

  50. 50.  void GAgent_MD5Encode(unsigned char *output, unsigned int *input, unsigned int len)


  51. 51.  {

  52. 52.      unsigned int i = 0, j = 0;

  53. 53.      while (j < len)

  54. 54.      {

  55. 55.        output[j] = input[i] & 0xFF;

  56. 56.        output[j + 1] = (input[i] >> 8) & 0xFF;


  57. 57.        output[j + 2] = (input[i] >> 16) & 0xFF;


  58. 58.        output[j + 3] = (input[i] >> 24) & 0xFF;


  59. 59.        i++;

  60. 60.        j += 4;

  61. 61.      }

  62. 62.  }

  63. 63.  void GAgent_MD5Decode(unsigned int *output, unsigned char *input, unsigned int len)


  64. 64.  {

  65. 65.      unsigned int i = 0, j = 0;

  66. 66.      while (j < len)

  67. 67.      {

  68. 68.        output[i] = (input[j]) |

  69. 69.                      (input[j + 1] << 8) |

  70. 70.                      (input[j + 2] << 16) |

  71. 71.                      (input[j + 3] << 24);

  72. 72.        i++;

  73. 73.        j += 4;

  74. 74.      }

  75. 75.  }

  76. 76.  void GAgent_MD5Transform(unsigned int state[4], unsigned char block[64])


  77. 77.  {

  78. 78.      unsigned int a = state[0];

  79. 79.      unsigned int b = state[1];

  80. 80.      unsigned int c = state[2];

  81. 81.      unsigned int d = state[3];

  82. 82.      unsigned int x[64];

  83. 83.      GAgent_MD5Decode(x, block, 64);

  84. 84.      FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */

  85. 85.      FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */


  86. 86.      FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */


  87. 87.      FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */


  88. 88.      FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */

  89. 89.      FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */


  90. 90.      FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */


  91. 91.      FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */


  92. 92.      FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */

  93. 93.      FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */


  94. 94.      FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */


  95. 95.      FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */


  96. 96.      FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */


  97. 97.      FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */


  98. 98.      FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */


  99. 99.      FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */


  100. 100.     

  101. 101.      /* Round 2 */

  102. 102.      GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */

  103. 103.      GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */

  104. 104.      GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */


  105. 105.      GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */

  106. 106.      GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */

  107. 107.      GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */

  108. 108.      GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */


  109. 109.      GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */

  110. 110.      GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */

  111. 111.      GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */

  112. 112.      GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */

  113. 113.      GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */

  114. 114.      GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */

  115. 115.      GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */

  116. 116.      GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */

  117. 117.      GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */


  118. 118.     

  119. 119.      /* Round 3 */

  120. 120.      HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */

  121. 121.      HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */

  122. 122.      HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */


  123. 123.      HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */


  124. 124.      HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */

  125. 125.      HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */

  126. 126.      HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */

  127. 127.      HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */


  128. 128.      HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */

  129. 129.      HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */

  130. 130.      HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */

  131. 131.      HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */

  132. 132.      HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */

  133. 133.      HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */


  134. 134.      HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */


  135. 135.      HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */

  136. 136.     

  137. 137.      /* Round 4 */

  138. 138.      II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */

  139. 139.      II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */

  140. 140.      II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */


  141. 141.      II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */

  142. 142.      II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */

  143. 143.      II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */

  144. 144.      II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */


  145. 145.      II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */

  146. 146.      II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */

  147. 147.      II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */


  148. 148.      II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */

  149. 149.      II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */


  150. 150.      II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */

  151. 151.      II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */


  152. 152.      II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */

  153. 153.      II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */

  154. 154.      state[0] += a;

  155. 155.      state[1] += b;

  156. 156.      state[2] += c;

  157. 157.      state[3] += d;

  158. 158.    }

复制代码


gagent_md5.h1.  
  1.   #ifndef _GAGENT_MD5_H


  2. 2.    #define _GAGENT_MD5_H


  3. 3.     

  4. 4.    typedef struct

  5. 5.    {

  6. 6.        unsigned int count[2];

  7. 7.        unsigned int state[4];

  8. 8.        unsigned char buffer[64];

  9. 9.    } MD5_CTX;

  10. 10.  #define F(x,y,z) ((x & y) | (~x & z))


  11. 11.  #define G(x,y,z) ((x & z) | (y & ~z))


  12. 12.  #define H(x,y,z) (x^y^z)

  13. 13.  #define I(x,y,z) (y ^ (x | ~z))


  14. 14.  #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))


  15. 15.  #define FF(a,b,c,d,x,s,ac) \


  16. 16.  { \

  17. 17.      a += F(b,c,d) + x + ac; \

  18. 18.      a = ROTATE_LEFT(a,s); \

  19. 19.      a += b; \

  20. 20.  }

  21. 21.  #define GG(a,b,c,d,x,s,ac) \


  22. 22.  { \

  23. 23.      a += G(b,c,d) + x + ac; \

  24. 24.      a = ROTATE_LEFT(a,s); \

  25. 25.      a += b; \

  26. 26.  }

  27. 27.  #define HH(a,b,c,d,x,s,ac) \


  28. 28.  { \

  29. 29.      a += H(b,c,d) + x + ac; \

  30. 30.      a = ROTATE_LEFT(a,s); \

  31. 31.      a += b; \

  32. 32.  }

  33. 33.  #define II(a,b,c,d,x,s,ac) \


  34. 34.  { \

  35. 35.      a += I(b,c,d) + x + ac; \

  36. 36.      a = ROTATE_LEFT(a,s); \

  37. 37.      a += b; \

  38. 38.  }

  39. 39.  void GAgent_MD5Init(MD5_CTX *context);


  40. 40.  void GAgent_MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen);


  41. 41.  void GAgent_MD5Final(MD5_CTX *context, unsigned char digest[16]);


  42. 42.  void GAgent_MD5Transform(unsigned int state[4], unsigned char block[64]);


  43. 43.  void GAgent_MD5Encode(unsigned char *output, unsigned int *input, unsigned int len);


  44. 44.  void GAgent_MD5Decode(unsigned int *output, unsigned char *input, unsigned int len);


  45. 45.  #endif


复制代码




高工
2021-01-26 23:44:35     打赏
2楼

厉害啦


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]