Merge branch 'shearwater-timesync'

This commit is contained in:
Jef Driesen 2023-07-05 23:49:38 +02:00
commit 3d82d6796f
4 changed files with 289 additions and 112 deletions

View File

@ -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;
}

View File

@ -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
}

View File

@ -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);
}
}

View File

@ -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);
}