Merge git://github.com/libdivecomputer/libdivecomputer into Subsurface-NG

Merge with upstream libdivecomputer from Jef.

This fixes some sleeping functions, and also implements support for the
Tecdiving DiveComputer.eu dive computers.

There's also various minor cleanups.  Most notable is perhaps the
unification of the Uwatec dive computer backends.

* git://github.com/libdivecomputer/libdivecomputer:
  Initialize the socket library for the bluetooth discovery
  Fix the length of the Suunto D6i gas change event
  Add support for the Tecdiving DiveComputer.eu
  Fix the Mac OS X timer implementation
  Add the average depth to the xml output
  Skip the handshake for BLE communication
  Unify the Uwatec Smart, Meridian and G2 backends
  Re-organize the packet send/receive code
  Use symbolic constants for the commands
  Implement an rfcomm filter function
  Remove the filter for HW OSTC's without bluetooth
  Implement the sleep function for IrDA and bluetooth
This commit is contained in:
Linus Torvalds 2018-06-27 08:05:00 -07:00
commit 02560a7e7f
21 changed files with 1259 additions and 1061 deletions

View File

@ -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[] = {

View File

@ -265,6 +265,20 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
fprintf (output->ostream, "<maxdepth>%.2f</maxdepth>\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, "<avgdepth>%.2f</avgdepth>\n",
convert_depth(avgdepth, output->units));
}
// Parse the temperature.
message ("Parsing the temperature.\n");
for (unsigned int i = 0; i < 3; ++i) {

View File

@ -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

View File

@ -482,6 +482,14 @@
RelativePath="..\src\suunto_vyper_parser.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
>
</File>
<File
RelativePath="..\src\timer.c"
>
@ -494,10 +502,6 @@
RelativePath="..\src\uwatec_aladin.c"
>
</File>
<File
RelativePath="..\src\uwatec_g2.c"
>
</File>
<File
RelativePath="..\src\uwatec_memomouse.c"
>
@ -506,10 +510,6 @@
RelativePath="..\src\uwatec_memomouse_parser.c"
>
</File>
<File
RelativePath="..\src\uwatec_meridian.c"
>
</File>
<File
RelativePath="..\src\uwatec_smart.c"
>
@ -824,6 +824,10 @@
RelativePath="..\src\suunto_vyper2.h"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.h"
>
</File>
<File
RelativePath="..\src\timer.h"
>
@ -840,18 +844,10 @@
RelativePath="..\src\uwatec_aladin.h"
>
</File>
<File
RelativePath="..\src\uwatec_g2.h"
>
</File>
<File
RelativePath="..\src\uwatec_memomouse.h"
>
</File>
<File
RelativePath="..\src\uwatec_meridian.h"
>
</File>
<File
RelativePath="..\src\uwatec_smart.h"
>

View File

@ -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 \

View File

@ -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;
}

View File

@ -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);
@ -125,20 +126,18 @@ static const dc_descriptor_t g_descriptors[] = {
{"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/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},
{"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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -34,6 +34,7 @@
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#include <sys/time.h>
#include <time.h>
#endif
#include <libdivecomputer/common.h>
@ -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);

View File

@ -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];

View File

@ -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 <string.h> // memcmp, memcpy
#include <stdlib.h> // 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;
}

View File

@ -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 <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
@ -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 */

View File

@ -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 <stdlib.h>
#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;
}

View File

@ -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) {

View File

@ -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 <stdlib.h> // malloc, free
#include <string.h> // 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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include <assert.h>
#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;
}

View File

@ -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 <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#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 */

View File

@ -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,50 +592,26 @@ 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);
rc = device->receive (device, &progress, CMD_DATA, data, length);
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;
}
return DC_STATUS_SUCCESS;
}