summaryrefslogtreecommitdiff
path: root/chip/g/usb_upgrade.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/usb_upgrade.c')
-rw-r--r--chip/g/usb_upgrade.c83
1 files changed, 44 insertions, 39 deletions
diff --git a/chip/g/usb_upgrade.c b/chip/g/usb_upgrade.c
index f7d548ecc2..5c213668d1 100644
--- a/chip/g/usb_upgrade.c
+++ b/chip/g/usb_upgrade.c
@@ -26,12 +26,13 @@
* programming blocks from the USB chunks, and invokes the programmer passing
* it the full block.
*
- * The programmer reports results by putting the return value of one or four
- * bytes into the same buffer where the block was passed in. This wrapper
- * retrieves the programmer's return value, normalizes it to 4 bytes and sends
- * it back to the host.
+ * The programmer reports results by putting the return value into the same
+ * buffer where the block was passed in. This wrapper retrieves the
+ * programmer's return value, and sends it back to the host. The return value
+ * is usually one byte in size, the only exception is the connection
+ * establishment phase where the return value is 16 bytes in size.
*
- * In the end of the successful image transfer and programming, the host send
+ * In the end of the successful image transfer and programming, the host sends
* the reset command, and the device reboots itself.
*/
@@ -117,7 +118,7 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
{
struct update_frame_header upfr;
size_t resp_size;
- uint32_t resp_value;
+ uint8_t resp_value;
uint64_t delta_time;
/* How much time since the previous USB callback? */
@@ -139,44 +140,43 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
}
if (rx_state_ == rx_idle) {
- struct first_response_pdu *startup_resp;
-
- if (!valid_transfer_start(consumer, count, &upfr))
+ /*
+ * The payload must be an update initiating PDU.
+ *
+ * The size of the response returned in the same buffer will
+ * exceed the received frame size; Let's make sure there is
+ * enough room for the response in the buffer.
+ */
+ union {
+ struct update_frame_header upfr;
+ struct {
+ uint32_t unused;
+ struct first_response_pdu startup_resp;
+ };
+ } u;
+
+ if (!valid_transfer_start(consumer, count, &u.upfr)) {
+ /*
+ * Someting is wrong, this payload is not a valid
+ * update start PDU. Let'w indicate this by returning
+ * a single byte error code.
+ */
+ resp_value = UPGRADE_GEN_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
+ }
CPRINTS("FW update: starting...");
-
- fw_upgrade_command_handler(&upfr.cmd, count -
+ fw_upgrade_command_handler(&u.upfr.cmd, count -
offsetof(struct update_frame_header,
cmd),
&resp_size);
- /*
- * The handler reuses receive buffer to return the result
- * value.
- */
- startup_resp = (struct first_response_pdu *)&upfr.cmd;
- if (resp_size == 4) {
- /*
- * The handler is happy, returned a 4 byte base
- * offset, it is in startup_resp->return_value now in
- * the proper byte order.
- */
- rx_state_ = rx_outside_block;
- } else {
- /*
- * This must be a single byte error code, convert it
- * into a 4 byte network order representation.
- */
- startup_resp->return_value = htobe32
- (*((uint8_t *)&startup_resp->return_value));
- }
- startup_resp->protocol_version =
- htobe32(UPGRADE_PROTOCOL_VERSION);
+ if (!u.startup_resp.return_value)
+ rx_state_ = rx_outside_block; /* We're in business. */
/* Let the host know what upgrader had to say. */
- QUEUE_ADD_UNITS(&upgrade_to_usb, startup_resp,
- sizeof(*startup_resp));
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &u.startup_resp, resp_size);
return;
}
@@ -205,9 +205,10 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
command = be32toh(command);
if (command == UPGRADE_DONE) {
CPRINTS("FW update: done");
+
resp_value = 0;
- QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value,
- sizeof(resp_value));
+ QUEUE_ADD_UNITS(&upgrade_to_usb,
+ &resp_value, 1);
rx_state_ = rx_awaiting_reset;
return;
}
@@ -220,22 +221,26 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
* field of all zeros.
*/
if (valid_transfer_start(consumer, count, &upfr) ||
- (count != sizeof(upfr)))
+ (count != sizeof(upfr))) {
/*
* Instead of a block start message we received either
* a transfer start message or a chunk. We must have
* gotten out of sync with the host.
*/
+ resp_value = UPGRADE_GEN_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
+ }
/* Let's allocate a large enough buffer. */
block_size = be32toh(upfr.block_size) -
offsetof(struct update_frame_header, cmd);
if (shared_mem_acquire(block_size, (char **)&block_buffer)
!= EC_SUCCESS) {
- /* TODO:(vbendeb) report out of memory here. */
CPRINTS("FW update: error: failed to alloc %d bytes.",
block_size);
+ resp_value = UPGRADE_MALLOC_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
}