From 344bfab229a17c7227b9bec02f616505a8d9e998 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 26 May 2017 19:29:39 -0400 Subject: [PATCH 01/35] Altered model detection after seeing some bytes change after a firmware update. --- src/cochran_commander.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 99fe369..d2c47b0 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -42,7 +42,7 @@ typedef enum cochran_endian_t { } cochran_endian_t; typedef struct cochran_commander_model_t { - unsigned char id[8 + 1]; + unsigned char id[2 + 1]; unsigned int model; } cochran_commander_model_t; @@ -199,10 +199,10 @@ static unsigned int cochran_commander_get_model (cochran_commander_device_t *device) { const cochran_commander_model_t models[] = { - {"AM\x11""2212\x02", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, - {"AM7303\x8b\x43", COCHRAN_MODEL_EMC_14}, - {"AMA315\xC3\xC5", COCHRAN_MODEL_EMC_16}, - {"AM2315\xA3\x71", COCHRAN_MODEL_EMC_20}, + {"\x11""2", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, + {"73", COCHRAN_MODEL_EMC_14}, + {"A3", COCHRAN_MODEL_EMC_16}, + {"23", COCHRAN_MODEL_EMC_20}, }; unsigned int model = 0xFFFFFFFF; From 45457307765eb77790484e70586bd8f99045125e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 27 May 2017 22:15:38 +0200 Subject: [PATCH 02/35] Report the average ppO2 in external O2 sensor mode only Just like the ppO2 values from the O2 sensors, the average ppO2 should be reported in external O2 sensor mode only. --- src/shearwater_predator_parser.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index e922220..c923153 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -444,11 +444,11 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal if ((status & OC) == 0) { // PPO2 -#ifdef SENSOR_AVERAGE - sample.ppo2 = data[offset + 6] / 100.0; - if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); -#else if ((status & PPO2_EXTERNAL) == 0) { +#ifdef SENSOR_AVERAGE + sample.ppo2 = data[offset + 6] / 100.0; + if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); +#else sample.ppo2 = data[offset + 12] * parser->calibration[0]; if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); @@ -457,8 +457,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.ppo2 = data[offset + 15] * parser->calibration[2]; if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); - } #endif + } // Setpoint if (parser->petrel) { From 0b22a2ba3311f54fae92aad289cf44c638960a95 Mon Sep 17 00:00:00 2001 From: Kristaps Dzonsons Date: Fri, 2 Jun 2017 11:24:00 +0200 Subject: [PATCH 03/35] Fix the Oceanic OCS freedive mode --- src/oceanic_atom2_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index bcc4cd5..d63b568 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -397,7 +397,8 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == T3B || parser->model == VT3 || parser->model == DG03) { mode = (data[2] & 0xC0) >> 6; - } else if (parser->model == VEO20 || parser->model == VEO30) { + } else if (parser->model == VEO20 || parser->model == VEO30 || + parser->model == OCS) { mode = (data[1] & 0x60) >> 5; } From 404cea7417ebe7ee642da736ef70f45125d2a0ca Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 24 Apr 2017 20:18:26 +0200 Subject: [PATCH 04/35] Move the dummy IrDA implementation The dummy IrDA implementation is integrated in the main file. The appropriate implementation is selected using conditional compilation based on the features detect by the autotools build system. --- configure.ac | 10 +---- msvc/libdivecomputer.vcproj | 4 +- src/Makefile.am | 9 +---- src/descriptor.c | 12 +++++- src/irda.c | 48 +++++++++++++++++++++++ src/irda_dummy.c | 78 ------------------------------------- 6 files changed, 64 insertions(+), 97 deletions(-) delete mode 100644 src/irda_dummy.c diff --git a/configure.ac b/configure.ac index 0164677..8aee86d 100644 --- a/configure.ac +++ b/configure.ac @@ -114,13 +114,13 @@ AS_IF([test "x$with_hidapi" != "xno"], [ AC_SUBST([DEPENDENCIES]) # Checks for IrDA support. -AC_CHECK_HEADERS([winsock2.h af_irda.h], [irda_win32=yes], [irda_win32=no], [ +AC_CHECK_HEADERS([winsock2.h af_irda.h], , , [ #if HAVE_WINSOCK2_H # include # endif ]) -AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], [irda_linux=yes], [irda_linux=no], [ +AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], , , [ #if HAVE_SYS_SOCKET_H # include # endif @@ -129,12 +129,6 @@ AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], [irda_linux=yes], [i # endif ]) -if test "$irda_win32" = "yes" || test "$irda_linux" = "yes"; then - AC_DEFINE([HAVE_IRDA], [1], [IrDA support]) -fi - -AM_CONDITIONAL([IRDA], [test "$irda_win32" = "yes" || test "$irda_linux" = "yes"]) - # Checks for header files. AC_CHECK_HEADERS([linux/serial.h]) AC_CHECK_HEADERS([IOKit/serial/ioss.h]) diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 260ecbf..738dd12 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -42,7 +42,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_IRDA" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -119,7 +119,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_IRDA" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" RuntimeLibrary="2" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/src/Makefile.am b/src/Makefile.am index b98e57e..966bf94 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ libdivecomputer_la_LDFLAGS = \ -export-symbols libdivecomputer.exp if OS_WIN32 +libdivecomputer_la_LIBADD += -lws2_32 libdivecomputer_la_LDFLAGS += -Wc,-static-libgcc endif @@ -74,15 +75,7 @@ else libdivecomputer_la_SOURCES += serial.h serial_posix.c endif -if IRDA -if OS_WIN32 -libdivecomputer_la_LIBADD += -lws2_32 -endif libdivecomputer_la_SOURCES += irda.h irda.c -else -libdivecomputer_la_SOURCES += irda.h irda_dummy.c -endif - libdivecomputer_la_SOURCES += usbhid.h usbhid.c if OS_WIN32 diff --git a/src/descriptor.c b/src/descriptor.c index 70b51d4..e019172 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -29,6 +29,16 @@ #define USBHID #endif +#ifdef _WIN32 +#ifdef HAVE_AF_IRDA_H +#define IRDA +#endif +#else +#ifdef HAVE_LINUX_IRDA_H +#define IRDA +#endif +#endif + #include #include @@ -100,7 +110,7 @@ static const dc_descriptor_t g_descriptors[] = { /* Uwatec Memomouse */ {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, /* Uwatec Smart */ -#ifdef HAVE_IRDA +#ifdef IRDA {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10}, {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11}, {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11}, diff --git a/src/irda.c b/src/irda.c index 2271a69..0f20970 100644 --- a/src/irda.c +++ b/src/irda.c @@ -19,21 +19,31 @@ * MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include // malloc, free #include // snprintf #ifdef _WIN32 #define NOGDI #include #include + #ifdef HAVE_AF_IRDA_H + #define IRDA #include + #endif #else #include // strerror #include // errno #include // close #include // socket, getsockopt #include // socket, getsockopt + #ifdef HAVE_LINUX_IRDA_H + #define IRDA #include // irda #include // irda + #endif #include // select #include // ioctl #include @@ -86,6 +96,7 @@ struct dc_irda_t { int timeout; }; +#ifdef IRDA static dc_status_t syserror(s_errcode_t errcode) { @@ -102,10 +113,12 @@ syserror(s_errcode_t errcode) return DC_STATUS_IO; } } +#endif dc_status_t dc_irda_open (dc_irda_t **out, dc_context_t *context) { +#ifdef IRDA dc_status_t status = DC_STATUS_SUCCESS; dc_irda_t *device = NULL; @@ -167,11 +180,15 @@ error_free: #endif free (device); return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_close (dc_irda_t *device) { +#ifdef IRDA dc_status_t status = DC_STATUS_SUCCESS; if (device == NULL) @@ -200,11 +217,15 @@ dc_irda_close (dc_irda_t *device) free (device); return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_set_timeout (dc_irda_t *device, int timeout) { +#ifdef IRDA if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -213,6 +234,9 @@ dc_irda_set_timeout (dc_irda_t *device, int timeout) device->timeout = timeout; return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif } @@ -230,6 +254,7 @@ dc_irda_set_timeout (dc_irda_t *device, int timeout) dc_status_t dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata) { +#ifdef IRDA if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -303,11 +328,15 @@ dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata } return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name) { +#ifdef IRDA if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -341,11 +370,15 @@ dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name) } return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap) { +#ifdef IRDA if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -374,11 +407,15 @@ dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap } return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_get_available (dc_irda_t *device, size_t *value) { +#ifdef IRDA if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -398,11 +435,15 @@ dc_irda_get_available (dc_irda_t *device, size_t *value) *value = bytes; return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) { +#ifdef IRDA dc_status_t status = DC_STATUS_SUCCESS; size_t nbytes = 0; @@ -463,11 +504,15 @@ out_invalidargs: *actual = nbytes; return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif } dc_status_t dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) { +#ifdef IRDA dc_status_t status = DC_STATUS_SUCCESS; size_t nbytes = 0; @@ -520,4 +565,7 @@ out_invalidargs: *actual = nbytes; return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif } diff --git a/src/irda_dummy.c b/src/irda_dummy.c deleted file mode 100644 index 7579324..0000000 --- a/src/irda_dummy.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2010 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 "irda.h" - -dc_status_t -dc_irda_open (dc_irda_t **out, dc_context_t *context) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_close (dc_irda_t *device) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_set_timeout (dc_irda_t *device, int timeout) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_get_available (dc_irda_t *device, size_t *value) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual) -{ - return DC_STATUS_UNSUPPORTED; -} - -dc_status_t -dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual) -{ - return DC_STATUS_UNSUPPORTED; -} From 3c7bd525bb738686bc41c91dd43417aecc7ba818 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 22 Sep 2013 07:21:46 +0200 Subject: [PATCH 05/35] Add support for native bluetooth communication For the time being, the bluetooth communication code is of very limited use. It's not used anywhere in the library, and as an internal api it's also not available to applications. It serves mainly as a reference implementation for future use. The implementation supports Windows and Linux. --- configure.ac | 20 ++ msvc/libdivecomputer.vcproj | 12 +- src/Makefile.am | 5 +- src/bluetooth.c | 604 ++++++++++++++++++++++++++++++++++++ src/bluetooth.h | 174 +++++++++++ 5 files changed, 811 insertions(+), 4 deletions(-) create mode 100644 src/bluetooth.c create mode 100644 src/bluetooth.h diff --git a/configure.ac b/configure.ac index 8aee86d..695a86b 100644 --- a/configure.ac +++ b/configure.ac @@ -111,8 +111,28 @@ AS_IF([test "x$with_hidapi" != "xno"], [ ]) ]) +# Checks for BlueZ (bluetooth) support. +AC_ARG_WITH([bluez], + [AS_HELP_STRING([--without-bluez], + [Build without the BlueZ library])], + [], [with_bluez=auto]) +AS_IF([test "x$with_bluez" != "xno"], [ + PKG_CHECK_MODULES([BLUEZ], [bluez], [have_bluez=yes], [have_bluez=no]) + AS_IF([test "x$have_bluez" = "xyes"], [ + AC_DEFINE([HAVE_BLUEZ], [1], [BlueZ library]) + DEPENDENCIES="$DEPENDENCIES bluez" + ]) +]) + AC_SUBST([DEPENDENCIES]) +# Checks for Windows bluetooth support. +AC_CHECK_HEADERS([winsock2.h ws2bth.h], , , [ +#if HAVE_WINSOCK2_H +# include +# endif +]) + # Checks for IrDA support. AC_CHECK_HEADERS([winsock2.h af_irda.h], , , [ #if HAVE_WINSOCK2_H diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 738dd12..f7b7883 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -42,7 +42,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -119,7 +119,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H" RuntimeLibrary="2" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -194,6 +194,10 @@ RelativePath="..\src\atomics_cobalt_parser.c" > + + @@ -524,6 +528,10 @@ RelativePath="..\include\libdivecomputer\atomics_cobalt.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 966bf94..60b5654 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,9 @@ AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include -AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) +AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS) lib_LTLIBRARIES = libdivecomputer.la -libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) -lm +libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm libdivecomputer_la_LDFLAGS = \ -version-info $(DC_VERSION_LIBTOOL) \ -no-undefined \ @@ -77,6 +77,7 @@ endif libdivecomputer_la_SOURCES += irda.h irda.c libdivecomputer_la_SOURCES += usbhid.h usbhid.c +libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c if OS_WIN32 libdivecomputer_la_SOURCES += libdivecomputer.rc diff --git a/src/bluetooth.c b/src/bluetooth.c new file mode 100644 index 0000000..bc9ffe0 --- /dev/null +++ b/src/bluetooth.c @@ -0,0 +1,604 @@ +/* + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include // malloc, free + +#ifdef _WIN32 +#define NOGDI +#include +#include +#ifdef HAVE_WS2BTH_H +#define BLUETOOTH +#include +#endif +#else +#include // errno +#include // close +#include // socket, getsockopt +#include // socket, getsockopt +#include // select +#include // ioctl +#include +#ifdef HAVE_BLUEZ +#define BLUETOOTH +#include +#include +#include +#include +#endif +#endif + +#include "bluetooth.h" +#include "common-private.h" +#include "context-private.h" + +#ifdef _WIN32 +typedef int s_ssize_t; +typedef DWORD s_errcode_t; +#define S_ERRNO WSAGetLastError () +#define S_EINTR WSAEINTR +#define S_EAGAIN WSAEWOULDBLOCK +#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY +#define S_EINVAL WSAEINVAL +#define S_EACCES WSAEACCES +#define S_EAFNOSUPPORT WSAEAFNOSUPPORT +#define S_INVALID INVALID_SOCKET +#define S_IOCTL ioctlsocket +#define S_CLOSE closesocket +#else +typedef ssize_t s_ssize_t; +typedef int s_errcode_t; +#define S_ERRNO errno +#define S_EINTR EINTR +#define S_EAGAIN EAGAIN +#define S_ENOMEM ENOMEM +#define S_EINVAL EINVAL +#define S_EACCES EACCES +#define S_EAFNOSUPPORT EAFNOSUPPORT +#define S_INVALID -1 +#define S_IOCTL ioctl +#define S_CLOSE close +#endif + +#ifdef _WIN32 +#define DC_ADDRESS_FORMAT "%012I64X" +#else +#define DC_ADDRESS_FORMAT "%012llX" +#endif + +#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) + +#define MAX_DEVICES 255 +#define MAX_PERIODS 8 + +struct dc_bluetooth_t { + dc_context_t *context; +#ifdef _WIN32 + SOCKET fd; +#else + int fd; +#endif + int timeout; +}; + +#ifdef BLUETOOTH +static dc_status_t +syserror(s_errcode_t errcode) +{ + switch (errcode) { + case S_EINVAL: + return DC_STATUS_INVALIDARGS; + case S_ENOMEM: + return DC_STATUS_NOMEMORY; + case S_EACCES: + return DC_STATUS_NOACCESS; + case S_EAFNOSUPPORT: + return DC_STATUS_UNSUPPORTED; + default: + return DC_STATUS_IO; + } +} +#endif + +#ifdef HAVE_BLUEZ +static dc_bluetooth_address_t +dc_address_get (const bdaddr_t *ba) +{ + dc_bluetooth_address_t address = 0; + + size_t shift = 0; + for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) { + address |= (dc_bluetooth_address_t) ba->b[i] << shift; + shift += 8; + } + + return address; +} + +static void +dc_address_set (bdaddr_t *ba, dc_bluetooth_address_t address) +{ + size_t shift = 0; + for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) { + ba->b[i] = (address >> shift) & 0xFF; + shift += 8; + } +} +#endif + +dc_status_t +dc_bluetooth_open (dc_bluetooth_t **out, dc_context_t *context) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + dc_bluetooth_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (dc_bluetooth_t *) malloc (sizeof (dc_bluetooth_t)); + if (device == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + // Library context. + device->context = context; + + // Default to blocking reads. + device->timeout = -1; + +#ifdef _WIN32 + // Initialize the winsock dll. + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD (2, 2); + int rc = WSAStartup (wVersionRequested, &wsaData); + if (rc != 0) { + SYSERROR (context, rc); + status = DC_STATUS_UNSUPPORTED; + goto error_free; + } + + // Confirm that the winsock dll supports version 2.2. + // Note that if the dll supports versions greater than 2.2 in addition to + // 2.2, it will still return 2.2 since that is the version we requested. + if (LOBYTE (wsaData.wVersion) != 2 || + HIBYTE (wsaData.wVersion) != 2) { + ERROR (context, "Incorrect winsock version."); + status = DC_STATUS_UNSUPPORTED; + goto error_wsacleanup; + } +#endif + + // Open the socket. +#ifdef _WIN32 + device->fd = socket (AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); +#else + device->fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +#endif + if (device->fd == S_INVALID) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = syserror(errcode); + goto error_wsacleanup; + } + + *out = device; + + return DC_STATUS_SUCCESS; + +error_wsacleanup: +#ifdef _WIN32 + WSACleanup (); +error_free: +#endif + free (device); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_close (dc_bluetooth_t *device) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + + if (device == NULL) + return DC_STATUS_SUCCESS; + + // Terminate all send and receive operations. + shutdown (device->fd, 0); + + // Close the socket. + if (S_CLOSE (device->fd) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + dc_status_set_error(&status, syserror(errcode)); + } + +#ifdef _WIN32 + // Terminate the winsock dll. + if (WSACleanup () != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + dc_status_set_error(&status, syserror(errcode)); + } +#endif + + // Free memory. + free (device); + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_set_timeout (dc_bluetooth_t *device, int timeout) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (device->context, "Timeout: value=%i", timeout); + + device->timeout = timeout; + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_discover (dc_bluetooth_t *device, dc_bluetooth_callback_t callback, void *userdata) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + + if (device == NULL) + return DC_STATUS_INVALIDARGS; + +#ifdef _WIN32 + WSAQUERYSET wsaq; + memset(&wsaq, 0, sizeof (wsaq)); + wsaq.dwSize = sizeof (wsaq); + wsaq.dwNameSpace = NS_BTH; + wsaq.lpcsaBuffer = NULL; + + HANDLE hLookup; + if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSASERVICE_NOT_FOUND) { + // No remote bluetooth devices found. + status = DC_STATUS_SUCCESS; + } else { + SYSERROR (device->context, errcode); + status = syserror(errcode); + } + goto error_exit; + } + + unsigned char buf[4096]; + LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; + memset(pwsaResults, 0, sizeof(WSAQUERYSET)); + pwsaResults->dwSize = sizeof(WSAQUERYSET); + pwsaResults->dwNameSpace = NS_BTH; + pwsaResults->lpBlob = NULL; + + while (1) { + DWORD dwSize = sizeof(buf); + if (WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { + break; // No more results. + } + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_close; + } + + if (pwsaResults->dwNumberOfCsAddrs == 0 || + pwsaResults->lpcsaBuffer == NULL || + pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { + ERROR (device->context, "Invalid results returned"); + status = DC_STATUS_IO; + goto error_close; + } + + SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; + dc_bluetooth_address_t address = sa->btAddr; + const char *name = (char *) pwsaResults->lpszServiceInstanceName; + + INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); + + if (callback) callback (address, name, userdata); + + } + +error_close: + WSALookupServiceEnd (hLookup); +#else + // Get the resource number for the first available bluetooth adapter. + int dev = hci_get_route (NULL); + if (dev < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_exit; + } + + // Open a socket to the bluetooth adapter. + int fd = hci_open_dev (dev); + if (fd < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_exit; + } + + // Allocate a buffer to store the results of the discovery. + inquiry_info *devices = (inquiry_info *) malloc (MAX_DEVICES * sizeof(inquiry_info)); + if (devices == NULL) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_close; + } + + // Perform the bluetooth device discovery. The inquiry lasts for at + // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices + // will be returned. + int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); + if (ndevices < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_free; + } + + for (unsigned int i = 0; i < ndevices; ++i) { + dc_bluetooth_address_t address = dc_address_get (&devices[i].bdaddr); + + // Get the user friendly name. + char buf[HCI_MAX_NAME_LENGTH], *name = buf; + int rc = hci_read_remote_name (fd, &devices[i].bdaddr, sizeof(buf), buf, 0); + if (rc < 0) { + name = NULL; + } + + INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); + + if (callback) callback (address, name, userdata); + } + +error_free: + free(devices); +error_close: + hci_close_dev(fd); +#endif + +error_exit: + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_connect (dc_bluetooth_t *device, dc_bluetooth_address_t address, unsigned int port) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (device->context, "Connect: address=" DC_ADDRESS_FORMAT ", port=%d", address, port); + +#ifdef _WIN32 + SOCKADDR_BTH sa; + sa.addressFamily = AF_BTH; + sa.btAddr = address; + sa.port = port; + memset(&sa.serviceClassId, 0, sizeof(sa.serviceClassId)); +#else + struct sockaddr_rc sa; + sa.rc_family = AF_BLUETOOTH; + sa.rc_channel = port; + dc_address_set (&sa.rc_bdaddr, address); +#endif + + if (connect (device->fd, (struct sockaddr *) &sa, sizeof (sa)) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + return syserror(errcode); + } + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_get_available (dc_bluetooth_t *device, size_t *value) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + +#ifdef _WIN32 + unsigned long bytes = 0; +#else + int bytes = 0; +#endif + + if (S_IOCTL (device->fd, FIONREAD, &bytes) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + return syserror(errcode); + } + + if (value) + *value = bytes; + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_read (dc_bluetooth_t *device, void *data, size_t size, size_t *actual) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + size_t nbytes = 0; + + if (device == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out_invalidargs; + } + + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + struct timeval tvt; + if (device->timeout > 0) { + tvt.tv_sec = (device->timeout / 1000); + tvt.tv_usec = (device->timeout % 1000) * 1000; + } else if (device->timeout == 0) { + timerclear (&tvt); + } + + int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL); + if (rc < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (rc == 0) { + break; // Timeout. + } + + s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0); + if (n < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (n == 0) { + break; // EOF reached. + } + + nbytes += n; + } + + if (nbytes != size) { + status = DC_STATUS_TIMEOUT; + } + +out: + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); + +out_invalidargs: + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_write (dc_bluetooth_t *device, const void *data, size_t size, size_t *actual) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + size_t nbytes = 0; + + if (device == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out_invalidargs; + } + + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + int rc = select (device->fd + 1, NULL, &fds, NULL, NULL); + if (rc < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (rc == 0) { + break; // Timeout. + } + + s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); + if (n < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (n == 0) { + break; // EOF. + } + + nbytes += n; + } + + if (nbytes != size) { + status = DC_STATUS_TIMEOUT; + } + +out: + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + +out_invalidargs: + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} diff --git a/src/bluetooth.h b/src/bluetooth.h new file mode 100644 index 0000000..d2e3868 --- /dev/null +++ b/src/bluetooth.h @@ -0,0 +1,174 @@ +/* + * 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 DC_BLUETOOTH_H +#define DC_BLUETOOTH_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Opaque object representing a bluetooth connection. + */ +typedef struct dc_bluetooth_t dc_bluetooth_t; + +/** + * Bluetooth address (48 bits). + */ +#if defined (_WIN32) && !defined (__GNUC__) +typedef unsigned __int64 dc_bluetooth_address_t; +#else +typedef unsigned long long dc_bluetooth_address_t; +#endif + +/** + * Bluetooth enumeration callback. + * + * @param[in] address The bluetooth device address. + * @param[in] name The bluetooth device name. + * @param[in] userdata The user data pointer. + */ +typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata); + +/** + * Open an bluetooth connection. + * + * @param[out] bluetooth A location to store the bluetooth connection. + * @param[in] context A valid context object. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_open (dc_bluetooth_t **bluetooth, dc_context_t *context); + +/** + * Close the bluetooth connection and free all resources. + * + * @param[in] bluetooth A valid bluetooth connection. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_close (dc_bluetooth_t *bluetooth); + +/** + * Set the read timeout. + * + * There are three distinct modes available: + * + * 1. Blocking (timeout < 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive, + * the operation will block forever. + * + * 2. Non-blocking (timeout == 0): + * + * The read operation returns immediately with the bytes that have + * already been received, even if no bytes have been received. + * + * 3. Timeout (timeout > 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive + * within the specified amount of time, the operation will return + * with the bytes that have already been received. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] timeout The timeout in milliseconds. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_set_timeout (dc_bluetooth_t *bluetooth, int timeout); + +/** + * Enumerate the bluetooth devices. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] callback The callback function to call. + * @param[in] userdata User data to pass to the callback function. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_discover (dc_bluetooth_t *bluetooth, dc_bluetooth_callback_t callback, void *userdata); + +/** + * Connect to an bluetooth device. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] address The bluetooth device address. + * @param[in] port The bluetooth port number. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_connect (dc_bluetooth_t *bluetooth, dc_bluetooth_address_t address, unsigned int port); + +/** + * Query the number of available bytes in the input buffer. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[out] value A location to store the number of bytes in + * the input buffer. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_get_available (dc_bluetooth_t *bluetooth, size_t *value); + +/** + * Read data from the bluetooth connection. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[out] data The memory buffer to read the data into. + * @param[in] size The number of bytes to read. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_read (dc_bluetooth_t *bluetooth, void *data, size_t size, size_t *actual); + +/** + * Write data to the bluetooth connection. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] data The memory buffer to write the data from. + * @param[in] size The number of bytes to write. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_write (dc_bluetooth_t *bluetooth, const void *data, size_t size, size_t *actual); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_BLUETOOTH_H */ From 58eebf4ece2e89320927f98245b7125014b00db7 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 25 Jan 2014 10:03:52 +0100 Subject: [PATCH 06/35] Add a bluetooth transport type --- include/libdivecomputer/descriptor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h index 6f9735d..f1c815d 100644 --- a/include/libdivecomputer/descriptor.h +++ b/include/libdivecomputer/descriptor.h @@ -33,7 +33,8 @@ typedef enum dc_transport_t { DC_TRANSPORT_NONE, DC_TRANSPORT_SERIAL, DC_TRANSPORT_USB, - DC_TRANSPORT_IRDA + DC_TRANSPORT_IRDA, + DC_TRANSPORT_BLUETOOTH } dc_transport_t; typedef struct dc_descriptor_t dc_descriptor_t; From 37392a03f277b0299d7e3412bc4d782b5fdc867b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 25 Apr 2017 19:19:55 +0200 Subject: [PATCH 07/35] Add a separate transport type for USB HID --- include/libdivecomputer/descriptor.h | 1 + src/descriptor.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h index f1c815d..a78b600 100644 --- a/include/libdivecomputer/descriptor.h +++ b/include/libdivecomputer/descriptor.h @@ -33,6 +33,7 @@ typedef enum dc_transport_t { DC_TRANSPORT_NONE, DC_TRANSPORT_SERIAL, DC_TRANSPORT_USB, + DC_TRANSPORT_USBHID, DC_TRANSPORT_IRDA, DC_TRANSPORT_BLUETOOTH } dc_transport_t; diff --git a/src/descriptor.c b/src/descriptor.c index e019172..f0301bb 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -428,7 +428,7 @@ dc_descriptor_get_transport (dc_descriptor_t *descriptor) if (descriptor->type == DC_FAMILY_ATOMICS_COBALT) return DC_TRANSPORT_USB; else if (descriptor->type == DC_FAMILY_SUUNTO_EONSTEEL) - return DC_TRANSPORT_USB; + return DC_TRANSPORT_USBHID; else if (descriptor->type == DC_FAMILY_UWATEC_SMART) return DC_TRANSPORT_IRDA; else From 31c68ba3386f4f3c2d35f68faf2589464ec68211 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 13 Jun 2017 16:31:16 +0200 Subject: [PATCH 08/35] Automatically re-transmit corrupt data packets Originally packets are only retried when a valid NAK packet with the busy error code is received. The retrying is now enabled for other types of errors also, such as data packets with checksum errors. --- src/divesystem_idive.c | 124 ++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index e2a5cda..3c53b8e 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -291,72 +291,94 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe static dc_status_t -divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +divesystem_idive_packet (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int *errorcode) { - dc_status_t rc = DC_STATUS_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; unsigned char packet[MAXPACKET] = {0}; - unsigned int length = 0; - unsigned int nretries = 0; + unsigned int length = sizeof(packet); + unsigned int errcode = 0; - while (1) { - // Send the command. - rc = divesystem_idive_send (device, command, csize); - if (rc != DC_STATUS_SUCCESS) - return rc; + // Send the command. + status = divesystem_idive_send (device, command, csize); + if (status != DC_STATUS_SUCCESS) { + goto error; + } - // Receive the answer. - length = sizeof(packet); - rc = divesystem_idive_receive (device, packet, &length); - if (rc != DC_STATUS_SUCCESS) - return rc; + // Receive the answer. + status = divesystem_idive_receive (device, packet, &length); + if (status != DC_STATUS_SUCCESS) { + goto error; + } - // Verify the command byte. - if (packet[0] != command[0]) { - ERROR (abstract->context, "Unexpected packet header."); - return DC_STATUS_PROTOCOL; - } + // Verify the command byte. + if (packet[0] != command[0]) { + ERROR (abstract->context, "Unexpected packet header."); + status = DC_STATUS_PROTOCOL; + goto error; + } - // Check the ACK byte. - if (packet[length - 1] == ACK) - break; - - // Verify the NAK byte. - if (packet[length - 1] != NAK) { - ERROR (abstract->context, "Unexpected ACK/NAK byte."); - return DC_STATUS_PROTOCOL; - } - - // Verify the length of the packet. - if (length != 3) { - ERROR (abstract->context, "Unexpected packet length."); - return DC_STATUS_PROTOCOL; - } - - // Verify the error code. - unsigned int errcode = packet[1]; - if (errcode != BUSY) { - ERROR (abstract->context, "Received NAK packet with error code %02x.", errcode); - return DC_STATUS_PROTOCOL; - } - - // Abort if the maximum number of retries is reached. - if (nretries++ >= MAXRETRIES) - return DC_STATUS_PROTOCOL; - - // Delay the next attempt. - dc_serial_sleep(device->port, 100); + // Verify the ACK/NAK byte. + unsigned int type = packet[length - 1]; + if (type != ACK && type != NAK) { + ERROR (abstract->context, "Unexpected ACK/NAK byte."); + status = DC_STATUS_PROTOCOL; + goto error; } // Verify the length of the packet. - if (asize != length - 2) { + unsigned int expected = (type == ACK ? asize : 1) + 2; + if (length != expected) { ERROR (abstract->context, "Unexpected packet length."); - return DC_STATUS_PROTOCOL; + status = DC_STATUS_PROTOCOL; + goto error; + } + + // Get the error code from a NAK packet. + if (type == NAK) { + errcode = packet[1]; + ERROR (abstract->context, "Received NAK packet with error code %02x.", errcode); + status = DC_STATUS_PROTOCOL; + goto error; } memcpy(answer, packet + 1, length - 2); - return DC_STATUS_SUCCESS; +error: + if (errorcode) { + *errorcode = errcode; + } + + return status; +} + + +static dc_status_t +divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + unsigned int errcode = 0; + + unsigned int nretries = 0; + while ((status = divesystem_idive_packet (device, command, csize, answer, asize, &errcode)) != DC_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (status != DC_STATUS_PROTOCOL && status != DC_STATUS_TIMEOUT) + break; + + // Abort if the device reports a fatal error. + if (errcode && errcode != BUSY) + break; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + break; + + // Delay the next attempt. + dc_serial_sleep (device->port, 100); + } + + return status; } static dc_status_t From 35d1e6ff14308e85304a6952d1d50b35a14c027d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 3 Jun 2017 09:56:02 +0200 Subject: [PATCH 09/35] Propagate the error code to the caller --- src/divesystem_idive.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 3c53b8e..485c6d8 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -354,7 +354,7 @@ error: static dc_status_t -divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int *errorcode) { dc_status_t status = DC_STATUS_SUCCESS; unsigned int errcode = 0; @@ -378,6 +378,10 @@ divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned cha dc_serial_sleep (device->port, 100); } + if (errorcode) { + *errorcode = errcode; + } + return status; } @@ -387,6 +391,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb dc_status_t rc = DC_STATUS_SUCCESS; divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract; unsigned char packet[MAXPACKET - 2]; + unsigned int errcode = 0; const divesystem_idive_commands_t *commands = &idive; if (device->model >= IX3M_EASY) { @@ -398,7 +403,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); unsigned char cmd_id[] = {commands->id.cmd, 0xED}; - rc = divesystem_idive_transfer (device, cmd_id, sizeof(cmd_id), packet, commands->id.size); + rc = divesystem_idive_transfer (device, cmd_id, sizeof(cmd_id), packet, commands->id.size, &errcode); if (rc != DC_STATUS_SUCCESS) return rc; @@ -424,7 +429,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb } unsigned char cmd_range[] = {commands->range.cmd, 0x8D}; - rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size); + rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size, &errcode); if (rc != DC_STATUS_SUCCESS) return rc; @@ -453,7 +458,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb unsigned char cmd_header[] = {commands->header.cmd, (number ) & 0xFF, (number >> 8) & 0xFF}; - rc = divesystem_idive_transfer (device, cmd_header, sizeof(cmd_header), packet, commands->header.size); + rc = divesystem_idive_transfer (device, cmd_header, sizeof(cmd_header), packet, commands->header.size, &errcode); if (rc != DC_STATUS_SUCCESS) return rc; @@ -475,7 +480,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb unsigned char cmd_sample[] = {commands->sample.cmd, (idx ) & 0xFF, (idx >> 8) & 0xFF}; - rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size * commands->nsamples); + rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size * commands->nsamples, &errcode); if (rc != DC_STATUS_SUCCESS) return rc; From 5178c0f755f9606a7c472a58d5172c89bb748d6a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 3 Jun 2017 09:59:17 +0200 Subject: [PATCH 10/35] Add extra NAK error codes --- src/divesystem_idive.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 485c6d8..1da536d 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -42,7 +42,14 @@ #define START 0x55 #define ACK 0x06 #define NAK 0x15 -#define BUSY 0x60 + +#define ERR_INVALID_CMD 0x10 +#define ERR_INVALID_LENGTH 0x20 +#define ERR_INVALID_DATA 0x30 +#define ERR_UNSUPPORTED 0x40 +#define ERR_UNAVAILABLE 0x58 +#define ERR_UNREADABLE 0x5F +#define ERR_BUSY 0x60 #define NSTEPS 1000 #define STEP(i,n) (NSTEPS * (i) / (n)) @@ -367,7 +374,7 @@ divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned cha break; // Abort if the device reports a fatal error. - if (errcode && errcode != BUSY) + if (errcode && errcode != ERR_BUSY) break; // Abort if the maximum number of retries is reached. From 3b967f9eb1296193ef154b663e59e9970bccc5ed Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 3 Jun 2017 10:01:45 +0200 Subject: [PATCH 11/35] Improve the handling of devices without any dives Don't return an error if the dive computer reports there are no dives available! --- src/divesystem_idive.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 1da536d..f0c31dc 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -437,8 +437,13 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb unsigned char cmd_range[] = {commands->range.cmd, 0x8D}; rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size, &errcode); - if (rc != DC_STATUS_SUCCESS) - return rc; + if (rc != DC_STATUS_SUCCESS) { + if (errcode == ERR_UNAVAILABLE) { + return DC_STATUS_SUCCESS; // No dives found. + } else { + return rc; + } + } // Get the range of the available dive numbers. unsigned int first = array_uint16_le (packet + 0); From 24621ed5196c1b74206a41929c12be21c5ec7964 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 3 Jun 2017 10:07:14 +0200 Subject: [PATCH 12/35] Improve the handling of unreadable dives Dives that are reported by the dive computer as unreadable (for example due to a power loss during the dive) are now skipped instead of being reported as a fatal error. Those dives can't be retrieved, so there is no good reason to abort the download. --- src/divesystem_idive.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index f0c31dc..d26e2b4 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -471,8 +471,14 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb (number ) & 0xFF, (number >> 8) & 0xFF}; rc = divesystem_idive_transfer (device, cmd_header, sizeof(cmd_header), packet, commands->header.size, &errcode); - if (rc != DC_STATUS_SUCCESS) - return rc; + if (rc != DC_STATUS_SUCCESS) { + if (errcode == ERR_UNREADABLE) { + WARNING(abstract->context, "Skipped unreadable dive!"); + continue; + } else { + return rc; + } + } if (memcmp(packet + 7, device->fingerprint, sizeof(device->fingerprint)) == 0) break; From f107d7c0d8242e3f07b0b8ed5c9c041cc733c8f8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 8 Jun 2017 22:16:14 -0700 Subject: [PATCH 13/35] Add initial Scubapro G2 frontend The back-end parser seems to be the same as for the Uwatec Smart (aka Galileo Sol). At least that's the assumption right now. The downloader just uses USB HID (very similar to EON Steel) rather than the horrible IrDA thing. There's also eventually a BLE thing, but that's for the future. This is an unholy mixture of the Uwatec Smart downloader logic and the EON Steel usbhid transfer code. The back-end is pure Uwatec Smart (model 0x11, same as Galileo Sol). I'm not at all sure this gets everything right, but it downloads *something*. [Jef Driesen: Renamed the backend to uwatec, and made some smaller cosmetic changes to match the existing coding style.] Signed-off-by: Linus Torvalds --- examples/common.c | 1 + include/libdivecomputer/common.h | 1 + src/Makefile.am | 1 + src/descriptor.c | 6 + src/device.c | 4 + src/parser.c | 3 + src/uwatec_g2.c | 380 +++++++++++++++++++++++++++++++ src/uwatec_g2.h | 39 ++++ 8 files changed, 435 insertions(+) create mode 100644 src/uwatec_g2.c create mode 100644 src/uwatec_g2.h diff --git a/examples/common.c b/examples/common.c index 3c4561b..1024fde 100644 --- a/examples/common.c +++ b/examples/common.c @@ -55,6 +55,7 @@ static const backend_table_t g_backends[] = { {"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}, diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 67398d1..293de3b 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -59,6 +59,7 @@ typedef enum dc_family_t { DC_FAMILY_UWATEC_MEMOMOUSE, DC_FAMILY_UWATEC_SMART, DC_FAMILY_UWATEC_MERIDIAN, + DC_FAMILY_UWATEC_G2, /* Oceanic */ DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VEO250, diff --git a/src/Makefile.am b/src/Makefile.am index 60b5654..387b284 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,7 @@ libdivecomputer_la_SOURCES = \ 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 \ diff --git a/src/descriptor.c b/src/descriptor.c index f0301bb..de4512e 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -135,6 +135,10 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, {"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24}, {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, + /* Scubapro G2 */ +#ifdef USBHID + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x11}, +#endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, @@ -429,6 +433,8 @@ dc_descriptor_get_transport (dc_descriptor_t *descriptor) return DC_TRANSPORT_USB; else if (descriptor->type == DC_FAMILY_SUUNTO_EONSTEEL) return DC_TRANSPORT_USBHID; + else if (descriptor->type == DC_FAMILY_UWATEC_G2) + return DC_TRANSPORT_USBHID; else if (descriptor->type == DC_FAMILY_UWATEC_SMART) return DC_TRANSPORT_IRDA; else diff --git a/src/device.c b/src/device.c index 2f3e1af..9dcc528 100644 --- a/src/device.c +++ b/src/device.c @@ -33,6 +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" @@ -138,6 +139,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_UWATEC_MERIDIAN: rc = uwatec_meridian_device_open (&device, context, name); break; + case DC_FAMILY_UWATEC_G2: + rc = uwatec_g2_device_open (&device, context); + break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_device_open (&device, context, name); break; diff --git a/src/parser.c b/src/parser.c index 9fdb1a9..47382e5 100644 --- a/src/parser.c +++ b/src/parser.c @@ -100,6 +100,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_UWATEC_MERIDIAN: rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); break; + case DC_FAMILY_UWATEC_G2: + rc = uwatec_smart_parser_create (&parser, context, 0x11, devtime, systime); + break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_parser_create (&parser, context, devtime, systime); break; diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c new file mode 100644 index 0000000..b946eca --- /dev/null +++ b/src/uwatec_g2.c @@ -0,0 +1,380 @@ +/* + * 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 "usbhid.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_g2_device_vtable) + +#define PACKET_SIZE 64 + +typedef struct uwatec_g2_device_t { + dc_device_t base; + dc_usbhid_t *usbhid; + unsigned int address; + 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 dc_status_t uwatec_g2_device_close (dc_device_t *abstract); + +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 */ + uwatec_g2_device_close /* 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 int +receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size) +{ + while (size) { + unsigned char buf[PACKET_SIZE]; + size_t transferred = 0; + dc_status_t rc = DC_STATUS_SUCCESS; + unsigned int len = 0; + + rc = dc_usbhid_read (device->usbhid, buf, PACKET_SIZE, &transferred); + if (rc != DC_STATUS_SUCCESS) { + ERROR (device->base.context, "read interrupt transfer failed"); + return -1; + } + if (transferred != PACKET_SIZE) { + ERROR (device->base.context, "incomplete read interrupt transfer (got %zu, expected %d)", transferred, PACKET_SIZE); + return -1; + } + len = buf[0]; + if (len >= PACKET_SIZE) { + ERROR (device->base.context, "read interrupt transfer returns impossible packet size (%d)", len); + return -1; + } + HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); + if (len > size) { + ERROR (device->base.context, "receive result buffer too small - truncating"); + len = size; + } + memcpy(data, buf + 1, len); + size -= len; + data += len; + } + return 0; +} + + +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[PACKET_SIZE]; + dc_status_t status = DC_STATUS_SUCCESS; + size_t transferred = 0; + + if (csize >= PACKET_SIZE) { + ERROR (device->base.context, "command too big (%d)", csize); + return DC_STATUS_INVALIDARGS; + } + + buf[0] = csize; + memcpy(buf + 1, command, csize); + status = dc_usbhid_write (device->usbhid, buf, csize + 1, &transferred); + if (status != DC_STATUS_SUCCESS) { + ERROR (device->base.context, "Failed to send the command."); + return status; + } + + if (receive_data (device, answer, asize) < 0) { + ERROR (device->base.context, "Failed to receive the answer."); + return DC_STATUS_IO; + } + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) +{ + 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->usbhid = NULL; + device->address = 0; + device->timestamp = 0; + device->systime = (dc_ticks_t) -1; + device->devtime = 0; + + // Open the irda socket. + status = dc_usbhid_open (&device->usbhid, context, 0x2e6c, 0x3201); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to open USB 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_close (dc_device_t *abstract) +{ + dc_status_t status = DC_STATUS_SUCCESS; + uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + + // Close the device. + rc = dc_usbhid_close (device->usbhid); + if (status != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } + + 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; + + // Erase the current contents of the buffer. + if (!dc_buffer_clear (buffer)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // 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; + } + + if (receive_data (device, data, length)) { + ERROR (abstract->context, "Failed to receive the answer."); + return DC_STATUS_IO; + } + + // Update and emit a progress event. + progress.current += length; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + 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_g2.h b/src/uwatec_g2.h new file mode 100644 index 0000000..c7ac7ca --- /dev/null +++ b/src/uwatec_g2.h @@ -0,0 +1,39 @@ +/* + * libdivecomputer + * + * Copyright (C) 2008 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_G2_H +#define UWATEC_G2_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +uwatec_g2_device_open (dc_device_t **device, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* UWATEC_G2_H */ From 4ef57b24bb2046b1a848d284ea0b98ceff3e5896 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 06:27:21 +0900 Subject: [PATCH 14/35] Scubapro G2: clean up unused field Jef Driesen correctly points out that the 'address' field is just leftover from the IrDA code, and is meaningless for the USB HID transport version of the Scubapro G2. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index b946eca..864ec32 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -36,7 +36,6 @@ typedef struct uwatec_g2_device_t { dc_device_t base; dc_usbhid_t *usbhid; - unsigned int address; unsigned int timestamp; unsigned int devtime; dc_ticks_t systime; @@ -144,7 +143,6 @@ uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) // Set the default values. device->usbhid = NULL; - device->address = 0; device->timestamp = 0; device->systime = (dc_ticks_t) -1; device->devtime = 0; From 8c0ab03706d2f30f3f750502b6f17c9347c46075 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 06:36:57 +0900 Subject: [PATCH 15/35] Scubapro G2: add back the initial handshake code When doing the G2 downloader, I dropped the initial handshake as I tried to keep the code minimal, and the handshake didn't seem to make any difference what-so-ever to me. And it probably doesn't matter for anybody else either. But the code isn't working for some people, and maybe it does actually matter. More importantly, Scubapro's own LogTRAK application does send those two initial commands, and it's probably a good idea to minimize the differences between the different downloaders anyway, so add the handshake sequence back in. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 864ec32..7fb8e0d 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -125,6 +125,43 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u } +static dc_status_t +uwatec_g2_handshake (uwatec_g2_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_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) { @@ -154,10 +191,19 @@ uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) goto error_free; } + // Perform the handshaking. + status = uwatec_g2_handshake (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to handshake with the device."); + goto error_close; + } + *out = (dc_device_t*) device; return DC_STATUS_SUCCESS; +error_close: + dc_usbhid_close (device->usbhid); error_free: dc_device_deallocate ((dc_device_t *) device); return status; From fcfee82cc875126db069fe2644cea08e42b921d0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 07:01:51 +0900 Subject: [PATCH 16/35] Scubapro G2: add missed command packet logging I did the packet logging for the received data side, but forgot to do the same thing on the command transfer side, which makes the debug logs a bit less useful. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 7fb8e0d..a1a2932 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -108,6 +108,8 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return DC_STATUS_INVALIDARGS; } + HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", command, csize); + buf[0] = csize; memcpy(buf + 1, command, csize); status = dc_usbhid_write (device->usbhid, buf, csize + 1, &transferred); From eae071c97d1efb9971a6d77fba52ca6bdbcd1a4b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:02:01 +0200 Subject: [PATCH 17/35] Use the standard libdivecomputer error codes The error code returned by the dc_usbhid_read() function should be returned as-is, instead of being replaced with some generic error, which gets translated again to DC_STATUS_IO in the caller. --- src/uwatec_g2.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index a1a2932..2fa86c8 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -60,7 +60,7 @@ static const dc_device_vtable_t uwatec_g2_device_vtable = { 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 int +static dc_status_t receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size) { while (size) { @@ -72,16 +72,16 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size rc = dc_usbhid_read (device->usbhid, buf, PACKET_SIZE, &transferred); if (rc != DC_STATUS_SUCCESS) { ERROR (device->base.context, "read interrupt transfer failed"); - return -1; + return rc; } if (transferred != PACKET_SIZE) { ERROR (device->base.context, "incomplete read interrupt transfer (got %zu, expected %d)", transferred, PACKET_SIZE); - return -1; + return DC_STATUS_PROTOCOL; } len = buf[0]; if (len >= PACKET_SIZE) { ERROR (device->base.context, "read interrupt transfer returns impossible packet size (%d)", len); - return -1; + return DC_STATUS_PROTOCOL; } HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); if (len > size) { @@ -92,7 +92,7 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size size -= len; data += len; } - return 0; + return DC_STATUS_SUCCESS; } @@ -118,9 +118,10 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return status; } - if (receive_data (device, answer, asize) < 0) { + status = receive_data (device, answer, asize); + if (status != DC_STATUS_SUCCESS) { ERROR (device->base.context, "Failed to receive the answer."); - return DC_STATUS_IO; + return status; } return DC_STATUS_SUCCESS; @@ -357,9 +358,10 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_PROTOCOL; } - if (receive_data (device, data, length)) { + rc = receive_data (device, data, length); + if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); - return DC_STATUS_IO; + return rc; } // Update and emit a progress event. From 06259fed1937ac3663922c5e1eebfa118933293c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:25:36 +0200 Subject: [PATCH 18/35] Abort with an error if the buffer is too small Silently truncating the data packet if the buffer is too small will result in a corrupt data stream. --- src/uwatec_g2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 2fa86c8..388b82c 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -85,8 +85,8 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size } HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); if (len > size) { - ERROR (device->base.context, "receive result buffer too small - truncating"); - len = size; + ERROR (device->base.context, "receive result buffer too small"); + return DC_STATUS_PROTOCOL; } memcpy(data, buf + 1, len); size -= len; From 7ee210f83f734c0d7dbac0df5a4ff91a5dcdba3e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:36:47 +0200 Subject: [PATCH 19/35] Enable more fine grained progress events At the moment, the progress reporting will jump straight from about 0% at the start of the download to 100% at the end of the download, without any updates in between. This is improved by updating after every packet. --- src/uwatec_g2.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 388b82c..e3cc527 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -61,7 +61,7 @@ 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, unsigned char *data, unsigned int size) +receive_data (uwatec_g2_device_t *device, dc_event_progress_t *progress, unsigned char *data, unsigned int size) { while (size) { unsigned char buf[PACKET_SIZE]; @@ -88,6 +88,13 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size ERROR (device->base.context, "receive result buffer too small"); 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; @@ -118,7 +125,7 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return status; } - status = receive_data (device, answer, asize); + status = receive_data (device, NULL, answer, asize); if (status != DC_STATUS_SUCCESS) { ERROR (device->base.context, "Failed to receive the answer."); return status; @@ -358,16 +365,12 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_PROTOCOL; } - rc = receive_data (device, data, length); + rc = receive_data (device, &progress, 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 += length; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - return DC_STATUS_SUCCESS; } From 0a5623a949eef1c12fb1ce39e4941b31f0c29eb1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Jun 2017 06:55:14 +0200 Subject: [PATCH 20/35] Use the correct model number The model number reported by the Scubapro G2 is 0x32. --- src/descriptor.c | 2 +- src/parser.c | 4 +--- src/uwatec_smart_parser.c | 8 ++++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index de4512e..9b537e1 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -137,7 +137,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, /* Scubapro G2 */ #ifdef USBHID - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x11}, + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, #endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, diff --git a/src/parser.c b/src/parser.c index 47382e5..29a5d41 100644 --- a/src/parser.c +++ b/src/parser.c @@ -98,10 +98,8 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa break; case DC_FAMILY_UWATEC_SMART: case DC_FAMILY_UWATEC_MERIDIAN: - rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); - break; case DC_FAMILY_UWATEC_G2: - rc = uwatec_smart_parser_create (&parser, context, 0x11, devtime, systime); + rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_parser_create (&parser, context, devtime, systime); diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index b350f1e..950757d 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -47,6 +47,7 @@ #define MERIDIAN 0x20 #define CHROMIS 0x24 #define MANTIS2 0x26 +#define G2 0x32 #define UNSUPPORTED 0xFFFFFFFF @@ -500,7 +501,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) divemode != DC_DIVEMODE_FREEDIVE) { if (parser->model == GALILEO || parser->model == GALILEOTRIMIX || parser->model == ALADIN2G || parser->model == MERIDIAN || - parser->model == CHROMIS || parser->model == MANTIS2) { + parser->model == CHROMIS || parser->model == MANTIS2 || + parser->model == G2) { unsigned int offset = header->tankpressure + 2 * i; endpressure = array_uint16_le(data + offset); beginpressure = array_uint16_le(data + offset + 2 * header->ngases); @@ -578,6 +580,7 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i case MERIDIAN: case CHROMIS: case MANTIS2: + case G2: parser->headersize = 152; parser->header = &uwatec_smart_galileo_header; parser->samples = uwatec_smart_galileo_samples; @@ -928,7 +931,8 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int id = 0; if (parser->model == GALILEO || parser->model == GALILEOTRIMIX || parser->model == ALADIN2G || parser->model == MERIDIAN || - parser->model == CHROMIS || parser->model == MANTIS2) { + parser->model == CHROMIS || parser->model == MANTIS2 || + parser->model == G2) { // Uwatec Galileo id = uwatec_galileo_identify (data[offset]); } else { From 3a05e0ecf022a35ff62f055e1775a80c272c7d81 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Jun 2017 06:56:48 +0200 Subject: [PATCH 21/35] Use the trimix data format --- src/uwatec_smart_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 950757d..99bfa28 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -440,6 +440,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) parser->events[2] = uwatec_smart_galileo_events_2; parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2); } + } else if (parser->model == G2) { + trimix = 1; } // Get the settings. From 7ce057d2babed0cc8ccbb810afd2d33b0c191ec2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 4 Jul 2017 14:58:52 +0200 Subject: [PATCH 22/35] Fix the progress events I accidentally passed the address of the progress pointer to the device_event_emit() function, instead of the pointer itself. Oops. --- src/uwatec_g2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index e3cc527..3328b4c 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -92,7 +92,7 @@ receive_data (uwatec_g2_device_t *device, dc_event_progress_t *progress, unsigne // Update and emit a progress event. if (progress) { progress->current += len; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + device_event_emit (&device->base, DC_EVENT_PROGRESS, progress); } memcpy(data, buf + 1, len); From c26755624daf363a65ea6ce234a3160094da8df2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 22 Jun 2017 21:17:46 +0200 Subject: [PATCH 23/35] Fix the id string offset Since commit 344bfab229a17c7227b9bec02f616505a8d9e998 only a subset of the id string is used to detect the model. But because the offset was never updated, the model detection always fails now. --- src/cochran_commander.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index d2c47b0..83dd14a 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -207,7 +207,7 @@ cochran_commander_get_model (cochran_commander_device_t *device) unsigned int model = 0xFFFFFFFF; for (unsigned int i = 0; i < C_ARRAY_SIZE(models); ++i) { - if (memcmp (device->id + 0x3B, models[i].id, sizeof(models[i].id) - 1) == 0) { + if (memcmp (device->id + 0x3D, models[i].id, sizeof(models[i].id) - 1) == 0) { model = models[i].model; break; } From 346391ae2c6e0c42d988e7950b8dd3c0b589b50c Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 24/35] Change profile download to be incremental It will result in a 30 minute download for full computers but it significantly reduces the time to download partial dives. --- src/cochran_commander.c | 360 ++++++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 157 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 83dd14a..e0c991f 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -28,6 +28,7 @@ #include "device-private.h" #include "serial.h" #include "array.h" +#include "ringbuffer.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) @@ -41,6 +42,11 @@ typedef enum cochran_endian_t { ENDIAN_BE, } cochran_endian_t; +typedef enum cochran_profile_size_t { + PROFILE_SIZE_FULL, + PROFILE_SIZE_READ, +} cochran_profile_size_t; + typedef struct cochran_commander_model_t { unsigned char id[2 + 1]; unsigned int model; @@ -49,10 +55,10 @@ typedef struct cochran_commander_model_t { typedef struct cochran_data_t { unsigned char config[1024]; unsigned char *logbook; - unsigned char *sample; unsigned short int dive_count; int fp_dive_num; + int invalid_profile_dive_num; unsigned int logbook_size; @@ -430,23 +436,120 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t } -static void +/* + * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF + * search for a reasonable size, e.g. using next dive start sample + * or end-of-samples to limit searching for recoverable samples + */ +static unsigned int +cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) +{ + const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; + + if (log_num == data->dive_count) + // Return next usable address from config page + return array_uint32_le(data->config + device->layout->rb_profile_end); + + // Next log's start address + return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); +} + + +static unsigned int +cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type) +{ + + const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size; + unsigned int sample_start_address = 0xFFFFFFFF; + unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); + + if (type == PROFILE_SIZE_FULL) { + // actual size, includes pre-dive events + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre); + } else if (type == PROFILE_SIZE_READ) { + // read size, only include dive profile + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); + } + + // Validate addresses + if (sample_start_address < device->layout->rb_profile_begin || + sample_start_address > device->layout->rb_profile_end || + sample_end_address < device->layout->rb_profile_begin || + (sample_end_address > device->layout->rb_profile_end && + sample_end_address != 0xFFFFFFFF)) { + return 0; + } + + if (sample_end_address == 0xFFFFFFFF) + // Corrupt dive, guess the end address + sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num); + + return ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end); +} + + +/* + * Do several things. Find the log that matches the fingerprint, + * calculate the total read size for progress indicator, + * Determine the most recent dive without profile data. + */ + +static unsigned int cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data) { - // Skip to fingerprint to reduce time - if (data->dive_count < device->layout->rb_logbook_entry_count) - data->fp_dive_num = data->dive_count; - else - data->fp_dive_num = device->layout->rb_logbook_entry_count; - data->fp_dive_num--; + // We track profile ringbuffer usage to determine which dives have profile data + int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; - while (data->fp_dive_num >= 0 && memcmp(device->fingerprint, - data->logbook + data->fp_dive_num * device->layout->rb_logbook_entry_size, - sizeof(device->fingerprint))) - data->fp_dive_num--; + int dive_count = -1; + + // Start at end of log + if (data->dive_count < device->layout->rb_logbook_entry_count) + dive_count = data->dive_count; + else + dive_count = device->layout->rb_logbook_entry_count; + dive_count--; + + unsigned int sample_read_size = 0; + data->invalid_profile_dive_num = -1; + + // Remove the pre-dive events that occur after the last dive + unsigned int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); + if (rb_head_ptr > last_dive_end_address) + profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + + // Loop through dives to find FP, Accumulate profile data size, + // and find the last dive with invalid profile + for (int i = dive_count; i > 0; i--) { + unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size; + + // We're done if we find the fingerprint + if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { + data->fp_dive_num = i; + break; + } + + unsigned int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_FULL); + unsigned int read_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_READ); + + // Determine if sample exists + if (profile_capacity_remaining > 0) { + // Subtract this dive's profile size including post-dive events + profile_capacity_remaining -= sample_size; + if (profile_capacity_remaining < 0) { + // Save the last dive that is missing profile data + data->invalid_profile_dive_num = i; + } + // Accumulate read size for progress bar + sample_read_size += read_size; + } + } + + return sample_read_size; } + static void cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data) { @@ -509,103 +612,6 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d } -/* - * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF - * search for a reasonable size, e.g. using next dive start sample - * or end-of-samples to limit searching for recoverable samples - */ -static unsigned int -cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) -{ - const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; - - if (log_num == data->dive_count) - // Return next usable address from config page - return array_uint32_le(data->config + device->layout->rb_profile_end); - - // Next log's start address - return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); -} - - -static dc_status_t -cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *data) -{ - dc_device_t *abstract = (dc_device_t *) device; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Calculate max data sizes - unsigned int max_config = sizeof(data->config); - unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; - unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; - - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = max_config + max_logbook + max_sample; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Emit ID block - dc_event_vendor_t vendor; - vendor.data = device->id; - vendor.size = sizeof (device->id); - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - - // Read config - rc = cochran_commander_read_config(device, &progress, data->config, sizeof(data->config)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine size of dive list to read. - if (device->layout->endian == ENDIAN_LE) - data->dive_count = array_uint16_le (data->config + device->layout->cf_dive_count); - else - data->dive_count = array_uint16_be (data->config + device->layout->cf_dive_count); - - if (data->dive_count > device->layout->rb_logbook_entry_count) { - data->logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; - } else { - data->logbook_size = data->dive_count * device->layout->rb_logbook_entry_size; - } - - progress.maximum -= max_logbook - data->logbook_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Allocate space for log book. - data->logbook = (unsigned char *) malloc(data->logbook_size); - if (data->logbook == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Request log book - rc = cochran_commander_read(device, &progress, 0, data->logbook, data->logbook_size); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine sample memory to read - cochran_commander_find_fingerprint(device, data); - cochran_commander_get_sample_parms(device, data); - - progress.maximum -= max_sample - data->sample_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - if (data->sample_size > 0) { - data->sample = (unsigned char *) malloc(data->sample_size); - if (data->sample == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Read the sample data - rc = cochran_commander_read (device, &progress, data->sample_data_offset, data->sample, data->sample_size); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the sample data."); - return rc; - } - } - - return DC_STATUS_SUCCESS; -} - dc_status_t cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name) { @@ -771,10 +777,66 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call cochran_data_t data; data.logbook = NULL; - data.sample = NULL; - status = cochran_commander_read_all (device, &data); - if (status != DC_STATUS_SUCCESS) + + // Calculate max data sizes + unsigned int max_config = sizeof(data.config); + unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; + unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; + + // setup progress indication + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = max_config + max_logbook + max_sample; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit ID block + dc_event_vendor_t vendor; + vendor.data = device->id; + vendor.size = sizeof (device->id); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Read config + dc_status_t rc = DC_STATUS_SUCCESS; + rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Determine size of dive list to read. + if (device->layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count); + else + data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + + if (data.dive_count > device->layout->rb_logbook_entry_count) { + data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; + } else { + data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + } + + // Update progress indicator with new maximum + progress.maximum -= max_logbook - data.logbook_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Allocate space for log book. + data.logbook = (unsigned char *) malloc(data.logbook_size); + if (data.logbook == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Request log book + rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size); + if (rc != DC_STATUS_SUCCESS) { + status = rc; goto error; + } + + // Locate fingerprint, recent dive with invalid profile and calc read size + unsigned int profile_read_size = cochran_commander_find_fingerprint(device, &data); + // Update progress indicator with new maximum + progress.maximum -= (max_sample - profile_read_size); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + cochran_commander_get_sample_parms(device, &data); // Emit a device info event. dc_event_devinfo_t devinfo; @@ -792,9 +854,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - // We track profile ringbuffer usage to determine which dives have profile data - int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; - unsigned int dive_count = 0; if (data.dive_count < device->layout->rb_logbook_entry_count) dive_count = data.dive_count; @@ -808,44 +867,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); - // Validate - if (sample_start_address < device->layout->rb_profile_begin || - sample_start_address > device->layout->rb_profile_end || - sample_end_address < device->layout->rb_profile_begin || - (sample_end_address > device->layout->rb_profile_end && - sample_end_address != 0xFFFFFFFF)) { - continue; - } - - if (sample_end_address == 0xFFFFFFFF) - // Corrupt dive, guess the end address - sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); - - // Determine if sample exists - if (profile_capacity_remaining > 0) { - // Subtract this dive's profile size including post-dive events - profile_capacity_remaining -= (last_start_address - sample_start_address); - // Adjust for a dive that wraps the buffer - if (sample_start_address > last_start_address) - profile_capacity_remaining -= device->layout->rb_profile_end - device->layout->rb_profile_begin; - } - last_start_address = sample_start_address; - - unsigned char *sample = NULL; int sample_size = 0; - if (profile_capacity_remaining < 0) { - // There is no profile for this dive - sample = NULL; - sample_size = 0; - } else { - // Calculate the size of the profile only - sample = data.sample + sample_start_address - data.sample_data_offset; - sample_size = sample_end_address - sample_start_address; - if (sample_size < 0) - // Adjust for ring buffer wrap-around - sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin; - } + // Determine if profile exists + if (i > data.invalid_profile_dive_num) + sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ); // Build dive blob unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; @@ -857,17 +883,38 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log - // Copy profile data + // Read profile data if (sample_size) { + if (sample_end_address == 0xFFFFFFFF) + // Corrupt dive, guess the end address + sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); + if (sample_start_address <= sample_end_address) { - memcpy(dive + device->layout->rb_logbook_entry_size, sample, sample_size); + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } } else { // It wrapped the buffer, copy two sections unsigned int size = device->layout->rb_profile_end - sample_start_address; - memcpy(dive + device->layout->rb_logbook_entry_size, sample, size); - memcpy(dive + device->layout->rb_logbook_entry_size + size, - data.sample, sample_end_address - device->layout->rb_profile_begin); + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } + rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } } } @@ -881,6 +928,5 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call error: free(data.logbook); - free(data.sample); return status; } From b3d2c603ddec9758fb36706bbde46ce23ca9f0ed Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 25/35] Retry read operations on failure [Jef Driesen: Modified to retry only for non-fatal errors.] --- src/cochran_commander.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index e0c991f..2854c78 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -32,6 +32,8 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) +#define MAXRETRIES 2 + #define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 #define COCHRAN_MODEL_EMC_14 1 #define COCHRAN_MODEL_EMC_16 2 @@ -435,6 +437,31 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t return DC_STATUS_SUCCESS; } +static unsigned int +cochran_commander_read_retry (cochran_commander_device_t *device, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size) +{ + // Save the state of the progress events. + unsigned int saved = progress->current; + + unsigned int nretries = 0; + dc_status_t rc = DC_STATUS_SUCCESS; + while ((rc = cochran_commander_read (device, progress, address, data, size)) != DC_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) + return rc; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return rc; + + // Restore the state of the progress events. + progress->current = saved; + } + + return rc; +} + /* * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF @@ -890,7 +917,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); if (sample_start_address <= sample_end_address) { - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -901,14 +928,15 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // It wrapped the buffer, copy two sections unsigned int size = device->layout->rb_profile_end - sample_start_address; - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; free(dive); goto error; } - rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + + rc = cochran_commander_read_retry (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; From 45f06056789a1986c26f8af38e1bc107eaa53c95 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 26/35] Fix problems with wrapped logbook ringbuffer --- src/cochran_commander.c | 69 +++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 2854c78..b09e9fd 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -528,6 +528,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; int dive_count = -1; + data->fp_dive_num = -1; // Start at end of log if (data->dive_count < device->layout->rb_logbook_entry_count) @@ -545,19 +546,32 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d if (rb_head_ptr > last_dive_end_address) profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + unsigned int head_dive = 0, tail_dive = 0; + + if (data->dive_count <= device->layout->rb_logbook_entry_count) { + head_dive = data->dive_count; + tail_dive = 0; + } else { + // Log wrapped + tail_dive = data->dive_count % device->layout->rb_logbook_entry_count; + head_dive = tail_dive; + } + // Loop through dives to find FP, Accumulate profile data size, // and find the last dive with invalid profile - for (int i = dive_count; i > 0; i--) { - unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size; + for (unsigned int i = 0; i <= dive_count; ++i) { + unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + + unsigned char *log_entry = data->logbook + idx * device->layout->rb_logbook_entry_size; // We're done if we find the fingerprint if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { - data->fp_dive_num = i; + data->fp_dive_num = idx; break; } - unsigned int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_FULL); - unsigned int read_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_READ); + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_FULL); + unsigned int read_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_READ); // Determine if sample exists if (profile_capacity_remaining > 0) { @@ -565,7 +579,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d profile_capacity_remaining -= sample_size; if (profile_capacity_remaining < 0) { // Save the last dive that is missing profile data - data->invalid_profile_dive_num = i; + data->invalid_profile_dive_num = idx; } // Accumulate read size for progress bar sample_read_size += read_size; @@ -833,6 +847,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call else data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + if (data.dive_count == 0) + // No dives to read + return DC_STATUS_SUCCESS; + if (data.dive_count > device->layout->rb_logbook_entry_count) { data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; } else { @@ -881,15 +899,31 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - unsigned int dive_count = 0; - if (data.dive_count < device->layout->rb_logbook_entry_count) - dive_count = data.dive_count; - else - dive_count = device->layout->rb_logbook_entry_count; + unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; + + if (data.dive_count <= device->layout->rb_logbook_entry_count) { + head_dive = data.dive_count; + tail_dive = 0; + } else { + // Log wrapped + tail_dive = data.dive_count % device->layout->rb_logbook_entry_count; + head_dive = tail_dive; + } + + // Change tail to dive following the fingerprint dive. + if (data.fp_dive_num > -1) + tail_dive = (data.fp_dive_num + 1) % device->layout->rb_logbook_entry_count; + + // Number of dives to read + dive_count = (device->layout->rb_logbook_entry_count + head_dive - tail_dive) % device->layout->rb_logbook_entry_count; + + int invalid_profile_flag = 0; // Loop through each dive - for (int i = dive_count - 1; i > data.fp_dive_num; i--) { - unsigned char *log_entry = data.logbook + i * device->layout->rb_logbook_entry_size; + for (unsigned int i = 0; i < dive_count; ++i) { + unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + + unsigned char *log_entry = data.logbook + idx * device->layout->rb_logbook_entry_size; unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); @@ -897,8 +931,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call int sample_size = 0; // Determine if profile exists - if (i > data.invalid_profile_dive_num) - sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ); + if (idx == data.invalid_profile_dive_num) + invalid_profile_flag = 1; + + if (!invalid_profile_flag) + sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); // Build dive blob unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; @@ -914,7 +951,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call if (sample_size) { if (sample_end_address == 0xFFFFFFFF) // Corrupt dive, guess the end address - sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); + sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx); if (sample_start_address <= sample_end_address) { rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); From 216e393f434def2361a57178c8c4487999f32095 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 2 Jun 2017 19:51:28 -0400 Subject: [PATCH 27/35] Add support for Pre-21000 s/n Commander dive computers This adds support for older Cochran Commander dive computers, specifically Commanders with serial numbers prior to 21000. This also renames "Commander" model to "Commander II" and adds "Commander I" to refer to pre-21000 models. --- src/array.c | 7 +++ src/array.h | 3 ++ src/cochran_commander.c | 92 +++++++++++++++++++++++++------- src/cochran_commander_parser.c | 96 ++++++++++++++++++++++++++++------ src/descriptor.c | 10 ++-- 5 files changed, 170 insertions(+), 38 deletions(-) diff --git a/src/array.c b/src/array.c index 13a73e5..5574083 100644 --- a/src/array.c +++ b/src/array.c @@ -198,6 +198,13 @@ array_uint32_le (const unsigned char data[]) } +unsigned int +array_uint32_word_be (const unsigned char data[]) +{ + return data[1] + (data[0] << 8) + (data[3] << 16) + (data[2] << 24); +} + + void array_uint32_le_set (unsigned char data[], const unsigned int input) { diff --git a/src/array.h b/src/array.h index cd0a3a1..da70efa 100644 --- a/src/array.h +++ b/src/array.h @@ -64,6 +64,9 @@ array_uint32_be (const unsigned char data[]); unsigned int array_uint32_le (const unsigned char data[]); +unsigned int +array_uint32_word_be (const unsigned char data[]); + void array_uint32_le_set (unsigned char data[], const unsigned int input); diff --git a/src/cochran_commander.c b/src/cochran_commander.c index b09e9fd..95c00fe 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -34,14 +34,16 @@ #define MAXRETRIES 2 -#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 -#define COCHRAN_MODEL_EMC_14 1 -#define COCHRAN_MODEL_EMC_16 2 -#define COCHRAN_MODEL_EMC_20 3 +#define COCHRAN_MODEL_COMMANDER_PRE21000 0 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1 +#define COCHRAN_MODEL_EMC_14 2 +#define COCHRAN_MODEL_EMC_16 3 +#define COCHRAN_MODEL_EMC_20 4 typedef enum cochran_endian_t { ENDIAN_LE, ENDIAN_BE, + ENDIAN_WORD_BE, } cochran_endian_t; typedef enum cochran_profile_size_t { @@ -50,7 +52,7 @@ typedef enum cochran_profile_size_t { } cochran_profile_size_t; typedef struct cochran_commander_model_t { - unsigned char id[2 + 1]; + unsigned char id[3 + 1]; unsigned int model; } cochran_commander_model_t; @@ -86,7 +88,9 @@ typedef struct cochran_device_layout_t { // Profile ringbuffer. unsigned int rb_profile_begin; unsigned int rb_profile_end; - // Profile pointers. + // pointers. + unsigned int pt_fingerprint; + unsigned int fingerprint_size; unsigned int pt_profile_pre; unsigned int pt_profile_begin; unsigned int pt_profile_end; @@ -117,6 +121,30 @@ static const dc_device_vtable_t cochran_commander_device_vtable = { cochran_commander_device_close /* close */ }; +// Cochran Commander pre-21000 s/n +static const cochran_device_layout_t cochran_cmdr_1_device_layout = { + COCHRAN_MODEL_COMMANDER_PRE21000, // model + 24, // address_bits + ENDIAN_WORD_BE, // endian + 115200, // baudrate + 0x046, // cf_dive_count + 0x6c, // cf_last_log + 0x70, // cf_last_interdive + 0x0AA, // cf_serial_number + 0x00000000, // rb_logbook_begin + 0x00020000, // rb_logbook_end + 256, // rb_logbook_entry_size + 512, // rb_logbook_entry_count + 0x00020000, // rb_profile_begin + 0x00100000, // rb_profile_end + 12, // pt_fingerprint + 4, // fingerprint_size + 28, // pt_profile_pre + 0, // pt_profile_begin + 128, // pt_profile_end +}; + + // Cochran Commander Nitrox static const cochran_device_layout_t cochran_cmdr_device_layout = { COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model @@ -133,6 +161,8 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { 512, // rb_logbook_entry_count 0x00020000, // rb_profile_begin 0x00100000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 128, // pt_profile_end @@ -154,6 +184,8 @@ static const cochran_device_layout_t cochran_emc14_device_layout = { 256, // rb_logbook_entry_count 0x00022000, // rb_profile_begin 0x00200000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -175,6 +207,8 @@ static const cochran_device_layout_t cochran_emc16_device_layout = { 1024, // rb_logbook_entry_count 0x00094000, // rb_profile_begin 0x00800000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -196,6 +230,8 @@ static const cochran_device_layout_t cochran_emc20_device_layout = { 1024, // rb_logbook_entry_count 0x00094000, // rb_profile_begin 0x01000000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -207,10 +243,14 @@ static unsigned int cochran_commander_get_model (cochran_commander_device_t *device) { const cochran_commander_model_t models[] = { - {"\x11""2", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, - {"73", COCHRAN_MODEL_EMC_14}, - {"A3", COCHRAN_MODEL_EMC_16}, - {"23", COCHRAN_MODEL_EMC_20}, + {"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000}, + {"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, + {"730", COCHRAN_MODEL_EMC_14}, + {"731", COCHRAN_MODEL_EMC_14}, + {"A30", COCHRAN_MODEL_EMC_16}, + {"A31", COCHRAN_MODEL_EMC_16}, + {"230", COCHRAN_MODEL_EMC_20}, + {"231", COCHRAN_MODEL_EMC_20}, }; unsigned int model = 0xFFFFFFFF; @@ -541,7 +581,11 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d data->invalid_profile_dive_num = -1; // Remove the pre-dive events that occur after the last dive - unsigned int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int rb_head_ptr = 0; + if (device->layout->endian == ENDIAN_WORD_BE) + rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + else + rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); if (rb_head_ptr > last_dive_end_address) profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; @@ -565,7 +609,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d unsigned char *log_entry = data->logbook + idx * device->layout->rb_logbook_entry_size; // We're done if we find the fingerprint - if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { + if (!memcmp(device->fingerprint, log_entry + device->layout->pt_fingerprint, device->layout->fingerprint_size)) { data->fp_dive_num = idx; break; } @@ -694,6 +738,9 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c unsigned int model = cochran_commander_get_model(device); switch (model) { + case COCHRAN_MODEL_COMMANDER_PRE21000: + device->layout = &cochran_cmdr_1_device_layout; + break; case COCHRAN_MODEL_COMMANDER_AIR_NITROX: device->layout = &cochran_cmdr_device_layout; break; @@ -744,13 +791,13 @@ cochran_commander_device_set_fingerprint (dc_device_t *abstract, const unsigned { cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; - if (size && size != sizeof (device->fingerprint)) + if (size && size != device->layout->fingerprint_size) return DC_STATUS_INVALIDARGS; if (size) - memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + memcpy (device->fingerprint, data, device->layout->fingerprint_size); else - memset (device->fingerprint, 0xFF, sizeof (device->fingerprint)); + memset (device->fingerprint, 0xFF, sizeof(device->fingerprint)); return DC_STATUS_SUCCESS; } @@ -887,12 +934,21 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call dc_event_devinfo_t devinfo; devinfo.model = device->layout->model; devinfo.firmware = 0; // unknown - devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + if (device->layout->endian == ENDIAN_WORD_BE) + devinfo.serial = array_uint32_word_be(data.config + device->layout->cf_serial_number); + else + devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Calculate profile RB effective head pointer // Cochran seems to erase 8K chunks so round up. - unsigned int last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int last_start_address; + if (device->layout->endian == ENDIAN_WORD_BE) + last_start_address = (array_uint32_word_be(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + else + last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) { ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); status = DC_STATUS_DATAFORMAT; @@ -983,7 +1039,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } - if (callback && !callback (dive, dive_size, dive, sizeof(device->fingerprint), userdata)) { + if (callback && !callback (dive, dive_size, dive + device->layout->pt_fingerprint, device->layout->fingerprint_size, userdata)) { free(dive); break; } diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index ab854ad..1c14536 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -31,10 +31,14 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) -#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 -#define COCHRAN_MODEL_EMC_14 1 -#define COCHRAN_MODEL_EMC_16 2 -#define COCHRAN_MODEL_EMC_20 3 +#define COCHRAN_MODEL_COMMANDER_PRE21000 0 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1 +#define COCHRAN_MODEL_EMC_14 2 +#define COCHRAN_MODEL_EMC_16 3 +#define COCHRAN_MODEL_EMC_20 4 + +// Cochran time stamps start at Jan 1, 1992 +#define COCHRAN_EPOCH 694242000 #define UNSUPPORTED 0xFFFFFFFF @@ -43,11 +47,19 @@ typedef enum cochran_sample_format_t { SAMPLE_EMC, } cochran_sample_format_t; + +typedef enum cochran_date_encoding_t { + DATE_ENCODING_MSDHYM, + DATE_ENCODING_SMHDMY, + DATE_ENCODING_TICKS, +} cochran_date_encoding_t; + typedef struct cochran_parser_layout_t { cochran_sample_format_t format; unsigned int headersize; unsigned int samplesize; - unsigned int second, minute, hour, day, month, year; + cochran_date_encoding_t date_encoding; + unsigned int datetime; unsigned int pt_profile_begin; unsigned int water_conductivity; unsigned int pt_profile_pre; @@ -101,11 +113,36 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = { NULL /* destroy */ }; +static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = { + SAMPLE_CMDR, // type + 256, // headersize + 2, // samplesize + DATE_ENCODING_TICKS, // date_encoding + 8, // datetime, 4 bytes + 0, // pt_profile_begin, 4 bytes + 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) + 28, // pt_profile_pre, 4 bytes + 43, // start_temp, 1 byte, F + 54, // start_depth, 2 bytes, /4=ft + 68, // dive_number, 2 bytes + 73, // altitude, 1 byte, /4=kilofeet + 128, // pt_profile_end, 4 bytes + 153, // end_temp, 1 byte F + 166, // divetime, 2 bytes, minutes + 168, // max_depth, 2 bytes, /4=ft + 170, // avg_depth, 2 bytes, /4=ft + 210, // oxygen, 4 bytes (2 of) 2 bytes, /256=% + UNSUPPORTED, // helium, 4 bytes (2 of) 2 bytes, /256=% + 232, // min_temp, 1 byte, /2+20=F + 233, // max_temp, 1 byte, /2+20=F +}; + static const cochran_parser_layout_t cochran_cmdr_parser_layout = { SAMPLE_CMDR, // type 256, // headersize 2, // samplesize - 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each + DATE_ENCODING_MSDHYM, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -128,7 +165,8 @@ static const cochran_parser_layout_t cochran_emc_parser_layout = { SAMPLE_EMC, // type 512, // headersize 3, // samplesize - 0, 1, 2, 3, 4, 5, // second, minute, hour, day, month, year, 1 byte each + DATE_ENCODING_SMHDMY, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -296,6 +334,11 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->model = model; switch (model) { + case COCHRAN_MODEL_COMMANDER_PRE21000: + parser->layout = &cochran_cmdr_1_parser_layout; + parser->events = cochran_cmdr_event_bytes; + parser->nevents = C_ARRAY_SIZE(cochran_cmdr_event_bytes); + break; case COCHRAN_MODEL_COMMANDER_AIR_NITROX: parser->layout = &cochran_cmdr_parser_layout; parser->events = cochran_cmdr_event_bytes; @@ -340,13 +383,32 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat if (abstract->size < layout->headersize) return DC_STATUS_DATAFORMAT; + dc_ticks_t ts = 0; + if (datetime) { - datetime->second = data[layout->second]; - datetime->minute = data[layout->minute]; - datetime->hour = data[layout->hour]; - datetime->day = data[layout->day]; - datetime->month = data[layout->month]; - datetime->year = data[layout->year] + (data[layout->year] > 91 ? 1900 : 2000); + switch (layout->date_encoding) + { + case DATE_ENCODING_MSDHYM: + datetime->second = data[layout->datetime + 1]; + datetime->minute = data[layout->datetime + 0]; + datetime->hour = data[layout->datetime + 3]; + datetime->day = data[layout->datetime + 2]; + datetime->month = data[layout->datetime + 5]; + datetime->year = data[layout->datetime + 4] + (data[layout->datetime + 4] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_SMHDMY: + datetime->second = data[layout->datetime + 0]; + datetime->minute = data[layout->datetime + 1]; + datetime->hour = data[layout->datetime + 2]; + datetime->day = data[layout->datetime + 3]; + datetime->month = data[layout->datetime + 4]; + datetime->year = data[layout->datetime + 5] + (data[layout->datetime + 5] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_TICKS: + ts = array_uint32_le(data + layout->datetime) + COCHRAN_EPOCH; + dc_datetime_localtime(datetime, ts); + break; + } } return DC_STATUS_SUCCESS; @@ -471,10 +533,11 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb // know what the dive summary values are (i.e. max depth, min temp) if (array_uint32_le(data + layout->pt_profile_end) == 0xFFFFFFFF) { corrupt_dive = 1; + dc_datetime_t d; + cochran_commander_parser_get_datetime(abstract, &d); WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples", - data[layout->year], data[layout->month], data[layout->day], - data[layout->hour], data[layout->minute], data[layout->second]); + d.year, d.month, d.day, d.hour, d.minute, d.second); // Eliminate inter-dive events size = cochran_commander_backparse(parser, samples, size); @@ -484,7 +547,8 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb // and temp every other second. // Prime values from the dive log section - if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX) { + if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX || + parser->model == COCHRAN_MODEL_COMMANDER_PRE21000) { // Commander stores start depth in quarter-feet start_depth = array_uint16_le (data + layout->start_depth) / 4.0; } else { diff --git a/src/descriptor.c b/src/descriptor.c index 9b537e1..633debd 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -313,10 +313,12 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iDive2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42}, {"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44}, {"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, - {"Cochran", "Commander", DC_FAMILY_COCHRAN_COMMANDER, 0}, - {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 1}, - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 2}, - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 3}, + /* Cochran Commander */ + {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 1}, + {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 2}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 3}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 4}, }; typedef struct dc_descriptor_iterator_t { From 3a2f2ff0c3767ecfb3d6eda2137eecb1ced4cd6f Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 12 Jun 2017 15:58:25 -0400 Subject: [PATCH 28/35] Add new EMC device model string --- src/cochran_commander.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 95c00fe..28f2ee4 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -251,6 +251,7 @@ cochran_commander_get_model (cochran_commander_device_t *device) {"A31", COCHRAN_MODEL_EMC_16}, {"230", COCHRAN_MODEL_EMC_20}, {"231", COCHRAN_MODEL_EMC_20}, + {"\x40""30", COCHRAN_MODEL_EMC_20}, }; unsigned int model = 0xFFFFFFFF; From 3545bf158a57a317bbdea51fc9271c27db5561b1 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 12 Jun 2017 15:58:25 -0400 Subject: [PATCH 29/35] Use a local variable for the layout pointer --- src/cochran_commander.c | 63 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 28f2ee4..496d08b 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -862,6 +862,7 @@ static dc_status_t cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; + const cochran_device_layout_t *layout = device->layout; dc_status_t status = DC_STATUS_SUCCESS; cochran_data_t data; @@ -869,8 +870,8 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Calculate max data sizes unsigned int max_config = sizeof(data.config); - unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; - unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; + unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin; + unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin; // setup progress indication dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; @@ -890,19 +891,19 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call return rc; // Determine size of dive list to read. - if (device->layout->endian == ENDIAN_LE) - data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count); + if (layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + layout->cf_dive_count); else - data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + data.dive_count = array_uint16_be (data.config + layout->cf_dive_count); if (data.dive_count == 0) // No dives to read return DC_STATUS_SUCCESS; - if (data.dive_count > device->layout->rb_logbook_entry_count) { - data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; + if (data.dive_count > layout->rb_logbook_entry_count) { + data.logbook_size = layout->rb_logbook_entry_count * layout->rb_logbook_entry_size; } else { - data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + data.logbook_size = data.dive_count * layout->rb_logbook_entry_size; } // Update progress indicator with new maximum @@ -933,24 +934,24 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Emit a device info event. dc_event_devinfo_t devinfo; - devinfo.model = device->layout->model; + devinfo.model = layout->model; devinfo.firmware = 0; // unknown - if (device->layout->endian == ENDIAN_WORD_BE) - devinfo.serial = array_uint32_word_be(data.config + device->layout->cf_serial_number); + if (layout->endian == ENDIAN_WORD_BE) + devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number); else - devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Calculate profile RB effective head pointer // Cochran seems to erase 8K chunks so round up. unsigned int last_start_address; - if (device->layout->endian == ENDIAN_WORD_BE) - last_start_address = (array_uint32_word_be(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + if (layout->endian == ENDIAN_WORD_BE) + last_start_address = (array_uint32_word_be(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; else - last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + last_start_address = (array_uint32_le(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) { + if (last_start_address < layout->rb_profile_begin || last_start_address > layout->rb_profile_end) { ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); status = DC_STATUS_DATAFORMAT; goto error; @@ -958,32 +959,32 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; - if (data.dive_count <= device->layout->rb_logbook_entry_count) { + if (data.dive_count <= layout->rb_logbook_entry_count) { head_dive = data.dive_count; tail_dive = 0; } else { // Log wrapped - tail_dive = data.dive_count % device->layout->rb_logbook_entry_count; + tail_dive = data.dive_count % layout->rb_logbook_entry_count; head_dive = tail_dive; } // Change tail to dive following the fingerprint dive. if (data.fp_dive_num > -1) - tail_dive = (data.fp_dive_num + 1) % device->layout->rb_logbook_entry_count; + tail_dive = (data.fp_dive_num + 1) % layout->rb_logbook_entry_count; // Number of dives to read - dive_count = (device->layout->rb_logbook_entry_count + head_dive - tail_dive) % device->layout->rb_logbook_entry_count; + dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count; int invalid_profile_flag = 0; // Loop through each dive for (unsigned int i = 0; i < dive_count; ++i) { - unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + unsigned int idx = (layout->rb_logbook_entry_count + head_dive - (i + 1)) % layout->rb_logbook_entry_count; - unsigned char *log_entry = data.logbook + idx * device->layout->rb_logbook_entry_size; + unsigned char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size; - unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); - unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); + unsigned int sample_start_address = array_uint32_le (log_entry + layout->pt_profile_begin); + unsigned int sample_end_address = array_uint32_le (log_entry + layout->pt_profile_end); int sample_size = 0; @@ -995,14 +996,14 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); // Build dive blob - unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; + unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; unsigned char *dive = (unsigned char *) malloc(dive_size); if (dive == NULL) { status = DC_STATUS_NOMEMORY; goto error; } - memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log + memcpy(dive, log_entry, layout->rb_logbook_entry_size); // log // Read profile data if (sample_size) { @@ -1011,7 +1012,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx); if (sample_start_address <= sample_end_address) { - rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1020,9 +1021,9 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } else { // It wrapped the buffer, copy two sections - unsigned int size = device->layout->rb_profile_end - sample_start_address; + unsigned int size = layout->rb_profile_end - sample_start_address; - rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1030,7 +1031,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - rc = cochran_commander_read_retry (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + rc = cochran_commander_read_retry (device, &progress, layout->rb_profile_begin, dive + layout->rb_logbook_entry_size + size, sample_end_address - layout->rb_profile_begin); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1040,7 +1041,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } - if (callback && !callback (dive, dive_size, dive + device->layout->pt_fingerprint, device->layout->fingerprint_size, userdata)) { + if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) { free(dive); break; } From 48d93e94047376a968175409e36e19c17abf97a3 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:56 -0400 Subject: [PATCH 30/35] Fixed location and encoding of Commander II pointers Commander II pointers to profile ringbuffer data was wrong. After seeing the Commander I encoding I realized the Commander II encoding of RB pointers was in a flipped word big endian format. It only appeared to be in normal big endian format because of an adjacent pointer that usually shared the same first two bytes. --- src/cochran_commander.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 496d08b..8ccf4ed 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -149,11 +149,11 @@ static const cochran_device_layout_t cochran_cmdr_1_device_layout = { static const cochran_device_layout_t cochran_cmdr_device_layout = { COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model 24, // address_bits - ENDIAN_BE, // endian + ENDIAN_WORD_BE, // endian 115200, // baudrate 0x046, // cf_dive_count - 0x06E, // cf_last_log - 0x200, // cf_last_interdive + 0x070, // cf_last_log + 0x06C, // cf_last_interdive 0x0AA, // cf_serial_number 0x00000000, // rb_logbook_begin 0x00020000, // rb_logbook_end From 9c795e6e4e9ecdfe3444c6de09451ca092ac2882 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:57 -0400 Subject: [PATCH 31/35] Changed cochran_comander_profile_size function parameters This function is much more useful if it works like a ringbuffer_distance() function. It assumed the wrong values when calculating profile size and it didn't have easy access to values it needed to properly calculate profile sizes. It makes sense to keep since it validates pointers. --- src/cochran_commander.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 8ccf4ed..651a0f9 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -46,11 +46,6 @@ typedef enum cochran_endian_t { ENDIAN_WORD_BE, } cochran_endian_t; -typedef enum cochran_profile_size_t { - PROFILE_SIZE_FULL, - PROFILE_SIZE_READ, -} cochran_profile_size_t; - typedef struct cochran_commander_model_t { unsigned char id[3 + 1]; unsigned int model; @@ -524,21 +519,8 @@ cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, c static unsigned int -cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type) +cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, unsigned int sample_start_address, unsigned int sample_end_address) { - - const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size; - unsigned int sample_start_address = 0xFFFFFFFF; - unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); - - if (type == PROFILE_SIZE_FULL) { - // actual size, includes pre-dive events - sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre); - } else if (type == PROFILE_SIZE_READ) { - // read size, only include dive profile - sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); - } - // Validate addresses if (sample_start_address < device->layout->rb_profile_begin || sample_start_address > device->layout->rb_profile_end || @@ -615,8 +597,12 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d break; } - unsigned int sample_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_FULL); - unsigned int read_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_READ); + unsigned int profile_pre = array_uint32_le(log_entry + device->layout->pt_profile_pre); + unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin); + unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end); + + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, profile_end); + unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end); // Determine if sample exists if (profile_capacity_remaining > 0) { @@ -993,7 +979,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call invalid_profile_flag = 1; if (!invalid_profile_flag) - sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); + sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address); // Build dive blob unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; From e1b679912abd107ba39050beeba4cc7858ed5ec8 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:58 -0400 Subject: [PATCH 32/35] Fix bad profiles when profile ringbuffer wraps around The method used to calculate the data used by dives (to determine when we run out of ringbuffer) incorrectly didn't include surface sample data. Ten to twenty minute of sample data is recorded at the surface in case the diver re-descends, continuing the dive. The code then thought that older dive profiles were not yet overwritten. The improper data was returned to the user. --- src/cochran_commander.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 651a0f9..0203ae6 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -147,8 +147,8 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { ENDIAN_WORD_BE, // endian 115200, // baudrate 0x046, // cf_dive_count - 0x070, // cf_last_log - 0x06C, // cf_last_interdive + 0x06C, // cf_last_log + 0x070, // cf_last_interdive 0x0AA, // cf_serial_number 0x00000000, // rb_logbook_begin 0x00020000, // rb_logbook_end @@ -566,12 +566,9 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d // Remove the pre-dive events that occur after the last dive unsigned int rb_head_ptr = 0; if (device->layout->endian == ENDIAN_WORD_BE) - rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; else - rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; - unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); - if (rb_head_ptr > last_dive_end_address) - profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; unsigned int head_dive = 0, tail_dive = 0; @@ -584,6 +581,18 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d head_dive = tail_dive; } + unsigned int last_profile_idx = (device->layout->rb_logbook_entry_count + head_dive - 1) % device->layout->rb_logbook_entry_count; + unsigned int last_profile_end = array_uint32_le(data->logbook + last_profile_idx * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); + unsigned int last_profile_pre = 0xFFFFFFFF; + + if (device->layout->endian == ENDIAN_WORD_BE) + last_profile_pre = array_uint32_word_be(data->config + device->layout->cf_last_log); + else + last_profile_pre = array_uint32_le(data->config + device->layout->cf_last_log); + + if (rb_head_ptr > last_profile_end) + profile_capacity_remaining -= rb_head_ptr - last_profile_end; + // Loop through dives to find FP, Accumulate profile data size, // and find the last dive with invalid profile for (unsigned int i = 0; i <= dive_count; ++i) { @@ -601,8 +610,9 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin); unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end); - unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, profile_end); + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre); unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end); + last_profile_pre = profile_pre; // Determine if sample exists if (profile_capacity_remaining > 0) { From 094b635f87183f399e3ad25b499f6a1a4fe99bb2 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:59 -0400 Subject: [PATCH 33/35] Added decompression event handling for the Commander I finally found an example of decompression on a Commander II computer and it seems to be identical to the EMC. --- src/cochran_commander_parser.c | 71 ++++++++++++++++------------------ 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 1c14536..8208e07 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -601,45 +601,42 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb if (s[0] & 0x80) { offset += cochran_commander_handle_event(parser, s[0], callback, userdata); - if (layout->format == SAMPLE_EMC) { - // EMC models have events indicating change in deco status - // Commander may have them but I don't have example data - switch (s[0]) { - case 0xC5: // Deco obligation begins - deco_obligation = 1; - break; - case 0xD8: // Deco obligation ends - deco_obligation = 0; - break; - case 0xAB: // Decrement ceiling (deeper) - deco_ceiling += 10; // feet + // Events indicating change in deco status + switch (s[0]) { + case 0xC5: // Deco obligation begins + deco_obligation = 1; + break; + case 0xD8: // Deco obligation ends + deco_obligation = 0; + break; + case 0xAB: // Decrement ceiling (deeper) + deco_ceiling += 10; // feet - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60; - sample.deco.depth = deco_ceiling * FEET; - if (callback) callback(DC_SAMPLE_DECO, sample, userdata); - break; - case 0xAD: // Increment ceiling (shallower) - deco_ceiling -= 10; // feet + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; + sample.deco.depth = deco_ceiling * FEET; + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + break; + case 0xAD: // Increment ceiling (shallower) + deco_ceiling -= 10; // feet - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = deco_ceiling * FEET; - sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60; - if (callback) callback(DC_SAMPLE_DECO, sample, userdata); - break; - case 0xC0: // Switched to FO2 21% mode (surface) - // Event seen upon surfacing - break; - case 0xCD: // Switched to deco blend - case 0xEF: // Switched to gas blend 2 - sample.gasmix = 1; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); - break; - case 0xF3: // Switched to gas blend 1 - sample.gasmix = 0; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); - break; - } + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.depth = deco_ceiling * FEET; + sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + break; + case 0xC0: // Switched to FO2 21% mode (surface) + // Event seen upon surfacing + break; + case 0xCD: // Switched to deco blend + case 0xEF: // Switched to gas blend 2 + sample.gasmix = 1; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + break; + case 0xF3: // Switched to gas blend 1 + sample.gasmix = 0; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + break; } continue; From 2c5e787b77ddb93ea97b7366ef291e0f1a8f6960 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 2 Jul 2017 10:07:45 -0400 Subject: [PATCH 34/35] Fixed duplicate gasmix event reports Newer cochran DCs record a gas change event at the begining of a dive. The code creates a gas change before processing samples so with newer DCs this resulted in duplicate events. --- src/cochran_commander_parser.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 8208e07..5a0ad55 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -567,6 +567,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb sample.gasmix = 0; if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + unsigned int last_gasmix = sample.gasmix; while (offset < size) { const unsigned char *s = samples + offset; @@ -630,12 +631,18 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb break; case 0xCD: // Switched to deco blend case 0xEF: // Switched to gas blend 2 - sample.gasmix = 1; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + if (last_gasmix != 1) { + sample.gasmix = 1; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + last_gasmix = sample.gasmix; + } break; case 0xF3: // Switched to gas blend 1 - sample.gasmix = 0; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + if (last_gasmix != 0) { + sample.gasmix = 0; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + last_gasmix = sample.gasmix; + } break; } From fd1557f634bfebf669ba852b5c3272ab4dcfa1b0 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 2 Jul 2017 22:05:11 -0400 Subject: [PATCH 35/35] Removed unused code --- src/cochran_commander.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 0203ae6..1ad38f0 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -939,20 +939,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - // Calculate profile RB effective head pointer - // Cochran seems to erase 8K chunks so round up. - unsigned int last_start_address; - if (layout->endian == ENDIAN_WORD_BE) - last_start_address = (array_uint32_word_be(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - else - last_start_address = (array_uint32_le(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - - if (last_start_address < layout->rb_profile_begin || last_start_address > layout->rb_profile_end) { - ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); - status = DC_STATUS_DATAFORMAT; - goto error; - } - unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; if (data.dive_count <= layout->rb_logbook_entry_count) {