Fix the download of dives without a profile
At the moment, trying to download an old dive for which the profile data has already been overwritten with newer data fails. This used to work fine, but around hwOS firmware v3.10, the behaviour described in commit 76187c550a806fe422920eb8795fa687244513f1 changed. When downloading the compact/full headers, the firmware always sends the headers without inspecting their content. Next, libdivecomputer uses the length field in these headers to determine how many bytes to expect when downloading the dive. However, when downloading the entire dive, the hwOS firmware now checks whether the profile data of the dive is still available. If that's no longer the case, the firmware sends a modified dive header (with the begin/end pointer fields reset to zero, and the length field reduced to 8 bytes), along with an empty dive profile. Since libdivecomputer expects to receive the full profile as indicated in the original header, the download fails with a timeout. To workaround this problem, download the dive data in two steps. First, download the 256 byte header and check whether it has been modified. If that's the case, reduce the length to that of the 5 byte empty profile. The header check is also updated to exclude the modified fields. For the progress events, just pretend the full profile has been downloaded.
This commit is contained in:
parent
89ae8b94cf
commit
9508401971
110
src/hw_ostc3.c
110
src/hw_ostc3.c
@ -99,6 +99,7 @@
|
||||
#define HDR_FULL_NUMBER 80 // 2 bytes
|
||||
#define HDR_FULL_VERSION 8 // 1 byte
|
||||
|
||||
#define HDR_FULL_POINTERS 2 // 6 bytes
|
||||
#define HDR_FULL_FIRMWARE 48 // 2 bytes
|
||||
|
||||
typedef enum hw_ostc3_state_t {
|
||||
@ -128,6 +129,7 @@ typedef struct hw_ostc3_logbook_t {
|
||||
unsigned int profile;
|
||||
unsigned int fingerprint;
|
||||
unsigned int number;
|
||||
unsigned int version;
|
||||
} hw_ostc3_logbook_t;
|
||||
|
||||
typedef struct hw_ostc3_firmware_t {
|
||||
@ -170,6 +172,7 @@ static const hw_ostc3_logbook_t hw_ostc3_logbook_compact = {
|
||||
HDR_COMPACT_LENGTH, /* profile */
|
||||
HDR_COMPACT_SUMMARY, /* fingerprint */
|
||||
HDR_COMPACT_NUMBER, /* number */
|
||||
HDR_COMPACT_VERSION, /* version */
|
||||
};
|
||||
|
||||
static const hw_ostc3_logbook_t hw_ostc3_logbook_full = {
|
||||
@ -177,6 +180,7 @@ static const hw_ostc3_logbook_t hw_ostc3_logbook_full = {
|
||||
HDR_FULL_LENGTH, /* profile */
|
||||
HDR_FULL_SUMMARY, /* fingerprint */
|
||||
HDR_FULL_NUMBER, /* number */
|
||||
HDR_FULL_VERSION, /* version */
|
||||
};
|
||||
|
||||
|
||||
@ -291,10 +295,15 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
unsigned int isize,
|
||||
unsigned char output[],
|
||||
unsigned int osize,
|
||||
unsigned int *actual,
|
||||
unsigned int delay)
|
||||
{
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
unsigned int length = osize;
|
||||
|
||||
if (cmd == DIVE && length < RB_LOGBOOK_SIZE_FULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (device_is_cancelled (abstract))
|
||||
return DC_STATUS_CANCELLED;
|
||||
@ -357,11 +366,44 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
}
|
||||
|
||||
if (output) {
|
||||
// Read the output data packet.
|
||||
status = hw_ostc3_read (device, progress, output, osize);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
if (cmd == DIVE) {
|
||||
// Read the dive header.
|
||||
status = hw_ostc3_read (device, progress, output, RB_LOGBOOK_SIZE_FULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the dive header.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// When the hwOS firmware detects the dive profile is no longer
|
||||
// valid, it sends a modified dive header (with the begin/end
|
||||
// pointer fields reset to zero, and the length field reduced to 8
|
||||
// bytes), along with an empty dive profile. Detect this condition
|
||||
// and adjust the expected length.
|
||||
if (array_isequal (output + HDR_FULL_POINTERS, 6, 0x00) &&
|
||||
array_uint24_le (output + HDR_FULL_LENGTH) == 8 &&
|
||||
length > RB_LOGBOOK_SIZE_FULL + 5) {
|
||||
length = RB_LOGBOOK_SIZE_FULL + 5;
|
||||
}
|
||||
|
||||
// Read the dive profile.
|
||||
status = hw_ostc3_read (device, progress, output + RB_LOGBOOK_SIZE_FULL, length - RB_LOGBOOK_SIZE_FULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the dive profile.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
if (progress && osize > length) {
|
||||
progress->current += osize - length;
|
||||
device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress);
|
||||
}
|
||||
} else {
|
||||
// Read the output data packet.
|
||||
status = hw_ostc3_read (device, progress, output, length);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,6 +427,9 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
}
|
||||
}
|
||||
|
||||
if (actual)
|
||||
*actual = length;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -457,9 +502,9 @@ hw_ostc3_device_id (hw_ostc3_device_t *device, unsigned char data[], unsigned in
|
||||
|
||||
// Send the command.
|
||||
unsigned char hardware[SZ_HARDWARE2] = {0};
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NULL, NODELAY);
|
||||
if (status == DC_STATUS_UNSUPPORTED) {
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NULL, NODELAY);
|
||||
}
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
@ -481,7 +526,7 @@ hw_ostc3_device_init_download (hw_ostc3_device_t *device)
|
||||
dc_context_t *context = (abstract ? abstract->context : NULL);
|
||||
|
||||
// Send the init command.
|
||||
dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NODELAY);
|
||||
dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to send the command.");
|
||||
return status;
|
||||
@ -574,7 +619,7 @@ hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
|
||||
|
||||
// Read the version information.
|
||||
unsigned char version[SZ_VERSION] = {0};
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the version information.");
|
||||
return rc;
|
||||
@ -604,7 +649,7 @@ hw_ostc3_device_close (dc_device_t *abstract)
|
||||
|
||||
// Send the exit command
|
||||
if (device->state == DOWNLOAD || device->state == SERVICE) {
|
||||
rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
dc_status_set_error(&status, rc);
|
||||
@ -648,7 +693,7 @@ hw_ostc3_device_version (dc_device_t *abstract, unsigned char data[], unsigned i
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -721,11 +766,11 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
// This is slower, but also works for older firmware versions.
|
||||
unsigned int compact = 1;
|
||||
rc = hw_ostc3_transfer (device, &progress, COMPACT,
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NODELAY);
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NULL, NODELAY);
|
||||
if (rc == DC_STATUS_UNSUPPORTED) {
|
||||
compact = 0;
|
||||
rc = hw_ostc3_transfer (device, &progress, HEADER,
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NODELAY);
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NULL, NODELAY);
|
||||
}
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the header.");
|
||||
@ -837,7 +882,7 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
// Download the dive.
|
||||
unsigned char number[1] = {idx};
|
||||
rc = hw_ostc3_transfer (device, &progress, DIVE,
|
||||
number, sizeof (number), profile, length, NODELAY);
|
||||
number, sizeof (number), profile, length, &length, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive.");
|
||||
free (profile);
|
||||
@ -846,12 +891,11 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
}
|
||||
|
||||
// Verify the header in the logbook and profile are identical.
|
||||
if (compact ?
|
||||
memcmp (profile + HDR_FULL_LENGTH, header + offset + HDR_COMPACT_LENGTH, 3) != 0 ||
|
||||
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_COMPACT_SUMMARY, 10) != 0 ||
|
||||
memcmp (profile + HDR_FULL_NUMBER, header + offset + HDR_COMPACT_NUMBER, 2) != 0 ||
|
||||
memcmp (profile + HDR_FULL_VERSION, header + offset + HDR_COMPACT_VERSION, 1) != 0 :
|
||||
memcmp (profile, header + offset, RB_LOGBOOK_SIZE_FULL) != 0) {
|
||||
if (memcmp (profile + HDR_FULL_VERSION, header + offset + logbook->version, 1) != 0 ||
|
||||
compact ?
|
||||
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_COMPACT_SUMMARY, 10) != 0 ||
|
||||
memcmp (profile + HDR_FULL_NUMBER, header + offset + HDR_COMPACT_NUMBER, 2) != 0 :
|
||||
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_FULL_SUMMARY, RB_LOGBOOK_SIZE_FULL - HDR_FULL_SUMMARY) != 0) {
|
||||
ERROR (abstract->context, "Unexpected profile header.");
|
||||
free (profile);
|
||||
free (header);
|
||||
@ -907,7 +951,7 @@ hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
unsigned char packet[6] = {
|
||||
datetime->hour, datetime->minute, datetime->second,
|
||||
datetime->month, datetime->day, datetime->year - 2000};
|
||||
rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -935,7 +979,7 @@ hw_ostc3_device_display (dc_device_t *abstract, const char *text)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -963,7 +1007,7 @@ hw_ostc3_device_customtext (dc_device_t *abstract, const char *text)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -989,7 +1033,7 @@ hw_ostc3_device_config_read (dc_device_t *abstract, unsigned int config, unsigne
|
||||
|
||||
// Send the command.
|
||||
unsigned char command[1] = {config};
|
||||
rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -1016,7 +1060,7 @@ hw_ostc3_device_config_write (dc_device_t *abstract, unsigned int config, const
|
||||
// Send the command.
|
||||
unsigned char command[SZ_CONFIG + 1] = {config};
|
||||
memcpy(command + 1, data, size);
|
||||
rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -1036,7 +1080,7 @@ hw_ostc3_device_config_reset (dc_device_t *abstract)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -1262,7 +1306,7 @@ hw_ostc3_firmware_erase (hw_ostc3_device_t *device, unsigned int addr, unsigned
|
||||
array_uint24_be_set (buffer, addr);
|
||||
buffer[3] = blocks;
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, delay);
|
||||
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, NULL, delay);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1272,7 +1316,7 @@ hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsi
|
||||
array_uint24_be_set (buffer, addr);
|
||||
array_uint24_be_set (buffer + 3, block_size);
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NODELAY);
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NULL, NODELAY);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1287,7 +1331,7 @@ hw_ostc3_firmware_block_write1 (hw_ostc3_device_t *device, unsigned int addr, co
|
||||
array_uint24_be_set (buffer, addr);
|
||||
memcpy (buffer + 3, block, block_size);
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, TIMEOUT);
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, NULL, TIMEOUT);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1306,7 +1350,7 @@ hw_ostc3_firmware_block_write2 (hw_ostc3_device_t *device, unsigned int address,
|
||||
array_uint24_be_set (buffer, address);
|
||||
memcpy (buffer + 3, data + nbytes, SZ_FIRMWARE_BLOCK2);
|
||||
|
||||
status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
@ -1347,7 +1391,7 @@ hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum)
|
||||
buffer[4] = (buffer[4]<<1 | buffer[4]>>7);
|
||||
}
|
||||
|
||||
rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to send flash firmware command");
|
||||
return rc;
|
||||
@ -1529,7 +1573,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
|
||||
// Read the firmware version info.
|
||||
unsigned char fwinfo[SZ_FWINFO] = {0};
|
||||
status = hw_ostc3_transfer (device, NULL, S_FWINFO,
|
||||
data + offset + 4, 1, fwinfo, sizeof(fwinfo), NODELAY);
|
||||
data + offset + 4, 1, fwinfo, sizeof(fwinfo), NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the firmware info.");
|
||||
goto error;
|
||||
@ -1542,7 +1586,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
|
||||
!array_isequal(fwinfo, sizeof(fwinfo), 0xFF))
|
||||
{
|
||||
status = hw_ostc3_transfer (device, &progress, S_UPLOAD,
|
||||
data + offset, length, NULL, 0, usecs / 1000);
|
||||
data + offset, length, NULL, 0, NULL, usecs / 1000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user