Instead of reading data packets, the code is actually sending some
random data to the dive computer! A small typo with bad consequences!
This is a critical bug because it not only causes the download to fail,
but also appears to change random settings on the dive computer. I
suspect that the garbage data that gets send to the dive computer
happens to contain some valid write settings commands.
This adds the string field interface to the Shearwater family of dive
computers.
That includes proper serial number formatting, but it also has a lot of
new fields for battery information (both the dive computer itself and
the transmitter) but also deco model information.
Much of the deco model cases come from Anton Lundin in the original
subsurface branch, and Dirk Hohndel added the battery type and serial
number and firmware version data. And I ended up massaging it even in
that original branch, so it blamed me for all these lines even back
there.
The sign-offs from Dirk and Anton are from the original commits.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This adds the string field interface to the Suunto EON Steel and EON
Core.
This is actually a big deal, because it gets rid of all the ad-hoc
string parsing, and actually just uses the strings that the EON Steel
events and warnings natively use.
It also reports the severity of the notification/warning/alarm, so that
Subsurface can then use the proper icon. An event isn't just an event,
there's a big difference between a warning and just a notification.
It also fills in the tank information data for closed-circuit cylinder
use.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This adds the string field interface to the Suunto D9 family.
It's really just the proper serial number handling. From Dirk's
original commit:
"We have the correct firmware in the devinfo, but that's the firmware
the dive computer is on NOW, not necessarily the firmware it was using
when recording the dive"
so thus just serial number.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This adds the string field interface to the Oceanic Atom2 family,
including the proper serial number handling.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This adds the string field interface to the HW OSTC family, including
the proper serial number handling.
The deco model information was done by Anton Lundin in the original
subsurface branch, and the salinity, serial number, battery voltage and
desat information was added by Dirk Hohndel. Jan Mulder added the
battery percentage.
[ The sign-offs have been taken from the original commits in that old
subsurface branch, and I'm marking Dirk as the main author because on
the whole most of the lines come from him - Linus ]
Signed-off-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
The libdivecomputer serial number handling is very very messy.
There are multiple issues that make it messy:
- it's not actually figured out at parse-time, it's figured out at
download time and passed up through the DC_EVENT_DEVINFO as part of
the devinfo structure.
- it's passed around as an "unsigned in" in the devinfo structure,
which is entirely useless to anybody but libdivecomputer, since a
serial number isn't actually a number, but a string, and the format
of the string depends on the dive computer.
- it is *not* passed to the parser, so the parser can't do a better job
at it later.
But it turns out that the sane "create new parser" helper function does
actually get it, as part of the "devinfo" that is passed to it. So as
long as you use that sane interface, we can now pass it in to the actual
parser creation, and then the dive computer parsers that want to do a
reasonable job of actually generating a real serial number string can
now save it off and do so.
This just adds the infrastructure to make this possible. I'll do the
dive computers one by one.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Dirk seems to have some documentation about the different ID's, plus it
just makes sense to order the switch statement by number.
This is partly based on Dirk's original commit to do the different model
numbers, with various changes over time due to merge conflict
resolution. Dirk's sign-off comes from Dirks commit in the original
subsurface branch.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Not a lot of fields, but give the serial number in the proper format,
and other version information (Software version and bootloader version).
And the Nofly time that the dive computer reports.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This extends the libdivecomputer notion of "dc_tankvolume_t" to not just
have the tank volume type (imperial or metric), but be a "dc_tankinfo_t"
that shows other information about the cylinder.
The imperial-vs-metric data remains the same two values:
1 - metric
2 - imperial
but instead of being an enumeration of volume types, it is extended to a
bitmap of tank information, and the other bits currently are
4 - CC diluent cylinder
8 - CC O2 cylinder
with possible future extensions (bailout gas, perhaps).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
For most I/O stream implementations the serial communication specific
functions are meaningless. Implementing them as no-ops allows the dive
computer backends the call the I/O stream functions unconditionally.
However, implementing the no-op with a dummy function returning
DC_STATUS_SUCCESS, does not only add some (small) overhead at runtime,
but also requires many such functions. This is inconvenient and the same
result can easily be obtained by using a NULL pointer instead.
The consequence is that the logic is reversed now. To obtain the
previous behaviour of returning the DC_STATUS_UNSUPPORTED error code
again, you'll need to implement a dummy function. But that's fine
because it's the less common case.
I/O functions with output parameters, should always initialize those
output parameters, even when an error is returned. This prevents the
(accidental) use of uninitialized variables, whenever the caller forgets
to check the return code.
As a nice side effect, the use of a local variable guarantees that the
underlying I/O implementation will always receive a valid pointer.
When two or more identical (or very similar) dive computers are
connected, the USB VID/PID can be ambiguous. That's because the VID/PID
identifies the type of the USB device, and not the individual device.
But each USB HID device descriptor returned by the device discovery
represents a single connected device, and thus guarantees to open the
correct USB device.
To obtain the same behaviour as before, an application can simply open
the first discovered device.
Replace the global USB library context with a reference counted session
to manage the lifetime of the USB library context. For the libusb based
implementation, this is actually a much better match for the underlying
libusb api, and allows to eliminate the global state. For the hidapi
based implementation, the global state is unavoidable because the hidapi
doesn't support multiple sessions. Therefore we use a singleton session.
The main difference with the serial communication is that the BLE
communication transmits each SLIP encoded data packet as one or more BLE
data packets. The BLE packets have an extra two byte header with the
total number of packets and the current packet number.
The main difference with the serial communication is that the BLE
communication uses data packets (with a maximum size of 20 bytes)
instead of a continuous data stream.
The main difference with the USB HID communication is that the BLE data
packets have a variable size and are no longer padded to the full 32
(Tx) or 64 (Rx) bytes.
The main difference with the USB HID communication is that the BLE data
stream is encoded using HDLC framing with a 32 bit CRC checksum. Due to
this encoding, the data packets can no longer be processed one by one
(as is done for the USB HID packets). The entire HDLC encoded stream
needs to be received before it can be processed. This requires some
additional buffering.
Currently the dive computer backends are responsible for opening (and
closing) the underlying I/O stream internally. The consequence is that
each backend is hardwired to a specific transport type (e.g. serial,
irda or usbhid). In order to remove this dependency and support more
than one transport type in the same backend, the opening (and closing)
of the I/O stream is moved to the application.
The dc_device_open() function is modified to accept a pointer to the I/O
stream, instead of a string with the device node (which only makes sense
for serial communication). The dive computer backends only depend on the
common I/O interface.
With the support for multiple transports per device, the
dc_descriptor_get_transport() function became obsolete because it does
support only a single transport type. Applications should use the new
dc_descriptor_get_transports() function instead.
With the support for multiple transports per device and the possibility
to use custom I/O implementations, libdivecomputer no longer knows which
devices are actually supported. Hence libdivecomputer needs to always
report all the devices it knows about, and it's up to the application to
filter out entries for which there is no suitable transport available
(either built-in or custom).
Because the list of supported built-in transports depends on the
availability of external libraries (libusb, hidapi) and the operating
system, the application needs some mechanism to retrieve this
information at runtime. Therefore, a new dc_context_get_transports()
function is added, which returns a bitmask with all the available
built-in transports.
Several dive computers support multiple transports. For example the
Suunto Eon Steel supports both USB HID and BLE. All devices using
bluetooth classic communication support both the native bluetooth
transport and the legacy serial port emulation.
To support this feature, the values of the dc_transport_t type are
changed into bitmasks, and the dc_descriptor_t struct is extended with a
bitfield with all the supported transports.
Add a function to query the underlying transport type. This allows the
dive computer backends to implement transport specific behaviour where
necessary.
For the built-in I/O implementations, the transport type is obviously
always hardcoded, but for a custom I/O implementation the application
needs to provide the correct type. Hence the transport type can't be
hardcoded in the vtable and needs to be passed as a parameter.
Trying to purge the input buffer by reading and discarding data packets,
results in an annoying and confusing error message if no data packet is
received. To avoid this error, the functionality should be integrated in
the USB HID code, either automatically during initialization or by
implementing the purge function.
But since there seems to be no evidence that this is actually necessary,
let's remove this code.
Check the fingerprint before downloading the dive. If a match is found,
this avoids some unnecessary communication and thus makes the download a
little bit faster.
The file list isn't freed when an error occurs, and the strings returned
from the lookup_enum function are dynamically allocated and thus should
be freed as well.
The descriptor strings are dynamically allocated and owned by the
struct. The const qualifiers are a bit misleading here, and result in
warnings when trying to free the pointers again.
The multiplication is evaluated using 32bit arithmetic, and then stored
in a 64bit integer. The 32bit integer overflow can be avoided by casting
to a 64bit type first.
The write function is modified to always fill the buffer completely
before sending out the data. For escaped characters, the previous
implementation needed to append two bytes at once. Thus, if there was
only space left for a single byte, the buffer got flushed early with one
byte still unused. This can be avoid by appending one byte at a time.
The read function is modified to use a simple state machine with only a
single read call. This is mainly preparation to support reading and
processing larger data packets instead of just single bytes.
I received a bug report complaining that the most recent dives did not
get downloaded. It turns out that the internal dive number in the
logbook entries got reset back to zero somehow:
Logbook 0: empty
...
Logbook 209: empty
Logbook 210: 1
Logbook 211: 2
...
Logbook 235: 26
Logbook 236: 27
Logbook 237: 0
Logbook 238: 1
...
Logbook 254: 17
Logbook 255: 18
This confuses the logic to locate the most recent dive. Because that
logic assumes that the entry with the highest internal dive number is
always the most recent dive, it finds logbook entry #236 instead of the
correct entry #255. Now, when processing the logbook entries backwards,
it stops at those empty entries, and thus never reaches then newest
entries #237 to #255.
The workaround is based on the fact that the OSTC4, unlike the other
hwos based models, already re-orders the logbook entries and always
sends the most recent logbook entry last. So we can ignore the dive
number and simply use the last non-empty entry.
I'd never noticed this before, since my date had always been already set
correctly, but the timesync with the EON Steel only set the time, not
the date.
The fix is trivial, since the code already filled in the datetime data,
it just didn't do the SET_DATE command.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
The Dive Rite NiTek Q supports OC and CC samples. For reporting the dive
mode, any dive containing at least one CC sample is considered to be a
CCR dive.
The half-duplex emulation is basically a workaround for a timing problem
in the vyper2 backend. Since no other dive computer backend uses or
needs this feature, it makes no sense to support this at the I/O layer.
It can be implemented directly in the vyper2 backend instead.
Replace the gettimeofday() based implementation with the new monotonic
timers. This makes the implementation more robust against unexpected
adjustments of the clock.
The new timer module provides an cross-platform interface for a high
resolution monotonic clock. The timestamps are always relative to the
creation of the timer and their unit is one microseconds.
The timers can be used for logging, measuring elapsed time and
implementing timeouts.
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.