diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..8fa7569
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,95 @@
+name: Build
+
+on: [push, pull_request]
+
+jobs:
+
+ linux:
+
+ name: Linux
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ compiler: [gcc, clang]
+
+ env:
+ CC: ${{ matrix.compiler }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install dependencies
+ run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
+ - run: autoreconf --install --force
+ - run: ./configure --prefix=/usr
+ - run: make
+ - run: make distcheck
+ - name: Package artifacts
+ run: |
+ make install DESTDIR=$PWD/artifacts
+ tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
+ - uses: actions/upload-artifact@v2
+ with:
+ name: ${{ github.job }}-${{ matrix.compiler }}
+ path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
+
+ mac:
+
+ name: Mac
+
+ runs-on: macos-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ compiler: [gcc, clang]
+
+ env:
+ CC: ${{ matrix.compiler }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install dependencies
+ run: brew install autoconf automake libtool hidapi libusb
+ - run: autoreconf --install --force
+ - run: ./configure --prefix=/usr
+ - run: make
+ - run: make distcheck
+ - name: Package artifacts
+ run: |
+ make install DESTDIR=$PWD/artifacts
+ tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
+ - uses: actions/upload-artifact@v2
+ with:
+ name: ${{ github.job }}-${{ matrix.compiler }}
+ path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
+
+ windows:
+
+ name: Windows
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: [i686, x86_64]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install dependencies
+ run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
+ - run: autoreconf --install --force
+ - run: ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
+ - run: make
+ - run: make distcheck
+ - name: Package artifacts
+ run: |
+ make install DESTDIR=$PWD/artifacts
+ tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr
+ - uses: actions/upload-artifact@v2
+ with:
+ name: ${{ github.job }}-${{ matrix.arch }}
+ path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..37f578e
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,61 @@
+name: Release
+
+on:
+ push:
+ tags: 'v*'
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Version number
+ id: version
+ run: |
+ VERSION="${GITHUB_REF/refs\/tags\/v/}"
+ if [ "${VERSION}" = "${VERSION%%-*}" ]; then
+ PRERELEASE=false
+ else
+ PRERELEASE=true
+ fi
+ echo ::set-output name=version::${VERSION}
+ echo ::set-output name=prerelease::${PRERELEASE}
+
+ - name: Build distribution tarball
+ id: build
+ run: |
+ sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
+ autoreconf --install --force
+ ./configure
+ make
+ make distcheck
+
+ - name: Check tarball version number
+ id: check
+ run: |
+ FILENAME="libdivecomputer-${{ steps.version.outputs.version }}.tar.gz"
+ if [ ! -f "${FILENAME}" ]; then
+ echo ::error ::Tarball \'${FILENAME}\' not found!
+ exit 1
+ fi
+
+ - uses: actions/create-release@v1
+ id: release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: ${{ github.ref }}
+ prerelease: ${{ steps.version.outputs.prerelease }}
+
+ - uses: actions/upload-release-asset@v1
+ id: upload
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.release.outputs.upload_url }}
+ asset_path: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
+ asset_name: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
+ asset_content_type: application/gzip
diff --git a/configure.ac b/configure.ac
index ca1c5bc..9b43b4b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,18 +72,21 @@ AM_CONDITIONAL([HAVE_MANDOC],[test -n "$MANDOC"])
# Enable automake silent build rules.
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
-# Checks for native Windows.
-AC_MSG_CHECKING([for native Win32])
+# Checks for operating system.
+AC_MSG_CHECKING([for operating system])
case "$host" in
*-*-mingw*)
- os_win32=yes
+ platform=windows
+ ;;
+ *-*-darwin*)
+ platform=mac
;;
*)
- os_win32=no
+ platform=default
;;
esac
-AC_MSG_RESULT([$os_win32])
-AM_CONDITIONAL([OS_WIN32], [test "$os_win32" = "yes"])
+AC_MSG_RESULT([$platform])
+AM_CONDITIONAL([OS_WIN32], [test "$platform" = "windows"])
DEPENDENCIES=""
@@ -187,6 +190,7 @@ AC_CHECK_FUNCS([clock_gettime mach_absolute_time])
AC_CHECK_FUNCS([getopt_long])
# Checks for supported compiler options.
+AX_APPEND_COMPILE_FLAGS([-Werror=unknown-warning-option],[ERROR_CFLAGS])
AX_APPEND_COMPILE_FLAGS([ \
-Wall \
-Wshadow \
@@ -204,10 +208,10 @@ AX_APPEND_COMPILE_FLAGS([ \
-Wno-pointer-sign \
-Wno-shadow \
-fmacro-prefix-map='$(top_srcdir)/'= \
-])
+],,[$ERROR_CFLAGS])
# Windows specific compiler options.
-AS_IF([test "$os_win32" = "yes"], [
+AS_IF([test "$platform" = "windows"], [
AX_APPEND_COMPILE_FLAGS([-Wno-pedantic-ms-format])
])
@@ -223,6 +227,25 @@ m4_ifset([dc_version_suffix],[
AC_DEFINE(HAVE_VERSION_SUFFIX, [1], [Define if a version suffix is present.])
])
+# Supported transports
+transport_serial="yes"
+transport_usb="${have_libusb-no}"
+if test "$have_hidapi" = "yes"; then
+ transport_usbhid="yes"
+elif test "$have_libusb" = "yes" && test "$platform" != "mac"; then
+ transport_usbhid="yes"
+else
+ transport_usbhid="no"
+fi
+if test "$platform" = "windows"; then
+ transport_irda="$ac_cv_header_af_irda_h"
+ transport_bluetooth="$ac_cv_header_ws2bth_h"
+else
+ transport_irda="$ac_cv_header_linux_irda_h"
+ transport_bluetooth="${have_bluez-no}"
+fi
+transport_ble="no"
+
AC_CONFIG_FILES([
libdivecomputer.pc
Makefile
@@ -237,3 +260,35 @@ AC_CONFIG_FILES([
examples/Makefile
])
AC_OUTPUT
+AC_MSG_NOTICE([
+ $PACKAGE $VERSION
+ ===============
+
+ Compiler:
+
+ CC : $CC
+ CFLAGS : $CFLAGS
+ LDFLAGS : $LDFLAGS
+
+ Features:
+
+ Logging : $enable_logging
+ Pseudo terminal : $enable_pty
+ Example applications : $enable_examples
+ Documentation : $enable_doc
+
+ Transports:
+
+ Serial : $transport_serial
+ USB : $transport_usb
+ USBHID : $transport_usbhid
+ IrDA : $transport_irda
+ Bluetooth : $transport_bluetooth
+ BLE : $transport_ble
+
+ Building:
+
+ Type 'make' to compile $PACKAGE.
+
+ Type 'make install' to install $PACKAGE.
+])
diff --git a/examples/common.c b/examples/common.c
index b943fd1..1972cfa 100644
--- a/examples/common.c
+++ b/examples/common.c
@@ -93,6 +93,7 @@ static const backend_table_t g_backends[] = {
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
{"mclean", DC_FAMILY_MCLEAN_EXTREME, 0},
{"lynx", DC_FAMILY_LIQUIVISION_LYNX, 0},
+ {"sp2", DC_FAMILY_SPORASUB_SP2, 0},
// Not merged upstream yet
{"descentmk1", DC_FAMILY_GARMIN, 0},
diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h
index ac11fa6..6e2c499 100644
--- a/include/libdivecomputer/common.h
+++ b/include/libdivecomputer/common.h
@@ -112,6 +112,8 @@ typedef enum dc_family_t {
DC_FAMILY_MCLEAN_EXTREME = (16 << 16),
/* Liquivision */
DC_FAMILY_LIQUIVISION_LYNX = (17 << 16),
+ /* Sporasub */
+ DC_FAMILY_SPORASUB_SP2 = (18 << 16),
// Not merged upstream yet
/* Garmin */
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj
index 8298de6..0e689a1 100644
--- a/msvc/libdivecomputer.vcproj
+++ b/msvc/libdivecomputer.vcproj
@@ -398,6 +398,10 @@
RelativePath="..\src\parser.c"
>
+
+
@@ -454,6 +458,14 @@
RelativePath="..\src\socket.c"
>
+
+
+
+
@@ -856,6 +868,10 @@
RelativePath="..\src\socket.h"
>
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index ab9ea2b..dd88eac 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,7 +64,7 @@ libdivecomputer_la_SOURCES = \
diverite_nitekq.h diverite_nitekq.c diverite_nitekq_parser.c \
citizen_aqualand.h citizen_aqualand.c citizen_aqualand_parser.c \
divesystem_idive.h divesystem_idive.c divesystem_idive_parser.c \
- platform.h \
+ platform.h platform.c \
ringbuffer.h ringbuffer.c \
rbstream.h rbstream.c \
checksum.h checksum.c \
@@ -74,6 +74,7 @@ libdivecomputer_la_SOURCES = \
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
mclean_extreme.h mclean_extreme.c mclean_extreme_parser.c \
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
+ sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \
socket.h socket.c \
irda.c \
usb.c \
diff --git a/src/aes.c b/src/aes.c
index 55eac5c..15a1963 100644
--- a/src/aes.c
+++ b/src/aes.c
@@ -98,7 +98,7 @@ typedef struct aes_state_t {
#if defined(CBC) && CBC
// Initial Vector used only for CBC mode
- uint8_t* Iv;
+ const uint8_t* Iv;
#endif
} aes_state_t;
@@ -542,7 +542,7 @@ void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length,
if(iv != 0)
{
- state.Iv = (uint8_t*)iv;
+ state.Iv = iv;
}
for(i = 0; i < length; i += KEYLEN)
@@ -584,7 +584,7 @@ void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length,
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
if(iv != 0)
{
- state.Iv = (uint8_t*)iv;
+ state.Iv = iv;
}
for(i = 0; i < length; i += KEYLEN)
diff --git a/src/atomics_cobalt.c b/src/atomics_cobalt.c
index a67ef82..1c2e3a5 100644
--- a/src/atomics_cobalt.c
+++ b/src/atomics_cobalt.c
@@ -210,6 +210,15 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init,
return DC_STATUS_NOMEMORY;
}
+ // Adjust the maximum value to take into account the two byte checksum and
+ // the 8 byte serial number. Those extra bytes are not stored inside the
+ // dive header and are added dynamically during the data transfer. Since we
+ // don't know the total number of dives in advance, we can't calculate the
+ // total number of extra bytes and adjust the maximum on the fly.
+ if (progress) {
+ progress->maximum += 2 + 8;
+ }
+
// Send the command to the dive computer.
unsigned char bRequest = 0;
if (device->simulation)
@@ -348,12 +357,6 @@ atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
return DC_STATUS_SUCCESS;
}
- // Adjust the maximum value to take into account the two checksum bytes
- // for the next dive. Since we don't know the total number of dives in
- // advance, we can't calculate the total number of checksum bytes and
- // adjust the maximum on the fly.
- progress.maximum += 2;
-
ndives++;
}
diff --git a/src/descriptor.c b/src/descriptor.c
index 2a75217..a25ebee 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -130,11 +130,13 @@ static const dc_descriptor_t g_descriptors[] = {
{"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C, DC_TRANSPORT_SERIAL, NULL},
{"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D, DC_TRANSPORT_SERIAL, NULL},
{"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E, DC_TRANSPORT_SERIAL, NULL},
+ {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1F, DC_TRANSPORT_SERIAL, NULL},
{"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20, DC_TRANSPORT_SERIAL, NULL},
/* Suunto EON Steel */
- {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
- {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
- {"Suunto", "D5", DC_FAMILY_SUUNTO_EONSTEEL, 2, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
+ {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
+ {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
+ {"Suunto", "D5", DC_FAMILY_SUUNTO_EONSTEEL, 2, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
+ {"Suunto", "EON Steel Black", DC_FAMILY_SUUNTO_EONSTEEL, 3, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_suunto},
/* Uwatec Aladin */
{"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C, DC_TRANSPORT_SERIAL, NULL},
{"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E, DC_TRANSPORT_SERIAL, NULL},
@@ -268,6 +270,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Oceanic", "Veo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4654, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Sherwood", "Wisdom 4", DC_FAMILY_OCEANIC_ATOM2, 0x4655, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
+ {"Sherwood", "Beacon", DC_FAMILY_OCEANIC_ATOM2, 0x4742, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Mares Nemo */
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
@@ -389,12 +392,12 @@ static const dc_descriptor_t g_descriptors[] = {
{"Ratio", "iDive Color Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x54, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Tech+",DC_FAMILY_DIVESYSTEM_IDIVE, 0x55, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x56, DC_TRANSPORT_SERIAL, NULL},
- {"Ratio", "iX3M 2021 GPS Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x60, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
- {"Ratio", "iX3M 2021 GPS Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x61, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
- {"Ratio", "iX3M 2021 GPS Pro ", DC_FAMILY_DIVESYSTEM_IDIVE, 0x62, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
- {"Ratio", "iX3M 2021 GPS Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x63, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
- {"Ratio", "iX3M 2021 GPS Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x64, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
- {"Ratio", "iX3M 2021 GPS Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x65, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x60, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x61, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Pro ", DC_FAMILY_DIVESYSTEM_IDIVE, 0x62, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x63, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x64, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
+ {"Ratio", "iX3M 2021 GPS Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x65, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2021 Pro Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x70, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x71, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x72, DC_TRANSPORT_SERIAL, NULL},
@@ -419,6 +422,8 @@ static const dc_descriptor_t g_descriptors[] = {
{"Liquivision", "Xeo", DC_FAMILY_LIQUIVISION_LYNX, 1, DC_TRANSPORT_SERIAL, NULL},
{"Liquivision", "Lynx", DC_FAMILY_LIQUIVISION_LYNX, 2, DC_TRANSPORT_SERIAL, NULL},
{"Liquivision", "Kaon", DC_FAMILY_LIQUIVISION_LYNX, 3, DC_TRANSPORT_SERIAL, NULL},
+ /* Sporasub */
+ {"Sporasub", "SP2", DC_FAMILY_SPORASUB_SP2, 0, DC_TRANSPORT_SERIAL, NULL},
// Not merged upstream yet
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
@@ -501,14 +506,16 @@ dc_match_oceanic (const void *key, const void *value)
0
};
- return dc_match_number_with_prefix (key, &prefix);
+ const char *p = prefix;
+
+ return dc_match_number_with_prefix (key, &p);
}
static int
dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match, void *params_dst, const void *params_src, size_t params_size)
{
if (key == NULL)
- return 0;
+ return 1;
for (size_t i = 0; i < count; ++i) {
if (match (key, (const unsigned char *) values + i * size)) {
@@ -571,11 +578,13 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
{0x1493, 0x0030}, // Eon Steel
{0x1493, 0x0033}, // Eon Core
{0x1493, 0x0035}, // D5
+ {0x1493, 0x0036}, // EON Steel Black
};
static const char * const bluetooth[] = {
"EON Steel",
"EON Core",
"Suunto D5",
+ "EON Steel Black",
};
if (transport == DC_TRANSPORT_USBHID) {
@@ -660,7 +669,7 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata,
"IX5M",
};
- if (transport == DC_TRANSPORT_BLUETOOTH) {
+ if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_number_with_prefix);
}
@@ -680,6 +689,7 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
0x4654, // Oceanic Veo 4.0
0x4655, // Sherwood Wisdom 4
0x4656, // Oceanic Pro Plus 4
+ 0x4742, // Sherwood Beacon
0x4743, // Aqualung i470TC
};
@@ -856,7 +866,7 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params)
{
- if (descriptor == NULL || descriptor->filter == NULL)
+ if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL)
return 1;
return descriptor->filter (transport, userdata, params);
diff --git a/src/device.c b/src/device.c
index cb19c8e..991937c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -59,6 +59,7 @@
#include "tecdiving_divecomputereu.h"
#include "mclean_extreme.h"
#include "liquivision_lynx.h"
+#include "sporasub_sp2.h"
// Not merged upstream yet
#include "garmin.h"
@@ -224,6 +225,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_LIQUIVISION_LYNX:
rc = liquivision_lynx_device_open (&device, context, iostream);
break;
+ case DC_FAMILY_SPORASUB_SP2:
+ rc = sporasub_sp2_device_open (&device, context, iostream);
+ break;
default:
return DC_STATUS_INVALIDARGS;
diff --git a/src/irda.c b/src/irda.c
index 2cc58ab..8c5d612 100644
--- a/src/irda.c
+++ b/src/irda.c
@@ -202,11 +202,7 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_
// modified by the previous getsockopt call.
size = sizeof (data);
-#ifdef _WIN32
- Sleep (1000);
-#else
- sleep (1);
-#endif
+ dc_platform_sleep (1000);
}
S_CLOSE (fd);
diff --git a/src/mclean_extreme.c b/src/mclean_extreme.c
index 5c8060c..b69546f 100644
--- a/src/mclean_extreme.c
+++ b/src/mclean_extreme.c
@@ -42,7 +42,7 @@
#define CMD_FIRMWARE 0xAD
#define SZ_PACKET 512
-#define SZ_FINGERPRINT 7
+#define SZ_FINGERPRINT 4
#define SZ_CFG 0x002D
#define SZ_COMPUTER (SZ_CFG + 0x6A)
#define SZ_HEADER (SZ_CFG + 0x31)
@@ -580,10 +580,10 @@ mclean_extreme_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback
unsigned char *data = dc_buffer_get_data(buffer);
unsigned int size = dc_buffer_get_size(buffer);
- if (memcmp(data, device->fingerprint, sizeof(device->fingerprint)) == 0)
+ if (memcmp(data + SZ_CFG, device->fingerprint, sizeof(device->fingerprint)) == 0)
break;
- if (callback && !callback (data, size, data, sizeof(device->fingerprint), userdata)) {
+ if (callback && !callback (data, size, data + SZ_CFG, sizeof(device->fingerprint), userdata)) {
break;
}
}
diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c
index 333b994..26f5c83 100644
--- a/src/oceanic_atom2.c
+++ b/src/oceanic_atom2.c
@@ -41,6 +41,7 @@
#define SAGE 0x4647
#define I770R 0x4651
#define GEO40 0x4653
+#define BEACON 0x4742
#define MAXPACKET 256
#define MAXRETRIES 2
@@ -493,6 +494,7 @@ static const oceanic_common_version_t versions[] = {
{"OCEANVTX \0\0 2048", 0, &aeris_a300cs_layout},
{"AQUAI750 \0\0 2048", 0, &aeris_a300cs_layout},
{"SWDRAGON \0\0 2048", 0, &aeris_a300cs_layout},
+ {"SWBEACON \0\0 2048", 0, &aeris_a300cs_layout},
{"AQUAI450 \0\0 2048", 0, &aqualung_i450t_layout},
@@ -904,7 +906,7 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
}
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE &&
- model != PROPLUSX && model != SAGE ) {
+ model != PROPLUSX && model != SAGE && model != BEACON) {
status = oceanic_atom2_ble_handshake(device);
if (status != DC_STATUS_SUCCESS) {
goto error_free;
diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index 0f58e4c..64be773 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -99,6 +99,7 @@
#define VEO40 0x4654
#define WISDOM4 0x4655
#define PROPLUS4 0x4656
+#define BEACON 0x4742
#define I470TC 0x4743
#define NORMAL 0
@@ -192,7 +193,8 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
parser->footersize = 0;
} else if (model == A300CS || model == VTX ||
model == I450T || model == I750TC ||
- model == I770R || model == SAGE) {
+ model == I770R || model == SAGE ||
+ model == BEACON) {
parser->headersize = 5 * PAGESIZE;
} else if (model == PROPLUSX) {
parser->headersize = 3 * PAGESIZE;
@@ -347,6 +349,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
case PROPLUSX:
case I770R:
case SAGE:
+ case BEACON:
datetime->year = (p[10]) + 2000;
datetime->month = (p[8]);
datetime->day = (p[9]);
@@ -468,7 +471,8 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
he_offset = 0x48;
ngasmixes = 6;
} else if (parser->model == A300CS || parser->model == VTX ||
- parser->model == I750TC || parser->model == SAGE) {
+ parser->model == I750TC || parser->model == SAGE ||
+ parser->model == BEACON) {
o2_offset = 0x2A;
if (data[0x39] & 0x04) {
ngasmixes = 1;
@@ -690,7 +694,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
if (parser->model == A300CS || parser->model == VTX ||
parser->model == I450T || parser->model == I750TC ||
parser->model == PROPLUSX || parser->model == I770R ||
- parser->model == SAGE)
+ parser->model == SAGE || parser->model == BEACON)
idx = 0x1f;
switch (data[idx] & 0x03) {
case 0:
@@ -747,7 +751,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == VTX || parser->model == I450T ||
parser->model == I750TC || parser->model == PROPLUSX ||
parser->model == I770R || parser->model == I470TC ||
- parser->model == SAGE) {
+ parser->model == SAGE || parser->model == BEACON) {
samplesize = PAGESIZE;
}
@@ -833,7 +837,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
pressure = (((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF);
} else if (parser->model == A300CS || parser->model == VTX ||
parser->model == I750TC || parser->model == I770R ||
- parser->model == SAGE) {
+ parser->model == SAGE || parser->model == BEACON) {
// Tank pressure (1 psi) and number (one based index)
tank = (data[offset + 1] & 0x03) - 1;
pressure = ((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF;
@@ -935,7 +939,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
temperature = ((data[offset + 7] & 0xF0) >> 4) | ((data[offset + 7] & 0x0C) << 2) | ((data[offset + 5] & 0x0C) << 4);
} else if (parser->model == A300CS || parser->model == VTX ||
parser->model == I750TC || parser->model == PROPLUSX ||
- parser->model == I770R|| parser->model == SAGE) {
+ parser->model == I770R|| parser->model == SAGE ||
+ parser->model == BEACON) {
temperature = data[offset + 11];
} else {
unsigned int sign;
@@ -980,7 +985,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
else if (parser->model == TX1 || parser->model == A300CS ||
parser->model == VTX || parser->model == I750TC ||
parser->model == PROPLUSX || parser->model == I770R ||
- parser->model == SAGE)
+ parser->model == SAGE || parser->model == BEACON)
pressure = array_uint16_le (data + offset + 4);
else
pressure -= data[offset + 1];
@@ -1032,7 +1037,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
unsigned int decostop = 0, decotime = 0;
if (parser->model == A300CS || parser->model == VTX ||
parser->model == I750TC || parser->model == SAGE ||
- parser->model == PROPLUSX || parser->model == I770R) {
+ parser->model == PROPLUSX || parser->model == I770R ||
+ parser->model == BEACON) {
decostop = (data[offset + 15] & 0x70) >> 4;
decotime = array_uint16_le(data + offset + 6) & 0x03FF;
have_deco = 1;
diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c
index d09b2f9..fff7389 100644
--- a/src/oceanic_vtpro_parser.c
+++ b/src/oceanic_vtpro_parser.c
@@ -181,7 +181,7 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
maxdepth = data[footer + 1];
} else {
oxygen = data[footer + 3];
- maxdepth = array_uint16_le(data + footer + 0) & 0x0FFF;
+ maxdepth = array_uint16_le(data + footer + 0) & 0x01FF;
}
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
@@ -359,6 +359,21 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
+ // NDL / Deco
+ if (parser->model != AERIS500AI) {
+ unsigned int decostop = (data[offset + 5] & 0xF0) >> 4;
+ unsigned int decotime = array_uint16_le(data + offset + 4) & 0x0FFF;
+ if (decostop) {
+ sample.deco.type = DC_DECO_DECOSTOP;
+ sample.deco.depth = decostop * 10 * FEET;
+ } else {
+ sample.deco.type = DC_DECO_NDL;
+ sample.deco.depth = 0.0;
+ }
+ sample.deco.time = decotime * 60;
+ if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
+ }
+
offset += PAGESIZE / 2;
}
diff --git a/src/parser.c b/src/parser.c
index 4a6d0a6..f1e1ebc 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -59,6 +59,7 @@
#include "tecdiving_divecomputereu.h"
#include "mclean_extreme.h"
#include "liquivision_lynx.h"
+#include "sporasub_sp2.h"
// Not merged upstream yet
#include "garmin.h"
@@ -185,6 +186,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_LIQUIVISION_LYNX:
rc = liquivision_lynx_parser_create (&parser, context, model);
break;
+ case DC_FAMILY_SPORASUB_SP2:
+ rc = sporasub_sp2_parser_create (&parser, context);
+ break;
default:
return DC_STATUS_INVALIDARGS;
diff --git a/src/platform.c b/src/platform.c
new file mode 100644
index 0000000..16a3c08
--- /dev/null
+++ b/src/platform.c
@@ -0,0 +1,51 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2021 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 _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#include
+#else
+#include
+#include
+#endif
+
+#include "platform.h"
+
+int
+dc_platform_sleep (unsigned int milliseconds)
+{
+#ifdef _WIN32
+ Sleep (milliseconds);
+#else
+ struct timespec ts;
+ ts.tv_sec = (milliseconds / 1000);
+ ts.tv_nsec = (milliseconds % 1000) * 1000000;
+
+ while (nanosleep (&ts, &ts) != 0) {
+ if (errno != EINTR ) {
+ return -1;
+ }
+ }
+#endif
+
+ return 0;
+}
diff --git a/src/platform.h b/src/platform.h
index ab82fb7..438aa7d 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -45,6 +45,8 @@ extern "C" {
#endif
#endif
+int dc_platform_sleep(unsigned int milliseconds);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/src/serial_posix.c b/src/serial_posix.c
index 4fbcb96..8872982 100644
--- a/src/serial_posix.c
+++ b/src/serial_posix.c
@@ -30,7 +30,6 @@
#include // fcntl
#include // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak
#include // ioctl
-#include // nanosleep
#ifdef HAVE_LINUX_SERIAL_H
#include
#endif
@@ -59,6 +58,7 @@
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
+#include "platform.h"
#include "timer.h"
#define DIRNAME "/dev"
@@ -995,16 +995,10 @@ dc_serial_get_lines (dc_iostream_t *abstract, unsigned int *value)
static dc_status_t
dc_serial_sleep (dc_iostream_t *abstract, unsigned int timeout)
{
- struct timespec ts;
- ts.tv_sec = (timeout / 1000);
- ts.tv_nsec = (timeout % 1000) * 1000000;
-
- while (nanosleep (&ts, &ts) != 0) {
+ if (dc_platform_sleep (timeout) != 0) {
int errcode = errno;
- if (errcode != EINTR ) {
- SYSERROR (abstract->context, errcode);
- return syserror (errcode);
- }
+ SYSERROR (abstract->context, errcode);
+ return syserror (errcode);
}
return DC_STATUS_SUCCESS;
diff --git a/src/serial_win32.c b/src/serial_win32.c
index 2b009f0..aeeeb83 100644
--- a/src/serial_win32.c
+++ b/src/serial_win32.c
@@ -32,6 +32,7 @@
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
+#include "platform.h"
static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item);
static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator);
@@ -836,7 +837,11 @@ dc_serial_get_lines (dc_iostream_t *abstract, unsigned int *value)
static dc_status_t
dc_serial_sleep (dc_iostream_t *abstract, unsigned int timeout)
{
- Sleep (timeout);
+ if (dc_platform_sleep (timeout) != 0) {
+ DWORD errcode = GetLastError ();
+ SYSERROR (abstract->context, errcode);
+ return syserror (errcode);
+ }
return DC_STATUS_SUCCESS;
}
diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c
index 9d03d6e..e142dd0 100644
--- a/src/shearwater_petrel.c
+++ b/src/shearwater_petrel.c
@@ -238,6 +238,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
case 0x0C0C:
case 0x0C0D:
case 0x0D0D:
+ case 0x7C2D:
model = PERDIXAI;
break;
case 0x0F0F:
diff --git a/src/socket.c b/src/socket.c
index 84d58a4..a99a8f2 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -20,6 +20,7 @@
*/
#include "socket.h"
+#include "platform.h"
#include "common-private.h"
#include "context-private.h"
@@ -357,21 +358,11 @@ dc_socket_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size
dc_status_t
dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout)
{
-#ifdef _WIN32
- Sleep (timeout);
-#else
- struct timespec ts;
- ts.tv_sec = (timeout / 1000);
- ts.tv_nsec = (timeout % 1000) * 1000000;
-
- while (nanosleep (&ts, &ts) != 0) {
- int errcode = errno;
- if (errcode != EINTR ) {
- SYSERROR (abstract->context, errcode);
- return dc_socket_syserror (errcode);
- }
+ if (dc_platform_sleep (timeout) != 0) {
+ s_errcode_t errcode = S_ERRNO;
+ SYSERROR (abstract->context, errcode);
+ return dc_socket_syserror(errcode);
}
-#endif
return DC_STATUS_SUCCESS;
}
diff --git a/src/sporasub_sp2.c b/src/sporasub_sp2.c
new file mode 100644
index 0000000..c0f6b49
--- /dev/null
+++ b/src/sporasub_sp2.c
@@ -0,0 +1,489 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2021 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 // memcpy, memcmp
+#include // malloc, free
+
+#include "sporasub_sp2.h"
+#include "context-private.h"
+#include "device-private.h"
+#include "checksum.h"
+#include "array.h"
+
+#define ISINSTANCE(device) dc_device_isinstance((device), &sporasub_sp2_device_vtable)
+
+#define SZ_MEMORY 0x10000
+
+#define RB_PROFILE_BEGIN 0x0060
+#define RB_PROFILE_END SZ_MEMORY
+
+#define MAXRETRIES 4
+#define MAXPACKET 256
+
+#define HEADER_HI 0xA0
+#define HEADER_LO 0xA2
+#define TRAILER_HI 0xB0
+#define TRAILER_LO 0xB3
+
+#define CMD_VERSION 0x10
+#define CMD_READ 0x12
+#define CMD_TIMESYNC 0x39
+
+#define SZ_VERSION 23
+#define SZ_READ 128
+
+#define SZ_HEADER 32
+#define SZ_SAMPLE 4
+
+typedef struct sporasub_sp2_device_t {
+ dc_device_t base;
+ dc_iostream_t *iostream;
+ unsigned char version[SZ_VERSION];
+ unsigned char fingerprint[6];
+} sporasub_sp2_device_t;
+
+static dc_status_t sporasub_sp2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
+static dc_status_t sporasub_sp2_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
+static dc_status_t sporasub_sp2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
+static dc_status_t sporasub_sp2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
+static dc_status_t sporasub_sp2_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
+
+static const dc_device_vtable_t sporasub_sp2_device_vtable = {
+ sizeof(sporasub_sp2_device_t),
+ DC_FAMILY_SPORASUB_SP2,
+ sporasub_sp2_device_set_fingerprint, /* set_fingerprint */
+ sporasub_sp2_device_read, /* read */
+ NULL, /* write */
+ sporasub_sp2_device_dump, /* dump */
+ sporasub_sp2_device_foreach, /* foreach */
+ sporasub_sp2_device_timesync, /* timesync */
+ NULL /* close */
+};
+
+static unsigned int
+iceil (unsigned int x, unsigned int n)
+{
+ // Round up to next higher multiple.
+ return ((x + n - 1) / n) * n;
+}
+
+static dc_status_t
+sporasub_sp2_send (sporasub_sp2_device_t *device, unsigned char command, const unsigned char data[], unsigned int size)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ if (size > MAXPACKET) {
+ return DC_STATUS_INVALIDARGS;
+ }
+
+ unsigned int len = size + 1;
+ unsigned int csum = checksum_add_uint16 (data, size, command);
+
+ unsigned char packet[MAXPACKET + 9] = {0};
+ packet[0] = HEADER_HI;
+ packet[1] = HEADER_LO;
+ packet[2] = (len >> 8) & 0xFF;
+ packet[3] = (len ) & 0xFF;
+ packet[4] = command;
+ if (size) {
+ memcpy(packet + 5, data, size);
+ }
+ packet[size + 5] = (csum >> 8) & 0xFF;
+ packet[size + 6] = (csum ) & 0xFF;
+ packet[size + 7] = TRAILER_HI;
+ packet[size + 8] = TRAILER_LO;
+
+ // Send the command to the device.
+ status = dc_iostream_write (device->iostream, packet, size + 9, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ return status;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+sporasub_sp2_receive (sporasub_sp2_device_t *device, unsigned char command, unsigned char data[], unsigned int size)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ if (size > MAXPACKET) {
+ return DC_STATUS_INVALIDARGS;
+ }
+
+ // Receive the answer of the device.
+ unsigned char packet[MAXPACKET + 9] = {0};
+ status = dc_iostream_read (device->iostream, packet, size + 9, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the answer.");
+ return status;
+ }
+
+ // Verify the header and trailer of the packet.
+ if (packet[0] != HEADER_HI || packet[1] != HEADER_LO ||
+ packet[size + 7] != TRAILER_HI || packet[size + 8] != TRAILER_LO) {
+ ERROR (abstract->context, "Unexpected answer header/trailer byte.");
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Verify the packet length.
+ unsigned int len = array_uint16_be (packet + 2);
+ if (len != size + 1) {
+ ERROR (abstract->context, "Unexpected packet length.");
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Verify the command byte.
+ if (packet[4] != command) {
+ ERROR (abstract->context, "Unexpected answer header/trailer byte.");
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Verify the checksum of the packet.
+ unsigned short crc = array_uint16_be (packet + size + 5);
+ unsigned short ccrc = checksum_add_uint16 (packet + 4, size + 1, 0);
+ if (crc != ccrc) {
+ ERROR (abstract->context, "Unexpected answer checksum.");
+ return DC_STATUS_PROTOCOL;
+ }
+
+ if (size) {
+ memcpy (data, packet + 5, size);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+sporasub_sp2_packet (sporasub_sp2_device_t *device, unsigned char cmd, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ if (device_is_cancelled (abstract))
+ return DC_STATUS_CANCELLED;
+
+ // Send the command to the device.
+ status = sporasub_sp2_send (device, cmd, command, csize);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ return status;
+ }
+
+ // Receive the answer of the device.
+ status = sporasub_sp2_receive (device, cmd + 1, answer, asize);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the answer.");
+ return status;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+sporasub_sp2_transfer (sporasub_sp2_device_t *device, unsigned char cmd, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
+{
+ unsigned int nretries = 0;
+ dc_status_t rc = DC_STATUS_SUCCESS;
+ while ((rc = sporasub_sp2_packet (device, cmd, command, csize, answer, asize)) != 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;
+
+ // Discard any garbage bytes.
+ dc_iostream_sleep (device->iostream, 100);
+ dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
+ }
+
+ return rc;
+}
+
+dc_status_t
+sporasub_sp2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ sporasub_sp2_device_t *device = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ device = (sporasub_sp2_device_t *) dc_device_allocate (context, &sporasub_sp2_device_vtable);
+ if (device == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Set the default values.
+ device->iostream = iostream;
+ memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+ // Set the serial communication protocol (460800 8N1).
+ status = dc_iostream_configure (device->iostream, 460800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the terminal attributes.");
+ goto error_free;
+ }
+
+ // Set the timeout for receiving data (1000 ms).
+ status = dc_iostream_set_timeout (device->iostream, 1000);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the timeout.");
+ goto error_free;
+ }
+
+ // Clear the RTS line.
+ status = dc_iostream_set_rts (device->iostream, 0);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to clear the RTS line.");
+ goto error_free;
+ }
+
+ // Set the DTR line.
+ status = dc_iostream_set_dtr (device->iostream, 1);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the DTR line.");
+ goto error_free;
+ }
+
+ dc_iostream_sleep (device->iostream, 100);
+ dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
+
+ // Read the version packet.
+ status = sporasub_sp2_packet(device, CMD_VERSION, NULL, 0, device->version, sizeof(device->version));
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to read the version packet.");
+ 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
+sporasub_sp2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
+{
+ sporasub_sp2_device_t *device = (sporasub_sp2_device_t *) abstract;
+
+ if (size && size != sizeof (device->fingerprint))
+ return DC_STATUS_INVALIDARGS;
+
+ if (size)
+ memcpy (device->fingerprint, data, sizeof (device->fingerprint));
+ else
+ memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+sporasub_sp2_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ sporasub_sp2_device_t *device = (sporasub_sp2_device_t *) abstract;
+
+ unsigned int nbytes = 0;
+ while (nbytes < size) {
+ // Calculate the packet size.
+ unsigned int len = size - nbytes;
+ if (len > SZ_READ)
+ len = SZ_READ;
+
+ // Build the raw command.
+ unsigned char command[] = {
+ (address ) & 0xFF,
+ (address >> 8) & 0xFF,
+ len};
+
+ // Send the command and receive the answer.
+ status = sporasub_sp2_transfer (device, CMD_READ, command, sizeof(command), data + nbytes, len);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ nbytes += len;
+ address += len;
+ data += len;
+ }
+
+ return status;
+}
+
+static dc_status_t
+sporasub_sp2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
+{
+ // Allocate the required amount of memory.
+ if (!dc_buffer_resize (buffer, SZ_MEMORY)) {
+ ERROR (abstract->context, "Insufficient buffer space available.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ return device_dump_read (abstract, dc_buffer_get_data (buffer),
+ dc_buffer_get_size (buffer), SZ_READ);
+}
+
+static dc_status_t
+sporasub_sp2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ sporasub_sp2_device_t *device = (sporasub_sp2_device_t *) abstract;
+
+ // Emit a device info event.
+ dc_event_devinfo_t devinfo;
+ devinfo.model = 0;
+ devinfo.firmware = 0;
+ devinfo.serial = array_uint16_be (device->version + 1);
+ device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
+
+ // Emit a vendor event.
+ dc_event_vendor_t vendor;
+ vendor.data = device->version;
+ vendor.size = sizeof (device->version);
+ device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
+
+ dc_buffer_t *buffer = dc_buffer_new (SZ_MEMORY);
+ if (buffer == NULL) {
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ status = sporasub_sp2_device_dump (abstract, buffer);
+ if (status != DC_STATUS_SUCCESS) {
+ goto error_free_buffer;
+ }
+
+ unsigned char *data = dc_buffer_get_data (buffer);
+
+ // Get the number of dives.
+ unsigned int ndives = array_uint16_le (data + 0x02);
+
+ // Get the profile pointer.
+ unsigned int eop = array_uint16_le (data + 0x04);
+ if (eop < RB_PROFILE_BEGIN || eop > RB_PROFILE_END) {
+ ERROR (abstract->context, "Invalid profile pointer (0x%04x).", eop);
+ status = DC_STATUS_DATAFORMAT;
+ goto error_free_buffer;
+ }
+
+ unsigned short *logbook = (unsigned short *) malloc(ndives * sizeof (unsigned short));
+ if (logbook == NULL) {
+ ERROR (abstract->context, "Out of memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free_buffer;
+ }
+
+ // Find all dives.
+ unsigned int count = 0;
+ unsigned int address = RB_PROFILE_BEGIN;
+ while (address + SZ_HEADER <= RB_PROFILE_END && count < ndives) {
+ if (address == eop) {
+ WARNING (abstract->context, "Reached end of profile pointer.");
+ break;
+ }
+
+ // Get the dive length.
+ unsigned int nsamples = array_uint16_le (data + address);
+ unsigned int length = SZ_HEADER + nsamples * SZ_SAMPLE;
+ if (address + length > RB_PROFILE_END) {
+ WARNING (abstract->context, "Reached end of memory.");
+ break;
+ }
+
+ // Store the address.
+ logbook[count] = address;
+ count++;
+
+ // The start of the next dive is always aligned to 32 bytes.
+ address += iceil (length, SZ_HEADER);
+ }
+
+ // Process the dives in reverse order (newest first).
+ for (unsigned int i = 0; i < count; ++i) {
+ unsigned int idx = count - 1 - i;
+ unsigned int offset = logbook[idx];
+
+ // Get the dive length.
+ unsigned int nsamples = array_uint16_le (data + offset);
+ unsigned int length = SZ_HEADER + nsamples * SZ_SAMPLE;
+
+ // Check the fingerprint data.
+ if (memcmp (data + offset + 2, device->fingerprint, sizeof (device->fingerprint)) == 0)
+ break;
+
+ if (callback && !callback (data + offset, length, data + offset + 2, sizeof (device->fingerprint), userdata)) {
+ break;
+ }
+ }
+
+ free (logbook);
+error_free_buffer:
+ dc_buffer_free (buffer);
+error_exit:
+ return status;
+}
+
+
+static dc_status_t
+sporasub_sp2_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ sporasub_sp2_device_t *device = (sporasub_sp2_device_t *) abstract;
+
+ if (datetime == NULL || datetime->year < 2000) {
+ ERROR (abstract->context, "Invalid parameter specified.");
+ return DC_STATUS_INVALIDARGS;
+ }
+
+ // Build the raw command.
+ unsigned char command[] = {
+ datetime->year - 2000,
+ datetime->month,
+ datetime->day,
+ datetime->hour,
+ datetime->minute,
+ datetime->second};
+
+ // Send the command and receive the answer.
+ unsigned char answer[1] = {0};
+ status = sporasub_sp2_transfer (device, CMD_TIMESYNC, command, sizeof(command), answer, sizeof(answer));
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ // Verify the response code.
+ if (answer[0] != 0) {
+ ERROR (abstract->context, "Invalid response code 0x%02x returned.", answer[0]);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/sporasub_sp2.h b/src/sporasub_sp2.h
new file mode 100644
index 0000000..de2ca42
--- /dev/null
+++ b/src/sporasub_sp2.h
@@ -0,0 +1,43 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2021 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 SPORASUB_SP2_H
+#define SPORASUB_SP2_H
+
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+dc_status_t
+sporasub_sp2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
+
+dc_status_t
+sporasub_sp2_parser_create (dc_parser_t **parser, dc_context_t *context);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* SPORASUB_SP2_H */
diff --git a/src/sporasub_sp2_parser.c b/src/sporasub_sp2_parser.c
new file mode 100644
index 0000000..e036dd2
--- /dev/null
+++ b/src/sporasub_sp2_parser.c
@@ -0,0 +1,200 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2021 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 "sporasub_sp2.h"
+#include "context-private.h"
+#include "parser-private.h"
+#include "array.h"
+
+#define ISINSTANCE(parser) dc_device_isinstance((parser), &sporasub_sp2_parser_vtable)
+
+#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
+
+#define SZ_HEADER 0x20
+#define SZ_SAMPLE 0x04
+
+typedef struct sporasub_sp2_parser_t sporasub_sp2_parser_t;
+
+struct sporasub_sp2_parser_t {
+ dc_parser_t base;
+};
+
+static dc_status_t sporasub_sp2_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
+static dc_status_t sporasub_sp2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
+static dc_status_t sporasub_sp2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
+static dc_status_t sporasub_sp2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
+
+static const dc_parser_vtable_t sporasub_sp2_parser_vtable = {
+ sizeof(sporasub_sp2_parser_t),
+ DC_FAMILY_SPORASUB_SP2,
+ sporasub_sp2_parser_set_data, /* set_data */
+ sporasub_sp2_parser_get_datetime, /* datetime */
+ sporasub_sp2_parser_get_field, /* fields */
+ sporasub_sp2_parser_samples_foreach, /* samples_foreach */
+ NULL /* destroy */
+};
+
+
+dc_status_t
+sporasub_sp2_parser_create (dc_parser_t **out, dc_context_t *context)
+{
+ sporasub_sp2_parser_t *parser = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ parser = (sporasub_sp2_parser_t *) dc_parser_allocate (context, &sporasub_sp2_parser_vtable);
+ if (parser == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ *out = (dc_parser_t *) parser;
+
+ return DC_STATUS_SUCCESS;
+}
+
+
+static dc_status_t
+sporasub_sp2_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
+{
+ return DC_STATUS_SUCCESS;
+}
+
+
+static dc_status_t
+sporasub_sp2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
+{
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ if (size < SZ_HEADER)
+ return DC_STATUS_DATAFORMAT;
+
+ if (datetime) {
+ datetime->year = data[4] + 2000;
+ datetime->month = data[3];
+ datetime->day = data[2];
+ datetime->hour = data[7];
+ datetime->minute = data[6];
+ datetime->second = data[5];
+ datetime->timezone = DC_TIMEZONE_NONE;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+
+static dc_status_t
+sporasub_sp2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
+{
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ if (size < SZ_HEADER)
+ return DC_STATUS_DATAFORMAT;
+
+ if (value) {
+ switch (type) {
+ case DC_FIELD_DIVETIME:
+ *((unsigned int *) value) = data[0x08] + data[0x09] * 60;
+ break;
+ case DC_FIELD_MAXDEPTH:
+ *((double *) value) = array_uint16_le (data + 0x14) / 100.0;
+ break;
+ case DC_FIELD_DIVEMODE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
+ break;
+ case DC_FIELD_TEMPERATURE_MINIMUM:
+ *((double *) value) = array_uint16_le (data + 0x18) / 10.0;
+ break;
+ case DC_FIELD_TEMPERATURE_MAXIMUM:
+ *((double *) value) = array_uint16_le (data + 0x16) / 10.0;
+ break;
+ default:
+ return DC_STATUS_UNSUPPORTED;
+ }
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+
+static dc_status_t
+sporasub_sp2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
+{
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ if (size < SZ_HEADER)
+ return DC_STATUS_DATAFORMAT;
+
+ unsigned int nsamples = array_uint16_le(data);
+
+ // Get the sample interval.
+ unsigned int interval_idx = data[0x1A];
+ const unsigned int intervals[] = {1, 2, 5, 10};
+ if (interval_idx >= C_ARRAY_SIZE(intervals)) {
+ ERROR (abstract->context, "Invalid sample interval index %u", interval_idx);
+ return DC_STATUS_DATAFORMAT;
+ }
+ unsigned int interval = intervals[interval_idx];
+
+ unsigned int time = 0;
+ unsigned int count = 0;
+ unsigned int offset = SZ_HEADER;
+ while (offset + SZ_SAMPLE <= size && count < nsamples) {
+ dc_sample_value_t sample = {0};
+
+ unsigned int value = array_uint32_le (data + offset);
+ unsigned int heartrate = (value & 0xFF000000) >> 24;
+ unsigned int temperature = (value & 0x00FFC000) >> 14;
+ unsigned int unknown = (value & 0x00003000) >> 12;
+ unsigned int depth = (value & 0x00000FFF) >> 0;
+
+ // Time (seconds)
+ time += interval;
+ sample.time = time;
+ if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
+
+ // Depth (1/100 m)
+ sample.depth = depth / 100.0;
+ if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
+
+ // Temperature (1/10 °C)
+ sample.temperature = temperature / 10.0 - 20.0;
+ if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
+
+ // Heartrate
+ if (heartrate) {
+ sample.heartbeat = heartrate;
+ if (callback) callback (DC_SAMPLE_HEARTBEAT, sample, userdata);
+ }
+
+ offset += SZ_SAMPLE;
+ count++;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/suunto_d9.c b/src/suunto_d9.c
index 8b8320e..17f7e7f 100644
--- a/src/suunto_d9.c
+++ b/src/suunto_d9.c
@@ -33,13 +33,14 @@
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-#define D4i 0x19
-#define D6i 0x1A
-#define D9tx 0x1B
-#define DX 0x1C
-#define VYPERNOVO 0x1D
-#define ZOOPNOVO 0x1E
-#define D4F 0x20
+#define D4i 0x19
+#define D6i 0x1A
+#define D9tx 0x1B
+#define DX 0x1C
+#define VYPERNOVO 0x1D
+#define ZOOPNOVO_A 0x1E
+#define ZOOPNOVO_B 0x1F
+#define D4F 0x20
typedef struct suunto_d9_device_t {
suunto_common2_device_t base;
@@ -100,7 +101,7 @@ suunto_d9_device_autodetect (suunto_d9_device_t *device, unsigned int model)
// Use the model number as a hint to speedup the detection.
unsigned int hint = 0;
if (model == D4i || model == D6i || model == D9tx ||
- model == DX || model == VYPERNOVO || model == ZOOPNOVO ||
+ model == DX || model == VYPERNOVO || model == ZOOPNOVO_A || model == ZOOPNOVO_B ||
model == D4F)
hint = 1;
@@ -184,7 +185,7 @@ suunto_d9_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *
// Override the base class values.
model = device->base.version[0];
if (model == D4i || model == D6i || model == D9tx ||
- model == VYPERNOVO || model == ZOOPNOVO ||
+ model == VYPERNOVO || model == ZOOPNOVO_A || model == ZOOPNOVO_B ||
model == D4F)
device->base.layout = &suunto_d9tx_layout;
else if (model == DX)
diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c
index 933f7bf..061dd8e 100644
--- a/src/suunto_d9_parser.c
+++ b/src/suunto_d9_parser.c
@@ -33,21 +33,22 @@
#define MAXPARAMS 3
#define NGASMIXES 11
-#define D9 0x0E
-#define D6 0x0F
-#define VYPER2 0x10
-#define COBRA2 0x11
-#define D4 0x12
-#define VYPERAIR 0x13
-#define COBRA3 0x14
-#define HELO2 0x15
-#define D4i 0x19
-#define D6i 0x1A
-#define D9tx 0x1B
-#define DX 0x1C
-#define VYPERNOVO 0x1D
-#define ZOOPNOVO 0x1E
-#define D4F 0x20
+#define D9 0x0E
+#define D6 0x0F
+#define VYPER2 0x10
+#define COBRA2 0x11
+#define D4 0x12
+#define VYPERAIR 0x13
+#define COBRA3 0x14
+#define HELO2 0x15
+#define D4i 0x19
+#define D6i 0x1A
+#define D9tx 0x1B
+#define DX 0x1C
+#define VYPERNOVO 0x1D
+#define ZOOPNOVO_A 0x1E
+#define ZOOPNOVO_B 0x1F
+#define D4F 0x20
#define ID_D6I_V1_MIX2 0x1871C062
#define ID_D6I_V1_MIX3 0x1871C063
@@ -144,8 +145,8 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
gasmode_offset = 0x1F;
gasmix_offset = 0x54;
gasmix_count = 8;
- } else if (parser->model == D4i || parser->model == ZOOPNOVO ||
- parser->model == D4F) {
+ } else if (parser->model == D4i || parser->model == ZOOPNOVO_A ||
+ parser->model == ZOOPNOVO_B || parser->model == D4F) {
gasmode_offset = 0x1D;
if (id == ID_D4I_V2)
gasmix_offset = 0x67;
@@ -182,8 +183,9 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
config += 1;
} else if (parser->model == HELO2 || parser->model == D4i ||
parser->model == D6i || parser->model == D9tx ||
- parser->model == DX || parser->model == ZOOPNOVO ||
- parser->model == VYPERNOVO || parser->model == D4F) {
+ parser->model == DX || parser->model == ZOOPNOVO_A ||
+ parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO ||
+ parser->model == D4F) {
config = gasmix_offset + gasmix_count * 6;
}
if (config + 1 > size)
@@ -206,8 +208,9 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
for (unsigned int i = 0; i < gasmix_count; ++i) {
if (parser->model == HELO2 || parser->model == D4i ||
parser->model == D6i || parser->model == D9tx ||
- parser->model == DX || parser->model == ZOOPNOVO ||
- parser->model == VYPERNOVO || parser->model == D4F) {
+ parser->model == DX || parser->model == ZOOPNOVO_A ||
+ parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO ||
+ parser->model == D4F) {
parser->oxygen[i] = data[gasmix_offset + 6 * i + 1];
parser->helium[i] = data[gasmix_offset + 6 * i + 2];
} else {
@@ -224,8 +227,9 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
if (parser->model == HELO2) {
parser->gasmix = data[0x26];
} else if (parser->model == D4i || parser->model == D6i ||
- parser->model == D9tx || parser->model == ZOOPNOVO ||
- parser->model == VYPERNOVO || parser->model == D4F) {
+ parser->model == D9tx || parser->model == ZOOPNOVO_A ||
+ parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO ||
+ parser->model == D4F) {
if (id == ID_D4I_V2 || id == ID_D6I_V2) {
parser->gasmix = data[0x2D];
} else {
@@ -312,8 +316,9 @@ suunto_d9_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
if (parser->model == HELO2 || parser->model == DX)
offset = 0x17;
else if (parser->model == D4i || parser->model == D6i ||
- parser->model == D9tx || parser->model == ZOOPNOVO ||
- parser->model == VYPERNOVO || parser->model == D4F)
+ parser->model == D9tx || parser->model == ZOOPNOVO_A ||
+ parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO ||
+ parser->model == D4F)
offset = 0x13;
if (abstract->size < offset + 7)
@@ -324,8 +329,8 @@ suunto_d9_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
if (datetime) {
if (parser->model == D4i || parser->model == D6i ||
parser->model == D9tx || parser->model == DX ||
- parser->model == ZOOPNOVO || parser->model == VYPERNOVO ||
- parser->model == D4F) {
+ parser->model == ZOOPNOVO_A || parser->model == ZOOPNOVO_B ||
+ parser->model == VYPERNOVO || parser->model == D4F) {
datetime->year = p[0] + (p[1] << 8);
datetime->month = p[2];
datetime->day = p[3];
@@ -371,8 +376,8 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
*((unsigned int *) value) = array_uint16_le (data + 0x0B);
else if (parser->model == D4i || parser->model == D6i ||
parser->model == D9tx || parser->model == DX ||
- parser->model == ZOOPNOVO || parser->model == VYPERNOVO ||
- parser->model == D4F)
+ parser->model == ZOOPNOVO_A || parser->model == ZOOPNOVO_B ||
+ parser->model == VYPERNOVO || parser->model == D4F)
*((unsigned int *) value) = array_uint16_le (data + 0x0D);
else if (parser->model == HELO2)
*((unsigned int *) value) = array_uint16_le (data + 0x0D) * 60;
@@ -494,8 +499,8 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca
unsigned int interval_sample_offset = 0x18;
if (parser->model == HELO2 || parser->model == D4i ||
parser->model == D6i || parser->model == D9tx ||
- parser->model == ZOOPNOVO || parser->model == VYPERNOVO ||
- parser->model == D4F)
+ parser->model == ZOOPNOVO_A || parser->model == ZOOPNOVO_B ||
+ parser->model == VYPERNOVO || parser->model == D4F)
interval_sample_offset = 0x1E;
else if (parser->model == DX)
interval_sample_offset = 0x22;
diff --git a/src/uwatec_aladin.c b/src/uwatec_aladin.c
index 3ebd094..1cfec54 100644
--- a/src/uwatec_aladin.c
+++ b/src/uwatec_aladin.c
@@ -96,8 +96,8 @@ uwatec_aladin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
goto error_free;
}
- // Set the timeout for receiving data (INFINITE).
- status = dc_iostream_set_timeout (device->iostream, -1);
+ // Set the timeout for receiving data (3000ms).
+ status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
@@ -164,20 +164,24 @@ uwatec_aladin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
unsigned char answer[SZ_MEMORY + 2] = {0};
// Receive the header of the package.
- for (unsigned int i = 0; i < 4;) {
+ unsigned int i = 0;
+ while (i < 4) {
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
status = dc_iostream_read (device->iostream, answer + i, 1, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
- return status;
+ if (status != DC_STATUS_TIMEOUT)
+ return status;
}
- if (answer[i] == (i < 3 ? 0x55 : 0x00)) {
- i++; // Continue.
- } else {
- i = 0; // Reset.
+
+ const unsigned char expected = i < 3 ? 0x55 : 0x00;
+ if (status != DC_STATUS_SUCCESS || answer[i] != expected) {
device_event_emit (abstract, DC_EVENT_WAITING, NULL);
+ i = 0;
+ } else {
+ i++;
}
}