X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Fao-stmload%2Fao-stmload.c;h=b9bf4392bc6e55f8b72c2ec758968e153b7009c7;hp=89b818dab5082baa9bf57761ea82c1e0bdf2a241;hb=c9ba2d17b979410acfa41f9954674757f7f321fc;hpb=1f30b1f14dbab6e6ea94177e459c80732e31e433 diff --git a/ao-tools/ao-stmload/ao-stmload.c b/ao-tools/ao-stmload/ao-stmload.c index 89b818da..b9bf4392 100644 --- a/ao-tools/ao-stmload/ao-stmload.c +++ b/ao-tools/ao-stmload/ao-stmload.c @@ -26,365 +26,107 @@ #include #include #include "stlink-common.h" +#include "ao-elf.h" +#include "ccdbg.h" +#include "cc-usb.h" +#include "cc.h" +#include "ao-stmload.h" #define AO_USB_DESC_STRING 3 -struct sym { - unsigned addr; - char *name; - int required; -} ao_symbols[] = { +struct sym ao_symbols[] = { - { 0, "ao_romconfig_version", 1 }, + { 0, 0x08002100, "ao_romconfig_version", 1 }, #define AO_ROMCONFIG_VERSION (ao_symbols[0].addr) - { 0, "ao_romconfig_check", 1 }, + { 0, 0x08002102, "ao_romconfig_check", 1 }, #define AO_ROMCONFIG_CHECK (ao_symbols[1].addr) - { 0, "ao_serial_number", 1 }, + { 0, 0x08002104, "ao_serial_number", 1 }, #define AO_SERIAL_NUMBER (ao_symbols[2].addr) - { 0, "ao_usb_descriptors", 0 }, -#define AO_USB_DESCRIPTORS (ao_symbols[3].addr) + { 0, 0x08002108, "ao_radio_cal", 0 }, +#define AO_RADIO_CAL (ao_symbols[3].addr) - { 0, "ao_radio_cal", 0 }, -#define AO_RADIO_CAL (ao_symbols[4].addr) + { 0, 0x0800210c, "ao_usb_descriptors", 0 }, +#define AO_USB_DESCRIPTORS (ao_symbols[4].addr) }; #define NUM_SYMBOLS 5 #define NUM_REQUIRED_SYMBOLS 3 -/* - * Look through the Elf file for the AltOS symbols - * that can be adjusted before the image is written - * to the device - */ -static int -find_symbols (Elf *e) -{ - Elf_Scn *scn; - Elf_Data *symbol_data = NULL; - GElf_Shdr shdr; - GElf_Sym sym; - int i, symbol_count, s; - int required = 0; - char *symbol_name; - char *section_name; - size_t shstrndx; - - if (elf_getshdrstrndx(e, &shstrndx) < 0) - return 0; - - /* - * Find the symbols - */ - - scn = NULL; - while ((scn = elf_nextscn(e, scn)) != NULL) { - - if (gelf_getshdr(scn, &shdr) != &shdr) - return 0; - -#if 0 - section_name = elf_strptr(e, shstrndx, shdr.sh_name); - - printf ("name %s\n", section_name); - - if (shdr.sh_type == SHT_PROGBITS) - { - printf ("\ttype %lx\n", shdr.sh_type); - printf ("\tflags %lx\n", shdr.sh_flags); - printf ("\taddr %lx\n", shdr.sh_addr); - printf ("\toffset %lx\n", shdr.sh_offset); - printf ("\tsize %lx\n", shdr.sh_size); - printf ("\tlink %lx\n", shdr.sh_link); - printf ("\tinfo %lx\n", shdr.sh_info); - printf ("\taddralign %lx\n", shdr.sh_addralign); - printf ("\tentsize %lx\n", shdr.sh_entsize); - } -#endif - - if (shdr.sh_type == SHT_SYMTAB) { - symbol_data = elf_getdata(scn, NULL); - symbol_count = shdr.sh_size / shdr.sh_entsize; - break; - } - } - - if (!symbol_data) - return 0; - - for (i = 0; i < symbol_count; i++) { - gelf_getsym(symbol_data, i, &sym); - - symbol_name = elf_strptr(e, shdr.sh_link, sym.st_name); - - for (s = 0; s < NUM_SYMBOLS; s++) - if (!strcmp (ao_symbols[s].name, symbol_name)) { - int t; - ao_symbols[s].addr = sym.st_value; - if (ao_symbols[s].required) - ++required; - } - } - - return required >= NUM_REQUIRED_SYMBOLS; -} - -struct load { - uint32_t addr; - uint32_t len; - uint8_t buf[0]; -}; - -uint32_t round4(uint32_t a) { - return (a + 3) & ~3; -} - -struct load * -new_load (uint32_t addr, uint32_t len) -{ - struct load *new; - - len = round4(len); - new = calloc (1, sizeof (struct load) + len); - if (!new) - abort(); - - new->addr = addr; - new->len = len; - return new; -} - -void -load_paste(struct load *into, struct load *from) -{ - if (from->addr < into->addr || into->addr + into->len < from->addr + from->len) - abort(); - - memcpy(into->buf + from->addr - into->addr, from->buf, from->len); -} - -/* - * Make a new load structure large enough to hold the old one and - * the new data - */ -struct load * -expand_load(struct load *from, uint32_t addr, uint32_t len) -{ - struct load *new; - - if (from) { - uint32_t from_last = from->addr + from->len; - uint32_t last = addr + len; - - if (addr > from->addr) - addr = from->addr; - if (last < from_last) - last = from_last; - - len = last - addr; - - if (addr == from->addr && len == from->len) - return from; - } - new = new_load(addr, len); - if (from) { - load_paste(new, from); - free (from); - } - return new; -} - -/* - * Create a new load structure with data from the existing one - * and the new data - */ -struct load * -load_write(struct load *from, uint32_t addr, uint32_t len, void *data) -{ - struct load *new; - - new = expand_load(from, addr, len); - memcpy(new->buf + addr - new->addr, data, len); - return new; -} - -/* - * Construct a large in-memory block for all - * of the loaded sections of the program - */ -static struct load * -get_load(Elf *e) -{ - Elf_Scn *scn; - size_t shstrndx; - GElf_Shdr shdr; - Elf_Data *data; - uint8_t *buf; - char *got_name; - size_t nphdr; - size_t p; - GElf_Phdr phdr; - GElf_Addr p_paddr; - GElf_Off p_offset; - GElf_Addr sh_paddr; - struct load *load = NULL; - char *section_name; - size_t nshdr; - size_t s; - - if (elf_getshdrstrndx(e, &shstrndx) < 0) - return 0; - - if (elf_getphdrnum(e, &nphdr) < 0) - return 0; - - if (elf_getshdrnum(e, &nshdr) < 0) - return 0; - - /* - * As far as I can tell, all of the phdr sections should - * be flashed to memory - */ - for (p = 0; p < nphdr; p++) { - - /* Find this phdr */ - gelf_getphdr(e, p, &phdr); - - if (phdr.p_type != PT_LOAD) - continue; - - p_offset = phdr.p_offset; - /* Get the associated file section */ - -#if 0 - printf ("offset %08x vaddr %08x paddr %08x filesz %08x memsz %08x\n", - (uint32_t) phdr.p_offset, - (uint32_t) phdr.p_vaddr, - (uint32_t) phdr.p_paddr, - (uint32_t) phdr.p_filesz, - (uint32_t) phdr.p_memsz); -#endif - - for (s = 0; s < nshdr; s++) { - scn = elf_getscn(e, s); - - if (!scn) { - printf ("getscn failed\n"); - abort(); - } - if (gelf_getshdr(scn, &shdr) != &shdr) { - printf ("gelf_getshdr failed\n"); - abort(); - } - - section_name = elf_strptr(e, shstrndx, shdr.sh_name); - - if (phdr.p_offset <= shdr.sh_offset && shdr.sh_offset < phdr.p_offset + phdr.p_filesz) { - - if (shdr.sh_size == 0) - continue; - - sh_paddr = phdr.p_paddr + shdr.sh_offset - phdr.p_offset; - - printf ("\tsize %08x rom %08x exec %08x %s\n", - (uint32_t) shdr.sh_size, - (uint32_t) sh_paddr, - (uint32_t) shdr.sh_addr, - section_name); - - data = elf_getdata(scn, NULL); - - /* Write the section data into the memory block */ - load = load_write(load, sh_paddr, shdr.sh_size, data->d_buf); - } - } - } - return load; -} +int ao_num_symbols = NUM_SYMBOLS; +int ao_num_required_symbols = NUM_REQUIRED_SYMBOLS; /* * Edit the to-be-written memory block */ static int -rewrite(struct load *load, unsigned addr, uint8_t *data, int len) +rewrite(struct hex_image *load, unsigned address, uint8_t *data, int length) { int i; - if (addr < load->addr || load->addr + load->len < addr + len) + if (address < load->address || load->address + load->length < address + length) return 0; - printf("rewrite %04x:", addr); - for (i = 0; i < len; i++) - printf (" %02x", load->buf[addr - load->addr + i]); + printf("rewrite %04x:", address); + for (i = 0; i < length; i++) + printf (" %02x", load->data[address - load->address + i]); printf(" ->"); - for (i = 0; i < len; i++) + for (i = 0; i < length; i++) printf (" %02x", data[i]); printf("\n"); - memcpy(load->buf + addr - load->addr, data, len); + memcpy(load->data + address - load->address, data, length); } /* - * Open the specified ELF file and - * check for the symbols we need + * Read a 16-bit value from the USB target */ -Elf * -ao_open_elf(char *name) +static uint16_t +get_uint16_cc(struct cc_usb *cc, uint32_t addr) { - int fd; - Elf *e; - Elf_Scn *scn; - Elf_Data *symbol_data = NULL; - GElf_Shdr shdr; - GElf_Sym sym; - size_t n, shstrndx, sz; - int i, symbol_count, s; - int required = 0; - - if (elf_version(EV_CURRENT) == EV_NONE) - return NULL; + struct hex_image *hex = ao_self_read(cc, addr, 2); + uint16_t v; + uint8_t *data; - fd = open(name, O_RDONLY, 0); - - if (fd < 0) - return NULL; - - e = elf_begin(fd, ELF_C_READ, NULL); - - if (!e) - return NULL; - - if (elf_kind(e) != ELF_K_ELF) - return NULL; - - if (elf_getshdrstrndx(e, &shstrndx) != 0) - return NULL; + if (!hex) + return 0; + data = hex->data + addr - hex->address; + v = data[0] | (data[1] << 8); + free(hex); + return v; +} - if (!find_symbols(e)) { - fprintf (stderr, "Cannot find required symbols\n"); - return NULL; - } +static uint32_t +get_uint32_cc(struct cc_usb *cc, uint32_t addr) +{ + struct hex_image *hex = ao_self_read(cc, addr, 4); + uint32_t v; + uint8_t *data; - return e; + if (!hex) + return 0; + data = hex->data + addr - hex->address; + v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + free(hex); + return v; } /* - * Read a 32-bit value from the target device with arbitrary + * Read a 16-bit value from the target device with arbitrary * alignment */ -static uint32_t -get_uint32(stlink_t *sl, uint32_t addr) +static uint16_t +get_uint16_sl(stlink_t *sl, uint32_t addr) { const uint8_t *data = sl->q_buf; uint32_t actual_addr; int off; - uint32_t result; + uint16_t result; sl->q_len = 0; - printf ("read 0x%x\n", addr); actual_addr = addr & ~3; @@ -394,25 +136,37 @@ get_uint32(stlink_t *sl, uint32_t addr) abort(); off = addr & 3; - result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24); - printf ("read 0x%08x = 0x%08x\n", addr, result); + result = data[off] | (data[off + 1] << 8); + return result; +} + +static uint16_t +get_uint16(stlink_t *sl, struct cc_usb *cc, uint32_t addr) +{ + uint16_t result; + if (cc) + result = get_uint16_cc(cc, addr); + else + result = get_uint16_sl(sl, addr); + printf ("read 0x%08x = 0x%04x\n", addr, result); return result; } /* - * Read a 16-bit value from the target device with arbitrary + * Read a 32-bit value from the target device with arbitrary * alignment */ -static uint16_t -get_uint16(stlink_t *sl, uint32_t addr) +static uint32_t +get_uint32_sl(stlink_t *sl, uint32_t addr) { const uint8_t *data = sl->q_buf; uint32_t actual_addr; int off; - uint16_t result; + uint32_t result; sl->q_len = 0; + printf ("read 0x%x\n", addr); actual_addr = addr & ~3; @@ -422,8 +176,24 @@ get_uint16(stlink_t *sl, uint32_t addr) abort(); off = addr & 3; - result = data[off] | (data[off + 1] << 8); - printf ("read 0x%08x = 0x%04x\n", addr, result); + result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24); + return result; +} + +/* + * Read a 32-bit value from the target device with arbitrary + * alignment + */ +static uint32_t +get_uint32(stlink_t *sl, struct cc_usb *cc, uint32_t addr) +{ + uint32_t result; + + if (cc) + result = get_uint32_cc(cc, addr); + else + result = get_uint32_sl(sl, addr); + printf ("read 0x%08x = 0x%08x\n", addr, result); return result; } @@ -436,10 +206,10 @@ get_uint16(stlink_t *sl, uint32_t addr) * places this at 0x100 from the start of the rom section */ static int -check_flashed(stlink_t *sl) +check_flashed(stlink_t *sl, struct cc_usb *cc) { - uint16_t romconfig_version = get_uint16(sl, AO_ROMCONFIG_VERSION); - uint16_t romconfig_check = get_uint16(sl, AO_ROMCONFIG_CHECK); + uint16_t romconfig_version = get_uint16(sl, cc, AO_ROMCONFIG_VERSION); + uint16_t romconfig_check = get_uint16(sl, cc, AO_ROMCONFIG_CHECK); if (romconfig_version != (uint16_t) ~romconfig_check) { fprintf (stderr, "Device has not been flashed before\n"); @@ -449,6 +219,8 @@ check_flashed(stlink_t *sl) } static const struct option options[] = { + { .name = "stlink", .has_arg = 0, .val = 'S' }, + { .name = "tty", .has_arg = 1, .val = 'T' }, { .name = "device", .has_arg = 1, .val = 'D' }, { .name = "cal", .has_arg = 1, .val = 'c' }, { .name = "serial", .has_arg = 1, .val = 's' }, @@ -457,13 +229,17 @@ static const struct option options[] = { static void usage(char *program) { - fprintf(stderr, "usage: %s [--cal=] [--serial=] file.elf\n", program); + fprintf(stderr, "usage: %s [--stlink] [--device=] [-tty=] [--cal=] [--serial=] file.{elf,ihx}\n", program); exit(1); } void -done(stlink_t *sl, int code) +done(stlink_t *sl, struct cc_usb *cc, int code) { + if (cc) { +/* cc_usb_printf(cc, "a\n"); */ + cc_usb_close(cc); + } if (sl) { stlink_reset(sl); stlink_run(sl); @@ -473,6 +249,17 @@ done(stlink_t *sl, int code) exit (code); } +static int +ends_with(char *whole, char *suffix) +{ + int whole_len = strlen(whole); + int suffix_len = strlen(suffix); + + if (suffix_len > whole_len) + return 0; + return strcmp(whole + whole_len - suffix_len, suffix) == 0; +} + int main (int argc, char **argv) { @@ -491,13 +278,20 @@ main (int argc, char **argv) char cal_int[4]; char *cal_end; int c; - stlink_t *sl; + stlink_t *sl = NULL; int was_flashed = 0; - struct load *load; + struct hex_image *load; int tries; + struct cc_usb *cc = NULL; + int use_stlink = 0; + char *tty = NULL; + int success; - while ((c = getopt_long(argc, argv, "D:c:s:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "T:D:c:s:S", options, NULL)) != -1) { switch (c) { + case 'T': + tty = optarg; + break; case 'D': device = optarg; break; @@ -511,6 +305,9 @@ main (int argc, char **argv) if (serial_end == optarg || *serial_end != '\0') usage(argv[0]); break; + case 'S': + use_stlink = 1; + break; default: usage(argv[0]); break; @@ -521,91 +318,145 @@ main (int argc, char **argv) if (filename == NULL) usage(argv[0]); - /* - * Open the source file and load the symbols and - * flash data - */ - - e = ao_open_elf(filename); - if (!e) { - fprintf(stderr, "Cannot open file \"%s\"\n", filename); - exit(1); - } - - if (!find_symbols(e)) { - fprintf(stderr, "Cannot find symbols in \"%s\"\n", filename); - exit(1); - } + if (ends_with (filename, ".elf")) { + load = ao_load_elf(filename); + } else if (ends_with (filename, ".ihx")) { + int i; + load = ccdbg_hex_load(filename); + for (i = 0; i < ao_num_symbols; i++) + ao_symbols[i].addr = ao_symbols[i].default_addr; + } else + usage(argv[0]); - if (!(load = get_load(e))) { - fprintf(stderr, "Cannot find program data in \"%s\"\n", filename); - exit(1); - } - - /* Connect to the programming dongle - */ + if (use_stlink) { + /* Connect to the programming dongle + */ - for (tries = 0; tries < 3; tries++) { - if (device) { - sl = stlink_v1_open(50); - } else { - sl = stlink_open_usb(50); + for (tries = 0; tries < 3; tries++) { + if (device) { + sl = stlink_v1_open(50); + } else { + sl = stlink_open_usb(50); + } + if (!sl) { + fprintf (stderr, "No STLink devices present\n"); + done (sl, NULL, 1); + } + + if (sl->chip_id != 0) + break; + stlink_reset(sl); + stlink_close(sl); + sl = NULL; } if (!sl) { - fprintf (stderr, "No STLink devices present\n"); - done (sl, 1); + fprintf (stderr, "Debugger connection failed\n"); + exit(1); } - if (sl->chip_id != 0) - break; - stlink_reset(sl); - stlink_close(sl); - } - if (sl->chip_id == 0) { - fprintf (stderr, "Debugger connection failed\n"); - done(sl, 1); - } + /* Verify that the loaded image fits entirely within device flash + */ + if (load->address < sl->flash_base || + sl->flash_base + sl->flash_size < load->address + load->length) { + fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename, + load->address, load->address + load->length); + done(sl, NULL, 1); + } - /* Verify that the loaded image fits entirely within device flash - */ - if (load->addr < sl->flash_base || - sl->flash_base + sl->flash_size < load->addr + load->len) { - fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename, - load->addr, load->addr + load->len); - done(sl, 1); + /* Enter debugging mode + */ + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) + stlink_exit_dfu_mode(sl); + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) + stlink_enter_swd_mode(sl); + } else { + int is_loader; + int tries; + + for (tries = 0; tries < 3; tries++) { + char *this_tty = tty; + if (!this_tty) + this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash"); + if (!this_tty) + this_tty = cc_usbdevs_find_by_arg(device, "MegaMetrum"); + if (!this_tty) + this_tty = getenv("ALTOS_TTY"); + if (!this_tty) + this_tty="/dev/ttyACM0"; + + cc = cc_usb_open(this_tty); + + if (!cc) + exit(1); + cc_usb_printf(cc, "v\n"); + is_loader = 0; + for (;;) { + char line[256]; + cc_usb_getline(cc, line, sizeof(line)); + if (!strncmp(line, "altos-loader", 12)) + is_loader = 1; + if (!strncmp(line, "software-version", 16)) + break; + } + if (is_loader) + break; + printf ("rebooting to loader\n"); + cc_usb_printf(cc, "L\n"); + cc_usb_close(cc); + sleep(1); + cc = NULL; + } + if (!is_loader) { + fprintf(stderr, "Cannot switch to boot loader\n"); + exit(1); + } + { + uint8_t check[256]; + int i = 0; + + ao_self_block_read(cc, 0x08002000, check); + for (;;) { + uint8_t block[256]; + putchar ('.'); + if (++i == 40) { + putchar('\n'); + i = 0; + } + fflush(stdout); + ao_self_block_write(cc, 0x08002000, block); + ao_self_block_read(cc, 0x08002000, block); + if (memcmp(block, check, 256/me ta) != 0) { + fprintf (stderr, "read differed\n"); + exit(1); + } + } + } } - /* Enter debugging mode - */ - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) - stlink_exit_dfu_mode(sl); - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) - stlink_enter_swd_mode(sl); - /* Go fetch existing config values * if available */ - was_flashed = check_flashed(sl); + was_flashed = check_flashed(sl, cc); if (!serial) { if (!was_flashed) { fprintf (stderr, "Must provide serial number\n"); - done(sl, 1); + done(sl, cc, 1); } - serial = get_uint16(sl, AO_SERIAL_NUMBER); + serial = get_uint16(sl, cc, AO_SERIAL_NUMBER); if (!serial || serial == 0xffff) { fprintf (stderr, "Invalid existing serial %d\n", serial); - done(sl, 1); + done(sl, cc, 1); } } if (!cal && AO_RADIO_CAL && was_flashed) { - cal = get_uint32(sl, AO_RADIO_CAL); + cal = get_uint32(sl, cc, AO_RADIO_CAL); if (!cal || cal == 0xffffffff) { fprintf (stderr, "Invalid existing rf cal %d\n", cal); - done(sl, 1); + done(sl, cc, 1); } } @@ -618,32 +469,31 @@ main (int argc, char **argv) if (!rewrite(load, AO_SERIAL_NUMBER, serial_int, sizeof (serial_int))) { fprintf(stderr, "Cannot rewrite serial integer at %08x\n", AO_SERIAL_NUMBER); - done(sl, 1); + done(sl, cc, 1); } if (AO_USB_DESCRIPTORS) { - unsigned usb_descriptors; - usb_descriptors = AO_USB_DESCRIPTORS - load->addr; + uint32_t usb_descriptors = AO_USB_DESCRIPTORS - load->address; string_num = 0; - while (load->buf[usb_descriptors] != 0 && usb_descriptors < load->len) { - if (load->buf[usb_descriptors+1] == AO_USB_DESC_STRING) { + while (load->data[usb_descriptors] != 0 && usb_descriptors < load->length) { + if (load->data[usb_descriptors+1] == AO_USB_DESC_STRING) { ++string_num; if (string_num == 4) break; } - usb_descriptors += load->buf[usb_descriptors]; + usb_descriptors += load->data[usb_descriptors]; } - if (usb_descriptors >= load->len || load->buf[usb_descriptors] == 0 ) { + if (usb_descriptors >= load->length || load->data[usb_descriptors] == 0 ) { fprintf(stderr, "Cannot rewrite serial string at %08x\n", AO_USB_DESCRIPTORS); - done(sl, 1); + done(sl, cc, 1); } - serial_ucs2_len = load->buf[usb_descriptors] - 2; + serial_ucs2_len = load->data[usb_descriptors] - 2; serial_ucs2 = malloc(serial_ucs2_len); if (!serial_ucs2) { fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len); - done(sl, 1); + done(sl, cc, 1); } s = serial; for (i = serial_ucs2_len / 2; i; i--) { @@ -651,9 +501,9 @@ main (int argc, char **argv) serial_ucs2[i * 2 - 2] = (s % 10) + '0'; s /= 10; } - if (!rewrite(load, usb_descriptors + 2 + load->addr, serial_ucs2, serial_ucs2_len)) { + if (!rewrite(load, usb_descriptors + 2 + load->address, serial_ucs2, serial_ucs2_len)) { fprintf (stderr, "Cannot rewrite USB descriptor at %08x\n", AO_USB_DESCRIPTORS); - done(sl, 1); + done(sl, cc, 1); } } @@ -671,10 +521,15 @@ main (int argc, char **argv) /* And flash the resulting image to the device */ - if (stlink_write_flash(sl, load->addr, load->buf, load->len) < 0) { + if (cc) + success = ao_self_write(cc, load); + else + success = (stlink_write_flash(sl, load->address, load->data, load->length) >= 0); + + if (!success) { fprintf (stderr, "\"%s\": Write failed\n", filename); - done(sl, 1); + done(sl, cc, 1); } - done(sl, 0); + done(sl, cc, 0); }