/* * Copyright (c) 2019 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief OTA upgrade interface */ #include #include #include #include #include #include #include #include #include #include #include #include "ota_image.h" #include #include "ota_manifest.h" #include "ota_breakpoint.h" #include "ota_file_patch.h" #include #include #include #include #ifdef CONFIG_OTA_LZMA #include #endif #ifdef CONFIG_BT_MANAGER #include #endif // full upgrade config (mbrec/param/recovery) // must set CONFIG_OTA_MUTIPLE_STORAGE=y for full upgrade #define OTA_FULL_UPGRADE (0) #define OTA_REQ_MAX_SIZE (256*1024) #define OTA_RX_STACKSIZE (1536) #ifdef CONFIG_UI_MEMORY_MANAGER #define OTA_RX_BUFSIZE (64*1024) #define OTA_IN_BUFSIZE (64*1024) #define OTA_OUT_BUFSIZE (32*1024) #else #define OTA_RX_BUFSIZE (8*1024) #define OTA_IN_BUFSIZE (4*1024) #define OTA_OUT_BUFSIZE (0) #endif #define OTA_MANIFESET_FILE_NAME "ota.xml" #define OTA_FLAG_USE_RECOVERY (1 << 0) #define OTA_FLAG_USE_RECOVERY_APP (1 << 1) #define OTA_FLAG_USE_NO_VERSION_CONTROL (1 << 2) #define OTA_FLAG_ERASE_PART_FOR_UPG (1 << 3) #define OTA_FLAG_KEEP_TEMP_PART (1 << 4) #define ota_use_recovery(ota) ((ota)->flags & OTA_FLAG_USE_RECOVERY) #define ota_use_recovery_app(ota) ((ota)->flags & OTA_FLAG_USE_RECOVERY_APP) #define ota_use_no_version_control(ota) ((ota)->flags & OTA_FLAG_USE_NO_VERSION_CONTROL) #define ota_erase_part_for_upg(ota) ((ota)->flags & OTA_FLAG_ERASE_PART_FOR_UPG) #define ota_keep_temp_part(ota) ((ota)->flags & OTA_FLAG_KEEP_TEMP_PART) #define EIO_READ (1001) struct ota_rx_info { char *rx_stack; uint8_t *rx_buf; uint32_t rx_bufsize; uint8_t *in_buf; uint32_t in_bufsize; uint8_t *out_buf; uint32_t out_bufsize; os_sem rx_get_sem; os_sem rx_put_sem; struct ring_buf rbuf; os_tid_t rx_tid; uint32_t offset; uint32_t size; uint32_t seg_size; bool is_raw; int rx_errno; }; struct ota_upgrade_info { int state; int backend_type; unsigned int flags; ota_notify_t notify; ota_ugrade_file_cb file_cb; struct ota_image *img; struct ota_storage *storage; #ifdef CONFIG_OTA_MUTIPLE_STORAGE struct ota_storage *storage_ext; #endif int data_buf_size; uint8_t *data_buf; uint32_t xml_offset; struct ota_manifest manifest; struct ota_breakpoint bp; struct ota_rx_info rx_info; }; #ifdef CONFIG_UI_MEMORY_MANAGER #define ota_rx_malloc ui_mem_res_alloc #define ota_rx_free ui_mem_res_free #else #define ota_rx_malloc mem_malloc #define ota_rx_free mem_free #endif static int ota_update_fw_version(struct ota_upgrade_info *ota, uint8_t file_id) { struct code_res_version code_res; bool need_save = false; if (file_id == PARTITION_FILE_ID_SDFS_PART_BASE || file_id == PARTITION_FILE_ID_SDFS_PART1 || file_id == PARTITION_FILE_ID_SDFS_PART2) { code_res.version_code = fw_version_get_code(); code_res.version_res = ota->manifest.fw_ver.version_res; need_save = true; } else if (file_id == PARTITION_FILE_ID_SYSTEM) { code_res.version_res = fw_version_get_res(); code_res.version_code = ota->manifest.fw_ver.version_code; need_save = true; } if (need_save) { if (nvram_config_set_factory(FIRMWARE_VERSION, &code_res, sizeof(struct code_res_version))) { SYS_LOG_ERR("SAVE FIRMWARE_VERSION FAILD\n"); return -1; } } return 0; } static int ota_save_res_version(struct ota_upgrade_info *ota) { struct ota_manifest *manifest = &ota->manifest; struct ota_file *file; for (int i = 0; i < manifest->file_cnt; i++) { file = &manifest->wfiles[i]; /*sdfs update need to save res version*/ if (file->file_id == PARTITION_FILE_ID_SDFS_PART_BASE || file->file_id == PARTITION_FILE_ID_SDFS_PART1 || file->file_id == PARTITION_FILE_ID_SDFS_PART2) { /*update res version*/ ota_update_fw_version(ota, file->file_id); break; } } return 0; } static void ota_upgrade_file_cb(struct ota_upgrade_info *ota, uint8_t file_id) { if (ota->file_cb) { ota->file_cb(file_id); } } static void ota_update_state(struct ota_upgrade_info *ota, enum ota_state state) { int old_state; SYS_LOG_INF("upadte ota state: %d\n", state); old_state = ota->state; ota->state = state; if (ota->notify) { ota->notify(state, old_state); } } static int ota_partition_erase_part(struct ota_upgrade_info *ota, const struct partition_entry *part, int start_offset) { int err, align_addr, align_size, is_clean; struct ota_storage *storage = ota->storage; #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(part->storage_id); if(storage == NULL) { SYS_LOG_INF("storage not init, update failed\n"); return -EINVAL; } #endif SYS_LOG_INF("erase part %s: offset 0x%x size 0x%x, start_offset 0x%x\n", part->name, part->offset, part->size, start_offset); align_addr = ROUND_DOWN(part->offset + start_offset, OTA_ERASE_ALIGN_SIZE); align_size = ROUND_UP(part->size - start_offset, OTA_ERASE_ALIGN_SIZE); SYS_LOG_INF("erase aligned offset 0x%x, size 0x%x\n", align_addr, align_size); if (ota->data_buf && ota->data_buf_size) { is_clean = ota_storage_is_clean(storage, align_addr, align_size, ota->data_buf, ota->data_buf_size); if (is_clean == 1) { SYS_LOG_INF("part is clean\n"); return 0; } } err = ota_storage_erase(storage, align_addr, align_size); if (err) { return err; } return 0; } static int ota_partition_update_prepare(struct ota_upgrade_info *ota) { struct ota_breakpoint *bp = &ota->bp; const struct partition_entry *part; int i, file_state, erase_offset; SYS_LOG_INF("bp->state %d", bp->state); if (bp->state == OTA_BP_STATE_CLEAN) { /* state is clean, skip erase */ SYS_LOG_INF("bp state is clean, skip erase parts"); return 0; } if (ota_use_recovery(ota)) { if (bp->state == OTA_BP_STATE_UPGRADE_PENDING) { /* state is clean, skip erase */ SYS_LOG_INF("upgrade pending, skip erase parts"); return 0; } /* don't erase temp part is upgrading is going, it will be erased before write file */ if (bp->state == OTA_BP_STATE_UPGRADE_WRITING || bp->state == OTA_BP_STATE_UPGRADING_FAIL) { SYS_LOG_INF("upgrade is in process, skip erase temp part"); return 0; } } for (i = 0; i < MAX_PARTITION_COUNT; i++) { part = partition_get_part_by_id(i); if (part == NULL) return -EINVAL; if (part->file_id == 0) continue; if (!ota_use_recovery(ota)) { /* skip current firmware's partitions */ if (!partition_is_mirror_part(part)) { SYS_LOG_INF("part[%d]: skip current used partition", i); continue; } } else { /* only temp partition need be erased when recovery is enabled */ if (part->type != PARTITION_TYPE_TEMP && part->file_id != PARTITION_FILE_ID_OTA_TEMP) { SYS_LOG_INF("part[%d]: file_id %d not ota temp partition, skip erase", i, part->file_id); continue; } /* don't erase partition that not in current storage */ if (part->storage_id != ota_storage_get_storage_id(ota->storage)) { SYS_LOG_INF("part[%d]: part file_id %d storage_id %d not current storage_id, skip erase", i, part->file_id, part->storage_id); continue; } } if (bp->state == OTA_BP_STATE_UPGRADE_WRITING || bp->state == OTA_BP_STATE_WRITING_IMG) { /* check breakpoint */ file_state = ota_breakpoint_get_file_state(bp, part->file_id); SYS_LOG_INF("bp->state %d file_state %d", bp->state, file_state); if (file_state == OTA_BP_FILE_STATE_CLEAN || file_state == OTA_BP_FILE_STATE_WRITE_DONE || file_state == OTA_BP_FILE_STATE_VERIFY_PASS || file_state == OTA_BP_FILE_STATE_WRITING_CLEAN) { SYS_LOG_INF("part[%d]: file_id %d file_state %d, skip erase", i, part->file_id, file_state); /* skip erase this partition */ continue; } else if (file_state == OTA_BP_FILE_STATE_WRITING) { if (ota_use_recovery(ota) || (bp->mirror_id == partition_get_current_mirror_id())) { /* parition is writing, not clean */ erase_offset = bp->cur_file.offset + bp->cur_orig_write_offset; erase_offset &= ~(OTA_ERASE_ALIGN_SIZE - 1); erase_offset -= part->offset; SYS_LOG_INF("part[%d]: file_id %d writing", i, part->file_id); SYS_LOG_INF("file offset 0x%x, write_offset 0x%x, need erase from 0x%x", bp->cur_file.offset, bp->cur_orig_write_offset, erase_offset); /* update write offset aligned with erase sector */ bp->cur_orig_write_offset = part->offset + erase_offset - bp->cur_file.offset; ota_partition_erase_part(ota, part, erase_offset); ota_breakpoint_set_file_state(bp, part->file_id, OTA_BP_FILE_STATE_WRITING_CLEAN); continue; } } } if (!ota_keep_temp_part(ota)) { ota_partition_erase_part(ota, part, 0); ota_breakpoint_set_file_state(bp, part->file_id, OTA_BP_FILE_STATE_CLEAN); } } if (bp->state != OTA_BP_STATE_UPGRADE_WRITING && bp->state != OTA_BP_STATE_WRITING_IMG) { if (bp->state != OTA_BP_STATE_UNKOWN) { /* clear all old status */ SYS_LOG_INF("clear old bp status"); ota_breakpoint_init_default_value(&ota->bp); } bp->state = OTA_BP_STATE_CLEAN; SYS_LOG_INF("bp state is clean"); } ota_breakpoint_save(bp); return 0; } static int ota_caculate_storage_file_crc(struct ota_upgrade_info *ota, struct ota_file *file) { struct ota_storage *storage = ota->storage; int addr, size, rlen; uint32_t crc; crc = 0; size = file->orig_size; addr = file->offset; #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(file->storage_id); #endif SYS_LOG_INF("check file %s: addr 0x%x, size 0x%x", file->name, addr, size); rlen = ota->data_buf_size; while (size > 0) { if (size < rlen) rlen = size; ota_storage_read(storage, addr, ota->data_buf, rlen); crc = utils_crc32(crc, ota->data_buf, rlen); size -= rlen; addr += rlen; } return crc; } static int ota_verify_file(struct ota_upgrade_info *ota, struct ota_file *file) { uint32_t crc_calc, crc_orig; // FIXME //if (file->file_id != PARTITION_FILE_ID_OTA_TEMP) //{ crc_calc = ota_caculate_storage_file_crc(ota, file); crc_orig = file->checksum; SYS_LOG_INF("check file %s: crc_orig 0x%x, crc_calc 0x%x", file->name, crc_orig, crc_calc); if (crc_calc != crc_orig) { return -1; } //} return 0; } #if 0 //#ifdef CONFIG_OTA_RES_PATCH static int ota_is_patch_res(struct ota_upgrade_info *ota) { const struct fw_version *old_fw_ver = &ota->manifest.old_fw_ver; return (old_fw_ver->version_res == 0) ? 0 : 1; } #endif #ifdef CONFIG_OTA_FILE_PATCH static int ota_is_patch_fw(struct ota_upgrade_info *ota) { const struct fw_version *old_fw_ver = &ota->manifest.old_fw_ver; return (old_fw_ver->version_code == 0) ? 0 : 1; } static int ota_write_file_by_patch(struct ota_upgrade_info *ota, struct ota_file *file, int start_file_offs) { struct ota_image *img = ota->img; struct ota_storage *storage = ota->storage; unsigned int img_file_offset; int err, is_clean, patch_file_size; uint32_t start_time, consume_time; struct ota_file_patch_info file_patch; const struct partition_entry *part; void *mapping_addr; SYS_LOG_INF("write file %s by patch to offset 0x%x", file->name, file->offset); start_time = k_uptime_get_32(); if (start_file_offs != 0) { SYS_LOG_ERR("cannot support breakpoint for file patch by now"); return -EINVAL; } img_file_offset = ota_image_get_file_offset(img, file->name); if (img_file_offset < 0) { SYS_LOG_ERR("cannot found file %s in image", file->name); return -EINVAL; } patch_file_size = ota_image_get_file_length(img, file->name); part = partition_get_part(file->file_id); if (part == NULL) return -EINVAL; /* check empty */ os_printk("file->size 0x%x, ota->data_buf %p, data_buf_size 0x%x, part->flag 0x%x\n", file->size, ota->data_buf, ota->data_buf_size, part->flag); is_clean = ota_storage_is_clean(storage, file->offset, file->size, ota->data_buf, ota->data_buf_size); if (is_clean != 1) { SYS_LOG_ERR("storage is not clean, offs 0x%x size 0x%x", file->offset, file->size); return -EINVAL; } ota_breakpoint_update_file_state(&ota->bp, file, OTA_BP_FILE_STATE_WRITING_DIRTY, 0, 0, 0); mapping_addr = soc_memctrl_create_temp_mapping(part->file_offset, part->flag & PARTITION_FLAG_ENABLE_CRC); memset(&file_patch, 0x0, sizeof(struct ota_file_patch_info)); file_patch.img = img; file_patch.storage = storage; file_patch.old_file_mapping_addr = mapping_addr; file_patch.old_file_offset = part->file_offset; file_patch.old_file_size = part->size; file_patch.new_file_offset = file->offset; // + start_file_offs; file_patch.new_file_size = file->size; file_patch.patch_file_offset = img_file_offset; file_patch.patch_file_size = patch_file_size; file_patch.flag_use_crc = (part->flag & PARTITION_FLAG_ENABLE_CRC) ? 1 : 0; file_patch.flag_use_encrypt = (part->flag & PARTITION_FLAG_ENABLE_ENCRYPTION) ? 1 : 0; file_patch.write_cache = ota->data_buf; file_patch.write_cache_size = 0x22; file_patch.write_cache_offs = 0; file_patch.write_cache_pos = 0; err = ota_file_patch_write(&file_patch); if (err) { SYS_LOG_ERR("storage write failed, offs 0x%x size 0x%x", file->offset, file->size); return -EIO; } consume_time = k_uptime_get_32() - start_time + 1; SYS_LOG_INF("write file %s: length %d KB patch size(%d KB), consume %d ms, %d KB/s\n", file->name, file->size / 1024, patch_file_size / 1024, consume_time, file->size / consume_time); soc_memctrl_clear_temp_mapping(mapping_addr); return 0; } #endif static void ota_rx_thread(void *p1, void *p2, void *p3) { struct ota_upgrade_info *ota = (struct ota_upgrade_info *)p1; struct ota_backend *backend = ota_image_get_backend(ota->img); struct ota_rx_info *rx_info = &ota->rx_info; bool is_bt_backend; int err, rlen, req_size, max_req_size; SYS_LOG_INF("ota_rx thread started"); is_bt_backend = (ota_backend_get_type(backend) == OTA_BACKEND_TYPE_BLUETOOTH); max_req_size = OTA_REQ_MAX_SIZE; ota_backend_ioctl(backend, OTA_BACKEND_IOCTL_GET_MAX_SIZE, (unsigned int)&max_req_size); while ((rx_info->size > 0) && (rx_info->rx_errno == 0)) { if (rx_info->is_raw) { //raw file req_size = rx_info->size; } else { //lzma file while ((req_size = ring_buf_space_get(&rx_info->rbuf)) == 0) { SYS_LOG_INF("ota_rx wait rbuf"); os_sem_take(&rx_info->rx_put_sem, OS_FOREVER); } if (req_size > rx_info->size) { req_size = rx_info->size; } } if (req_size > max_req_size) { req_size = max_req_size; } if (is_bt_backend) { err = ota_image_read_prepare(ota->img, rx_info->offset, ota->data_buf, req_size); if (err) { SYS_LOG_ERR("cannot read data, offs 0x%x", rx_info->offset); rx_info->rx_errno = -EAGAIN; } } while (req_size > 0) { if (req_size < rx_info->seg_size) { rlen = req_size; } else { rlen = rx_info->seg_size; } if (is_bt_backend) { err = ota_image_read_complete(ota->img, rx_info->offset, ota->data_buf, rlen); } else { err = ota_image_read(ota->img, rx_info->offset, ota->data_buf, rlen); } if (err) { SYS_LOG_ERR("cannot read data, offs 0x%x", rx_info->offset); rx_info->rx_errno = -EAGAIN; break; } while (ring_buf_space_get(&rx_info->rbuf) < rlen) { SYS_LOG_INF("ota_rx wait rbuf"); os_sem_take(&rx_info->rx_put_sem, OS_FOREVER); } ring_buf_put(&rx_info->rbuf, (const uint8_t *)ota->data_buf, rlen); os_sem_give(&rx_info->rx_get_sem); req_size -= rlen; rx_info->offset += rlen; rx_info->size -= rlen; } } SYS_LOG_INF("ota_rx thread exited"); os_sem_give(&rx_info->rx_get_sem); } static int ota_rx_init(struct ota_upgrade_info *ota) { struct ota_rx_info *rx_info = &ota->rx_info; rx_info->rx_stack = mem_malloc(OTA_RX_STACKSIZE); if (rx_info->rx_stack == NULL) { SYS_LOG_ERR("failed to allocate %d bytes", OTA_RX_STACKSIZE); return -EINVAL; } rx_info->rx_buf = ota_rx_malloc(OTA_RX_BUFSIZE); if (rx_info->rx_buf == NULL) { SYS_LOG_ERR("failed to allocate %d bytes", OTA_RX_BUFSIZE); return -EINVAL; } rx_info->rx_bufsize = OTA_RX_BUFSIZE; rx_info->in_buf = ota_rx_malloc(OTA_IN_BUFSIZE); if (rx_info->in_buf == NULL) { SYS_LOG_ERR("failed to allocate %d bytes", OTA_IN_BUFSIZE); return -EINVAL; } rx_info->in_bufsize = OTA_IN_BUFSIZE; #if OTA_OUT_BUFSIZE > 0 rx_info->out_buf = ota_rx_malloc(OTA_OUT_BUFSIZE); if (rx_info->out_buf != NULL) { rx_info->out_bufsize = OTA_OUT_BUFSIZE; } else { SYS_LOG_ERR("failed to allocate %d bytes", OTA_OUT_BUFSIZE); } #endif os_sem_init(&rx_info->rx_get_sem, 0, 5); os_sem_init(&rx_info->rx_put_sem, 0, 1); ring_buf_init(&rx_info->rbuf, rx_info->rx_bufsize, rx_info->rx_buf); return 0; } static int ota_rx_exit(struct ota_upgrade_info *ota) { struct ota_rx_info *rx_info = &ota->rx_info; if (rx_info->rx_stack != NULL) { mem_free(rx_info->rx_stack); rx_info->rx_stack = NULL; } if (rx_info->rx_buf != NULL) { ota_rx_free(rx_info->rx_buf); rx_info->rx_buf = NULL; rx_info->rx_bufsize = 0; } if (rx_info->in_buf != NULL) { ota_rx_free(rx_info->in_buf); rx_info->in_buf = NULL; rx_info->in_bufsize = 0; } if (rx_info->out_buf != NULL) { ota_rx_free(rx_info->out_buf); rx_info->out_buf = NULL; rx_info->out_bufsize = 0; } return 0; } static void ota_rx_start(struct ota_upgrade_info *ota, uint32_t offset, uint32_t size, uint32_t seg_size, bool is_raw) { struct ota_rx_info *rx_info = &ota->rx_info; char *stack_ptr; rx_info->offset = offset; rx_info->size = size; rx_info->seg_size = seg_size; rx_info->is_raw = is_raw; rx_info->rx_errno = 0; os_sem_reset(&rx_info->rx_get_sem); os_sem_reset(&rx_info->rx_put_sem); ring_buf_reset(&rx_info->rbuf); stack_ptr = (char *)ROUND_UP(rx_info->rx_stack, ARCH_STACK_PTR_ALIGN); rx_info->rx_tid = (os_tid_t)os_thread_create(stack_ptr, OTA_RX_STACKSIZE, ota_rx_thread, ota, NULL, NULL, 3, 0, OS_NO_WAIT); os_thread_name_set(rx_info->rx_tid, "ota_rx"); } static void ota_rx_stop(struct ota_upgrade_info *ota) { struct ota_rx_info *rx_info = &ota->rx_info; k_thread_join(rx_info->rx_tid, K_MSEC(5000)); } static int ota_write_file_normal(struct ota_upgrade_info *ota, struct ota_file *file, int start_file_offs, int start_orig_offs) { struct ota_image *img = ota->img; struct ota_storage *storage = ota->storage; struct ota_breakpoint *bp = &ota->bp; struct ota_rx_info *rx_info = &ota->rx_info; struct ota_backend *backend; unsigned int offs, file_offs; int img_file_offset; int ret, seg_size, unit_size, wlen, in_size, out_size; uint32_t start_time, consume_time, ts_start, ts_cost; bool is_record = false, no_wait = false; uint8_t *out_buf; lzma_head_t lzma_h = {0}; bool is_raw = (file->size == file->orig_size); SYS_LOG_INF("write file %s size 0x%x(0x%x) to offset 0x%x start_offset 0x%x(0x%x)", file->name, file->size, file->orig_size, file->offset, start_file_offs, start_orig_offs); start_time = k_uptime_get_32(); #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(file->storage_id); if(storage == NULL) { SYS_LOG_INF("storage not init, update failed\n"); return -EINVAL; } #endif if ((start_file_offs >= file->size) || (start_orig_offs >= file->orig_size)) { SYS_LOG_ERR("file %s: start file offs 0x%x(0x%x) > file size 0x%x(0x%x)", file->name, start_file_offs, start_orig_offs, file->size, file->orig_size); return -EINVAL; } file_offs = start_file_offs; offs = start_orig_offs; if (strlen(file->name) == 0) { img_file_offset = ota_image_get_file_offset(img, NULL); } else { img_file_offset = ota_image_get_file_offset(img, file->name); } if (img_file_offset < 0) { SYS_LOG_ERR("cannot found file %s in image", file->name); return -EINVAL; } wlen = file->orig_size - offs; backend = ota_image_get_backend(img); unit_size = OTA_ERASE_ALIGN_SIZE; ota_backend_ioctl(backend, OTA_BACKEND_IOCTL_GET_UNIT_SIZE, (unsigned int)&unit_size); seg_size = (ota->data_buf_size / unit_size) * unit_size; /* clear nvram to avoid erase */ nvram_config_clear(CONFIG_NVRAM_USER_REGION_SEGMENT_SIZE); ota_rx_start(ota, img_file_offset + file_offs, file->size - file_offs, seg_size, is_raw); while (wlen > 0) { if (!no_wait) { os_sem_take(&rx_info->rx_get_sem, OS_FOREVER); if (rx_info->rx_errno) { ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITING, file_offs, offs, 1); return rx_info->rx_errno; } } no_wait = false; in_size = ring_buf_size_get(&rx_info->rbuf); //os_printk("rbuf size 0x%x\n", in_size); if (in_size > rx_info->in_bufsize) { if (in_size >= rx_info->in_bufsize * 2) { no_wait = true; } in_size = rx_info->in_bufsize; } if (is_raw) { // raw file if (in_size < wlen) { if (in_size < OTA_ERASE_ALIGN_SIZE) { continue; } in_size = ROUND_DOWN(in_size, OTA_ERASE_ALIGN_SIZE); } } else { // lzma file if (lzma_h.ih_magic != LZMA_MAGIC) { if (in_size < sizeof(lzma_head_t)) { continue; } ring_buf_get(&rx_info->rbuf, (uint8_t*)&lzma_h, sizeof(lzma_head_t)); os_sem_give(&rx_info->rx_put_sem); if (lzma_h.ih_magic != LZMA_MAGIC) { SYS_LOG_ERR("lzma error magic: 0x%x", lzma_h.ih_magic); return -EAGAIN; } if (rx_info->in_bufsize < lzma_h.ih_img_size) { SYS_LOG_ERR("XzDecode error! in_bufsize 0x%x < 0x%x", rx_info->in_bufsize, lzma_h.ih_img_size); return -EINVAL; } if (rx_info->out_bufsize < lzma_h.ih_org_size) { SYS_LOG_ERR("XzDecode error! out_bufsize 0x%x < 0x%x", rx_info->out_bufsize, lzma_h.ih_org_size); return -EINVAL; } in_size -= sizeof(lzma_head_t); } if (in_size < lzma_h.ih_img_size) { continue; } if (in_size >= (lzma_h.ih_img_size + sizeof(lzma_head_t))) { no_wait = true; } in_size = lzma_h.ih_img_size; } if (!is_record) { ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITING, file_offs, offs, 1); is_record = true; } else { ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITING, file_offs, offs, 0); } ret = ring_buf_get(&rx_info->rbuf, rx_info->in_buf, in_size); os_sem_give(&rx_info->rx_put_sem); if (ret != in_size) { SYS_LOG_ERR("ring buf get failed, size 0x%x", ret); return -EAGAIN; } if (is_raw) { // raw file out_buf = rx_info->in_buf; out_size = in_size; } else { // lzma file ts_start = k_uptime_get_32(); if (rx_info->out_buf == NULL) { SYS_LOG_ERR("XzDecode error! out_buf is NULL"); return -EINVAL; } // decompress lzma block out_size = OTA_OUT_BUFSIZE; #ifdef CONFIG_OTA_LZMA ret = XzDecode(rx_info->in_buf, in_size, rx_info->out_buf, &out_size); #else ret = 0; #endif if (ret == 0) { SYS_LOG_ERR("XzDecode error! size 0x%x", out_size); return -EAGAIN; } ts_cost = k_uptime_get_32() - ts_start + 1; os_printk("XzDecode 0x%x->0x%x (%d ms)\n", in_size, out_size, ts_cost); // check origin size if (out_size != lzma_h.ih_org_size) { SYS_LOG_ERR("XzDecode out_size mismatch! 0x%x", out_size); return -EAGAIN; } out_buf = rx_info->out_buf; lzma_h.ih_magic = 0; } ts_start = k_uptime_get_32(); ret = ota_storage_write(storage, file->offset + offs, out_buf, out_size); if (ret) { SYS_LOG_ERR("storage write failed, offs 0x%x", offs); return -EIO; } ts_cost = k_uptime_get_32() - ts_start; os_printk("write 0x%x -> 0x%x(0x%x) (%d ms)\n", file_offs, offs, out_size, ts_cost); file_offs += (file->size != file->orig_size) ? in_size + sizeof(lzma_head_t) : in_size; offs += out_size; wlen -= out_size; } consume_time = k_uptime_get_32() - start_time + 1; SYS_LOG_INF("write file %s: length %d KB, consume %d ms, %d KB/s\n", file->name, file->size / 1024, consume_time, file->size / consume_time); ota_rx_stop(ota); return 0; } static int ota_write_file(struct ota_upgrade_info *ota, struct ota_file *file, int start_file_offs, int start_orig_offs) { #ifdef CONFIG_OTA_FILE_PATCH if (ota_is_patch_fw(ota)) { return ota_write_file_by_patch(ota, file, start_file_offs); } else { #endif return ota_write_file_normal(ota, file, start_file_offs, start_orig_offs); #ifdef CONFIG_OTA_FILE_PATCH } #endif } static int ota_write_and_verify_file(struct ota_upgrade_info *ota, const struct partition_entry *part, struct ota_file *file, bool need_verify) { struct ota_breakpoint *bp = &ota->bp; struct ota_storage *storage = ota->storage; int bp_file_state, bp_file_offset = 0, bp_orig_offset = 0; int err = 0, cur_storage_id, need_erase = 0; bp_file_state = ota_breakpoint_get_file_state(bp, file->file_id); SYS_LOG_INF("file %s: file_id %d, bp file state %d", file->name, file->file_id, bp_file_state); #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(file->storage_id); if(storage == NULL) { SYS_LOG_INF("storage not init, update failed\n"); err = -EINVAL; goto failed; } #endif switch (bp_file_state) { case OTA_BP_FILE_STATE_WRITE_DONE: case OTA_BP_FILE_STATE_VERIFY_PASS: SYS_LOG_INF("file %s: file_id %d, already write done\n", file->name, file->file_id); break; case OTA_BP_FILE_STATE_CLEAN: SYS_LOG_INF("file %s: file_id %d, part is clean\n", file->name, file->file_id); break; case OTA_BP_FILE_STATE_WRITING_CLEAN: SYS_LOG_INF("file %s: file_id %d, part is writing clean, write offset 0x%x(0x%x)\n", file->name, file->file_id, bp->cur_file_write_offset, bp->cur_orig_write_offset); bp_file_offset = bp->cur_file_write_offset; bp_orig_offset = bp->cur_orig_write_offset; break; case OTA_BP_FILE_STATE_WRITING: SYS_LOG_INF("file %s: file_id %d, part is writing not clean! , write_offset 0x%x(0x%x)\n", file->name, file->file_id, bp->cur_file_write_offset, bp->cur_orig_write_offset); bp_file_offset = bp->cur_file_write_offset; bp_orig_offset = bp->cur_orig_write_offset; need_erase = 1; break; default: SYS_LOG_INF("file %s: file_id %d, write offset 0 by default\n", file->name, file->file_id); need_erase = 1; break; } cur_storage_id = ota_storage_get_storage_id(storage); if (part->storage_id != cur_storage_id) { SYS_LOG_ERR("BUG: file_id %d storage_id %d not current storage_id %d", part->file_id, part->storage_id, cur_storage_id); err = -EINVAL; goto failed; } if (ota_use_recovery(ota) || ota_erase_part_for_upg(ota)) { /* we can erase flash in recovery app */ if (need_erase) { int erase_offset; if (!ota_use_recovery_app(ota) && !ota_erase_part_for_upg(ota)) { /* cannot erase flash if not in recovery app or single nor */ if (cur_storage_id == 0) { SYS_LOG_INF("update file_id %d: storage %d is xip, skip erase\n", cur_storage_id, file->file_id); goto skip_erase; } } erase_offset = ROUND_DOWN(file->offset + bp_orig_offset, OTA_ERASE_ALIGN_SIZE); bp_orig_offset = erase_offset - file->offset; ota_partition_erase_part(ota, part, erase_offset - part->offset); SYS_LOG_INF("update file_id %d write_offset from 0x%x to 0x%x\n", file->file_id, bp->cur_orig_write_offset, bp_orig_offset); bp->cur_orig_write_offset = bp_orig_offset; } } skip_erase: if (bp_file_state != OTA_BP_FILE_STATE_WRITE_DONE && bp_file_state != OTA_BP_FILE_STATE_VERIFY_PASS) { ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITE_START, bp_file_offset, bp_orig_offset, 0); err = ota_write_file(ota, file, bp_file_offset, bp_orig_offset); if (err) { SYS_LOG_ERR("failed to write file %s", file->name); goto failed; } /*base sdfs update need to erase extern sdfs part*/ if (file->file_id == PARTITION_FILE_ID_SDFS_PART_BASE) { const struct partition_entry *part_fatfs = partition_get_part(PARTITION_FILE_ID_SDFS_PART1); if (part_fatfs) { // erase res_b partition ota_partition_erase_part(ota, part_fatfs, 0); // clear first 32bytes to 0xff memset(ota->data_buf, 0xff, 32); ota_storage_write(ota->storage, part_fatfs->offset, ota->data_buf, 32); SYS_LOG_INF("clear res_b: offset 0x%x", part_fatfs->offset); } } ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITE_DONE, 0, 0, 0); } if (need_verify) { err = ota_verify_file(ota, file); if (err) { SYS_LOG_ERR("file %s, verify failed", file->name); ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_VERIFY_FAIL, 0, 0, 0); goto failed; } SYS_LOG_INF("file %s, verify pass", file->name); ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_VERIFY_PASS, 0, 0, 0); } return 0; failed: if (err != -EIO && err != -EAGAIN) { /* we assume -EIO error that can be resumed */ ota_breakpoint_update_file_state(bp, file, OTA_BP_FILE_STATE_WRITE_FAIL, 0, 0, 0); } return err; } static int ota_upgrade_verify_along(struct ota_upgrade_info *ota) { const struct partition_entry *part; struct ota_manifest *manifest = &ota->manifest; struct ota_file *file; int i, err; int cur_file_id; for (i = 0; i < manifest->file_cnt; i++) { file = &manifest->wfiles[i]; part = partition_get_mirror_part(file->file_id); if (part == NULL) { SYS_LOG_INF("cannt found mirror part entry for file_id %d", file->file_id); if (ota_use_recovery(ota)) { cur_file_id = partition_get_current_file_id(); part = partition_get_part(file->file_id); if (cur_file_id == file->file_id || part == NULL) { SYS_LOG_ERR("cannt found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); return -EINVAL; } SYS_LOG_INF("found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); } else { return -EINVAL; } } /* ignore boot partition */ if (partition_is_boot_part(part)) continue; if (partition_is_param_part(part)) continue; err = ota_verify_file(ota, file); if (err) { SYS_LOG_ERR("file %s, verify failed", file->name); ota_breakpoint_update_file_state(&ota->bp, file, OTA_BP_FILE_STATE_VERIFY_FAIL, 0, 0, 0); return -1; } ota_upgrade_file_cb(ota, file->file_id); SYS_LOG_INF("file %s, verify pass", file->name); ota_breakpoint_update_file_state(&ota->bp, file, OTA_BP_FILE_STATE_VERIFY_PASS, 0, 0, 0); } return 0; } #if 0 static int ota_auto_update_version(struct ota_upgrade_info *ota, const struct partition_entry *part, struct ota_file *file) { struct ota_storage *storage = ota->storage; const struct fw_version *cur_ver; struct fw_version *new_ver; uint32_t start_time, consume_time; uint32_t addr, len = file->size, wlen; uint8_t *param_ptr, *temp_param_ptr, *param_map_ptr; SYS_LOG_INF("write file %s len %d to offset 0x%x by auto update version", file->name, len, file->offset); #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(file->storage_id); #endif start_time = k_uptime_get_32(); cur_ver = (struct fw_version *)fw_version_get_current(); param_ptr = (uint8_t *)mem_malloc(file->size); if (!param_ptr) { SYS_LOG_INF("failed to malloc size %d", file->size); return -ENOMEM; } temp_param_ptr = param_ptr; param_map_ptr = soc_memctrl_create_temp_mapping(file->offset, false); memcpy(param_ptr, param_map_ptr, file->size); soc_memctrl_clear_temp_mapping(param_map_ptr); new_ver = (struct fw_version *)(param_ptr + SOC_BOOT_FIRMWARE_VERSION_OFFSET); /* Allow ota upgrade even though new ota version is bigger than the current's */ if (new_ver->version_code > cur_ver->version_code) { SYS_LOG_WRN("new fw version 0x%x is bigger than current's 0x%x", new_ver->version_code, cur_ver->version_code); } new_ver->version_code = cur_ver->version_code + 1; new_ver->checksum = utils_crc32(0, (const uint8_t *)new_ver, sizeof(struct fw_version) - 4); SYS_LOG_INF("current fw version: 0x%x", cur_ver->version_code); SYS_LOG_INF("current fw version name: %s", cur_ver->version_name); SYS_LOG_INF("new fw version: 0x%x", new_ver->version_code); SYS_LOG_INF("new fw version name: %s", new_ver->version_name); ota_partition_erase_part(ota, part, 0); addr = file->offset; /* enable encryption function */ if (part->flag & PARTITION_FLAG_ENABLE_ENCRYPTION) { SYS_LOG_INF("enable encryption write"); addr |= (1 << 31); if (len % 32) { SYS_LOG_ERR("len %d shall align with 32 bytes", len); mem_free(temp_param_ptr); return -EINVAL; } } wlen = 32; while (len) { if (len < wlen) wlen = len; if (ota_storage_write(storage, addr, param_ptr, wlen)) { SYS_LOG_ERR("storage write failed, offs 0x%x", addr); mem_free(temp_param_ptr); return -EFAULT; } param_ptr += wlen; addr += wlen; len -= wlen; } consume_time = k_uptime_get_32() - start_time + 1; SYS_LOG_INF("write file %s: length %d KB, consume %d ms, %d KB/s\n", file->name, len / 1024, consume_time, len / consume_time); mem_free(temp_param_ptr); return 0; } #endif static int ota_do_upgrade(struct ota_upgrade_info *ota) { const struct partition_entry *part, *boot_part = NULL, *param_part = NULL; struct ota_manifest *manifest = &ota->manifest; struct ota_file *file, *boot_file = NULL, *param_file = NULL; int i, err, max_file_size; int cur_file_id; int retry_times = 0; SYS_LOG_INF("ota file_cnt %d", manifest->file_cnt); try_again: for (i = 0; i < manifest->file_cnt; i++) { file = &manifest->wfiles[i]; part = partition_get_mirror_part(file->file_id); if (part == NULL) { SYS_LOG_INF("cannt found mirror part entry for file_id %d", file->file_id); if (ota_use_recovery(ota)) { cur_file_id = partition_get_current_file_id(); part = partition_get_part(file->file_id); if (cur_file_id == file->file_id || part == NULL) { SYS_LOG_ERR("cannt found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); return -EINVAL; } SYS_LOG_INF("found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); } else { return -EINVAL; } } max_file_size = partition_get_max_file_size(part); if (file->orig_size > max_file_size) { SYS_LOG_ERR("part %s: file size 0x%x > part max file size 0x%x", part->name, file->orig_size, max_file_size); return -EINVAL; } SYS_LOG_INF("[%d]: file %s, file_id %d write to nor addr 0x%x", i, file->name, file->file_id, part->file_offset); file->offset = part->file_offset; if (partition_is_boot_part(part)) { boot_file = file; boot_part = part; continue; } if (partition_is_param_part(part)) { param_file = file; param_part = part; continue; } err = ota_write_and_verify_file(ota, part, file, false); if (err) { return err; } } err = ota_upgrade_verify_along(ota); if (err) { /* retry upgrade if verify failed */ if (retry_times < 1) { SYS_LOG_ERR("OTA upgrade retry after verify failed"); retry_times++; ota_image_progress_reset(ota->img); goto try_again; } else { return err; } } /* don't upgrade for boot file and para file*/ #if OTA_FULL_UPGRADE /* write boot file at secondly last */ if (boot_file && boot_part) { /* write boot file at mirror part */ err = ota_write_and_verify_file(ota, boot_part, boot_file, true); if (err) { return err; } /* erase boot at current part */ boot_part = partition_get_part(boot_file->file_id); ota_partition_erase_part(ota, boot_part, 0); ota_breakpoint_set_file_state(&ota->bp, boot_file->file_id, OTA_BP_FILE_STATE_CLEAN); } /* write param file at last */ if (param_file && param_part) { /* write param file at mirror part */ err = ota_write_and_verify_file(ota, param_part, param_file, true); if (err) { return err; } /* erase param file at current part */ param_part = partition_get_part(param_file->file_id); ota_partition_erase_part(ota, param_part, 0); ota_breakpoint_set_file_state(&ota->bp, param_file->file_id, OTA_BP_FILE_STATE_CLEAN); // if (ota_use_no_version_control(ota)) { // err = ota_auto_update_version(ota, param_part, param_file); // if (err) { // return err; // } // } } #endif /* try to save res version */ ota_save_res_version(ota); return 0; } static int ota_is_need_upgrade(struct ota_upgrade_info *ota) { struct ota_breakpoint *bp = &ota->bp; const struct fw_version *cur_ver, *img_ver; struct ota_backend *backend; int backend_type; #if defined(CONFIG_OTA_FILE_PATCH) || defined(CONFIG_OTA_RES_PATCH) const struct fw_version *patch_old_ver; patch_old_ver = &ota->manifest.old_fw_ver; SYS_LOG_INF("OTA patch old fw version:"); fw_version_dump(patch_old_ver); #endif img_ver = &ota->manifest.fw_ver; cur_ver = fw_version_get_current(); SYS_LOG_INF("ota fw version:"); fw_version_dump(img_ver); SYS_LOG_INF("current fw version:"); fw_version_dump(cur_ver); backend = ota_image_get_backend(ota->img); backend_type = ota_backend_get_type(backend); if (backend_type != OTA_BACKEND_TYPE_TEMP_PART && !(backend_type == OTA_BACKEND_TYPE_CARD && ota_use_recovery_app(ota)) && bp->backend_type != OTA_BACKEND_TYPE_UNKNOWN && bp->backend_type != backend_type) { SYS_LOG_ERR("backend type is chagned(%d -> %d), need erase old firmware", bp->backend_type, ota_backend_get_type(backend)); return -1; } if (strcmp(cur_ver->board_name, img_ver->board_name)) { /* skip */ SYS_LOG_ERR("unmatched board name, skip ota"); return -1; } #ifdef CONFIG_OTA_FILE_PATCH if (ota_is_patch_fw(ota)) { /* validate ota patch firmware version */ if (cur_ver->version_code != patch_old_ver->version_code) { SYS_LOG_ERR("unmatched fw ver, curr 0x%x but OTA patch old ver is 0x%x", cur_ver->version_code, ota->manifest.old_fw_ver.version_code); return -1; } if (ota_use_no_version_control(ota)) { SYS_LOG_ERR("Patch FW only support with version control"); return -1; } } #endif #if 0 //#ifdef CONFIG_OTA_RES_PATCH if (ota_is_patch_res(ota)) { /* validate ota patch firmware version */ if (cur_ver->version_res != patch_old_ver->version_res) { SYS_LOG_ERR("unmatched fw ver, curr 0x%x but OTA patch old ver is 0x%x", cur_ver->version_res, ota->manifest.old_fw_ver.version_res); return -1; } if (ota_use_no_version_control(ota)) { SYS_LOG_ERR("Patch FW only support with version control"); return -1; } } #endif if (!ota_use_no_version_control(ota)) { if (cur_ver->version_code >= img_ver->version_code) { /* skip */ SYS_LOG_INF("ota image is same or older, skip ota"); return 0; } } if (bp->state == OTA_BP_STATE_WRITING_IMG || bp->state == OTA_BP_STATE_UPGRADE_WRITING || bp->state == OTA_BP_STATE_UPGRADE_PENDING) { if ((bp->new_version != 0 && bp->new_version != img_ver->version_code) || (bp->data_checksum != ota_image_get_checksum(ota->img))) { /* FIXME: has new version fw, need erase partition */ SYS_LOG_INF("has new version fw, need erase old firmware"); return 2; } } return 1; } static int ota_upgrade_statistics(struct ota_upgrade_info *ota) { const struct partition_entry *part; struct ota_manifest *manifest = &ota->manifest; struct ota_file *file; struct ota_breakpoint *bp = &ota->bp; int i, bp_file_state, start_write_offset = 0, erase_offset; int cur_file_id; uint32_t total_size = 0; for (i = 0; i < manifest->file_cnt; i++) { file = &manifest->wfiles[i]; part = partition_get_mirror_part(file->file_id); if (part == NULL) { SYS_LOG_INF("cannt found mirror part entry for file_id %d", file->file_id); if (ota_use_recovery(ota)) { cur_file_id = partition_get_current_file_id(); part = partition_get_part(file->file_id); if (cur_file_id == file->file_id || part == NULL) { SYS_LOG_ERR("cannt found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); return -EINVAL; } SYS_LOG_INF("found part entry for file_id %d, cur_file_id %d", file->file_id, cur_file_id); } else { return -EINVAL; } } bp_file_state = ota_breakpoint_get_file_state(bp, file->file_id); if (bp_file_state != OTA_BP_FILE_STATE_WRITE_DONE && bp_file_state != OTA_BP_FILE_STATE_VERIFY_PASS) { if (bp_file_state == OTA_BP_FILE_STATE_WRITING_CLEAN || bp_file_state == OTA_BP_FILE_STATE_WRITING || bp_file_state == OTA_BP_FILE_STATE_WRITE_START) { if (file->size == file->orig_size) { // raw file /* Align offset with erase size */ erase_offset = ROUND_DOWN(file->offset + bp->cur_file_write_offset, OTA_ERASE_ALIGN_SIZE); start_write_offset += (erase_offset - file->offset); } else { // lzma file start_write_offset += bp->cur_file_write_offset; } } } else { start_write_offset += file->size; } total_size += file->size; SYS_LOG_INF("ota file[%d]%s: total size %d, bp offset 0x%x", file->file_id, file->name, file->size, start_write_offset); } if (total_size) ota_image_progress_on(ota->img, total_size, start_write_offset); return 0; } static int ota_temp_part_is_upgrade(struct ota_upgrade_info *ota) { struct ota_manifest *manifest = &ota->manifest; struct ota_file *file; for (int i = 0; i < manifest->file_cnt; i++) { file = &manifest->wfiles[i]; if (file->type == PARTITION_TYPE_TEMP && file->file_id == PARTITION_FILE_ID_OTA_TEMP) return 1; } return 0; } static void ota_upgrade_exit(struct ota_upgrade_info *ota) { SYS_LOG_INF("exit"); if (ota) { if (ota->img) ota_image_exit(ota->img); if (ota->storage) ota_storage_exit(ota->storage); } } int ota_upgrade_check(struct ota_upgrade_info *ota) { struct ota_breakpoint *bp = &ota->bp; struct ota_backend *backend = NULL; int err, need_upgrade; int connect_type = 0; SYS_LOG_INF("handle upgrade"); if (ota->state != OTA_INIT) { SYS_LOG_ERR("ota state <%d> is not OTA_INIT, skip upgrade", ota->state); return -EINVAL; } if (ota_image_get_backend(ota->img) == NULL) { SYS_LOG_ERR("ota backend null\n"); return -EINVAL; } backend = ota_image_get_backend(ota->img); err = ota_image_open(ota->img); if (err) { if (ota_backend_get_type(backend) == OTA_BACKEND_TYPE_BLUETOOTH) { ota_backend_read_prepare(backend, 0, NULL, 0); } SYS_LOG_INF("ota image open failed"); err = -EIO; goto exit_invalid; } if (ota_use_recovery_app(ota)) { /* only check data in recovery app to save time */ err = ota_image_check_data(ota->img); if (err) { SYS_LOG_ERR("bad data crc"); ota_breakpoint_update_state(bp, OTA_BP_STATE_WRITING_IMG_FAIL); goto exit; } } err = ota_manifest_parse_file(&ota->manifest, ota->img, OTA_MANIFESET_FILE_NAME); if (err) { SYS_LOG_INF("cannot get manifest file in image"); err = -EAGAIN; goto exit; } /* need upgrade? */ need_upgrade = ota_is_need_upgrade(ota); if (need_upgrade <= 0) { SYS_LOG_INF("skip upgrade"); ota_breakpoint_update_state(bp, OTA_BP_STATE_WRITING_IMG_FAIL); err = -EINVAL; goto exit; } else if(need_upgrade == 2) { SYS_LOG_INF("bp pending"); ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADE_PENDING); ota_update_state(ota, OTA_FAIL); ota_partition_update_prepare(ota); ota_update_state(ota, OTA_INIT); } SYS_LOG_INF("burn firmware image"); if (!ota->data_buf) { ota->data_buf = mem_malloc(ota->data_buf_size); } if (!ota->data_buf) { SYS_LOG_ERR("failed to allocate %d bytes", ota->data_buf_size); err = -EAGAIN; goto exit; } /* update breakpoint for new firmware */ bp->backend_type = ota_backend_get_type(backend); bp->new_version = ota->manifest.fw_ver.version_code; bp->data_checksum = ota_image_get_checksum(ota->img); ota_upgrade_statistics(ota); ota_update_state(ota, OTA_RUNNING); err = ota_rx_init(ota); if (err) { goto exit; } if (!ota_use_recovery(ota) || ota_use_recovery_app(ota)) { ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADE_WRITING); err = ota_do_upgrade(ota); if (err) { SYS_LOG_INF("upgrade failed, err %d", err); if (err != -EIO && err != -EAGAIN) { ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADING_FAIL); } goto exit; } /* set version code */ ota_update_fw_version(ota, PARTITION_FILE_ID_SYSTEM); ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADE_DONE); } else { ota_breakpoint_update_state(bp, OTA_BP_STATE_WRITING_IMG); err = ota_do_upgrade(ota); if (err) { SYS_LOG_INF("write ota image failed, err %d", err); if (err != -EIO && err != -EAGAIN) { ota_breakpoint_update_state(bp, OTA_BP_STATE_WRITING_IMG_FAIL); } goto exit; } else { if (ota_temp_part_is_upgrade(ota)) { ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADE_PENDING); } else { ota_breakpoint_update_state(bp, OTA_BP_STATE_UPGRADE_DONE); } } } SYS_LOG_INF("upgrade successfully!"); ota_image_report_progress(ota->img, 0, 1); ota_image_ioctl(ota->img, OTA_BACKEND_IOCTL_REPORT_IMAGE_VALID, 1); ota_update_state(ota, OTA_DONE); exit: ota_rx_exit(ota); if (err) { ota_image_ioctl(ota->img, OTA_BACKEND_IOCTL_REPORT_IMAGE_VALID, 0); ota_update_state(ota, OTA_FAIL); if (err != -EAGAIN) { /* upgrade fail need to clear bp and erase dirty part*/ ota_partition_update_prepare(ota); } /* wait for upgrade resume */ SYS_LOG_INF("ota status -> OTA_INIT, wait for upgrading resume!"); ota_update_state(ota, OTA_INIT); } if (ota->data_buf) { if (ota_backend_get_type(backend) == OTA_BACKEND_TYPE_BLUETOOTH) { ota_backend_read_prepare(backend, 0, NULL, 0); } mem_free(ota->data_buf); ota->data_buf = NULL; } if ((!err) && backend) { // delay 500ms to close bt for sending remain data if (ota_backend_get_type(backend) == OTA_BACKEND_TYPE_BLUETOOTH) { os_sleep(500); } } ota_image_close(ota->img); exit_invalid: if (err) { if (ota_backend_get_type(backend) == OTA_BACKEND_TYPE_BLUETOOTH) { /* waiting for 500ms to disconnect ble.*/ int sp_cnt = 0; do { ota_backend_ioctl(backend, OTA_BACKEND_IOCTL_GET_CONNECT_TYPE, (unsigned int)&connect_type); SYS_LOG_INF("connect_type %d sp_cnt %d.",connect_type, sp_cnt); #ifdef CONFIG_BT_MANAGER if (BLE_CONNECT_TYPE != connect_type) { break; } #endif os_sleep(50); sp_cnt++; if (10 == sp_cnt) { ota_backend_ioctl(backend, OTA_BACKEND_IOCTL_EXECUTE_EXIT, 0); } } while (sp_cnt < 10); } ota_image_unbind(ota->img, ota_image_get_backend(ota->img)); } else { ota_upgrade_exit(ota); } return err; } int ota_upgrade_attach_backend(struct ota_upgrade_info *ota, struct ota_backend *backend) { struct ota_backend *img_backend = ota_image_get_backend(ota->img); SYS_LOG_INF("attach backend type %d", backend->type); if (img_backend != NULL && img_backend->type != backend->type) { SYS_LOG_ERR("already attached backend %d %d", img_backend->type, backend->type); return -EBUSY; } ota_image_bind(ota->img, backend); return 0; } void ota_upgrade_detach_backend(struct ota_upgrade_info *ota, struct ota_backend *backend) { SYS_LOG_INF("detach backend %p", backend); /* to avoid empty pointer */ #if 0 struct ota_backend *img_backend = ota_image_get_backend(ota->img); if (img_backend == backend) ota_image_unbind(ota->img, backend); #endif } int ota_upgrade_is_in_progress(struct ota_upgrade_info *ota) { struct ota_breakpoint *bp = &ota->bp; int bp_state; bp_state = ota_breakpoint_get_current_state(bp); switch (bp_state) { case OTA_BP_STATE_UPGRADE_PENDING: case OTA_BP_STATE_UPGRADE_WRITING: case OTA_BP_STATE_UPGRADE_DONE: return 1; default: break; } return 0; } int ota_upgrade_set_in_progress(struct ota_upgrade_info *ota) { if (!ota_upgrade_is_in_progress(ota)) { ota_breakpoint_update_state(&ota->bp, OTA_BP_STATE_UPGRADE_PENDING); } return 0; } static struct ota_upgrade_info global_ota_upgrade_info; struct ota_upgrade_info *ota_upgrade_init(struct ota_upgrade_param *param) { struct ota_upgrade_info *ota; SYS_LOG_INF("init"); ota = &global_ota_upgrade_info; memset(ota, 0x0, sizeof(struct ota_upgrade_info)); if (param->no_version_control) { SYS_LOG_INF("enable no version control"); ota->flags |= OTA_FLAG_USE_NO_VERSION_CONTROL; } /* allocate data buffer later */ ota->data_buf_size = OTA_DATA_BUFFER_SIZE; if (!ota->data_buf) { ota->data_buf = mem_malloc(ota->data_buf_size); } ota->storage = ota_storage_init(param->storage_name); if (!ota->storage) { SYS_LOG_INF("storage open err"); ota = NULL; goto init_exit; } #ifdef CONFIG_OTA_MUTIPLE_STORAGE ota->storage_ext = ota_storage_init(param->storage_ext_name); if (!ota->storage_ext) { SYS_LOG_INF("storage ext open err"); ota = NULL; goto init_exit; } #endif if (param->flag_use_recovery) { ota->flags |= OTA_FLAG_USE_RECOVERY; } if (param->flag_erase_part_for_upg) { ota->flags |= OTA_FLAG_ERASE_PART_FOR_UPG; } if (param->flag_keep_temp_part) { ota->flags |= OTA_FLAG_KEEP_TEMP_PART; } if (param->flag_use_recovery_app) { if (!param->flag_use_recovery) { SYS_LOG_ERR("invalid flag_is_recovery_app"); ota = NULL; goto init_exit; } ota->flags |= OTA_FLAG_USE_RECOVERY_APP; } ota_breakpoint_init(&ota->bp); ota_partition_update_prepare(ota); ota->img = ota_image_init(); if (!ota->img) { SYS_LOG_ERR("image init failed"); ota = NULL; goto init_exit; } ota->notify = param->notify; ota->file_cb = param->file_cb; ota_update_state(ota, OTA_INIT); // disable nor-suspend to reduce erase time when erase size >= 256KB ota_storage_set_max_erase_seg(ota->storage, OTA_STORAGE_MAX_ERASE_SEGMENT_SIZE); init_exit: if (ota && ota->data_buf) { mem_free(ota->data_buf); ota->data_buf = NULL; } return ota; } struct ota_storage *ota_upgrade_storage_fine(struct ota_file *file) { struct ota_storage *storage = global_ota_upgrade_info.storage; #ifdef CONFIG_OTA_MUTIPLE_STORAGE storage = ota_storage_find(file->storage_id); #endif return storage; }