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