static int rx_handler (const unsigned char *buffer, unsigned int buffer_size) { int ret = 1; /* Use 65 instead of 64 null gets dropped strcpy's need the extra byte */ char response[65]; if (download_size) { /* Something to download */ if (buffer_size) { /* Handle possible overflow */ unsigned int transfer_size = download_size - download_bytes; if (buffer_size < transfer_size) transfer_size = buffer_size; /* Save the data to the transfer buffer */ memcpy (interface.transfer_buffer + download_bytes, buffer, transfer_size); download_bytes += transfer_size; /* Check if transfer is done */ if (download_bytes >= download_size) { /* Reset global transfer variable, Keep download_bytes because it will be used in the next possible flashing command */ download_size = 0; if (download_error) { /* There was an earlier error */ sprintf(response, "ERROR"); } else { /* Everything has transferred, send the OK response */ sprintf(response, "OKAY"); } fastboot_tx_status(response, strlen(response)); printf ("\ndownloading of %d bytes finished\n", download_bytes); /* Padding is required only if storage medium is NAND */ if (interface.storage_medium == NAND) { /* Pad to block length In most cases, padding the download to be block aligned is correct. The exception is when the following flash writes to the oob area. This happens when the image is a YAFFS image. Since we do not know what the download is until it is flashed, go ahead and pad it, but save the true size in case if should have been unpadded */ download_bytes_unpadded = download_bytes; if (interface.nand_block_size) { if (download_bytes % interface.nand_block_size) { unsigned int pad = interface.nand_block_size - (download_bytes % interface.nand_block_size); unsigned int i; for (i = 0; i < pad; i++) { if (download_bytes >= interface.transfer_buffer_size) break; interface.transfer_buffer[download_bytes] = 0; download_bytes++; } } } } } /* Provide some feedback */ if (download_bytes && 0 == (download_bytes % (16 * interface.nand_block_size))) { /* Some feeback that the download is happening */ if (download_error) printf("X"); else printf("."); if (0 == (download_bytes % (80 * 16 * interface.nand_block_size))) printf("\n"); } } else { /* Ignore empty buffers */ printf ("Warning empty download buffer\n"); printf ("Ignoring\n"); } ret = 0; } else { /* A command */ fastboot_confirmed=1; /* Cast to make compiler happy with string functions */ const char *cmdbuf = (char *) buffer; /* Generic failed response */ sprintf(response, "FAIL"); /* reboot Reboot the board. */ if(memcmp(cmdbuf, "reboot-bootloader", 17) == 0) { sprintf(response,"OKAY"); fastboot_tx_status(response, strlen(response)); /* Clear all reset reasons */ __raw_writel(0xfff, PRM_RSTST); strcpy(PUBLIC_SAR_RAM_1_FREE, "reboot-bootloader"); /* now warm reset the silicon */ __raw_writel(PRM_RSTCTRL_RESET_WARM_BIT, PRM_RSTCTRL); return 0; } if(memcmp(cmdbuf, "reboot", 6) == 0) { sprintf(response,"OKAY"); fastboot_tx_status(response, strlen(response)); do_reset (NULL, 0, 0, NULL); /* This code is unreachable, leave it to make the compiler happy */ return 0; } /* getvar Get common fastboot variables Board has a chance to handle other variables */ if(memcmp(cmdbuf, "getvar:", 7) == 0) { int get_var_length = strlen("getvar:"); strcpy(response,"OKAY"); if(!strcmp(cmdbuf + get_var_length, "version")) { strcpy(response + 4, FASTBOOT_VERSION); } else if(!strcmp(cmdbuf + get_var_length, "product")) { if (interface.product_name) strcpy(response + 4, interface.product_name); } else if(!strcmp(cmdbuf + get_var_length, "serialno")) { if (interface.serial_no) strcpy(response + 4, interface.serial_no); } else if(!strcmp(cmdbuf + get_var_length, "downloadsize")) { if (interface.transfer_buffer_size) sprintf(response + 4, "%08x", interface.transfer_buffer_size); } else if(!strcmp(cmdbuf + get_var_length, "cpurev")) { if (interface.proc_rev) strcpy(response + 4, interface.proc_rev); } else if(!strcmp(cmdbuf + get_var_length, "secure")) { if (interface.proc_type) strcpy(response + 4, interface.proc_type); } else { fastboot_getvar(cmdbuf + 7, response + 4); } ret = 0; } /* %fastboot oem <cmd> */ if (memcmp(cmdbuf, "oem ", 4) == 0) { ret = 0; cmdbuf += 4; if (memcmp(cmdbuf, "shutdown", 8) == 0) { sprintf(response, "SHUTDOWN"); fastboot_tx_status(response, strlen(response)); do_powerdown (); } if (memcmp(cmdbuf, "idme ", 5) == 0) { ret = fastboot_idme(cmdbuf + 5); if (ret) { strcpy(response,"FAIL"); } else { strcpy(response,"OKAY"); } ret = 0; goto done; } /* fastboot oem format */ if(memcmp(cmdbuf, "format", 6) == 0){ ret = fastboot_oem(cmdbuf); if (ret < 0) { strcpy(response,"FAIL"); } else { strcpy(response,"OKAY"); } goto done; } /* fastboot oem recovery */ if(memcmp(cmdbuf, "recovery", 8) == 0){ sprintf(response,"OKAY"); fastboot_tx_status(response, strlen(response)); /* Clear all reset reasons */ __raw_writel(0xfff, PRM_RSTST); strcpy(PUBLIC_SAR_RAM_1_FREE, "recovery"); /* now warm reset the silicon */ __raw_writel(PRM_RSTCTRL_RESET_WARM_BIT, PRM_RSTCTRL); /* Never returns */ while(1); } /* fastboot oem unlock */ if(memcmp(cmdbuf, "unlock", 6) == 0){ sprintf(response,"FAIL"); printf("\nfastboot: oem unlock "\ "not implemented yet!!\n"); goto done; } /* fastboot oem [xxx] */ printf("\nfastboot: do not understand oem %s\n", cmdbuf); strcpy(response,"FAIL"); goto done; } /* end: %fastboot oem <cmd> */ /* erase Erase a register flash partition Board has to set up flash partitions */ if(memcmp(cmdbuf, "erase:", 6) == 0){ if (interface.storage_medium == NAND) { /* storage medium is NAND */ struct fastboot_ptentry *ptn; ptn = fastboot_flash_find_ptn(cmdbuf + 6); if (ptn == 0) { sprintf(response, "FAILpartition does not exist"); } else { char start[32], length[32]; int status = 0, repeat, repeat_max; printf("erasing '%s'\n", ptn->name); char *lock[5] = { "nand", "lock", NULL, NULL, NULL, }; char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, }; char *erase[5] = { "nand", "erase", NULL, NULL, NULL, }; lock[2] = unlock[2] = erase[2] = start; lock[3] = unlock[3] = erase[3] = length; repeat_max = 1; if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK) repeat_max = ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK; sprintf (length, "0x%x", ptn->length); for (repeat = 0; repeat < repeat_max; repeat++) { sprintf (start, "0x%x", ptn->start + (repeat * ptn->length)); #if 0 do_nand (NULL, 0, 4, unlock); status = do_nand (NULL, 0, 4, erase); do_nand (NULL, 0, 4, lock); #endif if (status) break; } if (status) { sprintf(response, "FAILfailed to erase partition"); } else { printf("partition '%s' erased\n", ptn->name); sprintf(response, "OKAY"); } } } else if (interface.storage_medium == EMMC) { /* storage medium is EMMC */ struct fastboot_ptentry *ptn; /* Save the MMC controller number */ #if defined(CONFIG_4430PANDA) /* panda board does not have eMMC on mmc1 */ mmc_controller_no = 0; #else /* blaze has emmc on mmc1 */ mmc_controller_no = 1; #endif /* Find the partition and erase it */ ptn = fastboot_flash_find_ptn(cmdbuf + 6); if (ptn == 0) { sprintf(response, "FAIL: partition doesn't exist"); } else { /* Call MMC erase function here */ char start[32], length[32]; char slot_no[32]; char *erase[5] = { "mmc", NULL, "erase", NULL, NULL, }; char *mmc_init[2] = {"mmcinit", NULL,}; mmc_init[1] = slot_no; erase[1] = slot_no; erase[3] = start; erase[4] = length; sprintf(slot_no, "%d", mmc_controller_no); sprintf(length, "0x%x", ptn->length); sprintf(start, "0x%x", ptn->start); printf("Initializing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 2, mmc_init)) sprintf(response, "FAIL: Init of MMC card"); else sprintf(response, "OKAY"); printf("Erasing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 5, erase)) { printf("Erasing '%s' FAILED!\n", ptn->name); sprintf(response, "FAIL: Erase partition"); } else { printf("Erasing '%s' DONE!\n", ptn->name); sprintf(response, "OKAY"); } } } ret = 0; } /* EMMC Erase Erase a register flash partition on MMC Board has to set up flash partitions */ if (memcmp(cmdbuf, "mmcerase:", 9) == 0) { struct fastboot_ptentry *ptn; /* Save the MMC controller number */ mmc_controller_no = simple_strtoul(cmdbuf + 9, NULL, 10); /* Find the partition and erase it */ ptn = fastboot_flash_find_ptn(cmdbuf + 11); if (ptn == 0) { sprintf(response, "FAIL: partition doesn't exist"); } else { /* Call MMC erase function here */ /* This is not complete */ char start[32], length[32]; char slot_no[32]; char *erase[5] = { "mmc", NULL, "erase", NULL, NULL, }; char *mmc_init[2] = {"mmcinit", NULL,}; mmc_init[1] = slot_no; erase[1] = slot_no; erase[3] = start; erase[4] = length; sprintf(slot_no, "%d", mmc_controller_no); sprintf(length, "0x%x", ptn->length); sprintf(start, "0x%x", ptn->start); printf("Initializing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 2, mmc_init)) { sprintf(response, "FAIL: Init of MMC card"); } else { sprintf(response, "OKAY"); } printf("Erasing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 5, erase)) { sprintf(response, "FAIL: Erase partition"); } else { sprintf(response, "OKAY"); } } } /* download download something .. What happens to it depends on the next command after data */ if(memcmp(cmdbuf, "download:", 9) == 0) { /* save the size */ download_size = simple_strtoul(cmdbuf + 9, NULL, 16); /* Reset the bytes count, now it is safe */ download_bytes = 0; /* Reset error */ download_error = 0; printf("Starting download of %d bytes\n", download_size); if (0 == download_size) { /* bad user input */ sprintf(response, "FAILdata invalid size"); } else if (download_size > interface.transfer_buffer_size) { /* set download_size to 0 * because this is an error */ download_size = 0; sprintf(response, "FAILdata too large"); } else { /* The default case, the transfer fits completely in the interface buffer */ sprintf(response, "DATA%08x", download_size); } ret = 0; } /* boot boot what was downloaded ** ** +-----------------+ ** | boot header | 1 page ** +-----------------+ ** | kernel | n pages ** +-----------------+ ** | ramdisk | m pages ** +-----------------+ ** | second stage | o pages ** +-----------------+ ** Pagesize has default value of CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE */ if(memcmp(cmdbuf, "boot", 4) == 0) { if ((download_bytes) && (CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < download_bytes)) { char start[32]; char *booti_args[4] = { "booti", NULL, "boot", NULL }; /* Skip the mkbootimage header */ //boot_img_hdr *hdr = // (boot_img_hdr *) // &interface.transfer_buffer[CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE]; booti_args[1] = start; sprintf (start, "0x%x", interface.transfer_buffer); /* Execution should jump to kernel so send the response now and wait a bit. */ sprintf(response, "OKAY"); fastboot_tx_status(response, strlen(response)); printf ("Booting kernel..\n"); /* For Future use * if (strlen ((char *) &fb_hdr->cmdline[0])) * set_env ("bootargs", (char *) &fb_hdr->cmdline[0]); */ /* boot the boot.img */ do_booti (NULL, 0, 3, booti_args); } sprintf(response, "FAILinvalid boot image"); ret = 0; } /* mmcwrite write what was downloaded on MMC*/ /* Write to MMC whatever was downloaded */ if (memcmp(cmdbuf, "mmcwrite:", 9) == 0) { if (download_bytes) { struct fastboot_ptentry *ptn; /* Save the MMC controller number */ mmc_controller_no = simple_strtoul(cmdbuf + 9, NULL, 10); /* Next is the partition name */ ptn = fastboot_flash_find_ptn(cmdbuf + 11); if (ptn == 0) { sprintf(response, "FAILpartition does not exist"); } else { char source[32], dest[32], length[32]; char slot_no[32]; printf("writing to partition '%s'\n", ptn->name); char *mmc_write[6] = {"mmc", NULL, "write", NULL, NULL, NULL}; char *mmc_init[2] = {"mmcinit", NULL,}; mmc_init[1] = slot_no; mmc_write[1] = slot_no; mmc_write[3] = source; mmc_write[4] = dest; mmc_write[5] = length; sprintf(slot_no, "%d", mmc_controller_no); sprintf(source, "0x%x", interface.transfer_buffer); sprintf(dest, "0x%x", ptn->start); sprintf(length, "0x%x", download_bytes); printf("Initializing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 2, mmc_init)) { sprintf(response, "FAIL:Init of MMC card"); } else { sprintf(response, "OKAY"); } printf("Writing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 6, mmc_write)) { sprintf(response, "FAIL: Write partition"); } else { sprintf(response, "OKAY"); } } } else { sprintf(response, "FAILno image downloaded"); } } /* flash Flash what was downloaded */ if(memcmp(cmdbuf, "flash:", 6) == 0) { if (interface.storage_medium == NAND) { /* storage medium is NAND */ if (download_bytes) { struct fastboot_ptentry *ptn; ptn = fastboot_flash_find_ptn(cmdbuf + 6); if (ptn == 0) { sprintf(response, "FAILpartition does not exist"); } else if ((download_bytes > ptn->length) && !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { sprintf(response, "FAILimage too large for partition"); /* TODO : Improve check for yaffs write */ } else { /* Check if this is not really a flash write but rather a saveenv */ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { /* Since the response can only be 64 bytes, there is no point in having a large error message. */ char err_string[32]; if (saveenv_to_ptn(ptn, &err_string[0])) { printf("savenv '%s' failed : %s\n", ptn->name, err_string); sprintf(response, "FAIL%s", err_string); } else { printf("partition '%s' saveenv-ed\n", ptn->name); sprintf(response, "OKAY"); } } else { /* Normal case */ if (write_to_ptn(ptn)) { printf("flashing '%s' failed\n", ptn->name); sprintf(response, "FAILfailed to flash partition"); } else { printf("partition '%s' flashed\n", ptn->name); sprintf(response, "OKAY"); } } } } else { sprintf(response, "FAILno image downloaded"); } } else if (interface.storage_medium == EMMC) { /* storage medium is EMMC */ if (download_bytes) { struct fastboot_ptentry *ptn; struct fastboot_ptentry tmpptn; char *argv[2] = {NULL, "-f"}; /* Save the MMC controller number */ #if defined(CONFIG_4430PANDA) /* panda board does not have eMMC on mmc1 */ mmc_controller_no = 0; #else /* blaze has emmc on mmc1 */ mmc_controller_no = 1; #endif /* Next is the partition name */ if(memcmp(cmdbuf+6, "all:", 4) == 0){ printf("Factory image testing"); ptn=&tmpptn; sprintf(ptn->name,"%s","all"); ptn->length=download_bytes; ptn->start=simple_strtoul(cmdbuf + 10, NULL, 16); printf("name=%s length=%x offset=%x\n",ptn->name,ptn->length,ptn->start); }else if(memcmp(cmdbuf+6, "bootpart:", 9) == 0){ printf("boot partition testing\n"); ptn=&tmpptn; sprintf(ptn->name,"bootpart%d",simple_strtoul(cmdbuf + 15, NULL, 10)); ptn->length=download_bytes; ptn->start=0; printf("name=%s length=%x offset=%x\n",ptn->name,ptn->length,ptn->start); }else{ ptn = fastboot_flash_find_ptn(cmdbuf + 6); } if (ptn == 0) { printf("Partition:'%s' does not exist\n", ptn->name); sprintf(response, "FAILpartition does not exist"); } else if ((download_bytes > ptn->length) && !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { printf("Image too large for the partition\n"); sprintf(response, "FAILimage too large for partition"); } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { /* Check if this is not really a flash write, * but instead a saveenv */ unsigned int i = 0; /* Env file is expected with a NULL delimeter between * env variables So replace New line Feeds (0x0a) with * NULL (0x00) */ for (i = 0; i < download_bytes; i++) { if (interface.transfer_buffer[i] == 0x0a) interface.transfer_buffer[i] = 0x00; } memset(env_ptr->data, 0, ENV_SIZE); memcpy(env_ptr->data, interface.transfer_buffer, download_bytes); do_saveenv(NULL, 0, 2, argv); printf("saveenv to '%s' DONE!\n", ptn->name); sprintf(response, "OKAY"); } else { /* Normal case */ char source[32], dest[32], length[32]; char slot_no[32]; printf("writing to partition '%s'\n", ptn->name); char *mmc_write[6] = {"mmc", NULL, "write", NULL, NULL, NULL}; char *mmc_sw_part[4] = {"mmc", NULL, "sw_part", NULL}; char *mmc_init[2] = {"mmcinit", NULL,}; mmc_init[1] = slot_no; mmc_write[1] = slot_no; mmc_write[3] = source; mmc_write[4] = dest; mmc_write[5] = length; sprintf(slot_no, "%d", mmc_controller_no); sprintf(source, "0x%x", interface.transfer_buffer); sprintf(dest, "0x%x", ptn->start); sprintf(length, "0x%x", download_bytes); printf("Initializing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 2, mmc_init)) { sprintf(response, "FAIL:Init of MMC card"); goto done; } else sprintf(response, "OKAY"); if(strncmp(ptn->name, "bootpart",8) == 0){ mmc_sw_part[1] = slot_no; mmc_sw_part[3] = source; sprintf(slot_no, "%d", mmc_controller_no); strncpy(source, &(ptn->name)+8, 1); if (do_mmc(NULL, 0, 4, mmc_sw_part)) { printf("sw_part '%s' FAILED!\n", ptn->name); sprintf(response, "FAIL: change partition"); } else { printf("sw_part '%s' DONE!\n", ptn->name); sprintf(source, "0x%x", interface.transfer_buffer); } } /* Check if we have sparse compressed image */ if ( ((sparse_header_t *)interface.transfer_buffer)->magic == SPARSE_HEADER_MAGIC) { printf("fastboot: %s is in sparse format\n", ptn->name); if (!do_unsparse(interface.transfer_buffer, ptn->start, ptn->length, slot_no)) { printf("Writing sparsed: '%s' DONE!\n", ptn->name); } else { printf("Writing sparsed '%s' FAILED!\n", ptn->name); sprintf(response, "FAIL: Sparsed Write"); } } else { /* Normal image: no sparse */ printf("Writing '%s'\n", ptn->name); if (do_mmc(NULL, 0, 6, mmc_write)) { printf("Writing '%s' FAILED!\n", ptn->name); sprintf(response, "FAIL: Write partition"); } else { printf("Writing '%s' DONE!\n", ptn->name); } } } /* Normal Case */ } else { sprintf(response, "FAILno image downloaded"); } } /* EMMC */ ret = 0; } /* fastboot flash ... */ done: fastboot_tx_status(response, strlen(response)); } /* End of command */ return ret; }
static int fastboot_flash(const char *partition) { int status = 0; struct fastboot_ptentry *ptn; char source[32], dest[32], length[32]; char *dev[3] = { "mmc", "dev", "1" }; char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; char *mmc_init[2] = {"mmc", "rescan",}; #ifdef CONFIG_SPL_SPI_SUPPORT char *sf_probe[3] = {"sf", "probe", "0"}; char *sf_write_xloader[5] = {"sf", "write", NULL, "0", "20000"}; char *sf_update_xloader[5] = {"sf", "update", NULL, "0", "20000"}; char *sf_write_bootloader[5] = {"sf", "write", NULL, "80000", "80000"}; char *sf_update_bootloader[5] = {"sf", "update", NULL, "80000", "80000"}; /*Check if this is for xloader or bootloader. Also, check if we have to flash to SPI*/ if (strcmp(partition, "xloader") == 0 && boot_from_spi) { printf("Flashing %s to SPI\n", partition); status = do_spi_flash(NULL, 0, 3, sf_probe); if (status) { fastboot_tx_write_str("FAIL:Could not probe SPI"); return status; } sf_write_xloader[2] = source; sf_update_xloader[2] = source; sprintf(source, "0x%x", (unsigned int)fb_cfg.transfer_buffer); printf("Updating X-LOADER to SPI\n"); status = do_spi_flash(NULL, 0, 5, sf_update_xloader); if(status) { fastboot_tx_write_str("FAIL:Could not update xloader to SPI"); return status; } printf("Writing X-LOADER to SPI\n"); status = do_spi_flash(NULL, 0, 5, sf_write_xloader); if (status) { fastboot_tx_write_str("FAIL:Could not write xloader to SPI"); return status; } printf("Writing xloader DONE\n"); fastboot_tx_write_str("OKAY"); return 0; } if (strcmp(partition, "bootloader") == 0 && boot_from_spi) { printf("Flashing %s to SPI\n", partition); status = do_spi_flash(NULL, 0, 3, sf_probe); if (status) { fastboot_tx_write_str("FAIL:Could not probe SPI"); return status; } sf_write_bootloader[2] = source; sf_update_bootloader[2] = source; sprintf(source, "0x%x", (unsigned int)fb_cfg.transfer_buffer); printf("Updating bootloader to SPI\n"); status = do_spi_flash(NULL, 0, 5, sf_update_bootloader); if (status) { fastboot_tx_write_str("FAIL:Could not update bootloader to SPI"); return status; } printf("Writing bootloader to SPI\n"); status = do_spi_flash(NULL, 0, 5, sf_write_bootloader); if (status) { fastboot_tx_write_str("FAIL:Could not write bootloader to SPI"); return status; } printf("Writing bootloader DONE\n"); fastboot_tx_write_str("OKAY"); return 0; } #endif if (!strcmp(partition, "zImage") || !strcmp(partition, "zimage")) { return fastboot_update_zimage(); } ptn = fastboot_flash_find_ptn(partition); if (ptn == 0) { printf("Partition:[%s] does not exist\n", partition); fastboot_tx_write_str("FAIL:Partition does not exist"); } else if ((download_bytes> ptn->length) && !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { printf("Image too large for the partition\n"); fastboot_tx_write_str("FAIL: image too large for partition"); } else { source[0] = '\0'; dest[0] = '\0'; length[0] = '\0'; mmc_write[2] = source; mmc_write[3] = dest; mmc_write[4] = length; sprintf(source, "0x%x", (unsigned int)fb_cfg.transfer_buffer); sprintf(dest, "0x%x", ptn->start); sprintf(length, "0x%x", (download_bytes/512) + 1); status = do_mmcops(NULL, 0, 3, dev); if (status) { printf("Unable to set MMC device\n"); fastboot_tx_write_str("FAIL: init of MMC"); goto exit; } status = do_mmcops(NULL, 0, 2, mmc_init); if (status) { fastboot_tx_write_str("FAIL:Init of MMC card"); goto exit; } if ( ((sparse_header_t *)fb_cfg.transfer_buffer)->magic == SPARSE_HEADER_MAGIC) { printf("fastboot: %s is in sparse format %u %llu \n", ptn->name, ptn->start, ptn->length); status = do_unsparse(fb_cfg.transfer_buffer, ptn->start, ptn->length); if (status) { printf("Writing '%s' FAILED!\n", ptn->name); fastboot_tx_write_str("FAIL: Write partition"); } else { printf("Writing sparsed: '%s' DONE!\n", ptn->name); printf("Writing '%s' DONE!\n", ptn->name); fastboot_tx_write_str("OKAY"); } } else { printf("Writing non-sparsed format '%s'\n", ptn->name); status = do_mmcops(NULL, 0, 5, mmc_write); if (status) { printf("Writing '%s' FAILED!\n", ptn->name); fastboot_tx_write_str("FAIL: Write partition"); goto exit; } else { printf("Writing '%s' DONE!\n", ptn->name); fastboot_tx_write_str("OKAY"); } } } exit: return status; }