From 3f9133def9b9e30af9b9ce98546df06910a092b5 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 31 Jan 2017 22:44:02 +0100 Subject: [PATCH 01/16] Always use the sample timestamp as the base value With a time based sample interval, the number of samples for a single timestamp should be constant. However in practice some devices occasionally store fewer samples. Since our sample time is based purely on the sample interval, it goes completely out of sync with the sample timestamp. To avoid this problem, the sample timestamp is used as the base value. For the Oceanic Pro Plus 2, this problem is very noticable. After about 115 minutes into a dive, the sample interval appears to increase to 60 seconds. Thus, without this fix, the resulting dive time for long dives is suddenly much shorter than it should be. --- src/oceanic_vtpro_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index 859025e..1f9d7aa 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -333,7 +333,7 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ // Time. if (interval) - time += interval; + time = timestamp * 60 + (i + 1) * interval; else time = timestamp * 60 + (i + 1) * 60.0 / count + 0.5; sample.time = time; From e96611cccd9be5b4f5f95125e87f13af81db4ce4 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2017 19:42:02 +0100 Subject: [PATCH 02/16] Fix the number of gas mixes The Tusa Zen supports a maximum of only 2 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 ea041a2..0b22615 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -437,6 +437,9 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == I450T) { o2_offset = 0x30; ngasmixes = 3; + } else if (parser->model == ZEN) { + o2_offset = header + 4; + ngasmixes = 2; } else { o2_offset = header + 4; ngasmixes = 3; From 24cbff9a9fab03e0d4bc1b16108b04c33a27d330 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2017 20:04:47 +0100 Subject: [PATCH 03/16] Fix a few null pointer dereferences --- src/irda.c | 6 ++++-- src/serial_posix.c | 6 ++++-- src/serial_win32.c | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/irda.c b/src/irda.c index 00b749c..02217e3 100644 --- a/src/irda.c +++ b/src/irda.c @@ -405,7 +405,7 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } struct timeval tv; @@ -449,6 +449,7 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) out: HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); +out_invalidargs: if (actual) *actual = nbytes; @@ -463,7 +464,7 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } while (nbytes < size) { @@ -485,6 +486,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); +out_invalidargs: if (actual) *actual = nbytes; diff --git a/src/serial_posix.c b/src/serial_posix.c index b545180..1a1567c 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -570,7 +570,7 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } // The total timeout. @@ -647,6 +647,7 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) out: HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); +out_invalidargs: if (actual) *actual = nbytes; @@ -661,7 +662,7 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } struct timeval tve, tvb; @@ -754,6 +755,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); +out_invalidargs: if (actual) *actual = nbytes; diff --git a/src/serial_win32.c b/src/serial_win32.c index 2d73cb9..bba78f8 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -414,7 +414,7 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } if (!ReadFile (device->hFile, data, size, &dwRead, NULL)) { @@ -431,6 +431,7 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) out: HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, dwRead); +out_invalidargs: if (actual) *actual = dwRead; @@ -445,7 +446,7 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act if (device == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } LARGE_INTEGER begin, end, freq; @@ -501,6 +502,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); +out_invalidargs: if (actual) *actual = dwWritten; From f83d156fdbecd179cfbe0dc8b57fff976abbffbc Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 14 Feb 2017 21:41:44 +0100 Subject: [PATCH 04/16] Improve the robustness of the IrDA I/O code The select system call modifies the file descriptor set, and depending on the underlying implementation also the timeout. Therefore these parameters should be re-initialized before every call. The existing code also didn't handle EINTR and EAGAIN correct. --- src/irda.c | 55 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/src/irda.c b/src/irda.c index 02217e3..2271a69 100644 --- a/src/irda.c +++ b/src/irda.c @@ -36,6 +36,7 @@ #include // irda #include // select #include // ioctl + #include #endif #include "irda.h" @@ -47,6 +48,7 @@ typedef int s_ssize_t; typedef DWORD s_errcode_t; #define S_ERRNO WSAGetLastError () +#define S_EINTR WSAEINTR #define S_EAGAIN WSAEWOULDBLOCK #define S_ENOMEM WSA_NOT_ENOUGH_MEMORY #define S_EINVAL WSAEINVAL @@ -59,6 +61,7 @@ typedef DWORD s_errcode_t; typedef ssize_t s_ssize_t; typedef int s_errcode_t; #define S_ERRNO errno +#define S_EINTR EINTR #define S_EAGAIN EAGAIN #define S_ENOMEM ENOMEM #define S_EINVAL EINVAL @@ -408,20 +411,24 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) goto out_invalidargs; } - struct timeval tv; - if (device->timeout >= 0) { - tv.tv_sec = (device->timeout / 1000); - tv.tv_usec = (device->timeout % 1000) * 1000; - } - - fd_set fds; - FD_ZERO (&fds); - FD_SET (device->fd, &fds); - while (nbytes < size) { - int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL)); + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + struct timeval tvt; + if (device->timeout > 0) { + tvt.tv_sec = (device->timeout / 1000); + tvt.tv_usec = (device->timeout % 1000) * 1000; + } else if (device->timeout == 0) { + timerclear (&tvt); + } + + int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL); if (rc < 0) { s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. SYSERROR (device->context, errcode); status = syserror(errcode); goto out; @@ -432,6 +439,8 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0); if (n < 0) { s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. SYSERROR (device->context, errcode); status = syserror(errcode); goto out; @@ -468,12 +477,32 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) } while (nbytes < size) { - s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); - if (n < 0) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + int rc = select (device->fd + 1, NULL, &fds, NULL, NULL); + if (rc < 0) { s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. SYSERROR (device->context, errcode); status = syserror(errcode); goto out; + } else if (rc == 0) { + break; // Timeout. + } + + s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); + if (n < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (n == 0) { + break; // EOF. } nbytes += n; From 3f82a553bdcd85a6a759913a637105608f6cd552 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 8 Oct 2016 21:22:30 +0200 Subject: [PATCH 05/16] Add a common ringbuffer reading algorithm Reading a ringbuffer backwards in order to process the most recent data first, is a very common operation. Nearly every dive computer backend has its own implementation. Thus with a common implementation, the amount of code duplication and complexity in the dive computer backends can be greatly reduced. The common algorithm is implemented as a simple ringbuffer stream, which takes care of all the technical details like the ringbuffer boundaries, alignment to the page size, using the optimal packet size and caching the remaining data. --- msvc/libdivecomputer.vcproj | 8 ++ src/Makefile.am | 1 + src/rbstream.c | 176 ++++++++++++++++++++++++++++++++++++ src/rbstream.h | 78 ++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 src/rbstream.c create mode 100644 src/rbstream.h diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 2f3dcdf..1e03b33 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -362,6 +362,10 @@ RelativePath="..\src\parser.c" > + + @@ -680,6 +684,10 @@ RelativePath="..\include\libdivecomputer\parser.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index c100380..db5f742 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,6 +62,7 @@ libdivecomputer_la_SOURCES = \ citizen_aqualand.c citizen_aqualand_parser.c \ divesystem_idive.c divesystem_idive_parser.c \ ringbuffer.h ringbuffer.c \ + rbstream.h rbstream.c \ checksum.h checksum.c \ array.h array.c \ buffer.c \ diff --git a/src/rbstream.c b/src/rbstream.c new file mode 100644 index 0000000..07188e0 --- /dev/null +++ b/src/rbstream.c @@ -0,0 +1,176 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 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 + +#include "rbstream.h" +#include "context-private.h" +#include "device-private.h" + +struct dc_rbstream_t { + dc_device_t *device; + unsigned int pagesize; + unsigned int packetsize; + unsigned int begin; + unsigned int end; + unsigned int address; + unsigned int available; + unsigned int skip; + unsigned char cache[]; +}; + +static unsigned int +ifloor (unsigned int x, unsigned int n) +{ + // Round down to next lower multiple. + return (x / n) * n; +} + +static unsigned int +iceil (unsigned int x, unsigned int n) +{ + // Round up to next higher multiple. + return ((x + n - 1) / n) * n; +} + +dc_status_t +dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address) +{ + dc_rbstream_t *rbstream = NULL; + + if (out == NULL || device == NULL) + return DC_STATUS_INVALIDARGS; + + // Page and packet size should be non-zero. + if (pagesize == 0 || packetsize == 0) { + ERROR (device->context, "Zero length page or packet size!"); + return DC_STATUS_INVALIDARGS; + } + + // Packet size should be a multiple of the page size. + if (packetsize % pagesize != 0) { + ERROR (device->context, "Packet size not a multiple of the page size!"); + return DC_STATUS_INVALIDARGS; + } + + // Ringbuffer boundaries should be aligned to the page size. + if (begin % pagesize != 0 || end % pagesize != 0) { + ERROR (device->context, "Ringbuffer not aligned to the page size!"); + return DC_STATUS_INVALIDARGS; + } + + // Address should be inside the ringbuffer. + if (address < begin || address > end) { + ERROR (device->context, "Address outside the ringbuffer!"); + return DC_STATUS_INVALIDARGS; + } + + // Allocate memory. + rbstream = (dc_rbstream_t *) malloc (sizeof(*rbstream) + packetsize); + if (rbstream == NULL) { + ERROR (device->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + rbstream->device = device; + rbstream->pagesize = pagesize; + rbstream->packetsize = packetsize; + rbstream->begin = begin; + rbstream->end = end; + rbstream->address = iceil(address, pagesize); + rbstream->available = 0; + rbstream->skip = rbstream->address - address; + + *out = rbstream; + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + + if (rbstream == NULL) + return DC_STATUS_INVALIDARGS; + + unsigned int address = rbstream->address; + unsigned int available = rbstream->available; + unsigned int skip = rbstream->skip; + + unsigned int nbytes = 0; + unsigned int offset = size; + while (nbytes < size) { + if (available == 0) { + // Handle the ringbuffer wrap point. + if (address == rbstream->begin) + address = rbstream->end; + + // Calculate the packet size. + unsigned int len = rbstream->packetsize; + if (rbstream->begin + len > address) + len = address - rbstream->begin; + + // Move to the begin of the current packet. + address -= len; + + // Read the packet into the cache. + rc = dc_device_read (rbstream->device, address, rbstream->cache, rbstream->packetsize); + if (rc != DC_STATUS_SUCCESS) + return rc; + + available = len - skip; + skip = 0; + } + + unsigned int length = available; + if (nbytes + length > size) + length = size - nbytes; + + offset -= length; + available -= length; + + memcpy (data + offset, rbstream->cache + available, length); + + // Update and emit a progress event. + if (progress) { + progress->current += length; + device_event_emit (rbstream->device, DC_EVENT_PROGRESS, progress); + } + + nbytes += length; + } + + rbstream->address = address; + rbstream->available = available; + rbstream->skip = skip; + + return rc; +} + +dc_status_t +dc_rbstream_free (dc_rbstream_t *rbstream) +{ + free (rbstream); + + return DC_STATUS_SUCCESS; +} diff --git a/src/rbstream.h b/src/rbstream.h new file mode 100644 index 0000000..be1d8f6 --- /dev/null +++ b/src/rbstream.h @@ -0,0 +1,78 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 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 DC_RBSTREAM_H +#define DC_RBSTREAM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Opaque object representing a ringbuffer stream. + */ +typedef struct dc_rbstream_t dc_rbstream_t; + +/** + * Create a new ringbuffer stream. + * + * @param[out] rbstream A location to store the ringbuffer stream. + * @param[in] device A valid device object. + * @param[in] pagesize The page size in bytes. + * @param[in] packetsize The packet size in bytes. + * @param[in] begin The ringbuffer begin address. + * @param[in] end The ringbuffer end address. + * @param[in] address The stream start address. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address); + +/** + * Read data from the ringbuffer stream. + * + * @param[in] rbstream A valid ringbuffer stream. + * @param[in] progress An (optional) progress event structure. + * @param[out] data The memory buffer to read the data into. + * @param[in] size The number of bytes to read. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size); + +/** + * Destroy the ringbuffer stream. + * + * @param[in] rbstream A valid ringbuffer stream. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_rbstream_free (dc_rbstream_t *rbstream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_RBSTREAM_H */ From 2646e4df86786b0b95a9f62582930c1b8e3f288b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 8 Oct 2016 21:24:14 +0200 Subject: [PATCH 06/16] Use the new ringbuffer stream All dive computer backends are updated to use the new ringbuffer stream. --- src/cressi_edy.c | 101 ++++------------- src/oceanic_common.c | 256 ++++++++++++------------------------------ src/suunto_common2.c | 107 ++++++------------ src/zeagle_n2ition3.c | 69 +++++------- 4 files changed, 160 insertions(+), 373 deletions(-) diff --git a/src/cressi_edy.c b/src/cressi_edy.c index 0dbe456..b28b423 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -31,6 +31,7 @@ #include "checksum.h" #include "array.h" #include "ringbuffer.h" +#include "rbstream.h" #define ISINSTANCE(device) dc_device_isinstance((device), &cressi_edy_device_vtable) @@ -100,20 +101,6 @@ static const cressi_edy_layout_t tusa_iq700_layout = { 0x3C, /* config */ }; -static unsigned int -ifloor (unsigned int x, unsigned int n) -{ - // Round down to next lower multiple. - return (x / n) * n; -} - -static unsigned int -iceil (unsigned int x, unsigned int n) -{ - // Round up to next higher multiple. - return ((x + n - 1) / n) * n; -} - static dc_status_t cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, int trailer) { @@ -492,41 +479,29 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v idx--; } - // Because dives are not necessary aligned to packet boundaries, and - // we always do aligned reads, there can be padding bytes present on - // both sides of the memory buffer. These extra bytes need to be - // included in the total length. - total += (previous - ifloor(previous, SZ_PACKET)) + - (iceil(eop, SZ_PACKET) - eop); - // Update and emit a progress event. progress.current += SZ_PACKET; progress.maximum = SZ_PACKET + total; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; + } + // Memory buffer for the profile data. unsigned char *buffer = (unsigned char *) malloc (total); if (buffer == NULL) { ERROR (abstract->context, "Failed to allocate memory."); + dc_rbstream_free (rbstream); return DC_STATUS_NOMEMORY; } - unsigned int available = 0; unsigned int offset = total; - // Align the ringbuffer to packet boundaries. This results in a - // virtual ringbuffer that is slightly larger than the actual - // ringbuffer. Data outside the real ringbuffer is downloaded - // and then immediately dropped. - unsigned int rb_profile_begin = ifloor(layout->rb_profile_begin, SZ_PACKET); - unsigned int rb_profile_end = iceil(layout->rb_profile_end, SZ_PACKET); - - // Align the initial memory address to the next packet boundary, and - // calculate the amount of padding bytes, so we can easily skip - // them later. - unsigned int address = iceil(eop, SZ_PACKET); - unsigned int skip = address - eop; - idx = last; previous = eop; for (unsigned int i = 0; i < count; ++i) { @@ -534,6 +509,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v unsigned int current = array_uint_le (logbook + idx * layout->rb_logbook_size, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin; if (current < layout->rb_profile_begin || current >= layout->rb_profile_end) { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x).", current); + dc_rbstream_free (rbstream); free(buffer); return DC_STATUS_DATAFORMAT; } @@ -541,51 +517,19 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v // Get the profile length. unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end); - unsigned nbytes = available; - while (nbytes < length) { - if (address == rb_profile_begin) - address = rb_profile_end; - address -= SZ_PACKET; + // Move to the begin of the current dive. + offset -= length; - // Read the memory page. - unsigned char packet[SZ_PACKET]; - rc = cressi_edy_device_read (abstract, address, packet, sizeof(packet)); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory page."); - free(buffer); - return rc; - } - - // At the head and tail of the ringbuffer, the packet can - // contain extra data, originating from the larger virtual - // ringbuffer. This data must be removed from the packet. - unsigned int head = 0; - unsigned int tail = 0; - unsigned int len = SZ_PACKET; - if (address < layout->rb_profile_begin) { - head = layout->rb_profile_begin - address; - } - if (address + SZ_PACKET > layout->rb_profile_end) { - tail = (address + SZ_PACKET) - layout->rb_profile_end; - } - len -= head + tail; - offset -= len; - - // Copy the data packet to the buffer. - memcpy(buffer + offset, packet + head, len); - - // Update and emit a progress event. - progress.current += len; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - nbytes += len - skip; - skip = 0; + // Read the dive. + rc = dc_rbstream_read (rbstream, &progress, buffer + offset, length); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (buffer); + return rc; } - available = nbytes - length; - previous = current; - - unsigned char *p = buffer + offset + available; + unsigned char *p = buffer + offset; if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) break; @@ -593,11 +537,14 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata)) break; + previous = current; + if (idx == layout->rb_logbook_begin) idx = layout->rb_logbook_end; idx--; } + dc_rbstream_free (rbstream); free(buffer); return DC_STATUS_SUCCESS; diff --git a/src/oceanic_common.c b/src/oceanic_common.c index a463038..427b326 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -27,6 +27,7 @@ #include "context-private.h" #include "device-private.h" #include "ringbuffer.h" +#include "rbstream.h" #include "array.h" #define VTABLE(abstract) ((oceanic_common_device_vtable_t *) abstract->vtable) @@ -39,22 +40,6 @@ #define INVALID 0 -static unsigned int -ifloor (unsigned int x, unsigned int n) -{ - // Round down to next lower multiple. - return (x / n) * n; -} - - -static unsigned int -iceil (unsigned int x, unsigned int n) -{ - // Round up to next higher multiple. - return ((x + n - 1) / n) * n; -} - - static unsigned int get_profile_first (const unsigned char data[], const oceanic_common_layout_t *layout) { @@ -234,177 +219,94 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr return DC_STATUS_DATAFORMAT; } - // Convert the first/last pointers to begin/end/count pointers. - unsigned int rb_logbook_entry_begin, rb_logbook_entry_end, - rb_logbook_entry_size; + // Calculate the end pointer and the number of bytes. + unsigned int rb_logbook_end, rb_logbook_size; if (layout->pt_mode_global == 0) { - rb_logbook_entry_begin = rb_logbook_first; - rb_logbook_entry_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout); - rb_logbook_entry_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size; + rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout); + rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size; } else { - rb_logbook_entry_begin = rb_logbook_first; - rb_logbook_entry_end = rb_logbook_last; - rb_logbook_entry_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout); + rb_logbook_end = rb_logbook_last; + rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout); // In a typical ringbuffer implementation with only two begin/end // pointers, there is no distinction possible between an empty and // a full ringbuffer. We always consider the ringbuffer full in // that case, because an empty ringbuffer can be detected by // inspecting the logbook entries once they are downloaded. if (rb_logbook_first == rb_logbook_last) - rb_logbook_entry_size = layout->rb_logbook_end - layout->rb_logbook_begin; + rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; } - // Check whether the ringbuffer is full. - int full = (rb_logbook_entry_size == (layout->rb_logbook_end - layout->rb_logbook_begin)); - - // Align the pointers to page boundaries. - unsigned int rb_logbook_page_begin, rb_logbook_page_end, - rb_logbook_page_size; - if (full) { - // Full ringbuffer. - rb_logbook_page_begin = iceil (rb_logbook_entry_end, PAGESIZE); - rb_logbook_page_end = rb_logbook_page_begin; - rb_logbook_page_size = rb_logbook_entry_size; - } else { - // Non-full ringbuffer. - rb_logbook_page_begin = ifloor (rb_logbook_entry_begin, PAGESIZE); - rb_logbook_page_end = iceil (rb_logbook_entry_end, PAGESIZE); - rb_logbook_page_size = rb_logbook_entry_size + - (rb_logbook_entry_begin - rb_logbook_page_begin) + - (rb_logbook_page_end - rb_logbook_entry_end); - } - - // Check whether the last entry is not aligned to a page boundary. - int unaligned = (rb_logbook_entry_end != rb_logbook_page_end); - // Update and emit a progress event. progress->current += PAGESIZE; progress->maximum += PAGESIZE; - progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_page_size; + progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_size; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); // Exit if there are no dives. - if (rb_logbook_page_size == 0) { + if (rb_logbook_size == 0) { return DC_STATUS_SUCCESS; } // Allocate memory for the logbook entries. - if (!dc_buffer_resize (logbook, rb_logbook_page_size)) + if (!dc_buffer_resize (logbook, rb_logbook_size)) return DC_STATUS_NOMEMORY; // Cache the logbook pointer. unsigned char *logbooks = dc_buffer_get_data (logbook); - // Since entries are not necessary aligned on page boundaries, - // the memory buffer may contain padding entries on both sides. - // The memory area which contains the valid entries is marked - // with a number of additional variables. - unsigned int begin = 0; - unsigned int end = rb_logbook_page_size; - if (!full) { - begin += rb_logbook_entry_begin - rb_logbook_page_begin; - end -= rb_logbook_page_end - rb_logbook_entry_end; + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_logbook_begin, layout->rb_logbook_end, rb_logbook_end); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; } // The logbook ringbuffer is read backwards to retrieve the most recent // entries first. If an already downloaded entry is identified (by means // of its fingerprint), the transfer is aborted immediately to reduce - // the transfer time. When necessary, padding entries are downloaded - // (but not processed) to align all read requests on page boundaries. + // the transfer time. unsigned int nbytes = 0; - unsigned int current = end; - unsigned int offset = rb_logbook_page_size; - unsigned int address = rb_logbook_page_end; - while (nbytes < rb_logbook_page_size) { - // Handle the ringbuffer wrap point. - if (address == layout->rb_logbook_begin) - address = layout->rb_logbook_end; + unsigned int offset = rb_logbook_size; + while (nbytes < rb_logbook_size) { + // Move to the start of the current entry. + offset -= layout->rb_logbook_entry_size; - // Calculate the optimal packet size. - unsigned int len = PAGESIZE * device->multipage; - if (layout->rb_logbook_begin + len > address) - len = address - layout->rb_logbook_begin; // End of ringbuffer. - if (nbytes + len > rb_logbook_page_size) - len = rb_logbook_page_size - nbytes; // End of logbooks. - - // Move to the start of the current page. - address -= len; - offset -= len; - - // Read the logbook page. - rc = dc_device_read (abstract, address, logbooks + offset, len); + // Read the logbook entry. + rc = dc_rbstream_read (rbstream, progress, logbooks + offset, layout->rb_logbook_entry_size); if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory page."); + ERROR (abstract->context, "Failed to read the memory."); + dc_rbstream_free (rbstream); return rc; } - // Update and emit a progress event. - progress->current += len; - device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + nbytes += layout->rb_logbook_entry_size; - // A full ringbuffer needs some special treatment to avoid - // having to download the first/last page twice. When a full - // ringbuffer is not aligned to page boundaries, this page - // will contain both the most recent and oldest entry. - if (full && unaligned) { - if (nbytes == 0) { - // After downloading the first page, move both the oldest - // and most recent entries to their correct location. - unsigned int oldest = rb_logbook_page_end - rb_logbook_entry_end; - unsigned int newest = len - oldest; - // Move the oldest entries down to the start of the buffer. - memcpy (logbooks, logbooks + offset + newest, oldest); - // Move the newest entries up to the end of the buffer. - memmove (logbooks + offset + oldest, logbooks + offset, newest); - // Adjust the current page offset to the new position. - offset += oldest; - } else if (nbytes + len == rb_logbook_page_size) { - // After downloading the last page, pretend we have also - // downloaded those oldest entries from the first page. - offset = 0; - } - } - - nbytes += len; - - // Process the logbook entries. - int abort = 0; - while (current >= offset + layout->rb_logbook_entry_size && - current != offset && current != begin) - { - // Move to the start of the current entry. - current -= layout->rb_logbook_entry_size; - - // Check for uninitialized entries. Normally, such entries are - // never present, except when the ringbuffer is actually empty, - // but the ringbuffer pointers are not set to their empty values. - // This appears to happen on some devices, and we attempt to - // fix this here. - if (array_isequal (logbooks + current, layout->rb_logbook_entry_size, 0xFF)) { - WARNING (abstract->context, "Uninitialized logbook entries detected!"); - begin = current + layout->rb_logbook_entry_size; - abort = 1; - break; - } - - // Compare the fingerprint to identify previously downloaded entries. - if (memcmp (logbooks + current, device->fingerprint, layout->rb_logbook_entry_size) == 0) { - begin = current + layout->rb_logbook_entry_size; - abort = 1; - break; - } - } - - // Stop reading pages too. - if (abort) + // Check for uninitialized entries. Normally, such entries are + // never present, except when the ringbuffer is actually empty, + // but the ringbuffer pointers are not set to their empty values. + // This appears to happen on some devices, and we attempt to + // fix this here. + if (array_isequal (logbooks + offset, layout->rb_logbook_entry_size, 0xFF)) { + WARNING (abstract->context, "Uninitialized logbook entries detected!"); + offset += layout->rb_logbook_entry_size; break; + } + + // Compare the fingerprint to identify previously downloaded entries. + if (memcmp (logbooks + offset, device->fingerprint, layout->rb_logbook_entry_size) == 0) { + offset += layout->rb_logbook_entry_size; + break; + } } // Update and emit a progress event. - progress->maximum -= rb_logbook_page_size - nbytes; + progress->maximum -= rb_logbook_size - nbytes; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); - dc_buffer_slice (logbook, begin, end - begin); + dc_buffer_slice (logbook, offset, rb_logbook_size - offset); + + dc_rbstream_free (rbstream); return DC_STATUS_SUCCESS; } @@ -492,20 +394,24 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr progress->maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; + } + // Memory buffer for the profile data. unsigned char *profiles = (unsigned char *) malloc (rb_profile_size + rb_logbook_size); if (profiles == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + dc_rbstream_free (rbstream); return DC_STATUS_NOMEMORY; } // Keep track of the current position. unsigned int offset = rb_profile_size + rb_logbook_size; - unsigned int address = rb_profile_end; - - // When using multipage reads, the last packet can contain data from more - // than one dive. Therefore, the remaining data of this package (and its - // size) needs to be preserved for the next dive. - unsigned int available = 0; // Traverse the logbook ringbuffer backwards to retrieve the most recent // dives first. The logbook ringbuffer is linearized at this point, so @@ -528,6 +434,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).", rb_entry_first, rb_entry_last); + dc_rbstream_free (rbstream); free (profiles); return DC_STATUS_DATAFORMAT; } @@ -549,56 +456,33 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr break; } - // Read the profile data. - unsigned int nbytes = available; - while (nbytes < rb_entry_size + gap) { - // Handle the ringbuffer wrap point. - if (address == layout->rb_profile_begin) - address = layout->rb_profile_end; + // Move to the start of the current dive. + offset -= rb_entry_size + gap; - // Calculate the optimal packet size. - unsigned int len = PAGESIZE * device->multipage; - if (layout->rb_profile_begin + len > address) - len = address - layout->rb_profile_begin; // End of ringbuffer. - if (nbytes + len > remaining) - len = remaining - nbytes; // End of profile. - - // Move to the start of the current page. - address -= len; - offset -= len; - - // Read the profile page. - rc = dc_device_read (abstract, address, profiles + offset, len); - if (rc != DC_STATUS_SUCCESS) { - free (profiles); - return rc; - } - - // Update and emit a progress event. - progress->current += len; - device_event_emit (abstract, DC_EVENT_PROGRESS, progress); - - nbytes += len; + // Read the dive. + rc = dc_rbstream_read (rbstream, progress, profiles + offset, rb_entry_size + gap); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (profiles); + return rc; } - available = nbytes - (rb_entry_size + gap); remaining -= rb_entry_size + gap; previous = rb_entry_first; // Prepend the logbook entry to the profile data. The memory buffer is - // large enough to store this entry, but any data that belongs to the - // next dive needs to be moved down first. - if (available) - memmove (profiles + offset - layout->rb_logbook_entry_size, profiles + offset, available); + // large enough to store this entry. offset -= layout->rb_logbook_entry_size; - memcpy (profiles + offset + available, logbooks + entry, layout->rb_logbook_entry_size); + memcpy (profiles + offset, logbooks + entry, layout->rb_logbook_entry_size); - unsigned char *p = profiles + offset + available; + unsigned char *p = profiles + offset; if (callback && !callback (p, rb_entry_size + layout->rb_logbook_entry_size, p, layout->rb_logbook_entry_size, userdata)) { break; } } + dc_rbstream_free (rbstream); free (profiles); return DC_STATUS_SUCCESS; diff --git a/src/suunto_common2.c b/src/suunto_common2.c index 2abe386..80b2477 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -26,6 +26,7 @@ #include "context-private.h" #include "suunto_common2.h" #include "ringbuffer.h" +#include "rbstream.h" #include "checksum.h" #include "array.h" @@ -294,102 +295,59 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac return DC_STATUS_DATAFORMAT; } - // Memory buffer to store all the dives. - - unsigned char *data = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin + SZ_MINIMUM); - if (data == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - // Calculate the total amount of bytes. - unsigned int 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; progress.current += sizeof (header); device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - // To reduce the number of read operations, we always try to read - // packages with the largest possible size. As a consequence, the - // last package of a dive can contain data from more than one dive. - // Therefore, the remaining data of this package (and its size) - // needs to be preserved for the next dive. + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, end); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; + } - unsigned int available = 0; + // Memory buffer to store all the dives. + unsigned char *data = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin); + if (data == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + dc_rbstream_free (rbstream); + return DC_STATUS_NOMEMORY; + } // The ring buffer is traversed backwards to retrieve the most recent // dives first. This allows us to download only the new dives. - unsigned int current = last; unsigned int previous = end; - unsigned int address = previous; - unsigned int offset = remaining + SZ_MINIMUM; - while (remaining) { + unsigned int offset = remaining; + while (offset) { // Calculate the size of the current dive. unsigned int size = RB_PROFILE_DISTANCE (layout, current, previous, 1); - if (size < 4 || size > remaining) { - ERROR (abstract->context, "Unexpected profile size (%u %u).", size, remaining); + if (size < 4 || size > offset) { + ERROR (abstract->context, "Unexpected profile size (%u %u).", size, offset); + dc_rbstream_free (rbstream); free (data); return DC_STATUS_DATAFORMAT; } - unsigned int nbytes = available; - while (nbytes < size) { - // Handle the ringbuffer wrap point. - if (address == layout->rb_profile_begin) - address = layout->rb_profile_end; + // Move to the begin of the current dive. + offset -= size; - // Calculate the package size. Try with the largest possible - // size first, and adjust when the end of the ringbuffer or - // the end of the profile data is reached. - unsigned int len = SZ_PACKET; - if (layout->rb_profile_begin + len > address) - len = address - layout->rb_profile_begin; // End of ringbuffer. - if (nbytes + len > remaining) - len = remaining - nbytes; // End of profile. - /*if (nbytes + len > size) - len = size - nbytes;*/ // End of dive (for testing only). - - // Move to the begin of the current package. - offset -= len; - address -= len; - - // Always read at least the minimum amount of bytes, because - // reading fewer bytes is unreliable. The memory buffer is - // large enough to prevent buffer overflows, and the extra - // bytes are automatically ignored (due to reading backwards). - unsigned int extra = 0; - if (len < SZ_MINIMUM) - extra = SZ_MINIMUM - len; - - // Read the package. - rc = suunto_common2_device_read (abstract, address - extra, data + offset - extra, len + extra); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory."); - free (data); - return rc; - } - - // Update and emit a progress event. - progress.current += len; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Next package. - nbytes += len; + // Read the dive. + rc = dc_rbstream_read (rbstream, &progress, data + offset, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (data); + return rc; } - // The last package of the current dive contains the previous and - // next pointers (in a continuous memory area). It can also contain - // a number of bytes from the next dive. - - remaining -= size; - available = nbytes - size; - - unsigned char *p = data + offset + available; + unsigned char *p = data + offset; unsigned int prev = array_uint16_le (p + 0); unsigned int next = array_uint16_le (p + 2); if (prev < layout->rb_profile_begin || @@ -398,11 +356,13 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac next >= layout->rb_profile_end) { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x 0x%04x).", prev, next); + dc_rbstream_free (rbstream); free (data); return DC_STATUS_DATAFORMAT; } if (next != previous && next != current) { ERROR (abstract->context, "Profiles are not continuous (0x%04x 0x%04x 0x%04x).", current, next, previous); + dc_rbstream_free (rbstream); free (data); return DC_STATUS_DATAFORMAT; } @@ -410,11 +370,13 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac if (next != current) { unsigned int fp_offset = layout->fingerprint + 4; if (memcmp (p + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) { + dc_rbstream_free (rbstream); free (data); return DC_STATUS_SUCCESS; } if (callback && !callback (p + 4, size - 4, p + fp_offset, sizeof (device->fingerprint), userdata)) { + dc_rbstream_free (rbstream); free (data); return DC_STATUS_SUCCESS; } @@ -428,6 +390,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac current = prev; } + dc_rbstream_free (rbstream); free (data); return status; diff --git a/src/zeagle_n2ition3.c b/src/zeagle_n2ition3.c index 516dd57..860238f 100644 --- a/src/zeagle_n2ition3.c +++ b/src/zeagle_n2ition3.c @@ -31,6 +31,7 @@ #include "checksum.h" #include "array.h" #include "ringbuffer.h" +#include "rbstream.h" #define ISINSTANCE(device) dc_device_isinstance((device), &zeagle_n2ition3_device_vtable) @@ -352,16 +353,21 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba progress.maximum = (RB_LOGBOOK_END - RB_LOGBOOK_BEGIN) * 2 + 8 + total; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, RB_PROFILE_BEGIN, RB_PROFILE_END, eop); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; + } + // Memory buffer for the profile data. unsigned char buffer[RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; - unsigned int available = 0; - unsigned int remaining = total; unsigned int offset = RB_PROFILE_END - RB_PROFILE_BEGIN; idx = last; previous = eop; - unsigned int address = previous; for (unsigned int i = 0; i < count; ++i) { // Get the pointer to the profile data. unsigned int current = array_uint16_le (config + 2 * idx); @@ -369,50 +375,37 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba // Get the profile length. unsigned int length = ringbuffer_distance (current, previous, 1, RB_PROFILE_BEGIN, RB_PROFILE_END); - unsigned nbytes = available; - while (nbytes < length) { - if (address == RB_PROFILE_BEGIN) - address = RB_PROFILE_END; + // Move to the begin of the current dive. + offset -= length; - unsigned int len = SZ_PACKET; - if (RB_PROFILE_BEGIN + len > address) - len = address - RB_PROFILE_BEGIN; // End of ringbuffer. - if (nbytes + len > remaining) - len = remaining - nbytes; // End of profile. - - address -= len; - offset -= len; - - // Read the memory page. - rc = zeagle_n2ition3_device_read (abstract, address, buffer + offset, len); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory page."); - return rc; - } - - // Update and emit a progress event. - progress.current += len; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - nbytes += len; + // Read the dive. + rc = dc_rbstream_read (rbstream, &progress, buffer + offset, length); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + return rc; + } + + unsigned char *p = buffer + offset; + + if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) { + dc_rbstream_free (rbstream); + return DC_STATUS_SUCCESS; + } + + if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata)) { + dc_rbstream_free (rbstream); + return DC_STATUS_SUCCESS; } - remaining -= length; - available = nbytes - length; previous = current; - unsigned char *p = buffer + offset + available; - - if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) - return DC_STATUS_SUCCESS; - - if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata)) - return DC_STATUS_SUCCESS; - if (idx == RB_LOGBOOK_BEGIN) idx = RB_LOGBOOK_END; idx--; } + dc_rbstream_free (rbstream); + return DC_STATUS_SUCCESS; } From 8735071fac481b1209a74545d621cf2fbf1f73bc Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 14 Feb 2017 22:12:06 +0100 Subject: [PATCH 07/16] Use a more efficient download algorithm The current algorithm always downloads a full memory dump, and extracts the dives afterwards. For the typical scenario where only a few dives are being downloaded, this is inefficient because most of the data isn't needed. This can easily be avoided by downloading the data on the fly. --- include/libdivecomputer/mares_iconhd.h | 3 - src/libdivecomputer.symbols | 1 - src/mares_iconhd.c | 142 ++++++++++++++++--------- 3 files changed, 91 insertions(+), 55 deletions(-) diff --git a/include/libdivecomputer/mares_iconhd.h b/include/libdivecomputer/mares_iconhd.h index 38f82b3..ce0c8c6 100644 --- a/include/libdivecomputer/mares_iconhd.h +++ b/include/libdivecomputer/mares_iconhd.h @@ -33,9 +33,6 @@ extern "C" { dc_status_t mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model); -dc_status_t -mares_iconhd_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); - dc_status_t mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index acadda3..5a2c18b 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -97,7 +97,6 @@ mares_puck_extract_dives mares_darwin_device_open mares_darwin_extract_dives mares_iconhd_device_open -mares_iconhd_extract_dives oceanic_atom2_device_open oceanic_atom2_device_open2 oceanic_atom2_device_version diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 9c33307..28e7717 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -29,6 +29,7 @@ #include "device-private.h" #include "serial.h" #include "array.h" +#include "rbstream.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) @@ -414,51 +415,46 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { + dc_status_t rc = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; - dc_buffer_t *buffer = dc_buffer_new (device->layout->memsize); - if (buffer == NULL) - return DC_STATUS_NOMEMORY; - - dc_status_t rc = mares_iconhd_device_dump (abstract, buffer); - if (rc != DC_STATUS_SUCCESS) { - dc_buffer_free (buffer); - 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_uint32_le (data + 0x0C); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - - rc = mares_iconhd_extract_dives (abstract, dc_buffer_get_data (buffer), - dc_buffer_get_size (buffer), callback, userdata); - - dc_buffer_free (buffer); - - return rc; -} - - -dc_status_t -mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) -{ - mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; - dc_context_t *context = (abstract ? abstract->context : NULL); - if (!ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; const mares_iconhd_layout_t *layout = device->layout; - if (size < layout->memsize) - return DC_STATUS_DATAFORMAT; + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = layout->rb_profile_end - layout->rb_profile_begin + 4; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit a vendor event. + dc_event_vendor_t vendor; + vendor.data = device->version; + vendor.size = sizeof (device->version); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Read the serial number. + unsigned char serial[4] = {0}; + rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory."); + return rc; + } + + // Update and emit a progress event. + progress.current += sizeof (serial); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (serial); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Get the model code. - unsigned int model = device ? device->model : data[0]; + unsigned int model = device->model; // Get the corresponding dive header size. unsigned int header = 0x5C; @@ -473,29 +469,57 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u unsigned int eop = 0; const unsigned int config[] = {0x2001, 0x3001}; for (unsigned int i = 0; i < sizeof (config) / sizeof (*config); ++i) { - eop = array_uint32_le (data + config[i]); + // Read the pointer. + unsigned char pointer[4] = {0}; + rc = mares_iconhd_device_read (abstract, config[i], pointer, sizeof (pointer)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory."); + return rc; + } + + // Update and emit a progress event. + progress.maximum += sizeof (pointer); + progress.current += sizeof (pointer); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + eop = array_uint32_le (pointer); if (eop != 0xFFFFFFFF) break; } if (eop < layout->rb_profile_begin || eop >= layout->rb_profile_end) { if (eop == 0xFFFFFFFF) return DC_STATUS_SUCCESS; // No dives available. - ERROR (context, "Ringbuffer pointer out of range (0x%08x).", eop); + ERROR (abstract->context, "Ringbuffer pointer out of range (0x%08x).", eop); return DC_STATUS_DATAFORMAT; } - // Make the ringbuffer linear, to avoid having to deal with the wrap point. + // Create the ringbuffer stream. + dc_rbstream_t *rbstream = NULL; + rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + return rc; + } + + // Allocate memory for the dives. unsigned char *buffer = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin); if (buffer == NULL) { - ERROR (context, "Failed to allocate memory."); + ERROR (abstract->context, "Failed to allocate memory."); + dc_rbstream_free (rbstream); return DC_STATUS_NOMEMORY; } - memcpy (buffer + 0, data + eop, layout->rb_profile_end - eop); - memcpy (buffer + layout->rb_profile_end - eop, data + layout->rb_profile_begin, eop - layout->rb_profile_begin); - unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin; while (offset >= header + 4) { + // Read the first part of the dive header. + rc = dc_rbstream_read (rbstream, &progress, buffer + offset - header, header); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (buffer); + return rc; + } + // Get the number of samples in the profile data. unsigned int type = 0, nsamples = 0; if (model == SMART || model == SMARTAPNEA) { @@ -533,6 +557,17 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u samplesize = 14; fingerprint = 0x40; } + if (offset < headersize) + break; + + // Read the second part of the dive header. + rc = dc_rbstream_read (rbstream, &progress, buffer + offset - headersize, headersize - header); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (buffer); + return rc; + } // Calculate the total number of bytes for this dive. // If the buffer does not contain that much bytes, we reached the @@ -542,9 +577,6 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u if (model == ICONHDNET) { nbytes += (nsamples / 4) * 8; } else if (model == SMARTAPNEA) { - if (offset < headersize) - break; - unsigned int settings = array_uint16_le (buffer + offset - headersize + 0x1C); unsigned int divetime = array_uint32_le (buffer + offset - headersize + 0x24); unsigned int samplerate = 1 << ((settings >> 9) & 0x03); @@ -554,6 +586,15 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u if (offset < nbytes) break; + // Read the remainder of the dive. + rc = dc_rbstream_read (rbstream, &progress, buffer + offset - nbytes, nbytes - headersize); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + dc_rbstream_free (rbstream); + free (buffer); + return rc; + } + // Move to the start of the dive. offset -= nbytes; @@ -566,17 +607,16 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u unsigned char *fp = buffer + offset + length - headersize + fingerprint; if (device && memcmp (fp, device->fingerprint, sizeof (device->fingerprint)) == 0) { - free (buffer); - return DC_STATUS_SUCCESS; + break; } if (callback && !callback (buffer + offset, length, fp, sizeof (device->fingerprint), userdata)) { - free (buffer); - return DC_STATUS_SUCCESS; + break; } } + dc_rbstream_free (rbstream); free (buffer); - return DC_STATUS_SUCCESS; + return rc; } From 57ffb2ba7ae83f714fdd49c9622323c3b732d153 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 7 Mar 2017 21:38:22 +0100 Subject: [PATCH 08/16] Fix some more null pointer dereferences --- src/cressi_leonardo.c | 2 +- src/usbhid.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index 7e1df90..5259fe5 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -498,7 +498,7 @@ cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[] } if (previous && previous != footer + 2) { - ERROR (abstract->context, "Profiles are not continuous (0x%04x 0x%04x 0x%04x).", header, footer, previous); + ERROR (context, "Profiles are not continuous (0x%04x 0x%04x 0x%04x).", header, footer, previous); free (buffer); return DC_STATUS_DATAFORMAT; } diff --git a/src/usbhid.c b/src/usbhid.c index 44c0886..0a93824 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -346,7 +346,7 @@ dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual) if (usbhid == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } #if defined(HAVE_LIBUSB) && !defined(__APPLE__) @@ -369,6 +369,7 @@ dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual) out: HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); +out_invalidargs: if (actual) *actual = nbytes; @@ -387,7 +388,7 @@ dc_usbhid_write (dc_usbhid_t *usbhid, const void *data, size_t size, size_t *act if (usbhid == NULL) { status = DC_STATUS_INVALIDARGS; - goto out; + goto out_invalidargs; } #if defined(HAVE_LIBUSB) && !defined(__APPLE__) @@ -410,6 +411,7 @@ dc_usbhid_write (dc_usbhid_t *usbhid, const void *data, size_t size, size_t *act out: HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); +out_invalidargs: if (actual) *actual = nbytes; From c5fac27bc8a14acebe85adea47dbc92e405bee3a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 7 Mar 2017 22:17:52 +0100 Subject: [PATCH 09/16] Fix some compiler warnings --- src/suunto_eonsteel.c | 2 +- src/suunto_eonsteel_parser.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 4f48bc8..213f27a 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -140,7 +140,7 @@ static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer, return -1; } if (transferred != PACKET_SIZE) { - ERROR(eon->base.context, "incomplete read interrupt transfer (got %d, expected %d)", transferred, PACKET_SIZE); + ERROR(eon->base.context, "incomplete read interrupt transfer (got %zu, expected %d)", transferred, PACKET_SIZE); return -1; } if (buf[0] != 0x3f) { diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index 72c0dd5..653ab5e 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -239,7 +239,7 @@ static int fill_in_group_details(suunto_eonsteel_parser_t *eon, struct type_desc } base = eon->type_desc + index; if (!base->desc) { - ERROR(eon->base.context, "Group type descriptor '%s' has undescribed index %d", desc->desc, index); + ERROR(eon->base.context, "Group type descriptor '%s' has undescribed index %ld", desc->desc, index); break; } if (!base->size) { @@ -267,7 +267,7 @@ static int fill_in_group_details(suunto_eonsteel_parser_t *eon, struct type_desc grp = end+1; continue; default: - ERROR(eon->base.context, "Group type descriptor '%s' has unparseable index %d", desc->desc, index); + ERROR(eon->base.context, "Group type descriptor '%s' has unparseable index %ld", desc->desc, index); return -1; } } From b1ba3fa0c65ebb04fddfe85669f1f91ad34afd4f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 26 Mar 2017 21:26:48 +0200 Subject: [PATCH 10/16] Mark the private function as static --- src/shearwater_predator_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 2635457..c3a49c8 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -104,7 +104,7 @@ shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned } -dc_status_t +static dc_status_t shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int petrel) { shearwater_predator_parser_t *parser = NULL; From 640308c0763ae263f2e4231b805794de585a6fc6 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 26 Mar 2017 18:17:05 +0200 Subject: [PATCH 11/16] Disable freedive mode for the Uwatec Aladin Tec 2G The Uwatec Aladin Tec 2G doesn't support freedive mode. This appears to be a bug in SmartTrak and LogTrak. They both report gauge and air/nitrox dives as apnea dives. --- src/uwatec_smart_parser.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 71d69a0..8c6ce24 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -59,8 +59,7 @@ #define FRESH 1.000 #define SALT 1.025 -#define FREEDIVE1 0x00000080 -#define FREEDIVE2 0x00000200 +#define FREEDIVE 0x00000080 #define GAUGE 0x00001000 #define SALINITY 0x00100000 @@ -451,12 +450,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) // Get the freedive/gauge bits. unsigned int freedive = 0; unsigned int gauge = (settings & GAUGE) != 0; - if (parser->model == ALADINTEC) { - freedive = 0; - } else if (parser->model == ALADINTEC2G) { - freedive = (settings & FREEDIVE2) != 0; - } else { - freedive = (settings & FREEDIVE1) != 0; + if (parser->model != ALADINTEC && parser->model != ALADINTEC2G) { + freedive = (settings & FREEDIVE) != 0; } // Get the dive mode. The freedive bit needs to be checked From c5d5220e287cf55ef8b043990c9c403c04315509 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 27 Mar 2017 19:17:29 +0200 Subject: [PATCH 12/16] Fix a bug in the tank pressure samples The number of tank pressure sensors is not necessary equal to the number of gas mixes. Take for example a dive with two gas mixes, but only a single tank pressure sensor attached to one of the two tanks. Because the tank index is shared with the gas mix index, it will refer to a non-existing sensor when switching to a tank without a pressure sensor attached. The invalid tank index should not be considered a fatal error. The tank pressure values should be ignored instead. The device appears to record zero values anyway, except for the first value immediately after the gas switch. I suspect that's caused by the fact that the pressure is only recorded every 4 samples, and therefore the last pressure value is reported with a small delay. --- src/mares_iconhd_parser.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index 534d2ca..8e356be 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -603,15 +603,13 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t // Some extra data. if (parser->model == ICONHDNET && (nsamples % 4) == 0) { // Pressure (1/100 bar). - if (parser->ntanks > 0) { - unsigned int pressure = array_uint16_le(data + offset); - if (gasmix >= parser->ntanks) { - ERROR (abstract->context, "Invalid tank index."); - return DC_STATUS_DATAFORMAT; - } + unsigned int pressure = array_uint16_le(data + offset); + if (gasmix < parser->ntanks) { sample.pressure.tank = gasmix; sample.pressure.value = pressure / 100.0; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + } else if (pressure != 0) { + WARNING (abstract->context, "Invalid tank with non-zero pressure."); } offset += 8; From 0609a4c80a83fd0a7a7aa80b3846b2664ff8d9d6 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 28 Mar 2017 20:45:12 +0200 Subject: [PATCH 13/16] Mark the private function as static --- src/hw_ostc_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 6d88139..9ac9c52 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -274,7 +274,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) return DC_STATUS_SUCCESS; } -dc_status_t +static dc_status_t hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int hwos, unsigned int model) { hw_ostc_parser_t *parser = NULL; From e2c020d4c722cf23494713c109ba9c125d8b36c2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 24 Mar 2017 23:10:16 +0100 Subject: [PATCH 14/16] Report errors from the close function --- src/cressi_edy.c | 5 ++++- src/diverite_nitekq.c | 5 ++++- src/oceanic_atom2.c | 5 ++++- src/oceanic_veo250.c | 5 ++++- src/oceanic_vtpro.c | 5 ++++- src/reefnet_sensus.c | 9 ++++++--- src/shearwater_petrel.c | 14 ++++++++++++-- 7 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/cressi_edy.c b/src/cressi_edy.c index b28b423..9270b92 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -320,7 +320,10 @@ cressi_edy_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Send the quit command. - cressi_edy_quit (device); + rc = cressi_edy_quit (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/diverite_nitekq.c b/src/diverite_nitekq.c index 30060cd..1565779 100644 --- a/src/diverite_nitekq.c +++ b/src/diverite_nitekq.c @@ -217,7 +217,10 @@ diverite_nitekq_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Disconnect. - diverite_nitekq_send (device, DISCONNECT); + rc = diverite_nitekq_send (device, DISCONNECT); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 01e4fd4..34cc592 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -714,7 +714,10 @@ oceanic_atom2_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Send the quit command. - oceanic_atom2_quit (device); + rc = oceanic_atom2_quit (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c index 9db215c..033081d 100644 --- a/src/oceanic_veo250.c +++ b/src/oceanic_veo250.c @@ -337,7 +337,10 @@ oceanic_veo250_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Switch the device back to surface mode. - oceanic_veo250_quit (device); + rc = oceanic_veo250_quit (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index 22d042b..3128079 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -517,7 +517,10 @@ oceanic_vtpro_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Switch the device back to surface mode. - oceanic_vtpro_quit (device); + rc = oceanic_vtpro_quit (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/reefnet_sensus.c b/src/reefnet_sensus.c index 51e9a7f..09c5266 100644 --- a/src/reefnet_sensus.c +++ b/src/reefnet_sensus.c @@ -152,9 +152,12 @@ reefnet_sensus_device_close (dc_device_t *abstract) // Safely close the connection if the last handshake was // successful, but no data transfer was ever initiated. - if (device->waiting) - reefnet_sensus_cancel (device); - + if (device->waiting) { + rc = reefnet_sensus_cancel (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } + } // Close the device. rc = dc_serial_close (device->port); diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index b791388..13e1496 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -115,14 +115,24 @@ error_free: static dc_status_t shearwater_petrel_device_close (dc_device_t *abstract) { + dc_status_t status = DC_STATUS_SUCCESS; shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; // Shutdown the device. unsigned char request[] = {0x2E, 0x90, 0x20, 0x00}; - shearwater_common_transfer (device, request, sizeof (request), NULL, 0, NULL); + rc = shearwater_common_transfer (device, request, sizeof (request), NULL, 0, NULL); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } // Close the device. - return shearwater_common_close (device); + rc = shearwater_common_close (device); + if (rc != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } + + return status; } From bfd7301945f430c0ffefbf3a745bf8bb8639702b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Mar 2017 23:51:14 +0200 Subject: [PATCH 15/16] Replace the deco events with a deco sample --- src/mares_darwin_parser.c | 11 ++++++----- src/mares_nemo_parser.c | 11 ++++++----- src/uwatec_memomouse_parser.c | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/mares_darwin_parser.c b/src/mares_darwin_parser.c index ac2c16f..73963e5 100644 --- a/src/mares_darwin_parser.c +++ b/src/mares_darwin_parser.c @@ -272,12 +272,13 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t // Deco stop if (deco) { - sample.event.type = SAMPLE_EVENT_DECOSTOP; - sample.event.time = 0; - sample.event.flags = 0; - sample.event.value = 0; - if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + sample.deco.type = DC_DECO_DECOSTOP; + } else { + sample.deco.type = DC_DECO_NDL; } + sample.deco.time = 0; + sample.deco.depth = 0.0; + if (callback) callback (DC_SAMPLE_DECO, sample, userdata); if (parser->samplesize == 3) { unsigned int type = (time / 20 + 2) % 3; diff --git a/src/mares_nemo_parser.c b/src/mares_nemo_parser.c index 29b51b1..7b24ca4 100644 --- a/src/mares_nemo_parser.c +++ b/src/mares_nemo_parser.c @@ -412,12 +412,13 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c // Deco stop if (deco) { - sample.event.type = SAMPLE_EVENT_DECOSTOP; - sample.event.time = 0; - sample.event.flags = 0; - sample.event.value = 0; - if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + sample.deco.type = DC_DECO_DECOSTOP; + } else { + sample.deco.type = DC_DECO_NDL; } + sample.deco.time = 0; + sample.deco.depth = 0.0; + if (callback) callback (DC_SAMPLE_DECO, sample, userdata); // Pressure (1 bar). if (parser->sample_size == 3) { diff --git a/src/uwatec_memomouse_parser.c b/src/uwatec_memomouse_parser.c index d03d31b..e5c66f9 100644 --- a/src/uwatec_memomouse_parser.c +++ b/src/uwatec_memomouse_parser.c @@ -243,6 +243,16 @@ uwatec_memomouse_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba gasmix_previous = gasmix; } + // NDL / Deco + if (warnings & 0x01) { + sample.deco.type = DC_DECO_DECOSTOP; + } else { + sample.deco.type = DC_DECO_NDL; + } + sample.deco.time = 0; + sample.deco.depth = 0.0; + if (callback) callback (DC_SAMPLE_DECO, sample, userdata); + // Warnings for (unsigned int i = 0; i < 6; ++i) { if (warnings & (1 << i)) { @@ -251,7 +261,7 @@ uwatec_memomouse_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba sample.event.value = 0; switch (i) { case 0: // Deco stop - sample.event.type = SAMPLE_EVENT_DECOSTOP; + sample.event.type = SAMPLE_EVENT_NONE; break; case 1: // Remaining bottom time too short (Air series only) sample.event.type = SAMPLE_EVENT_RBT; @@ -269,7 +279,9 @@ uwatec_memomouse_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba sample.event.type = SAMPLE_EVENT_TRANSMITTER; break; } - if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + if (sample.event.type != SAMPLE_EVENT_NONE) { + if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + } } } From d021bb98efea02ceb4a45cb8d1ce53a132d7992f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 1 Apr 2017 16:16:38 +0200 Subject: [PATCH 16/16] Disable the deco events These deco events have already been converted to a deco sample. --- src/suunto_d9_parser.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index 19b3218..550d515 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -576,28 +576,28 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca seconds = data[offset + 1]; switch (type & 0x7F) { case 0x00: // Voluntary Safety Stop - sample.event.type = SAMPLE_EVENT_SAFETYSTOP_VOLUNTARY; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~SAFETYSTOP; else in_deco |= SAFETYSTOP; break; case 0x01: // Mandatory Safety Stop - odd concept; model as deco stop - sample.event.type = SAMPLE_EVENT_SAFETYSTOP_MANDATORY; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~DECOSTOP; else in_deco |= DECOSTOP; break; case 0x02: // Deep Safety Stop - sample.event.type = SAMPLE_EVENT_DEEPSTOP; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~DEEPSTOP; else in_deco |= DEEPSTOP; break; case 0x03: // Deco - sample.event.type = SAMPLE_EVENT_DECOSTOP; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~DECOSTOP; else @@ -647,14 +647,14 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca sample.event.type = SAMPLE_EVENT_TISSUELEVEL; break; case 0x13: // Deep Safety Stop - sample.event.type = SAMPLE_EVENT_DEEPSTOP; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~DEEPSTOP; else in_deco |= DEEPSTOP; break; case 0x14: // Mandatory Safety Stop - again, model as deco stop - sample.event.type = SAMPLE_EVENT_SAFETYSTOP_MANDATORY; + sample.event.type = SAMPLE_EVENT_NONE; if (type & 0x80) in_deco &= ~DECOSTOP; else