diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e41d43a..00ff527 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -122,3 +122,52 @@ jobs:
with:
name: ${{ github.job }}-${{ matrix.arch }}
path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
+
+ msvc:
+
+ name: Visual Studio
+
+ runs-on: windows-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [x86, x64]
+
+ env:
+ CONFIGURATION: Release
+
+ steps:
+ - uses: actions/checkout@v3
+ - uses: msys2/setup-msys2@v2
+ with:
+ install: autoconf automake libtool pkg-config make gcc
+ - run: |
+ autoreconf --install --force
+ ./configure --prefix=/usr
+ make -C src revision.h
+ shell: msys2 {0}
+ - uses: microsoft/setup-msbuild@v1
+ - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj
+ - uses: actions/upload-artifact@v3
+ with:
+ name: ${{ github.job }}-${{ matrix.platform }}
+ path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin
+
+ android:
+
+ name: Android
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - run: |
+ autoreconf --install --force
+ ./configure --prefix=/usr
+ make -C src revision.h
+ - run: $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
+ - uses: actions/upload-artifact@v3
+ with:
+ name: ${{ github.job }}
+ path: contrib/android/libs
diff --git a/Makefile.am b/Makefile.am
index 9ae4414..3798611 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,5 +16,8 @@ pkgconfig_DATA = libdivecomputer.pc
EXTRA_DIST = \
libdivecomputer.pc.in \
- msvc/libdivecomputer.vcxproj \
- msvc/libdivecomputer.vcxproj.filters
+ contrib/README \
+ contrib/android/Android.mk \
+ contrib/msvc/libdivecomputer.vcxproj \
+ contrib/msvc/libdivecomputer.vcxproj.filters \
+ contrib/udev/libdivecomputer.rules
diff --git a/NEWS b/NEWS
index 3fabd4d..df264ae 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,38 @@
-Version 0.7.0 (2020-05-07)
+Version 0.8.0 (2023-05-11)
+==========================
+
+The v0.8.0 release is mainly a bugfix release, and brings in support for a
+number of new devices. This release is fully backwards compatible with the
+previous one.
+
+New features:
+
+ * Add support for new backends:
+ - excursion: Deep Six Excursion, Crest CR-4, Genesis Centauri, Tusa TC1, Scorpena Alpha
+ - screen: Seac Screen and Action
+ - cosmiq: Deepblu Cosmiq+
+ - s1: Oceans S1
+ - freedom: Divesoft Freedom and Liberty
+ * Add support for some new devices:
+ - Aqualung: i200C
+ - Cressi: Donatello, Michelangelo, Neon
+ - Mares: Puck Pro +
+ - Oceanic: Geo Air
+ - Ratio: iX3M 2
+ - Scubapro: G2 TEK
+ - Shearwater: Petrel 3, Perdix 2
+ - Sherwood: Amphos Air 2.0
+ * Add support for parsing the decompression model
+ * Add a public api to configure the depth calibration
+ * Add a public api to configure the clock synchronization
+ * Add a basic Android build system
+
+Removed/changed features:
+
+ * Migrate to Visual Studio 2013 (or newer)
+ * Move the Visual Studio project to the contrib directory
+
+Version 0.7.0 (2021-05-07)
==========================
The main highlight of the v0.7.0 release is the introduction of the new
diff --git a/configure.ac b/configure.ac
index a58d3e1..f580dbc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -253,7 +253,6 @@ AC_CONFIG_FILES([
include/libdivecomputer/Makefile
include/libdivecomputer/version.h
src/Makefile
- src/libdivecomputer.rc
doc/Makefile
doc/doxygen.cfg
doc/man/Makefile
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..cb0b77c
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,56 @@
+Alternative build systems
+=========================
+
+The autotools based build system is the official build system for the
+libdivecomputer project. But for convenience, a few alternative build systems
+are available as well. Unfortunately, these builds systems require a few extra
+steps to generate some header files.
+
+If you have access to a UNIX build system (for example a Linux virtual machine,
+MinGW, Cygwin or the Windows Subsystem for Linux), you can use the autotools
+build system to generate those files:
+
+ $ autoreconf --install --force
+ $ ./configure
+ $ make -C src revision.h
+
+Alternative, you can generate those files manually. First, create the version.h
+file from the version.h.in template:
+
+ $ cp include/libdivecomputer/version.h.in include/libdivecomputer/version.h
+
+and replace all the @DC_VERSION@ placeholders with the values defined in the
+configure.ac file.
+
+Next, generate the revision.h file:
+
+ $ echo "#define DC_VERSION_REVISION \"$(git rev-parse --verify HEAD)\"" > src/revision.h
+
+The alternative build systems are ready to use now.
+
+Visual Studio
+-------------
+
+The Visual Studio project file can be opened in the IDE, or build directly from
+the command-line:
+
+ msbuild -m -p:Platform=x86|x64 -p:Configuration=Debug|Release contrib/msvc/libdivecomputer.vcxproj
+
+Android NDK
+-----------
+
+ $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
+
+Linux udev rules
+================
+
+For dive computers using USB or USB HID communication, regular users typically
+don't have the necessary permissions to access the device nodes. This can be
+fixed with some udev rules.
+
+Install the udev rules, and reload them:
+
+ $ sudo cp contrib/udev/libdivecomputer.rules /etc/udev/rules.d/
+ $ sudo udevadm control --reload
+
+Note: the provided udev rules assume the user is in the plugdev group.
diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk
new file mode 100644
index 0000000..fcceb81
--- /dev/null
+++ b/contrib/android/Android.mk
@@ -0,0 +1,143 @@
+LOCAL_PATH := $(call my-dir)/../..
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdivecomputer
+LOCAL_CFLAGS := -DENABLE_LOGGING -DHAVE_VERSION_SUFFIX -DHAVE_PTHREAD_H -DHAVE_STRERROR_R -DHAVE_CLOCK_GETTIME -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DHAVE_TIMEGM -DHAVE_STRUCT_TM_TM_GMTOFF
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := \
+ src/aes.c \
+ src/array.c \
+ src/atomics_cobalt.c \
+ src/atomics_cobalt_parser.c \
+ src/bluetooth.c \
+ src/buffer.c \
+ src/checksum.c \
+ src/citizen_aqualand.c \
+ src/citizen_aqualand_parser.c \
+ src/cochran_commander.c \
+ src/cochran_commander_parser.c \
+ src/common.c \
+ src/context.c \
+ src/cressi_edy.c \
+ src/cressi_edy_parser.c \
+ src/cressi_goa.c \
+ src/cressi_goa_parser.c \
+ src/cressi_leonardo.c \
+ src/cressi_leonardo_parser.c \
+ src/custom.c \
+ src/datetime.c \
+ src/deepblu_cosmiq.c \
+ src/deepblu_cosmiq_parser.c \
+ src/deepsix_excursion.c \
+ src/deepsix_excursion_parser.c \
+ src/descriptor.c \
+ src/device.c \
+ src/diverite_nitekq.c \
+ src/diverite_nitekq_parser.c \
+ src/divesoft_freedom.c \
+ src/divesoft_freedom_parser.c \
+ src/divesystem_idive.c \
+ src/divesystem_idive_parser.c \
+ src/hdlc.c \
+ src/hw_frog.c \
+ src/hw_ostc3.c \
+ src/hw_ostc.c \
+ src/hw_ostc_parser.c \
+ src/ihex.c \
+ src/iostream.c \
+ src/irda.c \
+ src/iterator.c \
+ src/liquivision_lynx.c \
+ src/liquivision_lynx_parser.c \
+ src/mares_common.c \
+ src/mares_darwin.c \
+ src/mares_darwin_parser.c \
+ src/mares_iconhd.c \
+ src/mares_iconhd_parser.c \
+ src/mares_nemo.c \
+ src/mares_nemo_parser.c \
+ src/mares_puck.c \
+ src/mclean_extreme.c \
+ src/mclean_extreme_parser.c \
+ src/oceanic_atom2.c \
+ src/oceanic_atom2_parser.c \
+ src/oceanic_common.c \
+ src/oceanic_veo250.c \
+ src/oceanic_veo250_parser.c \
+ src/oceanic_vtpro.c \
+ src/oceanic_vtpro_parser.c \
+ src/oceans_s1.c \
+ src/oceans_s1_common.c \
+ src/oceans_s1_parser.c \
+ src/packet.c \
+ src/parser.c \
+ src/platform.c \
+ src/rbstream.c \
+ src/reefnet_sensus.c \
+ src/reefnet_sensus_parser.c \
+ src/reefnet_sensuspro.c \
+ src/reefnet_sensuspro_parser.c \
+ src/reefnet_sensusultra.c \
+ src/reefnet_sensusultra_parser.c \
+ src/ringbuffer.c \
+ src/seac_screen.c \
+ src/seac_screen_parser.c \
+ src/serial_posix.c \
+ src/shearwater_common.c \
+ src/shearwater_petrel.c \
+ src/shearwater_predator.c \
+ src/shearwater_predator_parser.c \
+ src/socket.c \
+ src/sporasub_sp2.c \
+ src/sporasub_sp2_parser.c \
+ src/suunto_common2.c \
+ src/suunto_common.c \
+ src/suunto_d9.c \
+ src/suunto_d9_parser.c \
+ src/suunto_eon.c \
+ src/suunto_eon_parser.c \
+ src/suunto_eonsteel.c \
+ src/suunto_eonsteel_parser.c \
+ src/suunto_solution.c \
+ src/suunto_solution_parser.c \
+ src/suunto_vyper2.c \
+ src/suunto_vyper.c \
+ src/suunto_vyper_parser.c \
+ src/tecdiving_divecomputereu.c \
+ src/tecdiving_divecomputereu_parser.c \
+ src/timer.c \
+ src/usb.c \
+ src/usbhid.c \
+ src/uwatec_aladin.c \
+ src/uwatec_memomouse.c \
+ src/uwatec_memomouse_parser.c \
+ src/uwatec_smart.c \
+ src/uwatec_smart_parser.c \
+ src/version.c \
+ src/zeagle_n2ition3.c
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dctool
+LOCAL_SHARED_LIBRARIES := libdivecomputer
+LOCAL_CFLAGS := -DHAVE_UNISTD_H -DHAVE_GETOPT_H -DHAVE_GETOPT_LONG -DHAVE_DECL_OPTRESET=1
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := \
+ examples/common.c \
+ examples/dctool.c \
+ examples/dctool_download.c \
+ examples/dctool_dump.c \
+ examples/dctool_fwupdate.c \
+ examples/dctool_help.c \
+ examples/dctool_list.c \
+ examples/dctool_parse.c \
+ examples/dctool_read.c \
+ examples/dctool_scan.c \
+ examples/dctool_timesync.c \
+ examples/dctool_version.c \
+ examples/dctool_write.c \
+ examples/output.c \
+ examples/output_raw.c \
+ examples/output_xml.c \
+ examples/utils.c
+include $(BUILD_EXECUTABLE)
diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj
new file mode 100644
index 0000000..7f1335c
--- /dev/null
+++ b/contrib/msvc/libdivecomputer.vcxproj
@@ -0,0 +1,407 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {CEA7215A-D6B5-4840-8086-3C854F371997}
+ libdivecomputer
+ Win32Proj
+
+
+
+ DynamicLibrary
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ DynamicLibrary
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ DynamicLibrary
+ false
+ $(DefaultPlatformToolset)
+ Unicode
+ true
+
+
+ DynamicLibrary
+ false
+ $(DefaultPlatformToolset)
+ Unicode
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
+ $(PlatformTarget)\$(Configuration)\obj\
+
+
+ true
+ $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
+ $(PlatformTarget)\$(Configuration)\obj\
+
+
+ false
+ $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
+ $(PlatformTarget)\$(Configuration)\obj\
+
+
+ false
+ $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
+ $(PlatformTarget)\$(Configuration)\obj\
+
+
+
+ Disabled
+ ..\..\include;%(AdditionalIncludeDirectories)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
+
+ Level3
+ true
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)libdivecomputer.def
+ true
+ Windows
+
+
+ ..\..\include;%(AdditionalIncludeDirectories)
+
+
+
+
+ Disabled
+ ..\..\include;%(AdditionalIncludeDirectories)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
+
+
+ Level3
+ true
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)libdivecomputer.def
+ true
+ Windows
+
+
+ ..\..\include;%(AdditionalIncludeDirectories)
+
+
+
+
+ MaxSpeed
+ true
+ ..\..\include;%(AdditionalIncludeDirectories)
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
+ true
+
+ Level3
+ true
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)libdivecomputer.def
+ true
+ Windows
+ true
+ true
+
+
+ ..\..\include;%(AdditionalIncludeDirectories)
+
+
+
+
+ MaxSpeed
+ true
+ ..\..\include;%(AdditionalIncludeDirectories)
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
+ true
+
+
+ Level3
+ true
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)libdivecomputer.def
+ true
+ Windows
+ true
+ true
+
+
+ ..\..\include;%(AdditionalIncludeDirectories)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
+ echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
+ $(OutDir)libdivecomputer.def;%(Outputs)
+ $(OutDir)libdivecomputer.def;%(Outputs)
+ echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
+ echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
+ $(OutDir)libdivecomputer.def;%(Outputs)
+ $(OutDir)libdivecomputer.def;%(Outputs)
+
+
+
+
+
+
diff --git a/msvc/libdivecomputer.vcxproj.filters b/contrib/msvc/libdivecomputer.vcxproj.filters
similarity index 100%
rename from msvc/libdivecomputer.vcxproj.filters
rename to contrib/msvc/libdivecomputer.vcxproj.filters
diff --git a/doc/man/dc_datetime_gmtime.3 b/doc/man/dc_datetime_gmtime.3
index 1e4bcf5..9297446 100644
--- a/doc/man/dc_datetime_gmtime.3
+++ b/doc/man/dc_datetime_gmtime.3
@@ -82,7 +82,7 @@ is
.Sh SEE ALSO
.Xr dc_datetime_localtime 3 ,
.Xr dc_datetime_mktime 3 ,
-.Xr dc_datetime_new 3
+.Xr dc_datetime_now 3
.Sh AUTHORS
The
.Lb libdivecomputer
diff --git a/doc/man/dc_datetime_localtime.3 b/doc/man/dc_datetime_localtime.3
index ec6681f..8b9c2f6 100644
--- a/doc/man/dc_datetime_localtime.3
+++ b/doc/man/dc_datetime_localtime.3
@@ -82,7 +82,7 @@ is
.Sh SEE ALSO
.Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_mktime 3 ,
-.Xr dc_datetime_new 3
+.Xr dc_datetime_now 3
.Sh AUTHORS
The
.Lb libdivecomputer
diff --git a/doc/man/dc_datetime_mktime.3 b/doc/man/dc_datetime_mktime.3
index a3dbccb..4ba38b3 100644
--- a/doc/man/dc_datetime_mktime.3
+++ b/doc/man/dc_datetime_mktime.3
@@ -47,7 +47,7 @@ may not sanely be converted.
.Sh SEE ALSO
.Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_localtime 3 ,
-.Xr dc_datetime_new 3
+.Xr dc_datetime_now 3
.Sh AUTHORS
The
.Lb libdivecomputer
diff --git a/examples/common.c b/examples/common.c
index 47d457f..f30337b 100644
--- a/examples/common.c
+++ b/examples/common.c
@@ -98,6 +98,7 @@ static const backend_table_t g_backends[] = {
{"screen", DC_FAMILY_SEAC_SCREEN, 0},
{"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0},
{"s1", DC_FAMILY_OCEANS_S1, 0},
+ {"freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19},
// Not merged upstream yet
{"descentmk1", DC_FAMILY_GARMIN, 0},
diff --git a/examples/dctool.c b/examples/dctool.c
index 9d3f74b..62a15fc 100644
--- a/examples/dctool.c
+++ b/examples/dctool.c
@@ -50,7 +50,7 @@
#define RESET 1
#endif
-#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD)
+#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) || defined(__ANDROID__)
#define NOPERMUTATION "+"
#else
#define NOPERMUTATION ""
diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h
index 6619bd0..0d5ab17 100644
--- a/include/libdivecomputer/common.h
+++ b/include/libdivecomputer/common.h
@@ -122,6 +122,8 @@ typedef enum dc_family_t {
DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16),
/* Oceans S1 */
DC_FAMILY_OCEANS_S1 = (22 << 16),
+ /* Divesoft Freedom */
+ DC_FAMILY_DIVESOFT_FREEDOM = (23 << 16),
// Not merged upstream yet
/* Garmin */
diff --git a/msvc/libdivecomputer.vcxproj b/msvc/libdivecomputer.vcxproj
deleted file mode 100644
index b3b370f..0000000
--- a/msvc/libdivecomputer.vcxproj
+++ /dev/null
@@ -1,388 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Debug
- x64
-
-
- Release
- Win32
-
-
- Release
- x64
-
-
-
- {CEA7215A-D6B5-4840-8086-3C854F371997}
- libdivecomputer
- Win32Proj
-
-
-
- DynamicLibrary
- true
- $(DefaultPlatformToolset)
- Unicode
-
-
- DynamicLibrary
- true
- $(DefaultPlatformToolset)
- Unicode
-
-
- DynamicLibrary
- false
- $(DefaultPlatformToolset)
- Unicode
- true
-
-
- DynamicLibrary
- false
- $(DefaultPlatformToolset)
- Unicode
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
- $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
- $(PlatformTarget)\$(Configuration)\obj\
-
-
- true
- $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
- $(PlatformTarget)\$(Configuration)\obj\
-
-
- false
- $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
- $(PlatformTarget)\$(Configuration)\obj\
-
-
- false
- $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\
- $(PlatformTarget)\$(Configuration)\obj\
-
-
-
- Disabled
- ..\include;%(AdditionalIncludeDirectories)
- _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
-
- Level3
- true
-
-
- ws2_32.lib;%(AdditionalDependencies)
- $(OutDir)libdivecomputer.def
- true
- Windows
-
-
-
-
- Disabled
- ..\include;%(AdditionalIncludeDirectories)
- _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
-
-
- Level3
- true
-
-
- ws2_32.lib;%(AdditionalDependencies)
- $(OutDir)libdivecomputer.def
- true
- Windows
-
-
-
-
- MaxSpeed
- true
- ..\include;%(AdditionalIncludeDirectories)
- _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
- true
-
- Level3
- true
-
-
- ws2_32.lib;%(AdditionalDependencies)
- $(OutDir)libdivecomputer.def
- true
- Windows
- true
- true
-
-
-
-
- MaxSpeed
- true
- ..\include;%(AdditionalIncludeDirectories)
- _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)
- true
-
-
- Level3
- true
-
-
- ws2_32.lib;%(AdditionalDependencies)
- $(OutDir)libdivecomputer.def
- true
- Windows
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
- echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
- $(OutDir)libdivecomputer.def;%(Outputs)
- $(OutDir)libdivecomputer.def;%(Outputs)
- echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
- echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def"
- $(OutDir)libdivecomputer.def;%(Outputs)
- $(OutDir)libdivecomputer.def;%(Outputs)
-
-
-
-
-
-
diff --git a/src/Makefile.am b/src/Makefile.am
index c269fd2..fec3d31 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -80,6 +80,9 @@ libdivecomputer_la_SOURCES = \
deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \
oceans_s1_common.h oceans_s1_common.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c \
+ divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \
+ hdlc.h hdlc.c \
+ packet.h packet.c \
socket.h socket.c \
irda.c \
usb.c \
@@ -109,7 +112,7 @@ libdivecomputer.exp: libdivecomputer.symbols
$(AM_V_GEN) sed -e '/^$$/d' $< > $@
.rc.lo:
- $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $< -o $@
+ $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $< -o $@
libdivecomputer.lo: revision.h
diff --git a/src/array.c b/src/array.c
index 1216d41..29f5faf 100644
--- a/src/array.c
+++ b/src/array.c
@@ -384,3 +384,23 @@ dec2bcd (unsigned char value)
unsigned char lo = value % 10;
return (hi << 4) | lo;
}
+
+/*
+ * When turning a two's-complement number with a certain number
+ * of bits into one with more bits, the sign bit must be repeated
+ * in all the extra bits.
+ */
+unsigned int
+signextend (unsigned int value, unsigned int nbits)
+{
+ if (nbits <= 0 || nbits > 32)
+ return 0;
+
+ unsigned int signbit = 1U << (nbits - 1);
+ unsigned int mask = signbit - 1;
+
+ if ((value & signbit) == signbit)
+ return value | ~mask;
+ else
+ return value & mask;
+}
diff --git a/src/array.h b/src/array.h
index ac4c3b1..a6ef21e 100644
--- a/src/array.h
+++ b/src/array.h
@@ -123,6 +123,9 @@ bcd2dec (unsigned char value);
unsigned char
dec2bcd (unsigned char value);
+unsigned int
+signextend (unsigned int value, unsigned int nbits);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/src/checksum.c b/src/checksum.c
index 9d06630..eeda912 100644
--- a/src/checksum.c
+++ b/src/checksum.c
@@ -68,8 +68,13 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char
}
+/*
+ * Polynomial: 0x1021
+ * RefIn: False
+ * RefOut: False
+ */
unsigned short
-checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init)
+checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
{
static const unsigned short crc_ccitt_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
@@ -110,11 +115,170 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh
for (unsigned int i = 0; i < size; ++i)
crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]];
- return crc;
+ return crc ^ xorout;
}
+
+/*
+ * Polynomial: 0x1021
+ * RefIn: True
+ * RefOut: True
+ */
+unsigned short
+checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
+{
+ static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+ };
+
+ unsigned short crc = init;
+ for (unsigned int i = 0; i < size; ++i)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff];
+
+ return crc ^ xorout;
+}
+
+/*
+ * Polynomial: 0x8005
+ * RefIn: False
+ * RefOut: False
+ */
+unsigned short
+checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
+{
+ static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
+ 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
+ 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
+ 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
+ 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
+ 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
+ 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
+ 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
+ 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
+ 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
+ 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
+ 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
+ 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
+ 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
+ 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
+ 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
+ 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
+ };
+
+ unsigned short crc = init;
+ for (unsigned int i = 0; i < size; ++i)
+ crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]];
+
+ return crc ^ xorout;
+}
+
+/*
+ * Polynomial: 0x8005
+ * RefIn: True
+ * RefOut: True
+ */
+unsigned short
+checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
+{
+ static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+ };
+
+ unsigned short crc = init;
+ for (unsigned int i = 0; i < size; ++i)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff];
+
+ return crc ^ xorout;
+}
+
+
+/*
+ * Polynomial: 0x04C11DB7
+ * Init: 0xffffffff
+ * XorOut: 0xffffffff
+ * RefIn: True
+ * RefOut: True
+ */
unsigned int
-checksum_crc32 (const unsigned char data[], unsigned int size)
+checksum_crc32r (const unsigned char data[], unsigned int size)
{
static const unsigned int crc_table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
@@ -158,8 +322,16 @@ checksum_crc32 (const unsigned char data[], unsigned int size)
return crc ^ 0xffffffff;
}
+
+/*
+ * Polynomial: 0x04C11DB7
+ * Init: 0xffffffff
+ * XorOut: 0xffffffff
+ * RefIn: False
+ * RefOut: False
+ */
unsigned int
-checksum_crc32b (const unsigned char data[], unsigned int size)
+checksum_crc32 (const unsigned char data[], unsigned int size)
{
static const unsigned int crc_table[] = {
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
diff --git a/src/checksum.h b/src/checksum.h
index c8a9001..f9e54bb 100644
--- a/src/checksum.h
+++ b/src/checksum.h
@@ -39,14 +39,23 @@ unsigned char
checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init);
unsigned short
-checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init);
+checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
+
+unsigned short
+checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
+
+unsigned short
+checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
+
+unsigned short
+checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
+
+unsigned int
+checksum_crc32r (const unsigned char data[], unsigned int size);
unsigned int
checksum_crc32 (const unsigned char data[], unsigned int size);
-unsigned int
-checksum_crc32b (const unsigned char data[], unsigned int size);
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/src/cressi_goa.c b/src/cressi_goa.c
index e414743..9df10e8 100644
--- a/src/cressi_goa.c
+++ b/src/cressi_goa.c
@@ -93,7 +93,7 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un
if (size) {
memcpy (packet + 5, data, size);
}
- crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000);
+ crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000, 0x0000);
packet[5 + size + 0] = (crc ) & 0xFF; // Low
packet[5 + size + 1] = (crc >> 8) & 0xFF; // High
packet[5 + size + 2] = TRAILER;
@@ -155,7 +155,7 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + length + 5);
- unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000);
+ unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@@ -203,7 +203,7 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2);
- unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000);
+ unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c
index f331537..dd941da 100644
--- a/src/cressi_leonardo.c
+++ b/src/cressi_leonardo.c
@@ -84,7 +84,7 @@ cressi_leonardo_make_ascii (const unsigned char raw[], unsigned int rsize, unsig
array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize);
// Checksum
- unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff);
+ unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff, 0x0000);
unsigned char checksum[] = {
(crc >> 8) & 0xFF, // High
(crc ) & 0xFF}; // Low
@@ -129,7 +129,7 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co
// Verify the checksum of the packet.
unsigned short crc = array_uint16_be (checksum);
- unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@@ -372,7 +372,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
// Verify the checksum.
unsigned int csum1 = array_uint16_be (checksum);
- unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff);
+ unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff, 0x0000);
if (csum1 != csum2) {
ERROR (abstract->context, "Unexpected answer bytes.");
return DC_STATUS_PROTOCOL;
diff --git a/src/descriptor.c b/src/descriptor.c
index 8414aec..7601e5e 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -65,6 +65,7 @@ static int dc_filter_atomic (dc_transport_t transport, const void *userdata, voi
static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params);
+static int dc_filter_divesoft (dc_transport_t transport, const void *userdata, void *params);
// Not merged upstream yet
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params);
@@ -454,6 +455,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
/* Oceans S1 */
{"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans},
+ /* Divesoft Freedom */
+ {"Divesoft", "Freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19, DC_TRANSPORT_BLE, dc_filter_divesoft},
+ {"Divesoft", "Liberty", DC_FAMILY_DIVESOFT_FREEDOM, 10, DC_TRANSPORT_BLE, dc_filter_divesoft},
// Not merged upstream yet
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
@@ -808,6 +812,20 @@ static int dc_filter_oceans (dc_transport_t transport, const void *userdata, voi
return 1;
}
+static int dc_filter_divesoft (dc_transport_t transport, const void *userdata, void *params)
+{
+ static const char * const bluetooth[] = {
+ "Freedom",
+ "Liberty",
+ };
+
+ if (transport == DC_TRANSPORT_BLE) {
+ return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
+ }
+
+ return 1;
+}
+
// Not merged upstream yet
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params)
{
diff --git a/src/device.c b/src/device.c
index 602f875..6443159 100644
--- a/src/device.c
+++ b/src/device.c
@@ -64,6 +64,7 @@
#include "seac_screen.h"
#include "deepblu_cosmiq.h"
#include "oceans_s1.h"
+#include "divesoft_freedom.h"
// Not merged upstream yet
#include "garmin.h"
@@ -242,6 +243,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_device_open (&device, context, iostream);
break;
+ case DC_FAMILY_DIVESOFT_FREEDOM:
+ rc = divesoft_freedom_device_open (&device, context, iostream);
+ break;
default:
return DC_STATUS_INVALIDARGS;
diff --git a/src/divesoft_freedom.c b/src/divesoft_freedom.c
new file mode 100644
index 0000000..ddf9162
--- /dev/null
+++ b/src/divesoft_freedom.c
@@ -0,0 +1,595 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jan Matoušek, Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include
+#include
+#include
+
+#include "divesoft_freedom.h"
+#include "context-private.h"
+#include "device-private.h"
+#include "platform.h"
+#include "checksum.h"
+#include "array.h"
+#include "hdlc.h"
+
+#define MAXDATA 256
+
+#define HEADER_SIGNATURE_V1 0x45766944 // "DivE"
+#define HEADER_SIGNATURE_V2 0x45566944 // "DiVE"
+
+#define HEADER_SIZE_V1 32
+#define HEADER_SIZE_V2 64
+
+#define RECORD_SIZE 16
+#define FINGERPRINT_SIZE 20
+
+#define INVALID 0xFFFFFFFF
+#define COMPRESSION 1
+#define DIRECTION 1
+#define NRECORDS 100
+
+#define DEVICE_CCR_CU 1 // Liberty HW rev. 1.X
+#define DEVICE_FREEDOM 2 // Freedom HW rev. 2.X
+#define DEVICE_FREEDOM3 5 // Freedom HW rev. 3.X
+#define DEVICE_CCR_CU15 10 // Liberty HW rev. 2.X, Bluetooth enabled
+#define DEVICE_FREEDOM4 19 // Freedom HW rev. 4.X, Bluetooth enabled
+
+typedef enum message_t {
+ MSG_ECHO = 0,
+ MSG_RESULT = 1,
+ MSG_CONNECT = 2,
+ MSG_CONNECTED = 3,
+ MSG_VERSION = 4,
+ MSG_VERSION_RSP = 5,
+ MSG_DIVE_DATA = 64,
+ MSG_DIVE_DATA_RSP = 65,
+ MSG_DIVE_LIST = 66,
+ MSG_DIVE_LIST_V1 = 67,
+ MSG_DIVE_LIST_V2 = 71,
+} message_t;
+
+typedef struct divesoft_freedom_device_t {
+ dc_device_t base;
+ dc_iostream_t *iostream;
+ unsigned char fingerprint[FINGERPRINT_SIZE];
+ unsigned int seqnum;
+} divesoft_freedom_device_t;
+
+static dc_status_t divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
+static dc_status_t divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
+static dc_status_t divesoft_freedom_device_close (dc_device_t *device);
+
+static const dc_device_vtable_t divesoft_freedom_device_vtable = {
+ sizeof(divesoft_freedom_device_t),
+ DC_FAMILY_DIVESOFT_FREEDOM,
+ divesoft_freedom_device_set_fingerprint, /* set_fingerprint */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* dump */
+ divesoft_freedom_device_foreach, /* foreach */
+ NULL, /* timesync */
+ divesoft_freedom_device_close, /* close */
+};
+
+static dc_status_t
+divesoft_freedom_send (divesoft_freedom_device_t *device, message_t message, const unsigned char data[], size_t size)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ size_t nbytes = 0, count = 0;
+ while (1) {
+ size_t len = size - nbytes;
+ if (len > MAXDATA)
+ len = MAXDATA;
+
+ unsigned int islast = nbytes + len == size;
+
+ unsigned char packet[6 + MAXDATA + 2] = {0};
+ packet[0] = ((count & 0x0F) << 4) | (device->seqnum & 0x0F);
+ packet[1] = 0x80 | (islast << 6);
+ array_uint16_le_set (packet + 2, message);
+ array_uint16_le_set (packet + 4, len);
+ if (len) {
+ memcpy (packet + 6, data + nbytes, len);
+ }
+ unsigned short crc = checksum_crc16r_ccitt (packet, len + 6, 0xFFFF, 0xFFFF);
+ array_uint16_le_set (packet + 6 + len, crc);
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "cmd", packet, 6 + len + 2);
+
+ status = dc_iostream_write (device->iostream, packet, 6 + len + 2, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the packet.");
+ return status;
+ }
+
+ nbytes += len;
+ count++;
+
+ if (islast)
+ break;
+ }
+
+ return status;
+}
+
+static dc_status_t
+divesoft_freedom_recv (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t *message, dc_buffer_t *buffer)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+ unsigned int msg = INVALID;
+
+ unsigned int count = 0;
+ while (1) {
+ size_t len = 0;
+ unsigned char packet[6 + MAXDATA + 2] = {0};
+ status = dc_iostream_read (device->iostream, packet, sizeof(packet), &len);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive the packet.");
+ return status;
+ }
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "rcv", packet, len);
+
+ if (len < 8) {
+ ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", len);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ unsigned int seqnum = packet[0];
+ unsigned int flags = packet[1];
+ unsigned int type = array_uint16_le (packet + 2);
+ unsigned int length = array_uint16_le (packet + 4);
+
+ unsigned int expected = ((count & 0x0F) << 4) | (device->seqnum & 0x0F);
+ if (seqnum != expected) {
+ ERROR (abstract->context, "Unexpected packet sequence number (%u %u).", seqnum, expected);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ if ((flags & ~0x40) != 0) {
+ ERROR (abstract->context, "Unexpected packet flags (%u).", flags);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ if (length != len - 8) {
+ ERROR (abstract->context, "Unexpected packet length (%u " DC_PRINTF_SIZE ").", length, len - 8);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ if (msg == INVALID) {
+ msg = type;
+ } else if (msg != type) {
+ ERROR (abstract->context, "Unexpected packet type (%u).", msg);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ unsigned short crc = array_uint16_le (packet + len - 2);
+ unsigned short ccrc = checksum_crc16r_ccitt (packet, len - 2, 0xFFFF, 0xFFFF);
+ if (crc != ccrc) {
+ ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Update and emit a progress event.
+ if (progress) {
+ progress->current += len - 8;
+ // Limit the progress to the maximum size. This could happen if the
+ // dive computer sends more data than requested for some reason.
+ if (progress->current > progress->maximum) {
+ WARNING (abstract->context, "Progress exceeds the maximum size.");
+ progress->current = progress->maximum;
+ }
+ device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
+ }
+
+ if (!dc_buffer_append (buffer, packet + 6, len - 8)) {
+ ERROR (abstract->context, "Insufficient buffer space available.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ count++;
+
+ if (flags & 0x40)
+ break;
+ }
+
+ if (message)
+ *message = msg;
+
+ return status;
+}
+
+static dc_status_t
+divesoft_freedom_transfer (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t cmd, const unsigned char data[], size_t size, message_t *msg, dc_buffer_t *buffer)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ if (device_is_cancelled (abstract))
+ return DC_STATUS_CANCELLED;
+
+ device->seqnum++;
+
+ status = divesoft_freedom_send (device, cmd, data, size);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to send the command.");
+ return status;
+ }
+
+ status = divesoft_freedom_recv (device, progress, msg, buffer);
+ if(status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to receive response.");
+ return status;
+ }
+
+ return status;
+}
+
+static dc_status_t
+divesoft_freedom_download (divesoft_freedom_device_t *device, message_t cmd, const unsigned char cdata[], size_t csize, unsigned char rdata[], size_t rsize)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_device_t *abstract = (dc_device_t *) device;
+
+ dc_buffer_t *buffer = dc_buffer_new (rsize);
+ if (buffer == NULL) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ message_t msg = MSG_ECHO;
+ status = divesoft_freedom_transfer (device, NULL, cmd, cdata, csize, &msg, buffer);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to transfer the packet.");
+ goto error_free;
+ }
+
+ if (msg != cmd + 1) {
+ ERROR (abstract->context, "Unexpected response message (%u).", msg);
+ status = DC_STATUS_PROTOCOL;
+ goto error_free;
+ }
+
+ size_t length = dc_buffer_get_size (buffer);
+ if (length != rsize) {
+ ERROR (abstract->context, "Unexpected response length (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", length, rsize);
+ status = DC_STATUS_PROTOCOL;
+ goto error_free;
+ }
+
+ if (rsize) {
+ memcpy (rdata, dc_buffer_get_data (buffer), rsize);
+ }
+
+error_free:
+ dc_buffer_free (buffer);
+error_exit:
+ return status;
+}
+
+dc_status_t
+divesoft_freedom_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ divesoft_freedom_device_t *device = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ device = (divesoft_freedom_device_t *) dc_device_allocate (context, &divesoft_freedom_device_vtable);
+ if (device == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Set the default values.
+ device->iostream = NULL;
+ memset(device->fingerprint, 0, sizeof(device->fingerprint));
+ device->seqnum = 0;
+
+ // Setup the HDLC communication.
+ status = dc_hdlc_open (&device->iostream, context, iostream, 244, 244);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the HDLC stream.");
+ goto error_free;
+ }
+
+ // Set the serial communication protocol (115200 8N1).
+ status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to set the terminal attributes.");
+ goto error_free_hdlc;
+ }
+
+ // 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_hdlc;
+ }
+
+ // Initiate the connection with the dive computer.
+ const char client[] = "libdivecomputer";
+ unsigned char cmd_connect[2 + sizeof(client) - 1] = {0};
+ array_uint16_le_set (cmd_connect, COMPRESSION);
+ memcpy (cmd_connect + 2, client, sizeof(client) - 1);
+ unsigned char rsp_connect[36] = {0};
+ status = divesoft_freedom_download (device, MSG_CONNECT, cmd_connect, sizeof(cmd_connect), rsp_connect, sizeof(rsp_connect));
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to connect to the device.");
+ goto error_free_hdlc;
+ }
+
+ DEBUG (context, "Connection: compression=%u, protocol=%u.%u, serial=%.16s",
+ array_uint16_le (rsp_connect),
+ rsp_connect[2], rsp_connect[3],
+ rsp_connect + 4);
+
+ *out = (dc_device_t *) device;
+
+ return DC_STATUS_SUCCESS;
+
+error_free_hdlc:
+ dc_iostream_close (device->iostream);
+error_free:
+ dc_device_deallocate ((dc_device_t *) device);
+ return status;
+}
+
+static dc_status_t
+divesoft_freedom_device_close (dc_device_t *abstract)
+{
+ divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract;
+
+ return dc_iostream_close (device->iostream);
+}
+
+static dc_status_t
+divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
+{
+ divesoft_freedom_device_t *device = (divesoft_freedom_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
+divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract;
+
+ // Enable progress notifications.
+ dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Read the device information.
+ unsigned char rsp_version[26] = {0};
+ status = divesoft_freedom_download (device, MSG_VERSION, NULL, 0, rsp_version, sizeof(rsp_version));
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the device information.");
+ goto error_exit;
+ }
+
+ DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u serial=%.16s",
+ rsp_version[0],
+ rsp_version[1], rsp_version[2],
+ rsp_version[3], rsp_version[4], rsp_version[5],
+ array_uint32_le (rsp_version + 6),
+ rsp_version + 10);
+
+ // Emit a device info event.
+ dc_event_devinfo_t devinfo;
+ devinfo.model = rsp_version[0];
+ devinfo.firmware = array_uint24_be (rsp_version + 3);
+ devinfo.serial = array_convert_str2num (rsp_version + 10 + 5, 11);
+ device_event_emit(abstract, DC_EVENT_DEVINFO, &devinfo);
+
+ // Allocate memory for the dive list.
+ dc_buffer_t *divelist = dc_buffer_new (0);
+ if (divelist == NULL) {
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ // Allocate memory for the download buffer.
+ dc_buffer_t *buffer = dc_buffer_new (NRECORDS * (4 + FINGERPRINT_SIZE + HEADER_SIZE_V2));
+ if (buffer == NULL) {
+ status = DC_STATUS_NOMEMORY;
+ goto error_free_divelist;
+ }
+
+ // Record version and size.
+ unsigned int version = 0;
+ unsigned int headersize = 0;
+ unsigned int recordsize = 0;
+
+ // Download the dive list.
+ unsigned int ndives = 0;
+ unsigned int total = 0;
+ unsigned int maxsize = 0;
+ unsigned int current = INVALID;
+ while (1) {
+ // Clear the buffer.
+ dc_buffer_clear (buffer);
+
+ // Prepare the command.
+ unsigned char cmd_list[6] = {0};
+ array_uint32_le_set (cmd_list, current);
+ cmd_list[4] = DIRECTION;
+ cmd_list[5] = NRECORDS;
+
+ // Download the dive list records.
+ message_t msg_list = MSG_ECHO;
+ status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_LIST, cmd_list, sizeof(cmd_list), &msg_list, buffer);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to download the dive list.");
+ goto error_free_buffer;
+ }
+
+ // Check the response message type.
+ if (msg_list != MSG_DIVE_LIST_V1 && msg_list != MSG_DIVE_LIST_V2) {
+ ERROR (abstract->context, "Unexpected response message (%u).", msg_list);
+ status = DC_STATUS_PROTOCOL;
+ goto error_free_buffer;
+ }
+
+ // Store/check the version.
+ if (version == 0) {
+ version = msg_list;
+ headersize = version == MSG_DIVE_LIST_V1 ?
+ HEADER_SIZE_V1 : HEADER_SIZE_V2;
+ recordsize = 4 + FINGERPRINT_SIZE + headersize;
+ } else if (version != msg_list) {
+ ERROR (abstract->context, "Unexpected response message (%u).", msg_list);
+ status = DC_STATUS_PROTOCOL;
+ goto error_free_buffer;
+ }
+
+ const unsigned char *data = dc_buffer_get_data (buffer);
+ size_t size = dc_buffer_get_size (buffer);
+
+ // Process the records.
+ size_t offset = 0, count = 0;
+ while (offset + recordsize <= size) {
+ // Get the record data.
+ unsigned int handle = array_uint32_le (data + offset);
+ const unsigned char *fingerprint = data + offset + 4;
+ const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE;
+
+ // Check the fingerprint data.
+ if (memcmp (device->fingerprint, fingerprint, sizeof(device->fingerprint)) == 0) {
+ break;
+ }
+
+ // Get the length of the dive.
+ unsigned int nrecords = version == MSG_DIVE_LIST_V1 ?
+ array_uint32_le (header + 16) & 0x3FFFF :
+ array_uint32_le (header + 20);
+ unsigned int length = headersize + nrecords * RECORD_SIZE;
+
+ // Calculate the total and maximum size.
+ if (length > maxsize)
+ maxsize = length;
+ total += length;
+
+ // Set the handle for the next request.
+ current = handle;
+
+ offset += recordsize;
+ count++;
+ ndives++;
+ }
+
+ // Append the records to the dive list buffer.
+ if (!dc_buffer_append (divelist, data, count * recordsize)) {
+ ERROR (abstract->context, "Insufficient buffer space available.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free_buffer;
+ }
+
+ // Stop downloading if there are no more records.
+ if (count < NRECORDS)
+ break;
+ }
+
+ // Update and emit a progress event.
+ progress.maximum = progress.current + total;
+ device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Reserve memory for the largest dive.
+ dc_buffer_reserve (buffer, maxsize);
+
+ const unsigned char *data = dc_buffer_get_data (divelist);
+ size_t size = dc_buffer_get_size (divelist);
+
+ size_t offset = 0;
+ while (offset + recordsize <= size) {
+ // Get the record data.
+ unsigned int handle = array_uint32_le (data + offset);
+ const unsigned char *fingerprint = data + offset + 4;
+ const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE;
+
+ // Get the length of the dive.
+ unsigned int nrecords = version == MSG_DIVE_LIST_V1 ?
+ array_uint32_le (header + 16) & 0x3FFFF :
+ array_uint32_le (header + 20);
+ unsigned int length = headersize + nrecords * RECORD_SIZE;
+
+ // Clear the buffer.
+ dc_buffer_clear (buffer);
+
+ // Prepare the command.
+ unsigned char cmd_dive[12] = {0};
+ array_uint32_le_set (cmd_dive + 0, handle);
+ array_uint32_le_set (cmd_dive + 4, 0);
+ array_uint32_le_set (cmd_dive + 8, length);
+
+ // Download the dive.
+ message_t msg_dive = MSG_ECHO;
+ status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_DATA, cmd_dive, sizeof(cmd_dive), &msg_dive, buffer);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to download the dive.");
+ goto error_free_buffer;
+ }
+
+ // Check the response message type.
+ if (msg_dive != MSG_DIVE_DATA_RSP) {
+ ERROR (abstract->context, "Unexpected response message (%u).", msg_dive);
+ status = DC_STATUS_PROTOCOL;
+ goto error_free_buffer;
+ }
+
+ // Verify both dive headers are identical.
+ if (dc_buffer_get_size (buffer) < headersize ||
+ memcmp (header, dc_buffer_get_data (buffer), headersize) != 0) {
+ ERROR (abstract->context, "Unexpected profile header.");
+ status = DC_STATUS_PROTOCOL;
+ goto error_free_buffer;
+ }
+
+ if (callback && !callback (dc_buffer_get_data(buffer), dc_buffer_get_size(buffer), fingerprint, sizeof (device->fingerprint), userdata)) {
+ break;
+ }
+
+ offset += recordsize;
+ }
+
+error_free_buffer:
+ dc_buffer_free (buffer);
+error_free_divelist:
+ dc_buffer_free (divelist);
+error_exit:
+ return status;
+}
diff --git a/src/divesoft_freedom.h b/src/divesoft_freedom.h
new file mode 100644
index 0000000..db04edb
--- /dev/null
+++ b/src/divesoft_freedom.h
@@ -0,0 +1,43 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jan Matoušek, 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 DIVESOFT_FREEDOM_H
+#define DIVESOFT_FREEDOM_H
+
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+dc_status_t
+divesoft_freedom_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
+
+dc_status_t
+divesoft_freedom_parser_create (dc_parser_t **parser, dc_context_t *context);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* DIVESOFT_FREEDOM_H */
diff --git a/src/divesoft_freedom_parser.c b/src/divesoft_freedom_parser.c
new file mode 100644
index 0000000..b595e63
--- /dev/null
+++ b/src/divesoft_freedom_parser.c
@@ -0,0 +1,1070 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jan Matoušek, Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include
+#include
+
+#include
+
+#include "divesoft_freedom.h"
+#include "context-private.h"
+#include "parser-private.h"
+#include "checksum.h"
+#include "array.h"
+
+#define UNDEFINED 0xFFFFFFFF
+
+#define EPOCH 946684800 // 2000-01-01 00:00:00 UTC
+
+#define OC 0
+#define OXYGEN 1
+#define DILUENT 2
+
+#define NSENSORS 4
+#define NGASMIXES 12
+#define NTANKS 12
+
+#define HEADER_SIGNATURE_V1 0x45766944 // "DivE"
+#define HEADER_SIGNATURE_V2 0x45566944 // "DiVE"
+
+#define HEADER_SIZE_V1 32
+#define HEADER_SIZE_V2 64
+
+#define RECORD_SIZE 16
+
+#define SEAWATER 1028
+#define FRESHWATER 1000
+
+typedef enum logrecord_t {
+ LREC_POINT = 0,
+ LREC_MANIPULATION = 1,
+ LREC_AUTO = 2,
+ LREC_DIVER_ERROR = 3,
+ LREC_INTERNAL_ERROR = 4,
+ LREC_ACTIVITY = 5,
+ LREC_CONFIGURATION = 6,
+ LREC_MEASURE = 7,
+ LREC_STATE = 8,
+ LREC_INFO = 9,
+} logrecord_t;
+
+typedef enum point_id_t {
+ POINT_1 = 0,
+ POINT_2 = 1,
+ POINT_1_OLD = 0x3FF,
+} point_id_t;
+
+typedef enum configuration_id_t {
+ CFG_ID_TEST_CCR_FULL = 0,
+ CFG_ID_TEST_CCR_PARTIAL = 1,
+ CFG_ID_OXYGEN_CALIBRATION = 2,
+ CFG_ID_SERIAL = 3,
+ CFG_ID_DECO = 4,
+ CFG_ID_VERSION = 5,
+ CFG_ID_ASCENT = 6,
+ CFG_ID_AI = 7,
+ CFG_ID_CCR = 8,
+ CFG_ID_DILUENTS = 9,
+} configuration_id_t;
+
+typedef enum measure_id_t {
+ MEASURE_ID_OXYGEN = 0,
+ MEASURE_ID_BATTERY = 1,
+ MEASURE_ID_HELIUM = 2,
+ MEASURE_ID_OXYGEN_MV = 3,
+ MEASURE_ID_GPS = 4,
+ MEASURE_ID_PRESSURE = 5,
+ MEASURE_ID_AI_SAC = 6,
+ MEASURE_ID_AI_PRESSURE = 7,
+ MEASURE_ID_BRIGHTNESS = 8,
+ MEASURE_ID_AI_STAT = 9,
+} measure_id_t;
+
+typedef enum state_id_t {
+ STATE_ID_DECO_N2LOW = 0,
+ STATE_ID_DECO_N2HIGH = 1,
+ STATE_ID_DECO_HELOW = 2,
+ STATE_ID_DECO_HEHIGH = 3,
+ STATE_ID_PLAN_STEPS = 4,
+} state_id_t;
+
+typedef enum event_t {
+ EVENT_DUMMY = 0,
+ EVENT_SETPOINT_MANUAL = 1,
+ EVENT_SETPOINT_AUTO = 2,
+ EVENT_OC = 3,
+ EVENT_CCR = 4,
+ EVENT_MIX_CHANGED = 5,
+ EVENT_START = 6,
+ EVENT_TOO_FAST = 7,
+ EVENT_ABOVE_CEILING = 8,
+ EVENT_TOXIC = 9,
+ EVENT_HYPOX = 10,
+ EVENT_CRITICAL = 11,
+ EVENT_SENSOR_DISABLED = 12,
+ EVENT_SENSOR_ENABLED = 13,
+ EVENT_O2_BACKUP = 14,
+ EVENT_PEER_DOWN = 15,
+ EVENT_HS_DOWN = 16,
+ EVENT_INCONSISTENT = 17,
+ EVENT_KEYDOWN = 18,
+ EVENT_SCR = 19,
+ EVENT_ABOVE_STOP = 20,
+ EVENT_SAFETY_MISS = 21,
+ EVENT_FATAL = 22,
+ EVENT_DILUENT = 23,
+ EVENT_CHANGE_MODE = 24,
+ EVENT_SOLENOID = 25,
+ EVENT_BOOKMARK = 26,
+ EVENT_GF_SWITCH = 27,
+ EVENT_PEER_UP = 28,
+ EVENT_HS_UP = 29,
+ EVENT_CNS = 30,
+ EVENT_BATTERY_LOW = 31,
+ EVENT_PPO2_LOST = 32,
+ EVENT_SENSOR_VALUE_BAD = 33,
+ EVENT_SAFETY_STOP_END = 34,
+ EVENT_DECO_STOP_END = 35,
+ EVENT_DEEP_STOP_END = 36,
+ EVENT_NODECO_END = 37,
+ EVENT_DEPTH_REACHED = 38,
+ EVENT_TIME_ELAPSED = 39,
+ EVENT_STACK_USAGE = 40,
+ EVENT_GAS_SWITCH_INFO = 41,
+ EVENT_PRESSURE_SENS_WARN = 42,
+ EVENT_PRESSURE_SENS_FAIL = 43,
+ EVENT_CHECK_O2_SENSORS = 44,
+ EVENT_SWITCH_TO_COMP_SCR = 45,
+ EVENT_GAS_LOST = 46,
+ EVENT_AIRBREAK = 47,
+ EVENT_AIRBREAK_END = 48,
+ EVENT_AIRBREAK_MISSED = 49,
+ EVENT_BORMT_EXPIRATION = 50,
+ EVENT_BORMT_EXPIRED = 51,
+ EVENT_SENSOR_EXCLUDED = 52,
+ EVENT_PREBR_SKIPPED = 53,
+ EVENT_BOCCR_BORMT_EXPIRED = 54,
+ EVENT_WAYPOINT = 55,
+ EVENT_TURNAROUND = 56,
+ EVENT_SOLENOID_FAILURE = 57,
+ EVENT_SM_CYL_PRESS_DIFF = 58,
+ EVENT_BAILOUT_MOD_EXCEEDED = 59,
+} event_t;
+
+typedef enum divemode_t {
+ STMODE_UNKNOWN = 0,
+ STMODE_OC = 1,
+ STMODE_CCR = 2,
+ STMODE_MCCR = 3,
+ STMODE_FREE = 4,
+ STMODE_GAUGE = 5,
+ STMODE_ASCR = 6,
+ STMODE_PSCR = 7,
+ STMODE_BOCCR = 8,
+} divemode_t;
+
+typedef enum setpoint_change_t {
+ SP_MANUAL = 0,
+ SP_AUTO_START = 1,
+ SP_AUTO_HYPOX = 2,
+ SP_AUTO_TIMEOUT = 3,
+ SP_AUTO_ASCENT = 4,
+ SP_AUTO_STALL = 5,
+ SP_AUTO_SPLOW = 6,
+ SP_AUTO_DEPTH_DESC = 7,
+ SP_AUTO_DEPTH_ASC = 8,
+} setpoint_change_t;
+
+typedef enum sensor_state_t {
+ SENSTAT_NORMAL = 0,
+ SENSTAT_OVERRANGE = 1,
+ SENSTAT_DISABLED = 2,
+ SENSTAT_EXCLUDED = 3,
+ SENSTAT_UNCALIBRATED = 4,
+ SENSTAT_ERROR = 5,
+ SENSTAT_OFFLINE = 6,
+ SENSTAT_INHIBITED = 7,
+ SENSTAT_NOT_EXIST = 8,
+} sensor_state_t;
+
+typedef enum battery_state_t {
+ BATSTATE_NO_BATTERY = 0,
+ BATSTATE_UNKNOWN = 1,
+ BATSTATE_DISCHARGING = 2,
+ BATSTATE_CHARGING = 3,
+ BATSTATE_FULL = 4,
+} battery_state_t;
+
+typedef struct divesoft_freedom_gasmix_t {
+ unsigned int oxygen;
+ unsigned int helium;
+ unsigned int type;
+ unsigned int id;
+} divesoft_freedom_gasmix_t;
+
+typedef struct divesoft_freedom_tank_t {
+ unsigned int volume;
+ unsigned int workpressure;
+ unsigned int beginpressure;
+ unsigned int endpressure;
+ unsigned int transmitter;
+ unsigned int active;
+} divesoft_freedom_tank_t;
+
+typedef struct divesoft_freedom_parser_t {
+ dc_parser_t base;
+ // Cached fields.
+ unsigned int cached;
+ unsigned int version;
+ unsigned int headersize;
+ unsigned int divetime;
+ unsigned int divemode;
+ int temperature_min;
+ unsigned int maxdepth;
+ unsigned int atmospheric;
+ unsigned int avgdepth;
+ unsigned int ngasmixes;
+ divesoft_freedom_gasmix_t gasmix[NGASMIXES];
+ unsigned int diluent;
+ unsigned int ntanks;
+ divesoft_freedom_tank_t tank[NTANKS];
+ unsigned int vpm;
+ unsigned int gf_lo;
+ unsigned int gf_hi;
+ unsigned int seawater;
+ unsigned int calibration[NSENSORS];
+ unsigned int calibrated;
+} divesoft_freedom_parser_t;
+
+static dc_status_t divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
+static dc_status_t divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
+static dc_status_t divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
+static dc_status_t divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
+
+static const dc_parser_vtable_t divesoft_freedom_parser_vtable = {
+ sizeof(divesoft_freedom_parser_t),
+ DC_FAMILY_DIVESOFT_FREEDOM,
+ divesoft_freedom_parser_set_data, /* set_data */
+ NULL, /* set_clock */
+ NULL, /* set_atmospheric */
+ NULL, /* set_density */
+ divesoft_freedom_parser_get_datetime, /* datetime */
+ divesoft_freedom_parser_get_field, /* fields */
+ divesoft_freedom_parser_samples_foreach, /* samples_foreach */
+ NULL /* destroy */
+};
+
+static unsigned int
+divesoft_freedom_find_gasmix (divesoft_freedom_gasmix_t gasmix[], unsigned int count, unsigned int oxygen, unsigned int helium, unsigned int type)
+{
+ unsigned int i = 0;
+ while (i < count) {
+ if (oxygen == gasmix[i].oxygen &&
+ helium == gasmix[i].helium &&
+ type == gasmix[i].type)
+ break;
+ i++;
+ }
+
+ return i;
+}
+
+static unsigned int
+divesoft_freedom_find_tank (divesoft_freedom_tank_t tank[], unsigned int count, unsigned int transmitter)
+{
+ unsigned int i = 0;
+ while (i < count) {
+ if (transmitter == tank[i].transmitter)
+ break;
+ i++;
+ }
+
+ return i;
+}
+
+static unsigned int
+divesoft_freedom_is_ccr (unsigned int divemode)
+{
+ return divemode == STMODE_CCR || divemode == STMODE_MCCR ||
+ divemode == STMODE_ASCR || divemode == STMODE_PSCR ||
+ divemode == STMODE_BOCCR;
+}
+
+static dc_status_t
+divesoft_freedom_cache (divesoft_freedom_parser_t *parser)
+{
+ dc_parser_t *abstract = (dc_parser_t *) parser;
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ if (parser->cached) {
+ return DC_STATUS_SUCCESS;
+ }
+
+ unsigned int headersize = 4;
+ if (size < headersize) {
+ ERROR (abstract->context, "Unexpected header size (%u).", size);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ unsigned int version = array_uint32_le (data);
+ if (version == HEADER_SIGNATURE_V1) {
+ headersize = HEADER_SIZE_V1;
+ } else if (version == HEADER_SIGNATURE_V2) {
+ headersize = HEADER_SIZE_V2;
+ } else {
+ ERROR (abstract->context, "Unexpected header version (%08x).", version);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ if (size < headersize) {
+ ERROR (abstract->context, "Unexpected header size (%u).", size);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ unsigned short crc = array_uint16_le (data + 4);
+ unsigned short ccrc = checksum_crc16r_ansi (data + 6, headersize - 6, 0xFFFF, 0x0000);
+ if (crc != ccrc) {
+ ERROR (abstract->context, "Invalid header checksum (%04x %04x).", crc, ccrc);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ // Parse the dive header.
+ unsigned int divetime = 0;
+ unsigned int divemode = 0;
+ unsigned int temperature_min = 0;
+ unsigned int maxdepth = 0;
+ unsigned int atmospheric = 0;
+ unsigned int avgdepth = 0;
+ unsigned int diluent_o2 = 0, diluent_he = 0;
+ if (version == HEADER_SIGNATURE_V1) {
+ unsigned int misc1 = array_uint32_le (data + 12);
+ unsigned int misc2 = array_uint32_le (data + 16);
+ divetime = misc1 & 0x1FFFF;
+ divemode = (misc1 & 0x38000000) >> 27;
+ temperature_min = (signed int) signextend ((misc2 & 0xFFC0000) >> 18, 10);
+ maxdepth = array_uint16_le (data + 20);
+ atmospheric = array_uint16_le (data + 24);
+ avgdepth = 0;
+ diluent_o2 = data[26];
+ diluent_he = data[27];
+ } else {
+ divetime = array_uint32_le (data + 12);
+ divemode = data[18];
+ temperature_min = (signed short) array_uint16_le (data + 24);
+ maxdepth = array_uint16_le (data + 28);
+ atmospheric = array_uint16_le (data + 32);
+ avgdepth = array_uint16_le (data + 38);
+ diluent_o2 = 0;
+ diluent_he = 0;
+
+ DEBUG (abstract->context, "Device: serial=%.4s-%.8s",
+ data + 52, data + 56);
+ }
+
+ divesoft_freedom_gasmix_t gasmix_ai[NGASMIXES] = {0},
+ gasmix_diluent[NGASMIXES] = {0},
+ gasmix_event[NGASMIXES] = {0};
+ unsigned int ngasmix_ai = 0,
+ ngasmix_diluent = 0,
+ ngasmix_event = 0;
+ divesoft_freedom_tank_t tank[NTANKS] = {0};
+ unsigned int ntanks = 0;
+
+ unsigned int vpm = 0, gf_lo = 0, gf_hi = 0;
+ unsigned int seawater = 0;
+ unsigned int calibration[NSENSORS] = {0};
+ unsigned int calibrated = 0;
+
+ unsigned int gasmixid_previous = UNDEFINED;
+
+
+ // Parse the dive profile.
+ unsigned int offset = headersize;
+ while (offset + RECORD_SIZE <= size) {
+ if (array_isequal(data + offset, RECORD_SIZE, 0xFF)) {
+ WARNING (abstract->context, "Skipping empty sample.");
+ offset += RECORD_SIZE;
+ continue;
+ }
+
+ unsigned int flags = array_uint32_le (data + offset);
+ unsigned int type = (flags & 0x0000000F) >> 0;
+ unsigned int id = (flags & 0x7FE00000) >> 21;
+
+ if (type == LREC_CONFIGURATION) {
+ // Configuration record.
+ if (id == CFG_ID_DECO) {
+ unsigned int misc = array_uint16_le (data + offset + 4);
+ gf_lo = data[offset + 8];
+ gf_hi = data[offset + 9];
+ seawater = misc & 0x02;
+ vpm = misc & 0x20;
+ } else if (id == CFG_ID_VERSION) {
+ DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u flags=%u",
+ data[offset + 4],
+ data[offset + 5], data[offset + 6],
+ data[offset + 7], data[offset + 8], data[offset + 9],
+ array_uint32_le (data + offset + 12),
+ array_uint16_le (data + offset + 10));
+ } else if (id == CFG_ID_SERIAL) {
+ DEBUG (abstract->context, "Device: serial=%.4s-%.8s",
+ data + offset + 4, data + offset + 8);
+ } else if (id == CFG_ID_DILUENTS) {
+ for (unsigned int i = 0; i < 4; ++i) {
+ unsigned int o2 = data[offset + 4 + i * 3 + 0];
+ unsigned int he = data[offset + 4 + i * 3 + 1];
+ unsigned int state = data[offset + 4 + i * 3 + 2];
+ if (state & 0x01) {
+ if (ngasmix_diluent >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix_diluent[ngasmix_diluent].oxygen = o2;
+ gasmix_diluent[ngasmix_diluent].helium = he;
+ gasmix_diluent[ngasmix_diluent].type = DILUENT;
+ gasmix_diluent[ngasmix_diluent].id = (state & 0xFE) >> 1;
+ ngasmix_diluent++;
+ }
+ }
+ } else if (id == CFG_ID_OXYGEN_CALIBRATION) {
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ calibration[i] = array_uint16_le (data + offset + 4 + i * 2);
+ }
+ calibrated = 1;
+ } else if (id == CFG_ID_AI) {
+ unsigned int o2 = data[offset + 4];
+ unsigned int he = data[offset + 5];
+ unsigned int volume = array_uint16_le (data + offset + 6);
+ unsigned int workpressure = array_uint16_le (data + offset + 8);
+ unsigned int transmitter = data[offset + 10];
+ unsigned int gasmixid = data[offset + 11];
+
+ // Workaround for a bug in some pre-release firmware versions,
+ // where the ID of the CCR gas mixes (oxygen and diluent) is
+ // not stored correctly.
+ if (gasmixid < 10 && gasmixid <= gasmixid_previous && gasmixid_previous != UNDEFINED) {
+ WARNING (abstract->context, "Fixed the CCR gas mix id (%u -> %u) for tank %u.",
+ gasmixid, gasmixid + 10, ntanks);
+ gasmixid += 10;
+ }
+ gasmixid_previous = gasmixid;
+
+ // Add the gas mix.
+ if (ngasmix_ai >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix_ai[ngasmix_ai].oxygen = o2;
+ gasmix_ai[ngasmix_ai].helium = he;
+ if (gasmixid == 10) {
+ gasmix_ai[ngasmix_ai].type = OXYGEN;
+ } else if (gasmixid == 11) {
+ gasmix_ai[ngasmix_ai].type = DILUENT;
+ } else {
+ gasmix_ai[ngasmix_ai].type = OC;
+ }
+ gasmix_ai[ngasmix_ai].id = gasmixid;
+ ngasmix_ai++;
+
+ // Add the tank.
+ if (ntanks >= NTANKS) {
+ ERROR (abstract->context, "Maximum number of tanks reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ tank[ntanks].volume = volume;
+ tank[ntanks].workpressure = workpressure;
+ tank[ntanks].transmitter = transmitter;
+ ntanks++;
+ }
+ } else if ((type >= LREC_MANIPULATION && type <= LREC_ACTIVITY) || type == LREC_INFO) {
+ // Event record.
+ unsigned int event = array_uint16_le (data + offset + 4);
+
+ if (event == EVENT_MIX_CHANGED || event == EVENT_DILUENT || event == EVENT_CHANGE_MODE) {
+ unsigned int o2 = data[offset + 6];
+ unsigned int he = data[offset + 7];
+ unsigned int mixtype = OC;
+ if (event == EVENT_DILUENT) {
+ mixtype = DILUENT;
+ } else if (event == EVENT_CHANGE_MODE) {
+ unsigned int mode = data[offset + 8];
+ if (divesoft_freedom_is_ccr (mode)) {
+ mixtype = DILUENT;
+ }
+ }
+
+ unsigned int idx = divesoft_freedom_find_gasmix (gasmix_event, ngasmix_event, o2, he, mixtype);
+ if (idx >= ngasmix_event) {
+ if (ngasmix_event >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix_event[ngasmix_event].oxygen = o2;
+ gasmix_event[ngasmix_event].helium = he;
+ gasmix_event[ngasmix_event].type = mixtype;
+ gasmix_event[ngasmix_event].id = UNDEFINED;
+ ngasmix_event++;
+ }
+ }
+ } else if (type == LREC_MEASURE) {
+ // Measurement record.
+ if (id == MEASURE_ID_AI_PRESSURE) {
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ unsigned int pressure = data[offset + 4 + i];
+ if (pressure == 0 || pressure == 0xFF)
+ continue;
+
+ unsigned int idx = divesoft_freedom_find_tank (tank, ntanks, i);
+ if (idx >= ntanks) {
+ ERROR (abstract->context, "Tank %u not found.", idx);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ if (!tank[idx].active) {
+ tank[idx].active = 1;
+ tank[idx].beginpressure = pressure;
+ tank[idx].endpressure = pressure;
+ }
+ tank[idx].endpressure = pressure;
+ }
+ }
+ }
+
+ offset += RECORD_SIZE;
+ }
+
+ unsigned int ngasmixes = 0;
+ divesoft_freedom_gasmix_t gasmix[NGASMIXES] = {0};
+ unsigned int diluent = UNDEFINED;
+
+ // Add the gas mixes from the AI integration records.
+ for (unsigned int i = 0; i < ngasmix_ai; ++i) {
+ gasmix[ngasmixes] = gasmix_ai[i];
+ ngasmixes++;
+ }
+
+ // Add the gas mixes from the diluent records.
+ for (unsigned int i = 0; i < ngasmix_diluent; ++i) {
+ unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes,
+ gasmix_diluent[i].oxygen, gasmix_diluent[i].helium, gasmix_diluent[i].type);
+ if (idx >= ngasmixes) {
+ if (ngasmixes >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix[ngasmixes] = gasmix_diluent[i];
+ ngasmixes++;
+ }
+ }
+
+ // Add the initial diluent.
+ if (divesoft_freedom_is_ccr (divemode) &&
+ (diluent_o2 != 0 || diluent_he != 0)) {
+ unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes,
+ diluent_o2, diluent_he, DILUENT);
+ if (idx >= ngasmixes) {
+ if (ngasmixes >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix[ngasmixes].oxygen = diluent_o2;
+ gasmix[ngasmixes].helium = diluent_he;
+ gasmix[ngasmixes].type = DILUENT;
+ gasmix[ngasmixes].id = UNDEFINED;
+ ngasmixes++;
+ }
+
+ // Index of the initial diluent.
+ diluent = idx;
+ }
+
+ // Add the gas mixes from the gas change events.
+ for (unsigned int i = 0; i < ngasmix_event; ++i) {
+ unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes,
+ gasmix_event[i].oxygen, gasmix_event[i].helium, gasmix_event[i].type);
+ if (idx >= ngasmixes) {
+ if (ngasmixes >= NGASMIXES) {
+ ERROR (abstract->context, "Maximum number of gas mixes reached.");
+ return DC_STATUS_NOMEMORY;
+ }
+ gasmix[ngasmixes] = gasmix_event[i];
+ ngasmixes++;
+ }
+ }
+
+ // Cache the data for later use.
+ parser->cached = 1;
+ parser->version = version;
+ parser->headersize = headersize;
+ parser->divetime = divetime;
+ parser->divemode = divemode;
+ parser->temperature_min = temperature_min;
+ parser->maxdepth = maxdepth;
+ parser->atmospheric = atmospheric;
+ parser->avgdepth = avgdepth;
+ parser->ngasmixes = ngasmixes;
+ for (unsigned int i = 0; i < ngasmixes; ++i) {
+ parser->gasmix[i] = gasmix[i];
+ }
+ parser->diluent = diluent;
+ parser->ntanks = ntanks;
+ for (unsigned int i = 0; i < ntanks; ++i) {
+ parser->tank[i] = tank[i];
+ }
+ parser->vpm = vpm;
+ parser->gf_lo = gf_lo;
+ parser->gf_hi = gf_hi;
+ parser->seawater = seawater;
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ parser->calibration[i] = calibration[i];
+ }
+ parser->calibrated = calibrated;
+
+ return DC_STATUS_SUCCESS;
+}
+
+dc_status_t
+divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context)
+{
+ divesoft_freedom_parser_t *parser = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ parser = (divesoft_freedom_parser_t *) dc_parser_allocate (context, &divesoft_freedom_parser_vtable);
+ if (parser == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Set the default values.
+ parser->cached = 0;
+ parser->version = 0;
+ parser->headersize = 0;
+ parser->divetime = 0;
+ parser->divemode = 0;
+ parser->temperature_min = 0;
+ parser->maxdepth = 0;
+ parser->atmospheric = 0;
+ parser->avgdepth = 0;
+ parser->ngasmixes = 0;
+ for (unsigned int i = 0; i < NGASMIXES; ++i) {
+ parser->gasmix[i].oxygen = 0;
+ parser->gasmix[i].helium = 0;
+ parser->gasmix[i].type = 0;
+ parser->gasmix[i].id = 0;
+ }
+ parser->diluent = UNDEFINED;
+ parser->ntanks = 0;
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i].volume = 0;
+ parser->tank[i].workpressure = 0;
+ parser->tank[i].beginpressure = 0;
+ parser->tank[i].endpressure = 0;
+ parser->tank[i].transmitter = 0;
+ parser->tank[i].active = 0;
+ }
+ parser->vpm = 0;
+ parser->gf_lo = 0;
+ parser->gf_hi = 0;
+ parser->seawater = 0;
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ parser->calibration[i] = 0;
+ }
+ parser->calibrated = 0;
+
+ *out = (dc_parser_t *) parser;
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
+{
+ divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract;
+
+ // Reset the cache.
+ parser->cached = 0;
+ parser->version = 0;
+ parser->headersize = 0;
+ parser->divetime = 0;
+ parser->divemode = 0;
+ parser->temperature_min = 0;
+ parser->maxdepth = 0;
+ parser->atmospheric = 0;
+ parser->avgdepth = 0;
+ parser->ngasmixes = 0;
+ for (unsigned int i = 0; i < NGASMIXES; ++i) {
+ parser->gasmix[i].oxygen = 0;
+ parser->gasmix[i].helium = 0;
+ parser->gasmix[i].type = 0;
+ parser->gasmix[i].id = 0;
+ }
+ parser->diluent = UNDEFINED;
+ parser->ntanks = 0;
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i].volume = 0;
+ parser->tank[i].workpressure = 0;
+ parser->tank[i].beginpressure = 0;
+ parser->tank[i].endpressure = 0;
+ parser->tank[i].transmitter = 0;
+ parser->tank[i].active = 0;
+ }
+ parser->vpm = 0;
+ parser->gf_lo = 0;
+ parser->gf_hi = 0;
+ parser->seawater = 0;
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ parser->calibration[i] = 0;
+ }
+ parser->calibrated = 0;
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract;
+ const unsigned char *data = abstract->data;
+
+ // Cache the header data.
+ status = divesoft_freedom_cache (parser);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ unsigned int timestamp = array_uint32_le (data + 8);
+ dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH;
+
+ if (!dc_datetime_gmtime (datetime, ticks))
+ return DC_STATUS_DATAFORMAT;
+
+ if (parser->version == HEADER_SIGNATURE_V2) {
+ datetime->timezone = ((signed short) array_uint16_le (data + 40)) * 60;
+ } else {
+ datetime->timezone = DC_TIMEZONE_NONE;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract;
+
+ // Cache the header data.
+ status = divesoft_freedom_cache (parser);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ dc_salinity_t *water = (dc_salinity_t *) value;
+ dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
+ dc_tank_t *tank = (dc_tank_t *) value;
+ dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
+
+ if (value) {
+ switch (type) {
+ case DC_FIELD_DIVETIME:
+ *((unsigned int *) value) = parser->divetime;
+ break;
+ case DC_FIELD_MAXDEPTH:
+ *((double *) value) = parser->maxdepth / 100.0;
+ break;
+ case DC_FIELD_AVGDEPTH:
+ if (parser->version != HEADER_SIGNATURE_V2)
+ return DC_STATUS_UNSUPPORTED;
+ *((double *) value) = parser->avgdepth / 100.0;
+ break;
+ case DC_FIELD_TEMPERATURE_MINIMUM:
+ *((double *) value) = parser->temperature_min / 10.0;
+ break;
+ case DC_FIELD_ATMOSPHERIC:
+ *((double *) value) = parser->atmospheric * 10.0 / BAR;
+ break;
+ case DC_FIELD_SALINITY:
+ water->type = parser->seawater ? DC_WATER_SALT : DC_WATER_FRESH;
+ water->density = parser->seawater ? SEAWATER : FRESHWATER;
+ break;
+ case DC_FIELD_DIVEMODE:
+ switch (parser->divemode) {
+ case STMODE_OC:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
+ break;
+ case STMODE_CCR:
+ case STMODE_MCCR:
+ case STMODE_BOCCR:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_CCR;
+ break;
+ case STMODE_FREE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
+ break;
+ case STMODE_GAUGE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
+ break;
+ case STMODE_ASCR:
+ case STMODE_PSCR:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_SCR;
+ break;
+ case STMODE_UNKNOWN:
+ return DC_STATUS_UNSUPPORTED;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
+ break;
+ case DC_FIELD_GASMIX_COUNT:
+ *((unsigned int *) value) = parser->ngasmixes;
+ break;
+ case DC_FIELD_GASMIX:
+ gasmix->helium = parser->gasmix[flags].helium / 100.0;
+ gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
+ gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
+ break;
+ case DC_FIELD_TANK_COUNT:
+ *((unsigned int *) value) = parser->ntanks;
+ break;
+ case DC_FIELD_TANK:
+ if (parser->tank[flags].volume > 990 ||
+ parser->tank[flags].workpressure > 400) {
+ tank->type = DC_TANKVOLUME_NONE;
+ tank->volume = 0.0;
+ tank->workpressure = 0.0;
+ } else {
+ tank->type = DC_TANKVOLUME_METRIC;
+ tank->volume = parser->tank[flags].volume / 10.0;
+ tank->workpressure = parser->tank[flags].workpressure;
+ }
+ tank->beginpressure = parser->tank[flags].beginpressure * 2.0;
+ tank->endpressure = parser->tank[flags].endpressure * 2.0;
+ tank->gasmix = flags;
+ break;
+ case DC_FIELD_DECOMODEL:
+ if (parser->vpm) {
+ decomodel->type = DC_DECOMODEL_VPM;
+ decomodel->conservatism = 0;
+ } else {
+ decomodel->type = DC_DECOMODEL_BUHLMANN;
+ decomodel->conservatism = 0;
+ decomodel->params.gf.low = parser->gf_lo;
+ decomodel->params.gf.high = parser->gf_hi;
+ }
+ break;
+ default:
+ return DC_STATUS_UNSUPPORTED;
+ }
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract;
+ const unsigned char *data = abstract->data;
+ unsigned int size = abstract->size;
+
+ // Cache the header data.
+ status = divesoft_freedom_cache (parser);
+ if (status != DC_STATUS_SUCCESS)
+ return status;
+
+ unsigned int time = UNDEFINED;
+ unsigned int initial = 0;
+ unsigned int offset = parser->headersize;
+ while (offset + RECORD_SIZE <= size) {
+ dc_sample_value_t sample = {0};
+
+ if (array_isequal(data + offset, RECORD_SIZE, 0xFF)) {
+ WARNING (abstract->context, "Skipping empty sample.");
+ offset += RECORD_SIZE;
+ continue;
+ }
+
+ unsigned int flags = array_uint32_le (data + offset);
+ unsigned int type = (flags & 0x0000000F) >> 0;
+ unsigned int timestamp = (flags & 0x001FFFF0) >> 4;
+ unsigned int id = (flags & 0x7FE00000) >> 21;
+
+ if (timestamp != time) {
+ if (timestamp < time && time != UNDEFINED) {
+ // The timestamp are supposed to be monotonically increasing,
+ // but occasionally there are small jumps back in time with just
+ // 1 or 2 seconds. To get back in sync, those samples are
+ // skipped. Larger jumps are treated as errors.
+ if (time - timestamp > 5) {
+ ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
+ return DC_STATUS_DATAFORMAT;
+ }
+ WARNING (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
+ offset += RECORD_SIZE;
+ continue;
+ }
+ time = timestamp;
+ sample.time = time;
+ if (callback) callback(DC_SAMPLE_TIME, sample, userdata);
+ }
+
+ // Initial diluent.
+ if (!initial) {
+ if (parser->diluent != UNDEFINED) {
+ sample.gasmix = parser->diluent;
+ if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
+ }
+ initial = 1;
+ }
+
+ if (type == LREC_POINT) {
+ // General log record.
+ unsigned int depth = array_uint16_le (data + offset + 4);
+ unsigned int ppo2 = array_uint16_le (data + offset + 6);
+
+ sample.depth = depth / 100.0;
+ if (callback) callback(DC_SAMPLE_DEPTH, sample, userdata);
+
+ if (ppo2) {
+ sample.ppo2 = ppo2 * 10.0 / BAR;
+ if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
+ }
+
+ if (id == POINT_2) {
+ unsigned int orientation = array_uint32_le (data + offset + 8);
+ unsigned int heading = orientation & 0x1FF;
+ sample.bearing = heading;
+ if (callback) callback (DC_SAMPLE_BEARING, sample, userdata);
+ } else if (id == POINT_1 || id == POINT_1_OLD) {
+ unsigned int misc = array_uint32_le (data + offset + 8);
+ unsigned int ceiling = array_uint16_le (data + offset + 12);
+ unsigned int setpoint = data[offset + 15];
+ unsigned int ndl = (misc & 0x000003FF);
+ unsigned int tts = (misc & 0x000FFC00) >> 10;
+ unsigned int temp = (misc & 0x3FF00000) >> 20;
+
+ // Temperature
+ sample.temperature = (signed int) signextend (temp, 10) / 10.0;
+ if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
+
+ // Deco / NDL
+ if (ceiling) {
+ sample.deco.type = DC_DECO_DECOSTOP;
+ sample.deco.time = 0;
+ sample.deco.depth = ceiling / 100.0;
+ } else {
+ sample.deco.type = DC_DECO_NDL;
+ sample.deco.time = ndl * 60;
+ sample.deco.depth = 0.0;
+ }
+ if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
+
+ // Setpoint
+ if (setpoint) {
+ sample.setpoint = setpoint / 100.0;
+ if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata);
+ }
+ }
+ } else if ((type >= LREC_MANIPULATION && type <= LREC_ACTIVITY) || type == LREC_INFO) {
+ // Event record.
+ unsigned int event = array_uint16_le (data + offset + 4);
+
+ if (event == EVENT_BOOKMARK) {
+ sample.event.type = SAMPLE_EVENT_BOOKMARK;
+ sample.event.time = 0;
+ sample.event.flags = 0;
+ sample.event.value = 0;
+ if (callback) callback(DC_SAMPLE_EVENT, sample, userdata);
+ } else if (event == EVENT_MIX_CHANGED || event == EVENT_DILUENT || event == EVENT_CHANGE_MODE) {
+ unsigned int o2 = data[offset + 6];
+ unsigned int he = data[offset + 7];
+ unsigned int mixtype = OC;
+ if (event == EVENT_DILUENT) {
+ mixtype = DILUENT;
+ } else if (event == EVENT_CHANGE_MODE) {
+ unsigned int mode = data[offset + 8];
+ if (divesoft_freedom_is_ccr (mode)) {
+ mixtype = DILUENT;
+ }
+ }
+
+ unsigned int idx = divesoft_freedom_find_gasmix (parser->gasmix, parser->ngasmixes, o2, he, mixtype);
+ if (idx >= parser->ngasmixes) {
+ ERROR (abstract->context, "Gas mix (%u/%u) not found.", o2, he);
+ return DC_STATUS_DATAFORMAT;
+ }
+ sample.gasmix = idx;
+ if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
+ } else if (event == EVENT_CNS) {
+ sample.cns = array_uint16_le (data + offset + 6) / 100.0;
+ if (callback) callback(DC_SAMPLE_CNS, sample, userdata);
+ } else if (event == EVENT_SETPOINT_MANUAL || event == EVENT_SETPOINT_AUTO) {
+ sample.setpoint = data[6] / 100.0;
+ if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata);
+ }
+ } else if (type == LREC_MEASURE) {
+ // Measurement record.
+ if (id == MEASURE_ID_AI_PRESSURE) {
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ unsigned int pressure = data[offset + 4 + i];
+ if (pressure == 0 || pressure == 0xFF)
+ continue;
+
+ unsigned int idx = divesoft_freedom_find_tank (parser->tank, parser->ntanks, i);
+ if (idx >= parser->ntanks) {
+ ERROR (abstract->context, "Tank %u not found.", idx);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ sample.pressure.tank = idx;
+ sample.pressure.value = pressure * 2.0;
+ if (callback) callback(DC_SAMPLE_PRESSURE, sample, userdata);
+ }
+ } else if (id == MEASURE_ID_OXYGEN) {
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ unsigned int ppo2 = array_uint16_le (data + offset + 4 + i * 2);
+ if (ppo2 == 0 || ppo2 == 0xFFFF)
+ continue;
+ sample.ppo2 = ppo2 * 10.0 / BAR;
+ if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
+ }
+ } else if (id == MEASURE_ID_OXYGEN_MV) {
+ for (unsigned int i = 0; i < NSENSORS; ++i) {
+ unsigned int value = array_uint16_le (data + offset + 4 + i * 2);
+ unsigned int state = data[offset + 12 + i];
+ if (!parser->calibrated || state == SENSTAT_UNCALIBRATED ||
+ state == SENSTAT_NOT_EXIST)
+ continue;
+ sample.ppo2 = value / 100.0 * parser->calibration[i] / BAR;
+ if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
+ }
+ }
+ } else if (type == LREC_STATE) {
+ // Tissue saturation record.
+ }
+
+ offset += RECORD_SIZE;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c
index 0410cd2..9841a6f 100644
--- a/src/divesystem_idive.c
+++ b/src/divesystem_idive.c
@@ -29,6 +29,7 @@
#include "platform.h"
#include "checksum.h"
#include "array.h"
+#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable)
@@ -102,6 +103,7 @@ typedef struct divesystem_idive_device_t {
static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
+static dc_status_t divesystem_idive_device_close (dc_device_t *abstract);
static const dc_device_vtable_t divesystem_idive_device_vtable = {
sizeof(divesystem_idive_device_t),
@@ -112,7 +114,7 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = {
NULL, /* dump */
divesystem_idive_device_foreach, /* foreach */
divesystem_idive_device_timesync, /* timesync */
- NULL /* close */
+ divesystem_idive_device_close /* close */
};
static const divesystem_idive_commands_t idive = {
@@ -152,6 +154,7 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
{
dc_status_t status = DC_STATUS_SUCCESS;
divesystem_idive_device_t *device = NULL;
+ dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL)
return DC_STATUS_INVALIDARGS;
@@ -164,22 +167,32 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
}
// Set the default values.
- device->iostream = iostream;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
device->model = model;
+ // Create the packet stream.
+ if (transport == DC_TRANSPORT_BLE) {
+ status = dc_packet_open (&device->iostream, context, iostream, 244, 244);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the packet stream.");
+ goto error_free;
+ }
+ } else {
+ device->iostream = iostream;
+ }
+
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
- goto error_free;
+ goto error_free_iostream;
}
// Set the timeout for receiving data (1000ms).
status = dc_iostream_set_timeout (device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
- goto error_free;
+ goto error_free_iostream;
}
// Make sure everything is in a sane state.
@@ -190,11 +203,27 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
return DC_STATUS_SUCCESS;
+error_free_iostream:
+ if (transport == DC_TRANSPORT_BLE) {
+ dc_iostream_close (device->iostream);
+ }
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
+static dc_status_t
+divesystem_idive_device_close (dc_device_t *abstract)
+{
+ divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract;
+
+ // Close the packet stream.
+ if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
+ return dc_iostream_close (device->iostream);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
static dc_status_t
divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
@@ -231,7 +260,7 @@ divesystem_idive_send (divesystem_idive_device_t *device, const unsigned char co
packet[0] = START;
packet[1] = csize;
memcpy(packet + 2, command, csize);
- crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff);
+ crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff, 0x0000);
packet[csize + 2] = (crc >> 8) & 0xFF;
packet[csize + 3] = (crc ) & 0xFF;
@@ -292,7 +321,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
// Verify the checksum.
unsigned short crc = array_uint16_be (packet + len + 2);
- unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum.");
return DC_STATUS_PROTOCOL;
diff --git a/src/hdlc.c b/src/hdlc.c
new file mode 100644
index 0000000..40e1ae7
--- /dev/null
+++ b/src/hdlc.c
@@ -0,0 +1,406 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include // malloc, free
+
+#include "hdlc.h"
+
+#include "iostream-private.h"
+#include "common-private.h"
+#include "context-private.h"
+
+#define END 0x7E
+#define ESC 0x7D
+#define ESC_BIT 0x20
+
+static dc_status_t dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout);
+static dc_status_t dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value);
+static dc_status_t dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
+static dc_status_t dc_hdlc_poll (dc_iostream_t *abstract, int timeout);
+static dc_status_t dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual);
+static dc_status_t dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual);
+static dc_status_t dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size);
+static dc_status_t dc_hdlc_flush (dc_iostream_t *abstract);
+static dc_status_t dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction);
+static dc_status_t dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds);
+static dc_status_t dc_hdlc_close (dc_iostream_t *abstract);
+
+typedef struct dc_hdlc_t {
+ /* Base class. */
+ dc_iostream_t base;
+ /* Internal state. */
+ dc_context_t *context;
+ dc_iostream_t *iostream;
+ unsigned char *rbuf;
+ unsigned char *wbuf;
+ size_t rbuf_size;
+ size_t rbuf_offset;
+ size_t rbuf_available;
+ size_t wbuf_size;
+ size_t wbuf_offset;
+} dc_hdlc_t;
+
+static const dc_iostream_vtable_t dc_hdlc_vtable = {
+ sizeof(dc_hdlc_t),
+ dc_hdlc_set_timeout, /* set_timeout */
+ dc_hdlc_set_break, /* set_break */
+ dc_hdlc_set_dtr, /* set_dtr */
+ dc_hdlc_set_rts, /* set_rts */
+ dc_hdlc_get_lines, /* get_lines */
+ NULL, /* get_available */
+ dc_hdlc_configure, /* configure */
+ dc_hdlc_poll, /* poll */
+ dc_hdlc_read, /* read */
+ dc_hdlc_write, /* write */
+ dc_hdlc_ioctl, /* ioctl */
+ dc_hdlc_flush, /* flush */
+ dc_hdlc_purge, /* purge */
+ dc_hdlc_sleep, /* sleep */
+ dc_hdlc_close, /* close */
+};
+
+dc_status_t
+dc_hdlc_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_hdlc_t *hdlc = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ if (base == NULL || isize == 0 || osize == 0)
+ return DC_STATUS_INVALIDARGS;
+
+ dc_transport_t transport = dc_iostream_get_transport (base);
+
+ // Allocate memory.
+ hdlc = (dc_hdlc_t *) dc_iostream_allocate (NULL, &dc_hdlc_vtable, transport);
+ if (hdlc == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ // Allocate the read buffer.
+ hdlc->rbuf = malloc (isize);
+ if (hdlc->rbuf == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free;
+ }
+
+ // Allocate the write buffer.
+ hdlc->wbuf = malloc (osize);
+ if (hdlc->wbuf == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free_rbuf;
+ }
+
+ hdlc->context = context;
+ hdlc->iostream = base;
+ hdlc->rbuf_size = isize;
+ hdlc->rbuf_offset = 0;
+ hdlc->rbuf_available = 0;
+ hdlc->wbuf_size = osize;
+ hdlc->wbuf_offset = 0;
+
+ *out = (dc_iostream_t *) hdlc;
+
+ return DC_STATUS_SUCCESS;
+
+error_free_rbuf:
+ free (hdlc->rbuf);
+error_free:
+ dc_iostream_deallocate ((dc_iostream_t *) hdlc);
+error_exit:
+ return status;
+}
+
+static dc_status_t
+dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_set_timeout (hdlc->iostream, timeout);
+}
+
+static dc_status_t
+dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_set_break (hdlc->iostream, value);
+}
+
+static dc_status_t
+dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_set_dtr (hdlc->iostream, value);
+}
+
+static dc_status_t
+dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_set_rts (hdlc->iostream, value);
+}
+
+static dc_status_t
+dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_get_lines (hdlc->iostream, value);
+}
+
+static dc_status_t
+dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_configure (hdlc->iostream, baudrate, databits, parity, stopbits, flowcontrol);
+}
+
+static dc_status_t
+dc_hdlc_poll (dc_iostream_t *abstract, int timeout)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ if (hdlc->rbuf_available) {
+ return DC_STATUS_SUCCESS;
+ }
+
+ return dc_iostream_poll (hdlc->iostream, timeout);
+}
+
+static dc_status_t
+dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+ size_t nbytes = 0;
+
+ unsigned int initialized = 0;
+ unsigned int escaped = 0;
+
+ while (1) {
+ if (hdlc->rbuf_available == 0) {
+ // Read a packet into the cache.
+ size_t len = 0;
+ status = dc_iostream_read (hdlc->iostream, hdlc->rbuf, hdlc->rbuf_size, &len);
+ if (status != DC_STATUS_SUCCESS) {
+ goto out;
+ }
+
+ hdlc->rbuf_available = len;
+ hdlc->rbuf_offset = 0;
+ }
+
+ while (hdlc->rbuf_available) {
+ unsigned char c = hdlc->rbuf[hdlc->rbuf_offset];
+ hdlc->rbuf_offset++;
+ hdlc->rbuf_available--;
+
+ if (c == END) {
+ if (escaped) {
+ ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c);
+ status = DC_STATUS_IO;
+ goto out;
+ }
+
+ if (initialized) {
+ goto out;
+ }
+
+ initialized = 1;
+ continue;
+ }
+
+ if (!initialized) {
+ continue;
+ }
+
+ if (c == ESC) {
+ if (escaped) {
+ ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c);
+ status = DC_STATUS_IO;
+ goto out;
+ }
+ escaped = 1;
+ continue;
+ }
+
+ if (escaped) {
+ c ^= ESC_BIT;
+ escaped = 0;
+ }
+
+ if (nbytes < size)
+ ((unsigned char *)data)[nbytes] = c;
+ nbytes++;
+ }
+ }
+
+out:
+ if (nbytes > size) {
+ ERROR (hdlc->context, "HDLC frame is too large (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", nbytes, size);
+ dc_status_set_error (&status, DC_STATUS_IO);
+ nbytes = size;
+ }
+
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+ size_t nbytes = 0;
+
+ // Clear the buffer.
+ hdlc->wbuf_offset = 0;
+
+ // Start of the packet.
+ hdlc->wbuf[hdlc->wbuf_offset++] = END;
+
+ // Flush the buffer if necessary.
+ if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
+ status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ goto out;
+ }
+
+ hdlc->wbuf_offset = 0;
+ }
+
+ while (nbytes < size) {
+ unsigned char c = ((const unsigned char *) data)[nbytes];
+
+ if (c == END || c == ESC) {
+ // Append the escape character.
+ hdlc->wbuf[hdlc->wbuf_offset++] = ESC;
+
+ // Flush the buffer if necessary.
+ if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
+ status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ goto out;
+ }
+
+ hdlc->wbuf_offset = 0;
+ }
+
+ // Escape the character.
+ c ^= ESC_BIT;
+ }
+
+ // Append the character.
+ hdlc->wbuf[hdlc->wbuf_offset++] = c;
+
+ // Flush the buffer if necessary.
+ if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
+ status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ goto out;
+ }
+
+ hdlc->wbuf_offset = 0;
+ }
+
+ nbytes++;
+ }
+
+ // End of the packet.
+ hdlc->wbuf[hdlc->wbuf_offset++] = END;
+
+ // Flush the buffer.
+ status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
+ if (status != DC_STATUS_SUCCESS) {
+ goto out;
+ }
+
+ hdlc->wbuf_offset = 0;
+
+out:
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_ioctl (hdlc->iostream, request, data, size);
+}
+
+static dc_status_t
+dc_hdlc_flush (dc_iostream_t *abstract)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_flush (hdlc->iostream);
+}
+
+static dc_status_t
+dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ if (direction & DC_DIRECTION_INPUT) {
+ hdlc->rbuf_available = 0;
+ hdlc->rbuf_offset = 0;
+ }
+
+ return dc_iostream_purge (hdlc->iostream, direction);
+}
+
+static dc_status_t
+dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ return dc_iostream_sleep (hdlc->iostream, milliseconds);
+}
+
+static dc_status_t
+dc_hdlc_close (dc_iostream_t *abstract)
+{
+ dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
+
+ free (hdlc->wbuf);
+ free (hdlc->rbuf);
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/hdlc.h b/src/hdlc.h
new file mode 100644
index 0000000..aedd9b5
--- /dev/null
+++ b/src/hdlc.h
@@ -0,0 +1,50 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef DC_HDLC_H
+#define DC_HDLC_H
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Create a HDLC I/O stream layered on top of another base I/O stream.
+ *
+ * @param[out] iostream A location to store the HDLC I/O stream.
+ * @param[in] context A valid context.
+ * @param[in] base A valid I/O stream.
+ * @param[in] isize The input packet size in bytes.
+ * @param[in] osize The output packet size in bytes.
+ * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
+ * on failure.
+ */
+dc_status_t
+dc_hdlc_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* DC_HDLC_H */
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c
index cfa6989..8f17906 100644
--- a/src/hw_ostc3.c
+++ b/src/hw_ostc3.c
@@ -30,6 +30,7 @@
#include "array.h"
#include "aes.h"
#include "platform.h"
+#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)
@@ -119,9 +120,6 @@ typedef struct hw_ostc3_device_t {
unsigned int firmware;
unsigned char fingerprint[5];
hw_ostc3_state_t state;
- unsigned char cache[244];
- unsigned int available;
- unsigned int offset;
} hw_ostc3_device_t;
typedef struct hw_ostc3_logbook_t {
@@ -207,41 +205,20 @@ static dc_status_t
hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
- dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0;
while (nbytes < size) {
- if (transport == DC_TRANSPORT_BLE) {
- if (device->available == 0) {
- // Read a packet into the cache.
- size_t len = 0;
- rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- device->available = len;
- device->offset = 0;
- }
- }
-
// Set the minimum packet size.
- size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : 1024;
+ size_t length = 1024;
// Limit the packet size to the total size.
if (nbytes + length > size)
length = size - nbytes;
- if (transport == DC_TRANSPORT_BLE) {
- // Copy the data from the cached packet.
- memcpy (data + nbytes, device->cache + device->offset, length);
- device->available -= length;
- device->offset += length;
- } else {
- // Read the packet.
- rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- }
+ // Read the packet.
+ rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
+ if (rc != DC_STATUS_SUCCESS)
+ return rc;
// Update and emit a progress event.
if (progress) {
@@ -259,12 +236,11 @@ static dc_status_t
hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
- dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0;
while (nbytes < size) {
// Set the maximum packet size.
- size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : 64;
+ size_t length = 1024;
// Limit the packet size to the total size.
if (nbytes + length > size)
@@ -313,7 +289,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Send the command.
unsigned char command[1] = {cmd};
- status = hw_ostc3_write (device, NULL, command, sizeof (command));
+ status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
@@ -321,7 +297,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Read the echo.
unsigned char echo[1] = {0};
- status = hw_ostc3_read (device, NULL, echo, sizeof (echo));
+ status = dc_iostream_read (device->iostream, echo, sizeof (echo), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the echo.");
return status;
@@ -407,14 +383,14 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
}
}
- if (delay && device->available == 0) {
+ if (delay) {
dc_iostream_poll (device->iostream, delay);
}
if (cmd != EXIT) {
// Read the ready byte.
unsigned char answer[1] = {0};
- status = hw_ostc3_read (device, NULL, answer, sizeof (answer));
+ status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the ready byte.");
return status;
@@ -439,6 +415,7 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
{
dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc3_device_t *device = NULL;
+ dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL)
return DC_STATUS_INVALIDARGS;
@@ -451,29 +428,36 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
}
// Set the default values.
- device->iostream = iostream;
device->hardware = INVALID;
device->feature = 0;
device->model = 0;
device->serial = 0;
device->firmware = 0;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
- memset (device->cache, 0, sizeof (device->cache));
- device->available = 0;
- device->offset = 0;
+
+ // Create the packet stream.
+ if (transport == DC_TRANSPORT_BLE) {
+ status = dc_packet_open (&device->iostream, context, iostream, 244, 20);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the packet stream.");
+ goto error_free;
+ }
+ } else {
+ device->iostream = iostream;
+ }
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
- goto error_free;
+ goto error_free_iostream;
}
// 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;
+ goto error_free_iostream;
}
// Make sure everything is in a sane state.
@@ -486,6 +470,10 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
return DC_STATUS_SUCCESS;
+error_free_iostream:
+ if (transport == DC_TRANSPORT_BLE) {
+ dc_iostream_close (device->iostream);
+ }
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
@@ -548,14 +536,14 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
unsigned char answer[5] = {0};
// Send the command and service key.
- status = hw_ostc3_write (device, NULL, command, sizeof (command));
+ status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
// Read the response.
- status = hw_ostc3_read (device, NULL, answer, sizeof (answer));
+ status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
@@ -656,6 +644,15 @@ hw_ostc3_device_close (dc_device_t *abstract)
}
}
+ // Close the packet stream.
+ if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
+ rc = dc_iostream_close (device->iostream);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to close the packet stream.");
+ dc_status_set_error(&status, rc);
+ }
+ }
+
return status;
}
diff --git a/src/libdivecomputer.rc b/src/libdivecomputer.rc
new file mode 100644
index 0000000..1441085
--- /dev/null
+++ b/src/libdivecomputer.rc
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include
+
+#ifdef HAVE_VERSION_SUFFIX
+#include "revision.h"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0
+PRODUCTVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef HAVE_VERSION_SUFFIX
+FILEFLAGS VS_FF_PRERELEASE
+#else
+FILEFLAGS 0
+#endif
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "The libdivecomputer developers"
+ VALUE "FileDescription", "A library for communication with various dive computers."
+ VALUE "FileVersion", DC_VERSION
+ VALUE "InternalName", "libdivecomputer"
+ VALUE "LegalCopyright", "Copyright © 2010 Jef Driesen"
+ VALUE "OriginalFilename", "libdivecomputer.dll"
+ VALUE "ProductName", "libdivecomputer"
+ VALUE "ProductVersion", DC_VERSION
+#ifdef HAVE_VERSION_SUFFIX
+ VALUE "Comments", DC_VERSION_REVISION
+#endif
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 1033, 1200
+ END
+END
diff --git a/src/libdivecomputer.rc.in b/src/libdivecomputer.rc.in
deleted file mode 100644
index f98d2ed..0000000
--- a/src/libdivecomputer.rc.in
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include
-
-#ifdef HAVE_VERSION_SUFFIX
-#include "revision.h"
-#endif
-
-VS_VERSION_INFO VERSIONINFO
-FILEVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0
-PRODUCTVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0
-FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-#ifdef HAVE_VERSION_SUFFIX
-FILEFLAGS VS_FF_PRERELEASE
-#else
-FILEFLAGS 0
-#endif
-FILEOS VOS__WINDOWS32
-FILETYPE VFT_DLL
-FILESUBTYPE 0
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904B0"
- BEGIN
- VALUE "CompanyName", "The libdivecomputer developers"
- VALUE "FileDescription", "A library for communication with various dive computers."
- VALUE "FileVersion", "@VERSION@"
- VALUE "InternalName", "libdivecomputer"
- VALUE "LegalCopyright", "Copyright © 2010 Jef Driesen"
- VALUE "OriginalFilename", "libdivecomputer.dll"
- VALUE "ProductName", "libdivecomputer"
- VALUE "ProductVersion", "@VERSION@"
-#ifdef HAVE_VERSION_SUFFIX
- VALUE "Comments", DC_VERSION_REVISION
-#endif
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 1033, 1200
- END
-END
diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c
index a0d8e0a..b755955 100644
--- a/src/liquivision_lynx.c
+++ b/src/liquivision_lynx.c
@@ -158,7 +158,7 @@ liquivision_lynx_recv (liquivision_lynx_device_t *device, unsigned char data[],
// Verify the checksum.
unsigned short crc = array_uint16_be (packet + 1 + size);
- unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
@@ -528,7 +528,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
memcpy (header + 0, device->info + 2, 4);
memcpy (header + 4, logbook + offset + 4, headersize - 4);
unsigned int crc = array_uint32_le (logbook + offset + 0);
- unsigned int ccrc = checksum_crc32b (header, headersize - unused);
+ unsigned int ccrc = checksum_crc32 (header, headersize - unused);
if (crc != ccrc) {
WARNING (abstract->context, "Invalid dive checksum (%08x %08x)", crc, ccrc);
status = DC_STATUS_DATAFORMAT;
diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c
index 3378063..eb4f337 100644
--- a/src/mares_iconhd.c
+++ b/src/mares_iconhd.c
@@ -29,6 +29,7 @@
#include "array.h"
#include "rbstream.h"
#include "platform.h"
+#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable)
@@ -93,9 +94,6 @@ typedef struct mares_iconhd_device_t {
unsigned char version[140];
unsigned int model;
unsigned int packetsize;
- unsigned char cache[20];
- unsigned int available;
- unsigned int offset;
unsigned int splitcommand;
} mares_iconhd_device_t;
@@ -103,6 +101,7 @@ static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, c
static dc_status_t mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
+static dc_status_t mares_iconhd_device_close (dc_device_t *abstract);
static const dc_device_vtable_t mares_iconhd_device_vtable = {
sizeof(mares_iconhd_device_t),
@@ -113,7 +112,7 @@ static const dc_device_vtable_t mares_iconhd_device_vtable = {
mares_iconhd_device_dump, /* dump */
mares_iconhd_device_foreach, /* foreach */
NULL, /* timesync */
- NULL /* close */
+ mares_iconhd_device_close /* close */
};
static const mares_iconhd_layout_t mares_iconhd_layout = {
@@ -178,78 +177,6 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
return model;
}
-static dc_status_t
-mares_iconhd_read (mares_iconhd_device_t *device, unsigned char data[], size_t size)
-{
- dc_status_t rc = DC_STATUS_SUCCESS;
- dc_transport_t transport = dc_iostream_get_transport(device->iostream);
-
- size_t nbytes = 0;
- while (nbytes < size) {
- if (transport == DC_TRANSPORT_BLE) {
- if (device->available == 0) {
- // Read a packet into the cache.
- size_t len = 0;
- rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- device->available = len;
- device->offset = 0;
- }
- }
-
- // Set the minimum packet size.
- size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : size - nbytes;
-
- // Limit the packet size to the total size.
- if (nbytes + length > size)
- length = size - nbytes;
-
- if (transport == DC_TRANSPORT_BLE) {
- // Copy the data from the cached packet.
- memcpy (data + nbytes, device->cache + device->offset, length);
- device->available -= length;
- device->offset += length;
- } else {
- // Read the packet.
- rc = dc_iostream_read (device->iostream, data + nbytes, length, &length);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- }
-
- nbytes += length;
- }
-
- return rc;
-}
-
-static dc_status_t
-mares_iconhd_write (mares_iconhd_device_t *device, const unsigned char data[], size_t size)
-{
- dc_status_t rc = DC_STATUS_SUCCESS;
- dc_transport_t transport = dc_iostream_get_transport(device->iostream);
-
- size_t nbytes = 0;
- while (nbytes < size) {
- // Set the maximum packet size.
- size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : size - nbytes;
-
- // Limit the packet size to the total size.
- if (nbytes + length > size)
- length = size - nbytes;
-
- // Write the packet.
- rc = dc_iostream_write (device->iostream, data + nbytes, length, &length);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- nbytes += length;
- }
-
- return rc;
-}
-
static dc_status_t
mares_iconhd_packet (mares_iconhd_device_t *device,
const unsigned char command[], unsigned int csize,
@@ -267,7 +194,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
split_csize = device->splitcommand ? 2 : csize;
// Send the command header to the dive computer.
- status = mares_iconhd_write (device, command, split_csize);
+ status = dc_iostream_write (device->iostream, command, split_csize, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
@@ -275,7 +202,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
// Receive the header byte.
unsigned char header[1] = {0};
- status = mares_iconhd_read (device, header, sizeof (header));
+ status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
@@ -289,7 +216,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
// Send any remaining command payload to the dive computer.
if (csize > split_csize) {
- status = mares_iconhd_write (device, command + split_csize, csize - split_csize);
+ status = dc_iostream_write (device->iostream, command + split_csize, csize - split_csize, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
@@ -297,7 +224,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
}
// Read the packet.
- status = mares_iconhd_read (device, answer, asize);
+ status = dc_iostream_read (device->iostream, answer, asize, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
@@ -305,7 +232,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
// Receive the trailer byte.
unsigned char trailer[1] = {0};
- status = mares_iconhd_read (device, trailer, sizeof (trailer));
+ status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
@@ -338,8 +265,6 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char comman
// Discard any garbage bytes.
dc_iostream_sleep (device->iostream, 100);
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
- device->available = 0;
- device->offset = 0;
}
return DC_STATUS_SUCCESS;
@@ -463,6 +388,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
{
dc_status_t status = DC_STATUS_SUCCESS;
mares_iconhd_device_t *device = NULL;
+ dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL)
return DC_STATUS_INVALIDARGS;
@@ -475,16 +401,23 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
}
// Set the default values.
- device->iostream = iostream;
device->layout = NULL;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
device->fingerprint_size = sizeof (device->fingerprint);
memset (device->version, 0, sizeof (device->version));
device->model = 0;
device->packetsize = 0;
- memset (device->cache, 0, sizeof (device->cache));
- device->available = 0;
- device->offset = 0;
+
+ // Create the packet stream.
+ if (transport == DC_TRANSPORT_BLE) {
+ status = dc_packet_open (&device->iostream, context, iostream, 20, 20);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the packet stream.");
+ goto error_free;
+ }
+ } else {
+ device->iostream = iostream;
+ }
/*
* At least the Mares Matrix needs the command to be split into
@@ -499,28 +432,28 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
- goto error_free;
+ goto error_free_iostream;
}
// Set the timeout for receiving data (3000 ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
- goto error_free;
+ goto error_free_iostream;
}
// Clear the DTR line.
status = dc_iostream_set_dtr (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to clear the DTR line.");
- goto error_free;
+ goto error_free_iostream;
}
// 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;
+ goto error_free_iostream;
}
// Make sure everything is in a sane state.
@@ -531,7 +464,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
status = mares_iconhd_transfer (device, command, sizeof (command),
device->version, sizeof (device->version));
if (status != DC_STATUS_SUCCESS) {
- goto error_free;
+ goto error_free_iostream;
}
// Autodetect the model using the version packet.
@@ -619,12 +552,31 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
return DC_STATUS_SUCCESS;
+
+error_free_iostream:
+ if (transport == DC_TRANSPORT_BLE) {
+ dc_iostream_close (device->iostream);
+ }
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
+static dc_status_t
+mares_iconhd_device_close (dc_device_t *abstract)
+{
+ mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
+
+ // Close the packet stream.
+ if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
+ return dc_iostream_close (device->iostream);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+
static dc_status_t
mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c
index ed2e4ae..50cf6f7 100644
--- a/src/mares_iconhd_parser.c
+++ b/src/mares_iconhd_parser.c
@@ -299,7 +299,7 @@ mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type
}
unsigned short crc = array_uint16_le(data + size - 6);
- unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000);
+ unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000);
if (crc != ccrc) {
return 0;
}
diff --git a/src/mclean_extreme.c b/src/mclean_extreme.c
index 98038ff..2ef1a87 100644
--- a/src/mclean_extreme.c
+++ b/src/mclean_extreme.c
@@ -26,6 +26,7 @@
#include "context-private.h"
#include "device-private.h"
#include "array.h"
+#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &mclean_extreme_device_vtable)
@@ -391,6 +392,7 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
{
dc_status_t status = DC_STATUS_SUCCESS;
mclean_extreme_device_t *device = NULL;
+ dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL)
return DC_STATUS_INVALIDARGS;
@@ -403,21 +405,31 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
}
// Set the default values.
- device->iostream = iostream;
memset(device->fingerprint, 0, sizeof(device->fingerprint));
+ // Create the packet stream.
+ if (transport == DC_TRANSPORT_BLE) {
+ status = dc_packet_open (&device->iostream, context, iostream, 244, 244);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the packet stream.");
+ goto error_free;
+ }
+ } else {
+ device->iostream = iostream;
+ }
+
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure(device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR(context, "Failed to set the terminal attributes.");
- goto error_free;
+ goto error_free_iostream;
}
// Set the timeout for receiving data (1000ms).
status = dc_iostream_set_timeout(device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) {
ERROR(context, "Failed to set the timeout.");
- goto error_free;
+ goto error_free_iostream;
}
// Make sure everything is in a sane state.
@@ -428,6 +440,10 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
return DC_STATUS_SUCCESS;
+error_free_iostream:
+ if (transport == DC_TRANSPORT_BLE) {
+ dc_iostream_close (device->iostream);
+ }
error_free:
dc_device_deallocate((dc_device_t *)device);
return status;
@@ -438,14 +454,24 @@ mclean_extreme_device_close(dc_device_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
mclean_extreme_device_t *device = (mclean_extreme_device_t *)abstract;
+ dc_status_t rc = DC_STATUS_SUCCESS;
- status = mclean_extreme_send(device, CMD_CLOSE, NULL, 0);
- if (status != DC_STATUS_SUCCESS) {
+ rc = mclean_extreme_send(device, CMD_CLOSE, NULL, 0);
+ if (rc != DC_STATUS_SUCCESS) {
ERROR(abstract->context, "Failed to send the exit command.");
- return status;
+ dc_status_set_error(&status, rc);
}
- return DC_STATUS_SUCCESS;
+ // Close the packet stream.
+ if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
+ rc = dc_iostream_close (device->iostream);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to close the packet stream.");
+ dc_status_set_error(&status, rc);
+ }
+ }
+
+ return status;
}
static dc_status_t
diff --git a/src/oceans_s1.c b/src/oceans_s1.c
index e26979e..125d090 100644
--- a/src/oceans_s1.c
+++ b/src/oceans_s1.c
@@ -302,7 +302,7 @@ oceans_s1_xmodem_packet (oceans_s1_device_t *device, unsigned char seq, unsigned
}
unsigned short crc = array_uint16_be (packet + nbytes - 2);
- unsigned short ccrc = checksum_crc16_ccitt (packet + 3, nbytes - 5, 0x0000);
+ unsigned short ccrc = checksum_crc16_ccitt (packet + 3, nbytes - 5, 0x0000, 0x0000);
if (crc != ccrc) {
ERROR (device->base.context, "Unexpected answer checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
diff --git a/src/packet.c b/src/packet.c
new file mode 100644
index 0000000..cc8304f
--- /dev/null
+++ b/src/packet.c
@@ -0,0 +1,321 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include // malloc, free
+#include
+
+#include "packet.h"
+
+#include "iostream-private.h"
+#include "common-private.h"
+#include "context-private.h"
+
+static dc_status_t dc_packet_set_timeout (dc_iostream_t *abstract, int timeout);
+static dc_status_t dc_packet_set_break (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value);
+static dc_status_t dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value);
+static dc_status_t dc_packet_get_available (dc_iostream_t *abstract, size_t *value);
+static dc_status_t dc_packet_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
+static dc_status_t dc_packet_poll (dc_iostream_t *abstract, int timeout);
+static dc_status_t dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual);
+static dc_status_t dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual);
+static dc_status_t dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size);
+static dc_status_t dc_packet_flush (dc_iostream_t *abstract);
+static dc_status_t dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction);
+static dc_status_t dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds);
+static dc_status_t dc_packet_close (dc_iostream_t *abstract);
+
+typedef struct dc_packet_t {
+ /* Base class. */
+ dc_iostream_t base;
+ /* Internal state. */
+ dc_iostream_t *iostream;
+ unsigned char *cache;
+ size_t available;
+ size_t offset;
+ size_t isize;
+ size_t osize;
+} dc_packet_t;
+
+static const dc_iostream_vtable_t dc_packet_vtable = {
+ sizeof(dc_packet_t),
+ dc_packet_set_timeout, /* set_timeout */
+ dc_packet_set_break, /* set_break */
+ dc_packet_set_dtr, /* set_dtr */
+ dc_packet_set_rts, /* set_rts */
+ dc_packet_get_lines, /* get_lines */
+ dc_packet_get_available, /* get_available */
+ dc_packet_configure, /* configure */
+ dc_packet_poll, /* poll */
+ dc_packet_read, /* read */
+ dc_packet_write, /* write */
+ dc_packet_ioctl, /* ioctl */
+ dc_packet_flush, /* flush */
+ dc_packet_purge, /* purge */
+ dc_packet_sleep, /* sleep */
+ dc_packet_close, /* close */
+};
+
+dc_status_t
+dc_packet_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_packet_t *packet = NULL;
+ unsigned char *buffer = NULL;
+
+ if (out == NULL || base == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ // Allocate memory.
+ packet = (dc_packet_t *) dc_iostream_allocate (NULL, &dc_packet_vtable, dc_iostream_get_transport(base));
+ if (packet == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_exit;
+ }
+
+ // Allocate the read buffer.
+ if (isize) {
+ buffer = (unsigned char *) malloc (isize);
+ if (buffer == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_free;
+ }
+ }
+
+ packet->iostream = base;
+ packet->cache = buffer;
+ packet->available = 0;
+ packet->offset = 0;
+ packet->isize = isize;
+ packet->osize = osize;
+
+ *out = (dc_iostream_t *) packet;
+
+ return DC_STATUS_SUCCESS;
+
+error_free:
+ dc_iostream_deallocate ((dc_iostream_t *) packet);
+error_exit:
+ return status;
+}
+
+static dc_status_t
+dc_packet_set_timeout (dc_iostream_t *abstract, int timeout)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_set_timeout (packet->iostream, timeout);
+}
+
+static dc_status_t
+dc_packet_set_break (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_set_break (packet->iostream, value);
+}
+
+static dc_status_t
+dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_set_dtr (packet->iostream, value);
+}
+
+static dc_status_t
+dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_set_rts (packet->iostream, value);
+}
+
+static dc_status_t
+dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_get_lines (packet->iostream, value);
+}
+
+static dc_status_t
+dc_packet_get_available (dc_iostream_t *abstract, size_t *value)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ if (packet->isize && packet->available) {
+ if (value)
+ *value = packet->available;
+ return DC_STATUS_SUCCESS;
+ }
+
+ return dc_iostream_get_available (packet->iostream, value);
+}
+
+static dc_status_t
+dc_packet_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_configure (packet->iostream, baudrate, databits, parity, stopbits, flowcontrol);
+}
+
+static dc_status_t
+dc_packet_poll (dc_iostream_t *abstract, int timeout)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ if (packet->isize && packet->available)
+ return DC_STATUS_SUCCESS;
+
+ return dc_iostream_poll (packet->iostream, timeout);
+}
+
+static dc_status_t
+dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+ size_t nbytes = 0;
+
+ while (nbytes < size) {
+ // Get the remaining size.
+ size_t length = size - nbytes;
+
+ if (packet->isize) {
+ if (packet->available == 0) {
+ // Read a packet into the cache.
+ size_t len = 0;
+ status = dc_iostream_read (packet->iostream, packet->cache, packet->isize, &len);
+ if (status != DC_STATUS_SUCCESS)
+ break;
+
+ packet->available = len;
+ packet->offset = 0;
+ }
+
+ // Limit to the maximum packet size.
+ if (length > packet->available)
+ length = packet->available;
+
+ // Copy the data from the cached packet.
+ memcpy ((unsigned char *) data + nbytes, packet->cache + packet->offset, length);
+ packet->available -= length;
+ packet->offset += length;
+ } else {
+ // Read the packet.
+ status = dc_iostream_read (packet->iostream, (unsigned char *) data + nbytes, length, &length);
+ if (status != DC_STATUS_SUCCESS)
+ break;
+ }
+
+ // Update the total number of bytes.
+ nbytes += length;
+ }
+
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+ size_t nbytes = 0;
+
+ while (nbytes < size) {
+ // Get the remaining size.
+ size_t length = size - nbytes;
+
+ // Limit to the maximum packet size.
+ if (packet->osize) {
+ if (length > packet->osize)
+ length = packet->osize;
+ }
+
+ // Write the packet.
+ status = dc_iostream_write (packet->iostream, (const unsigned char *) data + nbytes, length, &length);
+ if (status != DC_STATUS_SUCCESS)
+ break;
+
+ // Update the total number of bytes.
+ nbytes += length;
+ }
+
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_ioctl (packet->iostream, request, data, size);
+}
+
+static dc_status_t
+dc_packet_flush (dc_iostream_t *abstract)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_flush (packet->iostream);
+}
+
+static dc_status_t
+dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ if (direction & DC_DIRECTION_INPUT) {
+ packet->available = 0;
+ packet->offset = 0;
+ }
+
+ return dc_iostream_purge (packet->iostream, direction);
+}
+
+static dc_status_t
+dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ return dc_iostream_sleep (packet->iostream, milliseconds);
+}
+
+static dc_status_t
+dc_packet_close (dc_iostream_t *abstract)
+{
+ dc_packet_t *packet = (dc_packet_t *) abstract;
+
+ free (packet->cache);
+
+ return DC_STATUS_SUCCESS;
+}
diff --git a/src/packet.h b/src/packet.h
new file mode 100644
index 0000000..b420f08
--- /dev/null
+++ b/src/packet.h
@@ -0,0 +1,54 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2023 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef DC_PACKET_H
+#define DC_PACKET_H
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Create a packet I/O stream layered on top of another base I/O stream.
+ *
+ * This layered I/O allows reading and writing a byte stream from the
+ * underlying packet oriented transport. It changes the packet oriented
+ * base transport into a stream oriented transport.
+ *
+ * @param[out] iostream A location to store the packet I/O stream.
+ * @param[in] context A valid context.
+ * @param[in] base A valid I/O stream.
+ * @param[in] isize The input packet size in bytes.
+ * @param[in] osize The output packet size in bytes.
+ * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
+ * on failure.
+ */
+dc_status_t
+dc_packet_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* DC_PACKET_H */
diff --git a/src/parser.c b/src/parser.c
index 68ed4f3..b253070 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -63,6 +63,7 @@
#include "seac_screen.h"
#include "deepblu_cosmiq.h"
#include "oceans_s1.h"
+#include "divesoft_freedom.h"
// Not merged upstream yet
#include "garmin.h"
@@ -202,6 +203,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_parser_create (&parser, context);
break;
+ case DC_FAMILY_DIVESOFT_FREEDOM:
+ rc = divesoft_freedom_parser_create (&parser, context);
+ break;
default:
return DC_STATUS_INVALIDARGS;
diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c
index 835fbcb..27c2fdb 100644
--- a/src/reefnet_sensuspro.c
+++ b/src/reefnet_sensuspro.c
@@ -177,7 +177,7 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device)
// Verify the checksum of the handshake packet.
unsigned short crc = array_uint16_le (handshake + SZ_HANDSHAKE);
- unsigned short ccrc = checksum_crc16_ccitt (handshake, SZ_HANDSHAKE, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (handshake, SZ_HANDSHAKE, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@@ -280,7 +280,7 @@ reefnet_sensuspro_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
}
unsigned short crc = array_uint16_le (answer + SZ_MEMORY);
- unsigned short ccrc = checksum_crc16_ccitt (answer, SZ_MEMORY, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (answer, SZ_MEMORY, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c
index 6b059d6..bfe1c6a 100644
--- a/src/reefnet_sensusultra.c
+++ b/src/reefnet_sensusultra.c
@@ -224,7 +224,7 @@ reefnet_sensusultra_packet (reefnet_sensusultra_device_t *device, unsigned char
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (data + size - 2);
- unsigned short ccrc = checksum_crc16_ccitt (data + header, size - header - 2, 0xffff);
+ unsigned short ccrc = checksum_crc16_ccitt (data + header, size - header - 2, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@@ -477,7 +477,7 @@ reefnet_sensusultra_device_write_user (dc_device_t *abstract, const unsigned cha
}
// Send the checksum to the device.
- unsigned short crc = checksum_crc16_ccitt (data, SZ_USER, 0xffff);
+ unsigned short crc = checksum_crc16_ccitt (data, SZ_USER, 0xffff, 0x0000);
rc = reefnet_sensusultra_send_ushort (device, crc);
if (rc != DC_STATUS_SUCCESS)
return rc;
diff --git a/src/seac_screen.c b/src/seac_screen.c
index e9b5366..9b3796f 100644
--- a/src/seac_screen.c
+++ b/src/seac_screen.c
@@ -115,7 +115,7 @@ seac_screen_send (seac_screen_device_t *device, unsigned short cmd, const unsign
if (size) {
memcpy (packet + 5, data, size);
}
- crc = checksum_crc16_ccitt (packet, size + 5, 0xFFFF);
+ crc = checksum_crc16_ccitt (packet, size + 5, 0xFFFF, 0x0000);
packet[size + 5] = (crc >> 8) & 0xFF;
packet[size + 6] = (crc ) & 0xFF;
@@ -165,7 +165,7 @@ seac_screen_receive (seac_screen_device_t *device, unsigned short cmd, unsigned
// Verify the checksum.
unsigned short crc = array_uint16_be (packet + 1 + length - 2);
- unsigned short ccrc = checksum_crc16_ccitt (packet, 1 + length - 2, 0xFFFF);
+ unsigned short ccrc = checksum_crc16_ccitt (packet, 1 + length - 2, 0xFFFF, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
@@ -486,8 +486,8 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Check the header checksums.
- if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF) != 0 ||
- checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) {
+ if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 ||
+ checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) {
ERROR (abstract->context, "Unexpected header checksum.");
status = DC_STATUS_DATAFORMAT;
goto error_free_logbook;
diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c
index 8c588d2..c4a855c 100644
--- a/src/seac_screen_parser.c
+++ b/src/seac_screen_parser.c
@@ -283,8 +283,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
- if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF) != 0 ||
- checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) {
+ if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 ||
+ checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) {
ERROR (abstract->context, "Unexpected header checksum.");
return DC_STATUS_DATAFORMAT;
}
@@ -303,7 +303,7 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
while (offset + SZ_SAMPLE <= size) {
dc_sample_value_t sample = {0};
- if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF) != 0) {
+ if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF, 0x0000) != 0) {
ERROR (abstract->context, "Unexpected sample checksum.");
return DC_STATUS_DATAFORMAT;
}
diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c
index 6ffc7bc..a18d27c 100644
--- a/src/suunto_eonsteel.c
+++ b/src/suunto_eonsteel.c
@@ -29,6 +29,7 @@
#include "array.h"
#include "platform.h"
#include "checksum.h"
+#include "hdlc.h"
#define EONSTEEL 0
#define EONCORE 1
@@ -80,14 +81,10 @@ struct directory_entry {
#define MAXDATA_SIZE 2048
#define CRC_SIZE 4
-// HDLC special characters
-#define END 0x7E
-#define ESC 0x7D
-#define ESC_BIT 0x20
-
static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
+static dc_status_t suunto_eonsteel_device_close (dc_device_t *abstract);
static const dc_device_vtable_t suunto_eonsteel_device_vtable = {
sizeof(suunto_eonsteel_device_t),
@@ -98,7 +95,7 @@ static const dc_device_vtable_t suunto_eonsteel_device_vtable = {
NULL, /* dump */
suunto_eonsteel_device_foreach, /* foreach */
suunto_eonsteel_device_timesync, /* timesync */
- NULL /* close */
+ suunto_eonsteel_device_close /* close */
};
static const char dive_directory[] = "0:/dives";
@@ -127,156 +124,6 @@ static struct directory_entry *alloc_dirent(int type, int len, const char *name)
return res;
}
-static void put_le16(unsigned short val, unsigned char *p)
-{
- p[0] = val;
- p[1] = val >> 8;
-}
-
-static void put_le32(unsigned int val, unsigned char *p)
-{
- p[0] = val;
- p[1] = val >> 8;
- p[2] = val >> 16;
- p[3] = val >> 24;
-}
-
-static dc_status_t
-suunto_eonsteel_hdlc_write (suunto_eonsteel_device_t *device, const unsigned char data[], size_t size, size_t *actual)
-{
- dc_status_t status = DC_STATUS_SUCCESS;
- unsigned char buffer[20];
- size_t nbytes = 0;
-
- // Start of the packet.
- buffer[nbytes++] = END;
-
- for (size_t i = 0; i < size; ++i) {
- unsigned char c = data[i];
-
- if (c == END || c == ESC) {
- // Append the escape character.
- buffer[nbytes++] = ESC;
-
- // Flush the buffer if necessary.
- if (nbytes >= sizeof(buffer)) {
- status = dc_iostream_write(device->iostream, buffer, nbytes, NULL);
- if (status != DC_STATUS_SUCCESS) {
- ERROR(device->base.context, "Failed to send the packet.");
- return status;
- }
-
- nbytes = 0;
- }
-
- // Escape the character.
- c ^= ESC_BIT;
- }
-
- // Append the character.
- buffer[nbytes++] = c;
-
- // Flush the buffer if necessary.
- if (nbytes >= sizeof(buffer)) {
- status = dc_iostream_write(device->iostream, buffer, nbytes, NULL);
- if (status != DC_STATUS_SUCCESS) {
- ERROR(device->base.context, "Failed to send the packet.");
- return status;
- }
-
- nbytes = 0;
- }
- }
-
- // End of the packet.
- buffer[nbytes++] = END;
-
- // Flush the buffer.
- status = dc_iostream_write(device->iostream, buffer, nbytes, NULL);
- if (status != DC_STATUS_SUCCESS) {
- ERROR(device->base.context, "Failed to send the packet.");
- return status;
- }
-
- if (actual)
- *actual = size;
-
- return status;
-
-}
-
-static dc_status_t
-suunto_eonsteel_hdlc_read (suunto_eonsteel_device_t *device, unsigned char data[], size_t size, size_t *actual)
-{
- dc_status_t status = DC_STATUS_SUCCESS;
- unsigned char buffer[20];
- unsigned int initialized = 0;
- unsigned int escaped = 0;
- size_t nbytes = 0;
-
- while (1) {
- // Read a single data packet.
- size_t transferred = 0;
- status = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred);
- if (status != DC_STATUS_SUCCESS) {
- ERROR(device->base.context, "Failed to receive the packet.");
- return status;
- }
-
- for (size_t i = 0; i < transferred; ++i) {
- unsigned char c = buffer[i];
-
- if (c == END) {
- if (escaped) {
- ERROR (device->base.context, "HDLC frame escaped the special character %02x.", c);
- return DC_STATUS_PROTOCOL;
- }
-
- if (initialized) {
- goto done;
- }
-
- initialized = 1;
- continue;
- }
-
- if (!initialized) {
- continue;
- }
-
- if (c == ESC) {
- if (escaped) {
- ERROR (device->base.context, "HDLC frame escaped the special character %02x.", c);
- return DC_STATUS_PROTOCOL;
- }
- escaped = 1;
- continue;
- }
-
- if (escaped) {
- c ^= ESC_BIT;
- escaped = 0;
- }
-
- if (nbytes < size)
- data[nbytes] = c;
- nbytes++;
-
- }
- }
-
-done:
- if (nbytes > size) {
- ERROR(device->base.context, "Insufficient buffer space available.");
- return DC_STATUS_PROTOCOL;
- }
-
- if (actual)
- *actual = nbytes;
-
- return status;
-}
-
/*
* Get a single 64-byte packet from the dive computer. This handles packet
* logging and any obvious packet-level errors, and returns the payload of
@@ -338,7 +185,7 @@ suunto_eonsteel_receive_ble(suunto_eonsteel_device_t *device, unsigned char data
unsigned char buffer[HEADER_SIZE + MAXDATA_SIZE + CRC_SIZE];
size_t transferred = 0;
- rc = suunto_eonsteel_hdlc_read(device, buffer, sizeof(buffer), &transferred);
+ rc = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred);
if (rc != DC_STATUS_SUCCESS) {
ERROR(device->base.context, "Failed to receive the packet.");
return rc;
@@ -352,7 +199,7 @@ suunto_eonsteel_receive_ble(suunto_eonsteel_device_t *device, unsigned char data
unsigned int nbytes = transferred - CRC_SIZE;
unsigned int crc = array_uint32_le(buffer + nbytes);
- unsigned int ccrc = checksum_crc32(buffer, nbytes);
+ unsigned int ccrc = checksum_crc32r(buffer, nbytes);
if (crc != ccrc) {
ERROR(device->base.context, "Invalid checksum (expected %08x, received %08x).", ccrc, crc);
return DC_STATUS_PROTOCOL;
@@ -394,16 +241,16 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device,
buf[1] = size + HEADER_SIZE;
// 2-byte LE command word
- put_le16(cmd, buf + 2);
+ array_uint16_le_set(buf + 2, cmd);
// 4-byte LE magic value (starts at 1)
- put_le32(device->magic, buf + 4);
+ array_uint32_le_set(buf + 4, device->magic);
// 2-byte LE sequence number;
- put_le16(device->seq, buf + 8);
+ array_uint16_le_set(buf + 8, device->seq);
// 4-byte LE length
- put_le32(size, buf + 10);
+ array_uint32_le_set(buf + 10, size);
// .. followed by actual data
if (size) {
@@ -411,11 +258,11 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device,
}
// 4 byte LE checksum
- unsigned int crc = checksum_crc32(buf + 2, size + HEADER_SIZE);
- put_le32(crc, buf + 14 + size);
+ unsigned int crc = checksum_crc32r(buf + 2, size + HEADER_SIZE);
+ array_uint32_le_set(buf + 14 + size, crc);
if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) {
- rc = suunto_eonsteel_hdlc_write(device, buf + 2, size + HEADER_SIZE + CRC_SIZE, NULL);
+ rc = dc_iostream_write(device->iostream, buf + 2, size + HEADER_SIZE + CRC_SIZE, NULL);
} else {
rc = dc_iostream_write(device->iostream, buf, sizeof(buf) - CRC_SIZE, NULL);
}
@@ -591,8 +438,8 @@ read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf)
ask = size;
if (ask > 1024)
ask = 1024;
- put_le32(1234, cmdbuf+0); // Not file offset, after all
- put_le32(ask, cmdbuf+4); // Size of read
+ array_uint32_le_set(cmdbuf + 0, 1234); // Not file offset, after all
+ array_uint32_le_set(cmdbuf + 4, ask); // Size of read
rc = suunto_eonsteel_transfer(eon, CMD_FILE_READ,
cmdbuf, 8, result, sizeof(result), &n);
if (rc != DC_STATUS_SUCCESS) {
@@ -705,7 +552,7 @@ get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry **res)
unsigned int n = 0;
unsigned int cmdlen;
- put_le32(0, cmd);
+ array_uint32_le_set(cmd, 0);
memcpy(cmd + 4, dive_directory, sizeof(dive_directory));
cmdlen = 4 + sizeof(dive_directory);
rc = suunto_eonsteel_transfer(eon, CMD_DIR_OPEN,
@@ -771,6 +618,7 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea
{
dc_status_t status = DC_STATUS_SUCCESS;
suunto_eonsteel_device_t *eon = NULL;
+ dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL)
return DC_STATUS_INVALIDARGS;
@@ -780,17 +628,26 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea
return DC_STATUS_NOMEMORY;
// Set up the magic handshake fields
- eon->iostream = iostream;
eon->model = model;
eon->magic = INIT_MAGIC;
eon->seq = INIT_SEQ;
memset (eon->version, 0, sizeof (eon->version));
memset (eon->fingerprint, 0, sizeof (eon->fingerprint));
+ if (transport == DC_TRANSPORT_BLE) {
+ status = dc_hdlc_open (&eon->iostream, context, iostream, 20, 20);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (context, "Failed to create the HDLC stream.");
+ goto error_free;
+ }
+ } else {
+ eon->iostream = iostream;
+ }
+
status = dc_iostream_set_timeout(eon->iostream, 5000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
- goto error_free;
+ goto error_free_iostream;
}
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
@@ -798,18 +655,34 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea
init, sizeof(init), eon->version, sizeof(eon->version), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR(context, "unable to initialize device");
- goto error_free;
+ goto error_free_iostream;
}
*out = (dc_device_t *) eon;
return DC_STATUS_SUCCESS;
+error_free_iostream:
+ if (transport == DC_TRANSPORT_BLE) {
+ dc_iostream_close (eon->iostream);
+ }
error_free:
- free(eon);
+ dc_device_deallocate ((dc_device_t *) eon);
return status;
}
+static dc_status_t
+suunto_eonsteel_device_close (dc_device_t *abstract)
+{
+ suunto_eonsteel_device_t *device = (suunto_eonsteel_device_t *) abstract;
+
+ if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
+ return dc_iostream_close (device->iostream);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
static dc_status_t
suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
@@ -890,7 +763,7 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
break;
}
- put_le32(time, buf);
+ array_uint32_le_set(buf, time);
if (memcmp (buf, eon->fingerprint, sizeof (eon->fingerprint)) == 0) {
skip = 1;
diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c
index 39a927c..bd849a8 100644
--- a/src/uwatec_smart_parser.c
+++ b/src/uwatec_smart_parser.c
@@ -893,25 +893,6 @@ uwatec_galileo_identify (unsigned char value)
}
-static unsigned int
-uwatec_smart_fixsignbit (unsigned int x, unsigned int n)
-{
- if (n <= 0 || n > 32)
- return 0;
-
- unsigned int signbit = (1 << (n - 1));
- unsigned int mask = (signbit - 1);
-
- // When turning a two's-complement number with a certain number
- // of bits into one with more bits, the sign bit must be repeated
- // in all the extra bits.
- if ((x & signbit) == signbit)
- return x | ~mask;
- else
- return x & mask;
-}
-
-
static dc_status_t
uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback, void *userdata)
{
@@ -1018,7 +999,7 @@ uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback
}
// Fix the sign bit.
- signed int svalue = uwatec_smart_fixsignbit (value, nbits);
+ signed int svalue = signextend (value, nbits);
// Parse the value.
unsigned int idx = 0;