From 24f800d1852449734e10d65f5ec218199b076db3 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 26 Oct 2018 16:07:38 +0200 Subject: [PATCH 1/6] Add a workaround for invalid ringbuffer begin pointers Unfortunately there are several devices with an invalid ringbuffer begin pointer. In such cases, the strict validation of the pointer causes the download to fail, without being able to download any dives at all. Since the begin pointer is only needed to detect the start of the oldest dive, we can fall back to downloading the entire profile ringbuffer. Usually we can still detect the start of the oldest dive in some other (indirect) way. For example when reaching past the end of the ringbuffer, or the presence of invalid pointers in the linked list structure. The result is that, we'll be able to download at least some dives before hitting some other error. --- src/suunto_common2.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/suunto_common2.c b/src/suunto_common2.c index 4aa1699..fd56562 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -287,16 +287,22 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac if (last < layout->rb_profile_begin || last >= layout->rb_profile_end || end < layout->rb_profile_begin || - end >= layout->rb_profile_end || - begin < layout->rb_profile_begin || - begin >= layout->rb_profile_end) + end >= layout->rb_profile_end) { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x 0x%04x 0x%04x %u).", begin, last, end, count); return DC_STATUS_DATAFORMAT; } // Calculate the total amount of bytes. - unsigned int remaining = RB_PROFILE_DISTANCE (layout, begin, end, count != 0); + unsigned int remaining = 0; + if (begin < layout->rb_profile_begin || begin >= layout->rb_profile_end) { + // Fall back to downloading the entire ringbuffer as workaround + // for an invalid begin pointer! + ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x 0x%04x 0x%04x %u).", begin, last, end, count); + remaining = layout->rb_profile_end - layout->rb_profile_begin; + } else { + remaining = RB_PROFILE_DISTANCE (layout, begin, end, count != 0); + } // Update and emit a progress event. progress.maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - remaining; From e04a7fb33ddd9d1faa42a6cc9cbff4220c0f8fbc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 8 Oct 2018 12:09:50 -0700 Subject: [PATCH 2/6] Scubapro G2: update BLE downloading for new 1.4 firmware version The packetization format for the BLE communication used to be that the first byte of the BLE GATT payload was the size of the payload (1-19 bytes). That seems to no longer be true as of fw version 1.4. It is still the size of the payload for the simple small reply packets, but for the long data stream it ends up being an odd sequence of 13 different values that are almost - but not quite - 19 apart. Whatever. Modify our strict "length byte must make sense" rule to be more of a guidline than a hard rule. This makes the download succeed again. Very weird. Reported-by: Adric Norris Signed-off-by: Linus Torvalds --- src/uwatec_smart.c | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index c3e66b1..23ce6b9 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -326,11 +326,47 @@ uwatec_smart_usbhid_receive (uwatec_smart_device_t *device, dc_event_progress_t return DC_STATUS_PROTOCOL; } + /* + * Something changed in the G2 firmware between versions 1.2 and 1.4. + * + * The first byte of a packet always used to be the length of the + * packet data. That's still true for simple single-packet replies, + * but multi-packet replies seem to have some other data in it, at + * least for BLE. + * + * The new pattern *seems* to be: + * + * - simple one-packet reply: the byte remains the size of the reply + * + * - otherwise, it's an endlessly repeating sequence of + * + * 0xf7 247 + * 0x14 20 + * 0x27 39 + * 0x3a 58 + * 0x4d 77 + * 0x60 96 + * 0x73 115 + * 0x86 134 + * 0x99 153 + * 0xac 172 + * 0xbf 191 + * 0xd2 210 + * 0xe5 229 + * 0xf7 247 + * .. repeats .. + * + * which is basically "increase by 19" except for that last one (229->247 + * is an increase by 18). + * + * The number 19 is the real payload size for BLE GATT (20 bytes minus the + * one-byte magic size-that-isn't-size-any-more-byte). + * + * It may be just an oddly implemented sequence number. Whatever. + */ unsigned int len = buf[0]; - if (len + 1 > transferred) { - ERROR (abstract->context, "Invalid payload length (%u).", len); - return DC_STATUS_PROTOCOL; - } + if (len + 1 > transferred) + len = transferred-1; HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); From 04c367fd06454bf834f477e7987ba0a54d99741b Mon Sep 17 00:00:00 2001 From: Janice McLaughlin Date: Tue, 2 Oct 2018 16:03:36 -0700 Subject: [PATCH 3/6] Oceanic: fix up dive truncation issues This fixes the dive truncation that happened with long dives due to the removal of extra padding (commit a2100843b9cf: "Remove extra padding from the end of the profile"). It turns out the rest of the profile was in bits 13-15 (with bit 12 being something else). --- src/oceanic_common.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 184e5f9..bdc2f79 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -499,7 +499,10 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr // The logbook entry contains the total number of pages containing // profile data, excluding the footer page. Limit the profile size // to this size. - unsigned int npages = (array_uint16_le (profiles + offset + 12) & 0x0FFF) + 1; + unsigned int value = array_uint16_le (profiles + offset + 12); + unsigned int value_hi = value & 0xE000; + unsigned int value_lo = value & 0x0FFF; + unsigned int npages = ((value_hi >> 1) | value_lo) + 1; unsigned int length = npages * PAGESIZE; if (rb_entry_size > length) { rb_entry_size = length; From f5d636d12eb65d4f743a313283bc12c165004fc6 Mon Sep 17 00:00:00 2001 From: Janice McLaughlin Date: Thu, 4 Oct 2018 10:06:54 -0700 Subject: [PATCH 4/6] Fix the Pro Plus X gas mixes --- src/oceanic_atom2_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 6c699f2..bdf7174 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -451,6 +451,9 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == ZEN) { o2_offset = header + 4; ngasmixes = 2; + } else if (parser->model == PROPLUSX) { + o2_offset = 0x24; + ngasmixes = 4; } else { o2_offset = header + 4; ngasmixes = 3; From 52388efe593247bcd79d5fb3f5670c05b250b34e Mon Sep 17 00:00:00 2001 From: Janice McLaughlin Date: Thu, 20 Sep 2018 14:13:17 +0200 Subject: [PATCH 5/6] Add support for the Aqualung i770R It appears that the Aqualung i770R looks almost the same as the Pro Plus X, but has an additional pO2 field for each gas by the O2 field, which impacts the offset calculations. --- src/descriptor.c | 1 + src/oceanic_atom2.c | 25 ++++++++++++++++++++++++- src/oceanic_atom2_parser.c | 29 ++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index d9b5f01..e9f4540 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -220,6 +220,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i100", DC_FAMILY_OCEANIC_ATOM2, 0x464E, DC_TRANSPORT_SERIAL, NULL}, + {"Aqualung", "i770R", DC_FAMILY_OCEANIC_ATOM2, 0x4651, DC_TRANSPORT_SERIAL, NULL}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 7d1f68c..debade1 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -35,6 +35,7 @@ #define PROPLUSX 0x4552 #define VTX 0x4557 #define I750TC 0x455A +#define I770R 0x4651 #define MAXRETRIES 2 #define MAXDELAY 16 @@ -192,6 +193,10 @@ static const oceanic_common_version_t oceanic_proplusx_version[] = { {"OCEANOCX \0\0 2048"}, }; +static const oceanic_common_version_t aqualung_i770r_version[] = { + {"AQUA770R \0\0 2048"}, +}; + static const oceanic_common_version_t aeris_a300cs_version[] = { {"AER300CS \0\0 2048"}, {"OCEANVTX \0\0 2048"}, @@ -472,6 +477,21 @@ static const oceanic_common_layout_t oceanic_proplusx_layout = { 0, /* pt_mode_serial */ }; +static const oceanic_common_layout_t aqualung_i770r_layout = { + 0x440000, /* memsize */ + 0x40000, /* highmem */ + 0x0000, /* cf_devinfo */ + 0x0040, /* cf_pointers */ + 0x2000, /* rb_logbook_begin */ + 0x10000, /* rb_logbook_end */ + 16, /* rb_logbook_entry_size */ + 0x40000, /* rb_profile_begin */ + 0x440000, /* rb_profile_end */ + 0, /* pt_mode_global */ + 1, /* pt_mode_logbook */ + 0, /* pt_mode_serial */ +}; + static const oceanic_common_layout_t aeris_a300cs_layout = { 0x40000, /* memsize */ 0, /* highmem */ @@ -643,7 +663,7 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream // Get the correct baudrate. unsigned int baudrate = 38400; - if (model == VTX || model == I750TC || model == PROPLUSX) { + if (model == VTX || model == I750TC || model == PROPLUSX || model == I770R) { baudrate = 115200; } @@ -746,6 +766,9 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_proplusx_version)) { device->base.layout = &oceanic_proplusx_layout; device->bigpage = 16; + } else if (OCEANIC_COMMON_MATCH (device->base.version, aqualung_i770r_version)) { + device->base.layout = &aqualung_i770r_layout; + device->bigpage = 16; } else if (OCEANIC_COMMON_MATCH (device->base.version, aeris_a300cs_version)) { device->base.layout = &aeris_a300cs_layout; device->bigpage = 16; diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index bdf7174..8a35c4f 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -87,6 +87,7 @@ #define I550 0x4642 #define I200 0x4646 #define I100 0x464E +#define I770R 0x4651 #define NORMAL 0 #define GAUGE 1 @@ -174,7 +175,8 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned parser->headersize = 5 * PAGESIZE; parser->footersize = 0; } else if (model == A300CS || model == VTX || - model == I450T || model == I750TC) { + model == I450T || model == I750TC || + model == I770R) { parser->headersize = 5 * PAGESIZE; } else if (model == PROPLUSX) { parser->headersize = 3 * PAGESIZE; @@ -315,6 +317,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim case I450T: case I750TC: case PROPLUSX: + case I770R: datetime->year = (p[10]) + 2000; datetime->month = (p[8]); datetime->day = (p[9]); @@ -416,6 +419,7 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) unsigned int ngasmixes = 0; unsigned int o2_offset = 0; unsigned int he_offset = 0; + unsigned int o2_step = 1; if (mode == FREEDIVE) { ngasmixes = 0; } else if (parser->model == DATAMASK || parser->model == COMPUMASK) { @@ -454,6 +458,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == PROPLUSX) { o2_offset = 0x24; ngasmixes = 4; + } else if (parser->model == I770R) { + o2_offset = 0x30; + ngasmixes = 4; + o2_step = 2; } else { o2_offset = header + 4; ngasmixes = 3; @@ -465,8 +473,8 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) parser->mode = mode; parser->ngasmixes = ngasmixes; for (unsigned int i = 0; i < ngasmixes; ++i) { - if (data[o2_offset + i]) { - parser->oxygen[i] = data[o2_offset + i]; + if (data[o2_offset + i * o2_step]) { + parser->oxygen[i] = data[o2_offset + i * o2_step]; } else { parser->oxygen[i] = 21; } @@ -632,7 +640,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int idx = 0x17; if (parser->model == A300CS || parser->model == VTX || parser->model == I450T || parser->model == I750TC || - parser->model == PROPLUSX) + parser->model == PROPLUSX || parser->model == I770R) idx = 0x1f; switch (data[idx] & 0x03) { case 0: @@ -687,7 +695,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == OC1C || parser->model == OCI || parser->model == TX1 || parser->model == A300CS || parser->model == VTX || parser->model == I450T || - parser->model == I750TC || parser->model == PROPLUSX) { + parser->model == I750TC || parser->model == PROPLUSX || + parser->model == I770R) { samplesize = PAGESIZE; } @@ -865,7 +874,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == XPAIR) { temperature = ((data[offset + 7] & 0xF0) >> 4) | ((data[offset + 7] & 0x0C) << 2) | ((data[offset + 5] & 0x0C) << 4); } else if (parser->model == A300CS || parser->model == VTX || - parser->model == I750TC || parser->model == PROPLUSX) { + parser->model == I750TC || parser->model == PROPLUSX || + parser->model == I770R) { temperature = data[offset + 11]; } else { unsigned int sign; @@ -906,7 +916,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ pressure = (((data[offset + 0] & 0x03) << 8) + data[offset + 1]) * 5; else if (parser->model == TX1 || parser->model == A300CS || parser->model == VTX || parser->model == I750TC || - parser->model == PROPLUSX) + parser->model == PROPLUSX || parser->model == I770R) pressure = array_uint16_le (data + offset + 4); else pressure -= data[offset + 1]; @@ -955,7 +965,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int decostop = 0, decotime = 0; if (parser->model == A300CS || parser->model == VTX || parser->model == I450T || parser->model == I750TC || - parser->model == PROPLUSX) { + parser->model == PROPLUSX || parser->model == I770R) { decostop = (data[offset + 15] & 0x70) >> 4; decotime = array_uint16_le(data + offset + 6) & 0x03FF; have_deco = 1; @@ -999,7 +1009,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ have_rbt = 1; } else if (parser->model == I450T || parser->model == OC1A || parser->model == OC1B || parser->model == OC1C || - parser->model == OCI || parser->model == PROPLUSX) { + parser->model == OCI || parser->model == PROPLUSX || + parser->model == I770R) { rbt = array_uint16_le(data + offset + 8) & 0x01FF; have_rbt = 1; } else if (parser->model == VISION || parser->model == XPAIR || From 43303eadf631ec44fef8a9283b332bd875a19bc1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 14 Oct 2018 15:18:06 +0200 Subject: [PATCH 6/6] Add support for the Aqualung i300C --- src/descriptor.c | 1 + src/oceanic_atom2.c | 1 + src/oceanic_atom2_parser.c | 15 ++++++++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index e9f4540..794b825 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -219,6 +219,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL}, + {"Aqualung", "i300C", DC_FAMILY_OCEANIC_ATOM2, 0x4648, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i100", DC_FAMILY_OCEANIC_ATOM2, 0x464E, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i770R", DC_FAMILY_OCEANIC_ATOM2, 0x4651, DC_TRANSPORT_SERIAL, NULL}, /* Mares Nemo */ diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index debade1..950eb06 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -121,6 +121,7 @@ static const oceanic_common_version_t oceanic_atom2b_version[] = { {"AQUAI300 \0\0 512K"}, {"HOLLDG03 \0\0 512K"}, {"AQUAI100 \0\0 512K"}, + {"AQUA300C \0\0 512K"}, }; static const oceanic_common_version_t oceanic_atom2c_version[] = { diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 8a35c4f..e65cbbc 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -86,6 +86,7 @@ #define I450T 0x4641 #define I550 0x4642 #define I200 0x4646 +#define I300C 0x4648 #define I100 0x464E #define I770R 0x4651 @@ -159,7 +160,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned model == A300 || model == MANTA || model == INSIGHT2 || model == ZEN || model == I300 || model == I550 || - model == I200) { + model == I200 || model == I300C) { parser->headersize -= PAGESIZE; } else if (model == VT4 || model == VT41) { parser->headersize += PAGESIZE; @@ -276,6 +277,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim case I300: case I200: case I100: + case I300C: datetime->year = ((p[3] & 0xE0) >> 1) + (p[4] & 0x0F) + 2000; datetime->month = (p[4] & 0xF0) >> 4; datetime->day = p[3] & 0x1F; @@ -709,7 +711,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == A300 || parser->model == ZEN || parser->model == GEO || parser->model == GEO20 || parser->model == MANTA || parser->model == I300 || - parser->model == I200 || parser->model == I100) { + parser->model == I200 || parser->model == I100 || + parser->model == I300C) { have_pressure = 0; } @@ -864,7 +867,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == OC1B || parser->model == OC1C || parser->model == OCI || parser->model == A300 || parser->model == I450T || parser->model == I300 || - parser->model == I200 || parser->model == I100) { + parser->model == I200 || parser->model == I100 || + parser->model == I300C) { temperature = data[offset + 3]; } else if (parser->model == OCS || parser->model == TX1) { temperature = data[offset + 1]; @@ -934,7 +938,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == OC1B || parser->model == OC1C || parser->model == OCI || parser->model == A300 || parser->model == I450T || parser->model == I300 || - parser->model == I200 || parser->model == I100) + parser->model == I200 || parser->model == I100 || + parser->model == I300C) depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF; else if (parser->model == ATOM1) depth = data[offset + 3] * 16; @@ -985,7 +990,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } else if (parser->model == I200 || parser->model == I300 || parser->model == OC1A || parser->model == OC1B || parser->model == OC1C || parser->model == OCI || - parser->model == I100) { + parser->model == I100 || parser->model == I300C) { decostop = (data[offset + 7] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 6) & 0x0FFF; have_deco = 1;