diff --git a/examples/common.c b/examples/common.c index 09635f0..b97343f 100644 --- a/examples/common.c +++ b/examples/common.c @@ -65,8 +65,6 @@ static const backend_table_t g_backends[] = { {"aladin", DC_FAMILY_UWATEC_ALADIN, 0x3F}, {"memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, {"smart", DC_FAMILY_UWATEC_SMART, 0x10}, - {"meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, - {"g2", DC_FAMILY_UWATEC_G2, 0x11}, {"sensus", DC_FAMILY_REEFNET_SENSUS, 1}, {"sensuspro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, {"sensusultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3}, @@ -90,6 +88,7 @@ static const backend_table_t g_backends[] = { {"aqualand", DC_FAMILY_CITIZEN_AQUALAND, 0}, {"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03}, {"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0}, }; static const transport_table_t g_transports[] = { diff --git a/examples/output_xml.c b/examples/output_xml.c index 1e9c569..58e045e 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -265,6 +265,20 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u fprintf (output->ostream, "%.2f\n", convert_depth(maxdepth, output->units)); + // Parse the avgdepth. + message ("Parsing the avgdepth.\n"); + double avgdepth = 0.0; + status = dc_parser_get_field (parser, DC_FIELD_AVGDEPTH, 0, &avgdepth); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the avgdepth."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + fprintf (output->ostream, "%.2f\n", + convert_depth(avgdepth, output->units)); + } + // Parse the temperature. message ("Parsing the temperature.\n"); for (unsigned int i = 0; i < 3; ++i) { diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index db6d8eb..1058b01 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -68,8 +68,8 @@ typedef enum dc_family_t { DC_FAMILY_UWATEC_ALADIN = (3 << 16), DC_FAMILY_UWATEC_MEMOMOUSE, DC_FAMILY_UWATEC_SMART, - DC_FAMILY_UWATEC_MERIDIAN, - DC_FAMILY_UWATEC_G2, + DC_FAMILY_UWATEC_MERIDIAN, /* Deprecated: integrated into the Uwatec Smart family. */ + DC_FAMILY_UWATEC_G2, /* Deprecated: integrated into the Uwatec Smart family. */ /* Oceanic */ DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VEO250, @@ -101,6 +101,8 @@ typedef enum dc_family_t { DC_FAMILY_DIVESYSTEM_IDIVE = (13 << 16), /* Cochran */ DC_FAMILY_COCHRAN_COMMANDER = (14 << 16), + /* Tecdiving */ + DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16), } dc_family_t; #ifdef __cplusplus diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 64e2966..1a88ff8 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -482,6 +482,14 @@ RelativePath="..\src\suunto_vyper_parser.c" > + + + + @@ -494,10 +502,6 @@ RelativePath="..\src\uwatec_aladin.c" > - - @@ -506,10 +510,6 @@ RelativePath="..\src\uwatec_memomouse_parser.c" > - - @@ -824,6 +824,10 @@ RelativePath="..\src\suunto_vyper2.h" > + + @@ -840,18 +844,10 @@ RelativePath="..\src\uwatec_aladin.h" > - - - - diff --git a/src/Makefile.am b/src/Makefile.am index 730553a..6afa24e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,8 +39,6 @@ libdivecomputer_la_SOURCES = \ uwatec_aladin.h uwatec_aladin.c \ uwatec_memomouse.h uwatec_memomouse.c uwatec_memomouse_parser.c \ uwatec_smart.h uwatec_smart.c uwatec_smart_parser.c \ - uwatec_meridian.h uwatec_meridian.c \ - uwatec_g2.h uwatec_g2.c \ oceanic_common.h oceanic_common.c \ oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \ oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \ @@ -72,6 +70,7 @@ libdivecomputer_la_SOURCES = \ array.h array.c \ buffer.c \ cochran_commander.h cochran_commander.c cochran_commander_parser.c \ + tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \ socket.h socket.c \ irda.c \ usbhid.c \ diff --git a/src/bluetooth.c b/src/bluetooth.c index 088d027..4630523 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -110,7 +110,7 @@ static const dc_iostream_vtable_t dc_bluetooth_vtable = { dc_socket_write, /* write */ NULL, /* flush */ NULL, /* purge */ - NULL, /* sleep */ + dc_socket_sleep, /* sleep */ dc_socket_close, /* close */ }; @@ -312,6 +312,12 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri return DC_STATUS_NOMEMORY; } + // Initialize the socket library. + status = dc_socket_init (context); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + #ifdef _WIN32 WSAQUERYSET wsaq; memset(&wsaq, 0, sizeof (wsaq)); @@ -328,7 +334,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri } else { SYSERROR (context, errcode); status = dc_socket_syserror(errcode); - goto error_free; + goto error_socket_exit; } } @@ -340,7 +346,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri s_errcode_t errcode = S_ERRNO; SYSERROR (context, errcode); status = dc_socket_syserror(errcode); - goto error_free; + goto error_socket_exit; } // Open a socket to the bluetooth adapter. @@ -349,7 +355,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri s_errcode_t errcode = S_ERRNO; SYSERROR (context, errcode); status = dc_socket_syserror(errcode); - goto error_free; + goto error_socket_exit; } // Perform the bluetooth device discovery. The inquiry lasts for at @@ -379,6 +385,8 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri error_close: hci_close_dev(fd); #endif +error_socket_exit: + dc_socket_exit (context); error_free: dc_iterator_deallocate ((dc_iterator_t *) iterator); return status; @@ -486,6 +494,7 @@ dc_bluetooth_iterator_free (dc_iterator_t *abstract) bt_free(iterator->devices); hci_close_dev(iterator->fd); #endif + dc_socket_exit (abstract->context); return DC_STATUS_SUCCESS; } diff --git a/src/descriptor.c b/src/descriptor.c index c633083..20fd4b5 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -33,6 +33,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata); static int dc_filter_suunto (dc_transport_t transport, const void *userdata); static int dc_filter_shearwater (dc_transport_t transport, const void *userdata); static int dc_filter_hw (dc_transport_t transport, const void *userdata); +static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -111,34 +112,32 @@ static const dc_descriptor_t g_descriptors[] = { /* Uwatec Memomouse */ {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0, DC_TRANSPORT_SERIAL, NULL}, /* Uwatec Smart */ - {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Subgear","XP-10", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Subgear","XP-3G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Galileo Trimix",DC_FAMILY_UWATEC_SMART, 0x19, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - {"Subgear","XP Air", DC_FAMILY_UWATEC_SMART, 0x1C, DC_TRANSPORT_IRDA, dc_filter_uwatec}, - /* Scubapro/Uwatec Meridian */ - {"Scubapro", "Meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20, DC_TRANSPORT_SERIAL, NULL}, - {"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20, DC_TRANSPORT_SERIAL, NULL}, - {"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24, DC_TRANSPORT_SERIAL, NULL}, - {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26, DC_TRANSPORT_SERIAL, NULL}, - /* Scubapro G2 */ - {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17, DC_TRANSPORT_BLE, dc_filter_uwatec}, - {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22, DC_TRANSPORT_USBHID, dc_filter_uwatec}, - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, - {"Scubapro", "G2 Console", DC_FAMILY_UWATEC_G2, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, + {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Subgear", "XP-10", DC_FAMILY_UWATEC_SMART, 0x13, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Subgear", "XP-3G", DC_FAMILY_UWATEC_SMART, 0x15, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_SMART, 0x17, DC_TRANSPORT_BLE, dc_filter_uwatec}, + {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Galileo Trimix", DC_FAMILY_UWATEC_SMART, 0x19, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Subgear", "XP Air", DC_FAMILY_UWATEC_SMART, 0x1C, DC_TRANSPORT_IRDA, dc_filter_uwatec}, + {"Scubapro", "Meridian", DC_FAMILY_UWATEC_SMART, 0x20, DC_TRANSPORT_SERIAL, NULL}, + {"Scubapro", "Mantis", DC_FAMILY_UWATEC_SMART, 0x20, DC_TRANSPORT_SERIAL, NULL}, + {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_SMART, 0x22, DC_TRANSPORT_USBHID, dc_filter_uwatec}, + {"Scubapro", "Chromis", DC_FAMILY_UWATEC_SMART, 0x24, DC_TRANSPORT_SERIAL, NULL}, + {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_SMART, 0x26, DC_TRANSPORT_SERIAL, NULL}, + {"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, + {"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL}, @@ -256,12 +255,12 @@ static const dc_descriptor_t g_descriptors[] = { {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, - {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A, DC_TRANSPORT_SERIAL, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05, DC_TRANSPORT_SERIAL, dc_filter_hw}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, DC_TRANSPORT_SERIAL, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05, DC_TRANSPORT_SERIAL, NULL}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_hw}, {"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}, @@ -325,6 +324,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3, DC_TRANSPORT_SERIAL, NULL}, {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4, DC_TRANSPORT_SERIAL, NULL}, {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL}, + /* Tecdiving DiveComputer.eu */ + {"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving}, }; static int @@ -358,6 +359,27 @@ dc_filter_internal_usb (const dc_usb_desc_t *desc, const dc_usb_desc_t values[], return 0; } +static int +dc_filter_internal_rfcomm (const char *name) +{ + static const char *prefixes[] = { +#if defined (__linux__) + "/dev/rfcomm", +#endif + NULL + }; + + if (name == NULL) + return 0; + + for (size_t i = 0; prefixes[i] != NULL; ++i) { + if (strncmp (name, prefixes[i], strlen (prefixes[i])) == 0) + return 1; + } + + return prefixes[0] == NULL; +} + static int dc_filter_uwatec (dc_transport_t transport, const void *userdata) { static const char *irda[] = { @@ -403,6 +425,8 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata) if (transport == DC_TRANSPORT_BLUETOOTH) { return strncasecmp ((const char *) userdata, "OSTC", 4) == 0 || strncasecmp ((const char *) userdata, "FROG", 4) == 0; + } else if (transport == DC_TRANSPORT_SERIAL) { + return dc_filter_internal_rfcomm ((const char *) userdata); } return 1; @@ -419,6 +443,23 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata) if (transport == DC_TRANSPORT_BLUETOOTH) { return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth)); + } else if (transport == DC_TRANSPORT_SERIAL) { + return dc_filter_internal_rfcomm ((const char *) userdata); + } + + return 1; +} + +static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata) +{ + static const char *bluetooth[] = { + "DiveComputer", + }; + + if (transport == DC_TRANSPORT_BLUETOOTH) { + return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth)); + } else if (transport == DC_TRANSPORT_SERIAL) { + return dc_filter_internal_rfcomm ((const char *) userdata); } return 1; diff --git a/src/device.c b/src/device.c index 885fd10..fc59464 100644 --- a/src/device.c +++ b/src/device.c @@ -33,9 +33,7 @@ #include "reefnet_sensuspro.h" #include "reefnet_sensusultra.h" #include "uwatec_aladin.h" -#include "uwatec_g2.h" #include "uwatec_memomouse.h" -#include "uwatec_meridian.h" #include "uwatec_smart.h" #include "oceanic_atom2.h" #include "oceanic_veo250.h" @@ -57,6 +55,7 @@ #include "citizen_aqualand.h" #include "divesystem_idive.h" #include "cochran_commander.h" +#include "tecdiving_divecomputereu.h" #include "device-private.h" #include "context-private.h" @@ -136,12 +135,6 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_UWATEC_SMART: rc = uwatec_smart_device_open (&device, context, iostream); break; - case DC_FAMILY_UWATEC_MERIDIAN: - rc = uwatec_meridian_device_open (&device, context, iostream); - break; - case DC_FAMILY_UWATEC_G2: - rc = uwatec_g2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); - break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_device_open (&device, context, iostream); break; @@ -211,6 +204,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_COCHRAN_COMMANDER: rc = cochran_commander_device_open (&device, context, iostream); break; + case DC_FAMILY_TECDIVING_DIVECOMPUTEREU: + rc = tecdiving_divecomputereu_device_open (&device, context, iostream); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/irda.c b/src/irda.c index eedb703..a78ff31 100644 --- a/src/irda.c +++ b/src/irda.c @@ -102,7 +102,7 @@ static const dc_iostream_vtable_t dc_irda_vtable = { dc_socket_write, /* write */ NULL, /* flush */ NULL, /* purge */ - NULL, /* sleep */ + dc_socket_sleep, /* sleep */ dc_socket_close, /* close */ }; #endif diff --git a/src/parser.c b/src/parser.c index f5f55e3..e9e97a2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -33,7 +33,6 @@ #include "reefnet_sensusultra.h" #include "uwatec_aladin.h" #include "uwatec_memomouse.h" -#include "uwatec_meridian.h" #include "uwatec_smart.h" #include "oceanic_atom2.h" #include "oceanic_atom2.h" @@ -56,6 +55,7 @@ #include "citizen_aqualand.h" #include "divesystem_idive.h" #include "cochran_commander.h" +#include "tecdiving_divecomputereu.h" #include "context-private.h" #include "parser-private.h" @@ -97,8 +97,6 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa rc = uwatec_memomouse_parser_create (&parser, context, devtime, systime); break; case DC_FAMILY_UWATEC_SMART: - case DC_FAMILY_UWATEC_MERIDIAN: - case DC_FAMILY_UWATEC_G2: rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); break; case DC_FAMILY_REEFNET_SENSUS: @@ -167,6 +165,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_COCHRAN_COMMANDER: rc = cochran_commander_parser_create (&parser, context, model); break; + case DC_FAMILY_TECDIVING_DIVECOMPUTEREU: + rc = tecdiving_divecomputereu_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/socket.c b/src/socket.c index 948aa46..cbbb684 100644 --- a/src/socket.c +++ b/src/socket.c @@ -293,3 +293,25 @@ out: return status; } + +dc_status_t +dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout) +{ +#ifdef _WIN32 + Sleep (timeout); +#else + struct timespec ts; + ts.tv_sec = (timeout / 1000); + ts.tv_nsec = (timeout % 1000) * 1000000; + + while (nanosleep (&ts, &ts) != 0) { + int errcode = errno; + if (errcode != EINTR ) { + SYSERROR (abstract->context, errcode); + return dc_socket_syserror (errcode); + } + } +#endif + + return DC_STATUS_SUCCESS; +} diff --git a/src/socket.h b/src/socket.h index 5f5421d..280c486 100644 --- a/src/socket.h +++ b/src/socket.h @@ -34,6 +34,7 @@ #include // select #include // ioctl #include +#include #endif #include @@ -113,6 +114,9 @@ dc_socket_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual dc_status_t dc_socket_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +dc_status_t +dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout); + dc_status_t dc_socket_close (dc_iostream_t *iostream); diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index 6566bc9..33c87e8 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -76,6 +76,7 @@ struct suunto_d9_parser_t { unsigned int serial; // Cached fields. unsigned int cached; + unsigned int id; unsigned int mode; unsigned int ngasmixes; unsigned int oxygen[NGASMIXES]; @@ -227,6 +228,7 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser) } } parser->config = config; + parser->id = id; parser->cached = 1; return DC_STATUS_SUCCESS; @@ -251,6 +253,7 @@ suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int parser->model = model; parser->serial = serial; parser->cached = 0; + parser->id = 0; parser->mode = AIR; parser->ngasmixes = 0; for (unsigned int i = 0; i < NGASMIXES; ++i) { @@ -273,6 +276,7 @@ suunto_d9_parser_set_data (dc_parser_t *abstract, const unsigned char *data, uns // Reset the cache. parser->cached = 0; + parser->id = 0; parser->mode = AIR; parser->ngasmixes = 0; for (unsigned int i = 0; i < NGASMIXES; ++i) { @@ -733,10 +737,12 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca offset += 2; break; case 0x06: // Gas Change - if (parser->model == DX || parser->model == VYPERNOVO) + if (parser->model == DX || parser->model == VYPERNOVO || + (parser->model == D6i && parser->id == ID_D6I_V2)) { length = 5; - else + } else { length = 4; + } if (offset + length > size) { ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; @@ -744,7 +750,8 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca unknown = data[offset + 0]; he = data[offset + 1]; o2 = data[offset + 2]; - if (parser->model == DX || parser->model == VYPERNOVO) { + if (parser->model == DX || parser->model == VYPERNOVO || + (parser->model == D6i && parser->id == ID_D6I_V2)) { seconds = data[offset + 4]; } else { seconds = data[offset + 3]; diff --git a/src/tecdiving_divecomputereu.c b/src/tecdiving_divecomputereu.c new file mode 100644 index 0000000..bb2a35d --- /dev/null +++ b/src/tecdiving_divecomputereu.c @@ -0,0 +1,542 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 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 // memcmp, memcpy +#include // malloc, free + +#include "tecdiving_divecomputereu.h" +#include "context-private.h" +#include "device-private.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &tecdiving_divecomputereu_device_vtable) + +#define MAXRETRIES 14 + +#define STX 0x7E + +#define CMD_INIT 0x53 +#define CMD_LIST 0x57 +#define CMD_DIVE 0x58 +#define CMD_EXIT 0x59 + +#define RSP_INIT 0x56 +#define RSP_LIST CMD_LIST +#define RSP_HEADER 0x51 +#define RSP_PROFILE 0x52 + +#define SZ_MAXCMD 2 +#define SZ_SUMMARY 7 +#define SZ_SAMPLE 8 +#define SZ_INIT 56 +#define SZ_LIST (2 + 0x10000 * SZ_SUMMARY) +#define SZ_HEADER 100 +#define SZ_PROFILE (1000 * SZ_SAMPLE) + +#define NSTEPS 1000 +#define STEP(i,n) (NSTEPS * (i) / (n)) + +typedef struct tecdiving_divecomputereu_device_t { + dc_device_t base; + dc_iostream_t *iostream; + unsigned char fingerprint[SZ_SUMMARY]; + unsigned char version[SZ_INIT]; +} tecdiving_divecomputereu_device_t; + +static dc_status_t tecdiving_divecomputereu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t tecdiving_divecomputereu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t tecdiving_divecomputereu_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t tecdiving_divecomputereu_device_vtable = { + sizeof(tecdiving_divecomputereu_device_t), + DC_FAMILY_TECDIVING_DIVECOMPUTEREU, + tecdiving_divecomputereu_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + tecdiving_divecomputereu_device_foreach, /* foreach */ + NULL, /* timesync */ + tecdiving_divecomputereu_device_close, /* close */ +}; + +static unsigned short +checksum_crc (const unsigned char data[], unsigned int size, unsigned short init) +{ + unsigned short crc = init; + for (unsigned int i = 0; i < size; ++i) { + crc ^= data[i] << 8; + if (crc & 0x8000) { + crc <<= 1; + crc ^= 0x1021; + } else { + crc <<= 1; + } + } + + return crc; +} + +static dc_status_t +tecdiving_divecomputereu_send (tecdiving_divecomputereu_device_t *device, unsigned char cmd, const unsigned char data[], size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned short crc = 0; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + if (size > SZ_MAXCMD) + return DC_STATUS_INVALIDARGS; + + // Setup the data packet + unsigned char packet[SZ_MAXCMD + 11] = { + STX, + 0x00, + (size >> 0) & 0xFF, + (size >> 8) & 0xFF, + (size >> 16) & 0xFF, + (size >> 24) & 0xFF, + cmd, + }; + memcpy(packet + 7, data, size); + crc = checksum_crc (packet + 1, size + 6, 0); + packet[size + 7] = (crc >> 8) & 0xFF; + packet[size + 8] = (crc ) & 0xFF; + packet[size + 9] = 0x00; + packet[size + 10] = 0x00; + + // Give the dive computer some extra time. + dc_iostream_sleep (device->iostream, 300); + + // Send the data packet. + status = dc_iostream_write (device->iostream, packet, size + 11, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +tecdiving_divecomputereu_receive (tecdiving_divecomputereu_device_t *device, unsigned char rsp, unsigned char data[], size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char header[7]; + unsigned int nretries = 0; + + // Read the packet start byte. + // Unfortunately it takes a relative long time, about 6-8 seconds, + // before the STX byte arrives. Hence the standard timeout of one + // second is not sufficient, and we need to retry a few times on + // timeout. The advantage over using a single read operation with a + // large timeout is that we can give the user a chance to cancel the + // operation. + while (1) { + status = dc_iostream_read (device->iostream, header + 0, 1, NULL); + if (status != DC_STATUS_SUCCESS) { + if (status != DC_STATUS_TIMEOUT) { + ERROR (abstract->context, "Failed to receive the packet start byte."); + return status; + } + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return status; + + // Cancel if requested by the user. + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Try again. + continue; + } + + if (header[0] == STX) + break; + + // Reset the retry counter. + nretries = 0; + } + + // Read the packet header. + status = dc_iostream_read (device->iostream, header + 1, sizeof(header) - 1, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet header."); + return status; + } + + // Verify the type byte. + unsigned int type = header[1]; + if (type != 0x00) { + ERROR (abstract->context, "Unexpected type byte (%02x).", type); + return DC_STATUS_PROTOCOL; + } + + // Verify the length. + unsigned int length = array_uint32_le (header + 2); + if (length > size) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + // Get the command type. + unsigned int cmd = header[6]; + if (cmd != rsp) { + ERROR (abstract->context, "Unexpected command byte (%02x).", cmd); + return DC_STATUS_PROTOCOL; + } + + size_t nbytes = 0; + while (nbytes < length) { + // Set the maximum packet size. + size_t len = 1000; + + // Limit the packet size to the total size. + if (nbytes + len > length) + len = length - nbytes; + + // Read the packet payload. + status = dc_iostream_read (device->iostream, data + nbytes, len, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet payload."); + return status; + } + + nbytes += len; + } + + // Read the packet checksum. + unsigned char checksum[4]; + status = dc_iostream_read (device->iostream, checksum, sizeof(checksum), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet checksum."); + return status; + } + + // Verify the checksum. + unsigned short crc = array_uint16_be (checksum); + unsigned short ccrc = 0; + ccrc = checksum_crc (header + 1, sizeof(header) - 1, ccrc); + ccrc = checksum_crc (data, length, ccrc); + if (crc != ccrc || checksum[2] != 0x00 || checksum[3] != 0) { + ERROR (abstract->context, "Unexpected packet checksum."); + return DC_STATUS_PROTOCOL; + } + + if (actual == NULL) { + // Verify the actual length. + if (length != size) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + } else { + // Return the actual length. + *actual = length; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +tecdiving_divecomputereu_readdive (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int idx, dc_buffer_t *buffer) +{ + dc_status_t status = DC_STATUS_SUCCESS; + tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract; + + // Erase the buffer. + dc_buffer_clear (buffer); + + // Encode the one based logbook ID. + unsigned int number = idx + 1; + unsigned char id[] = { + (number >> 8) & 0xFF, + (number ) & 0xFF, + }; + + // Request the dive. + status = tecdiving_divecomputereu_send (device, CMD_DIVE, id, sizeof(id)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the dive command."); + return status; + } + + // Read the dive header. + unsigned char header[SZ_HEADER]; + status = tecdiving_divecomputereu_receive (device, RSP_HEADER, header, sizeof(header), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the dive header."); + return status; + } + + // Get the number of samples. + unsigned int nsamples = array_uint32_be (header + 36); + + // Calculate the total size. + unsigned int size = sizeof(header) + nsamples * SZ_SAMPLE; + + // Update and emit a progress event. + if (progress) { + progress->current = (idx + 1) * NSTEPS + STEP(sizeof(header), size); + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + // Allocate memory for the dive. + if (!dc_buffer_resize (buffer, size)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Cache the pointer. + unsigned char *data = dc_buffer_get_data(buffer); + + // Append the header. + memcpy (data, header, sizeof(header)); + + unsigned int nbytes = sizeof(header); + while (nbytes < size) { + // Get the packet size. The maximum size for a single data + // packet is 1000 samples. + unsigned int len = size - nbytes; + if (len > SZ_PROFILE) + len = SZ_PROFILE; + + // Read the dive samples. + status = tecdiving_divecomputereu_receive (device, RSP_PROFILE, data + nbytes, len, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the dive samples."); + return status; + } + + nbytes += len; + + // Update and emit a progress event. + if (progress) { + progress->current = (idx + 1) * NSTEPS + STEP(nbytes, size); + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + } + + return DC_STATUS_SUCCESS; +} + +dc_status_t +tecdiving_divecomputereu_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +{ + dc_status_t status = DC_STATUS_SUCCESS; + tecdiving_divecomputereu_device_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (tecdiving_divecomputereu_device_t *) dc_device_allocate (context, &tecdiving_divecomputereu_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 serial communication protocol (115200 8N1). + status = dc_iostream_configure (device->iostream, 115200, 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; + } + + // Set the timeout for receiving data (1000ms). + status = dc_iostream_set_timeout (device->iostream, 1000); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the timeout."); + goto error_free; + } + + // Send the init command. + status = tecdiving_divecomputereu_send (device, CMD_INIT, NULL, 0); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to send the init command."); + goto error_free; + } + + // Read the device info. + status = tecdiving_divecomputereu_receive (device, RSP_INIT, device->version, sizeof(device->version), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to receive the device info."); + goto error_free; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; + +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; +} + +static dc_status_t +tecdiving_divecomputereu_device_close (dc_device_t *abstract) +{ + dc_status_t status = DC_STATUS_SUCCESS; + tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract; + + status = tecdiving_divecomputereu_send (device, CMD_EXIT, NULL, 0); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the exit command."); + return status; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +tecdiving_divecomputereu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_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 +tecdiving_divecomputereu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_status_t status = DC_STATUS_SUCCESS; + tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract; + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = 0; + devinfo.serial = array_uint16_be (device->version + 0x22) << 16 | array_uint16_be (device->version + 0x26); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Emit a vendor event. + dc_event_vendor_t vendor; + vendor.data = device->version; + vendor.size = sizeof(device->version); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Allocate memory for the dive list. + size_t length = SZ_LIST; + unsigned char *logbook = (unsigned char *) malloc (length); + if (logbook == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Request the dive list. + status = tecdiving_divecomputereu_send (device, CMD_LIST, NULL, 0); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the list command."); + goto error_free; + } + + // Read the dive list. + status = tecdiving_divecomputereu_receive (device, RSP_LIST, logbook, length, &length); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the logbook."); + goto error_free; + } + + // Verify the minimum length. + if (length < 2) { + status = DC_STATUS_DATAFORMAT; + goto error_free; + } + + // Get the number of logbook entries. + unsigned int nlogbooks = array_uint16_be (logbook); + if (length != 2 + nlogbooks * SZ_SUMMARY) { + status = DC_STATUS_DATAFORMAT; + goto error_free; + } + + // Count the number of dives to download. + unsigned int ndives = 0; + for (unsigned int i = 0; i < nlogbooks; ++i) { + unsigned int offset = 2 + i * SZ_SUMMARY; + + if (memcmp(logbook + 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); + + // Allocate a memory buffer for a single dive. + dc_buffer_t *buffer = dc_buffer_new(0); + if (buffer == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_free; + } + + for (unsigned int i = 0; i < ndives; ++i) { + unsigned int offset = 2 + i * SZ_SUMMARY; + + // Read the dive. + status = tecdiving_divecomputereu_readdive (abstract, &progress, i, buffer); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + + // Cache the pointer. + unsigned char *data = dc_buffer_get_data(buffer); + unsigned int size = dc_buffer_get_size(buffer); + + // Verify the logbook entry. + if (memcmp (data, logbook + offset, SZ_SUMMARY) != 0) { + ERROR (abstract->context, "Dive header doesn't match logbook entry."); + status = DC_STATUS_DATAFORMAT; + goto error_free; + } + + if (callback && !callback (data, size, data, sizeof(device->fingerprint), userdata)) { + break; + } + } + +error_free: + dc_buffer_free (buffer); + free (logbook); +error_exit: + return status; +} diff --git a/src/uwatec_g2.h b/src/tecdiving_divecomputereu.h similarity index 74% rename from src/uwatec_g2.h rename to src/tecdiving_divecomputereu.h index 72a4390..714ae43 100644 --- a/src/uwatec_g2.h +++ b/src/tecdiving_divecomputereu.h @@ -1,7 +1,7 @@ /* * libdivecomputer * - * Copyright (C) 2008 Jef Driesen + * Copyright (C) 2018 Jef Driesen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,8 +19,8 @@ * MA 02110-1301 USA */ -#ifndef UWATEC_G2_H -#define UWATEC_G2_H +#ifndef TECDIVING_DIVECOMPUTEREU_H +#define TECDIVING_DIVECOMPUTEREU_H #include #include @@ -32,9 +32,12 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -uwatec_g2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); +tecdiving_divecomputereu_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); + +dc_status_t +tecdiving_divecomputereu_parser_create (dc_parser_t **parser, dc_context_t *context); #ifdef __cplusplus } #endif /* __cplusplus */ -#endif /* UWATEC_G2_H */ +#endif /* TECDIVING_DIVECOMPUTEREU_H */ diff --git a/src/tecdiving_divecomputereu_parser.c b/src/tecdiving_divecomputereu_parser.c new file mode 100644 index 0000000..a3006ea --- /dev/null +++ b/src/tecdiving_divecomputereu_parser.c @@ -0,0 +1,186 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 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 "tecdiving_divecomputereu.h" +#include "context-private.h" +#include "parser-private.h" +#include "array.h" + +#define ISINSTANCE(parser) dc_device_isinstance((parser), &tecdiving_divecomputereu_parser_vtable) + +#define SZ_HEADER 100 + +typedef struct tecdiving_divecomputereu_parser_t tecdiving_divecomputereu_parser_t; + +struct tecdiving_divecomputereu_parser_t { + dc_parser_t base; +}; + +static dc_status_t tecdiving_divecomputereu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t tecdiving_divecomputereu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t tecdiving_divecomputereu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t tecdiving_divecomputereu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); + +static const dc_parser_vtable_t tecdiving_divecomputereu_parser_vtable = { + sizeof(tecdiving_divecomputereu_parser_t), + DC_FAMILY_TECDIVING_DIVECOMPUTEREU, + tecdiving_divecomputereu_parser_set_data, /* set_data */ + tecdiving_divecomputereu_parser_get_datetime, /* datetime */ + tecdiving_divecomputereu_parser_get_field, /* fields */ + tecdiving_divecomputereu_parser_samples_foreach, /* samples_foreach */ + NULL /* destroy */ +}; + + +dc_status_t +tecdiving_divecomputereu_parser_create (dc_parser_t **out, dc_context_t *context) +{ + tecdiving_divecomputereu_parser_t *parser = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + parser = (tecdiving_divecomputereu_parser_t *) dc_parser_allocate (context, &tecdiving_divecomputereu_parser_vtable); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + *out = (dc_parser_t *) parser; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +tecdiving_divecomputereu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +tecdiving_divecomputereu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) +{ + const unsigned char *data = abstract->data; + + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + if (datetime) { + datetime->year = data[2] + 2000; + datetime->month = data[3]; + datetime->day = data[4]; + datetime->hour = data[5]; + datetime->minute = data[6]; + datetime->second = data[7]; + datetime->timezone = DC_TIMEZONE_NONE; + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +tecdiving_divecomputereu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + const unsigned char *data = abstract->data; + + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + if (value) { + switch (type) { + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = array_uint16_be (data + 23) * 60; + break; + case DC_FIELD_AVGDEPTH: + *((double *) value) = array_uint16_be (data + 27) / 100; + break; + case DC_FIELD_MAXDEPTH: + *((double *) value) = array_uint16_be (data + 29) / 10; + break; + case DC_FIELD_ATMOSPHERIC: + *((double *) value) = array_uint16_be (data + 14) / 1000.0; + break; + case DC_FIELD_TEMPERATURE_SURFACE: + *((double *) value) = (signed char) data[17]; + break; + case DC_FIELD_TEMPERATURE_MINIMUM: + *((double *) value) = (signed char) data[41]; + break; + case DC_FIELD_TEMPERATURE_MAXIMUM: + *((double *) value) = (signed char) data[42]; + break; + default: + return DC_STATUS_UNSUPPORTED; + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +tecdiving_divecomputereu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + unsigned int time = 0; + unsigned int interval = data[47]; + + unsigned int offset = SZ_HEADER; + while (offset + 8 <= size) { + dc_sample_value_t sample = {0}; + + // Time (seconds). + time += interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Depth (1/10 m). + unsigned int depth = array_uint16_be (data + offset + 2); + sample.depth = depth / 10.0; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + // Temperature (Celsius). + signed int temperature = (signed char) data[offset]; + sample.temperature = temperature; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + + // ppO2 + unsigned int ppo2 = data[offset + 1]; + sample.ppo2 = ppo2 / 10.0; + if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); + + // Setpoint + unsigned int setpoint = data[offset + 4]; + sample.setpoint = setpoint / 10.0; + if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); + + offset += 8; + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/timer.c b/src/timer.c index 36d1f8f..14ccd46 100644 --- a/src/timer.c +++ b/src/timer.c @@ -132,7 +132,7 @@ dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs) value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_nsec / 1000; #elif defined (HAVE_MACH_ABSOLUTE_TIME) uint64_t now = mach_absolute_time(); - value = (now - timer->timestamp) * timer->info.numer / timer->info.denom; + value = (now - timer->timestamp) * timer->info.numer / (timer->info.denom * 1000); #else struct timeval now, delta; if (gettimeofday (&now, NULL) != 0) { diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c deleted file mode 100644 index c8e7894..0000000 --- a/src/uwatec_g2.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2008 Jef Driesen - * (C) 2017 Linus Torvalds - * - * 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 // malloc, free -#include // strncmp, strstr - -#include "uwatec_g2.h" -#include "context-private.h" -#include "device-private.h" -#include "array.h" -#include "platform.h" - -#define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_g2_device_vtable) - -#define RX_PACKET_SIZE 64 -#define TX_PACKET_SIZE 32 - -#define ALADINSPORTMATRIX 0x17 -#define ALADINSQUARE 0x22 -#define G2 0x32 - -typedef struct uwatec_g2_device_t { - dc_device_t base; - dc_iostream_t *iostream; - unsigned int timestamp; - unsigned int devtime; - dc_ticks_t systime; -} uwatec_g2_device_t; - -static dc_status_t uwatec_g2_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size); -static dc_status_t uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); -static dc_status_t uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); - -static const dc_device_vtable_t uwatec_g2_device_vtable = { - sizeof(uwatec_g2_device_t), - DC_FAMILY_UWATEC_G2, - uwatec_g2_device_set_fingerprint, /* set_fingerprint */ - NULL, /* read */ - NULL, /* write */ - uwatec_g2_device_dump, /* dump */ - uwatec_g2_device_foreach, /* foreach */ - NULL, /* timesync */ - NULL /* close */ -}; - -static dc_status_t -uwatec_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); - -static dc_status_t -receive_data (uwatec_g2_device_t *device, dc_event_progress_t *progress, unsigned char *data, unsigned int size) -{ - while (size) { - unsigned char buf[RX_PACKET_SIZE]; - size_t transferred = 0; - dc_status_t rc = DC_STATUS_SUCCESS; - unsigned int len = 0; - - rc = dc_iostream_read (device->iostream, buf, sizeof(buf), &transferred); - if (rc != DC_STATUS_SUCCESS) { - ERROR (device->base.context, "Failed to receive the packet."); - return rc; - } - - if (transferred < 1) { - ERROR (device->base.context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred); - return DC_STATUS_PROTOCOL; - } - - len = buf[0]; - if (len + 1 > transferred) { - ERROR (device->base.context, "Invalid payload length (%u).", len); - return DC_STATUS_PROTOCOL; - } - - HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); - - if (len > size) { - ERROR (device->base.context, "Insufficient buffer space available."); - return DC_STATUS_PROTOCOL; - } - - // Update and emit a progress event. - if (progress) { - progress->current += len; - device_event_emit (&device->base, DC_EVENT_PROGRESS, progress); - } - - memcpy(data, buf + 1, len); - size -= len; - data += len; - } - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) -{ - unsigned char buf[TX_PACKET_SIZE + 1]; - dc_status_t status = DC_STATUS_SUCCESS; - size_t transferred = 0; - - if (csize + 2 > sizeof(buf)) { - ERROR (device->base.context, "command too big (%d)", csize); - return DC_STATUS_INVALIDARGS; - } - - HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", command, csize); - - buf[0] = 0; - buf[1] = csize; - memcpy(buf + 2, command, csize); - memset(buf + 2 + csize, 0, sizeof(buf) - (csize + 2)); - - if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { - status = dc_iostream_write(device->iostream, buf + 1, csize + 1, &transferred); - } else { - status = dc_iostream_write(device->iostream, buf, sizeof(buf), &transferred); - } - if (status != DC_STATUS_SUCCESS) { - ERROR (device->base.context, "Failed to send the command."); - return status; - } - - status = receive_data (device, NULL, answer, asize); - if (status != DC_STATUS_SUCCESS) { - ERROR (device->base.context, "Failed to receive the answer."); - return status; - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_g2_handshake (uwatec_g2_device_t *device, unsigned int model) -{ - dc_device_t *abstract = (dc_device_t *) device; - - // Command template. - unsigned char answer[1] = {0}; - unsigned char command[5] = {0x00, 0x10, 0x27, 0, 0}; - - // The vendor software does not do a handshake for the Aladin Sport Matrix, - // so let's not do any either. - if (model == ALADINSPORTMATRIX) - return DC_STATUS_SUCCESS; - - // Handshake (stage 1). - command[0] = 0x1B; - dc_status_t rc = uwatec_g2_transfer (device, command, 1, answer, 1); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Verify the answer. - if (answer[0] != 0x01) { - ERROR (abstract->context, "Unexpected answer byte(s)."); - return DC_STATUS_PROTOCOL; - } - - // Handshake (stage 2). - command[0] = 0x1C; - rc = uwatec_g2_transfer (device, command, 5, answer, 1); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Verify the answer. - if (answer[0] != 0x01) { - ERROR (abstract->context, "Unexpected answer byte(s)."); - return DC_STATUS_PROTOCOL; - } - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -uwatec_g2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) -{ - dc_status_t status = DC_STATUS_SUCCESS; - uwatec_g2_device_t *device = NULL; - - if (out == NULL) - return DC_STATUS_INVALIDARGS; - - // Allocate memory. - device = (uwatec_g2_device_t *) dc_device_allocate (context, &uwatec_g2_device_vtable); - if (device == NULL) { - ERROR (context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Set the default values. - device->iostream = iostream; - device->timestamp = 0; - device->systime = (dc_ticks_t) -1; - device->devtime = 0; - - // Perform the handshaking. - status = uwatec_g2_handshake (device, model); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to handshake with the device."); - goto error_free; - } - - *out = (dc_device_t*) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_device_deallocate ((dc_device_t *) device); - return status; -} - - -static dc_status_t -uwatec_g2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) -{ - uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; - - if (size && size != 4) - return DC_STATUS_INVALIDARGS; - - if (size) - device->timestamp = array_uint32_le (data); - else - device->timestamp = 0; - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) -{ - uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Enable progress notifications. - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - // Read the model number. - unsigned char cmd_model[1] = {0x10}; - unsigned char model[1] = {0}; - rc = uwatec_g2_transfer (device, cmd_model, sizeof (cmd_model), model, sizeof (model)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Read the serial number. - unsigned char cmd_serial[1] = {0x14}; - unsigned char serial[4] = {0}; - rc = uwatec_g2_transfer (device, cmd_serial, sizeof (cmd_serial), serial, sizeof (serial)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Read the device clock. - unsigned char cmd_devtime[1] = {0x1A}; - unsigned char devtime[4] = {0}; - rc = uwatec_g2_transfer (device, cmd_devtime, sizeof (cmd_devtime), devtime, sizeof (devtime)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Store the clock calibration values. - device->systime = dc_datetime_now (); - device->devtime = array_uint32_le (devtime); - - // Update and emit a progress event. - progress.current += 9; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - // Emit a clock event. - dc_event_clock_t clock; - clock.systime = device->systime; - clock.devtime = device->devtime; - device_event_emit (&device->base, DC_EVENT_CLOCK, &clock); - - // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = model[0]; - devinfo.firmware = 0; - devinfo.serial = array_uint32_le (serial); - device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); - - // Command template. - unsigned char command[9] = {0x00, - (device->timestamp ) & 0xFF, - (device->timestamp >> 8 ) & 0xFF, - (device->timestamp >> 16) & 0xFF, - (device->timestamp >> 24) & 0xFF, - 0x10, - 0x27, - 0, - 0}; - - // Data Length. - command[0] = 0xC6; - unsigned char answer[4] = {0}; - rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - unsigned int length = array_uint32_le (answer); - - // Update and emit a progress event. - progress.maximum = 4 + 9 + (length ? length + 4 : 0); - progress.current += 4; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - if (length == 0) - return DC_STATUS_SUCCESS; - - // Allocate the required amount of memory. - if (!dc_buffer_resize (buffer, length)) { - ERROR (abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; - } - - unsigned char *data = dc_buffer_get_data (buffer); - - // Data. - command[0] = 0xC4; - rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - unsigned int total = array_uint32_le (answer); - - // Update and emit a progress event. - progress.current += 4; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - if (total != length + 4) { - ERROR (abstract->context, "Received an unexpected size."); - return DC_STATUS_PROTOCOL; - } - - rc = receive_data (device, &progress, data, length); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); - return rc; - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) -{ - dc_buffer_t *buffer = dc_buffer_new (0); - if (buffer == NULL) - return DC_STATUS_NOMEMORY; - - dc_status_t rc = uwatec_g2_device_dump (abstract, buffer); - if (rc != DC_STATUS_SUCCESS) { - dc_buffer_free (buffer); - return rc; - } - - rc = uwatec_g2_extract_dives (abstract, - dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); - - dc_buffer_free (buffer); - - return rc; -} - - -static dc_status_t -uwatec_g2_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) -{ - if (abstract && !ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - const unsigned char header[4] = {0xa5, 0xa5, 0x5a, 0x5a}; - - // Search the data stream for start markers. - unsigned int previous = size; - unsigned int current = (size >= 4 ? size - 4 : 0); - while (current > 0) { - current--; - if (memcmp (data + current, header, sizeof (header)) == 0) { - // Get the length of the profile data. - unsigned int len = array_uint32_le (data + current + 4); - - // Check for a buffer overflow. - if (current + len > previous) - return DC_STATUS_DATAFORMAT; - - if (callback && !callback (data + current, len, data + current + 8, 4, userdata)) - return DC_STATUS_SUCCESS; - - // Prepare for the next dive. - previous = current; - current = (current >= 4 ? current - 4 : 0); - } - } - - return DC_STATUS_SUCCESS; -} diff --git a/src/uwatec_meridian.c b/src/uwatec_meridian.c deleted file mode 100644 index 378facb..0000000 --- a/src/uwatec_meridian.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2013 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 "uwatec_meridian.h" -#include "context-private.h" -#include "device-private.h" -#include "checksum.h" -#include "array.h" - -#define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_meridian_device_vtable) - -#define ACK 0x11 -#define NAK 0x66 - -typedef struct uwatec_meridian_device_t { - dc_device_t base; - dc_iostream_t *iostream; - unsigned int timestamp; - unsigned int devtime; - dc_ticks_t systime; -} uwatec_meridian_device_t; - -static dc_status_t uwatec_meridian_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size); -static dc_status_t uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); -static dc_status_t uwatec_meridian_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); - -static const dc_device_vtable_t uwatec_meridian_device_vtable = { - sizeof(uwatec_meridian_device_t), - DC_FAMILY_UWATEC_MERIDIAN, - uwatec_meridian_device_set_fingerprint, /* set_fingerprint */ - NULL, /* read */ - NULL, /* write */ - uwatec_meridian_device_dump, /* dump */ - uwatec_meridian_device_foreach, /* foreach */ - NULL, /* timesync */ - NULL /* close */ -}; - -static dc_status_t -uwatec_meridian_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); - -static dc_status_t -uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) -{ - dc_status_t status = DC_STATUS_SUCCESS; - dc_device_t *abstract = (dc_device_t *) device; - - assert (csize > 0 && csize <= 255); - - // Build the packet. - unsigned char packet[255 + 12] = { - 0xFF, 0xFF, 0xFF, - 0xA6, 0x59, 0xBD, 0xC2, - 0x00, /* length */ - 0x00, 0x00, 0x00, - 0x00}; /* data and checksum */ - memcpy (packet + 11, command, csize); - packet[7] = csize; - packet[11 + csize] = checksum_xor_uint8 (packet + 7, csize + 4, 0x00); - - // Send the packet. - status = dc_iostream_write (device->iostream, packet, csize + 12, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); - return status; - } - - // Read the echo. - unsigned char echo[sizeof(packet)]; - status = dc_iostream_read (device->iostream, echo, csize + 12, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the echo."); - return status; - } - - // Verify the echo. - if (memcmp (echo, packet, csize + 12) != 0) { - WARNING (abstract->context, "Unexpected echo."); - return DC_STATUS_PROTOCOL; - } - - // Read the header. - unsigned char header[6]; - status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the header."); - return status; - } - - // Verify the header. - if (header[0] != ACK || array_uint32_le (header + 1) != asize + 1 || header[5] != packet[11]) { - WARNING (abstract->context, "Unexpected header."); - return DC_STATUS_PROTOCOL; - } - - // Read the packet. - status = dc_iostream_read (device->iostream, answer, asize, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the packet."); - return status; - } - - // Read the checksum. - unsigned char csum = 0x00; - status = dc_iostream_read (device->iostream, &csum, sizeof (csum), NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the checksum."); - return status; - } - - // Verify the checksum. - unsigned char ccsum = 0x00; - ccsum = checksum_xor_uint8 (header + 1, sizeof (header) - 1, ccsum); - ccsum = checksum_xor_uint8 (answer, asize, ccsum); - if (csum != ccsum) { - ERROR (abstract->context, "Unexpected answer checksum."); - return DC_STATUS_PROTOCOL; - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_meridian_handshake (uwatec_meridian_device_t *device) -{ - dc_device_t *abstract = (dc_device_t *) device; - - // Command template. - unsigned char answer[1] = {0}; - unsigned char command[5] = {0x00, 0x10, 0x27, 0, 0}; - - // Handshake (stage 1). - command[0] = 0x1B; - dc_status_t rc = uwatec_meridian_transfer (device, command, 1, answer, 1); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Verify the answer. - if (answer[0] != 0x01) { - ERROR (abstract->context, "Unexpected answer byte(s)."); - return DC_STATUS_PROTOCOL; - } - - // Handshake (stage 2). - command[0] = 0x1C; - rc = uwatec_meridian_transfer (device, command, 5, answer, 1); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Verify the answer. - if (answer[0] != 0x01) { - ERROR (abstract->context, "Unexpected answer byte(s)."); - return DC_STATUS_PROTOCOL; - } - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -uwatec_meridian_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) -{ - dc_status_t status = DC_STATUS_SUCCESS; - uwatec_meridian_device_t *device = NULL; - - if (out == NULL) - return DC_STATUS_INVALIDARGS; - - // Allocate memory. - device = (uwatec_meridian_device_t *) dc_device_allocate (context, &uwatec_meridian_device_vtable); - if (device == NULL) { - ERROR (context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Set the default values. - device->iostream = iostream; - device->timestamp = 0; - device->systime = (dc_ticks_t) -1; - device->devtime = 0; - - // Set the serial communication protocol (57600 8N1). - status = dc_iostream_configure (device->iostream, 57600, 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; - } - - // 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); - - // Perform the handshaking. - status = uwatec_meridian_handshake (device); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to handshake with the device."); - goto error_free; - } - - *out = (dc_device_t*) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_device_deallocate ((dc_device_t *) device); - return status; -} - - -static dc_status_t -uwatec_meridian_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) -{ - uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract; - - if (size && size != 4) - return DC_STATUS_INVALIDARGS; - - if (size) - device->timestamp = array_uint32_le (data); - else - device->timestamp = 0; - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) -{ - dc_status_t status = DC_STATUS_SUCCESS; - uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Enable progress notifications. - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - // Command template. - unsigned char command[9] = {0x00, - (device->timestamp ) & 0xFF, - (device->timestamp >> 8 ) & 0xFF, - (device->timestamp >> 16) & 0xFF, - (device->timestamp >> 24) & 0xFF, - 0x10, - 0x27, - 0, - 0}; - - // Read the model number. - command[0] = 0x10; - unsigned char model[1] = {0}; - rc = uwatec_meridian_transfer (device, command, 1, model, sizeof (model)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Read the serial number. - command[0] = 0x14; - unsigned char serial[4] = {0}; - rc = uwatec_meridian_transfer (device, command, 1, serial, sizeof (serial)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Read the device clock. - command[0] = 0x1A; - unsigned char devtime[4] = {0}; - rc = uwatec_meridian_transfer (device, command, 1, devtime, sizeof (devtime)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Store the clock calibration values. - device->systime = dc_datetime_now (); - device->devtime = array_uint32_le (devtime); - - // Update and emit a progress event. - progress.current += 9; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - // Emit a clock event. - dc_event_clock_t clock; - clock.systime = device->systime; - clock.devtime = device->devtime; - device_event_emit (&device->base, DC_EVENT_CLOCK, &clock); - - // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = model[0]; - devinfo.firmware = 0; - devinfo.serial = array_uint32_le (serial); - device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); - - // Data Length. - command[0] = 0xC6; - unsigned char answer[4] = {0}; - rc = uwatec_meridian_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - unsigned int length = array_uint32_le (answer); - - // Update and emit a progress event. - progress.maximum = 4 + 9 + (length ? length + 4 : 0); - progress.current += 4; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - if (length == 0) - return DC_STATUS_SUCCESS; - - // Allocate the required amount of memory. - if (!dc_buffer_resize (buffer, length)) { - ERROR (abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; - } - - unsigned char *data = dc_buffer_get_data (buffer); - - // Data. - command[0] = 0xC4; - rc = uwatec_meridian_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - unsigned int total = array_uint32_le (answer); - - // Update and emit a progress event. - progress.current += 4; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - if (total != length + 4) { - ERROR (abstract->context, "Received an unexpected size."); - return DC_STATUS_PROTOCOL; - } - - unsigned int nbytes = 0; - while (nbytes < length) { - - // Read the header. - unsigned char header[5]; - status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the header."); - return status; - } - - // Get the packet size. - unsigned int packetsize = array_uint32_le (header); - if (packetsize < 1 || nbytes + packetsize - 1 > length) { - WARNING (abstract->context, "Unexpected header."); - return DC_STATUS_PROTOCOL; - } - - // Read the packet data. - status = dc_iostream_read (device->iostream, data + nbytes, packetsize - 1, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the packet."); - return status; - } - - // Read the checksum. - unsigned char csum = 0x00; - status = dc_iostream_read (device->iostream, &csum, sizeof (csum), NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the checksum."); - return status; - } - - // Verify the checksum. - unsigned char ccsum = 0x00; - ccsum = checksum_xor_uint8 (header, sizeof (header), ccsum); - ccsum = checksum_xor_uint8 (data + nbytes, packetsize - 1, ccsum); - if (csum != ccsum) { - ERROR (abstract->context, "Unexpected answer checksum."); - return DC_STATUS_PROTOCOL; - } - - - // Update and emit a progress event. - progress.current += packetsize - 1; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - nbytes += packetsize - 1; - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -uwatec_meridian_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) -{ - dc_buffer_t *buffer = dc_buffer_new (0); - if (buffer == NULL) - return DC_STATUS_NOMEMORY; - - dc_status_t rc = uwatec_meridian_device_dump (abstract, buffer); - if (rc != DC_STATUS_SUCCESS) { - dc_buffer_free (buffer); - return rc; - } - - rc = uwatec_meridian_extract_dives (abstract, - dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); - - dc_buffer_free (buffer); - - return rc; -} - - -static dc_status_t -uwatec_meridian_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) -{ - if (abstract && !ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - const unsigned char header[4] = {0xa5, 0xa5, 0x5a, 0x5a}; - - // Search the data stream for start markers. - unsigned int previous = size; - unsigned int current = (size >= 4 ? size - 4 : 0); - while (current > 0) { - current--; - if (memcmp (data + current, header, sizeof (header)) == 0) { - // Get the length of the profile data. - unsigned int len = array_uint32_le (data + current + 4); - - // Check for a buffer overflow. - if (current + len > previous) - return DC_STATUS_DATAFORMAT; - - if (callback && !callback (data + current, len, data + current + 8, 4, userdata)) - return DC_STATUS_SUCCESS; - - // Prepare for the next dive. - previous = current; - current = (current >= 4 ? current - 4 : 0); - } - } - - return DC_STATUS_SUCCESS; -} diff --git a/src/uwatec_meridian.h b/src/uwatec_meridian.h deleted file mode 100644 index 4ac80d3..0000000 --- a/src/uwatec_meridian.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2013 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 UWATEC_MERIDIAN_H -#define UWATEC_MERIDIAN_H - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -dc_status_t -uwatec_meridian_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* UWATEC_MERIDIAN_H */ diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 9bf0f99..c3e66b1 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -25,19 +25,44 @@ #include "uwatec_smart.h" #include "context-private.h" #include "device-private.h" +#include "checksum.h" +#include "platform.h" #include "array.h" #define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_smart_device_vtable) #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) -typedef struct uwatec_smart_device_t { +#define DATASIZE 254 +#define PACKETSIZE_USBHID_RX 64 +#define PACKETSIZE_USBHID_TX 32 + +#define CMD_MODEL 0x10 +#define CMD_SERIAL 0x14 +#define CMD_DEVTIME 0x1A +#define CMD_HANDSHAKE1 0x1B +#define CMD_HANDSHAKE2 0x1C +#define CMD_DATA 0xC4 +#define CMD_SIZE 0xC6 + +#define OK 0x01 +#define ACK 0x11 +#define NAK 0x66 + +typedef struct uwatec_smart_device_t uwatec_smart_device_t; + +typedef dc_status_t (*uwatec_smart_receive_t) (uwatec_smart_device_t *device, dc_event_progress_t *progress, unsigned char cmd, unsigned char data[], size_t size); +typedef dc_status_t (*uwatec_smart_send_t) (uwatec_smart_device_t *device, unsigned char cmd, const unsigned char data[], size_t size); + +struct uwatec_smart_device_t { dc_device_t base; dc_iostream_t *iostream; + uwatec_smart_send_t send; + uwatec_smart_receive_t receive; unsigned int timestamp; unsigned int devtime; dc_ticks_t systime; -} uwatec_smart_device_t; +}; static dc_status_t uwatec_smart_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size); static dc_status_t uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); @@ -59,18 +84,288 @@ static dc_status_t uwatec_smart_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); static dc_status_t -uwatec_smart_transfer (uwatec_smart_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +uwatec_smart_irda_send (uwatec_smart_device_t *device, unsigned char cmd, const unsigned char data[], size_t size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + if (size > DATASIZE) { + ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); + return DC_STATUS_PROTOCOL; + } + + // Build the packet. + unsigned char packet[1 + DATASIZE] = { + cmd}; + if (size) { + memcpy (packet + 1, data, size); + } + + // Send the packet. + rc = dc_iostream_write (device->iostream, packet, size + 1, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the data packet."); + return rc; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +uwatec_smart_irda_receive (uwatec_smart_device_t *device, dc_event_progress_t *progress, unsigned char cmd, unsigned char data[], size_t size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + size_t nbytes = 0; + while (nbytes < size) { + // Set the minimum packet size. + size_t len = 32; + + // Increase the packet size if more data is immediately available. + size_t available = 0; + rc = dc_iostream_get_available (device->iostream, &available); + if (rc == DC_STATUS_SUCCESS && available > len) + len = available; + + // Limit the packet size to the total size. + if (nbytes + len > size) + len = size - nbytes; + + rc = dc_iostream_read (device->iostream, data + nbytes, len, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the data packet."); + return rc; + } + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + nbytes += len; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +uwatec_smart_serial_send (uwatec_smart_device_t *device, unsigned char cmd, const unsigned char data[], size_t size) { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - status = dc_iostream_write (device->iostream, command, csize, NULL); + if (size > DATASIZE) { + ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); + return DC_STATUS_PROTOCOL; + } + + // Build the packet. + unsigned char packet[12 + DATASIZE + 1] = { + 0xFF, 0xFF, 0xFF, + 0xA6, 0x59, 0xBD, 0xC2, + size + 1, + 0x00, 0x00, 0x00, + cmd}; + if (size) { + memcpy (packet + 12, data, size); + } + packet[12 + size] = checksum_xor_uint8 (packet + 7, size + 5, 0x00); + + // Send the packet. + status = dc_iostream_write (device->iostream, packet, size + 13, NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; } - status = dc_iostream_read (device->iostream, answer, asize, NULL); + // Read the echo and the ACK byte. + unsigned char echo[sizeof(packet) + 1]; + status = dc_iostream_read (device->iostream, echo, size + 13 + 1, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the echo."); + return status; + } + + // Verify the echo. + if (memcmp (echo, packet, size + 13) != 0) { + WARNING (abstract->context, "Unexpected echo."); + return DC_STATUS_PROTOCOL; + } + + // Verify the ACK byte. + unsigned char ack = echo[size + 13]; + if (ack != ACK) { + WARNING (abstract->context, "Unexpected ACK byte (%02x).", ack); + return DC_STATUS_PROTOCOL; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +uwatec_smart_serial_receive (uwatec_smart_device_t *device, dc_event_progress_t *progress, unsigned char cmd, unsigned char data[], size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + size_t nbytes = 0; + while (nbytes < size) { + // Read the header. + unsigned char header[5]; + status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the header."); + return status; + } + + // Get the packet size. + unsigned int len = array_uint32_le (header); + if (len < 1 || nbytes + len - 1 > size) { + WARNING (abstract->context, "Unexpected header size (%u).", len); + return DC_STATUS_PROTOCOL; + } + + // Verify the command byte. + unsigned char rsp = header[4]; + if (rsp != cmd) { + ERROR (abstract->context, "Unexpected header command byte (%02x).", rsp); + return DC_STATUS_PROTOCOL; + } + + // Read the packet data. + status = dc_iostream_read (device->iostream, data + nbytes, len - 1, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet."); + return status; + } + + // Read the checksum. + unsigned char csum = 0x00; + status = dc_iostream_read (device->iostream, &csum, sizeof (csum), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the checksum."); + return status; + } + + // Verify the checksum. + unsigned char ccsum = 0x00; + ccsum = checksum_xor_uint8 (header, sizeof (header), ccsum); + ccsum = checksum_xor_uint8 (data + nbytes, len - 1, ccsum); + if (csum != ccsum) { + ERROR (abstract->context, "Unexpected answer checksum."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + if (progress) { + progress->current += len - 1; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + nbytes += len - 1; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +uwatec_smart_usbhid_send (uwatec_smart_device_t *device, unsigned char cmd, const unsigned char data[], size_t size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char buf[PACKETSIZE_USBHID_TX + 1]; + + if (size + 3 > sizeof(buf)) { + ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); + return DC_STATUS_INVALIDARGS; + } + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "cmd", data, size); + + buf[0] = 0; + buf[1] = size + 1; + buf[2] = cmd; + if (size) { + memcpy(buf + 3, data, size); + } + memset(buf + 3 + size, 0, sizeof(buf) - (size + 3)); + + if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { + rc = dc_iostream_write(device->iostream, buf + 1, size + 2, NULL); + } else { + rc = dc_iostream_write(device->iostream, buf, sizeof(buf), NULL); + } + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return rc; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +uwatec_smart_usbhid_receive (uwatec_smart_device_t *device, dc_event_progress_t *progress, unsigned char cmd, unsigned char data[], size_t size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char buf[PACKETSIZE_USBHID_RX]; + + size_t nbytes = 0; + while (nbytes < size) { + size_t transferred = 0; + rc = dc_iostream_read (device->iostream, buf, sizeof(buf), &transferred); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet."); + return rc; + } + + if (transferred < 1) { + ERROR (abstract->context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred); + return DC_STATUS_PROTOCOL; + } + + unsigned int len = buf[0]; + if (len + 1 > transferred) { + ERROR (abstract->context, "Invalid payload length (%u).", len); + return DC_STATUS_PROTOCOL; + } + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); + + if (len > size) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + memcpy(data + nbytes, buf + 1, len); + nbytes += len; + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +uwatec_smart_transfer (uwatec_smart_device_t *device, unsigned char cmd, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + status = device->send (device, cmd, command, csize); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + status = device->receive (device, NULL, cmd, answer, asize); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -84,31 +379,31 @@ static dc_status_t uwatec_smart_handshake (uwatec_smart_device_t *device) { dc_device_t *abstract = (dc_device_t *) device; - - // Command template. + const unsigned char params[] = {0x10, 0x27, 0, 0}; unsigned char answer[1] = {0}; - unsigned char command[5] = {0x00, 0x10, 0x27, 0, 0}; + + // Skip the handshake for BLE communication. + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) + return DC_STATUS_SUCCESS; // Handshake (stage 1). - command[0] = 0x1B; - dc_status_t rc = uwatec_smart_transfer (device, command, 1, answer, 1); + dc_status_t rc = uwatec_smart_transfer (device, CMD_HANDSHAKE1, NULL, 0, answer, sizeof(answer)); if (rc != DC_STATUS_SUCCESS) return rc; // Verify the answer. - if (answer[0] != 0x01) { + if (answer[0] != OK) { ERROR (abstract->context, "Unexpected answer byte(s)."); return DC_STATUS_PROTOCOL; } // Handshake (stage 2). - command[0] = 0x1C; - rc = uwatec_smart_transfer (device, command, 5, answer, 1); + rc = uwatec_smart_transfer (device, CMD_HANDSHAKE2, params, sizeof(params), answer, sizeof(answer)); if (rc != DC_STATUS_SUCCESS) return rc; // Verify the answer. - if (answer[0] != 0x01) { + if (answer[0] != OK) { ERROR (abstract->context, "Unexpected answer byte(s)."); return DC_STATUS_PROTOCOL; } @@ -139,6 +434,45 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ device->systime = (dc_ticks_t) -1; device->devtime = 0; + // Set the serial communication protocol (57600 8N1). + status = dc_iostream_configure (device->iostream, 57600, 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; + } + + // 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); + + // Select the correct send/receive function. + dc_transport_t transport = dc_iostream_get_transport(iostream); + switch (transport) { + case DC_TRANSPORT_IRDA: + device->send = uwatec_smart_irda_send; + device->receive = uwatec_smart_irda_receive; + break; + case DC_TRANSPORT_SERIAL: + device->send = uwatec_smart_serial_send; + device->receive = uwatec_smart_serial_receive; + break; + case DC_TRANSPORT_USBHID: + case DC_TRANSPORT_BLE: + device->send = uwatec_smart_usbhid_send; + device->receive = uwatec_smart_usbhid_receive; + break; + default: + ERROR (context, "Unsupported transport type (%u).", transport); + status = DC_STATUS_UNSUPPORTED; + goto error_free; + } + // Perform the handshaking. status = uwatec_smart_handshake (device); if (status != DC_STATUS_SUCCESS) { @@ -184,23 +518,20 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); // Read the model number. - unsigned char cmd_model[1] = {0x10}; unsigned char model[1] = {0}; - rc = uwatec_smart_transfer (device, cmd_model, sizeof (cmd_model), model, sizeof (model)); + rc = uwatec_smart_transfer (device, CMD_MODEL, NULL, 0, model, sizeof (model)); if (rc != DC_STATUS_SUCCESS) return rc; // Read the serial number. - unsigned char cmd_serial[1] = {0x14}; unsigned char serial[4] = {0}; - rc = uwatec_smart_transfer (device, cmd_serial, sizeof (cmd_serial), serial, sizeof (serial)); + rc = uwatec_smart_transfer (device, CMD_SERIAL, NULL, 0, serial, sizeof (serial)); if (rc != DC_STATUS_SUCCESS) return rc; // Read the device clock. - unsigned char cmd_devtime[1] = {0x1A}; unsigned char devtime[4] = {0}; - rc = uwatec_smart_transfer (device, cmd_devtime, sizeof (cmd_devtime), devtime, sizeof (devtime)); + rc = uwatec_smart_transfer (device, CMD_DEVTIME, NULL, 0, devtime, sizeof (devtime)); if (rc != DC_STATUS_SUCCESS) return rc; @@ -225,8 +556,8 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.serial = array_uint32_le (serial); device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); - // Command template. - unsigned char command[9] = {0x00, + // Command parameters. + const unsigned char params[] = { (device->timestamp ) & 0xFF, (device->timestamp >> 8 ) & 0xFF, (device->timestamp >> 16) & 0xFF, @@ -237,9 +568,8 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) 0}; // Data Length. - command[0] = 0xC6; unsigned char answer[4] = {0}; - rc = uwatec_smart_transfer (device, command, sizeof (command), answer, sizeof (answer)); + rc = uwatec_smart_transfer (device, CMD_SIZE, params, sizeof (params), answer, sizeof (answer)); if (rc != DC_STATUS_SUCCESS) return rc; @@ -262,48 +592,24 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) unsigned char *data = dc_buffer_get_data (buffer); // Data. - command[0] = 0xC4; - rc = uwatec_smart_transfer (device, command, sizeof (command), answer, sizeof (answer)); + rc = uwatec_smart_transfer (device, CMD_DATA, params, sizeof (params), answer, sizeof (answer)); if (rc != DC_STATUS_SUCCESS) return rc; - unsigned int total = array_uint32_le (answer); - // Update and emit a progress event. progress.current += 4; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + unsigned int total = array_uint32_le (answer); if (total != length + 4) { ERROR (abstract->context, "Received an unexpected size."); return DC_STATUS_PROTOCOL; } - unsigned int nbytes = 0; - while (nbytes < length) { - // Set the minimum packet size. - unsigned int len = 32; - - // Increase the packet size if more data is immediately available. - size_t available = 0; - rc = dc_iostream_get_available (device->iostream, &available); - if (rc == DC_STATUS_SUCCESS && available > len) - len = available; - - // Limit the packet size to the total size. - if (nbytes + len > length) - len = length - nbytes; - - rc = dc_iostream_read (device->iostream, data + nbytes, len, NULL); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); - return rc; - } - - // Update and emit a progress event. - progress.current += len; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - - nbytes += len; + rc = device->receive (device, &progress, CMD_DATA, data, length); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the answer."); + return rc; } return DC_STATUS_SUCCESS;