diff --git a/src/shearwater_common.c b/src/shearwater_common.c index a7f67f6..ca3f237 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -36,6 +36,14 @@ #define ESC_END 0xDC #define ESC_ESC 0xDD +#define RDBI_REQUEST 0x22 +#define RDBI_RESPONSE 0x62 + +#define WDBI_REQUEST 0x2E +#define WDBI_RESPONSE 0x6E + +#define NAK 0x7F + dc_status_t shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream) { @@ -512,39 +520,227 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf dc_status_t -shearwater_common_identifier (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int id) +shearwater_common_rdbi (shearwater_common_device_t *device, unsigned int id, unsigned char data[], unsigned int size) { + dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Erase the buffer. - if (!dc_buffer_clear (buffer)) { - ERROR (abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; - } // Transfer the request. unsigned int n = 0; - unsigned char request[] = {0x22, + unsigned char request[] = { + RDBI_REQUEST, (id >> 8) & 0xFF, (id ) & 0xFF}; unsigned char response[SZ_PACKET]; - rc = shearwater_common_transfer (device, request, sizeof (request), response, sizeof (response), &n); - if (rc != DC_STATUS_SUCCESS) { - return rc; + status = shearwater_common_transfer (device, request, sizeof (request), response, sizeof (response), &n); + if (status != DC_STATUS_SUCCESS) { + return status; } // Verify the response. - if (n < 3 || response[0] != 0x62 || response[1] != request[1] || response[2] != request[2]) { + if (n < 3 || response[0] != RDBI_RESPONSE || response[1] != request[1] || response[2] != request[2]) { + if (n == 3 && response[0] == NAK && response[1] == RDBI_REQUEST) { + ERROR (abstract->context, "Received NAK packet with error code 0x%02x.", response[2]); + return DC_STATUS_UNSUPPORTED; + } ERROR (abstract->context, "Unexpected response packet."); return DC_STATUS_PROTOCOL; } - // Append the packet to the output buffer. - if (!dc_buffer_append (buffer, response + 3, n - 3)) { - ERROR (abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; + unsigned int length = n - 3; + + if (length != size) { + ERROR (abstract->context, "Unexpected packet size (%u bytes).", length); + return DC_STATUS_PROTOCOL; } - return rc; + if (length) { + memcpy (data, response + 3, length); + } + + return status; +} + +dc_status_t +shearwater_common_wdbi (shearwater_common_device_t *device, unsigned int id, const unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + if (size + 3 > SZ_PACKET) { + return DC_STATUS_INVALIDARGS; + } + + // Transfer the request. + unsigned int n = 0; + unsigned char request[SZ_PACKET] = { + WDBI_REQUEST, + (id >> 8) & 0xFF, + (id ) & 0xFF}; + if (size) { + memcpy (request + 3, data, size); + } + unsigned char response[SZ_PACKET]; + status = shearwater_common_transfer (device, request, size + 3, response, sizeof (response), &n); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Verify the response. + if (n < 3 || response[0] != WDBI_RESPONSE || response[1] != request[1] || response[2] != request[2]) { + if (n == 3 && response[0] == NAK && response[1] == WDBI_REQUEST) { + ERROR (abstract->context, "Received NAK packet with error code 0x%02x.", response[2]); + return DC_STATUS_UNSUPPORTED; + } + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + return status; +} + +dc_status_t +shearwater_common_timesync_local (shearwater_common_device_t *device, const dc_datetime_t *datetime) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + // Convert to local time. + dc_datetime_t local = *datetime; + local.timezone = DC_TIMEZONE_NONE; + + dc_ticks_t ticks = dc_datetime_mktime (&local); + if (ticks == -1) { + ERROR (abstract->context, "Invalid date/time value specified."); + return DC_STATUS_INVALIDARGS; + } + + const unsigned char timestamp[] = { + (ticks >> 24) & 0xFF, + (ticks >> 16) & 0xFF, + (ticks >> 8) & 0xFF, + (ticks ) & 0xFF, + }; + + status = shearwater_common_wdbi (device, ID_TIME_LOCAL, timestamp, sizeof(timestamp)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to write the dive computer local time."); + return status; + } + + return status; +} + +dc_status_t +shearwater_common_timesync_utc (shearwater_common_device_t *device, const dc_datetime_t *datetime) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + // Convert to UTC time. + dc_ticks_t ticks = dc_datetime_mktime (datetime); + if (ticks == -1) { + ERROR (abstract->context, "Invalid date/time value specified."); + return DC_STATUS_INVALIDARGS; + } + + const unsigned char timestamp[] = { + (ticks >> 24) & 0xFF, + (ticks >> 16) & 0xFF, + (ticks >> 8) & 0xFF, + (ticks ) & 0xFF, + }; + + status = shearwater_common_wdbi (device, ID_TIME_UTC, timestamp, sizeof(timestamp)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to write the dive computer UTC time."); + return status; + } + + int timezone = datetime->timezone / 60; + const unsigned char offset[] = { + (timezone >> 24) & 0xFF, + (timezone >> 16) & 0xFF, + (timezone >> 8) & 0xFF, + (timezone ) & 0xFF, + }; + + status = shearwater_common_wdbi (device, ID_TIME_OFFSET, offset, sizeof (offset)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to write the dive computer timezone offset."); + return status; + } + + // We don't have a way to determine the daylight savings time setting, + // but the required offset is already factored into the timezone offset. + const unsigned char dst[] = {0, 0, 0, 0}; + + status = shearwater_common_wdbi (device, ID_TIME_DST, dst, sizeof (dst)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to write the dive computer DST setting."); + return status; + } + + return status; +} + +unsigned int +shearwater_common_get_model (shearwater_common_device_t *device, unsigned int hardware) +{ + unsigned int model = 0; + + switch (hardware) { + case 0x0101: + case 0x0202: + model = PREDATOR; + break; + case 0x0404: + case 0x0909: + model = PETREL; + break; + case 0x0505: + case 0x0808: + case 0x0838: + case 0x08A5: + case 0x0B0B: + case 0x7828: + case 0x7B2C: + case 0x8838: + model = PETREL2; + break; + case 0xB407: + model = PETREL3; + break; + case 0x0606: + case 0x0A0A: + model = NERD; + break; + case 0x0E0D: + case 0x7E2D: + model = NERD2; + break; + case 0x0707: + model = PERDIX; + break; + case 0x0C0D: + case 0x7C2D: + case 0x8D6C: + model = PERDIXAI; + break; + case 0xC407: + model = PERDIX2; + break; + case 0x0F0F: + case 0x1F0A: + case 0x1F0F: + model = TERIC; + break; + case 0x1512: + model = PEREGRINE; + break; + default: + WARNING (device->base.context, "Unknown hardware type 0x%04x.", hardware); + } + + return model; } diff --git a/src/shearwater_common.h b/src/shearwater_common.h index b1f8c3b..4c7f186 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -35,6 +35,11 @@ extern "C" { #define ID_LOGUPLOAD 0x8021 #define ID_HARDWARE 0x8050 +#define ID_TIME_LOCAL 0x9030 +#define ID_TIME_UTC 0x9031 +#define ID_TIME_OFFSET 0x9032 +#define ID_TIME_DST 0x9033 + #define PREDATOR 2 #define PETREL 3 #define PETREL2 PETREL @@ -65,7 +70,19 @@ dc_status_t shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression, dc_event_progress_t *progress); dc_status_t -shearwater_common_identifier (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int id); +shearwater_common_rdbi (shearwater_common_device_t *device, unsigned int id, unsigned char data[], unsigned int size); + +dc_status_t +shearwater_common_wdbi (shearwater_common_device_t *device, unsigned int id, const unsigned char data[], unsigned int size); + +dc_status_t +shearwater_common_timesync_local (shearwater_common_device_t *device, const dc_datetime_t *datetime); + +dc_status_t +shearwater_common_timesync_utc (shearwater_common_device_t *device, const dc_datetime_t *datetime); + +unsigned int +shearwater_common_get_model (shearwater_common_device_t *device, unsigned int hardware); #ifdef __cplusplus } diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index c047cca..7ec18ff 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -46,6 +46,7 @@ typedef struct shearwater_petrel_device_t { static dc_status_t shearwater_petrel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t shearwater_petrel_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime); static dc_status_t shearwater_petrel_device_close (dc_device_t *abstract); static const dc_device_vtable_t shearwater_petrel_device_vtable = { @@ -56,7 +57,7 @@ static const dc_device_vtable_t shearwater_petrel_device_vtable = { NULL, /* write */ NULL, /* dump */ shearwater_petrel_device_foreach, /* foreach */ - NULL, /* timesync */ + shearwater_petrel_device_timesync, shearwater_petrel_device_close /* close */ }; @@ -152,117 +153,48 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; - // Allocate memory buffers for the manifests. - dc_buffer_t *buffer = dc_buffer_new (MANIFEST_SIZE); - dc_buffer_t *manifests = dc_buffer_new (MANIFEST_SIZE); - if (buffer == NULL || manifests == NULL) { - ERROR (abstract->context, "Insufficient buffer space available."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); - return DC_STATUS_NOMEMORY; - } - // Enable progress notifications. unsigned int current = 0, maximum = 0; dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Read the serial number. - rc = shearwater_common_identifier (&device->base, buffer, ID_SERIAL); + unsigned char rsp_serial[8] = {0}; + rc = shearwater_common_rdbi (&device->base, ID_SERIAL, rsp_serial, sizeof(rsp_serial)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the serial number."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); return rc; } // Convert to a number. unsigned char serial[4] = {0}; - if (array_convert_hex2bin (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), - serial, sizeof (serial)) != 0 ) { + if (array_convert_hex2bin (rsp_serial, sizeof(rsp_serial), serial, sizeof (serial)) != 0 ) { ERROR (abstract->context, "Failed to convert the serial number."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); return DC_STATUS_DATAFORMAT; - } // Read the firmware version. - rc = shearwater_common_identifier (&device->base, buffer, ID_FIRMWARE); + unsigned char rsp_firmware[11] = {0}; + rc = shearwater_common_rdbi (&device->base, ID_FIRMWARE, rsp_firmware, sizeof(rsp_firmware)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the firmware version."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); return rc; } // Convert to a number. - unsigned int firmware = str2num (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), 1); + unsigned int firmware = str2num (rsp_firmware, sizeof(rsp_firmware), 1); // Read the hardware type. - rc = shearwater_common_identifier (&device->base, buffer, ID_HARDWARE); + unsigned char rsp_hardware[2] = {0}; + rc = shearwater_common_rdbi (&device->base, ID_HARDWARE, rsp_hardware, sizeof(rsp_hardware)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the hardware type."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); return rc; } // Convert and map to the model number. - unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); - unsigned int model = 0; - switch (hardware) { - case 0x0101: - case 0x0202: - model = PREDATOR; - break; - case 0x0404: - case 0x0909: - model = PETREL; - break; - case 0x0505: - case 0x0808: - case 0x0838: - case 0x08A5: - case 0x0B0B: - case 0x7828: - case 0x7B2C: - case 0x8838: - model = PETREL2; - break; - case 0xB407: - model = PETREL3; - break; - case 0x0606: - case 0x0A0A: - model = NERD; - break; - case 0x0E0D: - case 0x7E2D: - model = NERD2; - break; - case 0x0707: - model = PERDIX; - break; - case 0x0C0D: - case 0x7C2D: - case 0x8D6C: - model = PERDIXAI; - break; - case 0xC407: - model = PERDIX2; - break; - case 0x0F0F: - case 0x1F0A: - case 0x1F0F: - model = TERIC; - break; - case 0x1512: - model = PEREGRINE; - break; - default: - WARNING (abstract->context, "Unknown hardware type %04x.", hardware); - } + unsigned int hardware = array_uint16_be (rsp_hardware); + unsigned int model = shearwater_common_get_model (&device->base, hardware); // Emit a device info event. dc_event_devinfo_t devinfo; @@ -272,22 +204,14 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the logbook type - rc = shearwater_common_identifier (&device->base, buffer, ID_LOGUPLOAD); + unsigned char rsp_logupload[9] = {0}; + rc = shearwater_common_rdbi (&device->base, ID_LOGUPLOAD, rsp_logupload, sizeof(rsp_logupload)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the logbook type."); - dc_buffer_free (buffer); - dc_buffer_free (manifests); return rc; } - if (dc_buffer_get_size (buffer) != 9) { - ERROR (abstract->context, "Unexpected packet size (" DC_PRINTF_SIZE " bytes).", dc_buffer_get_size(buffer)); - dc_buffer_free (buffer); - dc_buffer_free (manifests); - return DC_STATUS_DATAFORMAT; - } - - unsigned int base_addr = array_uint32_be (dc_buffer_get_data (buffer) + 1); + unsigned int base_addr = array_uint32_be (rsp_logupload + 1); switch (base_addr) { case 0xDD000000: // Predator - we shouldn't get here, we could give up or we can try 0xC0000000 case 0xC0000000: // Predator-Like Format (what we used to call the Petrel format) @@ -300,9 +224,17 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call break; default: // unknown format ERROR (abstract->context, "Unknown logbook format %08x", base_addr); + return DC_STATUS_DATAFORMAT; + } + + // Allocate memory buffers for the manifests. + dc_buffer_t *buffer = dc_buffer_new (MANIFEST_SIZE); + dc_buffer_t *manifests = dc_buffer_new (MANIFEST_SIZE); + if (buffer == NULL || manifests == NULL) { + ERROR (abstract->context, "Insufficient buffer space available."); dc_buffer_free (buffer); dc_buffer_free (manifests); - return DC_STATUS_DATAFORMAT; + return DC_STATUS_NOMEMORY; } // Read the manifest pages @@ -419,3 +351,28 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call return rc; } + +static dc_status_t +shearwater_petrel_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime) +{ + dc_status_t status = DC_STATUS_SUCCESS; + shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; + + // Read the hardware type. + unsigned char rsp_hardware[2] = {0}; + status = shearwater_common_rdbi (device, ID_HARDWARE, rsp_hardware, sizeof(rsp_hardware)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the hardware type."); + return status; + } + + // Convert and map to the model number. + unsigned int hardware = array_uint16_be (rsp_hardware); + unsigned int model = shearwater_common_get_model (device, hardware); + + if (model == TERIC) { + return shearwater_common_timesync_utc (device, datetime); + } else { + return shearwater_common_timesync_local (device, datetime); + } +} diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index d4c97b4..fffd511 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -44,6 +44,7 @@ typedef struct shearwater_predator_device_t { static dc_status_t shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); static dc_status_t shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t shearwater_predator_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime); static const dc_device_vtable_t shearwater_predator_device_vtable = { sizeof(shearwater_predator_device_t), @@ -53,7 +54,7 @@ static const dc_device_vtable_t shearwater_predator_device_vtable = { NULL, /* write */ shearwater_predator_device_dump, /* dump */ shearwater_predator_device_foreach, /* foreach */ - NULL, /* timesync */ + shearwater_predator_device_timesync, NULL /* close */ }; @@ -357,3 +358,9 @@ shearwater_predator_extract_dives (dc_device_t *abstract, const unsigned char da return shearwater_predator_extract_predator (abstract, data, size, callback, userdata); } } + +static dc_status_t +shearwater_predator_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime) +{ + return shearwater_common_timesync_local ((shearwater_common_device_t *) abstract, datetime); +}