From 9f754dc563a78b9fecc6f62b26a0b97915d4eb65 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Jul 2017 07:03:13 -0700 Subject: [PATCH 01/15] Shearwater: extract log version from header The earliest document I have references log version 6. There are apparently older versions, but I don't know what the differences are. Before version 7, the log version wasn't always reliably stored, so we assume 6 is the minimum and use 7 (or later) if we find it. [Jef Driesen: Initialize and reset the cache correctly.] Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 4217f54..389c8e8 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -60,6 +60,7 @@ struct shearwater_predator_parser_t { unsigned int samplesize; // Cached fields. unsigned int cached; + unsigned int logversion; unsigned int headersize; unsigned int footersize; unsigned int ngasmixes; @@ -139,6 +140,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->petrel = petrel; parser->samplesize = samplesize; parser->cached = 0; + parser->logversion = 0; parser->headersize = 0; parser->footersize = 0; parser->ngasmixes = 0; @@ -175,6 +177,7 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char // Reset the cache. parser->cached = 0; + parser->logversion = 0; parser->headersize = 0; parser->footersize = 0; parser->ngasmixes = 0; @@ -226,6 +229,12 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) return DC_STATUS_DATAFORMAT; } + // Log versions before 6 weren't reliably stored in the data, but + // 6 is also the oldest version that we assume in our code + unsigned int logversion = 6; + if (data[127] > 6) + logversion = data[127]; + // Adjust the footersize for the final block. if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) { footersize += SZ_BLOCK; @@ -304,6 +313,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } // Cache the data for later use. + parser->logversion = logversion; parser->headersize = headersize; parser->footersize = footersize; parser->ngasmixes = ngasmixes; From 7f22f4ac86ca70f814fa6b99c1a6332afacc20dd Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Jul 2017 07:05:53 -0700 Subject: [PATCH 02/15] Shearwater: extract tank sensor data for log version 7 The first dive computer to support this is the Perdix AI. Interestingly, this keeps track of two sensors at all times. I haven't seen data with two sensors active, yet. [Jef Driesen: Update to the latest documentation.] Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 389c8e8..f406bfc 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -522,6 +522,34 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.deco.time = data[offset + 9] * 60; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); + // for logversion 7 and newer (introduced for Perdix AI) + // detect tank pressure + if (parser->logversion >= 7) { + // Tank pressure + // Values above 0xFFF0 are special codes: + // 0xFFFF AI is off + // 0xFFFE No comms for 90 seconds+ + // 0xFFFD No comms for 30 seconds + // 0xFFFC Transmitter not paired + // For regular values, the top 4 bits contain the battery + // level (0=normal, 1=critical, 2=warning), and the lower 12 + // bits the tank pressure in units of 2 psi. + unsigned int pressure = array_uint16_be (data + offset + 27); + if (pressure < 0xFFF0) { + pressure &= 0x0FFF; + sample.pressure.tank = 0; + sample.pressure.value = pressure * 2 * PSI / BAR; + if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + } + pressure = array_uint16_be (data + offset + 19); + if (pressure < 0xFFF0) { + pressure &= 0x0FFF; + sample.pressure.tank = 1; + sample.pressure.value = pressure * 2 * PSI / BAR; + if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + } + } + offset += parser->samplesize; } From 63d6af8c4141272cd9ee6cc086437d95dc5546d7 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Jul 2017 07:40:29 -0700 Subject: [PATCH 03/15] Shearwater: add support for remaining gas time [Jef Driesen: Update to the latest documentation.] Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index f406bfc..8100a9f 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -548,6 +548,18 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.pressure.value = pressure * 2 * PSI / BAR; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); } + + // Gas time remaining in minutes + // Values above 0xF0 are special codes: + // 0xFF Not paired + // 0xFE No communication + // 0xFD Not available in current mode + // 0xFC Not available because of DECO + // 0xFB Tank size or max pressure haven’t been set up + if (data[offset + 21] < 0xF0) { + sample.rbt = data[offset + 21]; + if (callback) callback (DC_SAMPLE_RBT, sample, userdata); + } } offset += parser->samplesize; From 2d7d5152b472f53764889f16352282465792bf3f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 15 Sep 2017 17:42:08 +0200 Subject: [PATCH 04/15] Detect the model number using the hardware type The model number is stored in the final block of each dive. But for an efficient implementation of the fingerprint feature, the devinfo event should be emitted before downloading the manifests or the dives. Thus reporting the correct model number is problematic. Currently the model number is simply hardcoded to the value of the Petrel. This is sufficient for the parser, because there the model number is only used to distinguish the Predator from all the other models. Now, because the petrel backend doesn't support the Predator, and the predator backend (which supports both the Predator and Petrel) can obtain the correct model number from the final block, the hardcoded value works fine. Except of course for identifying the actual model! Allthough there doesn't seems to be a command to retrieve the model number directly, we can retrieve the hardware type and map that to the model number. --- src/descriptor.c | 10 ++++++---- src/shearwater_common.h | 7 +++++++ src/shearwater_petrel.c | 34 +++++++++++++++++++++++++++++++++- src/shearwater_predator.c | 3 --- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 52186f4..b8106a6 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -287,10 +287,12 @@ static const dc_descriptor_t g_descriptors[] = { /* Shearwater Predator */ {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, /* Shearwater Petrel */ - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, - {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, - {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 3}, - {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 3}, + {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, + {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, + {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, + {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 4}, + {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, + {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0}, /* Citizen Hyper Aqualand */ diff --git a/src/shearwater_common.h b/src/shearwater_common.h index b93f973..2283154 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -31,6 +31,13 @@ extern "C" { #define ID_SERIAL 0x8010 #define ID_FIRMWARE 0x8011 +#define ID_HARDWARE 0x8050 + +#define PREDATOR 2 +#define PETREL 3 +#define NERD 4 +#define PERDIX 5 +#define PERDIXAI 6 typedef struct shearwater_common_device_t { dc_device_t base; diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 71f507e..3b60bf4 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -200,9 +200,41 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Convert to a number. unsigned int firmware = str2num (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), 1); + // Read the hardware type. + rc = shearwater_common_identifier (&device->base, buffer, ID_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 0x0808: // Petrel 2 + case 0x0909: // Petrel 1 + case 0x0B0B: // Petrel 1 (newer hardware) + model = PETREL; + break; + case 0x0A0A: // Nerd 1 + case 0x0E0D: // Nerd 2 + model = NERD; + break; + case 0x0707: + model = PERDIX; + break; + case 0x0C0D: + model = PERDIXAI; + break; + default: + WARNING (abstract->context, "Unknown hardware type %04x.", hardware); + } + // Emit a device info event. dc_event_devinfo_t devinfo; - devinfo.model = 3; + devinfo.model = model; devinfo.firmware = firmware; devinfo.serial = array_uint32_be (serial); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index 42a579b..3cc79f1 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -30,9 +30,6 @@ #define ISINSTANCE(device) dc_device_isinstance((device), &shearwater_predator_device_vtable) -#define PREDATOR 2 -#define PETREL 3 - #define SZ_BLOCK 0x80 #define SZ_MEMORY 0x20080 From 2ced18870dbd0e8b1f290b85e96e0bbeebec6686 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 15 Nov 2017 22:47:23 +0100 Subject: [PATCH 05/15] Improve the progress events At the moment the progress events are reported for each download operation separately. Combined with the fact that the size of the dives isn't known in advance, and thus the progress events are based on a worst case value, the user experience is far from optimal. In practice, the progress goes from 0 to 100% for every manifest, and it stays close to zero while downloading the dives. This is improved by combining the individual progress events into a single progress for the entire download. This global progress simply counts the number of individual download operations. Since each operation is now subdivided into a fixed number of steps, regardless of the size of the transfer, the perceived speed is no longer constant. --- src/shearwater_common.c | 31 +++++++++++++++++++++---------- src/shearwater_common.h | 5 ++++- src/shearwater_petrel.c | 36 ++++++++++++++++++++++++++++++++++-- src/shearwater_predator.c | 7 ++++++- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/shearwater_common.c b/src/shearwater_common.c index c6dafbb..d4b8297 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -342,7 +342,7 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c dc_status_t -shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression) +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_device_t *abstract = (dc_device_t *) device; dc_status_t rc = DC_STATUS_SUCCESS; @@ -370,9 +370,11 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Enable progress notifications. - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = 3 + size + 1; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + unsigned int initial = 0, current = 0, maximum = 3 + size + 1; + if (progress) { + initial = progress->current; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } // Transfer the init request. rc = shearwater_common_transfer (device, req_init, sizeof (req_init), response, 3, &n); @@ -387,8 +389,11 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Update and emit a progress event. - progress.current += 3; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + if (progress) { + current += 3; + progress->current = initial + STEP (current, maximum); + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } unsigned int done = 0; unsigned char block = 1; @@ -415,8 +420,11 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Update and emit a progress event. - progress.current += length; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + if (progress) { + current += length; + progress->current = initial + STEP (current, maximum); + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } if (compression) { if (shearwater_common_decompress_lre (response + 2, length, buffer, &done) != 0) { @@ -454,8 +462,11 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Update and emit a progress event. - progress.current += 1; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + if (progress) { + current += 1; + progress->current = initial + STEP (current, maximum); + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } return DC_STATUS_SUCCESS; } diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 2283154..4253a00 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -39,6 +39,9 @@ extern "C" { #define PERDIX 5 #define PERDIXAI 6 +#define NSTEPS 10000 +#define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n)) + typedef struct shearwater_common_device_t { dc_device_t base; dc_serial_t *port; @@ -54,7 +57,7 @@ dc_status_t shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual); dc_status_t -shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression); +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); diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 3b60bf4..46a7582 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -168,6 +168,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call 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); if (rc != DC_STATUS_SUCCESS) { @@ -240,8 +245,16 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); while (1) { + // Update the progress state. + // Assume the worst case scenario of a full manifest, and adjust the + // value with the actual number of dives after the manifest has been + // processed. + maximum += 1 + RECORD_COUNT; + // Download a manifest. - rc = shearwater_common_download (&device->base, buffer, MANIFEST_ADDR, MANIFEST_SIZE, 0); + progress.current = NSTEPS * current; + progress.maximum = NSTEPS * maximum; + rc = shearwater_common_download (&device->base, buffer, MANIFEST_ADDR, MANIFEST_SIZE, 0, &progress); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the manifest."); dc_buffer_free (buffer); @@ -270,6 +283,10 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call count++; } + // Update the progress state. + current += 1; + maximum -= RECORD_COUNT - count; + // Append the manifest records to the main buffer. if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) { ERROR (abstract->context, "Insufficient buffer space available."); @@ -283,6 +300,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call break; } + // Update and emit a progress event. + progress.current = NSTEPS * current; + progress.maximum = NSTEPS * maximum; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + // Cache the buffer pointer and size. unsigned char *data = dc_buffer_get_data (manifests); unsigned int size = dc_buffer_get_size (manifests); @@ -293,7 +315,9 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int address = array_uint32_be (data + offset + 20); // Download the dive. - rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1); + progress.current = NSTEPS * current; + progress.maximum = NSTEPS * maximum; + rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1, &progress); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the dive."); dc_buffer_free (buffer); @@ -301,6 +325,9 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call return rc; } + // Update the progress state. + current += 1; + unsigned char *buf = dc_buffer_get_data (buffer); unsigned int len = dc_buffer_get_size (buffer); if (callback && !callback (buf, len, buf + 12, sizeof (device->fingerprint), userdata)) @@ -309,6 +336,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call offset += RECORD_SIZE; } + // Update and emit a progress event. + progress.current = NSTEPS * current; + progress.maximum = NSTEPS * maximum; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + dc_buffer_free (manifests); dc_buffer_free (buffer); diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index 3cc79f1..a47d414 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -133,7 +133,12 @@ shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } - return shearwater_common_download (device, buffer, 0xDD000000, SZ_MEMORY, 0); + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.current = 0; + progress.maximum = NSTEPS; + + return shearwater_common_download (device, buffer, 0xDD000000, SZ_MEMORY, 0, &progress); } From f5df2653003330a19220f74ff1f4ee8274899ea1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Jul 2017 12:42:18 -0700 Subject: [PATCH 06/15] Add EON Steel time sync capability The EON Steel can use the new 'timesync' interface to set the time automatically from the computer it is connected to. This also regularizes the EON Steel command names a bit, and adds a few new commands (you can also read the time etc, which this doesn't actually use). [Jef Driesen: Modified to follow the existing naming conventions, return the correct error code and avoid arithmetic operations with signed integers.] Signed-off-by: Linus Torvalds --- src/suunto_eonsteel.c | 80 +++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 07cd85c..063716d 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -51,23 +51,29 @@ struct directory_entry { }; // EON Steel command numbers and other magic field values -#define INIT_CMD 0x00 -#define INIT_MAGIC 0x0001 -#define INIT_SEQ 0 +#define CMD_INIT 0x0000 +#define INIT_MAGIC 0x0001 +#define INIT_SEQ 0 -#define READ_STRING_CMD 0x0411 +#define CMD_READ_STRING 0x0411 -#define FILE_LOOKUP_CMD 0x0010 -#define FILE_READ_CMD 0x0110 -#define FILE_STAT_CMD 0x0710 -#define FILE_CLOSE_CMD 0x0510 +#define CMD_FILE_OPEN 0x0010 +#define CMD_FILE_READ 0x0110 +#define CMD_FILE_STAT 0x0710 +#define CMD_FILE_CLOSE 0x0510 -#define DIR_LOOKUP_CMD 0x0810 -#define READDIR_CMD 0x0910 -#define DIR_CLOSE_CMD 0x0a10 +#define CMD_DIR_OPEN 0x0810 +#define CMD_DIR_READDIR 0x0910 +#define CMD_DIR_CLOSE 0x0a10 + +#define CMD_SET_TIME 0x0003 +#define CMD_GET_TIME 0x0103 +#define CMD_SET_DATE 0x0203 +#define CMD_GET_DATE 0x0303 static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime); static dc_status_t suunto_eonsteel_device_close(dc_device_t *abstract); static const dc_device_vtable_t suunto_eonsteel_device_vtable = { @@ -78,7 +84,7 @@ static const dc_device_vtable_t suunto_eonsteel_device_vtable = { NULL, /* write */ NULL, /* dump */ suunto_eonsteel_device_foreach, /* foreach */ - NULL, /* timesync */ + suunto_eonsteel_device_timesync, /* timesync */ suunto_eonsteel_device_close /* close */ }; @@ -345,7 +351,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf return -1; } memcpy(cmdbuf+4, filename, len); - rc = send_receive(eon, FILE_LOOKUP_CMD, + rc = send_receive(eon, CMD_FILE_OPEN, len+4, cmdbuf, sizeof(result), result); if (rc < 0) { @@ -354,7 +360,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf } HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "lookup", result, rc); - rc = send_receive(eon, FILE_STAT_CMD, + rc = send_receive(eon, CMD_FILE_STAT, 0, NULL, sizeof(result), result); if (rc < 0) { @@ -374,7 +380,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf ask = 1024; put_le32(1234, cmdbuf+0); // Not file offset, after all put_le32(ask, cmdbuf+4); // Size of read - rc = send_receive(eon, FILE_READ_CMD, + rc = send_receive(eon, CMD_FILE_READ, 8, cmdbuf, sizeof(result), result); if (rc < 0) { @@ -409,11 +415,11 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf size -= got; } - rc = send_receive(eon, FILE_CLOSE_CMD, + rc = send_receive(eon, CMD_FILE_CLOSE, 0, NULL, sizeof(result), result); if (rc < 0) { - ERROR(eon->base.context, "cmd FILE_CLOSE_CMD failed"); + ERROR(eon->base.context, "cmd CMD_FILE_CLOSE failed"); return -1; } HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "close", result, rc); @@ -465,7 +471,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry * put_le32(0, cmd); memcpy(cmd + 4, dive_directory, sizeof(dive_directory)); cmdlen = 4 + sizeof(dive_directory); - rc = send_receive(eon, DIR_LOOKUP_CMD, + rc = send_receive(eon, CMD_DIR_OPEN, cmdlen, cmd, sizeof(result), result); if (rc < 0) { @@ -476,7 +482,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry * for (;;) { unsigned int nr, last; - rc = send_receive(eon, READDIR_CMD, + rc = send_receive(eon, CMD_DIR_READDIR, 0, NULL, sizeof(result), result); if (rc < 0) { @@ -496,7 +502,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry * break; } - rc = send_receive(eon, DIR_CLOSE_CMD, + rc = send_receive(eon, CMD_DIR_CLOSE, 0, NULL, sizeof(result), result); if (rc < 0) { @@ -528,7 +534,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon) dc_usbhid_set_timeout(eon->usbhid, 5000); - if (send_cmd(eon, INIT_CMD, sizeof(init), init)) { + if (send_cmd(eon, CMD_INIT, sizeof(init), init)) { ERROR(eon->base.context, "Failed to send initialization command"); return -1; } @@ -699,6 +705,38 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac return device_is_cancelled(abstract) ? DC_STATUS_CANCELLED : DC_STATUS_SUCCESS; } +static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime) +{ + suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract; + unsigned char result[64], cmd[8]; + unsigned int year, month, day; + unsigned int hour, min, msec; + int rc; + + year = datetime->year; + month = datetime->month; + day = datetime->day; + hour = datetime->hour; + min = datetime->minute; + msec = datetime->second * 1000; + + cmd[0] = year & 0xFF; + cmd[1] = year >> 8; + cmd[2] = month; + cmd[3] = day; + cmd[4] = hour; + cmd[5] = min; + cmd[6] = msec & 0xFF; + cmd[7] = msec >> 8; + + rc = send_receive(eon, CMD_SET_TIME, sizeof(cmd), cmd, sizeof(result), result); + if (rc < 0) { + return DC_STATUS_IO; + } + + return DC_STATUS_SUCCESS; +} + static dc_status_t suunto_eonsteel_device_close(dc_device_t *abstract) { From 812db650d42dd57f9681469330bad7d459dca6a9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 17 Nov 2017 23:02:46 +0100 Subject: [PATCH 07/15] Locate the most recent dive The Suunto Eon Steel seems to have a limit of maximum 400 dives. Once that limit is reached, the oldest dives get overwritten with newer dives. But the order in which the dive entries are downloaded isn't changed, and thus the most recent dive is no longer the last entry. For the first 400 dives, the order is always straightforward: D001 D002 ... D399 D400 The most recent dive is always the last entry, and no special processing is necessary. But once the limit is reached, the next few dives will start to overwrite the oldest dives, but the order remains unchanged: D401 D402 ... D399 D400 Thus in order to return the dives in the correct order (newest first), we can no longer assume the most recent dive is the last entry, and thus we need to locate it manually. The new algorithm is based on the assumption that the most recent dive will have the hightest timestamp. And to be able to walk backwards through all the entries, the list is assumed to be a circular list. --- src/suunto_eonsteel.c | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 063716d..cf36184 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -592,16 +592,6 @@ error_free: return status; } -static int count_dir_entries(struct directory_entry *de) -{ - int count = 0; - while (de) { - count++; - de = de->next; - } - return count; -} - static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) { @@ -630,9 +620,6 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac unsigned int count = 0; dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - if (get_file_list(eon, &de) < 0) - return DC_STATUS_IO; - // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = 0; @@ -640,16 +627,47 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac devinfo.serial = array_convert_str2num(eon->version + 0x10, 16); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - count = count_dir_entries(de); - if (count == 0) { + if (get_file_list(eon, &de) < 0) + return DC_STATUS_IO; + + if (de == NULL) { return DC_STATUS_SUCCESS; } + // Locate the most recent dive. + // The filename represent the time of the dive, encoded as a hexadecimal + // number. Thus the most recent dive can be found by simply sorting the + // filenames alphabetically. + struct directory_entry *head = de, *tail = de, *latest = de; + while (de) { + if (strcmp (de->name, latest->name) > 0) { + latest = de; + } + tail = de; + count++; + de = de->next; + } + + // Make the most recent dive the head of the list. + // The linked list is made circular, by attaching the head to the tail and + // then cut open again just before the most recent dive. + de = head; + while (de) { + if (de->next == latest) { + de->next = NULL; + tail->next = head; + break; + } + + de = de->next; + } + file = dc_buffer_new(0); progress.maximum = count; progress.current = 0; device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); + de = latest; while (de) { int len; struct directory_entry *next = de->next; From 4ffd514f7682ff1ebe920be920e718748bdd0a40 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 19 Nov 2017 12:37:39 +0100 Subject: [PATCH 08/15] Add support for the Suunto Eon Core The Suunto Eon Core uses a different USB PID, but otherwise it's compatible with the Eon Steel. It's probably an Eon Steel internally, but with a smaller form factor. To be able to distinguish between the two models and use the correct USB PID, each model is assigned a different (artificial) model number. Reported-by: Nick Shore --- src/descriptor.c | 1 + src/device.c | 2 +- src/suunto_eonsteel.c | 17 ++++++++++++++--- src/suunto_eonsteel.h | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index b8106a6..c68ef1f 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -99,6 +99,7 @@ static const dc_descriptor_t g_descriptors[] = { /* Suunto EON Steel */ #ifdef USBHID {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, + {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, #endif /* Uwatec Aladin */ {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, diff --git a/src/device.c b/src/device.c index 349ed1d..266007a 100644 --- a/src/device.c +++ b/src/device.c @@ -125,7 +125,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = suunto_d9_device_open (&device, context, name, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_SUUNTO_EONSTEEL: - rc = suunto_eonsteel_device_open (&device, context); + rc = suunto_eonsteel_device_open (&device, context, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_UWATEC_ALADIN: rc = uwatec_aladin_device_open (&device, context, name); diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index cf36184..cd7bc79 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -30,9 +30,13 @@ #include "usbhid.h" #include "platform.h" +#define EONSTEEL 0 +#define EONCORE 1 + typedef struct suunto_eonsteel_device_t { dc_device_t base; dc_usbhid_t *usbhid; + unsigned int model; unsigned int magic; unsigned short seq; unsigned char version[0x30]; @@ -551,7 +555,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon) } dc_status_t -suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context) +suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; suunto_eonsteel_device_t *eon = NULL; @@ -564,12 +568,19 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context) return DC_STATUS_NOMEMORY; // Set up the magic handshake fields + eon->model = model; eon->magic = INIT_MAGIC; eon->seq = INIT_SEQ; memset (eon->version, 0, sizeof (eon->version)); memset (eon->fingerprint, 0, sizeof (eon->fingerprint)); - status = dc_usbhid_open(&eon->usbhid, context, 0x1493, 0x0030); + unsigned int vid = 0x1493, pid = 0; + if (model == EONCORE) { + pid = 0x0033; + } else { + pid = 0x0030; + } + status = dc_usbhid_open(&eon->usbhid, context, vid, pid); if (status != DC_STATUS_SUCCESS) { ERROR(context, "unable to open device"); goto error_free; @@ -622,7 +633,7 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac // Emit a device info event. dc_event_devinfo_t devinfo; - devinfo.model = 0; + devinfo.model = eon->model; devinfo.firmware = array_uint32_be (eon->version + 0x20); devinfo.serial = array_convert_str2num(eon->version + 0x10, 16); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); diff --git a/src/suunto_eonsteel.h b/src/suunto_eonsteel.h index 98763c9..2927dce 100644 --- a/src/suunto_eonsteel.h +++ b/src/suunto_eonsteel.h @@ -31,7 +31,7 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context); +suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context, unsigned int model); dc_status_t suunto_eonsteel_parser_create(dc_parser_t **parser, dc_context_t *context, unsigned int model); From 93fc2f1c79246c4567605aaf122c697cd2e48c28 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 7 Nov 2017 22:43:12 +0100 Subject: [PATCH 09/15] Decode the firmware version for the iDive series For the older iDive series, the firmware is stored at a slightly different offset. --- src/divesystem_idive_parser.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index ca5c2b1..f876891 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -272,12 +272,14 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba unsigned int beginpressure = 0; unsigned int endpressure = 0; + unsigned int firmware = 0; + unsigned int apos4 = 0; unsigned int nsamples = array_uint16_le (data + 1); unsigned int samplesize = SZ_SAMPLE_IDIVE; if (parser->model >= IX3M_EASY) { // Detect the APOS4 firmware. - unsigned int firmware = array_uint32_le(data + 0x2A); - unsigned int apos4 = (firmware / 10000000) >= 4; + firmware = array_uint32_le(data + 0x2A); + apos4 = (firmware / 10000000) >= 4; if (apos4) { // Dive downloaded and recorded with the APOS4 firmware. samplesize = SZ_SAMPLE_IX3M_APOS4; @@ -289,6 +291,8 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba // Dive downloaded and recorded with an older firmware. samplesize = SZ_SAMPLE_IX3M; } + } else { + firmware = array_uint32_le(data + 0x2E); } unsigned int offset = parser->headersize; From 4ccfa51faf8c01ed1ca9d096fba5f49f030f3e43 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 7 Nov 2017 22:47:21 +0100 Subject: [PATCH 10/15] Fix the decoding of the ndl/deco information With the new APOS4 firmware, both the tts and the duration of the first deco stop are recorded while in deco. But compared with the older firmware, the tts field has moved to a slightly different offset. And contrary to the new documentation, it seems that the value for invalid or infinite has also changed from 0xFFFF to 0x7FFF, Note that for dives recorded with an older firmware version, the duration of the first deco stop isn't available, and libdivecomputer reports the tts instead. This is the same behaviour as before. Reported-by: Janice McLaughlin --- src/divesystem_idive_parser.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index f876891..24f7e53 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -370,17 +370,31 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba } // Deco stop / NDL. - unsigned int deco = array_uint16_le (data + offset + 21); - unsigned int tts = array_uint16_le (data + offset + 23); - if (tts != 0xFFFF) { - if (deco) { + unsigned int decostop = 0, decotime = 0, tts = 0; + if (apos4) { + decostop = array_uint16_le (data + offset + 21); + decotime = array_uint16_le (data + offset + 23); + tts = array_uint16_le (data + offset + 25); + if (tts == 0x7FFF) { + tts = INVALID; + } + } else { + decostop = array_uint16_le (data + offset + 21); + tts = array_uint16_le (data + offset + 23); + if (tts == 0xFFFF) { + tts = INVALID; + } + } + if (tts != INVALID) { + if (decostop) { sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = deco / 10.0; + sample.deco.depth = decostop / 10.0; + sample.deco.time = apos4 ? decotime : tts; } else { sample.deco.type = DC_DECO_NDL; sample.deco.depth = 0.0; + sample.deco.time = tts; } - sample.deco.time = tts; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); } From cbaebc777d0c9b1d5f355e5f23e5e438b9390f03 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 22 Nov 2017 19:49:16 +0100 Subject: [PATCH 11/15] Generate html documentation from the manpages --- configure.ac | 2 ++ doc/man/Makefile.am | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d0939b9..774f9b4 100644 --- a/configure.ac +++ b/configure.ac @@ -64,8 +64,10 @@ AM_CONDITIONAL([ENABLE_DOC], [test "x$enable_doc" = "xyes"]) AC_PROG_CC AC_PROG_CC_C99 AC_CHECK_PROGS([DOXYGEN], [doxygen]) +AC_CHECK_PROGS([MANDOC], [mandoc]) AM_CONDITIONAL([HAVE_DOXYGEN],[test -n "$DOXYGEN"]) +AM_CONDITIONAL([HAVE_MANDOC],[test -n "$MANDOC"]) # Enable automake silent build rules. m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 7020e2e..fbaa991 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,4 +1,4 @@ -dist_man_MANS = \ +MANPAGES = \ dc_buffer_append.3 \ dc_buffer_free.3 \ dc_buffer_get_data.3 \ @@ -32,3 +32,18 @@ dist_man_MANS = \ dc_parser_samples_foreach.3 \ dc_parser_set_data.3 \ libdivecomputer.3 + +HTMLPAGES = $(MANPAGES:%=%.html) + +dist_man_MANS = $(MANPAGES) + +if HAVE_MANDOC +doc_DATA = $(HTMLPAGES) +endif + +SUFFIXES = .3 .3.html + +.3.3.html: + $(AM_V_GEN) $(MANDOC) -Thtml -Ostyle=mandoc.css,man=%N.%S.html $< > $@ + +CLEANFILES = $(HTMLPAGES) From 91f5b34ae59ff0d42b37eadf99f3d203221cbd2a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 25 Oct 2017 14:28:56 +0200 Subject: [PATCH 12/15] Enable some useful compiler warnings by default Because some of those compiler warnings are GCC specific, they should only be enabled if the compiler actually supports them. This is take care of with some macros from the autoconf-archive. To avoid breaking the build on systems that don't have those macros installed (e.g. Mac OS X), they are included in the project. --- configure.ac | 17 ++++++++ m4/ax_append_compile_flags.m4 | 67 +++++++++++++++++++++++++++++++ m4/ax_append_flag.m4 | 71 +++++++++++++++++++++++++++++++++ m4/ax_check_compile_flag.m4 | 74 +++++++++++++++++++++++++++++++++++ m4/ax_require_defined.m4 | 37 ++++++++++++++++++ 5 files changed, 266 insertions(+) create mode 100644 m4/ax_append_compile_flags.m4 create mode 100644 m4/ax_append_flag.m4 create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_require_defined.m4 diff --git a/configure.ac b/configure.ac index 774f9b4..718f095 100644 --- a/configure.ac +++ b/configure.ac @@ -171,6 +171,23 @@ AC_FUNC_STRERROR_R AC_CHECK_FUNCS([localtime_r gmtime_r timegm _mkgmtime]) AC_CHECK_FUNCS([getopt_long]) +# Checks for supported compiler options. +AX_APPEND_COMPILE_FLAGS([ \ + -pedantic \ + -Wall \ + -Wextra \ + -Wshadow \ + -Wrestrict \ + -Wformat=2 \ + -Wwrite-strings \ + -Wcast-qual \ + -Wpointer-arith \ + -Wstrict-prototypes \ + -Wmissing-prototypes \ + -Wmissing-declarations \ + -Wno-unused-parameter \ +]) + # Versioning. AC_SUBST([DC_VERSION],[dc_version]) AC_SUBST([DC_VERSION_MAJOR],[dc_version_major]) diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4 new file mode 100644 index 0000000..5b6f1af --- /dev/null +++ b/m4/ax_append_compile_flags.m4 @@ -0,0 +1,67 @@ +# ============================================================================ +# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# For every FLAG1, FLAG2 it is checked whether the compiler works with the +# flag. If it does, the flag is added FLAGS-VARIABLE +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. During the check the flag is always added to the +# current language's flags. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: This macro depends on the AX_APPEND_FLAG and +# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with +# AX_APPEND_LINK_FLAGS. +# +# LICENSE +# +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AC_DEFUN([AX_APPEND_COMPILE_FLAGS], +[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +for flag in $1; do + AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) +done +])dnl AX_APPEND_COMPILE_FLAGS diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 new file mode 100644 index 0000000..e8c5312 --- /dev/null +++ b/m4/ax_append_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 7 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..dcabb92 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4 new file mode 100644 index 0000000..17c3eab --- /dev/null +++ b/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED From 68380b2ec07b37338edf8d913c3a58a05310aee0 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 24 Nov 2017 20:58:21 +0100 Subject: [PATCH 13/15] Fix some casts with constant pointers Casting away the const qualifier generates a compiler warning which can easily be avoided by preserving the const qualifier. --- examples/output_xml.c | 2 +- src/bluetooth.c | 4 ++-- src/device.c | 6 +++--- src/irda.c | 4 ++-- src/oceanic_common.c | 2 +- src/serial_posix.c | 2 +- src/serial_win32.c | 2 +- src/suunto_common2.c | 2 +- src/usbhid.c | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/output_xml.c b/examples/output_xml.c index 86a3957..bb88b2c 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -142,7 +142,7 @@ sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) case DC_SAMPLE_VENDOR: fprintf (sampledata->ostream, " ", value.vendor.type, value.vendor.size); for (unsigned int i = 0; i < value.vendor.size; ++i) - fprintf (sampledata->ostream, "%02X", ((unsigned char *) value.vendor.data)[i]); + fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value.vendor.data)[i]); fprintf (sampledata->ostream, "\n"); break; case DC_SAMPLE_SETPOINT: diff --git a/src/bluetooth.c b/src/bluetooth.c index bc9ffe0..01a3e96 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -571,7 +571,7 @@ dc_bluetooth_write (dc_bluetooth_t *device, const void *data, size_t size, size_ break; // Timeout. } - s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); + s_ssize_t n = send (device->fd, (const char *) data + nbytes, size - nbytes, 0); if (n < 0) { s_errcode_t errcode = S_ERRNO; if (errcode == S_EINTR || errcode == S_EAGAIN) @@ -591,7 +591,7 @@ dc_bluetooth_write (dc_bluetooth_t *device, const void *data, size_t size, size_ } out: - HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, nbytes); out_invalidargs: if (actual) diff --git a/src/device.c b/src/device.c index 266007a..aba42d5 100644 --- a/src/device.c +++ b/src/device.c @@ -408,7 +408,7 @@ dc_device_close (dc_device_t *device) void device_event_emit (dc_device_t *device, dc_event_type_t event, const void *data) { - dc_event_progress_t *progress = (dc_event_progress_t *) data; + const dc_event_progress_t *progress = (const dc_event_progress_t *) data; // Check the event data for errors. switch (event) { @@ -436,10 +436,10 @@ device_event_emit (dc_device_t *device, dc_event_type_t event, const void *data) // Cache the event data. switch (event) { case DC_EVENT_DEVINFO: - device->devinfo = *(dc_event_devinfo_t *) data; + device->devinfo = *(const dc_event_devinfo_t *) data; break; case DC_EVENT_CLOCK: - device->clock = *(dc_event_clock_t *) data; + device->clock = *(const dc_event_clock_t *) data; break; default: break; diff --git a/src/irda.c b/src/irda.c index d085ec0..1a2c38c 100644 --- a/src/irda.c +++ b/src/irda.c @@ -535,7 +535,7 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) break; // Timeout. } - s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); + s_ssize_t n = send (device->fd, (const char *) data + nbytes, size - nbytes, 0); if (n < 0) { s_errcode_t errcode = S_ERRNO; if (errcode == S_EINTR || errcode == S_EAGAIN) @@ -555,7 +555,7 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) } out: - HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, nbytes); out_invalidargs: if (actual) diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 427b326..43bc06d 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -30,7 +30,7 @@ #include "rbstream.h" #include "array.h" -#define VTABLE(abstract) ((oceanic_common_device_vtable_t *) abstract->vtable) +#define VTABLE(abstract) ((const oceanic_common_device_vtable_t *) abstract->vtable) #define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_logbook_begin, l->rb_logbook_end) #define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end) diff --git a/src/serial_posix.c b/src/serial_posix.c index 1a1567c..79fadeb 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -753,7 +753,7 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act } out: - HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, nbytes); out_invalidargs: if (actual) diff --git a/src/serial_win32.c b/src/serial_win32.c index bba78f8..c6f87f0 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -500,7 +500,7 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act } out: - HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, dwWritten); + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, dwWritten); out_invalidargs: if (actual) diff --git a/src/suunto_common2.c b/src/suunto_common2.c index 80b2477..4aa1699 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -38,7 +38,7 @@ #define RB_PROFILE_DISTANCE(l,a,b,m) ringbuffer_distance (a, b, m, l->rb_profile_begin, l->rb_profile_end) -#define VTABLE(abstract) ((suunto_common2_device_vtable_t *) abstract->vtable) +#define VTABLE(abstract) ((const suunto_common2_device_vtable_t *) abstract->vtable) void suunto_common2_device_init (suunto_common2_device_t *device) diff --git a/src/usbhid.c b/src/usbhid.c index f0c10df..0dff237 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -523,7 +523,7 @@ out: } #endif - HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, nbytes); out_invalidargs: if (actual) From 350893fb27719bf1779d57a645b69cca0a2bc988 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 23 Nov 2017 21:19:13 +0100 Subject: [PATCH 14/15] Fix some potential buffer overflows Verify the buffer size before accessing its content! --- src/hw_ostc_parser.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index d01f087..a96456c 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -569,6 +569,14 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int header = parser->header; const hw_ostc_layout_t *layout = parser->layout; + // Check the header length. + if (version == 0x23 || version == 0x24) { + if (size < header + 5) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + } + // Get the sample rate. unsigned int samplerate = 0; if (version == 0x23 || version == 0x24) @@ -595,6 +603,14 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call return DC_STATUS_DATAFORMAT; } + // Check the header length. + if (version == 0x23 || version == 0x24) { + if (size < header + 5 + 3 * nconfig) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + } + // Get the extended sample configuration. hw_ostc_sample_info_t info[MAXCONFIG] = {{0}}; for (unsigned int i = 0; i < nconfig; ++i) { From 1195abc2f4acc7b10175d570ec73549d0938c83e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 24 Nov 2017 23:47:58 +0100 Subject: [PATCH 15/15] Release version 0.6.0 --- NEWS | 37 +++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 938041d..7c03239 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,40 @@ +Version 0.6.0 (2017-11-24) +========================== + +The v0.6.0 release adds support for several new devices, introduces two +new features and fixes a couple of bugs. There are a few minor backwards +incompatible changes, but most applications won't be affected by those. + +New features: + + * Add support for new backends: + - g2: Scubapro G2, Aladin Sport Matrix, Aladin Square + * Add support for many new devices: + - Aqualung: i200, i750TC + - Cochran: Commander I, II and TM + - Cressi: Drake + - Hollis: DG02 + - Mares: Quad + - Oceanic: F10 + - Ratio: iX3M and iDive series + - Suunto: D4f, Eon Core + - Uwatec: Aladin Tec 3G + * Add basic timezone support + * Add support for synchronizing the device clock + * Document the public api with man pages + +Removed/changed features: + + * Remove the deprecated gas change events + * Remove the deprecated vendor_product_parser_create(), + vendor_product_device_open() and vendor_product_extract_dives() + functions from the public api + * Remove the hw_{frog,ostc,ostc3}_device_clock() functions + +Bug fixes: + + * Many small improvements + Version 0.5.0 (2016-09-30) ========================== diff --git a/configure.ac b/configure.ac index 718f095..e3abae5 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ m4_define([dc_version_major],[0]) m4_define([dc_version_minor],[6]) m4_define([dc_version_micro],[0]) -m4_define([dc_version_suffix],[devel]) +m4_define([dc_version_suffix],[]) m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix])) # Libtool versioning.