diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk
index 19702b81..08dd3e9e 100644
--- a/contrib/android/Android.mk
+++ b/contrib/android/Android.mk
@@ -39,6 +39,8 @@ LOCAL_SRC_FILES := \
src/divesoft_freedom_parser.c \
src/divesystem_idive.c \
src/divesystem_idive_parser.c \
+ src/halcyon_symbios.c \
+ src/halcyon_symbios_parser.c \
src/hdlc.c \
src/hw_frog.c \
src/hw_ostc3.c \
diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj
index d9176e7a..81d6df3a 100644
--- a/contrib/msvc/libdivecomputer.vcxproj
+++ b/contrib/msvc/libdivecomputer.vcxproj
@@ -207,6 +207,8 @@
+
+
@@ -337,6 +339,7 @@
+
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 4ea44de2..95c3bb2c 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -18,7 +18,7 @@ MANPAGES = \
dc_descriptor_get_product.3 \
dc_descriptor_get_vendor.3 \
dc_descriptor_get_transports.3 \
- dc_descriptor_iterator.3 \
+ dc_descriptor_iterator_new.3 \
dc_device_close.3 \
dc_device_foreach.3 \
dc_device_open.3 \
diff --git a/doc/man/dc_bluetooth_iterator_new.3 b/doc/man/dc_bluetooth_iterator_new.3
index e5322061..89a85aa4 100644
--- a/doc/man/dc_bluetooth_iterator_new.3
+++ b/doc/man/dc_bluetooth_iterator_new.3
@@ -44,7 +44,7 @@ opened with
and a
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Pp
On returning
.Dv DC_STATUS_SUCCESS
diff --git a/doc/man/dc_descriptor_free.3 b/doc/man/dc_descriptor_free.3
index e7f4fde2..6307d3fc 100644
--- a/doc/man/dc_descriptor_free.3
+++ b/doc/man/dc_descriptor_free.3
@@ -36,12 +36,12 @@
Frees a descriptor usually returned with
.Xr dc_iterator_next 3
as created with
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
It's safe to pass
.Dv NULL
to this function.
.Sh SEE ALSO
-.Xr dc_descriptor_iterator 3 ,
+.Xr dc_descriptor_iterator_new 3 ,
.Xr dc_iterator_free 3
.Sh AUTHORS
The
diff --git a/doc/man/dc_descriptor_get_model.3 b/doc/man/dc_descriptor_get_model.3
index 24ee9917..245113cd 100644
--- a/doc/man/dc_descriptor_get_model.3
+++ b/doc/man/dc_descriptor_get_model.3
@@ -31,7 +31,7 @@
.In libdivecomputer/descriptor.h
.Ft "unsigned int"
.Fo dc_descriptor_get_model
-.Fa "dc_descriptor_t *descriptor"
+.Fa "const dc_descriptor_t *descriptor"
.Fc
.Sh DESCRIPTION
Gets the model number of a dive computer descriptor or 0 if none was
@@ -40,7 +40,7 @@ defined for the computer.
.Sh RETURN VALUES
This returns the model number or 0 if none exists.
.Sh SEE ALSO
-.Xr dc_descriptor_iterator 3
+.Xr dc_descriptor_iterator_new 3
.Sh AUTHORS
The
.Lb libdivecomputer
diff --git a/doc/man/dc_descriptor_get_product.3 b/doc/man/dc_descriptor_get_product.3
index 541da7c9..8b63df46 100644
--- a/doc/man/dc_descriptor_get_product.3
+++ b/doc/man/dc_descriptor_get_product.3
@@ -30,7 +30,7 @@
.In libdivecomputer/descriptor.h
.Ft "const char *"
.Fo dc_descriptor_get_product
-.Fa "dc_descriptor_t *descriptor"
+.Fa "const dc_descriptor_t *descriptor"
.Fc
.Sh DESCRIPTION
Gets the product
diff --git a/doc/man/dc_descriptor_get_transports.3 b/doc/man/dc_descriptor_get_transports.3
index 93bef418..fe663bae 100644
--- a/doc/man/dc_descriptor_get_transports.3
+++ b/doc/man/dc_descriptor_get_transports.3
@@ -30,7 +30,7 @@
.In libdivecomputer/descriptor.h
.Ft "unsigned int"
.Fo dc_descriptor_get_transports
-.Fa "dc_descriptor_t *descriptor"
+.Fa "const dc_descriptor_t *descriptor"
.Fc
.Sh DESCRIPTION
Gets the transports supported by the given
@@ -38,7 +38,7 @@ Gets the transports supported by the given
The
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Sh RETURN VALUES
Returns a union (bitwise OR) of the transports supported by the given
.Fa descriptor .
@@ -59,7 +59,7 @@ if(transports & DC_TRANSPORT_USBHID) {
}
.Ed
.Sh SEE ALSO
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Sh AUTHORS
The
.Lb libdivecomputer
diff --git a/doc/man/dc_descriptor_get_vendor.3 b/doc/man/dc_descriptor_get_vendor.3
index 6c5de335..146f2f9f 100644
--- a/doc/man/dc_descriptor_get_vendor.3
+++ b/doc/man/dc_descriptor_get_vendor.3
@@ -30,7 +30,7 @@
.In libdivecomputer/descriptor.h
.Ft "const char *"
.Fo dc_descriptor_get_vendor
-.Fa "dc_descriptor_t *descriptor"
+.Fa "const dc_descriptor_t *descriptor"
.Fc
.Sh DESCRIPTION
Gets the vendor
diff --git a/doc/man/dc_descriptor_iterator.3 b/doc/man/dc_descriptor_iterator_new.3
similarity index 93%
rename from doc/man/dc_descriptor_iterator.3
rename to doc/man/dc_descriptor_iterator_new.3
index 5d6619dc..1d49e106 100644
--- a/doc/man/dc_descriptor_iterator.3
+++ b/doc/man/dc_descriptor_iterator_new.3
@@ -22,15 +22,16 @@
.Dt DC_DESCRIPTOR_ITERATOR 3
.Os
.Sh NAME
-.Nm dc_descriptor_iterator
+.Nm dc_descriptor_iterator_new
.Nd get all supported dive computers
.Sh LIBRARY
.Lb libdivecomputer
.Sh SYNOPSIS
.In libdivecomputer/descriptor.h
.Ft dc_status_t
-.Fo dc_descriptor_iterator
+.Fo dc_descriptor_iterator_new
.Fa "dc_iterator_t **iterator"
+.Fa "dc_descriptor_t *descriptor"
.Fc
.Sh DESCRIPTION
Gets all descriptors available to
@@ -56,7 +57,7 @@ The following iterates over all descriptors, printing the vendor, then
frees the iterator.
It does no error checking.
.Bd -literal
-dc_descriptor_iterator(&iter));
+dc_descriptor_iterator_new(&iter, context));
while (dc_iterator_next(iter, &desc) == DC_STATUS_SUCCESS) {
printf("%s\en", dc_descriptor_get_vendor(desc));
dc_descriptor_free(desc);
diff --git a/doc/man/dc_device_open.3 b/doc/man/dc_device_open.3
index 3ba335fd..b85451c1 100644
--- a/doc/man/dc_device_open.3
+++ b/doc/man/dc_device_open.3
@@ -44,7 +44,7 @@ opened with
a dive computer
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 ,
+.Xr dc_descriptor_iterator_new 3 ,
and a
.Fa iostream
opened with a transport specific open function like
@@ -70,7 +70,7 @@ On success, the
pointer is filled in with an open handle.
.Sh SEE ALSO
.Xr dc_context_new 3 ,
-.Xr dc_descriptor_iterator 3 ,
+.Xr dc_descriptor_iterator_new 3 ,
.Xr dc_device_close 3
.Sh AUTHORS
The
diff --git a/doc/man/dc_irda_iterator_new.3 b/doc/man/dc_irda_iterator_new.3
index b24f73dc..38c1eba1 100644
--- a/doc/man/dc_irda_iterator_new.3
+++ b/doc/man/dc_irda_iterator_new.3
@@ -44,7 +44,7 @@ opened with
and a
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Pp
On returning
.Dv DC_STATUS_SUCCESS
diff --git a/doc/man/dc_iterator_free.3 b/doc/man/dc_iterator_free.3
index b086aaa9..745b8d2a 100644
--- a/doc/man/dc_iterator_free.3
+++ b/doc/man/dc_iterator_free.3
@@ -36,7 +36,7 @@
Frees the iterator
.Fa iterator .
It may be invoked on any iterator type, e.g., one created with
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Sh RETURN VALUES
This returns
.Dv DC_STATUS_SUCCESS
diff --git a/doc/man/dc_serial_iterator_new.3 b/doc/man/dc_serial_iterator_new.3
index 91ad54cb..2e35a8f5 100644
--- a/doc/man/dc_serial_iterator_new.3
+++ b/doc/man/dc_serial_iterator_new.3
@@ -44,7 +44,7 @@ opened with
and a
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Pp
On returning
.Dv DC_STATUS_SUCCESS
diff --git a/doc/man/dc_usbhid_iterator_new.3 b/doc/man/dc_usbhid_iterator_new.3
index 0778d389..5a100b96 100644
--- a/doc/man/dc_usbhid_iterator_new.3
+++ b/doc/man/dc_usbhid_iterator_new.3
@@ -44,7 +44,7 @@ opened with
and a
.Fa descriptor
usually found by searching through
-.Xr dc_descriptor_iterator 3 .
+.Xr dc_descriptor_iterator_new 3 .
.Pp
On returning
.Dv DC_STATUS_SUCCESS
diff --git a/doc/man/libdivecomputer.3 b/doc/man/libdivecomputer.3
index cbe22eb9..c9a90a8a 100644
--- a/doc/man/libdivecomputer.3
+++ b/doc/man/libdivecomputer.3
@@ -48,7 +48,7 @@ and
.Xr dc_context_set_loglevel 3 .
.It
Find a descriptor for their dive computer by iterating through
-.Xr dc_descriptor_iterator 3
+.Xr dc_descriptor_iterator_new 3
and searching by name, vendor, or product family.
.It
Find the transport to use for the communication. To determine the supported transports use
@@ -145,7 +145,7 @@ return
.El
.Sh SEE ALSO
.Xr dc_context_new 3 ,
-.Xr dc_descriptor_iterator 3
+.Xr dc_descriptor_iterator_new 3
.Xr dc_device_open 3
.Xr dc_parser_new 3
.Sh AUTHORS
diff --git a/examples/common.c b/examples/common.c
index 4f965f16..7aacd90e 100644
--- a/examples/common.c
+++ b/examples/common.c
@@ -79,7 +79,7 @@ static const backend_table_t g_backends[] = {
{"iconhd", DC_FAMILY_MARES_ICONHD, 0x14},
{"ostc", DC_FAMILY_HW_OSTC, 0},
{"frog", DC_FAMILY_HW_FROG, 0},
- {"ostc3", DC_FAMILY_HW_OSTC3, 0x0A},
+ {"ostc3", DC_FAMILY_HW_OSTC3, 0x11},
{"edy", DC_FAMILY_CRESSI_EDY, 0x08},
{"leonardo", DC_FAMILY_CRESSI_LEONARDO, 1},
{"goa", DC_FAMILY_CRESSI_GOA, 2},
@@ -100,6 +100,7 @@ static const backend_table_t g_backends[] = {
{"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0},
{"s1", DC_FAMILY_OCEANS_S1, 0},
{"freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19},
+ {"symbios", DC_FAMILY_HALCYON_SYMBIOS, 1},
// Not merged upstream yet
{"descentmk1", DC_FAMILY_GARMIN, 0},
@@ -260,7 +261,7 @@ dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t f
dc_status_t rc = DC_STATUS_SUCCESS;
dc_iterator_t *iterator = NULL;
- rc = dc_descriptor_iterator (&iterator);
+ rc = dc_descriptor_iterator_new (&iterator, NULL);
if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error creating the device descriptor iterator.");
return rc;
diff --git a/examples/dctool_list.c b/examples/dctool_list.c
index bc70d13f..e40c0895 100644
--- a/examples/dctool_list.c
+++ b/examples/dctool_list.c
@@ -76,7 +76,7 @@ dctool_list_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t
dc_iterator_t *iterator = NULL;
dc_descriptor_t *descriptor = NULL;
- dc_descriptor_iterator (&iterator);
+ dc_descriptor_iterator_new (&iterator, context);
while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) {
printf ("%s %s\n",
dc_descriptor_get_vendor (descriptor),
diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h
index 0ec8a9ff..5a954ee5 100644
--- a/include/libdivecomputer/common.h
+++ b/include/libdivecomputer/common.h
@@ -125,6 +125,8 @@ typedef enum dc_family_t {
DC_FAMILY_OCEANS_S1 = (22 << 16),
/* Divesoft Freedom */
DC_FAMILY_DIVESOFT_FREEDOM = (23 << 16),
+ /* Halcyon Symbios */
+ DC_FAMILY_HALCYON_SYMBIOS = (24 << 16),
// Not merged upstream yet
/* Garmin */
diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h
index bdd8c73c..6eaba8ed 100644
--- a/include/libdivecomputer/descriptor.h
+++ b/include/libdivecomputer/descriptor.h
@@ -23,6 +23,7 @@
#define DC_DESCRIPTOR_H
#include "common.h"
+#include "context.h"
#include "iterator.h"
#ifdef __cplusplus
@@ -38,11 +39,15 @@ typedef struct dc_descriptor_t dc_descriptor_t;
* Create an iterator to enumerate the supported dive computers.
*
* @param[out] iterator A location to store the iterator.
+ * @param[in] context A valid device descriptor.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
-dc_descriptor_iterator (dc_iterator_t **iterator);
+dc_descriptor_iterator_new (dc_iterator_t **iterator, dc_context_t *context);
+
+/* For backwards compatibility */
+#define dc_descriptor_iterator(iterator) dc_descriptor_iterator_new(iterator, NULL)
/**
* Free the device descriptor.
@@ -59,7 +64,7 @@ dc_descriptor_free (dc_descriptor_t *descriptor);
* @returns The vendor name of the dive computer on success, or NULL on failure.
*/
const char *
-dc_descriptor_get_vendor (dc_descriptor_t *descriptor);
+dc_descriptor_get_vendor (const dc_descriptor_t *descriptor);
/**
* Get the product name of the dive computer.
@@ -69,7 +74,7 @@ dc_descriptor_get_vendor (dc_descriptor_t *descriptor);
* failure.
*/
const char *
-dc_descriptor_get_product (dc_descriptor_t *descriptor);
+dc_descriptor_get_product (const dc_descriptor_t *descriptor);
/**
* Get the family type of the dive computer.
@@ -79,7 +84,7 @@ dc_descriptor_get_product (dc_descriptor_t *descriptor);
* on failure.
*/
dc_family_t
-dc_descriptor_get_type (dc_descriptor_t *descriptor);
+dc_descriptor_get_type (const dc_descriptor_t *descriptor);
/**
* Get the model number of the dive computer.
@@ -89,7 +94,7 @@ dc_descriptor_get_type (dc_descriptor_t *descriptor);
* failure.
*/
unsigned int
-dc_descriptor_get_model (dc_descriptor_t *descriptor);
+dc_descriptor_get_model (const dc_descriptor_t *descriptor);
/**
* Get all transports supported by the dive computer.
@@ -99,7 +104,7 @@ dc_descriptor_get_model (dc_descriptor_t *descriptor);
* success, or DC_TRANSPORT_NONE on failure.
*/
unsigned int
-dc_descriptor_get_transports (dc_descriptor_t *descriptor);
+dc_descriptor_get_transports (const dc_descriptor_t *descriptor);
/**
* Check if a low-level I/O device matches a supported dive computer.
@@ -117,7 +122,7 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor);
* there is no match.
*/
int
-dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+dc_descriptor_filter (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
#ifdef __cplusplus
}
diff --git a/src/Makefile.am b/src/Makefile.am
index 888e49da..fb95fdc7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,6 +82,7 @@ libdivecomputer_la_SOURCES = \
oceans_s1_common.h oceans_s1_common.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c \
divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \
+ halcyon_symbios.h halcyon_symbios.c halcyon_symbios_parser.c \
hdlc.h hdlc.c \
packet.h packet.c \
socket.h socket.c \
diff --git a/src/array.c b/src/array.c
index f47c3eb0..6d97557c 100644
--- a/src/array.c
+++ b/src/array.c
@@ -51,6 +51,14 @@ array_reverse_bits (unsigned char data[], unsigned int size)
}
}
+void
+array_reverse_nibbles (unsigned char data[], unsigned int size)
+{
+ for (unsigned int i = 0; i < size; ++i) {
+ unsigned char tmp = data[i];
+ data[i] = ((tmp & 0xF0) >> 4) | ((tmp & 0x0F) << 4);
+ }
+}
int
array_isequal (const unsigned char data[], unsigned int size, unsigned char value)
diff --git a/src/array.h b/src/array.h
index fab1475e..81268779 100644
--- a/src/array.h
+++ b/src/array.h
@@ -23,6 +23,7 @@
#define ARRAY_H
#define C_ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
+#define C_ARRAY_ITEMSIZE(a) (sizeof *(a))
#ifdef __cplusplus
extern "C" {
@@ -34,6 +35,9 @@ array_reverse_bytes (unsigned char data[], unsigned int size);
void
array_reverse_bits (unsigned char data[], unsigned int size);
+void
+array_reverse_nibbles (unsigned char data[], unsigned int size);
+
int
array_isequal (const unsigned char data[], unsigned int size, unsigned char value);
diff --git a/src/atomics_cobalt.c b/src/atomics_cobalt.c
index fdda9099..10de3e7c 100644
--- a/src/atomics_cobalt.c
+++ b/src/atomics_cobalt.c
@@ -105,6 +105,8 @@ atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context, dc_iostrea
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->version, sizeof (device->version));
+
*out = (dc_device_t*) device;
return DC_STATUS_SUCCESS;
diff --git a/src/bluetooth.c b/src/bluetooth.c
index 309953cc..f2e55224 100644
--- a/src/bluetooth.c
+++ b/src/bluetooth.c
@@ -52,6 +52,7 @@
#include "iostream-private.h"
#include "iterator-private.h"
#include "platform.h"
+#include "array.h"
#ifdef _WIN32
#define DC_ADDRESS_FORMAT "%012I64X"
@@ -59,8 +60,6 @@
#define DC_ADDRESS_FORMAT "%012llX"
#endif
-#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
-
#define MAX_DEVICES 255
#define MAX_PERIODS 8
diff --git a/src/checksum.c b/src/checksum.c
index eeda9129..1a73a0c5 100644
--- a/src/checksum.c
+++ b/src/checksum.c
@@ -67,6 +67,55 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char
return crc;
}
+/*
+ * Polynomial: 0x07
+ * RefIn: False
+ * RefOut: False
+ */
+unsigned char
+checksum_crc8 (const unsigned char data[], unsigned int size, unsigned char init, unsigned char xorout)
+{
+ static const unsigned char crc_table[] = {
+ 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
+ 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
+ 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
+ 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
+ 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
+ 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
+ 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
+ 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
+ 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
+ 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
+ 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
+ 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
+ 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
+ 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
+ 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
+ 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
+ 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
+ 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
+ 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
+ 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
+ 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
+ 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
+ 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
+ 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
+ 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
+ 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63,
+ 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
+ 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
+ 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
+ 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
+ 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
+ 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
+ };
+
+ unsigned char crc = init;
+ for (unsigned int i = 0; i < size; ++i)
+ crc = crc_table[data[i] ^ crc];
+
+ return crc ^ xorout;
+}
/*
* Polynomial: 0x1021
diff --git a/src/checksum.h b/src/checksum.h
index f9e54bb5..c75385db 100644
--- a/src/checksum.h
+++ b/src/checksum.h
@@ -38,6 +38,9 @@ checksum_add_uint16 (const unsigned char data[], unsigned int size, unsigned sho
unsigned char
checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init);
+unsigned char
+checksum_crc8 (const unsigned char data[], unsigned int size, unsigned char init, unsigned char xorout);
+
unsigned short
checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c
index aeaf25c8..503b4f41 100644
--- a/src/cochran_commander_parser.c
+++ b/src/cochran_commander_parser.c
@@ -27,6 +27,7 @@
#include "cochran_commander.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#define COCHRAN_MODEL_COMMANDER_TM 0
@@ -819,7 +820,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
// Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
if (time % 2 == 0) {
// Ascent rate
- double ascent_rate = 0.0;
+ double DC_ATTR_UNUSED ascent_rate = 0.0;
if (s[1] & 0x80)
ascent_rate = (s[1] & 0x7f);
else
diff --git a/src/cressi_edy.c b/src/cressi_edy.c
index c4e78c13..6845a0ff 100644
--- a/src/cressi_edy.c
+++ b/src/cressi_edy.c
@@ -35,11 +35,11 @@
#define MAXRETRIES 4
-#define SZ_PACKET 0x80
-#define SZ_PAGE (SZ_PACKET / 4)
+#define SZ_PAGE 32
#define SZ_HEADER 32
+#define ARCHIMEDE 0x01
#define IQ700 0x05
#define EDY 0x08
@@ -60,6 +60,7 @@ typedef struct cressi_edy_device_t {
const cressi_edy_layout_t *layout;
unsigned char fingerprint[SZ_PAGE / 2];
unsigned int model;
+ unsigned int packetsize;
} cressi_edy_device_t;
static dc_status_t cressi_edy_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
@@ -141,9 +142,19 @@ cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], u
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
+ }
+
+ if (trailer) {
+ // Receive the trailer byte.
+ unsigned char end = 0;
+ status = dc_iostream_read (device->iostream, &end, 1, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the answer.");
+ return status;
+ }
// Verify the trailer of the packet.
- if (trailer && answer[asize - 1] != 0x45) {
+ if (end != 0x45) {
ERROR (abstract->context, "Unexpected answer trailer byte.");
return DC_STATUS_PROTOCOL;
}
@@ -193,6 +204,8 @@ cressi_edy_init2 (cressi_edy_device_t *device)
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "Model", answer, sizeof (answer));
+
device->model = answer[0];
return DC_STATUS_SUCCESS;
@@ -203,9 +216,8 @@ static dc_status_t
cressi_edy_init3 (cressi_edy_device_t *device)
{
unsigned char command[1] = {0x0C};
- unsigned char answer[1] = {0};
- return cressi_edy_transfer (device, command, sizeof (command), answer, sizeof (answer), 1);
+ return cressi_edy_transfer (device, command, sizeof (command), NULL, 0, 1);
}
@@ -238,6 +250,7 @@ cressi_edy_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
device->iostream = iostream;
device->layout = NULL;
device->model = 0;
+ device->packetsize = 0;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
// Set the serial communication protocol (1200 8N1).
@@ -275,19 +288,26 @@ cressi_edy_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
// Send the init commands.
cressi_edy_init1 (device);
cressi_edy_init2 (device);
- cressi_edy_init3 (device);
+ if (device->model != ARCHIMEDE) {
+ cressi_edy_init3 (device);
+ }
- if (device->model == IQ700) {
+ device->packetsize = device->model == ARCHIMEDE ?
+ SZ_PAGE : SZ_PAGE * 4;
+
+ if (device->model == IQ700 || device->model == ARCHIMEDE) {
device->layout = &tusa_iq700_layout;
} else {
device->layout = &cressi_edy_layout;
}
- // Set the serial communication protocol (4800 8N1).
- status = dc_iostream_configure (device->iostream, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (context, "Failed to set the terminal attributes.");
- goto error_free;
+ if (device->model != ARCHIMEDE) {
+ // Set the serial communication protocol (4800 8N1).
+ status = dc_iostream_configure (device->iostream, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the terminal attributes.");
+ goto error_free;
+ }
}
// Make sure everything is in a sane state.
@@ -324,32 +344,40 @@ cressi_edy_device_close (dc_device_t *abstract)
static dc_status_t
cressi_edy_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
{
+ dc_status_t status = DC_STATUS_SUCCESS;
cressi_edy_device_t *device = (cressi_edy_device_t*) abstract;
if ((address % SZ_PAGE != 0) ||
- (size % SZ_PACKET != 0))
+ (size % device->packetsize != 0))
return DC_STATUS_INVALIDARGS;
unsigned int nbytes = 0;
while (nbytes < size) {
// Read the package.
unsigned int number = address / SZ_PAGE;
- unsigned char answer[SZ_PACKET + 1] = {0};
- unsigned char command[3] = {0x52,
- (number >> 8) & 0xFF, // high
- (number ) & 0xFF}; // low
- dc_status_t rc = cressi_edy_transfer (device, command, sizeof (command), answer, sizeof (answer), 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
+ unsigned char command[3] = {0};
+ if (device->model == ARCHIMEDE) {
+ command[0] = 0x45;
+ command[1] = 0x52;
+ command[2] = number & 0xFF;
+ } else {
+ command[0] = 0x52;
+ command[1] = (number >> 8) & 0xFF; // high;
+ command[2] = (number ) & 0xFF; // low
+ }
+ status = cressi_edy_transfer (device, command, sizeof (command), data + nbytes, device->packetsize, 1);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
- memcpy (data, answer, SZ_PACKET);
+ nbytes += device->packetsize;
+ address += device->packetsize;
+ }
- nbytes += SZ_PACKET;
- address += SZ_PACKET;
- data += SZ_PACKET;
+ if (device->model == ARCHIMEDE) {
+ array_reverse_nibbles (data, size);
}
- return DC_STATUS_SUCCESS;
+ return status;
}
@@ -389,7 +417,7 @@ cressi_edy_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
return device_dump_read (abstract, 0, dc_buffer_get_data (buffer),
- dc_buffer_get_size (buffer), SZ_PACKET);
+ dc_buffer_get_size (buffer), device->packetsize);
}
@@ -401,7 +429,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
- progress.maximum = SZ_PACKET +
+ progress.maximum = 4 * SZ_PAGE +
(layout->rb_profile_end - layout->rb_profile_begin);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
@@ -413,7 +441,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Read the logbook data.
- unsigned char logbook[SZ_PACKET] = {0};
+ unsigned char logbook[4 * SZ_PAGE] = {0};
dc_status_t rc = cressi_edy_device_read (abstract, layout->rb_logbook_offset, logbook, sizeof (logbook));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the logbook data.");
@@ -475,13 +503,13 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
}
// Update and emit a progress event.
- progress.current += SZ_PACKET;
- progress.maximum = SZ_PACKET + total;
+ progress.current += 4 * SZ_PAGE;
+ progress.maximum = 4 * SZ_PAGE + 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, DC_RBSTREAM_BACKWARD);
+ rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
diff --git a/src/cressi_edy_parser.c b/src/cressi_edy_parser.c
index 951c032b..e45cfb1e 100644
--- a/src/cressi_edy_parser.c
+++ b/src/cressi_edy_parser.c
@@ -28,6 +28,7 @@
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &cressi_edy_parser_vtable)
+#define ARCHIMEDE 0x01
#define IQ700 0x05
#define EDY 0x08
@@ -35,9 +36,22 @@
typedef struct cressi_edy_parser_t cressi_edy_parser_t;
+typedef struct cressi_edy_layout_t {
+ unsigned int datetime_y;
+ unsigned int datetime_md;
+ unsigned int datetime_hm;
+ unsigned int avgdepth;
+ unsigned int maxdepth;
+ unsigned int temperature;
+ unsigned int divetime;
+ unsigned int gasmix;
+ unsigned int gasmix_count;
+} cressi_edy_layout_t;
+
struct cressi_edy_parser_t {
dc_parser_t base;
unsigned int model;
+ const cressi_edy_layout_t *layout;
};
static dc_status_t cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
@@ -56,16 +70,61 @@ static const dc_parser_vtable_t cressi_edy_parser_vtable = {
NULL /* destroy */
};
+static const cressi_edy_layout_t edy = {
+ 8, /* datetime_y */
+ 10, /* datetime_md */
+ 28, /* datetime_hm */
+ 1, /* avgdepth */
+ 5, /* maxdepth */
+ 22, /* temperature */
+ 25, /* divetime */
+ 46, 3, /* gasmix */
+};
+
+static const cressi_edy_layout_t archimede = {
+ 2, /* datetime_y */
+ 5, /* datetime_md */
+ 25, /* datetime_hm */
+ 22, /* avgdepth */
+ 9, /* maxdepth */
+ 45, /* temperature */
+ 29, /* divetime */
+ 43, 1, /* gasmix */
+};
static unsigned int
-cressi_edy_parser_count_gasmixes (const unsigned char *data)
+decode (const unsigned char data[], unsigned int offset, unsigned int n)
+{
+ unsigned int result = 0;
+
+ for (unsigned int i = 0; i < n; ++i) {
+ unsigned char byte = data[offset / 2];
+
+ unsigned char nibble = 0;
+ if ((offset & 1) == 0) {
+ nibble = (byte >> 4) & 0x0F;
+ } else {
+ nibble = byte & 0x0F;
+ }
+
+ result *= 10;
+ result += nibble;
+ offset++;
+ }
+
+ return result;
+}
+
+static unsigned int
+cressi_edy_parser_count_gasmixes (const unsigned char data[], const cressi_edy_layout_t *layout)
{
// Count the number of active gas mixes. The active gas
// mixes are always first, so we stop counting as soon
// as the first gas marked as disabled is found.
unsigned int i = 0;
- while (i < 3) {
- if (data[0x17 - i] == 0xF0)
+ while (i < layout->gasmix_count) {
+ unsigned int state = decode(data, layout->gasmix - i * 2, 1);
+ if (state == 0x0F)
break;
i++;
}
@@ -90,6 +149,12 @@ cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, const unsign
// Set the default values.
parser->model = model;
+ if (model == ARCHIMEDE) {
+ parser->layout = &archimede;
+ } else {
+ parser->layout = &edy;
+ }
+
*out = (dc_parser_t*) parser;
return DC_STATUS_SUCCESS;
@@ -99,17 +164,19 @@ cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, const unsign
static dc_status_t
cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
+ cressi_edy_parser_t *parser = (cressi_edy_parser_t *) abstract;
+ const cressi_edy_layout_t *layout = parser->layout;
+ const unsigned char *data = abstract->data;
+
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
- const unsigned char *p = abstract->data;
-
if (datetime) {
- datetime->year = bcd2dec (p[4]) + 2000;
- datetime->month = (p[5] & 0xF0) >> 4;
- datetime->day = (p[5] & 0x0F) * 10 + ((p[6] & 0xF0) >> 4);
- datetime->hour = bcd2dec (p[14]);
- datetime->minute = bcd2dec (p[15]);
+ datetime->year = decode(data, layout->datetime_y, 2) + 2000;
+ datetime->month = decode(data, layout->datetime_md, 1);
+ datetime->day = decode(data, layout->datetime_md + 1, 2);
+ datetime->hour = decode(data, layout->datetime_hm, 2);
+ datetime->minute = decode(data, layout->datetime_hm + 2, 2);
datetime->second = 0;
datetime->timezone = DC_TIMEZONE_NONE;
}
@@ -122,36 +189,39 @@ static dc_status_t
cressi_edy_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
cressi_edy_parser_t *parser = (cressi_edy_parser_t *) abstract;
+ const cressi_edy_layout_t *layout = parser->layout;
+ const unsigned char *data = abstract->data;
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
- const unsigned char *p = abstract->data;
-
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
if (parser->model == EDY)
- *((unsigned int *) value) = bcd2dec (p[0x0C] & 0x0F) * 60 + bcd2dec (p[0x0D]);
+ *((unsigned int *) value) = decode(data, layout->divetime, 1) * 60 + decode(data, layout->divetime + 1, 2);
else
- *((unsigned int *) value) = (bcd2dec (p[0x0C] & 0x0F) * 100 + bcd2dec (p[0x0D])) * 60;
+ *((unsigned int *) value) = decode(data, layout->divetime, 3) * 60;
break;
case DC_FIELD_MAXDEPTH:
- *((double *) value) = (bcd2dec (p[0x02] & 0x0F) * 100 + bcd2dec (p[0x03])) / 10.0;
+ *((double *) value) = decode(data, layout->maxdepth, 3) / 10.0;
+ break;
+ case DC_FIELD_AVGDEPTH:
+ *((double *) value) = decode(data, layout->avgdepth, 3) / 10.0;
break;
case DC_FIELD_GASMIX_COUNT:
- *((unsigned int *) value) = cressi_edy_parser_count_gasmixes(p);
+ *((unsigned int *) value) = cressi_edy_parser_count_gasmixes(data, layout);
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
- gasmix->oxygen = bcd2dec (p[0x17 - flags]) / 100.0;
+ gasmix->oxygen = decode(data, layout->gasmix - flags * 2, 2) / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
- *((double *) value) = (bcd2dec (p[0x0B]) * 100 + bcd2dec (p[0x0C])) / 100.0;
+ *((double *) value) = decode(data, layout->temperature, 3) / 10.0;
break;
default:
return DC_STATUS_UNSUPPORTED;
@@ -166,6 +236,7 @@ static dc_status_t
cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
cressi_edy_parser_t *parser = (cressi_edy_parser_t *) abstract;
+ const cressi_edy_layout_t *layout = parser->layout;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
@@ -179,7 +250,7 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
interval = 15;
}
- unsigned int ngasmixes = cressi_edy_parser_count_gasmixes(data);
+ unsigned int ngasmixes = cressi_edy_parser_count_gasmixes(data, layout);
unsigned int gasmix = 0xFFFFFFFF;
unsigned int offset = SZ_HEADER;
@@ -199,14 +270,14 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
- unsigned int depth = bcd2dec (data[offset + 0] & 0x0F) * 100 + bcd2dec (data[offset + 1]);
+ unsigned int depth = decode(data + offset, 1, 3);
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Current gasmix
if (ngasmixes) {
unsigned int idx = (data[offset + 0] & 0x60) >> 5;
- if (parser->model == IQ700)
+ if (parser->model == IQ700 || parser->model == ARCHIMEDE)
idx = 0; /* FIXME */
if (idx >= ngasmixes) {
ERROR (abstract->context, "Invalid gas mix index.");
diff --git a/src/cressi_goa.c b/src/cressi_goa.c
index 2b977767..0fe88519 100644
--- a/src/cressi_goa.c
+++ b/src/cressi_goa.c
@@ -522,6 +522,8 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
const unsigned char *id_data = dc_buffer_get_data(id);
size_t id_size = dc_buffer_get_size(id);
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", id_data, id_size);
+
if (id_size < 9) {
ERROR (abstract->context, "Unexpected version length (" DC_PRINTF_SIZE ").", id_size);
status = DC_STATUS_DATAFORMAT;
@@ -546,7 +548,7 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
version = 2;
} else if (firmware >= 100 && firmware <= 110) {
version = 3;
- } else if (firmware >= 200 && firmware <= 205) {
+ } else if (firmware >= 200 && firmware <= 299) {
version = 4;
} else if (firmware >= 300) {
version = 5;
diff --git a/src/cressi_goa_parser.c b/src/cressi_goa_parser.c
index 22e9c659..1de995b5 100644
--- a/src/cressi_goa_parser.c
+++ b/src/cressi_goa_parser.c
@@ -24,6 +24,7 @@
#include "cressi_goa.h"
#include "context-private.h"
#include "parser-private.h"
+#include "checksum.h"
#include "array.h"
#define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable)
@@ -332,7 +333,7 @@ cressi_goa_init(cressi_goa_parser_t *parser)
version = 2;
} else if (firmware >= 100 && firmware <= 110) {
version = 3;
- } else if (firmware >= 200 && firmware <= 205) {
+ } else if (firmware >= 200 && firmware <= 299) {
version = 4;
} else if (firmware >= 300) {
version = 5;
@@ -366,6 +367,13 @@ cressi_goa_init(cressi_goa_parser_t *parser)
return DC_STATUS_DATAFORMAT;
}
+ unsigned short crc = array_uint16_le (data + headersize + layout->headersize - 2);
+ unsigned short ccrc = checksum_crc16_ccitt (data + headersize, layout->headersize - 2, 0xffff, 0x0000);
+ if (crc != ccrc) {
+ ERROR (abstract->context, "Unexpected header checksum (%04x %04x).", crc, ccrc);
+ return DC_STATUS_DATAFORMAT;
+ }
+
parser->layout = layout;
parser->headersize = headersize;
parser->divemode = divemode;
diff --git a/src/deepblu_cosmiq.c b/src/deepblu_cosmiq.c
index 54548423..78c91ce6 100644
--- a/src/deepblu_cosmiq.c
+++ b/src/deepblu_cosmiq.c
@@ -391,6 +391,8 @@ deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
goto error_exit;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Firmware", fw, sizeof(fw));
+
// Read the MAC address.
unsigned char mac[6] = {0};
status = deepblu_cosmiq_transfer (device, CMD_SYSTEM_MAC, &zero, 1, mac, sizeof(mac));
@@ -399,6 +401,8 @@ deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
goto error_exit;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Serial", mac, sizeof(mac));
+
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
diff --git a/src/deepsix_excursion.c b/src/deepsix_excursion.c
index de0bb649..1ed78677 100644
--- a/src/deepsix_excursion.c
+++ b/src/deepsix_excursion.c
@@ -283,6 +283,8 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Hardware", rsp_hardware, sizeof(rsp_hardware));
+
// Read the software version.
unsigned char rsp_software[6] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_SOFTWARE, DIR_READ, NULL, 0, rsp_software, sizeof(rsp_software), NULL);
@@ -291,6 +293,8 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Software", rsp_software, sizeof(rsp_software));
+
// Read the serial number
unsigned char rsp_serial[12] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_SERIAL, DIR_READ, NULL, 0, rsp_serial, sizeof(rsp_serial), NULL);
@@ -299,6 +303,8 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Serial", rsp_serial, sizeof(rsp_serial));
+
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
diff --git a/src/deepsix_excursion_parser.c b/src/deepsix_excursion_parser.c
index e197891e..b448cfe5 100644
--- a/src/deepsix_excursion_parser.c
+++ b/src/deepsix_excursion_parser.c
@@ -29,6 +29,7 @@
#include "deepsix_excursion.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#define HEADERSIZE_MIN 128
@@ -403,7 +404,7 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
break;
}
- unsigned int misc = data[offset + 1];
+ unsigned int DC_ATTR_UNUSED misc = data[offset + 1];
unsigned int depth = array_uint16_le(data + offset + 2);
if (type == TEMPERATURE) {
@@ -416,8 +417,8 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
}
if (type == ALARM) {
- unsigned int alarm_time = array_uint16_le(data + offset + 4);
- unsigned int alarm_value = array_uint16_le(data + offset + 6);
+ unsigned int DC_ATTR_UNUSED alarm_time = array_uint16_le(data + offset + 4);
+ unsigned int DC_ATTR_UNUSED alarm_value = array_uint16_le(data + offset + 6);
} else if (type == TEMPERATURE) {
unsigned int temperature = array_uint16_le(data + offset + 4);
if (firmware4c) {
@@ -432,10 +433,10 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
} else if (type == DECO) {
- unsigned int deco = array_uint16_le(data + offset + 4);
+ unsigned int DC_ATTR_UNUSED deco = array_uint16_le(data + offset + 4);
} else if (type == CEILING) {
- unsigned int ceiling_depth = array_uint16_le(data + offset + 4);
- unsigned int ceiling_time = array_uint16_le(data + offset + 6);
+ unsigned int DC_ATTR_UNUSED ceiling_depth = array_uint16_le(data + offset + 4);
+ unsigned int DC_ATTR_UNUSED ceiling_time = array_uint16_le(data + offset + 6);
} else if (type == CNS) {
unsigned int cns = array_uint16_le(data + offset + 4);
sample.cns = cns / 100.0;
diff --git a/src/descriptor.c b/src/descriptor.c
index ec463e44..83fb1459 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -29,9 +29,7 @@
#include "iterator-private.h"
#include "platform.h"
-
-#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
-#define C_ARRAY_ITEMSIZE(array) (sizeof *(array))
+#include "array.h"
#define DC_FILTER_INTERNAL(key, values, isnullterminated, match) \
dc_filter_internal( \
@@ -41,25 +39,30 @@
C_ARRAY_ITEMSIZE(values), \
match)
+#undef dc_descriptor_iterator
+
+dc_status_t dc_descriptor_iterator (dc_iterator_t **out);
+
typedef int (*dc_match_t)(const void *, const void *);
-typedef int (*dc_filter_t) (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-
-static int dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_mclean (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_deepsix (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
-static int dc_filter_cressi (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+typedef int (*dc_filter_t) (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+
+static int dc_filter_uwatec (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_suunto (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_shearwater (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_hw (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_tecdiving (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_mares (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_divesystem (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_oceanic (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_mclean (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_atomic (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_deepsix (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_deepblu (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_oceans (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_divesoft (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_cressi (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
+static int dc_filter_halcyon (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
// Not merged upstream yet
static int dc_filter_garmin (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
@@ -321,6 +324,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
+ {"Mares", "Puck Lite", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
/* Heinrichs Weikamp */
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
@@ -340,6 +344,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw},
{"Heinrichs Weikamp", "OSTC 2 TR", DC_FAMILY_HW_OSTC3, 0x33, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw},
/* Cressi Edy */
+ {"Cressi", "Archimede", DC_FAMILY_CRESSI_EDY, 0x01, DC_TRANSPORT_SERIAL, NULL},
{"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05, DC_TRANSPORT_SERIAL, NULL},
{"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08, DC_TRANSPORT_SERIAL, NULL},
/* Cressi Leonardo */
@@ -477,8 +482,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"Genesis", "Centauri", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
{"Scorpena", "Alpha", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
/* Seac Screen */
- {"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
- {"Seac", "Action", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
+ {"Seac", "Action", DC_FAMILY_SEAC_SCREEN, 0x01, DC_TRANSPORT_SERIAL, NULL},
+ {"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0x02, DC_TRANSPORT_SERIAL, NULL},
+ {"Seac", "Tablet", DC_FAMILY_SEAC_SCREEN, 0x10, DC_TRANSPORT_SERIAL, NULL},
/* Deepblu Cosmiq */
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
/* Oceans S1 */
@@ -486,6 +492,9 @@ static const dc_descriptor_t g_descriptors[] = {
/* Divesoft Freedom */
{"Divesoft", "Freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19, DC_TRANSPORT_BLE, dc_filter_divesoft},
{"Divesoft", "Liberty", DC_FAMILY_DIVESOFT_FREEDOM, 10, DC_TRANSPORT_BLE, dc_filter_divesoft},
+ /* Halcyon Symbios */
+ {"Halcyon", "Symbios HUD", DC_FAMILY_HALCYON_SYMBIOS, 1, DC_TRANSPORT_BLE, dc_filter_halcyon},
+ {"Halcyon", "Symbios Handset", DC_FAMILY_HALCYON_SYMBIOS, 7, DC_TRANSPORT_BLE, dc_filter_halcyon},
// Not merged upstream yet
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
@@ -498,59 +507,50 @@ static const dc_descriptor_t g_descriptors[] = {
};
static int
-dc_match_name (const void *key, const void *value)
+dc_match_usb (const void *key, const void *value)
{
- const char *k = (const char *) key;
- const char *v = *(const char * const *) value;
+ const dc_usb_desc_t *k = (const dc_usb_desc_t *) key;
+ const dc_usb_desc_t *v = (const dc_usb_desc_t *) value;
- return strcasecmp (k, v) == 0;
+ return k->vid == v->vid && k->pid == v->pid;
}
static int
-dc_match_prefix (const void *key, const void *value)
+dc_match_usbhid (const void *key, const void *value)
{
- const char *k = (const char *) key;
- const char *v = *(const char * const *) value;
+ const dc_usbhid_desc_t *k = (const dc_usbhid_desc_t *) key;
+ const dc_usbhid_desc_t *v = (const dc_usbhid_desc_t *) value;
- return strncasecmp (k, v, strlen (v)) == 0;
+ return k->vid == v->vid && k->pid == v->pid;
}
static int
-dc_match_devname (const void *key, const void *value)
+dc_match_name (const void *key, const void *value)
{
const char *k = (const char *) key;
const char *v = *(const char * const *) value;
- return strncmp (k, v, strlen (v)) == 0;
-}
-
-static int
-dc_match_usb (const void *key, const void *value)
-{
- const dc_usb_desc_t *k = (const dc_usb_desc_t *) key;
- const dc_usb_desc_t *v = (const dc_usb_desc_t *) value;
-
- return k->vid == v->vid && k->pid == v->pid;
+ return strcasecmp (k, v) == 0;
}
static int
-dc_match_usbhid (const void *key, const void *value)
+dc_match_prefix (const void *key, const void *value)
{
- const dc_usbhid_desc_t *k = (const dc_usbhid_desc_t *) key;
- const dc_usbhid_desc_t *v = (const dc_usbhid_desc_t *) value;
+ const char *k = (const char *) key;
+ const char *v = *(const char * const *) value;
- return k->vid == v->vid && k->pid == v->pid;
+ return strncasecmp (k, v, strlen (v)) == 0;
}
static int
-dc_match_number_with_prefix (const void *key, const void *value)
+dc_match_prefix_with_number (const void *key, const void *value)
{
const char *str = (const char *) key;
const char *prefix = *(const char * const *) value;
size_t n = strlen (prefix);
- if (strncmp (str, prefix, n) != 0) {
+ if (strncasecmp (str, prefix, n) != 0) {
return 0;
}
@@ -566,14 +566,14 @@ dc_match_number_with_prefix (const void *key, const void *value)
}
static int
-dc_match_hex_with_prefix (const void *key, const void *value)
+dc_match_prefix_with_hex (const void *key, const void *value)
{
const char *str = (const char *) key;
const char *prefix = *(const char * const *) value;
size_t n = strlen (prefix);
- if (strncmp (str, prefix, n) != 0) {
+ if (strncasecmp (str, prefix, n) != 0) {
return 0;
}
@@ -603,7 +603,7 @@ dc_match_oceanic (const void *key, const void *value)
const char *p = prefix;
- return dc_match_number_with_prefix (key, &p);
+ return dc_match_prefix_with_number (key, &p);
}
static int
@@ -613,11 +613,40 @@ dc_match_cressi (const void *key, const void *value)
char prefix[16] = {0};
- dc_platform_snprintf(prefix, sizeof(prefix), "%u_", model);
+ dc_platform_snprintf(prefix, sizeof(prefix), "%x_", model);
const char *p = prefix;
- return dc_match_hex_with_prefix (key, &p);
+ return dc_match_prefix_with_hex (key, &p);
+}
+
+static int
+dc_match_halcyon (const void *key, const void *value)
+{
+ const char *str = (const char *) key;
+ unsigned int model = *(const unsigned int *) value;
+
+ char prefix[16] = {0};
+ dc_platform_snprintf(prefix, sizeof(prefix), "H%02u", model);
+
+ if (strncasecmp (str, prefix, 3) == 0) {
+ return 1;
+ }
+
+ size_t n = 0;
+ while (str[n] != 0) {
+ const char c = str[n];
+ if (c < '0' || c > '9') {
+ return 0;
+ }
+ n++;
+ }
+
+ if (n < 10) {
+ return 0;
+ }
+
+ return strncasecmp (str + 4, prefix + 1, 2) == 0;
}
static int
@@ -635,15 +664,8 @@ dc_filter_internal (const void *key, const void *values, size_t count, size_t si
return count == 0;
}
-static const char * const rfcomm[] = {
-#if defined (__linux__)
- "/dev/rfcomm",
-#endif
- NULL
-};
-
static int
-dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_uwatec (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const irda[] = {
"Aladin Smart Com",
@@ -684,7 +706,7 @@ dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const v
}
static int
-dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_suunto (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const dc_usbhid_desc_t usbhid[] = {
{0x1493, 0x0030}, // Eon Steel
@@ -709,7 +731,7 @@ dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const v
}
static int
-dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_hw (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"OSTC",
@@ -718,15 +740,13 @@ dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
- } else if (transport == DC_TRANSPORT_SERIAL) {
- return DC_FILTER_INTERNAL (userdata, rfcomm, 1, dc_match_devname);
}
return 1;
}
static int
-dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_shearwater (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Predator",
@@ -744,15 +764,13 @@ dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, con
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
- } else if (transport == DC_TRANSPORT_SERIAL) {
- return DC_FILTER_INTERNAL (userdata, rfcomm, 1, dc_match_devname);
}
return 1;
}
static int
-dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_tecdiving (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"DiveComputer",
@@ -760,15 +778,13 @@ dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, cons
if (transport == DC_TRANSPORT_BLUETOOTH) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
- } else if (transport == DC_TRANSPORT_SERIAL) {
- return DC_FILTER_INTERNAL (userdata, rfcomm, 1, dc_match_devname);
}
return 1;
}
static int
-dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_mares (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Mares bluelink pro",
@@ -776,6 +792,7 @@ dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const vo
"Sirius",
"Quad Ci",
"Puck4",
+ "Puck Lite",
};
if (transport == DC_TRANSPORT_BLE) {
@@ -786,7 +803,7 @@ dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const vo
}
static int
-dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_divesystem (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"DS",
@@ -795,14 +812,14 @@ dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, con
};
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
- return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_number_with_prefix);
+ return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix_with_number);
}
return 1;
}
static int
-dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_oceanic (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const unsigned int model[] = {
0x4552, // Oceanic Pro Plus X
@@ -833,7 +850,7 @@ dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const
}
static int
-dc_filter_mclean(dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_mclean(const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"McLean Extreme",
@@ -841,15 +858,13 @@ dc_filter_mclean(dc_descriptor_t *descriptor, dc_transport_t transport, const vo
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
- } else if (transport == DC_TRANSPORT_SERIAL) {
- return DC_FILTER_INTERNAL(userdata, rfcomm, 1, dc_match_devname);
}
return 1;
}
static int
-dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_atomic (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const dc_usb_desc_t usb[] = {
{0x0471, 0x0888}, // Atomic Aquatics Cobalt
@@ -863,7 +878,7 @@ dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const v
}
static int
-dc_filter_deepsix (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_deepsix (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"EXCURSION",
@@ -880,7 +895,7 @@ dc_filter_deepsix (dc_descriptor_t *descriptor, dc_transport_t transport, const
}
static int
-dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_deepblu (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"COSMIQ",
@@ -894,7 +909,7 @@ dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const
}
static int
-dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_oceans (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"S1",
@@ -908,7 +923,7 @@ dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const v
}
static int
-dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_divesoft (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Freedom",
@@ -923,7 +938,7 @@ dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const
}
static int
-dc_filter_cressi (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_filter_cressi (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const unsigned int model[] = {
1, // Cartesio
@@ -942,8 +957,23 @@ dc_filter_cressi (dc_descriptor_t *descriptor, dc_transport_t transport, const v
return 1;
}
+static int
+dc_filter_halcyon (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+{
+ static const unsigned int model[] = {
+ 1, // Symbios HUD
+ 7, // Symbios Handset
+ };
+
+ if (transport == DC_TRANSPORT_BLE) {
+ return DC_FILTER_INTERNAL (userdata, model, 0, dc_match_halcyon);
+ }
+
+ return 1;
+}
+
dc_status_t
-dc_descriptor_iterator (dc_iterator_t **out)
+dc_descriptor_iterator_new (dc_iterator_t **out, dc_context_t *context)
{
dc_descriptor_iterator_t *iterator = NULL;
@@ -961,6 +991,12 @@ dc_descriptor_iterator (dc_iterator_t **out)
return DC_STATUS_SUCCESS;
}
+dc_status_t
+dc_descriptor_iterator (dc_iterator_t **out)
+{
+ return dc_descriptor_iterator_new (out, NULL);
+}
+
static dc_status_t
dc_descriptor_iterator_next (dc_iterator_t *abstract, void *out)
{
@@ -989,7 +1025,7 @@ dc_descriptor_free (dc_descriptor_t *descriptor)
}
const char *
-dc_descriptor_get_vendor (dc_descriptor_t *descriptor)
+dc_descriptor_get_vendor (const dc_descriptor_t *descriptor)
{
if (descriptor == NULL)
return NULL;
@@ -998,7 +1034,7 @@ dc_descriptor_get_vendor (dc_descriptor_t *descriptor)
}
const char *
-dc_descriptor_get_product (dc_descriptor_t *descriptor)
+dc_descriptor_get_product (const dc_descriptor_t *descriptor)
{
if (descriptor == NULL)
return NULL;
@@ -1007,7 +1043,7 @@ dc_descriptor_get_product (dc_descriptor_t *descriptor)
}
dc_family_t
-dc_descriptor_get_type (dc_descriptor_t *descriptor)
+dc_descriptor_get_type (const dc_descriptor_t *descriptor)
{
if (descriptor == NULL)
return DC_FAMILY_NULL;
@@ -1016,7 +1052,7 @@ dc_descriptor_get_type (dc_descriptor_t *descriptor)
}
unsigned int
-dc_descriptor_get_model (dc_descriptor_t *descriptor)
+dc_descriptor_get_model (const dc_descriptor_t *descriptor)
{
if (descriptor == NULL)
return 0;
@@ -1025,7 +1061,7 @@ dc_descriptor_get_model (dc_descriptor_t *descriptor)
}
unsigned int
-dc_descriptor_get_transports (dc_descriptor_t *descriptor)
+dc_descriptor_get_transports (const dc_descriptor_t *descriptor)
{
if (descriptor == NULL)
return DC_TRANSPORT_NONE;
@@ -1034,9 +1070,15 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
}
int
-dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
+dc_descriptor_filter (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
- if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL)
+ if (descriptor == NULL)
+ return 1;
+
+ if ((descriptor->transports & transport) == 0)
+ return 0;
+
+ if (descriptor->filter == NULL || userdata == NULL)
return 1;
return descriptor->filter (descriptor, transport, userdata);
diff --git a/src/device.c b/src/device.c
index 172f9411..5dbd57b3 100644
--- a/src/device.c
+++ b/src/device.c
@@ -66,6 +66,7 @@
#include "deepblu_cosmiq.h"
#include "oceans_s1.h"
#include "divesoft_freedom.h"
+#include "halcyon_symbios.h"
// Not merged upstream yet
#include "garmin.h"
@@ -250,6 +251,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_DIVESOFT_FREEDOM:
rc = divesoft_freedom_device_open (&device, context, iostream);
break;
+ case DC_FAMILY_HALCYON_SYMBIOS:
+ rc = halcyon_symbios_device_open (&device, context, iostream);
+ break;
default:
return DC_STATUS_INVALIDARGS;
@@ -321,6 +325,8 @@ dc_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsi
if (device->vtable->set_fingerprint == NULL)
return DC_STATUS_UNSUPPORTED;
+ HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Fingerprint", data, size);
+
return device->vtable->set_fingerprint (device, data, size);
}
diff --git a/src/diverite_nitekq.c b/src/diverite_nitekq.c
index 676fb90e..aee0784f 100644
--- a/src/diverite_nitekq.c
+++ b/src/diverite_nitekq.c
@@ -191,6 +191,8 @@ diverite_nitekq_device_open (dc_device_t **out, dc_context_t *context, dc_iostre
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->version, sizeof (device->version));
+
*out = (dc_device_t*) device;
return DC_STATUS_SUCCESS;
diff --git a/src/divesoft_freedom.c b/src/divesoft_freedom.c
index ddf9162f..80117769 100644
--- a/src/divesoft_freedom.c
+++ b/src/divesoft_freedom.c
@@ -344,6 +344,8 @@ divesoft_freedom_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
goto error_free_hdlc;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Connection", rsp_connect, sizeof(rsp_connect));
+
DEBUG (context, "Connection: compression=%u, protocol=%u.%u, serial=%.16s",
array_uint16_le (rsp_connect),
rsp_connect[2], rsp_connect[3],
@@ -402,6 +404,8 @@ divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
goto error_exit;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", rsp_version, sizeof(rsp_version));
+
DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u serial=%.16s",
rsp_version[0],
rsp_version[1], rsp_version[2],
@@ -436,7 +440,6 @@ divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
unsigned int recordsize = 0;
// Download the dive list.
- unsigned int ndives = 0;
unsigned int total = 0;
unsigned int maxsize = 0;
unsigned int current = INVALID;
@@ -509,7 +512,6 @@ divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
offset += recordsize;
count++;
- ndives++;
}
// Append the records to the dive list buffer.
diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c
index 9841a6f9..ece18e1d 100644
--- a/src/divesystem_idive.c
+++ b/src/divesystem_idive.c
@@ -453,6 +453,8 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", packet, commands->id.size);
+
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = array_uint16_le (packet);
diff --git a/src/halcyon_symbios.c b/src/halcyon_symbios.c
new file mode 100644
index 00000000..1ecdb992
--- /dev/null
+++ b/src/halcyon_symbios.c
@@ -0,0 +1,579 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 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
+
+#include "halcyon_symbios.h"
+#include "context-private.h"
+#include "device-private.h"
+#include "platform.h"
+#include "checksum.h"
+#include "array.h"
+
+#define CMD_GET_STATUS 0x01
+#define CMD_GET_SETTINGS 0x02
+#define CMD_SET_SETTINGS 0x03
+#define CMD_LOGBOOK_REQUEST 0x04
+#define CMD_DIVELOG_REQUEST 0x05
+#define CMD_SET_TIME 0x07
+#define CMD_LOGBOOK_BLOCK 0x08
+#define CMD_DIVELOG_BLOCK 0x09
+
+#define CMD_RESPONSE 0x80
+
+#define ERR_BASE 0x80000000
+#define ERR_CRC 0
+#define ERR_BOUNDARY 1
+#define ERR_CMD_LENGTH 2
+#define ERR_CMD_UNKNOWN 3
+#define ERR_TIMEOUT 4
+#define ERR_FILE 5
+#define ERR_UNKNOWN 6
+
+#define ACK 0x06
+#define NAK 0x15
+
+#define MAXRETRIES 3
+
+#define MAXPACKET 256
+
+#define SZ_BLOCK 200
+#define SZ_LOGBOOK 32
+
+#define FP_OFFSET 20
+#define FP_SIZE 4
+
+#define NSTEPS 1000
+#define STEP(i,n) (NSTEPS * (i) / (n))
+
+typedef struct halcyon_symbios_device_t {
+ dc_device_t base;
+ dc_iostream_t *iostream;
+ unsigned char fingerprint[FP_SIZE];
+} halcyon_symbios_device_t;
+
+static dc_status_t halcyon_symbios_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
+static dc_status_t halcyon_symbios_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
+static dc_status_t halcyon_symbios_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
+
+static const dc_device_vtable_t halcyon_symbios_device_vtable = {
+ sizeof(halcyon_symbios_device_t),
+ DC_FAMILY_HALCYON_SYMBIOS,
+ halcyon_symbios_device_set_fingerprint, /* set_fingerprint */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* dump */
+ halcyon_symbios_device_foreach, /* foreach */
+ halcyon_symbios_device_timesync, /* timesync */
+ NULL, /* close */
+};
+
+static dc_status_t
+halcyon_symbios_send (halcyon_symbios_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+ unsigned char packet[1 + MAXPACKET + 1] = {0};
+ unsigned int length = 1;
+
+ if (device_is_cancelled (abstract))
+ return DC_STATUS_CANCELLED;
+
+ if (size > MAXPACKET)
+ return DC_STATUS_INVALIDARGS;
+
+ // Setup the data packet
+ packet[0] = cmd;
+ if (size) {
+ memcpy (packet + 1, data, size);
+ packet[1 + size] = checksum_crc8 (data, size, 0x00, 0x00);
+ length += size + 1;
+ }
+
+ // Send the data packet.
+ status = dc_iostream_write (device->iostream, packet, length, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ return status;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+halcyon_symbios_recv (halcyon_symbios_device_t *device, unsigned char cmd, unsigned char data[], unsigned int size, unsigned int *actual, unsigned int *errorcode)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+ unsigned char packet[2 + MAXPACKET + 1] = {0};
+ unsigned int length = 0;
+ unsigned int errcode = 0;
+
+ // Receive the answer.
+ size_t len = 0;
+ status = dc_iostream_read (device->iostream, packet, sizeof(packet), &len);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the packet.");
+ goto error_exit;
+ }
+
+ // Verify the minimum length of the packet.
+ if (len < 3) {
+ ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", len);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Verify the checksum.
+ unsigned char crc = packet[len - 1];
+ unsigned char ccrc = checksum_crc8 (packet + 1, len - 2, 0x00, 0x00);
+ if (crc != ccrc) {
+ ERROR (abstract->context, "Unexpected packet checksum (%02x %02x).", crc, ccrc);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Verify the command byte.
+ unsigned int rsp = packet[0];
+ unsigned int expected = cmd | CMD_RESPONSE;
+ if (rsp != expected) {
+ ERROR (abstract->context, "Unexpected command byte (%02x).", rsp);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Verify the ACK/NAK byte.
+ unsigned int type = packet[1];
+ if (type != ACK && type != NAK) {
+ ERROR (abstract->context, "Unexpected ACK/NAK byte (%02x).", type);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Get the error code from a NAK packet.
+ if (type == NAK) {
+ // Verify the length of the NAK packet.
+ if (len != 4) {
+ ERROR (abstract->context, "Unexpected NAK packet length (" DC_PRINTF_SIZE ").", len);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Set the ERR_BASE bit to indicate an error code is available.
+ errcode = packet[2] | ERR_BASE;
+
+ ERROR (abstract->context, "Received NAK packet with error code %u.", errcode & ~ERR_BASE);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Verify the maximum length of the packet.
+ if (len - 3 > size) {
+ ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", len);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ if (len - 3) {
+ memcpy (data, packet + 2, len - 3);
+ }
+ length = len - 3;
+
+error_exit:
+ if (actual) {
+ *actual = length;
+ }
+ if (errorcode) {
+ *errorcode = errcode;
+ }
+ return status;
+}
+
+static dc_status_t
+halcyon_symbios_transfer (halcyon_symbios_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *errorcode)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+ unsigned int errcode = 0;
+
+ // Send the command.
+ status = halcyon_symbios_send (device, cmd, data, size);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ goto error_exit;
+ }
+
+ // Receive the answer.
+ unsigned int length = 0;
+ status = halcyon_symbios_recv (device, cmd, answer, asize, &length, &errcode);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the answer.");
+ goto error_exit;
+ }
+
+ // Verify the length of the packet.
+ if (length != asize) {
+ ERROR (abstract->context, "Unexpected packet length (%u).", length);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+error_exit:
+ if (errorcode) {
+ *errorcode = errcode;
+ }
+ return status;
+}
+
+static dc_status_t
+halcyon_symbios_download (halcyon_symbios_device_t *device, dc_event_progress_t *progress, unsigned char request, const unsigned char data[], unsigned int size, unsigned char block, dc_buffer_t *buffer, unsigned int *errorcode)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+ unsigned int errcode = 0;
+
+ // Clear the buffer.
+ dc_buffer_clear (buffer);
+
+ // Request the data.
+ unsigned char response[4] = {0};
+ status = halcyon_symbios_transfer (device, request, data, size, response, sizeof(response), &errcode);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to request the data.");
+ goto error_exit;
+ }
+
+ // Get the total length.
+ unsigned int length = array_uint32_le (response);
+
+ // Resize the buffer.
+ if (!dc_buffer_reserve (buffer, length)) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ // Send the request for the first data block.
+ status = halcyon_symbios_send (device, block, NULL, 0);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ goto error_exit;
+ }
+
+ const unsigned int initial = progress ? progress->current : 0;
+
+ unsigned int counter = 1;
+ unsigned int nbytes = 0;
+ while (1) {
+ // Receive the data block.
+ unsigned int len = 0;
+ unsigned char payload[2 + SZ_BLOCK] = {0};
+ unsigned int nretries = 0;
+ while ((status = halcyon_symbios_recv (device, block, payload, sizeof(payload), &len, NULL)) != DC_STATUS_SUCCESS) {
+ // Abort if the error is fatal.
+ if (status != DC_STATUS_PROTOCOL) {
+ ERROR (abstract->context, "Failed to receive the answer.");
+ goto error_exit;
+ }
+
+ // Abort if the maximum number of retries is reached.
+ if (nretries++ >= MAXRETRIES) {
+ ERROR (abstract->context, "Reached the maximum number of retries.");
+ goto error_exit;
+ }
+
+ // Send a NAK to request a re-transmission.
+ status = halcyon_symbios_send (device, NAK, NULL, 0);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the NAK.");
+ goto error_exit;
+ }
+ }
+
+ // Verify the minimum block length.
+ if (len < 2) {
+ ERROR (abstract->context, "Unexpected block length (%u).", len);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Verify the sequence number.
+ unsigned int id = array_uint16_le (payload);
+ unsigned int seqnum = id & 0x7FFF;
+ if (seqnum != counter) {
+ ERROR (abstract->context, "Unexpected block sequence number (%04x %04x).", seqnum, counter);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ // Append the payload data to the output buffer.
+ if (!dc_buffer_append (buffer, payload + 2, len - 2)) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ nbytes += len - 2;
+ counter += 1;
+ counter &= 0x7FFF;
+
+ // Update and emit a progress event.
+ if (progress) {
+ // Limit the progress events to the announced length.
+ unsigned int n = nbytes > length ? length : nbytes;
+ progress->current = initial + STEP(n, length);
+ device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
+ }
+
+ // Send an ACK to request the next block or finalize the download.
+ status = halcyon_symbios_send (device, ACK, NULL, 0);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the ACK.");
+ goto error_exit;
+ }
+
+ // Check for the last packet.
+ if (id & 0x8000)
+ break;
+ }
+
+ // Verify the length of the data.
+ if (nbytes != length) {
+ ERROR (abstract->context, "Unexpected data length (%u %u).", nbytes, length);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+error_exit:
+ if (errorcode) {
+ *errorcode = errcode;
+ }
+ return status;
+}
+
+dc_status_t
+halcyon_symbios_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ halcyon_symbios_device_t *device = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ device = (halcyon_symbios_device_t *) dc_device_allocate (context, &halcyon_symbios_device_vtable);
+ if (device == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Set the default values.
+ device->iostream = iostream;
+ memset(device->fingerprint, 0, sizeof(device->fingerprint));
+
+ // Set the timeout for receiving data (3000ms).
+ status = dc_iostream_set_timeout (device->iostream, 3000);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the timeout.");
+ goto error_free;
+ }
+
+ // Make sure everything is in a sane state.
+ dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
+
+ *out = (dc_device_t *) device;
+
+ return DC_STATUS_SUCCESS;
+
+error_free:
+ dc_device_deallocate ((dc_device_t *) device);
+ return status;
+}
+
+static dc_status_t
+halcyon_symbios_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
+{
+ halcyon_symbios_device_t *device = (halcyon_symbios_device_t *) abstract;
+
+ if (size && size != sizeof (device->fingerprint))
+ return DC_STATUS_INVALIDARGS;
+
+ if (size)
+ memcpy (device->fingerprint, data, sizeof (device->fingerprint));
+ else
+ memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+halcyon_symbios_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ halcyon_symbios_device_t *device = (halcyon_symbios_device_t *) abstract;
+ unsigned int errcode = 0;
+
+ // Enable progress notifications.
+ dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Read the device status.
+ unsigned char info[20] = {0};
+ status = halcyon_symbios_transfer (device, CMD_GET_STATUS, NULL, 0, info, sizeof(info), NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the device status.");
+ goto error_exit;
+ }
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", info, sizeof(info));
+
+ // Emit a vendor event.
+ dc_event_vendor_t vendor;
+ vendor.data = info;
+ vendor.size = sizeof(info);
+ device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
+
+ // Emit a device info event.
+ dc_event_devinfo_t devinfo;
+ devinfo.model = info[5];
+ devinfo.firmware = 0;
+ devinfo.serial = array_uint32_le (info);
+ device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
+
+ DEBUG (abstract->context, "Device: serial=%u, hw=%u, model=%u, bt=%u.%u, battery=%u, pressure=%u, errorbits=%u",
+ array_uint32_le (info),
+ info[4], info[5], info[6], info[7],
+ array_uint16_le (info + 8),
+ array_uint16_le (info + 10),
+ array_uint32_le (info + 12));
+
+ dc_buffer_t *logbook = dc_buffer_new (0);
+ dc_buffer_t *dive = dc_buffer_new (0);
+ if (logbook == NULL || dive == NULL) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free;
+ }
+
+ // Download the logbook.
+ status = halcyon_symbios_download (device, &progress, CMD_LOGBOOK_REQUEST, NULL, 0, CMD_LOGBOOK_BLOCK, logbook, &errcode);
+ if (status != DC_STATUS_SUCCESS) {
+ if (errcode == (ERR_FILE | ERR_BASE)) {
+ WARNING (abstract->context, "Logbook not available!");
+
+ // Update and emit a progress event.
+ progress.current = NSTEPS;
+ progress.maximum = NSTEPS;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ status = DC_STATUS_SUCCESS;
+ goto error_free;
+ }
+ ERROR (abstract->context, "Failed to download the logbook.");
+ goto error_free;
+ }
+
+ const unsigned char *data = dc_buffer_get_data (logbook);
+ size_t size = dc_buffer_get_size (logbook);
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Logbook", data, size);
+
+ // Get the number of dives.
+ unsigned int ndives = 0;
+ unsigned int offset = size;
+ while (offset >= SZ_LOGBOOK) {
+ offset -= SZ_LOGBOOK;
+
+ // Compare the fingerprint to identify previously downloaded entries.
+ if (memcmp (data + offset + FP_OFFSET, device->fingerprint, sizeof(device->fingerprint)) == 0) {
+ break;
+ }
+
+ ndives++;
+ }
+
+ // Update and emit a progress event.
+ progress.current = 1 * NSTEPS;
+ progress.maximum = (ndives + 1) * NSTEPS;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ offset = size;
+ for (unsigned int i = 0; i < ndives; ++i) {
+ offset -= SZ_LOGBOOK;
+
+ // Clear the buffer.
+ dc_buffer_clear (dive);
+
+ // Download the dive.
+ status = halcyon_symbios_download (device, &progress, CMD_DIVELOG_REQUEST, data + offset + 16, 2, CMD_DIVELOG_BLOCK, dive, &errcode);
+ if (status != DC_STATUS_SUCCESS) {
+ if (errcode == (ERR_FILE | ERR_BASE)) {
+ WARNING (abstract->context, "Dive #%u not available!",
+ array_uint16_le (data + offset + 16));
+
+ // Update and emit a progress event.
+ progress.current = (i + 2) * NSTEPS;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ status = DC_STATUS_SUCCESS;
+ continue;
+ }
+ ERROR (abstract->context, "Failed to download the dive.");
+ goto error_free;
+ }
+
+ if (callback && !callback (dc_buffer_get_data (dive), dc_buffer_get_size (dive), data + offset + FP_OFFSET, FP_SIZE, userdata)) {
+ break;
+ }
+ }
+
+error_free:
+ dc_buffer_free (dive);
+ dc_buffer_free (logbook);
+error_exit:
+ return status;
+}
+
+static dc_status_t
+halcyon_symbios_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ halcyon_symbios_device_t *device = (halcyon_symbios_device_t *) abstract;
+
+ unsigned char request[] = {
+ datetime->year - 2000,
+ datetime->month,
+ datetime->day,
+ datetime->hour,
+ datetime->minute,
+ datetime->second,
+ };
+ status = halcyon_symbios_transfer (device, CMD_SET_TIME, request, sizeof(request), NULL, 0, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to set the time.");
+ goto error_exit;
+ }
+
+error_exit:
+ return status;
+}
diff --git a/src/halcyon_symbios.h b/src/halcyon_symbios.h
new file mode 100644
index 00000000..84a2c962
--- /dev/null
+++ b/src/halcyon_symbios.h
@@ -0,0 +1,43 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 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 HALCYON_SYMBIOS_H
+#define HALCYON_SYMBIOS_H
+
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+dc_status_t
+halcyon_symbios_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
+
+dc_status_t
+halcyon_symbios_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* HALCYON_SYMBIOS_H */
diff --git a/src/halcyon_symbios_parser.c b/src/halcyon_symbios_parser.c
new file mode 100644
index 00000000..32dbd4d4
--- /dev/null
+++ b/src/halcyon_symbios_parser.c
@@ -0,0 +1,716 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 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
+
+#include "halcyon_symbios.h"
+#include "context-private.h"
+#include "parser-private.h"
+#include "platform.h"
+#include "array.h"
+
+#define ID_HEADER 0x01
+#define ID_GAS_SWITCH 0x02
+#define ID_DEPTH 0x03
+#define ID_TEMPERATURE 0x04
+#define ID_OC_CC_SWITCH 0x05
+#define ID_GAS_TRANSMITTER 0x06
+#define ID_COMPARTMENTS 0x07
+#define ID_GPS 0x08
+#define ID_PO2_BOARD 0x09
+#define ID_DECO 0x0A
+#define ID_GF 0x0B
+#define ID_FOOTER 0x0C
+#define ID_PO2_REBREATHER 0x0D
+#define ID_COMPASS 0x0E
+#define ID_LOG_VERSION 0x0F
+#define ID_TRIM 0x10
+#define ID_GAS_CONFIG 0x11
+#define ID_TANK_TRANSMITTER 0x12
+#define ID_GF_INFO 0x13
+
+#define ISCONFIG(type) ( \
+ (type) == ID_LOG_VERSION || \
+ (type) == ID_HEADER || \
+ (type) == ID_FOOTER)
+
+#define UNDEFINED 0xFFFFFFFF
+
+#define EPOCH 1609459200 /* 2021-01-01 00:00:00 */
+
+#define OC 0
+#define CC 1
+#define GAUGE 2
+#define SIDEMOUNT 3
+
+#define NGASMIXES 10
+#define NTANKS 10
+
+#define TRANSMITTER_ID (1u << 16)
+
+typedef struct halcyon_symbios_gasmix_t {
+ unsigned int id;
+ unsigned int oxygen;
+ unsigned int helium;
+} halcyon_symbios_gasmix_t;
+
+typedef struct halcyon_symbios_tank_t {
+ unsigned int id;
+ unsigned int beginpressure;
+ unsigned int endpressure;
+ unsigned int gasmix;
+ dc_tank_usage_t usage;
+} halcyon_symbios_tank_t;
+
+typedef struct halcyon_symbios_parser_t {
+ dc_parser_t base;
+ // Cached fields.
+ unsigned int cached;
+ unsigned int datetime;
+ unsigned int divetime;
+ unsigned int maxdepth;
+ unsigned int divemode;
+ unsigned int atmospheric;
+ unsigned int ngasmixes;
+ unsigned int ntanks;
+ halcyon_symbios_gasmix_t gasmix[NGASMIXES];
+ halcyon_symbios_tank_t tank[NTANKS];
+ unsigned int gf_lo;
+ unsigned int gf_hi;
+ unsigned int have_location;
+ int latitude, longitude;
+} halcyon_symbios_parser_t;
+
+static dc_status_t halcyon_symbios_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
+static dc_status_t halcyon_symbios_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
+static dc_status_t halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
+
+static const dc_parser_vtable_t halcyon_symbios_parser_vtable = {
+ sizeof(halcyon_symbios_parser_t),
+ DC_FAMILY_HALCYON_SYMBIOS,
+ NULL, /* set_clock */
+ NULL, /* set_atmospheric */
+ NULL, /* set_density */
+ halcyon_symbios_parser_get_datetime, /* datetime */
+ halcyon_symbios_parser_get_field, /* fields */
+ halcyon_symbios_parser_samples_foreach, /* samples_foreach */
+ NULL /* destroy */
+};
+
+dc_status_t
+halcyon_symbios_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
+{
+ halcyon_symbios_parser_t *parser = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ parser = (halcyon_symbios_parser_t *) dc_parser_allocate (context, &halcyon_symbios_parser_vtable, data, size);
+ if (parser == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Set the default values.
+ parser->cached = 0;
+ parser->datetime = UNDEFINED;
+ parser->divetime = 0;
+ parser->maxdepth = 0;
+ parser->divemode = UNDEFINED;
+ parser->atmospheric = UNDEFINED;
+ parser->gf_lo = UNDEFINED;
+ parser->gf_hi = UNDEFINED;
+ parser->have_location = 0;
+ parser->latitude = 0;
+ parser->longitude = 0;
+ parser->ngasmixes = 0;
+ parser->ntanks = 0;
+ for (unsigned int i = 0; i < NGASMIXES; ++i) {
+ parser->gasmix[i].oxygen = 0;
+ parser->gasmix[i].helium = 0;
+ }
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i].id = 0;
+ parser->tank[i].beginpressure = 0;
+ parser->tank[i].endpressure = 0;
+ parser->tank[i].gasmix = DC_GASMIX_UNKNOWN;
+ parser->tank[i].usage = DC_TANK_USAGE_NONE;
+ }
+
+ *out = (dc_parser_t *) parser;
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+halcyon_symbios_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ halcyon_symbios_parser_t *parser = (halcyon_symbios_parser_t *) abstract;
+
+ // Cache the profile data.
+ if (!parser->cached) {
+ status = halcyon_symbios_parser_samples_foreach (abstract, NULL, NULL);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+ }
+
+ if (parser->datetime == UNDEFINED)
+ return DC_STATUS_UNSUPPORTED;
+
+ dc_ticks_t ticks = (dc_ticks_t) parser->datetime + EPOCH;
+
+ if (!dc_datetime_gmtime (datetime, ticks))
+ return DC_STATUS_DATAFORMAT;
+
+ datetime->timezone = DC_TIMEZONE_NONE;
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+halcyon_symbios_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ halcyon_symbios_parser_t *parser = (halcyon_symbios_parser_t *) abstract;
+
+ // Cache the profile data.
+ if (!parser->cached) {
+ status = halcyon_symbios_parser_samples_foreach (abstract, NULL, NULL);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+ }
+
+ dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
+ dc_tank_t *tank = (dc_tank_t *) value;
+ dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
+ dc_location_t *location = (dc_location_t *) value;
+
+ if (value) {
+ switch (type) {
+ case DC_FIELD_DIVETIME:
+ *((unsigned int *) value) = parser->divetime;
+ break;
+ case DC_FIELD_MAXDEPTH:
+ *((double *) value) = parser->maxdepth / 100.0;
+ break;
+ case DC_FIELD_ATMOSPHERIC:
+ if (parser->atmospheric == UNDEFINED)
+ return DC_STATUS_UNSUPPORTED;
+ *((double *) value) = parser->atmospheric / 1000.0;
+ break;
+ case DC_FIELD_DIVEMODE:
+ switch (parser->divemode) {
+ case OC:
+ case SIDEMOUNT:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
+ break;
+ case CC:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_CCR;
+ break;
+ case GAUGE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
+ break;
+ case UNDEFINED:
+ return DC_STATUS_UNSUPPORTED;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
+ break;
+ case DC_FIELD_GASMIX_COUNT:
+ *((unsigned int *) value) = parser->ngasmixes;
+ break;
+ case DC_FIELD_GASMIX:
+ gasmix->helium = parser->gasmix[flags].helium / 100.0;
+ gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
+ gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
+ gasmix->usage = DC_USAGE_NONE;
+ break;
+ case DC_FIELD_TANK_COUNT:
+ *((unsigned int *) value) = parser->ntanks;
+ break;
+ case DC_FIELD_TANK:
+ tank->type = DC_TANKVOLUME_NONE;
+ tank->volume = 0.0;
+ tank->workpressure = 0.0;
+ tank->beginpressure = parser->tank[flags].beginpressure / 10.0;
+ tank->endpressure = parser->tank[flags].endpressure / 10.0;
+ tank->gasmix = parser->tank[flags].gasmix;
+ tank->usage = parser->tank[flags].usage;
+ break;
+ case DC_FIELD_DECOMODEL:
+ if (parser->gf_lo == UNDEFINED || parser->gf_hi == UNDEFINED)
+ return DC_STATUS_UNSUPPORTED;
+ decomodel->type = DC_DECOMODEL_BUHLMANN;
+ decomodel->conservatism = 0;
+ decomodel->params.gf.low = parser->gf_lo;
+ decomodel->params.gf.high = parser->gf_hi;
+ break;
+ case DC_FIELD_LOCATION:
+ if (!parser->have_location)
+ return DC_STATUS_UNSUPPORTED;
+ location->latitude = parser->latitude / 1000000.0;
+ location->longitude = parser->longitude / 1000000.0;
+ location->altitude = 0.0;
+ break;
+ default:
+ return DC_STATUS_UNSUPPORTED;
+ }
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
+{
+ halcyon_symbios_parser_t *parser = (halcyon_symbios_parser_t *) abstract;
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ static const unsigned int lengths[] = {
+ 4, /* ID_LOG_VERSION */
+ 64, /* ID_HEADER */
+ 4, /* ID_GAS_SWITCH */
+ 4, /* ID_DEPTH */
+ 4, /* ID_TEMPERATURE */
+ 4, /* ID_OC_CC_SWITCH */
+ 12, /* ID_GAS_TRANSMITTER */
+ 68, /* ID_COMPARTMENTS */
+ 12, /* ID_GPS */
+ 8, /* ID_PO2_BOARD */
+ 16, /* ID_DECO */
+ 4, /* ID_GF */
+ 16, /* ID_FOOTER */
+ 12, /* ID_PO2_REBREATHER */
+ 4, /* ID_COMPASS */
+ 4, /* ID_LOG_VERSION */
+ 4, /* ID_TRIM */
+ 8, /* ID_GAS_CONFIG */
+ 8, /* ID_TANK_TRANSMITTER */
+ 6, /* ID_GF_INFO */
+ };
+
+ unsigned int time_start = UNDEFINED, time_end = UNDEFINED;
+ unsigned int maxdepth = 0;
+ unsigned int divemode = UNDEFINED;
+ unsigned int atmospheric = UNDEFINED;
+ unsigned int gf_lo = UNDEFINED;
+ unsigned int gf_hi = UNDEFINED;
+ unsigned int have_location = 0;
+ int latitude = 0, longitude = 0;
+ unsigned int ngasmixes = 0;
+ unsigned int ntanks = 0;
+ halcyon_symbios_gasmix_t gasmix[NGASMIXES] = {0};
+ halcyon_symbios_tank_t tank[NTANKS] = {0};
+ unsigned int gasmix_id_previous = UNDEFINED;
+ unsigned int gasmix_idx = DC_GASMIX_UNKNOWN;
+ unsigned int tank_id_previous = UNDEFINED;
+ unsigned int tank_usage_previous = UNDEFINED;
+ unsigned int tank_idx = UNDEFINED;
+ unsigned int interval = 0;
+
+ unsigned int have_time = 0;
+ unsigned int have_depth = 0;
+ unsigned int have_gasmix = 0;
+
+ unsigned int time = 0;
+ unsigned int offset = 0;
+ while (offset + 2 <= size) {
+ dc_sample_value_t sample = {0};
+
+ unsigned int type = data[offset + 0];
+ unsigned int length = data[offset + 1];
+
+ if (length < 2 || offset + length > size) {
+ ERROR (abstract->context, "Buffer overflow detected!");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ if (type < C_ARRAY_SIZE(lengths)) {
+ if (length != lengths[type]) {
+ ERROR (abstract->context, "Unexpected record size (%u %u).", length, lengths[type]);
+ return DC_STATUS_DATAFORMAT;
+ }
+ }
+
+ // Generate a timestamp for the first non-config record and every
+ // depth record, except the first one. The first depth record must be
+ // excluded because the sample already has a timestamp from the first
+ // non-config record.
+ if ((!have_time && !ISCONFIG(type)) ||
+ (have_depth && type == ID_DEPTH)) {
+ time += interval;
+ sample.time = time * 1000;
+ if (callback) callback(DC_SAMPLE_TIME, &sample, userdata);
+ have_time = 1;
+ }
+
+ if (type == ID_LOG_VERSION) {
+ unsigned int version_major = data[offset + 2];
+ unsigned int version_minor = data[offset + 3];
+ DEBUG (abstract->context, "Version: %u.%u",
+ version_major, version_minor);
+ } else if (type == ID_HEADER) {
+ unsigned int model = data[offset + 2];
+ unsigned int hw_major = data[offset + 3];
+ unsigned int hw_minor = data[offset + 4];
+ unsigned int fw_major = data[offset + 5];
+ unsigned int fw_minor = data[offset + 6];
+ unsigned int fw_bugfix = data[offset + 7];
+ unsigned int deco_major = data[offset + 8];
+ unsigned int deco_minor = data[offset + 9];
+ interval = data[offset + 10];
+ unsigned int DC_ATTR_UNUSED detection = data[offset + 11];
+ unsigned int DC_ATTR_UNUSED noflytime = data[offset + 12];
+ divemode = data[offset + 13];
+ atmospheric = array_uint16_le(data + offset + 16);
+ unsigned int DC_ATTR_UNUSED number = array_uint16_le(data + offset + 18);
+ unsigned int DC_ATTR_UNUSED battery = array_uint16_le(data + offset + 20);
+ time_start = array_uint32_le(data + offset + 24);
+ unsigned int serial = array_uint32_le(data + offset + 28);
+ DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, fw=%u.%u.%u, deco=%u.%u, serial=%u",
+ model,
+ hw_major, hw_minor,
+ fw_major, fw_minor, fw_bugfix,
+ deco_major, deco_minor,
+ serial);
+ } else if (type == ID_GAS_SWITCH) {
+ unsigned int id = UNDEFINED;
+ unsigned int o2 = data[offset + 2];
+ unsigned int he = data[offset + 3];
+
+ unsigned int idx = 0;
+ while (idx < ngasmixes) {
+ if (id == gasmix[idx].id &&
+ o2 == gasmix[idx].oxygen &&
+ he == gasmix[idx].helium)
+ break;
+ idx++;
+ }
+ if (idx >= ngasmixes) {
+ if (ngasmixes >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix[ngasmixes].id = id;
+ gasmix[ngasmixes].oxygen = o2;
+ gasmix[ngasmixes].helium = he;
+ ngasmixes++;
+ }
+ sample.gasmix = idx;
+ if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
+ } else if (type == ID_DEPTH) {
+ unsigned int depth = array_uint16_le (data + offset + 2);
+ if (maxdepth < depth)
+ maxdepth = depth;
+ sample.depth = depth / 100.0;
+ if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
+ have_depth = 1;
+ } else if (type == ID_TEMPERATURE) {
+ unsigned int temperature = array_uint16_le (data + offset + 2);
+ sample.depth = temperature / 10.0;
+ if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
+ } else if (type == ID_OC_CC_SWITCH) {
+ unsigned int DC_ATTR_UNUSED ccr = data[offset + 2];
+ } else if (type == ID_GAS_TRANSMITTER) {
+ unsigned int gas_id = data[offset + 2];
+ unsigned int DC_ATTR_UNUSED battery = array_uint16_le (data + offset + 4);
+ unsigned int pressure = array_uint16_le (data + offset + 6);
+ unsigned int transmitter = array_uint16_le (data + offset + 8);
+ dc_tank_usage_t usage = DC_TANK_USAGE_NONE;
+
+ if (have_gasmix && gasmix_id_previous != gas_id) {
+ unsigned int idx = 0;
+ while (idx < ngasmixes) {
+ if (gas_id == gasmix[idx].id)
+ break;
+ idx++;
+ }
+ if (idx >= ngasmixes) {
+ ERROR (abstract->context, "Invalid gas mix id (%u).", gas_id);
+ return DC_STATUS_DATAFORMAT;
+ }
+ sample.gasmix = idx;
+ if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
+ gasmix_id_previous = gas_id;
+ gasmix_idx = idx;
+ }
+
+ if (tank_id_previous != transmitter ||
+ tank_usage_previous != usage) {
+ // Find the tank in the list.
+ unsigned int idx = 0;
+ while (idx < ntanks) {
+ if (tank[idx].id == transmitter &&
+ tank[idx].usage == usage)
+ break;
+ idx++;
+ }
+
+ // Add a new tank if necessary.
+ if (idx >= ntanks) {
+ if (ngasmixes >= NTANKS) {
+ ERROR (abstract->context, "Maximum number of tanks reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ tank[ntanks].id = transmitter;
+ tank[ntanks].beginpressure = pressure;
+ tank[ntanks].endpressure = pressure;
+ tank[ntanks].gasmix = gasmix_idx;
+ tank[ntanks].usage = usage;
+ ntanks++;
+ }
+
+ tank_id_previous = transmitter;
+ tank_usage_previous = usage;
+ tank_idx = idx;
+ }
+ tank[tank_idx].endpressure = pressure;
+
+ sample.pressure.tank = tank_idx;
+ sample.pressure.value = pressure / 10.0;
+ if (callback) callback(DC_SAMPLE_PRESSURE, &sample, userdata);
+ } else if (type == ID_COMPARTMENTS) {
+ for (unsigned int i = 0; i < 16; ++i) {
+ unsigned int DC_ATTR_UNUSED n2 = array_uint16_le (data + offset + 4 + i * 2);
+ unsigned int DC_ATTR_UNUSED he = array_uint16_le (data + offset + 36 + i * 2);
+ }
+ } else if (type == ID_GPS) {
+ if (!have_location) {
+ longitude = (signed int) array_uint32_le (data + offset + 4);
+ latitude = (signed int) array_uint32_le (data + offset + 8);
+ have_location = 1;
+ } else {
+ WARNING (abstract->context, "Multiple GPS locations present.");
+ }
+ } else if (type == ID_PO2_BOARD) {
+ unsigned int DC_ATTR_UNUSED serial = array_uint16_le (data + offset + 6);
+ for (unsigned int i = 0; i < 3; ++i) {
+ unsigned int ppo2 = data[offset + 2 + i];
+ sample.ppo2.sensor = i;
+ sample.ppo2.value = ppo2 / 100.0;
+ if (callback) callback(DC_SAMPLE_PPO2, &sample, userdata);
+ }
+ } else if (type == ID_DECO) {
+ unsigned int ndt = data[offset + 2];
+ unsigned int ceiling = data[offset + 3];
+ unsigned int cns = data[offset + 4];
+ unsigned int DC_ATTR_UNUSED safetystop = data[offset + 5];
+ unsigned int DC_ATTR_UNUSED ceiling_max = array_uint16_le (data + offset + 6);
+ unsigned int tts = array_uint16_le (data + offset + 8);
+ unsigned int DC_ATTR_UNUSED otu = array_uint16_le (data + offset + 10);
+
+ // Deco / NDL
+ if (ceiling) {
+ sample.deco.type = DC_DECO_DECOSTOP;
+ sample.deco.time = 0;
+ sample.deco.depth = ceiling;
+ } else {
+ sample.deco.type = DC_DECO_NDL;
+ sample.deco.time = ndt * 60;
+ sample.deco.depth = 0.0;
+ }
+ sample.deco.tts = tts;
+ if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
+
+ sample.cns = cns / 100.0;
+ if (callback) callback(DC_SAMPLE_CNS, &sample, userdata);
+ } else if (type == ID_GF) {
+ if (gf_lo == UNDEFINED && gf_hi == UNDEFINED) {
+ gf_lo = data[offset + 2];
+ gf_hi = data[offset + 3];
+ } else {
+ WARNING (abstract->context, "Multiple GF values present.");
+ }
+ } else if (type == ID_FOOTER) {
+ unsigned int DC_ATTR_UNUSED cns = data[offset + 2];
+ unsigned int DC_ATTR_UNUSED violations = data[offset + 3];
+ unsigned int DC_ATTR_UNUSED otu = array_uint16_le (data + offset + 4);
+ unsigned int DC_ATTR_UNUSED battery = array_uint16_le (data + offset + 6);
+ time_end = array_uint32_le(data + offset + 8);
+ unsigned int DC_ATTR_UNUSED desaturation = array_uint32_le (data + offset + 12);
+ } else if (type == ID_PO2_REBREATHER) {
+ for (unsigned int i = 0; i < 3; ++i) {
+ unsigned int ppo2 = data[offset + 2 + i];
+ sample.ppo2.sensor = i;
+ sample.ppo2.value = ppo2 / 100.0;
+ if (callback) callback(DC_SAMPLE_PPO2, &sample, userdata);
+ }
+ unsigned int pressure = array_uint16_le (data + offset + 8);
+ unsigned int serial = array_uint16_le (data + offset + 10);
+ dc_tank_usage_t usage = DC_TANK_USAGE_NONE;
+
+ if (tank_id_previous != serial ||
+ tank_usage_previous != usage) {
+ // Find the tank in the list.
+ unsigned int idx = 0;
+ while (idx < ntanks) {
+ if (tank[idx].id == serial &&
+ tank[idx].usage == usage)
+ break;
+ idx++;
+ }
+
+ // Add a new tank if necessary.
+ if (idx >= ntanks) {
+ if (ngasmixes >= NTANKS) {
+ ERROR (abstract->context, "Maximum number of tanks reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ tank[ntanks].id = serial;
+ tank[ntanks].beginpressure = pressure;
+ tank[ntanks].endpressure = pressure;
+ tank[ntanks].gasmix = DC_GASMIX_UNKNOWN;
+ tank[ntanks].usage = usage;
+ ntanks++;
+ }
+
+ tank_id_previous = serial;
+ tank_usage_previous = usage;
+ tank_idx = idx;
+ }
+ tank[tank_idx].endpressure = pressure;
+
+ sample.pressure.tank = tank_idx;
+ sample.pressure.value = pressure / 10.0;
+ if (callback) callback(DC_SAMPLE_PRESSURE, &sample, userdata);
+ } else if (type == ID_COMPASS) {
+ unsigned int heading = array_uint16_le (data + offset + 4);
+ sample.bearing = heading;
+ if (callback) callback(DC_SAMPLE_BEARING, &sample, userdata);
+ } else if (type == ID_TRIM) {
+ int DC_ATTR_UNUSED trim = (signed int) data[offset + 2];
+ } else if (type == ID_GAS_CONFIG) {
+ unsigned int id = data[offset + 2];
+ unsigned int o2 = data[offset + 3];
+ unsigned int he = data[offset + 4];
+ if (o2 != 0 || he != 0) {
+ unsigned int idx = 0;
+ while (idx < ngasmixes) {
+ if (id == gasmix[idx].id)
+ break;
+ idx++;
+ }
+ if (idx >= ngasmixes) {
+ if (ngasmixes >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix[ngasmixes].id = id;
+ gasmix[ngasmixes].oxygen = o2;
+ gasmix[ngasmixes].helium = he;
+ ngasmixes++;
+ have_gasmix = 1;
+ } else {
+ if (gasmix[idx].oxygen != o2 ||
+ gasmix[idx].helium != he) {
+ ERROR (abstract->context, "Gas mix %u changed (%u/%u -> %u/%u).",
+ gasmix[idx].id,
+ gasmix[idx].oxygen, gasmix[idx].helium,
+ o2, he);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ sample.gasmix = idx;
+ if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
+ }
+ }
+ } else if (type == ID_TANK_TRANSMITTER) {
+ unsigned int id = data[offset + 2] | TRANSMITTER_ID;
+ unsigned int DC_ATTR_UNUSED battery = array_uint16_le (data + offset + 4);
+ unsigned int pressure = array_uint16_le (data + offset + 6) / 10;
+ dc_tank_usage_t usage = DC_TANK_USAGE_NONE;
+
+ if (tank_id_previous != id ||
+ tank_usage_previous != usage) {
+ // Find the tank in the list.
+ unsigned int idx = 0;
+ while (idx < ntanks) {
+ if (tank[idx].id == id &&
+ tank[idx].usage == usage)
+ break;
+ idx++;
+ }
+
+ // Add a new tank if necessary.
+ if (idx >= ntanks) {
+ if (ngasmixes >= NTANKS) {
+ ERROR (abstract->context, "Maximum number of tanks reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ tank[ntanks].id = id;
+ tank[ntanks].beginpressure = pressure;
+ tank[ntanks].endpressure = pressure;
+ tank[ntanks].gasmix = DC_GASMIX_UNKNOWN;
+ tank[ntanks].usage = usage;
+ ntanks++;
+ }
+
+ tank_id_previous = id;
+ tank_usage_previous = usage;
+ tank_idx = idx;
+ }
+ tank[tank_idx].endpressure = pressure;
+
+ sample.pressure.tank = tank_idx;
+ sample.pressure.value = pressure / 10.0;
+ if (callback) callback(DC_SAMPLE_PRESSURE, &sample, userdata);
+ } else if (type == ID_GF_INFO) {
+ unsigned int DC_ATTR_UNUSED gf_now = array_uint16_le (data + offset + 2);
+ unsigned int DC_ATTR_UNUSED gf_surface = array_uint16_le (data + offset + 4);
+ } else {
+ WARNING (abstract->context, "Unknown record (type=%u, size=%u", type, length);
+ }
+
+ offset += length;
+ }
+
+ parser->cached = 1;
+ parser->datetime = time_start;
+ if (time_start != UNDEFINED && time_end != UNDEFINED) {
+ parser->divetime = time_end - time_start;
+ } else {
+ parser->divetime = time;
+ }
+ parser->maxdepth = maxdepth;
+ parser->divemode = divemode;
+ parser->atmospheric = atmospheric;
+ parser->gf_lo = gf_lo;
+ parser->gf_hi = gf_hi;
+ parser->have_location = have_location;
+ parser->latitude = latitude;
+ parser->longitude = longitude;
+ parser->ngasmixes = ngasmixes;
+ parser->ntanks = ntanks;
+ for (unsigned int i = 0; i < NGASMIXES; ++i) {
+ parser->gasmix[i] = gasmix[i];
+ }
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i] = tank[i];
+ }
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/hw_frog.c b/src/hw_frog.c
index 2de4ebac..dcd9dec3 100644
--- a/src/hw_frog.c
+++ b/src/hw_frog.c
@@ -326,6 +326,8 @@ hw_frog_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
return rc;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", id, sizeof (id));
+
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c
index aff0ff05..b3dc1b2a 100644
--- a/src/hw_ostc3.c
+++ b/src/hw_ostc3.c
@@ -612,6 +612,8 @@ hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
return rc;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Hardware", hardware, sizeof(hardware));
+
// Read the version information.
unsigned char version[SZ_VERSION] = {0};
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NULL, NODELAY);
@@ -620,6 +622,8 @@ hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
return rc;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", version, sizeof(version));
+
// Cache the descriptor.
device->hardware = array_uint16_be(hardware + 0);
if (device->hardware != OSTC4) {
diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols
index c04e6270..859dbe11 100644
--- a/src/libdivecomputer.symbols
+++ b/src/libdivecomputer.symbols
@@ -28,6 +28,7 @@ dc_iterator_next
dc_iterator_free
dc_descriptor_iterator
+dc_descriptor_iterator_new
dc_descriptor_free
dc_descriptor_get_vendor
dc_descriptor_get_product
diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c
index 3ce87438..74916583 100644
--- a/src/liquivision_lynx.c
+++ b/src/liquivision_lynx.c
@@ -284,6 +284,8 @@ liquivision_lynx_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Info", device->info, sizeof(device->info));
+
// Send the more info command.
const unsigned char cmd_more[] = {0x4D, 0x4F, 0x52, 0x45, 0x49, 0x4E, 0x46, 0x4F, 0x4D, 0x4F, 0x52, 0x45};
status = liquivision_lynx_transfer (device, cmd_more, sizeof(cmd_more), device->more, sizeof(device->more));
@@ -292,6 +294,8 @@ liquivision_lynx_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "More", device->more, sizeof(device->more));
+
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
diff --git a/src/liquivision_lynx_parser.c b/src/liquivision_lynx_parser.c
index d34e828b..ea3c205b 100644
--- a/src/liquivision_lynx_parser.c
+++ b/src/liquivision_lynx_parser.c
@@ -27,6 +27,7 @@
#include "liquivision_lynx.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &liquivision_lynx_parser_vtable)
@@ -376,7 +377,7 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
}
unsigned int type = value & 0x7FFF;
- unsigned int timestamp = array_uint32_le (data + offset + 2);
+ unsigned int DC_ATTR_UNUSED timestamp = array_uint32_le (data + offset + 2);
offset += 4;
// Get the sample length.
diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c
index 92073ac3..3c59c503 100644
--- a/src/mares_iconhd.c
+++ b/src/mares_iconhd.c
@@ -91,6 +91,7 @@
#define CMD_OBJ_ODD 0xFE
#define OBJ_DEVICE 0x2000
+#define OBJ_DEVICE_MODEL 0x02
#define OBJ_DEVICE_SERIAL 0x04
#define OBJ_LOGBOOK 0x2008
#define OBJ_LOGBOOK_COUNT 0x01
@@ -195,6 +196,7 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
{"Sirius", SIRIUS},
{"Quad Ci", QUADCI},
{"Puck4", PUCK4},
+ {"Puck Lite", PUCK4},
};
// Check the product name in the version packet against the list
@@ -244,17 +246,18 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device,
}
// Receive the header byte.
- unsigned char header[1] = {0};
- status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to receive the packet header.");
- return status;
- }
+ while (1) {
+ unsigned char header[1] = {0};
+ status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the packet header.");
+ return status;
+ }
- // Verify the header byte.
- if (header[0] != ACK) {
- ERROR (abstract->context, "Unexpected packet header byte (%02x).", header[0]);
- return DC_STATUS_PROTOCOL;
+ if (header[0] == ACK)
+ break;
+
+ WARNING (abstract->context, "Unexpected packet header byte (%02x).", header[0]);
}
// Send the command payload to the dive computer.
@@ -411,7 +414,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const u
return rc;
// Discard any garbage bytes.
- dc_iostream_sleep (device->iostream, 100);
+ dc_iostream_sleep (device->iostream, 1000);
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
}
@@ -613,6 +616,8 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
goto error_free_iostream;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->version, sizeof (device->version));
+
// Autodetect the model using the version packet.
device->model = mares_iconhd_get_model (device);
@@ -658,6 +663,10 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
break;
case GENIUS:
case HORIZON:
+ case PUCKAIR2:
+ case SIRIUS:
+ case QUADCI:
+ case PUCK4:
device->layout = &mares_genius_layout;
device->packetsize = 4096;
device->fingerprint_size = 4;
@@ -1032,6 +1041,28 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca
return DC_STATUS_NOMEMORY;
}
+ // Read the model number.
+ rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_DEVICE, OBJ_DEVICE_MODEL);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the model number.");
+ dc_buffer_free (buffer);
+ return rc;
+ }
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Model", dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
+
+ if (dc_buffer_get_size (buffer) < 4) {
+ ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").",
+ dc_buffer_get_size (buffer));
+ dc_buffer_free (buffer);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ unsigned int DC_ATTR_UNUSED model = array_uint32_le (dc_buffer_get_data (buffer));
+
+ // Erase the buffer.
+ dc_buffer_clear (buffer);
+
// Read the serial number.
rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_DEVICE, OBJ_DEVICE_SERIAL);
if (rc != DC_STATUS_SUCCESS) {
@@ -1040,6 +1071,8 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca
return rc;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Serial", dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
+
if (dc_buffer_get_size (buffer) < 16) {
ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").",
dc_buffer_get_size (buffer));
diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c
index c968b786..b7545601 100644
--- a/src/mares_iconhd_parser.c
+++ b/src/mares_iconhd_parser.c
@@ -28,6 +28,7 @@
#include "mares_iconhd.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#include "checksum.h"
@@ -504,7 +505,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
unsigned int type = array_uint16_le (data);
unsigned int minor = data[2];
unsigned int major = data[3];
- if (type != 1 || OBJVERSION(major,minor) > OBJVERSION(1,1)) {
+ if (type != 1 || OBJVERSION(major,minor) > OBJVERSION(2,0)) {
ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
type, major, minor);
return DC_STATUS_DATAFORMAT;
@@ -523,7 +524,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
// The Genius header (v1.x) has 10 bytes more at the end.
unsigned int more = 0;
- if (major == 1) {
+ if (major >= 1) {
more = 16;
}
@@ -578,7 +579,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
unsigned int n2 = (gasmixparams >> 7) & 0x7F;
unsigned int he = (gasmixparams >> 14) & 0x7F;
unsigned int state = (gasmixparams >> 21) & 0x03;
- unsigned int changed = (gasmixparams >> 23) & 0x01;
+ unsigned int DC_ATTR_UNUSED changed = (gasmixparams >> 23) & 0x01;
if (o2 + n2 + he != 100) {
WARNING (abstract->context, "Invalid gas mix (%u%% He, %u%% O2, %u%% N2).", he, o2, n2);
@@ -938,7 +939,7 @@ mares_iconhd_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void
dc_sample_value_t sample = {0};
if (parser->model == SMARTAPNEA) {
- unsigned int maxdepth = array_uint16_le (data + offset + 0);
+ unsigned int DC_ATTR_UNUSED maxdepth = array_uint16_le (data + offset + 0);
unsigned int divetime = array_uint16_le (data + offset + 2);
unsigned int surftime = array_uint16_le (data + offset + 4);
diff --git a/src/mclean_extreme.c b/src/mclean_extreme.c
index 2ef1a879..8dbe7c23 100644
--- a/src/mclean_extreme.c
+++ b/src/mclean_extreme.c
@@ -541,6 +541,8 @@ mclean_extreme_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Firmware", firmware, sizeof(firmware));
+
// Read the serial number.
size_t serial_len = 0;
unsigned char serial[SZ_PACKET] = {0};
@@ -550,6 +552,8 @@ mclean_extreme_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Serial", serial, serial_len);
+
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
@@ -565,6 +569,8 @@ mclean_extreme_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback
return status;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Config", computer, sizeof(computer));
+
// Verify the format version.
unsigned int format = computer[0x0000];
if (format != 0) {
diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c
index 0601c15b..2f086981 100644
--- a/src/oceanic_atom2.c
+++ b/src/oceanic_atom2.c
@@ -948,6 +948,8 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->base.version, sizeof (device->base.version));
+
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
status = oceanic_atom2_ble_handshake(device);
if (status != DC_STATUS_SUCCESS) {
diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index 05a135c7..8b317f87 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -778,7 +778,6 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
// Initial gas mix.
unsigned int gasmix_previous = 0xFFFFFFFF;
- unsigned int count = 0;
unsigned int complete = 1;
unsigned int previous = 0;
unsigned int offset = parser->headersize;
@@ -1145,7 +1144,6 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
- count++;
complete = 1;
}
diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c
index f39b1cdb..58ba0c13 100644
--- a/src/oceanic_veo250.c
+++ b/src/oceanic_veo250.c
@@ -318,6 +318,8 @@ oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, dc_iostrea
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->base.version, sizeof (device->base.version));
+
// Detect the memory layout.
const oceanic_common_version_t *version = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
if (version == NULL) {
diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c
index b63c0fb4..14c25656 100644
--- a/src/oceanic_vtpro.c
+++ b/src/oceanic_vtpro.c
@@ -526,6 +526,8 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->base.version, sizeof (device->base.version));
+
// Calibrate the device. Although calibration is optional, it's highly
// recommended because it reduces the transfer time considerably, even
// when processing the command itself is quite slow.
diff --git a/src/parser.c b/src/parser.c
index 4c9dbbec..36236979 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -65,6 +65,7 @@
#include "deepblu_cosmiq.h"
#include "oceans_s1.h"
#include "divesoft_freedom.h"
+#include "halcyon_symbios.h"
// Not merged upstream yet
#include "garmin.h"
@@ -197,7 +198,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned
rc = deepsix_excursion_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_SEAC_SCREEN:
- rc = seac_screen_parser_create (&parser, context, data, size);
+ rc = seac_screen_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_DEEPBLU_COSMIQ:
rc = deepblu_cosmiq_parser_create (&parser, context, data, size);
@@ -208,6 +209,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned
case DC_FAMILY_DIVESOFT_FREEDOM:
rc = divesoft_freedom_parser_create (&parser, context, data, size);
break;
+ case DC_FAMILY_HALCYON_SYMBIOS:
+ rc = halcyon_symbios_parser_create (&parser, context, data, size);
+ break;
default:
return DC_STATUS_INVALIDARGS;
diff --git a/src/platform.h b/src/platform.h
index e4a5cd58..f98f0d7a 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -30,8 +30,10 @@ extern "C" {
#if defined(__GNUC__)
#define DC_ATTR_FORMAT_PRINTF(a,b) __attribute__((format(printf, a, b)))
+#define DC_ATTR_UNUSED __attribute__((unused))
#else
#define DC_ATTR_FORMAT_PRINTF(a,b)
+#define DC_ATTR_UNUSED
#endif
#ifdef _WIN32
diff --git a/src/reefnet_sensus.c b/src/reefnet_sensus.c
index 675a4cb5..0e0345c5 100644
--- a/src/reefnet_sensus.c
+++ b/src/reefnet_sensus.c
@@ -219,6 +219,8 @@ reefnet_sensus_handshake (reefnet_sensus_device_t *device)
return DC_STATUS_PROTOCOL;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Handshake", handshake + 2, sizeof(handshake) - 2);
+
// The device is now waiting for a data request.
device->waiting = 1;
diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c
index 27c2fdbc..4ba4b20c 100644
--- a/src/reefnet_sensuspro.c
+++ b/src/reefnet_sensuspro.c
@@ -183,6 +183,8 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device)
return DC_STATUS_PROTOCOL;
}
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Handshake", handshake, SZ_HANDSHAKE);
+
// Store the clock calibration values.
device->systime = dc_datetime_now ();
device->devtime = array_uint32_le (handshake + 6);
diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c
index bfe1c6a4..a29d853c 100644
--- a/src/reefnet_sensusultra.c
+++ b/src/reefnet_sensusultra.c
@@ -243,6 +243,8 @@ reefnet_sensusultra_handshake (reefnet_sensusultra_device_t *device, unsigned sh
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "Handshake", handshake, sizeof(handshake) - 2);
+
// Store the clock calibration values.
device->systime = dc_datetime_now ();
device->devtime = array_uint32_le (handshake + 4);
diff --git a/src/seac_screen.c b/src/seac_screen.c
index 3015fd65..e8f91c67 100644
--- a/src/seac_screen.c
+++ b/src/seac_screen.c
@@ -34,14 +34,31 @@
#define MAXRETRIES 4
+#define START 0x55
+#define ACK 0x09
+#define NAK 0x30
+
+#define ERR_INVALID_CMD 0x02
+#define ERR_INVALID_LENGTH 0x03
+#define ERR_INVALID_DATA 0x04
+#define ERR_BATTERY_LOW 0x05
+#define ERR_BUSY 0x06
+
#define SZ_MAXCMD 8
#define SZ_MAXRSP SZ_READ
#define CMD_HWINFO 0x1833
#define CMD_SWINFO 0x1834
-#define CMD_RANGE 0x1840
-#define CMD_ADDRESS 0x1841
-#define CMD_READ 0x1842
+
+// Screen
+#define CMD_SCREEN_RANGE 0x1840
+#define CMD_SCREEN_ADDRESS 0x1841
+#define CMD_SCREEN_READ 0x1842
+
+// Tablet
+#define CMD_TABLET_RANGE 0x1850
+#define CMD_TABLET_ADDRESS 0x1851
+#define CMD_TABLET_READ 0x1852
#define SZ_HWINFO 256
#define SZ_SWINFO 256
@@ -55,15 +72,29 @@
#define FP_OFFSET 0x0A
#define FP_SIZE 7
-#define RB_PROFILE_BEGIN 0x010000
-#define RB_PROFILE_END 0x200000
-#define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN)
-#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END)
-#define RB_PROFILE_INCR(a,d) ringbuffer_increment (a, d, RB_PROFILE_BEGIN, RB_PROFILE_END)
+#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, l->rb_profile_begin, l->rb_profile_end)
+#define RB_PROFILE_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_profile_begin, l->rb_profile_end)
+
+#define ACTION 0x01
+#define SCREEN 0x02
+#define TABLET 0x10
+
+typedef struct seac_screen_commands_t {
+ unsigned short range;
+ unsigned short address;
+ unsigned short read;
+} seac_screen_commands_t;
+
+typedef struct seac_screen_layout_t {
+ unsigned int rb_profile_begin;
+ unsigned int rb_profile_end;
+} seac_screen_layout_t;
typedef struct seac_screen_device_t {
dc_device_t base;
dc_iostream_t *iostream;
+ const seac_screen_commands_t *cmds;
+ const seac_screen_layout_t *layout;
unsigned char info[SZ_HWINFO + SZ_SWINFO];
unsigned char fingerprint[FP_SIZE];
} seac_screen_device_t;
@@ -90,6 +121,28 @@ static const dc_device_vtable_t seac_screen_device_vtable = {
NULL, /* close */
};
+static const seac_screen_commands_t cmds_screen = {
+ CMD_SCREEN_RANGE,
+ CMD_SCREEN_ADDRESS,
+ CMD_SCREEN_READ,
+};
+
+static const seac_screen_commands_t cmds_tablet = {
+ CMD_TABLET_RANGE,
+ CMD_TABLET_ADDRESS,
+ CMD_TABLET_READ,
+};
+
+static const seac_screen_layout_t layout_screen = {
+ 0x010000, /* rb_profile_begin */
+ 0x200000, /* rb_profile_end */
+};
+
+static const seac_screen_layout_t layout_tablet = {
+ 0x0A0000, /* rb_profile_begin */
+ 0x200000, /* rb_profile_end */
+};
+
static dc_status_t
seac_screen_send (seac_screen_device_t *device, unsigned short cmd, const unsigned char data[], size_t size)
{
@@ -106,7 +159,7 @@ seac_screen_send (seac_screen_device_t *device, unsigned short cmd, const unsign
// Setup the data packet
unsigned len = size + 6;
unsigned char packet[SZ_MAXCMD + 7] = {
- 0x55,
+ START,
(len >> 8) & 0xFF,
(len ) & 0xFF,
(cmd >> 8) & 0xFF,
@@ -136,17 +189,25 @@ seac_screen_receive (seac_screen_device_t *device, unsigned short cmd, unsigned
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[SZ_MAXRSP + 8] = {0};
- // Read the packet header.
- status = dc_iostream_read (device->iostream, packet, 3, NULL);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to receive the packet header.");
- return status;
+ // Read the packet start byte.
+ while (1) {
+ status = dc_iostream_read (device->iostream, packet + 0, 1, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the packet start byte.");
+ return status;
+ }
+
+ if (packet[0] == START)
+ break;
+
+ WARNING (abstract->context, "Unexpected packet header byte (%02x).", packet[0]);
}
- // Verify the start byte.
- if (packet[0] != 0x55) {
- ERROR (abstract->context, "Unexpected start byte (%02x).", packet[0]);
- return DC_STATUS_PROTOCOL;
+ // Read the packet length.
+ status = dc_iostream_read (device->iostream, packet + 1, 2, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the packet length.");
+ return status;
}
// Verify the length.
@@ -173,17 +234,32 @@ seac_screen_receive (seac_screen_device_t *device, unsigned short cmd, unsigned
// Verify the command response.
unsigned int rsp = array_uint16_be (packet + 3);
- unsigned int misc = packet[1 + length - 3];
- if (rsp != cmd || misc != 0x09) {
- ERROR (abstract->context, "Unexpected command response (%04x %02x).", rsp, misc);
+ if (rsp != cmd) {
+ ERROR (abstract->context, "Unexpected command response (%04x).", rsp);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Verify the ACK/NAK byte.
+ unsigned int type = packet[1 + length - 3];
+ if (type != ACK && type != NAK) {
+ ERROR (abstract->context, "Unexpected ACK/NAK byte (%02x).", type);
return DC_STATUS_PROTOCOL;
}
- if (length - 7 != size) {
+ // Verify the length of the packet.
+ unsigned int expected = (type == ACK ? size : 1) + 7;
+ if (length != expected) {
ERROR (abstract->context, "Unexpected packet length (%u).", length);
return DC_STATUS_PROTOCOL;
}
+ // Get the error code from a NAK packet.
+ if (type == NAK) {
+ unsigned int errcode = packet[5];
+ ERROR (abstract->context, "Received NAK packet with error code %02x.", errcode);
+ return DC_STATUS_PROTOCOL;
+ }
+
memcpy (data, packet + 5, length - 7);
return DC_STATUS_SUCCESS;
@@ -251,6 +327,9 @@ seac_screen_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
// Set the default values.
device->iostream = iostream;
+ device->cmds = NULL;
+ device->layout = NULL;
+ memset (device->info, 0, sizeof (device->info));
memset (device->fingerprint, 0, sizeof (device->fingerprint));
// Set the serial communication protocol (115200 8N1).
@@ -282,6 +361,8 @@ seac_screen_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Hardware", device->info, SZ_HWINFO);
+
// Read the software info.
status = seac_screen_transfer (device, CMD_SWINFO, NULL, 0, device->info + SZ_HWINFO, SZ_SWINFO);
if (status != DC_STATUS_SUCCESS) {
@@ -289,6 +370,17 @@ seac_screen_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Software", device->info + SZ_HWINFO, SZ_SWINFO);
+
+ unsigned int model = array_uint32_le (device->info + 4);
+ if (model == TABLET) {
+ device->cmds = &cmds_tablet;
+ device->layout = &layout_tablet;
+ } else {
+ device->cmds = &cmds_screen;
+ device->layout = &layout_screen;
+ }
+
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
@@ -341,7 +433,8 @@ seac_screen_device_read (dc_device_t *abstract, unsigned int address, unsigned c
(len ) & 0xFF,
};
unsigned char packet[SZ_READ] = {0};
- status = seac_screen_transfer (device, CMD_READ, params, sizeof(params), packet, sizeof(packet));
+ const unsigned int packetsize = device->cmds->read == CMD_TABLET_READ ? len : sizeof(packet);
+ status = seac_screen_transfer (device, device->cmds->read, params, sizeof(params), packet, packetsize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the read command.");
return status;
@@ -362,11 +455,16 @@ static dc_status_t
seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
{
seac_screen_device_t *device = (seac_screen_device_t *) abstract;
+ const seac_screen_layout_t *layout = device->layout;
// Emit a device info event.
dc_event_devinfo_t devinfo;
- devinfo.model = 0;
- devinfo.firmware = array_uint32_le (device->info + 0x11C);
+ devinfo.model = array_uint32_le (device->info + 4);
+ if (devinfo.model == TABLET) {
+ devinfo.firmware = array_uint32_le (device->info + 0x114);
+ } else {
+ devinfo.firmware = array_uint32_le (device->info + 0x11C);
+ }
devinfo.serial = array_uint32_le (device->info + 0x10);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
@@ -377,12 +475,12 @@ seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Allocate the required amount of memory.
- if (!dc_buffer_resize (buffer, RB_PROFILE_SIZE)) {
+ if (!dc_buffer_resize (buffer, layout->rb_profile_end - layout->rb_profile_begin)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
- return device_dump_read (abstract, RB_PROFILE_BEGIN, dc_buffer_get_data (buffer),
+ return device_dump_read (abstract, layout->rb_profile_begin, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), SZ_READ);
}
@@ -391,16 +489,21 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
{
dc_status_t status = DC_STATUS_SUCCESS;
seac_screen_device_t *device = (seac_screen_device_t *) abstract;
+ const seac_screen_layout_t *layout = device->layout;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
- progress.maximum = RB_PROFILE_SIZE;
+ progress.maximum = layout->rb_profile_end - layout->rb_profile_begin;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Emit a device info event.
dc_event_devinfo_t devinfo;
- devinfo.model = 0;
- devinfo.firmware = array_uint32_le (device->info + 0x11C);
+ devinfo.model = array_uint32_le (device->info + 4);
+ if (devinfo.model == TABLET) {
+ devinfo.firmware = array_uint32_le (device->info + 0x114);
+ } else {
+ devinfo.firmware = array_uint32_le (device->info + 0x11C);
+ }
devinfo.serial = array_uint32_le (device->info + 0x010);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
@@ -412,7 +515,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Read the range of the available dive numbers.
unsigned char range[SZ_RANGE] = {0};
- status = seac_screen_transfer (device, CMD_RANGE, NULL, 0, range, sizeof(range));
+ status = seac_screen_transfer (device, device->cmds->range, NULL, 0, range, sizeof(range));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the range command.");
goto error_exit;
@@ -448,7 +551,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
unsigned int count = 0;
unsigned int skip = 0;
unsigned int rb_profile_size = 0;
- unsigned int remaining = RB_PROFILE_SIZE;
+ unsigned int remaining = layout->rb_profile_end - layout->rb_profile_begin;
for (unsigned int i = 0; i < ndives; ++i) {
unsigned int number = last - i;
@@ -460,7 +563,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
(number ) & 0xFF,
};
unsigned char rsp_address[SZ_ADDRESS] = {0};
- status = seac_screen_transfer (device, CMD_ADDRESS, cmd_address, sizeof(cmd_address), rsp_address, sizeof(rsp_address));
+ status = seac_screen_transfer (device, device->cmds->address, cmd_address, sizeof(cmd_address), rsp_address, sizeof(rsp_address));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive address.");
goto error_free_logbook;
@@ -468,7 +571,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Get the dive address.
logbook[i].address = array_uint32_be (rsp_address);
- if (logbook[i].address < RB_PROFILE_BEGIN || logbook[i].address >= RB_PROFILE_END) {
+ if (logbook[i].address < layout->rb_profile_begin || logbook[i].address >= layout->rb_profile_end) {
ERROR (abstract->context, "Invalid ringbuffer pointer (0x%08x).", logbook[i].address);
status = DC_STATUS_DATAFORMAT;
goto error_free_logbook;
@@ -505,11 +608,11 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Get the end-of-profile pointer.
if (eop == 0) {
- eop = previous = RB_PROFILE_INCR (logbook[i].address, nbytes);
+ eop = previous = RB_PROFILE_INCR (logbook[i].address, nbytes, layout);
}
// Calculate the length.
- unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous);
+ unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous, layout);
// Check for the end of the ringbuffer.
if (length > remaining) {
@@ -529,7 +632,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Update and emit a progress event.
progress.maximum -= (ndives - count - skip) * (SZ_ADDRESS + SZ_HEADER) +
- (RB_PROFILE_SIZE - rb_profile_size);
+ ((layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Exit if no dives to download.
@@ -546,7 +649,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
- status = dc_rbstream_new (&rbstream, abstract, SZ_READ, SZ_READ, RB_PROFILE_BEGIN, RB_PROFILE_END, eop, DC_RBSTREAM_BACKWARD);
+ status = dc_rbstream_new (&rbstream, abstract, SZ_READ, SZ_READ, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error_free_profile;
@@ -556,7 +659,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
unsigned int offset = rb_profile_size;
for (unsigned int i = 0; i < count; ++i) {
// Calculate the length.
- unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous);
+ unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous, layout);
// Move to the start of the current dive.
offset -= length;
diff --git a/src/seac_screen.h b/src/seac_screen.h
index 4ff05eae..ecab8dfd 100644
--- a/src/seac_screen.h
+++ b/src/seac_screen.h
@@ -35,7 +35,7 @@ dc_status_t
seac_screen_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
-seac_screen_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
+seac_screen_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus
}
diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c
index 07553d6c..b19c8ea9 100644
--- a/src/seac_screen_parser.c
+++ b/src/seac_screen_parser.c
@@ -36,10 +36,15 @@
#define INVALID 0xFFFFFFFF
+#define ACTION 0x01
+#define SCREEN 0x02
+#define TABLET 0x10
+
typedef struct seac_screen_parser_t seac_screen_parser_t;
struct seac_screen_parser_t {
dc_parser_t base;
+ unsigned int model;
// Cached fields.
unsigned int cached;
unsigned int ngasmixes;
@@ -65,7 +70,7 @@ static const dc_parser_vtable_t seac_screen_parser_vtable = {
};
dc_status_t
-seac_screen_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
+seac_screen_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
seac_screen_parser_t *parser = NULL;
@@ -80,6 +85,7 @@ seac_screen_parser_create (dc_parser_t **out, dc_context_t *context, const unsig
}
// Set the default values.
+ parser->model = model;
parser->cached = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
@@ -298,6 +304,7 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
unsigned int decodepth = array_uint16_le (data + offset + 0x0E);
unsigned int decotime = array_uint16_le (data + offset + 0x10);
unsigned int ndl_tts = array_uint16_le (data + offset + 0x12);
+ unsigned int pressure = array_uint16_le (data + offset + 0x14);
unsigned int cns = array_uint16_le (data + offset + 0x16);
unsigned int gf_hi = data[offset + 0x3B];
unsigned int gf_lo = data[offset + 0x3C];
@@ -353,7 +360,7 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
if (decodepth) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = decotime;
- sample.deco.depth = decodepth;
+ sample.deco.depth = decodepth / 100.0;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.time = ndl_tts;
@@ -366,6 +373,13 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.cns = cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
+ // Tank pressure
+ if (pressure && parser->model == TABLET) {
+ sample.pressure.tank = 0;
+ sample.pressure.value = pressure;
+ if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
+ }
+
// Deco model
if (gf_low == 0 && gf_high == 0) {
gf_low = gf_lo;
diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c
index 671ef926..3c144ddf 100644
--- a/src/shearwater_petrel.c
+++ b/src/shearwater_petrel.c
@@ -167,6 +167,8 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return rc;
}
+ HEXDUMP(abstract->context, DC_LOGLEVEL_DEBUG, "Serial", rsp_serial, sizeof(rsp_serial));
+
// Convert to a number.
unsigned char serial[4] = {0};
if (array_convert_hex2bin (rsp_serial, sizeof(rsp_serial), serial, sizeof (serial)) != 0 ) {
@@ -182,6 +184,8 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return rc;
}
+ HEXDUMP(abstract->context, DC_LOGLEVEL_DEBUG, "Firmware", rsp_firmware, sizeof(rsp_firmware));
+
// Convert to a number.
unsigned int firmware = str2num (rsp_firmware, sizeof(rsp_firmware), 1);
@@ -252,6 +256,8 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
return rc;
}
+ HEXDUMP(abstract->context, DC_LOGLEVEL_DEBUG, "Manifest", dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
+
// Cache the buffer pointer and size.
unsigned char *data = dc_buffer_get_data (buffer);
unsigned int size = dc_buffer_get_size (buffer);
diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c
index d6a213bf..ef5c59d9 100644
--- a/src/shearwater_predator_parser.c
+++ b/src/shearwater_predator_parser.c
@@ -29,6 +29,7 @@
#include "shearwater_petrel.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#include "field-cache.h"
@@ -590,14 +591,15 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
if (pressure < 0xFFF0) {
unsigned int battery = 1u << (pressure >> 12);
pressure &= 0x0FFF;
- if (!tank[id].active) {
- tank[id].active = 1;
- tank[id].beginpressure = pressure;
+ if (pressure) {
+ if (!tank[id].active) {
+ tank[id].active = 1;
+ tank[id].beginpressure = pressure;
+ tank[id].endpressure = pressure;
+ }
tank[id].endpressure = pressure;
- tank[id].battery = 0;
+ tank[id].battery |= battery;
}
- tank[id].endpressure = pressure;
- tank[id].battery |= battery;
}
}
}
@@ -609,12 +611,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
unsigned int id = 2 + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
- if (!tank[id].active) {
- tank[id].active = 1;
- tank[id].beginpressure = pressure;
+ if (pressure) {
+ if (!tank[id].active) {
+ tank[id].active = 1;
+ tank[id].beginpressure = pressure;
+ tank[id].endpressure = pressure;
+ }
tank[id].endpressure = pressure;
}
- tank[id].endpressure = pressure;
}
}
}
@@ -1232,9 +1236,11 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
unsigned int id = (parser->aimode == AI_HPCCR ? 4 : 0) + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
- sample.pressure.tank = parser->tankidx[id];
- sample.pressure.value = pressure * 2 * PSI / BAR;
- if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
+ if (pressure) {
+ sample.pressure.tank = parser->tankidx[id];
+ sample.pressure.value = pressure * 2 * PSI / BAR;
+ if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
+ }
}
}
@@ -1258,9 +1264,11 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
unsigned int id = 2 + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
- sample.pressure.tank = parser->tankidx[id];
- sample.pressure.value = pressure * 2 * PSI / BAR;
- if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
+ if (pressure) {
+ sample.pressure.tank = parser->tankidx[id];
+ sample.pressure.value = pressure * 2 * PSI / BAR;
+ if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
+ }
}
}
}
@@ -1305,7 +1313,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
}
} else if (type == LOG_RECORD_INFO_EVENT) {
unsigned int event = data[offset + 1];
- unsigned int timestamp = array_uint32_be (data + offset + 4);
+ unsigned int DC_ATTR_UNUSED timestamp = array_uint32_be (data + offset + 4);
unsigned int w1 = array_uint32_be (data + offset + 8);
unsigned int w2 = array_uint32_be (data + offset + 12);
diff --git a/src/sporasub_sp2.c b/src/sporasub_sp2.c
index 8be2e8a5..e34966f6 100644
--- a/src/sporasub_sp2.c
+++ b/src/sporasub_sp2.c
@@ -282,6 +282,8 @@ sporasub_sp2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->version, sizeof(device)->version);
+
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
diff --git a/src/suunto_common2.c b/src/suunto_common2.c
index c26eb54e..f80bda84 100644
--- a/src/suunto_common2.c
+++ b/src/suunto_common2.c
@@ -397,3 +397,32 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
return status;
}
+
+dc_status_t
+suunto_common2_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+
+ unsigned char answer[5] = {0};
+ unsigned char command[11] = {0x10, 0x00, 0x07,
+ (datetime->year >> 8) & 0xFF,
+ (datetime->year ) & 0xFF,
+ datetime->month,
+ datetime->day,
+ datetime->hour,
+ datetime->minute,
+ datetime->second,
+ 0}; // CRC
+ command[10] = checksum_xor_uint8 (command, 10, 0x00);
+
+ status = suunto_common2_transfer (abstract, command, sizeof (command), answer, sizeof (answer), 1);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ if (answer[3] != 1) {
+ ERROR (abstract->context, "Unexpected response code (%u).", answer[3]);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ return status;
+}
diff --git a/src/suunto_common2.h b/src/suunto_common2.h
index 5eff8d29..6359db53 100644
--- a/src/suunto_common2.h
+++ b/src/suunto_common2.h
@@ -73,6 +73,9 @@ suunto_common2_device_dump (dc_device_t *device, dc_buffer_t *buffer);
dc_status_t
suunto_common2_device_foreach (dc_device_t *device, dc_dive_callback_t callback, void *userdata);
+dc_status_t
+suunto_common2_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
+
dc_status_t
suunto_common2_device_reset_maxdepth (dc_device_t *device);
diff --git a/src/suunto_d9.c b/src/suunto_d9.c
index 9622975a..d2b29c4e 100644
--- a/src/suunto_d9.c
+++ b/src/suunto_d9.c
@@ -56,7 +56,7 @@ static const suunto_common2_device_vtable_t suunto_d9_device_vtable = {
suunto_common2_device_write, /* write */
suunto_common2_device_dump, /* dump */
suunto_common2_device_foreach, /* foreach */
- NULL, /* timesync */
+ suunto_common2_device_timesync, /* timesync */
NULL /* close */
},
suunto_d9_device_packet
@@ -180,6 +180,8 @@ suunto_d9_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->base.version, sizeof (device->base.version));
+
// Override the base class values.
model = device->base.version[0];
if (model == D4i || model == D6i || model == D9tx ||
diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c
index 55d45ef9..bda7f6ea 100644
--- a/src/suunto_d9_parser.c
+++ b/src/suunto_d9_parser.c
@@ -26,6 +26,7 @@
#include "suunto_d9.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &suunto_d9_parser_vtable)
@@ -569,7 +570,7 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca
if ((nsamples + 1) == marker) {
while (offset < size) {
unsigned int event = data[offset++];
- unsigned int seconds, type, state, number, heading;
+ unsigned int seconds, type, DC_ATTR_UNUSED state, DC_ATTR_UNUSED number, heading;
unsigned int current, next;
unsigned int he, o2, ppo2, idx;
unsigned int length;
diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c
index 7a9f7ce7..2c1b6ef3 100644
--- a/src/suunto_vyper2.c
+++ b/src/suunto_vyper2.c
@@ -51,7 +51,7 @@ static const suunto_common2_device_vtable_t suunto_vyper2_device_vtable = {
suunto_common2_device_write, /* write */
suunto_common2_device_dump, /* dump */
suunto_common2_device_foreach, /* foreach */
- NULL, /* timesync */
+ suunto_common2_device_timesync, /* timesync */
suunto_vyper2_device_close /* close */
},
suunto_vyper2_device_packet
@@ -141,6 +141,8 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
goto error_timer_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->base.version, sizeof (device->base.version));
+
// Override the base class values.
unsigned int model = device->base.version[0];
if (model == HELO2)
diff --git a/src/tecdiving_divecomputereu.c b/src/tecdiving_divecomputereu.c
index 93190e29..a430e0f2 100644
--- a/src/tecdiving_divecomputereu.c
+++ b/src/tecdiving_divecomputereu.c
@@ -394,6 +394,8 @@ tecdiving_divecomputereu_device_open (dc_device_t **out, dc_context_t *context,
goto error_free;
}
+ HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Version", device->version, sizeof(device->version));
+
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
diff --git a/src/uwatec_memomouse_parser.c b/src/uwatec_memomouse_parser.c
index 3b11f277..bb2c2fca 100644
--- a/src/uwatec_memomouse_parser.c
+++ b/src/uwatec_memomouse_parser.c
@@ -26,6 +26,7 @@
#include "uwatec_memomouse.h"
#include "context-private.h"
#include "parser-private.h"
+#include "platform.h"
#include "array.h"
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &uwatec_memomouse_parser_vtable)
@@ -128,7 +129,7 @@ uwatec_memomouse_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int model = data[3];
- int is_nitrox = 0, is_oxygen = 0, is_air = 0;
+ int is_nitrox = 0, is_oxygen = 0, DC_ATTR_UNUSED is_air = 0;
if ((model & 0xF0) == 0xF0)
is_nitrox = 1;
if ((model & 0xF0) == 0xA0)
@@ -214,7 +215,7 @@ uwatec_memomouse_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
unsigned int model = data[3];
- int is_nitrox = 0, is_oxygen = 0, is_air = 0;
+ int is_nitrox = 0, is_oxygen = 0, DC_ATTR_UNUSED is_air = 0;
if ((model & 0xF0) == 0xF0)
is_nitrox = 1;
if ((model & 0xF0) == 0xA0)
diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c
index 981efc5e..1a4734a7 100644
--- a/src/uwatec_smart.c
+++ b/src/uwatec_smart.c
@@ -571,30 +571,40 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Model", model, sizeof (model));
+
// Read the hardware version.
unsigned char hardware[1] = {0};
rc = uwatec_smart_transfer (device, CMD_HARDWARE, NULL, 0, hardware, sizeof (hardware));
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Hardware", hardware, sizeof (hardware));
+
// Read the software version.
unsigned char software[1] = {0};
rc = uwatec_smart_transfer (device, CMD_SOFTWARE, NULL, 0, software, sizeof (software));
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Software", software, sizeof (software));
+
// Read the serial number.
unsigned char serial[4] = {0};
rc = uwatec_smart_transfer (device, CMD_SERIAL, NULL, 0, serial, sizeof (serial));
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Serial", serial, sizeof (serial));
+
// Read the device clock.
unsigned char devtime[4] = {0};
rc = uwatec_smart_transfer (device, CMD_DEVTIME, NULL, 0, devtime, sizeof (devtime));
if (rc != DC_STATUS_SUCCESS)
return rc;
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Clock", devtime, sizeof (devtime));
+
// Store the clock calibration values.
device->systime = dc_datetime_now ();
device->devtime = array_uint32_le (devtime);