From 3b1c3ba36febf50fa3be0e9b68e40be8e413e04d Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Sat, 30 Apr 2016 15:48:48 -0700 Subject: usb_updater: retransmit upgrade blocks if target does not reply When the target is running upgrade protocol version 2, it is capable of processing multiple transfer attempts of the same block. This patch allows timeouts when expecting the target acknowledges. If the acknowledge does not arrive in time, the host reports the timeout on the console and retransmits the same block to the target. BRANCH=none BUG=chrome-os-partner:52856 TEST=it is now possible to successfully upgrade cr50 on Kevin in one go: $ ./extra/usb_updater/usb_updater build/cr50/ec.bin read 0x80000 bytes from build/cr50/ec.bin open_device 18d1:5014 found interface 4 endpoint 5, chunk_len 64 READY ------- erase Target running protocol version 2 Updating at offset 0x00004000 sending 0x29620/0x3c000 bytes Timeout! Timeout! Timeout! Timeout! ------- update complete reboot bye Change-Id: Ib1c3179cb3a02c0ae6e5e949476553ae28b6a295 Signed-off-by: Vadim Bendebury Reviewed-on: https://chromium-review.googlesource.com/341583 Reviewed-by: Mary Ruthven --- extra/usb_updater/usb_updater.c | 75 +++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/extra/usb_updater/usb_updater.c b/extra/usb_updater/usb_updater.c index 808c042e87..1b071b46e6 100644 --- a/extra/usb_updater/usb_updater.c +++ b/extra/usb_updater/usb_updater.c @@ -39,6 +39,7 @@ struct usb_endpoint { }; /* Globals */ +static uint32_t protocol_version; static char *progname; static char *short_opts = ":d:h"; static const struct option long_opts[] = { @@ -323,6 +324,48 @@ struct startup_resp { uint32_t version; }; +static int transfer_block(struct usb_endpoint *uep, struct update_pdu *updu, + uint8_t *transfer_data_ptr, size_t payload_size) +{ + size_t transfer_size; + uint32_t reply; + int actual; + int r; + + /* First send the header. */ + xfer(uep, updu, sizeof(*updu), NULL, 0); + + /* Now send the block, chunk by chunk. */ + for (transfer_size = 0; transfer_size < payload_size;) { + int chunk_size; + + chunk_size = MIN(uep->chunk_len, payload_size - transfer_size); + xfer(uep, transfer_data_ptr, chunk_size, NULL, 0); + transfer_data_ptr += chunk_size; + transfer_size += chunk_size; + } + + /* Now get the reply. */ + r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80, + (void *) &reply, sizeof(reply), + &actual, 1000); + if (r) { + if ((r == -7) && (protocol_version >= 2)) { + fprintf(stderr, "Timeout!\n"); + return r; + } + USB_ERROR("libusb_bulk_transfer", r); + shut_down(uep); + } + + if (reply) { + fprintf(stderr, "error: status %#08x\n", be32toh(reply)); + exit(1); + } + + return 0; +} + static void transfer_and_reboot(struct usb_endpoint *uep, uint8_t *data, uint32_t data_len) { @@ -333,7 +376,6 @@ static void transfer_and_reboot(struct usb_endpoint *uep, struct update_pdu updu; struct startup_resp first_resp; int rxed_size; - uint32_t protocol_version; /* Send start/erase request */ printf("erase\n"); @@ -369,14 +411,11 @@ static void transfer_and_reboot(struct usb_endpoint *uep, data_len--; printf("sending 0x%x/0x%x bytes\n", data_len, CONFIG_RW_SIZE); - while (data_len) { size_t payload_size; SHA_CTX ctx; uint8_t digest[SHA_DIGEST_LENGTH]; - - uint8_t *transfer_data_ptr; - size_t transfer_size; + int max_retries; /* prepare the header to prepend to the block. */ payload_size = MIN(data_len, SIGNED_TRANSFER_SIZE); @@ -396,29 +435,15 @@ static void transfer_and_reboot(struct usb_endpoint *uep, memcpy(&updu.cmd.block_digest, digest, sizeof(updu.cmd.block_digest)); - /* Now send the header. */ - xfer(uep, &updu, sizeof(updu), NULL, 0); - /* Now send the block, chunk by chunk. */ - transfer_data_ptr = data_ptr; - for (transfer_size = 0; transfer_size < payload_size;) { - int chunk_size; - - chunk_size = MIN(uep->chunk_len, - payload_size - transfer_size); - xfer(uep, transfer_data_ptr, chunk_size, - NULL, 0); - transfer_data_ptr += chunk_size; - transfer_size += chunk_size; - } + for (max_retries = 10; max_retries; max_retries--) + if (!transfer_block(uep, &updu, data_ptr, payload_size)) + break; - /* Now get the reply. */ - xfer(uep, NULL, 0, &reply, sizeof(reply)); - if (reply) { - fprintf(stderr, "error: status %#08x remaining %#08x\n", - be32toh(reply), data_len); + if (!max_retries) { + fprintf(stderr, "Failed to trasfer block, %d to go\n", + data_len); exit(1); } - data_len -= payload_size; data_ptr += payload_size; next_offset += payload_size; -- cgit v1.2.1