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);