From a9e8281750ab95cc4b7ffcdc0c925da15e45bb77 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sat, 13 Dec 2014 00:04:27 +0100 Subject: fw-update: fill in more TODOs Not usable, the DFU magic packet is wrong! --- fw-update.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 225 insertions(+), 31 deletions(-) (limited to 'fw-update.c') diff --git a/fw-update.c b/fw-update.c index 5b70219..acd53bf 100644 --- a/fw-update.c +++ b/fw-update.c @@ -1,12 +1,17 @@ +#include #include #include #include #include #include #include +#include +#include "hidpp10.h" +#include "hidpp20.h" size_t read_fw(const char *filename, uint8_t **fw_buf) { - int fw_fd, r; + int fw_fd; + ssize_t r; size_t read_bytes = 0; size_t bufsize = 0; const size_t blocksize = 1024; @@ -30,7 +35,7 @@ size_t read_fw(const char *filename, uint8_t **fw_buf) { r = read(fw_fd, *fw_buf + read_bytes, bufsize - read_bytes); if (r > 0) - read_bytes += r; + read_bytes += (size_t) r; } while (r > 0); if (r < 0) @@ -49,43 +54,230 @@ size_t read_fw(const char *filename, uint8_t **fw_buf) { return read_bytes; } -typedef struct { +#if 1 +#define DPRINTF(...) printf(__VA_ARGS__) +#endif + +/* HID++-specific routines */ +#define WPID_T650 0x4101 /* Wireless PID of T650 touchpad */ +#define FEATURE_ID_DFU 0x00C0 + +struct match_notif_data { uint8_t report_id; uint8_t device_index; - uint8_t addr; - union { - uint8_t params[3]; - uint8_t params_l[16]; + uint8_t notif_id; + bool matched; + HidppMessage match; +}; +static bool match_notif(HidppMessage *msg, void *userdata) { + struct match_notif_data *d = userdata; + DPRINTF("Received report: %02x %02x %02x %02x %02x %02x %02x\n", + msg->report_id, msg->device_index, msg->sub_id, + msg->address, msg->params[0], msg->params[1], msg->params[2]); + if (msg->report_id == d->report_id && + msg->device_index == d->device_index && + msg->sub_id == d->notif_id) { + d->matched = true; + d->match = *msg; + return true; + } + return false; +} + +#if 0 +#define RETURN_IF_FAIL(cond, ...) \ + if (!(cond)) { warnx(__VA_ARGS__); return false; } +#else +/* debug: additionally print the message */ +#define RETURN_IF_FAIL(cond, ...) \ + if (!(cond)) { warnx(__VA_ARGS__); return false; } \ + else { DPRINTF("PASSED: " __VA_ARGS__); DPRINTF("\n"); } +#define PRWARN_IF_FAIL(cond, ...) \ + if (!(cond)) { warnx(__VA_ARGS__); } \ + else { DPRINTF("PASSED: " __VA_ARGS__); DPRINTF("\n"); } +#endif + +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; + uint8_t *params; + uint16_t wpid; + + // [ix feat] find HID++ version, confirm >= 0x0200 + version = hidpp20_get_version(fd, device_index); + PRWARN_IF_FAIL(version >= 0x0200, "HID++ 2.0 wanted, got %#x", version); + + // [ix feat] discover DFU feature + r = hidpp20_get_feature_by_id(fd, device_index, FEATURE_ID_DFU, &feat_dfu); + PRWARN_IF_FAIL(r == 0, "DFU feature not available"); + if (r != 0) { /* HACK: hard-coded for specific T650 */ + feat_dfu.feature_index = 13; + feat_dfu.feature_id = FEATURE_ID_DFU; + feat_dfu.feature_type = 0; + } + + r = hidpp10_enable_wireless_notifications(fd, true); + RETURN_IF_FAIL(r == 0, "Failed to enable wireless notifications!"); + + // print: ask user to restart device + puts("Please restart your device"); + + // [ix notif] wait for device connect + HidppMessage conn_notif; + struct match_notif_data match_notif_data = { + .report_id = HIDPP_SHORT, + .device_index = device_index, + .notif_id = NOTIF_DEV_CONNECT + }; + b = hidpp_read_msg(fd, 10000, &conn_notif, match_notif, &match_notif_data); + params = conn_notif.params; + PRWARN_IF_FAIL(b, "Timeout waiting for device connect"); + PRWARN_IF_FAIL(params[0] == 4, "Connected device is not Unifying"); + PRWARN_IF_FAIL(params[1] & (1 << 6), "Device is not connected"); + wpid = (params[2] << 8) | params[1]; + PRWARN_IF_FAIL (wpid == WPID_T650, "Not a T650 (%04x) but WPID:%04x", + WPID_T650, wpid); + + // [ix feat req] send "DFU" to DFU feature + HidppMessage req_dfu = { + .report_id = HIDPP_SHORT, + .device_index = device_index, + .feature_index = feat_dfu.feature_index, + .func = HIDPP20_FUNC(0), + }; + r = hidpp10_request(fd, &req_dfu, NULL, NULL); + PRWARN_IF_FAIL(r == 0, "DFU feature not found!"); + // [ix feat resp] confirm DFU enablement + PRWARN_IF_FAIL(req_dfu.params[0] == 0, "DFU not enabled?"); + + // [ff SHORT_REG req] write "LT" ix to f0 + const uint8_t lt_params[3] = { 'L', 'T', device_index }; + HidppMessage req_set_lt = { + .report_id = HIDPP_SHORT, + .device_index = 0xFF, /* receiver */ + .sub_id = SUB_SET_REGISTER, + .address = 0xF0, + }; + memcpy(req_set_lt.params, lt_params, 3); + struct match_notif_data req_lt_notif = { + .report_id = DJ_SHORT, + .device_index = device_index, + .notif_id = 0x42, /* Connection Status */ }; -} HidppReport; - -int fw_update(int fd, uint8_t device_index, uint8_t *fw, size_t fw_len) { - HidppReport report; - report.report_id = 0x10; - report.device_index = device_index; - report.addr = 0xE2; - report.params[0]; - -// TODO: refactor ltunify to allow re-using its feature discovery functionality. - // TODO: [ix feat] find HID++ version, confirm >= 0x0200 - // TODO: [ix feat] discover DFU feature - // TODO: print: ask user to restart device - // TODO: [ix notif] wait for device connect - // TODO: [ix feat req] send "DFU" to DFU feature - // TODO: [ix feat resp] confirm DFU enablement - // TODO: [ff SHORT_REG req] write "LT" ix to f0 - // TODO: [DJ ix notif resp] Link-loss notif (0x42) - // TODO: [ff SHORT_REG resp] write must be succesful - // TODO: [ff SHORT_REG] read f0 and confirm it says LT ix + r = hidpp10_request(fd, &req_set_lt, match_notif, &req_lt_notif); +#if 0 + // [DJ ix notif resp] Link-loss notif (0x42) + /* maybe this doesn't work because DJ notifs are not enabled? */ + RETURN_IF_FAIL(req_lt_notif.matched, "Did not receive link-loss notif!"); + RETURN_IF_FAIL(req_lt_notif.match.params[0] & 1, "Expected link-loss!"); +#endif + // [ff SHORT_REG resp] write must be succesful + RETURN_IF_FAIL(r == 0, "SET_REG(0xF0, \"LT\\%i\") failed with %i\n", + device_index, r); + + // [ff SHORT_REG] read f0 and confirm it says LT ix + HidppMessage req_get_lt = { + .report_id = HIDPP_SHORT, + .device_index = 0xFF, /* receiver */ + .sub_id = SUB_GET_REGISTER, + .address = 0xF0, + }; + r = hidpp10_request(fd, &req_get_lt, NULL, NULL); + RETURN_IF_FAIL(r == 0, "GET_REG(0xF0) failed with %i\n", r); + RETURN_IF_FAIL(memcmp(req_get_lt.params, lt_params, 3) == 0, + "Expected \"LT\\%i\" from 0xF0 register!", device_index); + /* Now "DUD communication" is acknowledged */ + +#define DFU_INIT(code, ...) \ + ((HidppMessage) { \ + .report_id = HIDPP_LONG, \ + .device_index = device_index, \ + .sub_id = SUB_SET_LONG_REGISTER, \ + .address = 0xE2, \ + .params_l = { code, __VA_ARGS__ } \ + }) +#define DFU_WRITE(_msg) \ + do { \ + r = hidpp10_request(fd, &_msg, NULL, NULL); \ + RETURN_IF_FAIL(r == 0, "Write E2 failed"); \ + } 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); + 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. */ + break; + } else { + RETURN_IF_FAIL(code == 0x12 || code == 0x01, + "Unexpected DFU resp %#04x", code); + } + } + // 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?"); + // TODO: [ff SHORT_REG req] send "XIT + HidppMessage xit_req = { + .report_id = HIDPP_SHORT, + .device_index = 0xFF, + .sub_id = SUB_SET_REGISTER, + .address = 0xF0, + .params = { 'X', 'I', 'T' } + }; + struct match_notif_data recon_data = { + .report_id = HIDPP_SHORT, + .device_index = device_index, + .notif_id = NOTIF_DEV_CONNECT + }; + hidpp10_request(fd, &xit_req, match_notif, &recon_data); + RETURN_IF_FAIL(r == 0, "XIT failed"); + // TODO: [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"); + } + /* not addr, but notif has different formatting */ + uint8_t proto = recon_data.match.address; + RETURN_IF_FAIL(proto == 5, "Expected proto 5, got %#04x", proto); + params = recon_data.match.params_l; + RETURN_IF_FAIL(params[0] & (1 << 6), "Device is not connected"); + wpid = (params[2] << 8) | params[1]; + RETURN_IF_FAIL (wpid == WPID_T650, "Not a T650 (%#04x) but WPID:%04x", + WPID_T650, wpid); + // TODO: (optional): ask user to restart device, confirm notif - // TODO: print: success + puts("Please restart your device"); - return 1; + // print: success + puts("Firmware update success!"); + + return true; } int main(int argc, char **argv) { @@ -100,7 +292,7 @@ int main(int argc, char **argv) { return 1; } - device_index = atoi(argv[2]); + device_index = (uint8_t) (argv[2][0] - '0'); if (!(device_index >= 1 && device_index <= 6)) { fprintf(stderr, "device_index must be between 1 and 6 (inclusive)\n"); return 1; @@ -114,7 +306,7 @@ int main(int argc, char **argv) { hid_fd = open(argv[1], O_RDWR); if (hid_fd >= 0) { - r = fw_update(hid_fd, device_index, fw, fw_len); + r = fw_update(hid_fd, device_index, fw, fw_len) ? 0 : 1; } else { perror(argv[1]); r = 1; @@ -125,3 +317,5 @@ int main(int argc, char **argv) { free(fw); return r; } + +/* vim: set sw=4 et ts=4: */ -- cgit v1.2.1