From c97ff4308ace592d1a9ced27b4598d09fc969e38 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 14 Dec 2014 23:09:00 +0100 Subject: Fix FW writing The magic packet should not be treated specially. Added reverse-engineered docs --- fw-update.c | 106 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 32 deletions(-) (limited to 'fw-update.c') diff --git a/fw-update.c b/fw-update.c index e3e67b4..70f5e10 100644 --- a/fw-update.c +++ b/fw-update.c @@ -99,7 +99,6 @@ static bool match_notif(HidppMessage *msg, void *userdata) { bool fw_update(int fd, uint8_t device_index, uint8_t *fw, size_t fw_len) { int r; - int i; bool b; uint16_t version; FeatureInfo feat_dfu; @@ -202,46 +201,89 @@ bool fw_update(int fd, uint8_t device_index, uint8_t *fw, size_t fw_len) { #define DFU_WRITE(_msg) \ do { \ r = hidpp10_request(fd, &_msg, NULL, NULL); \ - RETURN_IF_FAIL(r == 0, "Write E2 failed"); \ + RETURN_IF_FAIL(r == 0, "Write E2 failed, r=%#04x", r); \ } while (0) - // TODO: [ix 82 E2] send magic packet (+verify) HidppMessage dfu_req; - // 0X11 0X2 0X82 0Xe2 0X2 0X9 0X5 0X41 0X1 0X0 0X38 0X0 0X0 0X0 0X0 0X0 - // 11 02 82 e2 HID++ long, device index, SubID (Set_Reg_Long), Addr - // 02 DFU upgrade signature packet command? - // 09 05 ??? - // 41 01 Wireless PID (MSB LSB) - // 00 38 New firmware version (build number) - // 00 00 00 00 00 (padding) - wpid = WPID_T650; - for (i = 3; i >= 0; i--) { - RETURN_IF_FAIL(i > 1, "Too many DFU magic attempts"); - dfu_req = DFU_INIT(0x02, 0x09, 0x05, - wpid >> 8, (uint8_t) wpid, - 0x00, 0x38); + /* Format (req): E2 [action] [firmware blob] + * Possible action for requests (from sw to dvc; it seems to be dependent on + * the responses received from the device): + * 02 indicates the begin of firmware (always sent by sw if the DFU + * Upgrade Signature bit (rsp.bit1) is set by dvc). ("even toggle + * bit" as it is the first fw block at index 0) + * 00/01 for even/odd fw block indices. + * 04 DFU failed, execute recovery procedure. (params are all 00) + * 08 DFU end (verify?). (params are all 00) + * If rsp.bit2 is set, then respond with the same result code and firmware + * blob in place of the action. Probably for this reason, the device sends + * result 12 instead of just 02 which would have an ambiguous meaning. + * + * Format (rsp): E2 [result] + * Sw expects the result to toggle, so if action is 01, then result is + * 00 and if action is 00, then result is 01. Bit layout for rsp: + * 7-4 f0 ignored (but hw must copy this) + * 3 08 set to signal a critical error (sw must abort with action 04) + * 2 04 Unidentified packet failure (sw must retry same fw data) + * 1 02 DFU Upgrade signature bit (sw must start with begin of fw) + * 0 01 toggle bit (dvc flips this bit for every response. If it + * doesn't, then Sw should warn and expect the next bit to be the + * complement of this one) + */ + unsigned retries = 0; + // [ix 82 E2] send magic packet (+verify) + uint8_t action = 2; + uint8_t dvc_toggle = -1; + size_t fw_pos = 0; + printf("Starting DFU (%zi blocks)\n", fw_len / 15); + while (fw_pos < fw_len) { + // [ix 82 E2] send firmware (+check toggle) + dfu_req = DFU_INIT(action); + memcpy(&dfu_req.params_l[1], fw + fw_pos, 15); DFU_WRITE(dfu_req); + /* verify response */ int code = dfu_req.params[0]; - if (code == 0x12) { /* Retry? */ - } else if (code == 0x01) { /* toggled? */ - /* assume that 0x12 wants us to retry, and 1 means success. */ + PRWARN_IF_FAIL(dvc_toggle != (uint8_t) -1 && (code & 1) == dvc_toggle, + "Device did not toggle properly!"); + dvc_toggle = (code & 1) ^ 1; /* toggle flag: inverse of previous resp */ + + if (code & 8) { /* Critical Error, abort! */ + warnx("The Device does not accept this firmware code (Wrong Signature)"); break; - } else { - RETURN_IF_FAIL(code == 0x12 || code == 0x01, - "Unexpected DFU resp %#04x", code); + } else if (code & 2) { /* DFU Upgrade signature, reset! */ + if (retries++ < 12) { + warnx("DFU error at pos %#zx, retry %i\n", fw_pos, retries); + dvc_toggle = -1; + fw_pos = 0; + action = 2; + } else { + warnx("DFU error, not retrying anymore!"); + break; + } + } else if (code & 4) { /* Unidentified packet failure, resend packet */ + action = code; /* send this "tag"/result as "tag"/action */ + /* TODO: maybe check for infinite loop? The official fw updater + * doesn't do this either though, stopped counting after 61 + * retries... */ + } else { /* fw packet ack. */ + action = (fw_pos / 15) & 1; /* enable toggle bit for odd blocks */ + fw_pos += 15; /* go to next fw block */ } } - // TODO: [ix 82 E2] send firmware (+check toggle) - (void)fw, (void)fw_len; - - // TODO: [ix 82 E2] send verify (+check toggle) - dfu_req = DFU_INIT(0x04); - DFU_WRITE(dfu_req); - RETURN_IF_FAIL(dfu_req.params[0] == 0x01, "DFU verification failure?"); + if (fw_pos == fw_len) { /* DFU success */ + // [ix 82 E2] send verify (+check toggle) + dfu_req = DFU_INIT(8); /* verify, write, or whatever */ + DFU_WRITE(dfu_req); + PRWARN_IF_FAIL((dfu_req.params[0] & ~1) == 0x04, + "DFU verification failure?"); + } else { /* DFU failed */ + warnx("DFU failed, executing recovery procedure"); + dfu_req = DFU_INIT(4); + DFU_WRITE(dfu_req); + } - // TODO: [ff SHORT_REG req] send "XIT + // [ff SHORT_REG req] send "XIT HidppMessage xit_req = { .report_id = HIDPP_SHORT, .device_index = 0xFF, @@ -255,9 +297,9 @@ bool fw_update(int fd, uint8_t device_index, uint8_t *fw, size_t fw_len) { .notif_id = NOTIF_DEV_CONNECT }; hidpp10_request(fd, &xit_req, match_notif, &recon_data); - RETURN_IF_FAIL(r == 0, "XIT failed"); + RETURN_IF_FAIL(r == 0, "Failed to deactivate DFU mode"); - // TODO: [ix notif] expect re-connection (0x41, proto 5) + // [ix notif] expect re-connection (0x41, proto 5) if (!recon_data.matched) { b = hidpp_read_msg(fd, 10000, &conn_notif, match_notif, &recon_data); RETURN_IF_FAIL(b, "Timeout waiting for re-connect"); -- cgit v1.2.1