From 42662f2214aa43982d44edd4dc12721d6f77e018 Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Fri, 29 Apr 2016 16:11:28 -0700 Subject: g: recover from usb_upgrade interruptions The usb upgrade protocol is very fragile, any error while transferring data causes the state machine on the target to lock up, and the only way to resume the upgrade is to power cycle the device. With this patch USB callbacks which happen more than 5 seconds since the previous callback would be considered a start of new transfer, thus allowing to attempt a new upgrade without the power cycle. BRANCH=none BUG=chrome-os-partner:52856 TEST=the following script allows to upgrade successfully: $ while not ./extra/usb_updater/usb_updater build/cr50/ec.bin; do sleep 6; done Change-Id: Ibe1078cf62073ce89a31416522b0d6917bc923b9 Signed-off-by: Vadim Bendebury Reviewed-on: https://chromium-review.googlesource.com/341572 Reviewed-by: Marius Schilder Reviewed-by: Mary Ruthven --- chip/g/usb_upgrade.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/chip/g/usb_upgrade.c b/chip/g/usb_upgrade.c index 7e86a0c035..bca76d401e 100644 --- a/chip/g/usb_upgrade.c +++ b/chip/g/usb_upgrade.c @@ -91,12 +91,37 @@ static uint8_t *block_buffer; static uint32_t block_size; static uint32_t block_index; +/* + * When was last time a USB callback was called, in microseconds, free running + * timer. + */ +static uint64_t prev_activity_timestamp; + /* Called to deal with data from the host */ static void upgrade_out_handler(struct consumer const *consumer, size_t count) { struct update_pdu_header updu; size_t resp_size; uint32_t resp_value; + uint64_t delta_time; + + /* How much time since the previous USB callback? */ + delta_time = get_time().val - prev_activity_timestamp; + prev_activity_timestamp += delta_time; + + /* If timeout exceeds 5 seconds - let's start over. */ + if ((delta_time > 5000000) && (rx_state_ != rx_idle)) { + if (block_buffer) { + /* + * Previous transfer could have been aborted mid + * block. + */ + shared_mem_release(block_buffer); + block_buffer = NULL; + } + rx_state_ = rx_idle; + CPRINTS("FW update: recovering after timeout\n"); + } if (rx_state_ == rx_idle) { /* This better be the first block, of zero size. */ @@ -210,10 +235,11 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count) */ fw_upgrade_command_handler(block_buffer, block_index, &resp_size); - shared_mem_release(block_buffer); resp_value = block_buffer[0]; QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, sizeof(resp_value)); rx_state_ = rx_outside_block; + shared_mem_release(block_buffer); + block_buffer = NULL; } static void upgrade_flush(struct consumer const *consumer) -- cgit v1.2.1