/* Copyright 2023 Quectel Wireless Solutions Co.,Ltd Quectel hereby grants customers of Quectel a license to use, modify, distribute and publish the Software in binary form provided that customers shall have no right to reverse engineer, reverse assemble, decompile or reduce to source code form any portion of the Software. Under no circumstances may customers modify, demonstrate, use, deliver or disclose any portion of the Software in source code form. */ #include "usb_linux.h" #include #include #include /* #define error_return() \ do \ { \ dbg_time("%s %s %d fail\n", __FILE__, __func__, __LINE__); \ return __LINE__; \ } while (0) */ int recv_sc600y_configure_num = 1; extern const char *q_device_type; static int fh_recv_cmd_sk[2]; extern unsigned q_module_packet_sign; extern unsigned q_erase_all_before_download; extern int update_transfer_bytes(long long bytes_cur); extern int show_progress(); char file_name_image[128] = {0}; char file_name_image_dir[256] = {0}; typedef struct sparse_header { uint32_t magic; /* 0xed26ff3a */ uint16_t major_version; /* (0x1) - reject images with higher major versions */ uint16_t minor_version; /* (0x0) - allow images with higer minor versions */ uint16_t file_hdr_sz; /* 28 bytes for first revision of the file format */ uint16_t chunk_hdr_sz; /* 12 bytes for first revision of the file format */ uint32_t blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ uint32_t total_blks; /* total blocks in the non-sparse output image */ uint32_t total_chunks; /* total chunks in the sparse input image */ uint32_t image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ /* as 0. Standard 802.3 polynomial, use a Public Domain */ /* table implementation */ } sparse_header_t; #define SPARSE_HEADER_MAGIC 0xed26ff3a typedef struct chunk_header { uint16_t chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ uint16_t reserved1; uint32_t chunk_sz; /* in blocks in output image */ uint32_t total_sz; /* in bytes of chunk input file including chunk header and data */ } chunk_header_t; typedef struct chunk_polymerization_params { uint32_t total_chunk_sz; uint32_t total_sz; uint16_t total_chunk_count; // uint16_t file_sector_offset; } chunk_polymerization_param; typedef struct SparseImgParams { chunk_polymerization_param chunk_polymerization_data[100]; chunk_polymerization_param chunk_polymerization_cac3[100]; uint16_t total_count; uint16_t total_cac3_count; uint16_t file_first_sector_offset; //��һ����ͷ��ȡ�����涼���Լ������ } SparseImgParam; SparseImgParam SparseImgData; struct fh_configure_cmd { const char *type; const char *MemoryName; uint32_t Verbose; uint32_t AlwaysValidate; uint32_t MaxDigestTableSizeInBytes; uint32_t MaxPayloadSizeToTargetInBytes; uint32_t MaxPayloadSizeFromTargetInBytes; // 2048 uint32_t MaxPayloadSizeToTargetInByteSupported; // 16k uint32_t ZlpAwareHost; uint32_t SkipStorageInit; }; struct fh_erase_cmd { const char *type; // uint32_t PAGES_PER_BLOCK; uint32_t SECTOR_SIZE_IN_BYTES; // char label[32]; uint32_t last_sector; uint32_t num_partition_sectors; // uint32_t physical_partition_number; uint32_t start_sector; }; struct fh_program_cmd { const char *type; char *filename; char *sparse; uint32_t filesz; // uint32_t PAGES_PER_BLOCK; uint32_t SECTOR_SIZE_IN_BYTES; // char label[32]; // uint32_t last_sector; uint32_t num_partition_sectors; uint32_t physical_partition_number; uint32_t start_sector; uint32_t file_sector_offset; uint32_t UNSPARSE_FILE_SIZE; // char sparse[16]; }; struct fh_response_cmd { const char *type; const char *value; uint32_t rawmode; uint32_t MaxPayloadSizeToTargetInBytes; }; struct fh_log_cmd { const char *type; }; struct fh_patch_cmd { const char *type; char *filename; uint32_t filesz; uint32_t SECTOR_SIZE_IN_BYTES; uint32_t num_partition_sectors; }; struct fh_cmd_header { const char *type; }; struct fh_vendor_defines { const char *type; // "vendor" }; struct fh_cmd { union { struct fh_cmd_header cmd; struct fh_configure_cmd cfg; struct fh_erase_cmd erase; struct fh_program_cmd program; struct fh_response_cmd response; struct fh_log_cmd log; struct fh_patch_cmd patch; struct fh_vendor_defines vdef; }; int part_upgrade; char xml_original_data[512]; }; #define fh_cmd_num 1024 // AG525 have more than 64 partition struct fh_data { const char *firehose_dir; const void *usb_handle; unsigned MaxPayloadSizeToTargetInBytes; unsigned fh_cmd_count; unsigned fh_patch_count; unsigned ZlpAwareHost; struct fh_cmd fh_cmd_table[fh_cmd_num]; unsigned xml_tx_size; unsigned xml_rx_size; char xml_tx_buf[1024]; char xml_rx_buf[1024]; }; static const char *fh_xml_find_value(const char *xml_line, const char *key, char **ppend) { char *pchar = strstr(xml_line, key); char *pend; if (!pchar) { if (strcmp(key, "sparse")) dbg_time("%s: no key %s in %s\n", __func__, key, xml_line); return NULL; } pchar += strlen(key); if (pchar[0] != '=' && pchar[1] != '"') { dbg_time("%s: no start %s in %s\n", __func__, "=\"", xml_line); return NULL; } pchar += strlen("=\""); pend = strstr(pchar, "\""); if (!pend) { dbg_time("%s: no end %s in %s\n", __func__, "\"", xml_line); return NULL; } *ppend = pend; return pchar; } static const char *fh_xml_get_value(const char *xml_line, const char *key) { static char value[64]; char *pend; const char *pchar = fh_xml_find_value(xml_line, key, &pend); if (!pchar) { return NULL; } int len = pend - pchar; if (len >= 64) return NULL; strncpy(value, pchar, pend - pchar); value[pend - pchar] = '\0'; return value; } static void fh_xml_set_value(char *xml_line, const char *key, unsigned value) { char *pend; const char *pchar = fh_xml_find_value(xml_line, key, &pend); char value_str[32]; char *tmp_line = malloc(strlen(xml_line) + 1 + sizeof(value_str)); if (!pchar || !tmp_line) { if (tmp_line) { free(tmp_line); tmp_line = NULL; } return; } strcpy(tmp_line, xml_line); snprintf(value_str, sizeof(value_str), "%u", value); tmp_line[pchar - xml_line] = '\0'; strcat(tmp_line, value_str); strcat(tmp_line, pend); strcpy(xml_line, tmp_line); free(tmp_line); } static int fh_parse_xml_line(const char *xml_line, struct fh_cmd *fh_cmd) { const char *pchar = NULL; size_t len = strlen(xml_line); memset(fh_cmd, 0, sizeof(struct fh_cmd)); strncpy(fh_cmd->xml_original_data, xml_line, 512); if (fh_cmd->xml_original_data[len - 1] == '\n') fh_cmd->xml_original_data[len - 1] = '\0'; if (strstr(xml_line, "vendor=\"quectel\"")) { fh_cmd->vdef.type = "vendor"; return 0; } else if (!strncmp(xml_line, "erase.type = "erase"; if (strstr(xml_line, "last_sector")) { if ((pchar = fh_xml_get_value(xml_line, "last_sector"))) fh_cmd->erase.last_sector = atoi(pchar); } if ((pchar = fh_xml_get_value(xml_line, "start_sector"))) fh_cmd->erase.start_sector = atoi(pchar); if ((pchar = fh_xml_get_value(xml_line, "num_partition_sectors"))) fh_cmd->erase.num_partition_sectors = atoi(pchar); if ((pchar = fh_xml_get_value(xml_line, "SECTOR_SIZE_IN_BYTES"))) fh_cmd->erase.SECTOR_SIZE_IN_BYTES = atoi(pchar); return 0; } else if (!strncmp(xml_line, "program.type = "program"; if ((pchar = fh_xml_get_value(xml_line, "filename"))) { fh_cmd->program.filename = strdup(pchar); if (fh_cmd->program.filename[0] == '\0') { // some fw version have blank program line, ignore it. return -1; } } if ((pchar = fh_xml_get_value(xml_line, "sparse"))) { fh_cmd->program.sparse = strdup(pchar); } else fh_cmd->program.sparse = NULL; if ((pchar = fh_xml_get_value(xml_line, "start_sector"))) fh_cmd->program.start_sector = atoi(pchar); if ((pchar = fh_xml_get_value(xml_line, "num_partition_sectors"))) fh_cmd->program.num_partition_sectors = atoi(pchar); if ((pchar = fh_xml_get_value(xml_line, "SECTOR_SIZE_IN_BYTES"))) fh_cmd->program.SECTOR_SIZE_IN_BYTES = atoi(pchar); if (fh_cmd->program.sparse != NULL && !strncasecmp(fh_cmd->program.sparse, "true", 4)) { if ((pchar = fh_xml_get_value(xml_line, "file_sector_offset"))) fh_cmd->program.file_sector_offset = atoi(pchar); if ((pchar = fh_xml_get_value(xml_line, "physical_partition_number"))) fh_cmd->program.physical_partition_number = atoi(pchar); } return 0; } else if (!strncmp(xml_line, "patch.type = "patch"; pchar = fh_xml_get_value(xml_line, "filename"); if (pchar && strcmp(pchar, "DISK")) return -1; return 0; } else if (!strncmp(xml_line, "response.type = "response"; pchar = fh_xml_get_value(xml_line, "value"); if (pchar) { if (!strcmp(pchar, "ACK")) fh_cmd->response.value = "ACK"; else if (!strcmp(pchar, "NAK")) fh_cmd->response.value = "NAK"; else fh_cmd->response.value = "OTHER"; } if (strstr(xml_line, "rawmode")) { pchar = fh_xml_get_value(xml_line, "rawmode"); if (pchar) { fh_cmd->response.rawmode = !strcmp(pchar, "true"); } } else if (strstr(xml_line, "MaxPayloadSizeToTargetInBytes")) { pchar = fh_xml_get_value(xml_line, "MaxPayloadSizeToTargetInBytes"); if (pchar) { fh_cmd->response.MaxPayloadSizeToTargetInBytes = atoi(pchar); } } return 0; } else if (!strncmp(xml_line, "program.type = "log"; return 0; } error_return(); } static int fh_parse_xml_file(struct fh_data *fh_data, const char *xml_file) { FILE *fp = fopen(xml_file, "rb"); if (fp == NULL) { dbg_time("%s fail to fopen(%s), errno: %d (%s)\n", __func__, xml_file, errno, strerror(errno)); error_return(); } while (fgets(fh_data->xml_tx_buf, fh_data->xml_tx_size, fp)) { char *xml_line = strstr(fh_data->xml_tx_buf, "<"); char *c_start = NULL; if (!xml_line) continue; c_start = strstr(xml_line, ""); if (c_end) { /* */ char *tmp = strstr(xml_line, "/>"); if (tmp && (tmp < c_start || tmp > c_end)) { memset(c_start, ' ', c_end - c_start + strlen("-->")); goto __fh_parse_xml_line; } continue; } else { /* --> */ do { if (fgets(fh_data->xml_tx_buf, fh_data->xml_tx_size, fp) == NULL) { break; }; xml_line = fh_data->xml_tx_buf; } while (!strstr(xml_line, "-->") && strstr(xml_line, "