漫談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,以使用對應的鏈接腳本來構建對應的執行文件。