漫談LiteOS-Huawei_IoT_Link_SDK_OTA 開發指導

  • 2020 年 4 月 21 日
  • 筆記

1概述

在應用升級過程中,無線下載更新(OTA)是一種常用,且方便的升級方式。Liteos採用的OTA升級方案基於LwM2M協議,實現了韌體升級(FOTA)和軟體升級(SOTA)兩種升級方案。用戶可根據自己的開發環境選擇合適的升級方式。 OTA功能程式碼結構如下圖:

2升級文件二進位文件結構

如圖所示,升級壓縮包中二進位文件如下圖所示,FOTA與SOTA採用相同的韌體格式。

 

  •  簽名校驗值:長度256位元組,對剩餘文件進行hash計算後,並進行sha256加密後得到的簽名密文。

  •  二進位資訊:預留長度32位元組,升級文件是全量升級文件或增量升級文件等資訊。

  •  升級文件內容:經壓縮後的升級文件,升級文件使用hdiffpatch演算法對新、舊鏡像進行運算生成的差分包,並使用lzma演算法進行壓縮。

3 存儲器介面

存儲器結構程式碼位於iot_link/sotrage目錄下。存儲器結構被劃分為兩部分,分別定義為存儲設備(storage.c)與設備分區(partition.c)。 存儲設備定義的是系統中使用的不同類型存儲器及介面,如內部flash,spi flash 或 nandflash等,所使用結構體如下:

typedef struct {
  int id;
  char *name;
  uint32_t size;
 
  void (*init)();
  int (*read)(void *buf, int32_t len, uint32_t offset);
  int (*write)(const uint8_t *buf, int32_t len, uint32_t offset);
  int (*erase)(uint32_t offset, int32_t len);
  int (*erase_write)(const void *buf, int32_t len, uint32_t offset);
}storage_device;

設備分區定義了用戶劃分的分區資訊,如下所示:

typedef struct _partition {
  uint8_t dev_id;
  char *name;
  uint32_t start_addr;
  uint32_t size;
}storage_partition;

設備分區定義了一組外部使用的介面,系統中可以使用這組介面進行相應的讀寫等操作。

int ota_storage_bin_read(int offset, void *buf, int len);
int ota_storage_bin_write(int offset, void *msg, int len);
int ota_storage_flag_read(ota_flag_t *flag);
int ota_storage_flag_write(ota_flag_t *flag);

4 OTA管理介面

系統中的OTA介面分為三個部分,OTA鏡像介面、OTA包管理介面,OTA簽名校驗介面。

4.1 OTA鏡像介面

OTA鏡像介面包括了OTA標誌鏡像和OTA二進位鏡像。其中OTA標誌存儲了升級版本號、升級文件大小、當前OTA狀態及OTA升級結果等資訊,其結構如下:

#pragma pack(1)
typedef struct
{
    uint8_t  ver[CN_OTA_VERSION_LEN];
    uint32_t ver_code;
    uint32_t file_size;   ///< the new bin file size
    uint32_t blk_size;    ///< the new bin block size
    uint32_t blk_num;     ///< the new bin block num
    uint32_t blk_cur;
    uint32_t file_off;    ///< the current offet to write
    uint32_t cur_state;   ///< defined by en_ota_status_t
    uint32_t ret_upgrade; ///< the upgrade,filled by the loader
    uint32_t updater;     ///< fota or sota
    uint32_t crc;         ///< all the ota information computed
}ota_flag_t;
#pragma pack()

在loader和app中維護的同一份OTA標誌,APP中會根據下載進度更改cur_state值,在loader中完成升級後會重置cur_state值並填充升級結果到ret_upgrade中,在進入app後將該結果上報至伺服器。 外部可調用以下介面進行OTA鏡像讀寫操作:

int storage_partition_read(int part_id, uint8_t *buf, uint32_t len, uint32_t offset);
int storage_partition_write(int part_id, uint8_t *buf, uint32_t len, uint32_t offset);
int storage_partition_erase_write(int part_id, uint8_t *buf, uint32_t len, uint32_t offset);
int storage_partition_erase(int part_id, uint32_t offset, uint32_t len);

4.2 OTA包管理介面

ota_pack實現對接FOTA功能的介面封裝。該文件內實現了以下介面:

struct pack_storage_device_api_tag_s { 
    int (*write_software)(pack_storage_device_api_s *thi, uint32_t offset, const uint8_t *buffer, uint32_t len); 
    int (*write_software_end)(pack_storage_device_api_s *thi, pack_download_result_e result, uint32_t total_len); 
    int (*active_software)(pack_storage_device_api_s *thi); 
};

4.3 OTA簽名校驗介面

Ota_checksum實現了升級包簽名驗證介面。系統中可調用下面介面獲取簽名校驗結果:

int ota_pack_get_signature_verify_result(int sign_len, int file_len);

若用戶使用自己的公私鑰對,可在此處更改prv_public_key為對應的公鑰key值。

5 簽名驗證

OTA升級包簽名驗證在下載完成後,發送執行升級命令階段時執行。

收到執行命令後,系統首先將調用簽名校驗介面進行二進位文件簽名校驗,只有在驗簽通過後,才會執行後續的升級流程,否則會退出升級流程並上報升級失敗到伺服器。

SOTA在接收到EN_PCP_MSG_EXCUTEUPDATE命令後,位於pcp.c文件中pcp_handle_msg函數。 FOTA流程的簽名校驗放在了ota_pack_man_software_write_end函數中。

6 Loader

進入Loader後會在ota_detection函數中讀取OTA標誌,檢測當前OTA狀態。若為UPDATING狀態,則執行升級,否則跳轉至APP。

增量升級過程首先會讀取升級文件內容並解析出新、舊鏡像大小及所使用的壓縮插件等資訊。

隨後調用以下介面:

hpatch_BOOL patch_decompress_with_cache(const hpatch_TStreamOutput* out_newData,
    const hpatch_TStreamInput*  oldData,const hpatch_TStreamInput*  compressedDiff,
    hpatch_TDecompress* decompressPlugin,TByte* temp_cache,TByte* temp_cache_end)

執行還原差分鏡像。 其中介面參數out_newData、oldData、compressedDiff,需要由用戶定義並實現對應的鏡像讀寫介面。

7 FOTA / SOTA

FOTA和SOTA都是基於LwM2M協議實現的升級方案,區別是基於不同的對象。

FOTA使用的是協議中定義的韌體對象升級方案,基於對象5實現。

SOTA使用自定義對象19,並使用了PCP協議作為數據傳輸協議。因此,在使用SOTA時需要將config.mk中的CONFIG_PCP_ENABLE選項使能並使用oc_lwm2m_ota_demo。

8 編譯

實現OTA功能需要編譯Loader鏡像和App鏡像。需要對編譯選項進行修改:

  •  Loader和App鏡像大小定義在鏈接腳本中,當定義的鏡像空間不足以容納生成的鏡像時,需要適當的調整其大小。Loader和app的鏈接腳本分別是os_loader.ld和os_loader.ld,在其中更改MEMORY中的FLASH大小,即可調節對應鏡像空間。

  • 在Makefile文件中,將cfg_seperate_load_mode值改為yes,使編譯系統通過鏈接腳本構建目標文件。

  •   Config.mk中,需要修改以下值:

CONFIG_MQTT_ENABLE := n // config中默認使用的時mqtt,需將其關閉 
CONFIG_LWM2M_ENABLE := y // 使能lwm2m 
CONFIG_LWM2M_TYPE := "wakaama_raw" //選擇使用的lwm2m實現類型 
CONFIG_OC_LWM2M_ENABLE := y // 使能lwm2m的oc介面 
CONFIG_OC_LWM2M_TYPE := "atiny_lwm2m_raw" CONFIG_OTA_ENABLE := y 
CONFIG_PCP_ENABLE := y // 若使用SOTA,需使能該選項 
CONFIG_DEMO_TYPE := "oc_lwm2m_ota_demo" // 並使用該DEMO進行SOTA功能驗證
  • 編譯loader:在GCC目錄下的config.mk文件中,將CONFIG_LOADER_ENABLE和CONFIG_OTA_ENABLE的值改為y,進行loader鏡像編譯。

  • 編譯APP:在config.mk文件中,將CONFIG_LOADER_ENABLE值改為n,同時需要使能CONFIG_PCP_ENABLE和CONFIG_OTA_ENABLE選項,進行App鏡像編譯。

9 新平台適配

若用戶需要在新平台上使用OTA升級功能,需要完成以下工作:

  •  完成存儲器的定義。

用戶需定義所使用的存儲器類型介面及分區資訊,並使用以下介面將其註冊到系統中:

int storage_dev_install(storage_device *dev, uint32_t max_num);
int storage_partition_init(storage_partition *part, int32_t max_num);
  •   完成OTA鏡像介面的定義。

用戶需定義ota_storage_t中flag鏡像與bin鏡像的讀寫介面,並通過下面介面進行註冊:

int ota_storage_install(const ota_storage_t *device);

對於FOTA系統,需要同時適配hal_get_ota_opt函數,填充並返回ota_opt_s的read_flash和write_flash介面。

  • 對於loader,需完成ota_detection函數,實現ota狀態檢查及鏡像的升級功能。

  • 在config.mk中定義CONFIG_LOADER_ENABLE值為y,進行Loader鏡像的編譯。將改值改為n,進行APP鏡像的編譯。

  • 在Makefile文件中將cfg_seperate_load_mode賦值為yes,以使用對應的鏈接腳本來構建對應的執行文件。