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 <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/341583
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
This commit is contained in:
Vadim Bendebury
2016-04-30 15:48:48 -07:00
committed by chrome-bot
parent acaec88101
commit 3b1c3ba36f

View File

@@ -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;
for (max_retries = 10; max_retries; max_retries--)
if (!transfer_block(uep, &updu, data_ptr, payload_size))
break;
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. */
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;