Since logbook entries with invalid ringbuffer pointers are considered a
fatal error, the download is aborted. The result is that any remaining
entries, located after the invalid entry, can't be downloaded at all.
This can be avoided by skipping the problematic entry instead of
aborting the download.
With just a single table, the version string matching not only becomes
simpler, but adding new layouts also requires no code changes anymore.
For devices that need different layouts depending on the firmware
version (e.g. Oceanic Atom 2.0 and Aeris Manta), the table may contain
multiple entries with the different firmware versions. The only
requirement is that the entry with the highest firmware version must be
listed first.
The patterns used for the version string have the firmware version
masked out because it varies. We can re-use those masks to extract the
firmware version.
Due to how the Oceanic ringbuffer is implemented, the ringbuffer always
contains at least one entry. If there are no dives recorded yet, the
content of that entry will be empty. Such entries are already ignored
during processing, but instead of returning this empty entry to the
caller, simply clear the logbook buffer, and return no entries at all.
Previously, invalid ringbuffer pointers were always handled during the
second pass. But that changed after the previous commit. If the invalid
pointer is located in the first logbook entry, this is now handled as
"no dives present". Fixed by returning the correct error code instead.
If all the entries in the logbook ringbuffer happen to be empty, the
ringbuffer end pointer will not have a valid value. Creating the
ringbuffer stream will fail, and an error will be returned to the
caller. Fixed by adding an extra check, and exit if there are no dives.
On certain devices, for example the Aeris Elite T3, the logbook
ringbuffer can sometimes contain an empty logbook entry in between the
valid entries. Because the presence of such an empty entry is currently
being interpreted as having reached the last valid entry, the download
is aborted. The result is that all remaining valid entries, located
after the empty entry, can't be downloaded.
This can be avoided by skipping the empty entry instead of aborting the
download.
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).
Even after removing the pages padded with 0xFF bytes, there are still
some invalid sample pages present at the end of the profile. But it
turns out the number of valid profile pages is stored in the logbook
entry.
The only caveat is that the number of pages appears to be stored as a 12
bit number, which limits the total profile size to only 64Kb. We don't
known what happens for larger dives.
The Oceanic Pro Plus X is quite different from the previous models. The
profile data is now stored in a dedicated memory area, and hence there
are a few important differences:
Reading data from the new profile memory area is done with a new F6
command. This new command is very similar to the existing B8 command,
but accesses a completely different memory area. In order to integrate
those two memory areas as transparantly as possible into the existing
infrastructure, a virtual memory space is introduced. The lower part of
the virtual memory is mapped onto the main memory area, while the upper
part is mapped onto the new profile memory area.
The page size of the new profile memory area also increased from 16 to
256 bytes. If the profile size is not an exact multiple of 256 bytes,
the dive computer pads the profile data with 0xFF bytes.
The other changes are the usual Oceanic device specific changes.
Unfortunately there are several devices where an invalid logbook begin
pointer occurs relative frequently. Typical examples are the Oceanic VT
4.1 and the Sherwood Wisdom 2. 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 oldest logbook
entry, we can fall back to downloading the entire logbook ringbuffer. If
we're lucky (and we usually are), we can detect the oldest entry by
inspecting the logbook entries once they are downloaded (e.g. presence
of uninitialized entries) and then the download will finish succesfully.
In the worst case scenario, we'll be able to download at least some
dives before hitting another error.
The ringbuffer_distance() function has a parameter to specify whether a
ringbuffer with identical begin/end pointers should be considered an
empty or a full ringbuffer. Hence there is no need to handle the case of
a full ringbuffer manually.
The Aeris 500AI serial number appears to be located at a slightly
different offset, and with an unusual encoding. It's a BCD encoded
number, but with the digits of each byte swapped.
At the moment, the encoding of the serial number is tied to the global
pointer mode. To support devices where this is no longer the case, a new
entry for the serial number encoding is added.
By adding the logbook and profile functions to the vtable, a dive
computer backend can now easily replace the default implementation with
a custom one, without having to duplicate the common code.
The large and complex oceanic_common_device_foreach() function is
refactored into two independent helper functions. One for reading the
entries in the logbook ringbuffer, and another for reading the profile
data.
Both the allocation and initialization of the object data structure is
now moved to a single function. The corresponding deallocation function
is intended to free objects that have been allocated, but are not fully
initialized yet. The public cleanup function shouldn't be used in such
case, because it may try to release resources that haven't been
initialized yet.
For devices without a logbook ringbuffer, such as the Oceanic Veo 1.0
and the Aeris XR-1 NX, the ringbuffer begin and end are identical. In
this case, the changes in the previous commit will always result in a
fatal error due to an invalid ringbuffer pointer. To avoid the error, we
exit before trying to use the pointers.
Until now, an invalid logbook pointer was silently ignored and handled
as an empty ringbuffer. But this hides real errors, which is worse than
failing if no dives are present. Trying to download dives from an empty
device should be a rather uncommon scenario anyway.
Most of the changes needed for the F11 are the standard model specific
tweaks. But the F11 also has another interesting "feature". If you try
to download a full memory dump using the standard B1 read command, then
the data starts to repeat after the first 64K. It seems that somehow,
the B1 command can only address the first 64K of the memory. To avoid
this problem, the newer B4 read command needs to be used instead.
This might be a firmware bug, or maybe internally they store the address
in a 12bit variable, which causes the upper bits to get lost? That would
explain the repeating data.
The Hollis TX1 has several new features compared to the other models. It
supports trimix and up to 6 different gas mixes can be configured. It
also has twice the amount of memory, which requires an extra bit for the
ringbuffer pointers.
The term "backend" can be confusing because it can refer to both the
virtual function table and the device/parser backends. The use of the
term "vtable" avoids this.
The new vendor event provides a mechanism to deliver auxiliary data,
which is automatically retrieved during the data transfer, but not
accessible through the library interface otherwise. Possible examples
include handshake data and/or device identification data.
This event is mainly intended for diagnostic purposes, in combination
with the memory dumping support. Very few applications will actually
need it for anything else.
The hardcoded version strings are now grouped into arrays, according to
their corresponding layout descriptor. The main advantage of using
arrays is that new versions strings can now easily be added, without
having to modify any code.
I forgot to update the device and parser initialization functions to
store the context pointer into the objects. As a result, the internal
context pointers were always NULL.
The public api is changed to require a context object for all
operations. Because other library objects store the context pointer
internally, only the constructor functions need an explicit context
object as a parameter.
Adding the "dc_" namespace prefix (which is of course an abbreviation
for libdivecomputer) should avoid conflicts with other libraries. For
the time being, only the high-level device and parser layers are
changed.
The public header files are moved to a new subdirectory, to separate
the definition of the public interface from the actual implementation.
Using an identical directory layout as the final installation has the
advantage that the example code can be build outside the project tree
without any modifications to the #include statements.
The assumption that two consecutive dive profiles are stored without any
gaps in between them, appears to be incorrect in some cases. Instead of
failing with an error we just skip those gaps now.
The Aeris Elite T3 appears to update the global logbook pointer
incorrectly when overwriting old dives. As a result there can be logbook
entries pointing to profile data that has already been overwritten with
newer dives, and those cause problems when calculating the total amount
of bytes in the profile ringbuffer.
As a workaround we validate the logbook pointers immediately after
downloading. At this early stage we can check manually for ringbuffer
overflows without having to rely on the values stored in the data.
If a logbook entry contains an invalid ringbuffer pointer, the error is
not returned immediately, but delayed until the end of the download.
With this approach we can download at least the dives before the
problematic logbook entry.