From 0753f10661d76cbe1848d5ac079d511c36e1addd Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 30 Mar 2022 20:52:07 +0200 Subject: [PATCH 01/10] Fix changing the OSTC settings The command to change the OSTC settings has a variable length payload (1 to 4 bytes). Therefore, the expected number of bytes is only know after evaluating the first option index byte. Due to the limited UART buffer in the OSTC, sending the remainder of the packet immediately after the first byte, can cause the OSTC to get out of sync. Introduce a small delay between sending the option index and the remaining parameters to avoid this problem. --- src/hw_ostc3.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index ff821c6..550662f 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -318,11 +318,29 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, } if (input) { - // Send the input data packet. - status = hw_ostc3_write (device, progress, input, isize); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the data packet."); - return status; + if (cmd == WRITE) { + // Send the first byte of the input data packet. + status = hw_ostc3_write (device, progress, input, 1); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the data packet."); + return status; + } + + dc_iostream_sleep (device->iostream, 10); + + // Send the reamainder of the input data packet. + status = hw_ostc3_write (device, progress, input + 1, isize - 1); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the data packet."); + return status; + } + } else { + // Send the input data packet. + status = hw_ostc3_write (device, progress, input, isize); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the data packet."); + return status; + } } } From 7e3bf7eeb8e1ed63178af885dd6d5589e82d4354 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 29 Apr 2022 17:00:43 +0200 Subject: [PATCH 02/10] Use helper functions to decode multibyte values Add and use some helper functions for decoding multibyte BCD and binary encoded values, to remove some code duplication. --- src/array.c | 12 ++++++++++++ src/array.h | 3 +++ src/oceanic_common.c | 4 ++-- src/suunto_common2.c | 6 +----- src/suunto_eon.c | 6 +----- src/suunto_solution.c | 6 +----- src/suunto_vyper.c | 6 +----- 7 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/array.c b/src/array.c index 1777a01..6d9550e 100644 --- a/src/array.c +++ b/src/array.c @@ -160,6 +160,18 @@ array_convert_str2num (const unsigned char data[], unsigned int size) return value; } +unsigned int +array_convert_bin2dec (const unsigned char data[], unsigned int size) +{ + unsigned int value = 0; + for (unsigned int i = 0; i < size; ++i) { + value *= 100; + value += data[i]; + } + + return value; +} + unsigned int array_convert_bcd2dec (const unsigned char data[], unsigned int size) { diff --git a/src/array.h b/src/array.h index d69fa32..3c45785 100644 --- a/src/array.h +++ b/src/array.h @@ -52,6 +52,9 @@ array_convert_hex2bin (const unsigned char input[], unsigned int isize, unsigned unsigned int array_convert_str2num (const unsigned char data[], unsigned int size); +unsigned int +array_convert_bin2dec (const unsigned char data[], unsigned int size); + unsigned int array_convert_bcd2dec (const unsigned char data[], unsigned int size); diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 51c234f..77f4dc7 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -612,9 +612,9 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac devinfo.model = array_uint16_be (id + 8); devinfo.firmware = device->firmware; if (layout->pt_mode_serial == 0) - devinfo.serial = bcd2dec (id[10]) * 10000 + bcd2dec (id[11]) * 100 + bcd2dec (id[12]); + devinfo.serial = array_convert_bcd2dec (id + 10, 3); else if (layout->pt_mode_serial == 1) - devinfo.serial = id[11] * 10000 + id[12] * 100 + id[13]; + devinfo.serial = array_convert_bin2dec (id + 11, 3); else devinfo.serial = (id[11] & 0x0F) * 100000 + ((id[11] & 0xF0) >> 4) * 10000 + diff --git a/src/suunto_common2.c b/src/suunto_common2.c index fd56562..e8f7c51 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -264,11 +264,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac dc_event_devinfo_t devinfo; devinfo.model = device->version[0]; devinfo.firmware = array_uint24_be (device->version + 1); - devinfo.serial = 0; - for (unsigned int i = 0; i < 4; ++i) { - devinfo.serial *= 100; - devinfo.serial += serial[i]; - } + devinfo.serial = array_convert_bin2dec (serial, 4); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the header bytes. diff --git a/src/suunto_eon.c b/src/suunto_eon.c index 84fc367..cc0ea5d 100644 --- a/src/suunto_eon.c +++ b/src/suunto_eon.c @@ -205,11 +205,7 @@ suunto_eon_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v dc_event_devinfo_t devinfo; devinfo.model = 0; devinfo.firmware = 0; - devinfo.serial = 0; - for (unsigned int i = 0; i < 3; ++i) { - devinfo.serial *= 100; - devinfo.serial += bcd2dec (data[244 + i]); - } + devinfo.serial = array_convert_bcd2dec (data + 244, 3); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = suunto_common_extract_dives (device, &suunto_eon_layout, data, callback, userdata); diff --git a/src/suunto_solution.c b/src/suunto_solution.c index a09c6d5..99ac331 100644 --- a/src/suunto_solution.c +++ b/src/suunto_solution.c @@ -243,11 +243,7 @@ suunto_solution_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba dc_event_devinfo_t devinfo; devinfo.model = 0; devinfo.firmware = 0; - devinfo.serial = 0; - for (unsigned int i = 0; i < 3; ++i) { - devinfo.serial *= 100; - devinfo.serial += bcd2dec (data[0x1D + i]); - } + devinfo.serial = array_convert_bcd2dec (data + 0x1D, 3); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = suunto_solution_extract_dives (abstract, diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c index fe3b405..8465b9c 100644 --- a/src/suunto_vyper.c +++ b/src/suunto_vyper.c @@ -476,11 +476,7 @@ suunto_vyper_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, dc_event_devinfo_t devinfo; devinfo.model = header[hoffset + 0]; devinfo.firmware = header[hoffset + 1]; - devinfo.serial = 0; - for (unsigned int i = 0; i < 4; ++i) { - devinfo.serial *= 100; - devinfo.serial += header[hoffset + 2 + i]; - } + devinfo.serial = array_convert_bin2dec (header + hoffset + 2, 4); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate a memory buffer. From 52d2684479d9ef9689bf26de86aa9c093f1e0625 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 23 Mar 2022 17:19:12 +0100 Subject: [PATCH 03/10] Read the info and more info data during startup --- src/liquivision_lynx.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index b91b326..b461248 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -82,6 +82,8 @@ typedef struct liquivision_lynx_device_t { dc_device_t base; dc_iostream_t *iostream; unsigned char fingerprint[4]; + unsigned char info[6]; + unsigned char more[12]; } liquivision_lynx_device_t; static dc_status_t liquivision_lynx_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); @@ -274,6 +276,22 @@ liquivision_lynx_device_open (dc_device_t **out, dc_context_t *context, dc_iostr dc_iostream_write (device->iostream, init, sizeof (init), NULL); } + // Send the info command. + const unsigned char cmd_info[] = {0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F}; + status = liquivision_lynx_transfer (device, cmd_info, sizeof(cmd_info), device->info, sizeof(device->info)); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to send the info command."); + goto error_free; + } + + // Send the more info command. + const unsigned char cmd_more[] = {0x4D, 0x4F, 0x52, 0x45, 0x49, 0x4E, 0x46, 0x4F, 0x4D, 0x4F, 0x52, 0x45}; + status = liquivision_lynx_transfer (device, cmd_more, sizeof(cmd_more), device->more, sizeof(device->more)); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to send the more info command."); + goto error_free; + } + *out = (dc_device_t *) device; return DC_STATUS_SUCCESS; @@ -369,33 +387,15 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb progress.maximum = SEGMENTSIZE + RB_LOGBOOK_SIZE + RB_PROFILE_SIZE; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - // Send the info command. - unsigned char rsp_info[6] = {0}; - const unsigned char cmd_info[] = {0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F}; - status = liquivision_lynx_transfer (device, cmd_info, sizeof(cmd_info), rsp_info, sizeof(rsp_info)); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the info command."); - goto error_exit; - } - // Get the model and version. - unsigned int model = array_uint16_le(rsp_info + 0); - unsigned int version = array_uint32_le(rsp_info + 2); - - // Send the more info command. - unsigned char rsp_more[12] = {0}; - const unsigned char cmd_more[] = {0x4D, 0x4F, 0x52, 0x45, 0x49, 0x4E, 0x46, 0x4F, 0x4D, 0x4F, 0x52, 0x45}; - status = liquivision_lynx_transfer (device, cmd_more, sizeof(cmd_more), rsp_more, sizeof(rsp_more)); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the more info command."); - goto error_exit; - } + unsigned int model = array_uint16_le (device->info + 0); + unsigned int version = array_uint32_le (device->info + 2); // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = model; devinfo.firmware = 0; - devinfo.serial = array_uint32_le(rsp_more + 0); + devinfo.serial = array_uint32_le (device->more + 0); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the config segment. @@ -515,7 +515,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb unused = 6; } unsigned char header[SZ_HEADER_MAX] = {0}; - memcpy (header + 0, rsp_info + 2, 4); + memcpy (header + 0, device->info + 2, 4); memcpy (header + 4, logbook + offset + 4, headersize - 4); unsigned int crc = array_uint32_le (logbook + offset + 0); unsigned int ccrc = checksum_crc32b (header, headersize - unused); @@ -638,7 +638,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb // Prepend the logbook entry to the profile data. The memory buffer is // large enough to store this entry. The checksum is replaced with the // flash version number. - memcpy (profile + remaining + 0, rsp_info + 2, 4); + memcpy (profile + remaining + 0, device->info + 2, 4); memcpy (profile + remaining + 4, logbook + entry + 4, headersize - 4); if (callback && !callback (profile + remaining, headersize + length, logbook + entry, sizeof(device->fingerprint), userdata)) { From 4512a0a5d79fc77fac7694bfebf0d963ca61f335 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 23 Mar 2022 17:25:36 +0100 Subject: [PATCH 04/10] Emit a devinfo event when downloading a memory dump For diagnostics purposes it's often very useful to have the device information available when downloading a memory dump. --- src/cressi_edy.c | 7 +++++++ src/cressi_leonardo.c | 14 +++++++------- src/hw_ostc.c | 29 ++++++++++++++--------------- src/hw_ostc3.c | 15 +++++++++++++++ src/liquivision_lynx.c | 10 ++++++++++ src/mares_darwin.c | 25 ++++++++++++++++--------- src/mares_iconhd.c | 17 ++++++++++++++++- src/mares_nemo.c | 14 ++++++++------ src/mares_puck.c | 28 ++++++++++++++++++---------- src/oceanic_common.c | 29 +++++++++++++++++++++++++++-- src/shearwater_predator.c | 28 ++++++++++++++++++---------- src/suunto_eon.c | 18 +++++++++--------- src/suunto_solution.c | 15 +++++++-------- src/suunto_vyper.c | 27 ++++++++++++++++++++++++++- 14 files changed, 198 insertions(+), 78 deletions(-) diff --git a/src/cressi_edy.c b/src/cressi_edy.c index 89c4b48..e23eee7 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -379,6 +379,13 @@ cressi_edy_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = 0; + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); } diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index 244b887..f331537 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -378,6 +378,13 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_PROTOCOL; } + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = data[0]; + devinfo.firmware = 0; + devinfo.serial = array_uint24_le (data + 1); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return DC_STATUS_SUCCESS; } @@ -394,13 +401,6 @@ cressi_leonardo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba return rc; } - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = data[0]; - devinfo.firmware = 0; - devinfo.serial = array_uint24_le (data + 1); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - rc = cressi_leonardo_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); diff --git a/src/hw_ostc.c b/src/hw_ostc.c index cb64818..cbb97bb 100644 --- a/src/hw_ostc.c +++ b/src/hw_ostc.c @@ -274,6 +274,20 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) nbytes += len; } + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.firmware = array_uint16_be (data + 264); + devinfo.serial = array_uint16_le (data + 6); + if (devinfo.serial > 7000) + devinfo.model = 3; // OSTC 2C + else if (devinfo.serial > 2048) + devinfo.model = 2; // OSTC 2N + else if (devinfo.serial > 300) + devinfo.model = 1; // OSTC Mk2 + else + devinfo.model = 0; // OSTC + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return DC_STATUS_SUCCESS; } @@ -291,21 +305,6 @@ hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.firmware = array_uint16_be (data + 264); - devinfo.serial = array_uint16_le (data + 6); - if (devinfo.serial > 7000) - devinfo.model = 3; // OSTC 2C - else if (devinfo.serial > 2048) - devinfo.model = 2; // OSTC 2N - else if (devinfo.serial > 300) - devinfo.model = 1; // OSTC Mk2 - else - devinfo.model = 0; // OSTC - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - rc = hw_ostc_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 550662f..e8dc223 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -1662,6 +1662,21 @@ hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return rc; } + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.firmware = device->firmware; + devinfo.serial = device->serial; + if (device->hardware != UNKNOWN) { + devinfo.model = device->hardware; + } else { + // Fallback to the serial number. + if (devinfo.serial > 10000) + devinfo.model = SPORT; + else + devinfo.model = OSTC3; + } + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + // Allocate the required amount of memory. if (!dc_buffer_resize (buffer, SZ_MEMORY)) { ERROR (abstract->context, "Insufficient buffer space available."); diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index b461248..56abb37 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -366,12 +366,22 @@ liquivision_lynx_device_read (dc_device_t *abstract, unsigned int address, unsig static dc_status_t liquivision_lynx_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + liquivision_lynx_device_t *device = (liquivision_lynx_device_t *) abstract; + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = array_uint16_le (device->info + 0); + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (device->more + 0); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + // Allocate the required amount of memory. if (!dc_buffer_resize (buffer, MEMSIZE)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } + // Download the memory dump. return device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SEGMENTSIZE); } diff --git a/src/mares_darwin.c b/src/mares_darwin.c index 92172f2..cefa717 100644 --- a/src/mares_darwin.c +++ b/src/mares_darwin.c @@ -189,6 +189,7 @@ mares_darwin_device_set_fingerprint (dc_device_t *abstract, const unsigned char static dc_status_t mares_darwin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; mares_darwin_device_t *device = (mares_darwin_device_t *) abstract; assert (device->layout != NULL); @@ -199,8 +200,22 @@ mares_darwin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } - return device_dump_read (abstract, dc_buffer_get_data (buffer), + // Download the memory dump. + status = device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PACKETSIZE); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = array_uint16_be (data + 8); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } @@ -221,14 +236,6 @@ mares_darwin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = device->model; - devinfo.firmware = 0; - devinfo.serial = array_uint16_be (data + 8); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - rc = mares_darwin_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 21f88fa..0c39b86 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -650,6 +650,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; // Allocate the required amount of memory. @@ -664,8 +665,22 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - return device_dump_read (abstract, dc_buffer_get_data (buffer), + // Download the memory dump. + status = device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), device->packetsize); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (data + 0x0C); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } static dc_status_t diff --git a/src/mares_nemo.c b/src/mares_nemo.c index f5e22a3..4b6929b 100644 --- a/src/mares_nemo.c +++ b/src/mares_nemo.c @@ -245,6 +245,14 @@ mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) nbytes += PACKETSIZE; } + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = data[1]; + devinfo.firmware = 0; + devinfo.serial = array_uint16_be (data + 8); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return DC_STATUS_SUCCESS; } @@ -264,13 +272,7 @@ mares_nemo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v return rc; } - // Emit a device info event. unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = data[1]; - devinfo.firmware = 0; - devinfo.serial = array_uint16_be (data + 8); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); const mares_common_layout_t *layout = NULL; switch (data[1]) { diff --git a/src/mares_puck.c b/src/mares_puck.c index 614d886..aaf1c41 100644 --- a/src/mares_puck.c +++ b/src/mares_puck.c @@ -192,6 +192,7 @@ mares_puck_device_set_fingerprint (dc_device_t *abstract, const unsigned char da static dc_status_t mares_puck_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; mares_puck_device_t *device = (mares_puck_device_t *) abstract; assert (device->layout != NULL); @@ -202,8 +203,22 @@ mares_puck_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } - return device_dump_read (abstract, dc_buffer_get_data (buffer), + // Download the memory dump. + status = device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PACKETSIZE); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = data[1]; + devinfo.firmware = 0; + devinfo.serial = array_uint16_be (data + 8); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } @@ -224,15 +239,8 @@ mares_puck_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = data[1]; - devinfo.firmware = 0; - devinfo.serial = array_uint16_be (data + 8); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - - rc = mares_common_extract_dives (abstract->context, device->layout, device->fingerprint, data, callback, userdata); + rc = mares_common_extract_dives (abstract->context, device->layout, device->fingerprint, + dc_buffer_get_data (buffer), callback, userdata); dc_buffer_free (buffer); diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 77f4dc7..d2a4818 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -188,13 +188,16 @@ oceanic_common_device_set_fingerprint (dc_device_t *abstract, const unsigned cha dc_status_t oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; assert (device != NULL); assert (device->layout != NULL); + const oceanic_common_layout_t *layout = device->layout; + // Allocate the required amount of memory. - if (!dc_buffer_resize (buffer, device->layout->memsize)) { + if (!dc_buffer_resize (buffer, layout->memsize)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } @@ -205,8 +208,30 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - return device_dump_read (abstract, dc_buffer_get_data (buffer), + // Download the memory dump. + status = device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PAGESIZE * device->multipage); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Emit a device info event. + unsigned char *id = dc_buffer_get_data (buffer) + layout->cf_devinfo; + dc_event_devinfo_t devinfo; + devinfo.model = array_uint16_be (id + 8); + devinfo.firmware = device->firmware; + if (layout->pt_mode_serial == 0) + devinfo.serial = array_convert_bcd2dec (id + 10, 3); + else if (layout->pt_mode_serial == 1) + devinfo.serial = array_convert_bin2dec (id + 11, 3); + else + devinfo.serial = + (id[11] & 0x0F) * 100000 + ((id[11] & 0xF0) >> 4) * 10000 + + (id[12] & 0x0F) * 1000 + ((id[12] & 0xF0) >> 4) * 100 + + (id[13] & 0x0F) * 10 + ((id[13] & 0xF0) >> 4) * 1; + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index e861c37..d4c97b4 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -115,6 +115,7 @@ shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigne static dc_status_t shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; // Pre-allocate the required amount of memory. @@ -128,7 +129,21 @@ shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) progress.current = 0; progress.maximum = NSTEPS; - return shearwater_common_download (device, buffer, 0xDD000000, SZ_MEMORY, 0, &progress); + // Download the memory dump. + status = shearwater_common_download (device, buffer, 0xDD000000, SZ_MEMORY, 0, &progress); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = data[0x2000D]; + devinfo.firmware = bcd2dec (data[0x2000A]); + devinfo.serial = array_uint32_be (data + 0x20002); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } @@ -145,15 +160,8 @@ shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t ca return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = data[0x2000D]; - devinfo.firmware = bcd2dec (data[0x2000A]); - devinfo.serial = array_uint32_be (data + 0x20002); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - - rc = shearwater_predator_extract_dives (abstract, data, SZ_MEMORY, callback, userdata); + rc = shearwater_predator_extract_dives (abstract, dc_buffer_get_data (buffer), + dc_buffer_get_size (buffer), callback, userdata); dc_buffer_free (buffer); diff --git a/src/suunto_eon.c b/src/suunto_eon.c index cc0ea5d..a7f09dd 100644 --- a/src/suunto_eon.c +++ b/src/suunto_eon.c @@ -181,6 +181,13 @@ suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) dc_buffer_append (buffer, answer, SZ_MEMORY); + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = 0; + devinfo.serial = array_convert_bcd2dec (answer + 244, 3); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return DC_STATUS_SUCCESS; } @@ -200,15 +207,8 @@ suunto_eon_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = 0; - devinfo.firmware = 0; - devinfo.serial = array_convert_bcd2dec (data + 244, 3); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - - rc = suunto_common_extract_dives (device, &suunto_eon_layout, data, callback, userdata); + rc = suunto_common_extract_dives (device, &suunto_eon_layout, dc_buffer_get_data (buffer), + callback, userdata); dc_buffer_free (buffer); diff --git a/src/suunto_solution.c b/src/suunto_solution.c index 99ac331..e72fe94 100644 --- a/src/suunto_solution.c +++ b/src/suunto_solution.c @@ -221,6 +221,13 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) progress.current += 1; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = 0; + devinfo.serial = array_convert_bcd2dec (data + 0x1D, 3); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + return DC_STATUS_SUCCESS; } @@ -238,14 +245,6 @@ suunto_solution_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba return rc; } - // Emit a device info event. - unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; - devinfo.model = 0; - devinfo.firmware = 0; - devinfo.serial = array_convert_bcd2dec (data + 0x1D, 3); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - rc = suunto_solution_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c index 8465b9c..02bd42f 100644 --- a/src/suunto_vyper.c +++ b/src/suunto_vyper.c @@ -426,14 +426,39 @@ suunto_vyper_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc static dc_status_t suunto_vyper_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { + dc_status_t status = DC_STATUS_SUCCESS; + // Allocate the required amount of memory. if (!dc_buffer_resize (buffer, SZ_MEMORY)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } - return device_dump_read (abstract, dc_buffer_get_data (buffer), + // Download the memory dump. + status = device_dump_read (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Identify the connected device as a Vyper or a Spyder, by inspecting + // the Vyper model code. For a Spyder, this value will contain the + // sample interval (20, 30 or 60s) instead of the model code. + unsigned char *data = dc_buffer_get_data (buffer); + unsigned int hoffset = HDR_DEVINFO_VYPER; + if (data[hoffset] == 20 || data[hoffset] == 30 || data[hoffset] == 60) { + hoffset = HDR_DEVINFO_SPYDER; + } + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = data[hoffset + 0]; + devinfo.firmware = data[hoffset + 1]; + devinfo.serial = 0; + devinfo.serial = array_convert_bin2dec (data + hoffset + 2, 4); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return status; } From 82c01348116c667cf946a9f403cb43ca03a9ff79 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 13 May 2022 21:13:54 +0200 Subject: [PATCH 05/10] Fix the timezone offset in the xml output For negative timezone offsets, only the hour part should have a sign. The minute part must always be formatted as a positive number. --- examples/output_xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/output_xml.c b/examples/output_xml.c index 9a5a9a8..854646b 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -238,7 +238,7 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u fprintf (output->ostream, "%04i-%02i-%02i %02i:%02i:%02i %+03i:%02i\n", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, - dt.timezone / 3600, (dt.timezone % 3600) / 60); + dt.timezone / 3600, (abs(dt.timezone) % 3600) / 60); } // Parse the divetime. From 8bfb96558949bf2b7ee0e6fadb01fbc10ca9d69a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 13 May 2022 21:18:05 +0200 Subject: [PATCH 06/10] Move the C_ARRAY_SIZE macro to a common place --- src/array.h | 2 ++ src/cochran_commander.c | 2 -- src/cochran_commander_parser.c | 2 -- src/cressi_goa_parser.c | 2 -- src/divesystem_idive.c | 2 -- src/divesystem_idive_parser.c | 2 -- src/hw_ostc.c | 2 -- src/liquivision_lynx_parser.c | 2 -- src/mares_iconhd.c | 2 -- src/sporasub_sp2_parser.c | 2 -- src/suunto_d9.c | 2 -- src/suunto_eonsteel_parser.c | 2 -- src/uwatec_smart.c | 2 -- src/uwatec_smart_parser.c | 2 -- 14 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/array.h b/src/array.h index 3c45785..f0f54cd 100644 --- a/src/array.h +++ b/src/array.h @@ -22,6 +22,8 @@ #ifndef ARRAY_H #define ARRAY_H +#define C_ARRAY_SIZE(a) (sizeof (a) / sizeof *(a)) + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/src/cochran_commander.c b/src/cochran_commander.c index e3677e9..38328ae 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -30,8 +30,6 @@ #include "ringbuffer.h" #include "rbstream.h" -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define MAXRETRIES 2 #define COCHRAN_MODEL_COMMANDER_TM 0 diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index e8ba76f..853b01b 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -29,8 +29,6 @@ #include "parser-private.h" #include "array.h" -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define COCHRAN_MODEL_COMMANDER_TM 0 #define COCHRAN_MODEL_COMMANDER_PRE21000 1 #define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2 diff --git a/src/cressi_goa_parser.c b/src/cressi_goa_parser.c index 35312db..256dbc3 100644 --- a/src/cressi_goa_parser.c +++ b/src/cressi_goa_parser.c @@ -28,8 +28,6 @@ #define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable) -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define SZ_HEADER 23 #define SZ_HEADER_SCUBA 0x61 #define SZ_HEADER_FREEDIVE 0x2B diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index b544ecb..61ec469 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -30,8 +30,6 @@ #include "checksum.h" #include "array.h" -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable) #define ISIX3M(model) ((model) >= 0x21) diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index a6375a0..5ace8f4 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -26,8 +26,6 @@ #include "parser-private.h" #include "array.h" -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable) #define ISIX3M(model) ((model) >= 0x21) diff --git a/src/hw_ostc.c b/src/hw_ostc.c index cbb97bb..7017321 100644 --- a/src/hw_ostc.c +++ b/src/hw_ostc.c @@ -31,8 +31,6 @@ #define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc_device_vtable) -#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - #define MAXRETRIES 9 #define FW_190 0x015A diff --git a/src/liquivision_lynx_parser.c b/src/liquivision_lynx_parser.c index ca41e21..c4272d8 100644 --- a/src/liquivision_lynx_parser.c +++ b/src/liquivision_lynx_parser.c @@ -31,8 +31,6 @@ #define ISINSTANCE(parser) dc_parser_isinstance((parser), &liquivision_lynx_parser_vtable) -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define XEN 0 #define XEO 1 #define LYNX 2 diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 0c39b86..e3f8ee5 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -30,8 +30,6 @@ #include "rbstream.h" #include "platform.h" -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable) #define NSTEPS 1000 diff --git a/src/sporasub_sp2_parser.c b/src/sporasub_sp2_parser.c index e041495..42cde0c 100644 --- a/src/sporasub_sp2_parser.c +++ b/src/sporasub_sp2_parser.c @@ -28,8 +28,6 @@ #define ISINSTANCE(parser) dc_device_isinstance((parser), &sporasub_sp2_parser_vtable) -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define SZ_HEADER 0x20 #define SZ_SAMPLE 0x04 diff --git a/src/suunto_d9.c b/src/suunto_d9.c index 17f7e7f..9622975 100644 --- a/src/suunto_d9.c +++ b/src/suunto_d9.c @@ -31,8 +31,6 @@ #define ISINSTANCE(device) dc_device_isinstance((device), (const dc_device_vtable_t *) &suunto_d9_device_vtable) -#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - #define D4i 0x19 #define D6i 0x1A #define D9tx 0x1B diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index 033a2f2..de00001 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -30,8 +30,6 @@ #include "array.h" #include "platform.h" -#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - enum eon_sample { ES_none = 0, ES_dtime, // duint16,precision=3 (time delta in ms) diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 5918646..d5d7ab0 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -31,8 +31,6 @@ #define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_smart_device_vtable) -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define DATASIZE 254 #define PACKETSIZE_USBHID_RX 64 #define PACKETSIZE_USBHID_TX 32 diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 3e4ed09..bb324e3 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -31,8 +31,6 @@ #define ISINSTANCE(parser) dc_parser_isinstance((parser), &uwatec_smart_parser_vtable) -#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) - #define NBITS 8 #define SMARTPRO 0x10 From 0064097c03c30c7386738d63c7f2a23118866802 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 18 May 2022 23:37:12 +0200 Subject: [PATCH 07/10] Add support for the Cressi Michelangelo The Cressi Michelangelo appears to be compatible with the Goa, with just a different model number. --- src/descriptor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/descriptor.c b/src/descriptor.c index fefef66..b55b2c3 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -330,6 +330,7 @@ static const dc_descriptor_t g_descriptors[] = { /* Cressi Goa */ {"Cressi", "Cartesio", DC_FAMILY_CRESSI_GOA, 1, DC_TRANSPORT_SERIAL, NULL}, {"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL, NULL}, + {"Cressi", "Michelangelo", DC_FAMILY_CRESSI_GOA, 5, DC_TRANSPORT_SERIAL, NULL}, {"Cressi", "Neon", DC_FAMILY_CRESSI_GOA, 9, DC_TRANSPORT_SERIAL, NULL}, /* Zeagle N2iTiON3 */ {"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL}, From d0c7562c410fe16144678bdc40b4bb8cbc611acb Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 31 May 2022 22:07:54 +0200 Subject: [PATCH 08/10] Ignore invalid gas mixes The Cressi Michelangelo appears to store inactive gas mixes as a zero oxygen percentage. Such invalid values shouldn't be reported. --- src/cressi_goa_parser.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cressi_goa_parser.c b/src/cressi_goa_parser.c index 256dbc3..53263b1 100644 --- a/src/cressi_goa_parser.c +++ b/src/cressi_goa_parser.c @@ -43,6 +43,8 @@ #define FREEDIVE 2 #define GAUGE 3 +#define NGASMIXES 2 + typedef struct cressi_goa_parser_t cressi_goa_parser_t; struct cressi_goa_parser_t { @@ -174,6 +176,15 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign parser->maxdepth = statistics.maxdepth; } + unsigned int ngasmixes = 0; + if (divemode == SCUBA || divemode == NITROX) { + for (unsigned int i = 0; i < NGASMIXES; ++i) { + if (data[0x20 + 2 * i] == 0) + break; + ngasmixes++; + } + } + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; if (value) { @@ -185,7 +196,7 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign *((double *) value) = parser->maxdepth; break; case DC_FIELD_GASMIX_COUNT: - *((unsigned int *) value) = divemode == SCUBA || divemode == NITROX ? 2 : 0; + *((unsigned int *) value) = ngasmixes; break; case DC_FIELD_GASMIX: gasmix->helium = 0.0; From 2443d3ea47aa3efc7c34ad2ed361f09c3b61876c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 12 May 2022 16:24:55 +0200 Subject: [PATCH 09/10] Add an address parameter to the memory dump helper function To support devices where not all memory is readable, the memory dump helper function needs an extra parameter to specify the start address. --- src/cressi_edy.c | 2 +- src/device-private.h | 2 +- src/device.c | 4 ++-- src/liquivision_lynx.c | 2 +- src/mares_darwin.c | 2 +- src/mares_iconhd.c | 2 +- src/mares_puck.c | 2 +- src/oceanic_common.c | 2 +- src/sporasub_sp2.c | 2 +- src/suunto_common2.c | 2 +- src/suunto_vyper.c | 2 +- src/zeagle_n2ition3.c | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cressi_edy.c b/src/cressi_edy.c index e23eee7..86ed76e 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -386,7 +386,7 @@ cressi_edy_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.serial = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - return device_dump_read (abstract, dc_buffer_get_data (buffer), + return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); } diff --git a/src/device-private.h b/src/device-private.h index 7ca6610..04ee68b 100644 --- a/src/device-private.h +++ b/src/device-private.h @@ -92,7 +92,7 @@ int device_is_cancelled (dc_device_t *device); dc_status_t -device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size, unsigned int blocksize); +device_dump_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size, unsigned int blocksize); #ifdef __cplusplus } diff --git a/src/device.c b/src/device.c index 3cd3e3a..affebc8 100644 --- a/src/device.c +++ b/src/device.c @@ -342,7 +342,7 @@ dc_device_dump (dc_device_t *device, dc_buffer_t *buffer) dc_status_t -device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size, unsigned int blocksize) +device_dump_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size, unsigned int blocksize) { if (device == NULL) return DC_STATUS_UNSUPPORTED; @@ -363,7 +363,7 @@ device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size, len = blocksize; // Read the packet. - dc_status_t rc = device->vtable->read (device, nbytes, data + nbytes, len); + dc_status_t rc = device->vtable->read (device, address + nbytes, data + nbytes, len); if (rc != DC_STATUS_SUCCESS) return rc; diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index 56abb37..a0d8e0a 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -382,7 +382,7 @@ liquivision_lynx_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Download the memory dump. - return device_dump_read (abstract, dc_buffer_get_data (buffer), + return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SEGMENTSIZE); } diff --git a/src/mares_darwin.c b/src/mares_darwin.c index cefa717..cac1717 100644 --- a/src/mares_darwin.c +++ b/src/mares_darwin.c @@ -201,7 +201,7 @@ mares_darwin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Download the memory dump. - status = device_dump_read (abstract, dc_buffer_get_data (buffer), + status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PACKETSIZE); if (status != DC_STATUS_SUCCESS) { return status; diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index e3f8ee5..55b684f 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -664,7 +664,7 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); // Download the memory dump. - status = device_dump_read (abstract, dc_buffer_get_data (buffer), + status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), device->packetsize); if (status != DC_STATUS_SUCCESS) { return status; diff --git a/src/mares_puck.c b/src/mares_puck.c index aaf1c41..14a25a2 100644 --- a/src/mares_puck.c +++ b/src/mares_puck.c @@ -204,7 +204,7 @@ mares_puck_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Download the memory dump. - status = device_dump_read (abstract, dc_buffer_get_data (buffer), + status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PACKETSIZE); if (status != DC_STATUS_SUCCESS) { return status; diff --git a/src/oceanic_common.c b/src/oceanic_common.c index d2a4818..c33655b 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -209,7 +209,7 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); // Download the memory dump. - status = device_dump_read (abstract, dc_buffer_get_data (buffer), + status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), PAGESIZE * device->multipage); if (status != DC_STATUS_SUCCESS) { return status; diff --git a/src/sporasub_sp2.c b/src/sporasub_sp2.c index 4593f6d..8112305 100644 --- a/src/sporasub_sp2.c +++ b/src/sporasub_sp2.c @@ -363,7 +363,7 @@ sporasub_sp2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - return device_dump_read (abstract, dc_buffer_get_data (buffer), + return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_READ); } diff --git a/src/suunto_common2.c b/src/suunto_common2.c index e8f7c51..063ea2b 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -218,7 +218,7 @@ suunto_common2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - return device_dump_read (abstract, dc_buffer_get_data (buffer), + return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); } diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c index 02bd42f..90ef702 100644 --- a/src/suunto_vyper.c +++ b/src/suunto_vyper.c @@ -435,7 +435,7 @@ suunto_vyper_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Download the memory dump. - status = device_dump_read (abstract, dc_buffer_get_data (buffer), + status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); if (status != DC_STATUS_SUCCESS) { return status; diff --git a/src/zeagle_n2ition3.c b/src/zeagle_n2ition3.c index 5d0c3bd..420ac0c 100644 --- a/src/zeagle_n2ition3.c +++ b/src/zeagle_n2ition3.c @@ -241,7 +241,7 @@ zeagle_n2ition3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } - return device_dump_read (abstract, dc_buffer_get_data (buffer), + return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), SZ_PACKET); } From 4b4efb2c07884161b38065288607042992005696 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 25 Mar 2022 20:00:39 +0100 Subject: [PATCH 10/10] Add support for the Seac Screen and Action --- examples/common.c | 1 + include/libdivecomputer/common.h | 2 + msvc/libdivecomputer.vcxproj | 3 + src/Makefile.am | 1 + src/descriptor.c | 3 + src/device.c | 4 + src/parser.c | 4 + src/seac_screen.c | 604 +++++++++++++++++++++++++++++++ src/seac_screen.h | 43 +++ src/seac_screen_parser.c | 376 +++++++++++++++++++ 10 files changed, 1041 insertions(+) create mode 100644 src/seac_screen.c create mode 100644 src/seac_screen.h create mode 100644 src/seac_screen_parser.c diff --git a/examples/common.c b/examples/common.c index 10eb496..4b3a63f 100644 --- a/examples/common.c +++ b/examples/common.c @@ -95,6 +95,7 @@ static const backend_table_t g_backends[] = { {"lynx", DC_FAMILY_LIQUIVISION_LYNX, 0}, {"sp2", DC_FAMILY_SPORASUB_SP2, 0}, {"excursion", DC_FAMILY_DEEPSIX_EXCURSION, 0}, + {"screen", DC_FAMILY_SEAC_SCREEN, 0}, }; static const transport_table_t g_transports[] = { diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index f6ca9fe..3553cf8 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -112,6 +112,8 @@ typedef enum dc_family_t { DC_FAMILY_SPORASUB_SP2 = (18 << 16), /* Deep Six */ DC_FAMILY_DEEPSIX_EXCURSION = (19 << 16), + /* Seac Screen */ + DC_FAMILY_SEAC_SCREEN = (20 << 16), } dc_family_t; #ifdef __cplusplus diff --git a/msvc/libdivecomputer.vcxproj b/msvc/libdivecomputer.vcxproj index 5c8fe3a..84bac6b 100644 --- a/msvc/libdivecomputer.vcxproj +++ b/msvc/libdivecomputer.vcxproj @@ -227,6 +227,8 @@ + + @@ -336,6 +338,7 @@ + diff --git a/src/Makefile.am b/src/Makefile.am index 5cec346..58916b6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,7 @@ libdivecomputer_la_SOURCES = \ liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \ sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \ deepsix_excursion.h deepsix_excursion.c deepsix_excursion_parser.c \ + seac_screen.h seac_screen.c seac_screen_parser.c \ socket.h socket.c \ irda.c \ usb.c \ diff --git a/src/descriptor.c b/src/descriptor.c index b55b2c3..0416157 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -429,6 +429,9 @@ static const dc_descriptor_t g_descriptors[] = { {"Crest", "CR-4", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix}, {"Genesis", "Centauri", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix}, {"Tusa", "TC1", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix}, + /* Seac Screen */ + {"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL}, + {"Seac", "Action", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL}, }; static int diff --git a/src/device.c b/src/device.c index affebc8..3080e6a 100644 --- a/src/device.c +++ b/src/device.c @@ -61,6 +61,7 @@ #include "liquivision_lynx.h" #include "sporasub_sp2.h" #include "deepsix_excursion.h" +#include "seac_screen.h" #include "device-private.h" #include "context-private.h" @@ -227,6 +228,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_DEEPSIX_EXCURSION: rc = deepsix_excursion_device_open (&device, context, iostream); break; + case DC_FAMILY_SEAC_SCREEN: + rc = seac_screen_device_open (&device, context, iostream); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/parser.c b/src/parser.c index 9a0f71c..22833e2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -61,6 +61,7 @@ #include "liquivision_lynx.h" #include "sporasub_sp2.h" #include "deepsix_excursion.h" +#include "seac_screen.h" #include "context-private.h" #include "parser-private.h" @@ -188,6 +189,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_DEEPSIX_EXCURSION: rc = deepsix_excursion_parser_create (&parser, context); break; + case DC_FAMILY_SEAC_SCREEN: + rc = seac_screen_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/seac_screen.c b/src/seac_screen.c new file mode 100644 index 0000000..e9b5366 --- /dev/null +++ b/src/seac_screen.c @@ -0,0 +1,604 @@ +/* + * libdivecomputer + * + * Copyright (C) 2022 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // memcmp, memcpy +#include // malloc, free + +#include "seac_screen.h" +#include "context-private.h" +#include "device-private.h" +#include "ringbuffer.h" +#include "rbstream.h" +#include "checksum.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &seac_screen_device_vtable) + +#define MAXRETRIES 4 + +#define SZ_MAXCMD 8 +#define SZ_MAXRSP SZ_READ + +#define CMD_HWINFO 0x1833 +#define CMD_SWINFO 0x1834 +#define CMD_RANGE 0x1840 +#define CMD_ADDRESS 0x1841 +#define CMD_READ 0x1842 + +#define SZ_HWINFO 256 +#define SZ_SWINFO 256 +#define SZ_RANGE 8 +#define SZ_ADDRESS 4 +#define SZ_READ 2048 + +#define SZ_HEADER 128 +#define SZ_SAMPLE 64 + +#define FP_OFFSET 0x0A +#define FP_SIZE 7 + +#define RB_PROFILE_BEGIN 0x010000 +#define RB_PROFILE_END 0x200000 +#define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_INCR(a,d) ringbuffer_increment (a, d, RB_PROFILE_BEGIN, RB_PROFILE_END) + +typedef struct seac_screen_device_t { + dc_device_t base; + dc_iostream_t *iostream; + unsigned char info[SZ_HWINFO + SZ_SWINFO]; + unsigned char fingerprint[FP_SIZE]; +} seac_screen_device_t; + +typedef struct seac_screen_logbook_t { + unsigned int address; + unsigned char header[SZ_HEADER]; +} seac_screen_logbook_t; + +static dc_status_t seac_screen_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t seac_screen_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); +static dc_status_t seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); +static dc_status_t seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); + +static const dc_device_vtable_t seac_screen_device_vtable = { + sizeof(seac_screen_device_t), + DC_FAMILY_SEAC_SCREEN, + seac_screen_device_set_fingerprint, /* set_fingerprint */ + seac_screen_device_read, /* read */ + NULL, /* write */ + seac_screen_device_dump, /* dump */ + seac_screen_device_foreach, /* foreach */ + NULL, /* timesync */ + NULL, /* close */ +}; + +static dc_status_t +seac_screen_send (seac_screen_device_t *device, unsigned short cmd, const unsigned char data[], size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned short crc = 0; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + if (size > SZ_MAXCMD) + return DC_STATUS_INVALIDARGS; + + // Setup the data packet + unsigned len = size + 6; + unsigned char packet[SZ_MAXCMD + 7] = { + 0x55, + (len >> 8) & 0xFF, + (len ) & 0xFF, + (cmd >> 8) & 0xFF, + (cmd ) & 0xFF, + }; + if (size) { + memcpy (packet + 5, data, size); + } + crc = checksum_crc16_ccitt (packet, size + 5, 0xFFFF); + packet[size + 5] = (crc >> 8) & 0xFF; + packet[size + 6] = (crc ) & 0xFF; + + // Send the data packet. + status = dc_iostream_write (device->iostream, packet, size + 7, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_receive (seac_screen_device_t *device, unsigned short cmd, unsigned char data[], size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[SZ_MAXRSP + 8] = {0}; + + // Read the packet header. + status = dc_iostream_read (device->iostream, packet, 3, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet header."); + return status; + } + + // Verify the start byte. + if (packet[0] != 0x55) { + ERROR (abstract->context, "Unexpected start byte (%02x).", packet[0]); + return DC_STATUS_PROTOCOL; + } + + // Verify the length. + unsigned int length = array_uint16_be (packet + 1); + if (length < 7 || length + 1 > sizeof(packet)) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + // Read the packet payload. + status = dc_iostream_read (device->iostream, packet + 3, length - 2, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet payload."); + return status; + } + + // Verify the checksum. + unsigned short crc = array_uint16_be (packet + 1 + length - 2); + unsigned short ccrc = checksum_crc16_ccitt (packet, 1 + length - 2, 0xFFFF); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_PROTOCOL; + } + + // Verify the command response. + unsigned int rsp = array_uint16_be (packet + 3); + unsigned int misc = packet[1 + length - 3]; + if (rsp != cmd || misc != 0x09) { + ERROR (abstract->context, "Unexpected command response (%04x %02x).", rsp, misc); + return DC_STATUS_PROTOCOL; + } + + if (length - 7 != size) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + memcpy (data, packet + 5, length - 7); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_packet (seac_screen_device_t *device, unsigned int cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + status = seac_screen_send (device, cmd, data, size); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + status = seac_screen_receive (device, cmd, answer, asize); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the response."); + return status; + } + + return status; +} + +static dc_status_t +seac_screen_transfer (seac_screen_device_t *device, unsigned int cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +{ + unsigned int nretries = 0; + dc_status_t rc = DC_STATUS_SUCCESS; + while ((rc = seac_screen_packet (device, cmd, data, size, answer, asize)) != DC_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) + return rc; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return rc; + + // Discard any garbage bytes. + dc_iostream_sleep (device->iostream, 100); + dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT); + } + + return DC_STATUS_SUCCESS; +} + +dc_status_t +seac_screen_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +{ + dc_status_t status = DC_STATUS_SUCCESS; + seac_screen_device_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (seac_screen_device_t *) dc_device_allocate (context, &seac_screen_device_vtable); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + device->iostream = iostream; + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Set the serial communication protocol (115200 8N1). + status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the terminal attributes."); + goto error_free; + } + + // Set the timeout for receiving data (1000ms). + status = dc_iostream_set_timeout (device->iostream, 1000); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the timeout."); + goto error_free; + } + + // Make sure everything is in a sane state. + dc_iostream_sleep (device->iostream, 100); + dc_iostream_purge (device->iostream, DC_DIRECTION_ALL); + + // Wakeup the device. + const unsigned char init[] = {0x61}; + dc_iostream_write (device->iostream, init, sizeof (init), NULL); + + // Read the hardware info. + status = seac_screen_transfer (device, CMD_HWINFO, NULL, 0, device->info, SZ_HWINFO); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to read the hardware info."); + goto error_free; + } + + // Read the software info. + status = seac_screen_transfer (device, CMD_SWINFO, NULL, 0, device->info + SZ_HWINFO, SZ_SWINFO); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to read the software info."); + goto error_free; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; + +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; +} + +static dc_status_t +seac_screen_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + seac_screen_device_t *device = (seac_screen_device_t *) abstract; + + if (size && size != sizeof (device->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + seac_screen_device_t *device = (seac_screen_device_t *) abstract; + + unsigned int nbytes = 0; + while (nbytes < size) { + // Maximum payload size. + unsigned int len = size - nbytes; + if (len > SZ_READ) + len = SZ_READ; + + // Read the data packet. + // Regardless of the requested payload size, the packet size is always + // the maximum size. The remainder of the packet is padded with zeros. + const unsigned char params[] = { + (address >> 24) & 0xFF, + (address >> 16) & 0xFF, + (address >> 8) & 0xFF, + (address ) & 0xFF, + (len >> 24) & 0xFF, + (len >> 16) & 0xFF, + (len >> 8) & 0xFF, + (len ) & 0xFF, + }; + unsigned char packet[SZ_READ] = {0}; + status = seac_screen_transfer (device, CMD_READ, params, sizeof(params), packet, sizeof(packet)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the read command."); + return status; + } + + // Copy only the payload bytes. + memcpy (data, packet, len); + + nbytes += len; + address += len; + data += len; + } + + return status; +} + +static dc_status_t +seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) +{ + seac_screen_device_t *device = (seac_screen_device_t *) abstract; + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = array_uint32_le (device->info + 0x11C); + devinfo.serial = array_uint32_le (device->info + 0x10); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Emit a vendor event. + dc_event_vendor_t vendor; + vendor.data = device->info; + vendor.size = sizeof(device->info); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Allocate the required amount of memory. + if (!dc_buffer_resize (buffer, RB_PROFILE_SIZE)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + return device_dump_read (abstract, RB_PROFILE_BEGIN, dc_buffer_get_data (buffer), + dc_buffer_get_size (buffer), SZ_READ); +} + +static dc_status_t +seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_status_t status = DC_STATUS_SUCCESS; + seac_screen_device_t *device = (seac_screen_device_t *) abstract; + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = RB_PROFILE_SIZE; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = array_uint32_le (device->info + 0x11C); + devinfo.serial = array_uint32_le (device->info + 0x010); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Emit a vendor event. + dc_event_vendor_t vendor; + vendor.data = device->info; + vendor.size = sizeof(device->info); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Read the range of the available dive numbers. + unsigned char range[SZ_RANGE] = {0}; + status = seac_screen_transfer (device, CMD_RANGE, NULL, 0, range, sizeof(range)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the range command."); + goto error_exit; + } + + // Extract the first and last dive number. + unsigned int first = array_uint32_be (range + 0); + unsigned int last = array_uint32_be (range + 4); + if (first > last) { + ERROR (abstract->context, "Invalid dive numbers (%u %u).", first, last); + status = DC_STATUS_DATAFORMAT; + goto error_exit; + } + + // Calculate the number of dives. + unsigned int ndives = last - first + 1; + + // Update and emit a progress event. + progress.current += SZ_RANGE; + progress.maximum += SZ_RANGE + ndives * (SZ_ADDRESS + SZ_HEADER); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Allocate memory for the logbook data. + seac_screen_logbook_t *logbook = (seac_screen_logbook_t *) malloc (ndives * sizeof (seac_screen_logbook_t)); + if (logbook == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Read the header of each dive in reverse order (most recent first). + unsigned int eop = 0; + unsigned int previous = 0; + unsigned int count = 0; + unsigned int skip = 0; + unsigned int rb_profile_size = 0; + unsigned int remaining = RB_PROFILE_SIZE; + for (unsigned int i = 0; i < ndives; ++i) { + unsigned int number = last - i; + + // Read the dive address. + const unsigned char cmd_address[] = { + (number >> 24) & 0xFF, + (number >> 16) & 0xFF, + (number >> 8) & 0xFF, + (number ) & 0xFF, + }; + unsigned char rsp_address[SZ_ADDRESS] = {0}; + status = seac_screen_transfer (device, CMD_ADDRESS, cmd_address, sizeof(cmd_address), rsp_address, sizeof(rsp_address)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive address."); + goto error_free_logbook; + } + + // Get the dive address. + logbook[i].address = array_uint32_be (rsp_address); + if (logbook[i].address < RB_PROFILE_BEGIN || logbook[i].address >= RB_PROFILE_END) { + ERROR (abstract->context, "Invalid ringbuffer pointer (0x%08x).", logbook[i].address); + status = DC_STATUS_DATAFORMAT; + goto error_free_logbook; + } + + // Read the dive header. + status = seac_screen_device_read (abstract, logbook[i].address, logbook[i].header, SZ_HEADER); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive header."); + goto error_free_logbook; + } + + // Update and emit a progress event. + progress.current += SZ_ADDRESS + SZ_HEADER; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Check the header checksums. + if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF) != 0 || + checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) { + ERROR (abstract->context, "Unexpected header checksum."); + status = DC_STATUS_DATAFORMAT; + goto error_free_logbook; + } + + // Check the fingerprint. + if (memcmp (logbook[i].header + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) { + skip = 1; + break; + } + + // Get the number of samples. + unsigned int nsamples = array_uint32_le (logbook[i].header + 0x44); + unsigned int nbytes = SZ_HEADER + nsamples * SZ_SAMPLE; + + // Get the end-of-profile pointer. + if (eop == 0) { + eop = previous = RB_PROFILE_INCR (logbook[i].address, nbytes); + } + + // Calculate the length. + unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous); + + // Check for the end of the ringbuffer. + if (length > remaining) { + WARNING (abstract->context, "Reached the end of the ringbuffer."); + skip = 1; + break; + } + + // Update the total profile size. + rb_profile_size += length; + + // Move to the start of the current dive. + remaining -= length; + previous = logbook[i].address; + count++; + } + + // Update and emit a progress event. + progress.maximum -= (ndives - count - skip) * (SZ_ADDRESS + SZ_HEADER) + + (RB_PROFILE_SIZE - rb_profile_size); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Exit if no dives to download. + if (count == 0) { + goto error_free_logbook; + } + + // Allocate memory for the profile data. + unsigned char *profile = (unsigned char *) malloc (rb_profile_size); + if (profile == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_free_logbook; + } + + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + status = dc_rbstream_new (&rbstream, abstract, SZ_READ, SZ_READ, RB_PROFILE_BEGIN, RB_PROFILE_END, eop); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + goto error_free_profile; + } + + previous = eop; + unsigned int offset = rb_profile_size; + for (unsigned int i = 0; i < count; ++i) { + // Calculate the length. + unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous); + + // Move to the start of the current dive. + offset -= length; + previous = logbook[i].address; + + // Read the dive. + status = dc_rbstream_read (rbstream, &progress, profile + offset, length); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + goto error_free_rbstream; + } + + // Check the dive header. + if (memcmp (profile + offset, logbook[i].header, SZ_HEADER) != 0) { + ERROR (abstract->context, "Unexpected dive header."); + status = DC_STATUS_DATAFORMAT; + goto error_free_rbstream; + } + + // Get the number of samples. + // The actual size of the dive, based on the number of samples, can + // sometimes be smaller than the maximum length. In that case, the + // remainder of the data is padded with 0xFF bytes. + unsigned int nsamples = array_uint32_le (logbook[i].header + 0x44); + unsigned int nbytes = SZ_HEADER + nsamples * SZ_SAMPLE; + if (nbytes > length) { + ERROR (abstract->context, "Unexpected dive length (%u %u).", nbytes, length); + status = DC_STATUS_DATAFORMAT; + goto error_free_rbstream; + } + + if (callback && !callback (profile + offset, nbytes, profile + offset + FP_OFFSET, sizeof(device->fingerprint), userdata)) { + break; + } + } + +error_free_rbstream: + dc_rbstream_free (rbstream); +error_free_profile: + free (profile); +error_free_logbook: + free (logbook); +error_exit: + return status; +} diff --git a/src/seac_screen.h b/src/seac_screen.h new file mode 100644 index 0000000..64c15b6 --- /dev/null +++ b/src/seac_screen.h @@ -0,0 +1,43 @@ +/* + * libdivecomputer + * + * Copyright (C) 2022 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef SEAC_SCREEN_H +#define SEAC_SCREEN_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +seac_screen_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); + +dc_status_t +seac_screen_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* SEAC_SCREEN_H */ diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c new file mode 100644 index 0000000..28988e4 --- /dev/null +++ b/src/seac_screen_parser.c @@ -0,0 +1,376 @@ +/* + * libdivecomputer + * + * Copyright (C) 2022 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +#include "seac_screen.h" +#include "context-private.h" +#include "parser-private.h" +#include "checksum.h" +#include "array.h" + +#define ISINSTANCE(parser) dc_device_isinstance((parser), &seac_screen_parser_vtable) + +#define SZ_HEADER 128 +#define SZ_SAMPLE 64 + +#define NGASMIXES 2 + +#define INVALID 0xFFFFFFFF + +typedef struct seac_screen_parser_t seac_screen_parser_t; + +struct seac_screen_parser_t { + dc_parser_t base; + // Cached fields. + unsigned int cached; + unsigned int ngasmixes; + unsigned int oxygen[NGASMIXES]; +}; + +static dc_status_t seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t seac_screen_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); + +static const dc_parser_vtable_t seac_screen_parser_vtable = { + sizeof(seac_screen_parser_t), + DC_FAMILY_SEAC_SCREEN, + seac_screen_parser_set_data, /* set_data */ + seac_screen_parser_get_datetime, /* datetime */ + seac_screen_parser_get_field, /* fields */ + seac_screen_parser_samples_foreach, /* samples_foreach */ + NULL /* destroy */ +}; + +dc_status_t +seac_screen_parser_create (dc_parser_t **out, dc_context_t *context) +{ + seac_screen_parser_t *parser = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + parser = (seac_screen_parser_t *) dc_parser_allocate (context, &seac_screen_parser_vtable); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + parser->cached = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + } + + *out = (dc_parser_t *) parser; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + seac_screen_parser_t *parser = (seac_screen_parser_t *)abstract; + + // Reset the cache. + parser->cached = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) +{ + const unsigned char *data = abstract->data; + + static const signed int tz_array[] = { + -12 * 60, /* UTC-12 */ + -11 * 60, /* UTC-11 */ + -10 * 60, /* UTC-10 */ + -9 * 60 - 30, /* UTC-9:30 */ + -9 * 60, /* UTC-9 */ + -8 * 60, /* UTC-8 */ + -7 * 60, /* UTC-7 */ + -6 * 60, /* UTC-6 */ + -5 * 60, /* UTC-5 */ + -4 * 60 - 30, /* UTC-4:30 */ + -4 * 60, /* UTC-4 */ + -3 * 60 - 30, /* UTC-3:30 */ + -3 * 60, /* UTC-3 */ + -2 * 60, /* UTC-2 */ + -1 * 60, /* UTC-1 */ + 0 * 60, /* UTC */ + 1 * 60, /* UTC+1 */ + 2 * 60, /* UTC+2 */ + 3 * 60, /* UTC+3 */ + 3 * 60 + 30, /* UTC+3:30 */ + 4 * 60, /* UTC+4 */ + 4 * 60 + 30, /* UTC+4:30 */ + 5 * 60, /* UTC+5 */ + 5 * 60 + 30, /* UTC+5:30 */ + 5 * 60 + 45, /* UTC+5:45 */ + 6 * 60, /* UTC+6 */ + 6 * 60 + 30, /* UTC+6:30 */ + 7 * 60, /* UTC+7 */ + 8 * 60, /* UTC+8 */ + 8 * 60 + 45, /* UTC+8:45 */ + 9 * 60, /* UTC+9 */ + 9 * 60 + 30, /* UTC+9:30 */ + 9 * 60 + 45, /* UTC+9:45 */ + 10 * 60, /* UTC+10 */ + 10 * 60 + 30, /* UTC+10:30 */ + 11 * 60, /* UTC+11 */ + 11 * 60 + 30, /* UTC+11:30 */ + 12 * 60, /* UTC+12 */ + 12 * 60 + 45, /* UTC+12:45 */ + 13 * 60, /* UTC+13 */ + 13 * 60 + 45, /* UTC+13:45 */ + 14 * 60, /* UTC+14 */ + }; + + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + // The date/time is stored as UTC time with a timezone offset. To convert to + // local time, the UTC time is first converted to unix time (seconds since + // the epoch), then adjusted for the timezone offset, and finally converted + // back into the broken-down time format. + + dc_datetime_t utc = {0}; + utc.year = data[0x10] + 2000; + utc.month = data[0x0F]; + utc.day = data[0x0E]; + utc.hour = data[0x0B]; + utc.minute = data[0x0C]; + utc.second = data[0x0D]; + utc.timezone = 0; + + unsigned int tz_idx = data[0x0A]; + if (tz_idx >= C_ARRAY_SIZE(tz_array)) { + ERROR (abstract->context, "Invalid timezone index (%u).", tz_idx); + return DC_STATUS_DATAFORMAT; + } + int timezone = tz_array[tz_idx] * 60; + + dc_ticks_t ticks = dc_datetime_mktime (&utc); + if (ticks == -1) + return DC_STATUS_DATAFORMAT; + + ticks += timezone; + + if (!dc_datetime_gmtime (datetime, ticks)) + return DC_STATUS_DATAFORMAT; + + datetime->timezone = timezone; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + seac_screen_parser_t *parser = (seac_screen_parser_t *) abstract; + const unsigned char *data = abstract->data; + + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + if (!parser->cached) { + dc_status_t rc = seac_screen_parser_samples_foreach (abstract, NULL, NULL); + if (rc != DC_STATUS_SUCCESS) + return rc; + } + + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + + if (value) { + switch (type) { + case DC_FIELD_AVGDEPTH: + *((double *) value) = array_uint16_le (data + 0x72) / 100.0; + break; + case DC_FIELD_MAXDEPTH: + *((double *) value) = array_uint16_le (data + 0x62) / 100.0; + break; + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = array_uint32_le (data + 0x64); + break; + case DC_FIELD_TEMPERATURE_MINIMUM: + *((double *) value) = array_uint16_le (data + 0x6A) / 100.0; + break; + case DC_FIELD_TEMPERATURE_MAXIMUM: + *((double *) value) = array_uint16_le (data + 0x68) / 100.0; + break; + case DC_FIELD_GASMIX_COUNT: + *((unsigned int *)value) = parser->ngasmixes; + break; + case DC_FIELD_GASMIX: + gasmix->helium = 0.0; + gasmix->oxygen = parser->oxygen[flags] / 100.0; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + case DC_FIELD_DIVEMODE: + switch (data[0x26]) { + case 1: + *((dc_divemode_t *) value) = DC_DIVEMODE_OC; + break; + case 2: + *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; + break; + case 3: + *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; + break; + default: + ERROR (abstract->context, "Unknown dive mode %i", data[0x26]); + return DC_STATUS_DATAFORMAT; + } + break; + default: + return DC_STATUS_UNSUPPORTED; + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + seac_screen_parser_t *parser = (seac_screen_parser_t *) abstract; + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF) != 0 || + checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) { + ERROR (abstract->context, "Unexpected header checksum."); + return DC_STATUS_DATAFORMAT; + } + + unsigned int dive_id = array_uint32_le (data + 0x00); + + unsigned int ngasmixes = 0; + unsigned int oxygen[NGASMIXES] = {0}; + unsigned int o2_previous = INVALID; + + unsigned int time = 0; + unsigned int offset = SZ_HEADER; + while (offset + SZ_SAMPLE <= size) { + dc_sample_value_t sample = {0}; + + if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF) != 0) { + ERROR (abstract->context, "Unexpected sample checksum."); + return DC_STATUS_DATAFORMAT; + } + + unsigned int id = array_uint32_le (data + offset + 0x00); + unsigned int timestamp = array_uint32_le (data + offset + 0x04); + unsigned int depth = array_uint16_le (data + offset + 0x08); + unsigned int temperature = array_uint16_le (data + offset + 0x0A); + unsigned int o2 = data[offset + 0x0C]; + unsigned int decodepth = array_uint16_le (data + offset + 0x0E); + unsigned int decotime = array_uint16_le (data + offset + 0x10); + unsigned int ndl_tts = array_uint16_le (data + offset + 0x12); + unsigned int cns = array_uint16_le (data + offset + 0x16); + + if (id != dive_id) { + ERROR (abstract->context, "Unexpected sample id (%u %u).", dive_id, id); + return DC_STATUS_DATAFORMAT; + } + + // Time (seconds). + if (timestamp < time) { + ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time); + return DC_STATUS_DATAFORMAT; + } + time = timestamp; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Depth (1/100 m). + sample.depth = depth / 100.0; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + // Temperature (1/100 °C). + sample.temperature = temperature / 100.0; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + + // Gas mix + if (o2 != o2_previous) { + // Find the gasmix in the list. + unsigned int idx = 0; + while (idx < ngasmixes) { + if (o2 == oxygen[idx]) + break; + idx++; + } + + // Add it to list if not found. + if (idx >= ngasmixes) { + if (idx >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_DATAFORMAT; + } + oxygen[idx] = o2; + ngasmixes = idx + 1; + } + + sample.gasmix = idx; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + o2_previous = o2; + } + + // NDL / Deco + if (decodepth) { + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.time = decotime; + sample.deco.depth = decodepth; + } else { + sample.deco.type = DC_DECO_NDL; + sample.deco.time = ndl_tts; + sample.deco.depth = 0; + } + if (callback) callback (DC_SAMPLE_DECO, sample, userdata); + + // CNS + sample.cns = cns / 100.0; + if (callback) callback (DC_SAMPLE_CNS, sample, userdata); + + offset += SZ_SAMPLE; + } + + // Cache the data for later use. + for (unsigned int i = 0; i < ngasmixes; ++i) { + parser->oxygen[i] = oxygen[i]; + } + parser->ngasmixes = ngasmixes; + parser->cached = 1; + + return DC_STATUS_SUCCESS; +}