/* * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of The Linux Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define QCDT_MAGIC "QCDT" /* Master DTB magic */ #define QCDT_VERSION 3 /* QCDT version */ #define QCDT_DT_TAG "qcom,msm-id = <" #define QCDT_BOARD_TAG "qcom,board-id = <" #define QCDT_PMIC_TAG "qcom,pmic-id = <" #define PAGE_SIZE_DEF 2048 #define PAGE_SIZE_MAX (1024*1024) #define log_err(x...) printf(x) #define log_info(x...) printf(x) #define log_dbg(x...) { if (verbose) printf(x); } #define COPY_BLK 1024 /* File copy block size */ #define RC_SUCCESS 0 #define RC_ERROR -1 struct chipInfo_t { uint32_t chipset; uint32_t platform; uint32_t subtype; uint32_t revNum; uint32_t pmic_model[4]; uint32_t dtb_size; char *dtb_file; struct chipInfo_t *prev; struct chipInfo_t *next; struct chipInfo_t *master; int wroteDtb; uint32_t master_offset; struct chipInfo_t *t_next; }; struct chipInfo_t *chip_list; struct chipId_t { uint32_t chipset; uint32_t revNum; struct chipId_t *next; struct chipId_t *t_next; }; struct chipSt_t { uint32_t platform; uint32_t subtype; struct chipSt_t *next; struct chipSt_t *t_next; }; struct chipPt_t { uint32_t pmic0; uint32_t pmic1; uint32_t pmic2; uint32_t pmic3; struct chipPt_t *next; struct chipPt_t *t_next; }; char *input_dir; char *output_file; char *dtc_path; int verbose; int page_size = PAGE_SIZE_DEF; void print_help() { log_info("dtbTool version %d\n", QCDT_VERSION); log_info("dtbTool [options] -o \n"); log_info(" options:\n"); log_info(" --output-file/-o output file\n"); log_info(" --dtc-path/-p path to dtc\n"); log_info(" --page-size/-s page size in bytes\n"); log_info(" --verbose/-v verbose\n"); log_info(" --help/-h this help screen\n"); } int parse_commandline(int argc, char *const argv[]) { int c; struct option long_options[] = { {"output-file", 1, 0, 'o'}, {"dtc-path", 1, 0, 'p'}, {"page-size", 1, 0, 's'}, {"verbose", 0, 0, 'v'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while ((c = getopt_long(argc, argv, "-o:p:s:vh", long_options, NULL)) != -1) { switch (c) { case 1: if (!input_dir) input_dir = optarg; break; case 'o': output_file = optarg; break; case 'p': dtc_path = optarg; break; case 's': page_size = atoi(optarg); if ((page_size <= 0) || (page_size > (PAGE_SIZE_MAX))) { log_err("Invalid page size (> 0 and <=1MB\n"); return RC_ERROR; } break; case 'v': verbose = 1; break; case 'h': default: return RC_ERROR; } } if (!output_file) { log_err("Output file must be specified\n"); return RC_ERROR; } if (!input_dir) input_dir = "./"; if (!dtc_path) dtc_path = ""; return RC_SUCCESS; } /* Unique entry sorted list add (by chipset->platform->rev) */ int chip_add(struct chipInfo_t *c) { struct chipInfo_t *x = chip_list; if (!chip_list) { chip_list = c; c->next = NULL; c->prev = NULL; return RC_SUCCESS; } while (1) { if ((c->chipset < x->chipset) || ((c->chipset == x->chipset) && ((c->platform < x->platform) || ((c->platform == x->platform) && ((c->subtype < x->subtype) || ((c->subtype == x->subtype) && (c->revNum < x->revNum))))))) { if (!x->prev) { c->next = x; c->prev = NULL; x->prev = c; chip_list = c; break; } else { c->next = x; c->prev = x->prev; x->prev->next = c; x->prev = c; break; } } if ((c->chipset == x->chipset) && (c->platform == x->platform) && (c->subtype == x->subtype) && (c->revNum == x->revNum) && (c->pmic_model[0] == x->pmic_model[0]) && (c->pmic_model[1] == x->pmic_model[1]) && (c->pmic_model[2] == x->pmic_model[2]) && (c->pmic_model[3] == x->pmic_model[3])) { return RC_ERROR; /* duplicate */ } if (!x->next) { c->prev = x; c->next = NULL; x->next = c; break; } x = x->next; } return RC_SUCCESS; } void chip_deleteall() { struct chipInfo_t *c = chip_list, *t; while (c) { t = c; c = c->next; if (t->dtb_file) free(t->dtb_file); free(t); } } /* For v1 Extract 'qcom,msm-id' parameter triplet from DTB qcom,msm-id = ; For v2 Extract 'qcom,msm-id', 'qcom,board-id' parameter double from DTB qcom,msm-id = i.e chipset, revision number; qcom,board-id = i.e platform and sub-type; */ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversion) { const char str1[] = "dtc -I dtb -O dts \""; const char str2[] = "\" 2>&1"; char *buf, *pos; char *line = NULL; size_t line_size; FILE *pfile; int llen; struct chipInfo_t *chip = NULL, *tmp, *chip_t; uint32_t data[3] = {0, 0, 0}; uint32_t data_st[2] = {0, 0}; uint32_t data_pt[4] = {0, 0, 0, 0}; char *tok, *sptr = NULL; int i, entryValid, entryEnded; int count = 0, count1 = 0, count2 = 0, count3 = 0; int entryValidST, entryEndedST, entryValidDT, entryEndedDT, entryValidPT, entryEndedPT; struct chipId_t *chipId = NULL, *cId = NULL, *tmp_id = NULL; struct chipSt_t *chipSt = NULL, *cSt = NULL, *tmp_st = NULL; struct chipPt_t *chipPt = NULL, *cPt = NULL, *tmp_pt = NULL; struct chipId_t *chipId_tmp = NULL; struct chipSt_t *chipSt_tmp = NULL; struct chipPt_t *chipPt_tmp = NULL; line_size = 1024; line = (char *)malloc(line_size); if (!line) { log_err("Out of memory\n"); return NULL; } llen = sizeof(char) * (strlen(dtc_path) + strlen(str1) + strlen(str2) + strlen(filename) + 1); buf = (char *)malloc(llen); if (!buf) { log_err("Out of memory\n"); free(line); return NULL; } strncpy(buf, dtc_path, llen); strncat(buf, str1, llen); strncat(buf, filename, llen); strncat(buf, str2, llen); pfile = popen(buf, "r"); free(buf); if (pfile == NULL) { log_err("... skip, fail to decompile dtb\n"); } else { /* Find "qcom,msm-id" */ while ((llen = getline(&line, &line_size, pfile)) != -1) { if (msmversion == 1) { if ((pos = strstr(line, QCDT_DT_TAG)) != NULL) { pos += strlen(QCDT_DT_TAG); entryEnded = 0; while (1) { entryValid = 1; for (i = 0; i < 3; i++) { tok = strtok_r(pos, " \t", &sptr); pos = NULL; if (tok != NULL) { if (*tok == '>') { entryEnded = 1; entryValid = 0; break; } data[i] = strtoul(tok, NULL, 0); } else { data[i] = 0; entryValid = 0; entryEnded = 1; } } if (entryEnded) { free(line); pclose(pfile); *num = count; return chip; } if (entryValid) { tmp = (struct chipInfo_t *) malloc(sizeof(struct chipInfo_t)); if (!tmp) { log_err("Out of memory\n"); break; } if (!chip) { chip = tmp; chip->t_next = NULL; } else { tmp->t_next = chip->t_next; chip->t_next = tmp; } tmp->chipset = data[0]; tmp->platform = data[1]; tmp->subtype = 0; tmp->revNum = data[2]; tmp->pmic_model[0] = 0; tmp->pmic_model[1] = 0; tmp->pmic_model[2] = 0; tmp->pmic_model[3] = 0; tmp->dtb_size = 0; tmp->dtb_file = NULL; tmp->master = chip; tmp->wroteDtb = 0; tmp->master_offset = 0; count++; } } log_err("... skip, incorrect '%s' format\n", QCDT_DT_TAG); break; } } else if (msmversion == 2 || msmversion == 3) { if ((pos = strstr(line, QCDT_DT_TAG)) != NULL) { pos += strlen(QCDT_DT_TAG); entryEndedDT = 0; for (;entryEndedDT < 1;) { entryValidDT = 1; for (i = 0; i < 2; i++) { tok = strtok_r(pos, " \t", &sptr); pos = NULL; if (tok != NULL) { if (*tok == '>') { entryEndedDT = 1; entryValidDT = 0; break; } data_st[i] = strtoul(tok, NULL, 0); } else { data_st[i] = 0; entryValidDT = 0; entryEndedDT = 1; } } if (entryValidDT) { tmp_id = (struct chipId_t *) malloc(sizeof(struct chipId_t)); if (!tmp_id) { log_err("Out of memory\n"); break; } if (!chipId) { chipId = tmp_id; cId = tmp_id; chipId->t_next = NULL; } else { tmp_id->t_next = chipId->t_next; chipId->t_next = tmp_id; } tmp_id->chipset = data_st[0]; tmp_id->revNum= data_st[1]; count1++; } } } if ((pos = strstr(line,QCDT_BOARD_TAG)) != NULL) { pos += strlen(QCDT_BOARD_TAG); entryEndedST = 0; for (;entryEndedST < 1;) { entryValidST = 1; for (i = 0; i < 2; i++) { tok = strtok_r(pos, " \t", &sptr); pos = NULL; if (tok != NULL) { if (*tok == '>') { entryEndedST = 1; entryValidST = 0; break; } data_st[i] = strtoul(tok, NULL, 0); } else { data_st[i] = 0; entryValidST = 0; entryEndedST = 1; } } if (entryValidST) { tmp_st = (struct chipSt_t *) malloc(sizeof(struct chipSt_t)); if (!tmp_st) { log_err("Out of memory\n"); break; } if (!chipSt) { chipSt = tmp_st; cSt = tmp_st; chipSt->t_next = NULL; } else { tmp_st->t_next = chipSt->t_next; chipSt->t_next = tmp_st; } tmp_st->platform = data_st[0]; tmp_st->subtype= data_st[1]; count2++; } } } if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) { pos += strlen(QCDT_PMIC_TAG); entryEndedPT = 0; for (;entryEndedPT < 1;) { entryValidPT = 1; for (i = 0; i < 4; i++) { tok = strtok_r(pos, " \t", &sptr); pos = NULL; if (tok != NULL) { if (*tok == '>') { entryEndedPT = 1; entryValidPT = 0; break; } data_pt[i] = strtoul(tok, NULL, 0); } else { data_pt[i] = 0; entryValidPT = 0; entryEndedPT = 1; } } if (entryValidPT) { tmp_pt = (struct chipPt_t *) malloc(sizeof(struct chipPt_t)); if (!tmp_pt) { log_err("Out of memory\n"); break; } if (!chipPt) { chipPt = tmp_pt; cPt = tmp_pt; chipPt->t_next = NULL; } else { tmp_pt->t_next = chipPt->t_next; chipPt->t_next = tmp_pt; } tmp_pt->pmic0 = data_pt[0]; tmp_pt->pmic1 = data_pt[1]; tmp_pt->pmic2 = data_pt[2]; tmp_pt->pmic3 = data_pt[3]; count3++; } } } } } } if (line) free(line); if (count1 == 0) { log_err("... skip, incorrect '%s' format\n", QCDT_DT_TAG); return NULL; } if (count2 == 0) { log_err("... skip, incorrect '%s' format\n", QCDT_BOARD_TAG); return NULL; } if (count3 == 0 && msmversion == 3) { log_err("... skip, incorrect '%s' format\n", QCDT_PMIC_TAG); return NULL; } tmp_st = cSt; tmp_pt = cPt; while (cId != NULL) { while (cSt != NULL) { if (msmversion == 3) { while (cPt != NULL) { tmp = (struct chipInfo_t *) malloc(sizeof(struct chipInfo_t)); if (!tmp) { log_err("Out of memory\n"); break; } if (!chip) { chip = tmp; chip->t_next = NULL; } else { tmp->t_next = chip->t_next; chip->t_next = tmp; } tmp->chipset = cId->chipset; tmp->platform = cSt->platform; tmp->revNum = cId->revNum; tmp->subtype = cSt->subtype; tmp->pmic_model[0] = cPt->pmic0; tmp->pmic_model[1] = cPt->pmic1; tmp->pmic_model[2] = cPt->pmic2; tmp->pmic_model[3] = cPt->pmic3; tmp->dtb_size = 0; tmp->dtb_file = NULL; tmp->master = chip; tmp->wroteDtb = 0; tmp->master_offset = 0; cPt = cPt->t_next; } cPt = tmp_pt; } else { tmp = (struct chipInfo_t *) malloc(sizeof(struct chipInfo_t)); if (!tmp) { log_err("Out of memory\n"); break; } if (!chip) { chip = tmp; chip->t_next = NULL; } else { tmp->t_next = chip->t_next; chip->t_next = tmp; } tmp->chipset = cId->chipset; tmp->platform = cSt->platform; tmp->revNum = cId->revNum; tmp->subtype = cSt->subtype; tmp->pmic_model[0] = 0; tmp->pmic_model[1] = 0; tmp->pmic_model[2] = 0; tmp->pmic_model[3] = 0; tmp->dtb_size = 0; tmp->dtb_file = NULL; tmp->master = chip; tmp->wroteDtb = 0; tmp->master_offset = 0; } cSt = cSt->t_next; } cSt = tmp_st; cId = cId->t_next; } if (msmversion == 2) entryEndedPT = 1; /* clear memory*/ pclose(pfile); while (chipId) { chipId_tmp = chipId; chipId = chipId->t_next; free(chipId_tmp); } while (chipSt) { chipSt_tmp= chipSt; chipSt = chipSt->t_next; free(chipSt_tmp); } while (chipPt) { chipPt_tmp= chipPt; chipPt = chipPt->t_next; free(chipPt_tmp); } if (entryEndedST == 1 && entryEndedDT == 1 && entryEndedPT == 1) { *num = count1; return chip; } /* clear memory*/ while (chip) { chip_t = chip; chip = chip->next; if (chip_t->dtb_file) free(chip_t->dtb_file); free(chip_t); } return NULL; } /* Get the version-id based on dtb files */ int GetVersionInfo(const char *filename) { const char str1[] = "dtc -I dtb -O dts \""; const char str2[] = "\" 2>&1"; char *buf, *pos; char *line = NULL; size_t line_size; FILE *pfile; int llen; int v = 1; line_size = 1024; line = (char *)malloc(line_size); if (!line) { log_err("Out of memory\n"); return 0; } llen = sizeof(char) * (strlen(dtc_path) + strlen(str1) + strlen(str2) + strlen(filename) + 1); buf = (char *)malloc(llen); if (!buf) { log_err("Out of memory\n"); free(line); return 0; } strncpy(buf, dtc_path, llen); strncat(buf, str1, llen); strncat(buf, filename, llen); strncat(buf, str2, llen); pfile = popen(buf, "r"); free(buf); if (pfile == NULL) { log_err("... skip, fail to decompile dtb\n"); } else { /* Find the type of version */ while ((llen = getline(&line, &line_size, pfile)) != -1) { if ((pos = strstr(line,QCDT_BOARD_TAG)) != NULL) { v = 2; } if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) { v = 3; break; } } } free(line); log_info("Version:%d\n", v); return v; } /* Extract 'qcom,msm-id' 'qcom,board-id' parameter from DTB v1 format: qcom,msm-id = [, ...]; v2 format: qcom,msm-id = [, ...; qcom,board-id = [, ...; Fields: x = chipset y = platform y' = subtype z = soc rev */ int main(int argc, char **argv) { char buf[COPY_BLK]; struct chipInfo_t *chip, *t_chip; struct dirent *dp; FILE *pInputFile; char *filename; int padding; uint8_t *filler = NULL; int numBytesRead = 0; int totBytesRead = 0; int out_fd; int flen; int rc = RC_SUCCESS; int dtb_count = 0, dtb_offset = 0; size_t wrote = 0, expected = 0; struct stat st; uint32_t version = QCDT_VERSION; int num; uint32_t dtb_size; int msmversion = 0; log_info("DTB combiner:\n"); if (parse_commandline(argc, argv) != RC_SUCCESS) { print_help(); return RC_ERROR; } log_info(" Input directory: '%s'\n", input_dir); log_info(" Output file: '%s'\n", output_file); DIR *dir = opendir(input_dir); if (!dir) { log_err("Failed to open input directory '%s'\n", input_dir); return RC_ERROR; } filler = (uint8_t *)malloc(page_size); if (!filler) { log_err("Out of memory\n"); closedir(dir); return RC_ERROR; } memset(filler, 0, page_size); /* Open the .dtb files in the specified path, decompile and extract "qcom,msm-id" parameter */ while ((dp = readdir(dir)) != NULL) { if ((dp->d_type == DT_REG)) { flen = strlen(dp->d_name); if ((flen > 4) && (strncmp(&dp->d_name[flen-4], ".dtb", 4) == 0)) { log_info("Found file: %s ... \n", dp->d_name); flen = strlen(input_dir) + strlen(dp->d_name) + 1; filename = (char *)malloc(flen); if (!filename) { log_err("Out of memory\n"); rc = RC_ERROR; break; } strncpy(filename, input_dir, flen); strncat(filename, dp->d_name, flen); /* To identify the version number */ msmversion = GetVersionInfo(filename); num = 1; chip = getChipInfo(filename, &num, msmversion); if (msmversion == 1) { if (!chip) { log_err("skip, failed to scan for '%s' tag\n", QCDT_DT_TAG); free(filename); continue; } } if (msmversion == 2) { if (!chip) { log_err("skip, failed to scan for '%s' or '%s' tag\n", QCDT_DT_TAG, QCDT_BOARD_TAG); free(filename); continue; } } if (msmversion == 3) { if (!chip) { log_err("skip, failed to scan for '%s', '%s' or '%s' tag\n", QCDT_DT_TAG, QCDT_BOARD_TAG, QCDT_PMIC_TAG); free(filename); continue; } } if ((stat(filename, &st) != 0) || (st.st_size == 0)) { log_err("skip, failed to get DTB size\n"); free(filename); continue; } log_info("chipset: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n", chip->chipset, chip->revNum, chip->platform, chip->subtype, chip->pmic_model[0], chip->pmic_model[1], chip->pmic_model[2], chip->pmic_model[3]); for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { log_info("additional chipset: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n", t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype, t_chip->pmic_model[0], t_chip->pmic_model[1], t_chip->pmic_model[2], t_chip->pmic_model[3]); } rc = chip_add(chip); if (rc != RC_SUCCESS) { log_err("... duplicate info, skipped\n"); free(filename); continue; } dtb_count++; chip->dtb_size = st.st_size + (page_size - (st.st_size % page_size)); chip->dtb_file = filename; for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { rc = chip_add(t_chip); if (rc != RC_SUCCESS) { log_err("... duplicate info, skipped (chipset %u, rev: %u, platform: %u, subtype %u:\n", t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype); continue; } dtb_count++; } } } } closedir(dir); log_info("=> Found %d unique DTB(s)\n", dtb_count); if (!dtb_count) goto cleanup; /* Generate the master DTB file: Simplify write error handling by just checking for actual vs expected bytes written at the end. */ log_info("\nGenerating master DTB... "); out_fd = open(output_file, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if (!out_fd < 0) { log_err("Cannot create '%s'\n", output_file); rc = RC_ERROR; goto cleanup; } /* Write header info */ wrote += write(out_fd, QCDT_MAGIC, sizeof(uint8_t) * 4); /* magic */ wrote += write(out_fd, &version, sizeof(uint32_t)); /* version */ wrote += write(out_fd, (uint32_t *)&dtb_count, sizeof(uint32_t)); /* #DTB */ /* Calculate offset of first DTB block */ dtb_offset = 12 + /* header */ (40 * dtb_count) + /* DTB table entries */ 4; /* end of table indicator */ /* Round up to page size */ padding = page_size - (dtb_offset % page_size); dtb_offset += padding; expected = dtb_offset; /* Write index table: chipset platform subtype soc rev pmic model0 pmic model1 pmic model2 pmic model3 dtb offset dtb size */ for (chip = chip_list; chip; chip = chip->next) { wrote += write(out_fd, &chip->chipset, sizeof(uint32_t)); wrote += write(out_fd, &chip->platform, sizeof(uint32_t)); wrote += write(out_fd, &chip->subtype, sizeof(uint32_t)); wrote += write(out_fd, &chip->revNum, sizeof(uint32_t)); wrote += write(out_fd, &chip->pmic_model[0], sizeof(uint32_t)); wrote += write(out_fd, &chip->pmic_model[1], sizeof(uint32_t)); wrote += write(out_fd, &chip->pmic_model[2], sizeof(uint32_t)); wrote += write(out_fd, &chip->pmic_model[3], sizeof(uint32_t)); if (chip->master->master_offset != 0) { wrote += write(out_fd, &chip->master->master_offset, sizeof(uint32_t)); } else { wrote += write(out_fd, &expected, sizeof(uint32_t)); chip->master->master_offset = expected; expected += chip->master->dtb_size; } wrote += write(out_fd, &chip->master->dtb_size, sizeof(uint32_t)); } rc = RC_SUCCESS; wrote += write(out_fd, &rc, sizeof(uint32_t)); /* end of table indicator */ if (padding > 0) wrote += write(out_fd, filler, padding); /* Write DTB's */ for (chip = chip_list; chip; chip = chip->next) { if (chip->master->wroteDtb) { continue; } chip->master->wroteDtb = 1; filename = chip->master->dtb_file; dtb_size = chip->master->dtb_size; log_dbg("\n (writing '%s' - %u bytes) ", filename, dtb_size); pInputFile = fopen(filename, "r"); if (pInputFile != NULL) { totBytesRead = 0; while ((numBytesRead = fread(buf, 1, COPY_BLK, pInputFile)) > 0) { wrote += write(out_fd, buf, numBytesRead); totBytesRead += numBytesRead; } fclose(pInputFile); padding = page_size - (totBytesRead % page_size); if ((uint32_t)(totBytesRead + padding) != dtb_size) { log_err("DTB size mismatch, please re-run: expected %d vs actual %d (%s)\n", dtb_size, totBytesRead + padding, filename); rc = RC_ERROR; break; } if (padding > 0) wrote += write(out_fd, filler, padding); } else { log_err("failed to open DTB '%s'\n", filename); rc = RC_ERROR; break; } } close(out_fd); if (expected != wrote) { log_err("error writing output file, please rerun: size mismatch %d vs %d\n", expected, wrote); rc = RC_ERROR; } else log_dbg("Total wrote %u bytes\n", wrote); if (rc != RC_SUCCESS) unlink(output_file); else log_info("completed\n"); cleanup: free(filler); chip_deleteall(); return rc; }