Merge branch 'master' of git://git.libdivecomputer.org/libdivecomputer into Subsurface-branch
Merge with upstream libdivecomputer from Jef:
- Jef merged my Scubapro G2 work, but renamed everything, and didn't
get the newer IO model code. Very annoying.
I went along with changing the G2 model family name to
DC_FAMILY_UWATEC_G2 just to keep some of the basic infrastructure
more easily mergeable. But his uwatec_g2 version is not usable.
- Cochran updates from John Van Ostrand
- Misc improvements from Jef:
* divesystems idive improvements
* Oceanic OCS freedive mode
* ppO2 callback cleanup
- Some transport type work:
* changes to IRDA configuration
* basic bluetooth rfcomm transport mode
* 'master' of git://git.libdivecomputer.org/libdivecomputer: (35 commits)
Removed unused code
Fixed duplicate gasmix event reports
Added decompression event handling for the Commander
Fix bad profiles when profile ringbuffer wraps around
Changed cochran_comander_profile_size function parameters
Fixed location and encoding of Commander II pointers
Use a local variable for the layout pointer
Add new EMC device model string
Add support for Pre-21000 s/n Commander dive computers
Fix problems with wrapped logbook ringbuffer
Retry read operations on failure
Change profile download to be incremental
Fix the id string offset
Fix the progress events
Use the trimix data format
Use the correct model number
Enable more fine grained progress events
Abort with an error if the buffer is too small
Use the standard libdivecomputer error codes
Scubapro G2: add missed command packet logging
...
This commit is contained in:
commit
9fd6635cf6
32
configure.ac
32
configure.ac
@ -111,16 +111,36 @@ AS_IF([test "x$with_hidapi" != "xno"], [
|
||||
])
|
||||
])
|
||||
|
||||
# Checks for BlueZ (bluetooth) support.
|
||||
AC_ARG_WITH([bluez],
|
||||
[AS_HELP_STRING([--without-bluez],
|
||||
[Build without the BlueZ library])],
|
||||
[], [with_bluez=auto])
|
||||
AS_IF([test "x$with_bluez" != "xno"], [
|
||||
PKG_CHECK_MODULES([BLUEZ], [bluez], [have_bluez=yes], [have_bluez=no])
|
||||
AS_IF([test "x$have_bluez" = "xyes"], [
|
||||
AC_DEFINE([HAVE_BLUEZ], [1], [BlueZ library])
|
||||
DEPENDENCIES="$DEPENDENCIES bluez"
|
||||
])
|
||||
])
|
||||
|
||||
AC_SUBST([DEPENDENCIES])
|
||||
|
||||
# Checks for IrDA support.
|
||||
AC_CHECK_HEADERS([winsock2.h af_irda.h], [irda_win32=yes], [irda_win32=no], [
|
||||
# Checks for Windows bluetooth support.
|
||||
AC_CHECK_HEADERS([winsock2.h ws2bth.h], , , [
|
||||
#if HAVE_WINSOCK2_H
|
||||
# include <winsock2.h>
|
||||
# endif
|
||||
])
|
||||
|
||||
AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], [irda_linux=yes], [irda_linux=no], [
|
||||
# Checks for IrDA support.
|
||||
AC_CHECK_HEADERS([winsock2.h af_irda.h], , , [
|
||||
#if HAVE_WINSOCK2_H
|
||||
# include <winsock2.h>
|
||||
# endif
|
||||
])
|
||||
|
||||
AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], , , [
|
||||
#if HAVE_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
# endif
|
||||
@ -129,12 +149,6 @@ AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], [irda_linux=yes], [i
|
||||
# endif
|
||||
])
|
||||
|
||||
if test "$irda_win32" = "yes" || test "$irda_linux" = "yes"; then
|
||||
AC_DEFINE([HAVE_IRDA], [1], [IrDA support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([IRDA], [test "$irda_win32" = "yes" || test "$irda_linux" = "yes"])
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([linux/serial.h])
|
||||
AC_CHECK_HEADERS([IOKit/serial/ioss.h])
|
||||
|
||||
@ -54,8 +54,8 @@ static const backend_table_t g_backends[] = {
|
||||
{"aladin", DC_FAMILY_UWATEC_ALADIN, 0x3F},
|
||||
{"memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0},
|
||||
{"smart", DC_FAMILY_UWATEC_SMART, 0x10},
|
||||
{"g2", DC_FAMILY_SCUBAPRO_G2, 0x11},
|
||||
{"meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20},
|
||||
{"g2", DC_FAMILY_UWATEC_G2, 0x11},
|
||||
{"sensus", DC_FAMILY_REEFNET_SENSUS, 1},
|
||||
{"sensuspro", DC_FAMILY_REEFNET_SENSUSPRO, 2},
|
||||
{"sensusultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3},
|
||||
|
||||
@ -59,8 +59,7 @@ typedef enum dc_family_t {
|
||||
DC_FAMILY_UWATEC_MEMOMOUSE,
|
||||
DC_FAMILY_UWATEC_SMART,
|
||||
DC_FAMILY_UWATEC_MERIDIAN,
|
||||
/* We'll enumerate the Scubapro G2 under Uwatec */
|
||||
DC_FAMILY_SCUBAPRO_G2,
|
||||
DC_FAMILY_UWATEC_G2,
|
||||
/* Oceanic */
|
||||
DC_FAMILY_OCEANIC_VTPRO = (4 << 16),
|
||||
DC_FAMILY_OCEANIC_VEO250,
|
||||
|
||||
@ -33,7 +33,9 @@ typedef enum dc_transport_t {
|
||||
DC_TRANSPORT_NONE,
|
||||
DC_TRANSPORT_SERIAL,
|
||||
DC_TRANSPORT_USB,
|
||||
DC_TRANSPORT_IRDA
|
||||
DC_TRANSPORT_USBHID,
|
||||
DC_TRANSPORT_IRDA,
|
||||
DC_TRANSPORT_BLUETOOTH
|
||||
} dc_transport_t;
|
||||
|
||||
typedef struct dc_descriptor_t dc_descriptor_t;
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\include"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_IRDA"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
@ -119,7 +119,7 @@
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
AdditionalIncludeDirectories="..\include"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_IRDA"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
@ -194,6 +194,10 @@
|
||||
RelativePath="..\src\atomics_cobalt_parser.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\bluetooth.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\buffer.c"
|
||||
>
|
||||
@ -524,6 +528,10 @@
|
||||
RelativePath="..\include\libdivecomputer\atomics_cobalt.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\bluetooth.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\include\libdivecomputer\buffer.h"
|
||||
>
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
||||
AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS)
|
||||
AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = libdivecomputer.la
|
||||
|
||||
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) -lm -lz
|
||||
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm -lz
|
||||
libdivecomputer_la_LDFLAGS = \
|
||||
-version-info $(DC_VERSION_LIBTOOL) \
|
||||
-no-undefined \
|
||||
-export-symbols libdivecomputer.exp
|
||||
|
||||
if OS_WIN32
|
||||
libdivecomputer_la_LIBADD += -lws2_32
|
||||
libdivecomputer_la_LDFLAGS += -Wc,-static-libgcc
|
||||
endif
|
||||
|
||||
@ -75,16 +76,9 @@ else
|
||||
libdivecomputer_la_SOURCES += serial.h serial_posix.c
|
||||
endif
|
||||
|
||||
if IRDA
|
||||
if OS_WIN32
|
||||
libdivecomputer_la_LIBADD += -lws2_32
|
||||
endif
|
||||
libdivecomputer_la_SOURCES += irda.h irda.c
|
||||
else
|
||||
libdivecomputer_la_SOURCES += irda.h irda_dummy.c
|
||||
endif
|
||||
|
||||
libdivecomputer_la_SOURCES += usbhid.h usbhid.c
|
||||
libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c
|
||||
|
||||
if OS_WIN32
|
||||
libdivecomputer_la_SOURCES += libdivecomputer.rc
|
||||
|
||||
@ -198,6 +198,13 @@ array_uint32_le (const unsigned char data[])
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
array_uint32_word_be (const unsigned char data[])
|
||||
{
|
||||
return data[1] + (data[0] << 8) + (data[3] << 16) + (data[2] << 24);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
|
||||
@ -64,6 +64,9 @@ array_uint32_be (const unsigned char data[]);
|
||||
unsigned int
|
||||
array_uint32_le (const unsigned char data[]);
|
||||
|
||||
unsigned int
|
||||
array_uint32_word_be (const unsigned char data[]);
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
|
||||
604
src/bluetooth.c
Normal file
604
src/bluetooth.c
Normal file
@ -0,0 +1,604 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2013 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOGDI
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#ifdef HAVE_WS2BTH_H
|
||||
#define BLUETOOTH
|
||||
#include <ws2bth.h>
|
||||
#endif
|
||||
#else
|
||||
#include <errno.h> // errno
|
||||
#include <unistd.h> // close
|
||||
#include <sys/types.h> // socket, getsockopt
|
||||
#include <sys/socket.h> // socket, getsockopt
|
||||
#include <sys/select.h> // select
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/time.h>
|
||||
#ifdef HAVE_BLUEZ
|
||||
#define BLUETOOTH
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/rfcomm.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "bluetooth.h"
|
||||
#include "common-private.h"
|
||||
#include "context-private.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef int s_ssize_t;
|
||||
typedef DWORD s_errcode_t;
|
||||
#define S_ERRNO WSAGetLastError ()
|
||||
#define S_EINTR WSAEINTR
|
||||
#define S_EAGAIN WSAEWOULDBLOCK
|
||||
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
|
||||
#define S_EINVAL WSAEINVAL
|
||||
#define S_EACCES WSAEACCES
|
||||
#define S_EAFNOSUPPORT WSAEAFNOSUPPORT
|
||||
#define S_INVALID INVALID_SOCKET
|
||||
#define S_IOCTL ioctlsocket
|
||||
#define S_CLOSE closesocket
|
||||
#else
|
||||
typedef ssize_t s_ssize_t;
|
||||
typedef int s_errcode_t;
|
||||
#define S_ERRNO errno
|
||||
#define S_EINTR EINTR
|
||||
#define S_EAGAIN EAGAIN
|
||||
#define S_ENOMEM ENOMEM
|
||||
#define S_EINVAL EINVAL
|
||||
#define S_EACCES EACCES
|
||||
#define S_EAFNOSUPPORT EAFNOSUPPORT
|
||||
#define S_INVALID -1
|
||||
#define S_IOCTL ioctl
|
||||
#define S_CLOSE close
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DC_ADDRESS_FORMAT "%012I64X"
|
||||
#else
|
||||
#define DC_ADDRESS_FORMAT "%012llX"
|
||||
#endif
|
||||
|
||||
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
|
||||
|
||||
#define MAX_DEVICES 255
|
||||
#define MAX_PERIODS 8
|
||||
|
||||
struct dc_bluetooth_t {
|
||||
dc_context_t *context;
|
||||
#ifdef _WIN32
|
||||
SOCKET fd;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
int timeout;
|
||||
};
|
||||
|
||||
#ifdef BLUETOOTH
|
||||
static dc_status_t
|
||||
syserror(s_errcode_t errcode)
|
||||
{
|
||||
switch (errcode) {
|
||||
case S_EINVAL:
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
case S_ENOMEM:
|
||||
return DC_STATUS_NOMEMORY;
|
||||
case S_EACCES:
|
||||
return DC_STATUS_NOACCESS;
|
||||
case S_EAFNOSUPPORT:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
default:
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BLUEZ
|
||||
static dc_bluetooth_address_t
|
||||
dc_address_get (const bdaddr_t *ba)
|
||||
{
|
||||
dc_bluetooth_address_t address = 0;
|
||||
|
||||
size_t shift = 0;
|
||||
for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) {
|
||||
address |= (dc_bluetooth_address_t) ba->b[i] << shift;
|
||||
shift += 8;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
static void
|
||||
dc_address_set (bdaddr_t *ba, dc_bluetooth_address_t address)
|
||||
{
|
||||
size_t shift = 0;
|
||||
for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) {
|
||||
ba->b[i] = (address >> shift) & 0xFF;
|
||||
shift += 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_open (dc_bluetooth_t **out, dc_context_t *context)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_bluetooth_t *device = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
device = (dc_bluetooth_t *) malloc (sizeof (dc_bluetooth_t));
|
||||
if (device == NULL) {
|
||||
SYSERROR (context, S_ENOMEM);
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Library context.
|
||||
device->context = context;
|
||||
|
||||
// Default to blocking reads.
|
||||
device->timeout = -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Initialize the winsock dll.
|
||||
WSADATA wsaData;
|
||||
WORD wVersionRequested = MAKEWORD (2, 2);
|
||||
int rc = WSAStartup (wVersionRequested, &wsaData);
|
||||
if (rc != 0) {
|
||||
SYSERROR (context, rc);
|
||||
status = DC_STATUS_UNSUPPORTED;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
// Confirm that the winsock dll supports version 2.2.
|
||||
// Note that if the dll supports versions greater than 2.2 in addition to
|
||||
// 2.2, it will still return 2.2 since that is the version we requested.
|
||||
if (LOBYTE (wsaData.wVersion) != 2 ||
|
||||
HIBYTE (wsaData.wVersion) != 2) {
|
||||
ERROR (context, "Incorrect winsock version.");
|
||||
status = DC_STATUS_UNSUPPORTED;
|
||||
goto error_wsacleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Open the socket.
|
||||
#ifdef _WIN32
|
||||
device->fd = socket (AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
|
||||
#else
|
||||
device->fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
#endif
|
||||
if (device->fd == S_INVALID) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_wsacleanup;
|
||||
}
|
||||
|
||||
*out = device;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_wsacleanup:
|
||||
#ifdef _WIN32
|
||||
WSACleanup ();
|
||||
error_free:
|
||||
#endif
|
||||
free (device);
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_close (dc_bluetooth_t *device)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
if (device == NULL)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
// Terminate all send and receive operations.
|
||||
shutdown (device->fd, 0);
|
||||
|
||||
// Close the socket.
|
||||
if (S_CLOSE (device->fd) != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
dc_status_set_error(&status, syserror(errcode));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Terminate the winsock dll.
|
||||
if (WSACleanup () != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
dc_status_set_error(&status, syserror(errcode));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Free memory.
|
||||
free (device);
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_set_timeout (dc_bluetooth_t *device, int timeout)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
INFO (device->context, "Timeout: value=%i", timeout);
|
||||
|
||||
device->timeout = timeout;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_discover (dc_bluetooth_t *device, dc_bluetooth_callback_t callback, void *userdata)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSAQUERYSET wsaq;
|
||||
memset(&wsaq, 0, sizeof (wsaq));
|
||||
wsaq.dwSize = sizeof (wsaq);
|
||||
wsaq.dwNameSpace = NS_BTH;
|
||||
wsaq.lpcsaBuffer = NULL;
|
||||
|
||||
HANDLE hLookup;
|
||||
if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == WSASERVICE_NOT_FOUND) {
|
||||
// No remote bluetooth devices found.
|
||||
status = DC_STATUS_SUCCESS;
|
||||
} else {
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
}
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
unsigned char buf[4096];
|
||||
LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf;
|
||||
memset(pwsaResults, 0, sizeof(WSAQUERYSET));
|
||||
pwsaResults->dwSize = sizeof(WSAQUERYSET);
|
||||
pwsaResults->dwNameSpace = NS_BTH;
|
||||
pwsaResults->lpBlob = NULL;
|
||||
|
||||
while (1) {
|
||||
DWORD dwSize = sizeof(buf);
|
||||
if (WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) {
|
||||
break; // No more results.
|
||||
}
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
if (pwsaResults->dwNumberOfCsAddrs == 0 ||
|
||||
pwsaResults->lpcsaBuffer == NULL ||
|
||||
pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) {
|
||||
ERROR (device->context, "Invalid results returned");
|
||||
status = DC_STATUS_IO;
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr;
|
||||
dc_bluetooth_address_t address = sa->btAddr;
|
||||
const char *name = (char *) pwsaResults->lpszServiceInstanceName;
|
||||
|
||||
INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name);
|
||||
|
||||
if (callback) callback (address, name, userdata);
|
||||
|
||||
}
|
||||
|
||||
error_close:
|
||||
WSALookupServiceEnd (hLookup);
|
||||
#else
|
||||
// Get the resource number for the first available bluetooth adapter.
|
||||
int dev = hci_get_route (NULL);
|
||||
if (dev < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
// Open a socket to the bluetooth adapter.
|
||||
int fd = hci_open_dev (dev);
|
||||
if (fd < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
// Allocate a buffer to store the results of the discovery.
|
||||
inquiry_info *devices = (inquiry_info *) malloc (MAX_DEVICES * sizeof(inquiry_info));
|
||||
if (devices == NULL) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
// Perform the bluetooth device discovery. The inquiry lasts for at
|
||||
// most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices
|
||||
// will be returned.
|
||||
int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH);
|
||||
if (ndevices < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < ndevices; ++i) {
|
||||
dc_bluetooth_address_t address = dc_address_get (&devices[i].bdaddr);
|
||||
|
||||
// Get the user friendly name.
|
||||
char buf[HCI_MAX_NAME_LENGTH], *name = buf;
|
||||
int rc = hci_read_remote_name (fd, &devices[i].bdaddr, sizeof(buf), buf, 0);
|
||||
if (rc < 0) {
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name);
|
||||
|
||||
if (callback) callback (address, name, userdata);
|
||||
}
|
||||
|
||||
error_free:
|
||||
free(devices);
|
||||
error_close:
|
||||
hci_close_dev(fd);
|
||||
#endif
|
||||
|
||||
error_exit:
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_connect (dc_bluetooth_t *device, dc_bluetooth_address_t address, unsigned int port)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
INFO (device->context, "Connect: address=" DC_ADDRESS_FORMAT ", port=%d", address, port);
|
||||
|
||||
#ifdef _WIN32
|
||||
SOCKADDR_BTH sa;
|
||||
sa.addressFamily = AF_BTH;
|
||||
sa.btAddr = address;
|
||||
sa.port = port;
|
||||
memset(&sa.serviceClassId, 0, sizeof(sa.serviceClassId));
|
||||
#else
|
||||
struct sockaddr_rc sa;
|
||||
sa.rc_family = AF_BLUETOOTH;
|
||||
sa.rc_channel = port;
|
||||
dc_address_set (&sa.rc_bdaddr, address);
|
||||
#endif
|
||||
|
||||
if (connect (device->fd, (struct sockaddr *) &sa, sizeof (sa)) != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
return syserror(errcode);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_get_available (dc_bluetooth_t *device, size_t *value)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long bytes = 0;
|
||||
#else
|
||||
int bytes = 0;
|
||||
#endif
|
||||
|
||||
if (S_IOCTL (device->fd, FIONREAD, &bytes) != 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
SYSERROR (device->context, errcode);
|
||||
return syserror(errcode);
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = bytes;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_read (dc_bluetooth_t *device, void *data, size_t size, size_t *actual)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t nbytes = 0;
|
||||
|
||||
if (device == NULL) {
|
||||
status = DC_STATUS_INVALIDARGS;
|
||||
goto out_invalidargs;
|
||||
}
|
||||
|
||||
while (nbytes < size) {
|
||||
fd_set fds;
|
||||
FD_ZERO (&fds);
|
||||
FD_SET (device->fd, &fds);
|
||||
|
||||
struct timeval tvt;
|
||||
if (device->timeout > 0) {
|
||||
tvt.tv_sec = (device->timeout / 1000);
|
||||
tvt.tv_usec = (device->timeout % 1000) * 1000;
|
||||
} else if (device->timeout == 0) {
|
||||
timerclear (&tvt);
|
||||
}
|
||||
|
||||
int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL);
|
||||
if (rc < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == S_EINTR)
|
||||
continue; // Retry.
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
break; // Timeout.
|
||||
}
|
||||
|
||||
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
|
||||
if (n < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == S_EINTR || errcode == S_EAGAIN)
|
||||
continue; // Retry.
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto out;
|
||||
} else if (n == 0) {
|
||||
break; // EOF reached.
|
||||
}
|
||||
|
||||
nbytes += n;
|
||||
}
|
||||
|
||||
if (nbytes != size) {
|
||||
status = DC_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes);
|
||||
|
||||
out_invalidargs:
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_bluetooth_write (dc_bluetooth_t *device, const void *data, size_t size, size_t *actual)
|
||||
{
|
||||
#ifdef BLUETOOTH
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t nbytes = 0;
|
||||
|
||||
if (device == NULL) {
|
||||
status = DC_STATUS_INVALIDARGS;
|
||||
goto out_invalidargs;
|
||||
}
|
||||
|
||||
while (nbytes < size) {
|
||||
fd_set fds;
|
||||
FD_ZERO (&fds);
|
||||
FD_SET (device->fd, &fds);
|
||||
|
||||
int rc = select (device->fd + 1, NULL, &fds, NULL, NULL);
|
||||
if (rc < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == S_EINTR)
|
||||
continue; // Retry.
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
break; // Timeout.
|
||||
}
|
||||
|
||||
s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0);
|
||||
if (n < 0) {
|
||||
s_errcode_t errcode = S_ERRNO;
|
||||
if (errcode == S_EINTR || errcode == S_EAGAIN)
|
||||
continue; // Retry.
|
||||
SYSERROR (device->context, errcode);
|
||||
status = syserror(errcode);
|
||||
goto out;
|
||||
} else if (n == 0) {
|
||||
break; // EOF.
|
||||
}
|
||||
|
||||
nbytes += n;
|
||||
}
|
||||
|
||||
if (nbytes != size) {
|
||||
status = DC_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes);
|
||||
|
||||
out_invalidargs:
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
174
src/bluetooth.h
Normal file
174
src/bluetooth.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2013 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DC_BLUETOOTH_H
|
||||
#define DC_BLUETOOTH_H
|
||||
|
||||
#include <libdivecomputer/common.h>
|
||||
#include <libdivecomputer/context.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Opaque object representing a bluetooth connection.
|
||||
*/
|
||||
typedef struct dc_bluetooth_t dc_bluetooth_t;
|
||||
|
||||
/**
|
||||
* Bluetooth address (48 bits).
|
||||
*/
|
||||
#if defined (_WIN32) && !defined (__GNUC__)
|
||||
typedef unsigned __int64 dc_bluetooth_address_t;
|
||||
#else
|
||||
typedef unsigned long long dc_bluetooth_address_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Bluetooth enumeration callback.
|
||||
*
|
||||
* @param[in] address The bluetooth device address.
|
||||
* @param[in] name The bluetooth device name.
|
||||
* @param[in] userdata The user data pointer.
|
||||
*/
|
||||
typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata);
|
||||
|
||||
/**
|
||||
* Open an bluetooth connection.
|
||||
*
|
||||
* @param[out] bluetooth A location to store the bluetooth connection.
|
||||
* @param[in] context A valid context object.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_open (dc_bluetooth_t **bluetooth, dc_context_t *context);
|
||||
|
||||
/**
|
||||
* Close the bluetooth connection and free all resources.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_close (dc_bluetooth_t *bluetooth);
|
||||
|
||||
/**
|
||||
* Set the read timeout.
|
||||
*
|
||||
* There are three distinct modes available:
|
||||
*
|
||||
* 1. Blocking (timeout < 0):
|
||||
*
|
||||
* The read operation is blocked until all the requested bytes have
|
||||
* been received. If the requested number of bytes does not arrive,
|
||||
* the operation will block forever.
|
||||
*
|
||||
* 2. Non-blocking (timeout == 0):
|
||||
*
|
||||
* The read operation returns immediately with the bytes that have
|
||||
* already been received, even if no bytes have been received.
|
||||
*
|
||||
* 3. Timeout (timeout > 0):
|
||||
*
|
||||
* The read operation is blocked until all the requested bytes have
|
||||
* been received. If the requested number of bytes does not arrive
|
||||
* within the specified amount of time, the operation will return
|
||||
* with the bytes that have already been received.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[in] timeout The timeout in milliseconds.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_set_timeout (dc_bluetooth_t *bluetooth, int timeout);
|
||||
|
||||
/**
|
||||
* Enumerate the bluetooth devices.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[in] callback The callback function to call.
|
||||
* @param[in] userdata User data to pass to the callback function.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_discover (dc_bluetooth_t *bluetooth, dc_bluetooth_callback_t callback, void *userdata);
|
||||
|
||||
/**
|
||||
* Connect to an bluetooth device.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[in] address The bluetooth device address.
|
||||
* @param[in] port The bluetooth port number.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_connect (dc_bluetooth_t *bluetooth, dc_bluetooth_address_t address, unsigned int port);
|
||||
|
||||
/**
|
||||
* Query the number of available bytes in the input buffer.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[out] value A location to store the number of bytes in
|
||||
* the input buffer.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_get_available (dc_bluetooth_t *bluetooth, size_t *value);
|
||||
|
||||
/**
|
||||
* Read data from the bluetooth connection.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[out] data The memory buffer to read the data into.
|
||||
* @param[in] size The number of bytes to read.
|
||||
* @param[out] actual An (optional) location to store the actual
|
||||
* number of bytes transferred.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_read (dc_bluetooth_t *bluetooth, void *data, size_t size, size_t *actual);
|
||||
|
||||
/**
|
||||
* Write data to the bluetooth connection.
|
||||
*
|
||||
* @param[in] bluetooth A valid bluetooth connection.
|
||||
* @param[in] data The memory buffer to write the data from.
|
||||
* @param[in] size The number of bytes to write.
|
||||
* @param[out] actual An (optional) location to store the actual
|
||||
* number of bytes transferred.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_bluetooth_write (dc_bluetooth_t *bluetooth, const void *data, size_t size, size_t *actual);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* DC_BLUETOOTH_H */
|
||||
@ -28,31 +28,36 @@
|
||||
#include "device-private.h"
|
||||
#include "serial.h"
|
||||
#include "array.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
|
||||
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0
|
||||
#define COCHRAN_MODEL_EMC_14 1
|
||||
#define COCHRAN_MODEL_EMC_16 2
|
||||
#define COCHRAN_MODEL_EMC_20 3
|
||||
#define MAXRETRIES 2
|
||||
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 0
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1
|
||||
#define COCHRAN_MODEL_EMC_14 2
|
||||
#define COCHRAN_MODEL_EMC_16 3
|
||||
#define COCHRAN_MODEL_EMC_20 4
|
||||
|
||||
typedef enum cochran_endian_t {
|
||||
ENDIAN_LE,
|
||||
ENDIAN_BE,
|
||||
ENDIAN_WORD_BE,
|
||||
} cochran_endian_t;
|
||||
|
||||
typedef struct cochran_commander_model_t {
|
||||
unsigned char id[8 + 1];
|
||||
unsigned char id[3 + 1];
|
||||
unsigned int model;
|
||||
} cochran_commander_model_t;
|
||||
|
||||
typedef struct cochran_data_t {
|
||||
unsigned char config[1024];
|
||||
unsigned char *logbook;
|
||||
unsigned char *sample;
|
||||
|
||||
unsigned short int dive_count;
|
||||
int fp_dive_num;
|
||||
int invalid_profile_dive_num;
|
||||
|
||||
unsigned int logbook_size;
|
||||
|
||||
@ -78,7 +83,9 @@ typedef struct cochran_device_layout_t {
|
||||
// Profile ringbuffer.
|
||||
unsigned int rb_profile_begin;
|
||||
unsigned int rb_profile_end;
|
||||
// Profile pointers.
|
||||
// pointers.
|
||||
unsigned int pt_fingerprint;
|
||||
unsigned int fingerprint_size;
|
||||
unsigned int pt_profile_pre;
|
||||
unsigned int pt_profile_begin;
|
||||
unsigned int pt_profile_end;
|
||||
@ -109,15 +116,15 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
|
||||
cochran_commander_device_close /* close */
|
||||
};
|
||||
|
||||
// Cochran Commander Nitrox
|
||||
static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model
|
||||
// Cochran Commander pre-21000 s/n
|
||||
static const cochran_device_layout_t cochran_cmdr_1_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_PRE21000, // model
|
||||
24, // address_bits
|
||||
ENDIAN_BE, // endian
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
0x046, // cf_dive_count
|
||||
0x06E, // cf_last_log
|
||||
0x200, // cf_last_interdive
|
||||
0x6c, // cf_last_log
|
||||
0x70, // cf_last_interdive
|
||||
0x0AA, // cf_serial_number
|
||||
0x00000000, // rb_logbook_begin
|
||||
0x00020000, // rb_logbook_end
|
||||
@ -125,6 +132,32 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
512, // rb_logbook_entry_count
|
||||
0x00020000, // rb_profile_begin
|
||||
0x00100000, // rb_profile_end
|
||||
12, // pt_fingerprint
|
||||
4, // fingerprint_size
|
||||
28, // pt_profile_pre
|
||||
0, // pt_profile_begin
|
||||
128, // pt_profile_end
|
||||
};
|
||||
|
||||
|
||||
// Cochran Commander Nitrox
|
||||
static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model
|
||||
24, // address_bits
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
0x046, // cf_dive_count
|
||||
0x06C, // cf_last_log
|
||||
0x070, // cf_last_interdive
|
||||
0x0AA, // cf_serial_number
|
||||
0x00000000, // rb_logbook_begin
|
||||
0x00020000, // rb_logbook_end
|
||||
256, // rb_logbook_entry_size
|
||||
512, // rb_logbook_entry_count
|
||||
0x00020000, // rb_profile_begin
|
||||
0x00100000, // rb_profile_end
|
||||
0, // pt_fingerprint
|
||||
6, // fingerprint_size
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
128, // pt_profile_end
|
||||
@ -146,6 +179,8 @@ static const cochran_device_layout_t cochran_emc14_device_layout = {
|
||||
256, // rb_logbook_entry_count
|
||||
0x00022000, // rb_profile_begin
|
||||
0x00200000, // rb_profile_end
|
||||
0, // pt_fingerprint
|
||||
6, // fingerprint_size
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
@ -167,6 +202,8 @@ static const cochran_device_layout_t cochran_emc16_device_layout = {
|
||||
1024, // rb_logbook_entry_count
|
||||
0x00094000, // rb_profile_begin
|
||||
0x00800000, // rb_profile_end
|
||||
0, // pt_fingerprint
|
||||
6, // fingerprint_size
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
@ -188,6 +225,8 @@ static const cochran_device_layout_t cochran_emc20_device_layout = {
|
||||
1024, // rb_logbook_entry_count
|
||||
0x00094000, // rb_profile_begin
|
||||
0x01000000, // rb_profile_end
|
||||
0, // pt_fingerprint
|
||||
6, // fingerprint_size
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
@ -199,15 +238,20 @@ static unsigned int
|
||||
cochran_commander_get_model (cochran_commander_device_t *device)
|
||||
{
|
||||
const cochran_commander_model_t models[] = {
|
||||
{"AM\x11""2212\x02", COCHRAN_MODEL_COMMANDER_AIR_NITROX},
|
||||
{"AM7303\x8b\x43", COCHRAN_MODEL_EMC_14},
|
||||
{"AMA315\xC3\xC5", COCHRAN_MODEL_EMC_16},
|
||||
{"AM2315\xA3\x71", COCHRAN_MODEL_EMC_20},
|
||||
{"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000},
|
||||
{"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX},
|
||||
{"730", COCHRAN_MODEL_EMC_14},
|
||||
{"731", COCHRAN_MODEL_EMC_14},
|
||||
{"A30", COCHRAN_MODEL_EMC_16},
|
||||
{"A31", COCHRAN_MODEL_EMC_16},
|
||||
{"230", COCHRAN_MODEL_EMC_20},
|
||||
{"231", COCHRAN_MODEL_EMC_20},
|
||||
{"\x40""30", COCHRAN_MODEL_EMC_20},
|
||||
};
|
||||
|
||||
unsigned int model = 0xFFFFFFFF;
|
||||
for (unsigned int i = 0; i < C_ARRAY_SIZE(models); ++i) {
|
||||
if (memcmp (device->id + 0x3B, models[i].id, sizeof(models[i].id) - 1) == 0) {
|
||||
if (memcmp (device->id + 0x3D, models[i].id, sizeof(models[i].id) - 1) == 0) {
|
||||
model = models[i].model;
|
||||
break;
|
||||
}
|
||||
@ -429,23 +473,164 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cochran_commander_read_retry (cochran_commander_device_t *device, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size)
|
||||
{
|
||||
// Save the state of the progress events.
|
||||
unsigned int saved = progress->current;
|
||||
|
||||
static void
|
||||
unsigned int nretries = 0;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
while ((rc = cochran_commander_read (device, progress, address, data, size)) != DC_STATUS_SUCCESS) {
|
||||
// Automatically discard a corrupted packet,
|
||||
// and request a new one.
|
||||
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
|
||||
return rc;
|
||||
|
||||
// Abort if the maximum number of retries is reached.
|
||||
if (nretries++ >= MAXRETRIES)
|
||||
return rc;
|
||||
|
||||
// Restore the state of the progress events.
|
||||
progress->current = saved;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For corrupt dives the end-of-samples pointer is 0xFFFFFFFF
|
||||
* search for a reasonable size, e.g. using next dive start sample
|
||||
* or end-of-samples to limit searching for recoverable samples
|
||||
*/
|
||||
static unsigned int
|
||||
cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num)
|
||||
{
|
||||
const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num;
|
||||
|
||||
if (log_num == data->dive_count)
|
||||
// Return next usable address from config page
|
||||
return array_uint32_le(data->config + device->layout->rb_profile_end);
|
||||
|
||||
// Next log's start address
|
||||
return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, unsigned int sample_start_address, unsigned int sample_end_address)
|
||||
{
|
||||
// Validate addresses
|
||||
if (sample_start_address < device->layout->rb_profile_begin ||
|
||||
sample_start_address > device->layout->rb_profile_end ||
|
||||
sample_end_address < device->layout->rb_profile_begin ||
|
||||
(sample_end_address > device->layout->rb_profile_end &&
|
||||
sample_end_address != 0xFFFFFFFF)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sample_end_address == 0xFFFFFFFF)
|
||||
// Corrupt dive, guess the end address
|
||||
sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num);
|
||||
|
||||
return ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Do several things. Find the log that matches the fingerprint,
|
||||
* calculate the total read size for progress indicator,
|
||||
* Determine the most recent dive without profile data.
|
||||
*/
|
||||
|
||||
static unsigned int
|
||||
cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data)
|
||||
{
|
||||
// Skip to fingerprint to reduce time
|
||||
if (data->dive_count < device->layout->rb_logbook_entry_count)
|
||||
data->fp_dive_num = data->dive_count;
|
||||
else
|
||||
data->fp_dive_num = device->layout->rb_logbook_entry_count;
|
||||
data->fp_dive_num--;
|
||||
// We track profile ringbuffer usage to determine which dives have profile data
|
||||
int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
|
||||
while (data->fp_dive_num >= 0 && memcmp(device->fingerprint,
|
||||
data->logbook + data->fp_dive_num * device->layout->rb_logbook_entry_size,
|
||||
sizeof(device->fingerprint)))
|
||||
data->fp_dive_num--;
|
||||
int dive_count = -1;
|
||||
data->fp_dive_num = -1;
|
||||
|
||||
// Start at end of log
|
||||
if (data->dive_count < device->layout->rb_logbook_entry_count)
|
||||
dive_count = data->dive_count;
|
||||
else
|
||||
dive_count = device->layout->rb_logbook_entry_count;
|
||||
dive_count--;
|
||||
|
||||
unsigned int sample_read_size = 0;
|
||||
data->invalid_profile_dive_num = -1;
|
||||
|
||||
// Remove the pre-dive events that occur after the last dive
|
||||
unsigned int rb_head_ptr = 0;
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
else
|
||||
rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
|
||||
unsigned int head_dive = 0, tail_dive = 0;
|
||||
|
||||
if (data->dive_count <= device->layout->rb_logbook_entry_count) {
|
||||
head_dive = data->dive_count;
|
||||
tail_dive = 0;
|
||||
} else {
|
||||
// Log wrapped
|
||||
tail_dive = data->dive_count % device->layout->rb_logbook_entry_count;
|
||||
head_dive = tail_dive;
|
||||
}
|
||||
|
||||
unsigned int last_profile_idx = (device->layout->rb_logbook_entry_count + head_dive - 1) % device->layout->rb_logbook_entry_count;
|
||||
unsigned int last_profile_end = array_uint32_le(data->logbook + last_profile_idx * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end);
|
||||
unsigned int last_profile_pre = 0xFFFFFFFF;
|
||||
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
last_profile_pre = array_uint32_word_be(data->config + device->layout->cf_last_log);
|
||||
else
|
||||
last_profile_pre = array_uint32_le(data->config + device->layout->cf_last_log);
|
||||
|
||||
if (rb_head_ptr > last_profile_end)
|
||||
profile_capacity_remaining -= rb_head_ptr - last_profile_end;
|
||||
|
||||
// Loop through dives to find FP, Accumulate profile data size,
|
||||
// and find the last dive with invalid profile
|
||||
for (unsigned int i = 0; i <= dive_count; ++i) {
|
||||
unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count;
|
||||
|
||||
unsigned char *log_entry = data->logbook + idx * device->layout->rb_logbook_entry_size;
|
||||
|
||||
// We're done if we find the fingerprint
|
||||
if (!memcmp(device->fingerprint, log_entry + device->layout->pt_fingerprint, device->layout->fingerprint_size)) {
|
||||
data->fp_dive_num = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int profile_pre = array_uint32_le(log_entry + device->layout->pt_profile_pre);
|
||||
unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin);
|
||||
unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end);
|
||||
|
||||
unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre);
|
||||
unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end);
|
||||
last_profile_pre = profile_pre;
|
||||
|
||||
// Determine if sample exists
|
||||
if (profile_capacity_remaining > 0) {
|
||||
// Subtract this dive's profile size including post-dive events
|
||||
profile_capacity_remaining -= sample_size;
|
||||
if (profile_capacity_remaining < 0) {
|
||||
// Save the last dive that is missing profile data
|
||||
data->invalid_profile_dive_num = idx;
|
||||
}
|
||||
// Accumulate read size for progress bar
|
||||
sample_read_size += read_size;
|
||||
}
|
||||
}
|
||||
|
||||
return sample_read_size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data)
|
||||
@ -509,103 +694,6 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For corrupt dives the end-of-samples pointer is 0xFFFFFFFF
|
||||
* search for a reasonable size, e.g. using next dive start sample
|
||||
* or end-of-samples to limit searching for recoverable samples
|
||||
*/
|
||||
static unsigned int
|
||||
cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num)
|
||||
{
|
||||
const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num;
|
||||
|
||||
if (log_num == data->dive_count)
|
||||
// Return next usable address from config page
|
||||
return array_uint32_le(data->config + device->layout->rb_profile_end);
|
||||
|
||||
// Next log's start address
|
||||
return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin);
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *data)
|
||||
{
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
|
||||
// Calculate max data sizes
|
||||
unsigned int max_config = sizeof(data->config);
|
||||
unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin;
|
||||
unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
progress.maximum = max_config + max_logbook + max_sample;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Emit ID block
|
||||
dc_event_vendor_t vendor;
|
||||
vendor.data = device->id;
|
||||
vendor.size = sizeof (device->id);
|
||||
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
|
||||
|
||||
// Read config
|
||||
rc = cochran_commander_read_config(device, &progress, data->config, sizeof(data->config));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Determine size of dive list to read.
|
||||
if (device->layout->endian == ENDIAN_LE)
|
||||
data->dive_count = array_uint16_le (data->config + device->layout->cf_dive_count);
|
||||
else
|
||||
data->dive_count = array_uint16_be (data->config + device->layout->cf_dive_count);
|
||||
|
||||
if (data->dive_count > device->layout->rb_logbook_entry_count) {
|
||||
data->logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size;
|
||||
} else {
|
||||
data->logbook_size = data->dive_count * device->layout->rb_logbook_entry_size;
|
||||
}
|
||||
|
||||
progress.maximum -= max_logbook - data->logbook_size;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Allocate space for log book.
|
||||
data->logbook = (unsigned char *) malloc(data->logbook_size);
|
||||
if (data->logbook == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Request log book
|
||||
rc = cochran_commander_read(device, &progress, 0, data->logbook, data->logbook_size);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Determine sample memory to read
|
||||
cochran_commander_find_fingerprint(device, data);
|
||||
cochran_commander_get_sample_parms(device, data);
|
||||
|
||||
progress.maximum -= max_sample - data->sample_size;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
if (data->sample_size > 0) {
|
||||
data->sample = (unsigned char *) malloc(data->sample_size);
|
||||
if (data->sample == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Read the sample data
|
||||
rc = cochran_commander_read (device, &progress, data->sample_data_offset, data->sample, data->sample_size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name)
|
||||
{
|
||||
@ -647,6 +735,9 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c
|
||||
|
||||
unsigned int model = cochran_commander_get_model(device);
|
||||
switch (model) {
|
||||
case COCHRAN_MODEL_COMMANDER_PRE21000:
|
||||
device->layout = &cochran_cmdr_1_device_layout;
|
||||
break;
|
||||
case COCHRAN_MODEL_COMMANDER_AIR_NITROX:
|
||||
device->layout = &cochran_cmdr_device_layout;
|
||||
break;
|
||||
@ -697,11 +788,11 @@ cochran_commander_device_set_fingerprint (dc_device_t *abstract, const unsigned
|
||||
{
|
||||
cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
|
||||
|
||||
if (size && size != sizeof (device->fingerprint))
|
||||
if (size && size != device->layout->fingerprint_size)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (size)
|
||||
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
|
||||
memcpy (device->fingerprint, data, device->layout->fingerprint_size);
|
||||
else
|
||||
memset (device->fingerprint, 0xFF, sizeof(device->fingerprint));
|
||||
|
||||
@ -767,111 +858,172 @@ static dc_status_t
|
||||
cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
|
||||
const cochran_device_layout_t *layout = device->layout;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
cochran_data_t data;
|
||||
data.logbook = NULL;
|
||||
data.sample = NULL;
|
||||
status = cochran_commander_read_all (device, &data);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
|
||||
// Calculate max data sizes
|
||||
unsigned int max_config = sizeof(data.config);
|
||||
unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin;
|
||||
unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin;
|
||||
|
||||
// setup progress indication
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
progress.maximum = max_config + max_logbook + max_sample;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Emit ID block
|
||||
dc_event_vendor_t vendor;
|
||||
vendor.data = device->id;
|
||||
vendor.size = sizeof (device->id);
|
||||
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
|
||||
|
||||
// Read config
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Determine size of dive list to read.
|
||||
if (layout->endian == ENDIAN_LE)
|
||||
data.dive_count = array_uint16_le (data.config + layout->cf_dive_count);
|
||||
else
|
||||
data.dive_count = array_uint16_be (data.config + layout->cf_dive_count);
|
||||
|
||||
if (data.dive_count == 0)
|
||||
// No dives to read
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
if (data.dive_count > layout->rb_logbook_entry_count) {
|
||||
data.logbook_size = layout->rb_logbook_entry_count * layout->rb_logbook_entry_size;
|
||||
} else {
|
||||
data.logbook_size = data.dive_count * layout->rb_logbook_entry_size;
|
||||
}
|
||||
|
||||
// Update progress indicator with new maximum
|
||||
progress.maximum -= max_logbook - data.logbook_size;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Allocate space for log book.
|
||||
data.logbook = (unsigned char *) malloc(data.logbook_size);
|
||||
if (data.logbook == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Request log book
|
||||
rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
status = rc;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Locate fingerprint, recent dive with invalid profile and calc read size
|
||||
unsigned int profile_read_size = cochran_commander_find_fingerprint(device, &data);
|
||||
// Update progress indicator with new maximum
|
||||
progress.maximum -= (max_sample - profile_read_size);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
cochran_commander_get_sample_parms(device, &data);
|
||||
|
||||
// Emit a device info event.
|
||||
dc_event_devinfo_t devinfo;
|
||||
devinfo.model = device->layout->model;
|
||||
devinfo.model = layout->model;
|
||||
devinfo.firmware = 0; // unknown
|
||||
devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number);
|
||||
if (layout->endian == ENDIAN_WORD_BE)
|
||||
devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number);
|
||||
else
|
||||
devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number);
|
||||
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
// Calculate profile RB effective head pointer
|
||||
// Cochran seems to erase 8K chunks so round up.
|
||||
unsigned int last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000;
|
||||
if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) {
|
||||
ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block.");
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error;
|
||||
unsigned int head_dive = 0, tail_dive = 0, dive_count = 0;
|
||||
|
||||
if (data.dive_count <= layout->rb_logbook_entry_count) {
|
||||
head_dive = data.dive_count;
|
||||
tail_dive = 0;
|
||||
} else {
|
||||
// Log wrapped
|
||||
tail_dive = data.dive_count % layout->rb_logbook_entry_count;
|
||||
head_dive = tail_dive;
|
||||
}
|
||||
|
||||
// We track profile ringbuffer usage to determine which dives have profile data
|
||||
int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
// Change tail to dive following the fingerprint dive.
|
||||
if (data.fp_dive_num > -1)
|
||||
tail_dive = (data.fp_dive_num + 1) % layout->rb_logbook_entry_count;
|
||||
|
||||
unsigned int dive_count = 0;
|
||||
if (data.dive_count < device->layout->rb_logbook_entry_count)
|
||||
dive_count = data.dive_count;
|
||||
else
|
||||
dive_count = device->layout->rb_logbook_entry_count;
|
||||
// Number of dives to read
|
||||
dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count;
|
||||
|
||||
int invalid_profile_flag = 0;
|
||||
|
||||
// Loop through each dive
|
||||
for (int i = dive_count - 1; i > data.fp_dive_num; i--) {
|
||||
unsigned char *log_entry = data.logbook + i * device->layout->rb_logbook_entry_size;
|
||||
for (unsigned int i = 0; i < dive_count; ++i) {
|
||||
unsigned int idx = (layout->rb_logbook_entry_count + head_dive - (i + 1)) % layout->rb_logbook_entry_count;
|
||||
|
||||
unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin);
|
||||
unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end);
|
||||
unsigned char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size;
|
||||
|
||||
// Validate
|
||||
if (sample_start_address < device->layout->rb_profile_begin ||
|
||||
sample_start_address > device->layout->rb_profile_end ||
|
||||
sample_end_address < device->layout->rb_profile_begin ||
|
||||
(sample_end_address > device->layout->rb_profile_end &&
|
||||
sample_end_address != 0xFFFFFFFF)) {
|
||||
continue;
|
||||
}
|
||||
unsigned int sample_start_address = array_uint32_le (log_entry + layout->pt_profile_begin);
|
||||
unsigned int sample_end_address = array_uint32_le (log_entry + layout->pt_profile_end);
|
||||
|
||||
if (sample_end_address == 0xFFFFFFFF)
|
||||
// Corrupt dive, guess the end address
|
||||
sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i);
|
||||
|
||||
// Determine if sample exists
|
||||
if (profile_capacity_remaining > 0) {
|
||||
// Subtract this dive's profile size including post-dive events
|
||||
profile_capacity_remaining -= (last_start_address - sample_start_address);
|
||||
// Adjust for a dive that wraps the buffer
|
||||
if (sample_start_address > last_start_address)
|
||||
profile_capacity_remaining -= device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
}
|
||||
last_start_address = sample_start_address;
|
||||
|
||||
unsigned char *sample = NULL;
|
||||
int sample_size = 0;
|
||||
if (profile_capacity_remaining < 0) {
|
||||
// There is no profile for this dive
|
||||
sample = NULL;
|
||||
sample_size = 0;
|
||||
} else {
|
||||
// Calculate the size of the profile only
|
||||
sample = data.sample + sample_start_address - data.sample_data_offset;
|
||||
sample_size = sample_end_address - sample_start_address;
|
||||
|
||||
if (sample_size < 0)
|
||||
// Adjust for ring buffer wrap-around
|
||||
sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
}
|
||||
// Determine if profile exists
|
||||
if (idx == data.invalid_profile_dive_num)
|
||||
invalid_profile_flag = 1;
|
||||
|
||||
if (!invalid_profile_flag)
|
||||
sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address);
|
||||
|
||||
// Build dive blob
|
||||
unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size;
|
||||
unsigned int dive_size = layout->rb_logbook_entry_size + sample_size;
|
||||
unsigned char *dive = (unsigned char *) malloc(dive_size);
|
||||
if (dive == NULL) {
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log
|
||||
memcpy(dive, log_entry, layout->rb_logbook_entry_size); // log
|
||||
|
||||
// Copy profile data
|
||||
// Read profile data
|
||||
if (sample_size) {
|
||||
if (sample_end_address == 0xFFFFFFFF)
|
||||
// Corrupt dive, guess the end address
|
||||
sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx);
|
||||
|
||||
if (sample_start_address <= sample_end_address) {
|
||||
memcpy(dive + device->layout->rb_logbook_entry_size, sample, sample_size);
|
||||
rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
// It wrapped the buffer, copy two sections
|
||||
unsigned int size = device->layout->rb_profile_end - sample_start_address;
|
||||
unsigned int size = layout->rb_profile_end - sample_start_address;
|
||||
|
||||
memcpy(dive + device->layout->rb_logbook_entry_size, sample, size);
|
||||
memcpy(dive + device->layout->rb_logbook_entry_size + size,
|
||||
data.sample, sample_end_address - device->layout->rb_profile_begin);
|
||||
rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = cochran_commander_read_retry (device, &progress, layout->rb_profile_begin, dive + layout->rb_logbook_entry_size + size, sample_end_address - layout->rb_profile_begin);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback && !callback (dive, dive_size, dive, sizeof(device->fingerprint), userdata)) {
|
||||
if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) {
|
||||
free(dive);
|
||||
break;
|
||||
}
|
||||
@ -881,6 +1033,5 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
error:
|
||||
free(data.logbook);
|
||||
free(data.sample);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -31,10 +31,14 @@
|
||||
|
||||
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
|
||||
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0
|
||||
#define COCHRAN_MODEL_EMC_14 1
|
||||
#define COCHRAN_MODEL_EMC_16 2
|
||||
#define COCHRAN_MODEL_EMC_20 3
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 0
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1
|
||||
#define COCHRAN_MODEL_EMC_14 2
|
||||
#define COCHRAN_MODEL_EMC_16 3
|
||||
#define COCHRAN_MODEL_EMC_20 4
|
||||
|
||||
// Cochran time stamps start at Jan 1, 1992
|
||||
#define COCHRAN_EPOCH 694242000
|
||||
|
||||
#define UNSUPPORTED 0xFFFFFFFF
|
||||
|
||||
@ -43,11 +47,19 @@ typedef enum cochran_sample_format_t {
|
||||
SAMPLE_EMC,
|
||||
} cochran_sample_format_t;
|
||||
|
||||
|
||||
typedef enum cochran_date_encoding_t {
|
||||
DATE_ENCODING_MSDHYM,
|
||||
DATE_ENCODING_SMHDMY,
|
||||
DATE_ENCODING_TICKS,
|
||||
} cochran_date_encoding_t;
|
||||
|
||||
typedef struct cochran_parser_layout_t {
|
||||
cochran_sample_format_t format;
|
||||
unsigned int headersize;
|
||||
unsigned int samplesize;
|
||||
unsigned int second, minute, hour, day, month, year;
|
||||
cochran_date_encoding_t date_encoding;
|
||||
unsigned int datetime;
|
||||
unsigned int pt_profile_begin;
|
||||
unsigned int water_conductivity;
|
||||
unsigned int pt_profile_pre;
|
||||
@ -101,11 +113,36 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = {
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = {
|
||||
SAMPLE_CMDR, // type
|
||||
256, // headersize
|
||||
2, // samplesize
|
||||
DATE_ENCODING_TICKS, // date_encoding
|
||||
8, // datetime, 4 bytes
|
||||
0, // pt_profile_begin, 4 bytes
|
||||
24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea)
|
||||
28, // pt_profile_pre, 4 bytes
|
||||
43, // start_temp, 1 byte, F
|
||||
54, // start_depth, 2 bytes, /4=ft
|
||||
68, // dive_number, 2 bytes
|
||||
73, // altitude, 1 byte, /4=kilofeet
|
||||
128, // pt_profile_end, 4 bytes
|
||||
153, // end_temp, 1 byte F
|
||||
166, // divetime, 2 bytes, minutes
|
||||
168, // max_depth, 2 bytes, /4=ft
|
||||
170, // avg_depth, 2 bytes, /4=ft
|
||||
210, // oxygen, 4 bytes (2 of) 2 bytes, /256=%
|
||||
UNSUPPORTED, // helium, 4 bytes (2 of) 2 bytes, /256=%
|
||||
232, // min_temp, 1 byte, /2+20=F
|
||||
233, // max_temp, 1 byte, /2+20=F
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_cmdr_parser_layout = {
|
||||
SAMPLE_CMDR, // type
|
||||
256, // headersize
|
||||
2, // samplesize
|
||||
1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each
|
||||
DATE_ENCODING_MSDHYM, // date_encoding
|
||||
0, // datetime, 6 bytes
|
||||
6, // pt_profile_begin, 4 bytes
|
||||
24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea)
|
||||
30, // pt_profile_pre, 4 bytes
|
||||
@ -128,7 +165,8 @@ static const cochran_parser_layout_t cochran_emc_parser_layout = {
|
||||
SAMPLE_EMC, // type
|
||||
512, // headersize
|
||||
3, // samplesize
|
||||
0, 1, 2, 3, 4, 5, // second, minute, hour, day, month, year, 1 byte each
|
||||
DATE_ENCODING_SMHDMY, // date_encoding
|
||||
0, // datetime, 6 bytes
|
||||
6, // pt_profile_begin, 4 bytes
|
||||
24, // water_conductivity, 1 byte 0=low(fresh), 2=high(sea)
|
||||
30, // pt_profile_pre, 4 bytes
|
||||
@ -296,6 +334,11 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
parser->model = model;
|
||||
|
||||
switch (model) {
|
||||
case COCHRAN_MODEL_COMMANDER_PRE21000:
|
||||
parser->layout = &cochran_cmdr_1_parser_layout;
|
||||
parser->events = cochran_cmdr_event_bytes;
|
||||
parser->nevents = C_ARRAY_SIZE(cochran_cmdr_event_bytes);
|
||||
break;
|
||||
case COCHRAN_MODEL_COMMANDER_AIR_NITROX:
|
||||
parser->layout = &cochran_cmdr_parser_layout;
|
||||
parser->events = cochran_cmdr_event_bytes;
|
||||
@ -340,13 +383,32 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat
|
||||
if (abstract->size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
dc_ticks_t ts = 0;
|
||||
|
||||
if (datetime) {
|
||||
datetime->second = data[layout->second];
|
||||
datetime->minute = data[layout->minute];
|
||||
datetime->hour = data[layout->hour];
|
||||
datetime->day = data[layout->day];
|
||||
datetime->month = data[layout->month];
|
||||
datetime->year = data[layout->year] + (data[layout->year] > 91 ? 1900 : 2000);
|
||||
switch (layout->date_encoding)
|
||||
{
|
||||
case DATE_ENCODING_MSDHYM:
|
||||
datetime->second = data[layout->datetime + 1];
|
||||
datetime->minute = data[layout->datetime + 0];
|
||||
datetime->hour = data[layout->datetime + 3];
|
||||
datetime->day = data[layout->datetime + 2];
|
||||
datetime->month = data[layout->datetime + 5];
|
||||
datetime->year = data[layout->datetime + 4] + (data[layout->datetime + 4] > 91 ? 1900 : 2000);
|
||||
break;
|
||||
case DATE_ENCODING_SMHDMY:
|
||||
datetime->second = data[layout->datetime + 0];
|
||||
datetime->minute = data[layout->datetime + 1];
|
||||
datetime->hour = data[layout->datetime + 2];
|
||||
datetime->day = data[layout->datetime + 3];
|
||||
datetime->month = data[layout->datetime + 4];
|
||||
datetime->year = data[layout->datetime + 5] + (data[layout->datetime + 5] > 91 ? 1900 : 2000);
|
||||
break;
|
||||
case DATE_ENCODING_TICKS:
|
||||
ts = array_uint32_le(data + layout->datetime) + COCHRAN_EPOCH;
|
||||
dc_datetime_localtime(datetime, ts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -471,10 +533,11 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
// know what the dive summary values are (i.e. max depth, min temp)
|
||||
if (array_uint32_le(data + layout->pt_profile_end) == 0xFFFFFFFF) {
|
||||
corrupt_dive = 1;
|
||||
dc_datetime_t d;
|
||||
cochran_commander_parser_get_datetime(abstract, &d);
|
||||
|
||||
WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples",
|
||||
data[layout->year], data[layout->month], data[layout->day],
|
||||
data[layout->hour], data[layout->minute], data[layout->second]);
|
||||
d.year, d.month, d.day, d.hour, d.minute, d.second);
|
||||
|
||||
// Eliminate inter-dive events
|
||||
size = cochran_commander_backparse(parser, samples, size);
|
||||
@ -484,7 +547,8 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
// and temp every other second.
|
||||
|
||||
// Prime values from the dive log section
|
||||
if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX) {
|
||||
if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX ||
|
||||
parser->model == COCHRAN_MODEL_COMMANDER_PRE21000) {
|
||||
// Commander stores start depth in quarter-feet
|
||||
start_depth = array_uint16_le (data + layout->start_depth) / 4.0;
|
||||
} else {
|
||||
@ -503,6 +567,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
|
||||
sample.gasmix = 0;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
unsigned int last_gasmix = sample.gasmix;
|
||||
|
||||
while (offset < size) {
|
||||
const unsigned char *s = samples + offset;
|
||||
@ -537,9 +602,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
if (s[0] & 0x80) {
|
||||
offset += cochran_commander_handle_event(parser, s[0], callback, userdata);
|
||||
|
||||
if (layout->format == SAMPLE_EMC) {
|
||||
// EMC models have events indicating change in deco status
|
||||
// Commander may have them but I don't have example data
|
||||
// Events indicating change in deco status
|
||||
switch (s[0]) {
|
||||
case 0xC5: // Deco obligation begins
|
||||
deco_obligation = 1;
|
||||
@ -551,7 +614,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
deco_ceiling += 10; // feet
|
||||
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60;
|
||||
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
|
||||
sample.deco.depth = deco_ceiling * FEET;
|
||||
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
|
||||
break;
|
||||
@ -560,7 +623,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
sample.deco.depth = deco_ceiling * FEET;
|
||||
sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60;
|
||||
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
|
||||
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
|
||||
break;
|
||||
case 0xC0: // Switched to FO2 21% mode (surface)
|
||||
@ -568,14 +631,19 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
break;
|
||||
case 0xCD: // Switched to deco blend
|
||||
case 0xEF: // Switched to gas blend 2
|
||||
if (last_gasmix != 1) {
|
||||
sample.gasmix = 1;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
last_gasmix = sample.gasmix;
|
||||
}
|
||||
break;
|
||||
case 0xF3: // Switched to gas blend 1
|
||||
if (last_gasmix != 0) {
|
||||
sample.gasmix = 0;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
break;
|
||||
last_gasmix = sample.gasmix;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
@ -29,6 +29,16 @@
|
||||
#define USBHID
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef HAVE_AF_IRDA_H
|
||||
#define IRDA
|
||||
#endif
|
||||
#else
|
||||
#ifdef HAVE_LINUX_IRDA_H
|
||||
#define IRDA
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -89,7 +99,6 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
/* Suunto EON Steel */
|
||||
#ifdef USBHID
|
||||
{"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, // BLE
|
||||
{"Scubapro", "G2", DC_FAMILY_SCUBAPRO_G2, 0x11}, // BLE
|
||||
#endif
|
||||
/* Uwatec Aladin */
|
||||
{"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, // FTDI
|
||||
@ -102,7 +111,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
/* Uwatec Memomouse */
|
||||
{"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, // FTDI
|
||||
/* Uwatec Smart */
|
||||
#ifdef HAVE_IRDA
|
||||
#ifdef IRDA
|
||||
{"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10},
|
||||
{"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11},
|
||||
{"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11},
|
||||
@ -127,6 +136,10 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20},
|
||||
{"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24},
|
||||
{"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26},
|
||||
/* Scubapro G2 */
|
||||
#ifdef USBHID
|
||||
{"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, // BLE
|
||||
#endif
|
||||
/* Reefnet */
|
||||
{"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1},
|
||||
{"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2},
|
||||
@ -302,10 +315,12 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"DiveSystem", "iDive2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42},
|
||||
{"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44},
|
||||
{"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45},
|
||||
{"Cochran", "Commander", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 1},
|
||||
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 2},
|
||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 3},
|
||||
/* Cochran Commander */
|
||||
{"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||
{"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 1},
|
||||
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 2},
|
||||
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 3},
|
||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 4},
|
||||
};
|
||||
|
||||
typedef struct dc_descriptor_iterator_t {
|
||||
@ -430,9 +445,9 @@ dc_descriptor_get_transport (dc_descriptor_t *descriptor)
|
||||
if (descriptor->type == DC_FAMILY_ATOMICS_COBALT)
|
||||
return DC_TRANSPORT_USB;
|
||||
else if (descriptor->type == DC_FAMILY_SUUNTO_EONSTEEL)
|
||||
return DC_TRANSPORT_USB;
|
||||
else if (descriptor->type == DC_FAMILY_SCUBAPRO_G2)
|
||||
return DC_TRANSPORT_USB;
|
||||
return DC_TRANSPORT_USBHID;
|
||||
else if (descriptor->type == DC_FAMILY_UWATEC_G2)
|
||||
return DC_TRANSPORT_USBHID;
|
||||
else if (descriptor->type == DC_FAMILY_UWATEC_SMART)
|
||||
return DC_TRANSPORT_IRDA;
|
||||
else
|
||||
|
||||
@ -127,9 +127,6 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
case DC_FAMILY_SUUNTO_EONSTEEL:
|
||||
rc = suunto_eonsteel_device_open (&device, context, name);
|
||||
break;
|
||||
case DC_FAMILY_SCUBAPRO_G2:
|
||||
rc = scubapro_g2_device_open (&device, context, name);
|
||||
break;
|
||||
case DC_FAMILY_UWATEC_ALADIN:
|
||||
rc = uwatec_aladin_device_open (&device, context, name);
|
||||
break;
|
||||
@ -142,6 +139,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
case DC_FAMILY_UWATEC_MERIDIAN:
|
||||
rc = uwatec_meridian_device_open (&device, context, name);
|
||||
break;
|
||||
case DC_FAMILY_UWATEC_G2:
|
||||
rc = scubapro_g2_device_open (&device, context, name);
|
||||
break;
|
||||
case DC_FAMILY_REEFNET_SENSUS:
|
||||
rc = reefnet_sensus_device_open (&device, context, name);
|
||||
break;
|
||||
|
||||
@ -42,7 +42,14 @@
|
||||
#define START 0x55
|
||||
#define ACK 0x06
|
||||
#define NAK 0x15
|
||||
#define BUSY 0x60
|
||||
|
||||
#define ERR_INVALID_CMD 0x10
|
||||
#define ERR_INVALID_LENGTH 0x20
|
||||
#define ERR_INVALID_DATA 0x30
|
||||
#define ERR_UNSUPPORTED 0x40
|
||||
#define ERR_UNAVAILABLE 0x58
|
||||
#define ERR_UNREADABLE 0x5F
|
||||
#define ERR_BUSY 0x60
|
||||
|
||||
#define NSTEPS 1000
|
||||
#define STEP(i,n) (NSTEPS * (i) / (n))
|
||||
@ -291,72 +298,98 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
|
||||
|
||||
|
||||
static dc_status_t
|
||||
divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
|
||||
divesystem_idive_packet (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int *errorcode)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
unsigned char packet[MAXPACKET] = {0};
|
||||
unsigned int length = 0;
|
||||
unsigned int nretries = 0;
|
||||
unsigned int length = sizeof(packet);
|
||||
unsigned int errcode = 0;
|
||||
|
||||
while (1) {
|
||||
// Send the command.
|
||||
rc = divesystem_idive_send (device, command, csize);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
status = divesystem_idive_send (device, command, csize);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Receive the answer.
|
||||
length = sizeof(packet);
|
||||
rc = divesystem_idive_receive (device, packet, &length);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
status = divesystem_idive_receive (device, packet, &length);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Verify the command byte.
|
||||
if (packet[0] != command[0]) {
|
||||
ERROR (abstract->context, "Unexpected packet header.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
status = DC_STATUS_PROTOCOL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Check the ACK byte.
|
||||
if (packet[length - 1] == ACK)
|
||||
break;
|
||||
|
||||
// Verify the NAK byte.
|
||||
if (packet[length - 1] != NAK) {
|
||||
// Verify the ACK/NAK byte.
|
||||
unsigned int type = packet[length - 1];
|
||||
if (type != ACK && type != NAK) {
|
||||
ERROR (abstract->context, "Unexpected ACK/NAK byte.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
status = DC_STATUS_PROTOCOL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Verify the length of the packet.
|
||||
if (length != 3) {
|
||||
unsigned int expected = (type == ACK ? asize : 1) + 2;
|
||||
if (length != expected) {
|
||||
ERROR (abstract->context, "Unexpected packet length.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
status = DC_STATUS_PROTOCOL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Verify the error code.
|
||||
unsigned int errcode = packet[1];
|
||||
if (errcode != BUSY) {
|
||||
// Get the error code from a NAK packet.
|
||||
if (type == NAK) {
|
||||
errcode = packet[1];
|
||||
ERROR (abstract->context, "Received NAK packet with error code %02x.", errcode);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
status = DC_STATUS_PROTOCOL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(answer, packet + 1, length - 2);
|
||||
|
||||
error:
|
||||
if (errorcode) {
|
||||
*errorcode = errcode;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int *errorcode)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
unsigned int errcode = 0;
|
||||
|
||||
unsigned int nretries = 0;
|
||||
while ((status = divesystem_idive_packet (device, command, csize, answer, asize, &errcode)) != DC_STATUS_SUCCESS) {
|
||||
// Automatically discard a corrupted packet,
|
||||
// and request a new one.
|
||||
if (status != DC_STATUS_PROTOCOL && status != DC_STATUS_TIMEOUT)
|
||||
break;
|
||||
|
||||
// Abort if the device reports a fatal error.
|
||||
if (errcode && errcode != ERR_BUSY)
|
||||
break;
|
||||
|
||||
// Abort if the maximum number of retries is reached.
|
||||
if (nretries++ >= MAXRETRIES)
|
||||
return DC_STATUS_PROTOCOL;
|
||||
break;
|
||||
|
||||
// Delay the next attempt.
|
||||
dc_serial_sleep (device->port, 100);
|
||||
}
|
||||
|
||||
// Verify the length of the packet.
|
||||
if (asize != length - 2) {
|
||||
ERROR (abstract->context, "Unexpected packet length.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
if (errorcode) {
|
||||
*errorcode = errcode;
|
||||
}
|
||||
|
||||
memcpy(answer, packet + 1, length - 2);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -365,6 +398,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract;
|
||||
unsigned char packet[MAXPACKET - 2];
|
||||
unsigned int errcode = 0;
|
||||
|
||||
const divesystem_idive_commands_t *commands = &idive;
|
||||
if (device->model >= IX3M_EASY) {
|
||||
@ -376,7 +410,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
unsigned char cmd_id[] = {commands->id.cmd, 0xED};
|
||||
rc = divesystem_idive_transfer (device, cmd_id, sizeof(cmd_id), packet, commands->id.size);
|
||||
rc = divesystem_idive_transfer (device, cmd_id, sizeof(cmd_id), packet, commands->id.size, &errcode);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -402,9 +436,14 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
|
||||
}
|
||||
|
||||
unsigned char cmd_range[] = {commands->range.cmd, 0x8D};
|
||||
rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
rc = divesystem_idive_transfer (device, cmd_range, sizeof(cmd_range), packet, commands->range.size, &errcode);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
if (errcode == ERR_UNAVAILABLE) {
|
||||
return DC_STATUS_SUCCESS; // No dives found.
|
||||
} else {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the range of the available dive numbers.
|
||||
unsigned int first = array_uint16_le (packet + 0);
|
||||
@ -431,9 +470,15 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
|
||||
unsigned char cmd_header[] = {commands->header.cmd,
|
||||
(number ) & 0xFF,
|
||||
(number >> 8) & 0xFF};
|
||||
rc = divesystem_idive_transfer (device, cmd_header, sizeof(cmd_header), packet, commands->header.size);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
rc = divesystem_idive_transfer (device, cmd_header, sizeof(cmd_header), packet, commands->header.size, &errcode);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
if (errcode == ERR_UNREADABLE) {
|
||||
WARNING(abstract->context, "Skipped unreadable dive!");
|
||||
continue;
|
||||
} else {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(packet + 7, device->fingerprint, sizeof(device->fingerprint)) == 0)
|
||||
break;
|
||||
@ -453,7 +498,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
|
||||
unsigned char cmd_sample[] = {commands->sample.cmd,
|
||||
(idx ) & 0xFF,
|
||||
(idx >> 8) & 0xFF};
|
||||
rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size * commands->nsamples);
|
||||
rc = divesystem_idive_transfer (device, cmd_sample, sizeof(cmd_sample), packet, commands->sample.size * commands->nsamples, &errcode);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
|
||||
48
src/irda.c
48
src/irda.c
@ -19,21 +19,31 @@
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <stdio.h> // snprintf
|
||||
#ifdef _WIN32
|
||||
#define NOGDI
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#ifdef HAVE_AF_IRDA_H
|
||||
#define IRDA
|
||||
#include <af_irda.h>
|
||||
#endif
|
||||
#else
|
||||
#include <string.h> // strerror
|
||||
#include <errno.h> // errno
|
||||
#include <unistd.h> // close
|
||||
#include <sys/types.h> // socket, getsockopt
|
||||
#include <sys/socket.h> // socket, getsockopt
|
||||
#ifdef HAVE_LINUX_IRDA_H
|
||||
#define IRDA
|
||||
#include <linux/types.h> // irda
|
||||
#include <linux/irda.h> // irda
|
||||
#endif
|
||||
#include <sys/select.h> // select
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/time.h>
|
||||
@ -86,6 +96,7 @@ struct dc_irda_t {
|
||||
int timeout;
|
||||
};
|
||||
|
||||
#ifdef IRDA
|
||||
static dc_status_t
|
||||
syserror(s_errcode_t errcode)
|
||||
{
|
||||
@ -102,10 +113,12 @@ syserror(s_errcode_t errcode)
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dc_status_t
|
||||
dc_irda_open (dc_irda_t **out, dc_context_t *context)
|
||||
{
|
||||
#ifdef IRDA
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_irda_t *device = NULL;
|
||||
|
||||
@ -167,11 +180,15 @@ error_free:
|
||||
#endif
|
||||
free (device);
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_close (dc_irda_t *device)
|
||||
{
|
||||
#ifdef IRDA
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
if (device == NULL)
|
||||
@ -200,11 +217,15 @@ dc_irda_close (dc_irda_t *device)
|
||||
free (device);
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_set_timeout (dc_irda_t *device, int timeout)
|
||||
{
|
||||
#ifdef IRDA
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -213,6 +234,9 @@ dc_irda_set_timeout (dc_irda_t *device, int timeout)
|
||||
device->timeout = timeout;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -230,6 +254,7 @@ dc_irda_set_timeout (dc_irda_t *device, int timeout)
|
||||
dc_status_t
|
||||
dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata)
|
||||
{
|
||||
#ifdef IRDA
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -303,11 +328,15 @@ dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name)
|
||||
{
|
||||
#ifdef IRDA
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -341,11 +370,15 @@ dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name)
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap)
|
||||
{
|
||||
#ifdef IRDA
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -374,11 +407,15 @@ dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_get_available (dc_irda_t *device, size_t *value)
|
||||
{
|
||||
#ifdef IRDA
|
||||
if (device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -398,11 +435,15 @@ dc_irda_get_available (dc_irda_t *device, size_t *value)
|
||||
*value = bytes;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
|
||||
{
|
||||
#ifdef IRDA
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t nbytes = 0;
|
||||
|
||||
@ -463,11 +504,15 @@ out_invalidargs:
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual)
|
||||
{
|
||||
#ifdef IRDA
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t nbytes = 0;
|
||||
|
||||
@ -520,4 +565,7 @@ out_invalidargs:
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2010 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "irda.h"
|
||||
|
||||
dc_status_t
|
||||
dc_irda_open (dc_irda_t **out, dc_context_t *context)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_close (dc_irda_t *device)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_set_timeout (dc_irda_t *device, int timeout)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_discover (dc_irda_t *device, dc_irda_callback_t callback, void *userdata)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_connect_name (dc_irda_t *device, unsigned int address, const char *name)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_connect_lsap (dc_irda_t *device, unsigned int address, unsigned int lsap)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_get_available (dc_irda_t *device, size_t *value)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -402,7 +402,8 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
|
||||
} else if (parser->model == T3B || parser->model == VT3 ||
|
||||
parser->model == DG03) {
|
||||
mode = (data[2] & 0xC0) >> 6;
|
||||
} else if (parser->model == VEO20 || parser->model == VEO30) {
|
||||
} else if (parser->model == VEO20 || parser->model == VEO30 ||
|
||||
parser->model == OCS) {
|
||||
mode = (data[1] & 0x60) >> 5;
|
||||
}
|
||||
|
||||
|
||||
@ -95,11 +95,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
case DC_FAMILY_UWATEC_MEMOMOUSE:
|
||||
rc = uwatec_memomouse_parser_create (&parser, context, devtime, systime);
|
||||
break;
|
||||
case DC_FAMILY_SCUBAPRO_G2:
|
||||
rc = uwatec_smart_parser_create (&parser, context, 0x11, devtime, systime);
|
||||
break;
|
||||
case DC_FAMILY_UWATEC_SMART:
|
||||
case DC_FAMILY_UWATEC_MERIDIAN:
|
||||
case DC_FAMILY_UWATEC_G2:
|
||||
rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime);
|
||||
break;
|
||||
case DC_FAMILY_REEFNET_SENSUS:
|
||||
|
||||
@ -45,7 +45,7 @@ static dc_status_t scubapro_g2_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t scubapro_g2_device_vtable = {
|
||||
sizeof(scubapro_g2_device_t),
|
||||
DC_FAMILY_SCUBAPRO_G2,
|
||||
DC_FAMILY_UWATEC_G2,
|
||||
scubapro_g2_device_set_fingerprint, /* set_fingerprint */
|
||||
NULL, /* read */
|
||||
NULL, /* write */
|
||||
|
||||
@ -619,11 +619,11 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
|
||||
if ((status & OC) == 0) {
|
||||
// PPO2
|
||||
if ((status & PPO2_EXTERNAL) == 0) {
|
||||
#ifdef SENSOR_AVERAGE
|
||||
sample.ppo2 = data[offset + 6] / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||
#else
|
||||
if ((status & PPO2_EXTERNAL) == 0) {
|
||||
sample.ppo2 = data[offset + 12] * parser->calibration[0];
|
||||
if (callback && (data[86] & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||
|
||||
@ -632,8 +632,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
|
||||
sample.ppo2 = data[offset + 15] * parser->calibration[2];
|
||||
if (callback && (data[86] & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Setpoint
|
||||
if (parser->model > PREDATOR) {
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
#define MERIDIAN 0x20
|
||||
#define CHROMIS 0x24
|
||||
#define MANTIS2 0x26
|
||||
#define G2 0x32
|
||||
|
||||
#define UNSUPPORTED 0xFFFFFFFF
|
||||
|
||||
@ -439,6 +440,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser)
|
||||
parser->events[2] = uwatec_smart_galileo_events_2;
|
||||
parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2);
|
||||
}
|
||||
} else if (parser->model == G2) {
|
||||
trimix = 1;
|
||||
}
|
||||
|
||||
// Get the settings.
|
||||
@ -500,7 +503,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser)
|
||||
divemode != DC_DIVEMODE_FREEDIVE) {
|
||||
if (parser->model == GALILEO || parser->model == GALILEOTRIMIX ||
|
||||
parser->model == ALADIN2G || parser->model == MERIDIAN ||
|
||||
parser->model == CHROMIS || parser->model == MANTIS2) {
|
||||
parser->model == CHROMIS || parser->model == MANTIS2 ||
|
||||
parser->model == G2) {
|
||||
unsigned int offset = header->tankpressure + 2 * i;
|
||||
endpressure = array_uint16_le(data + offset);
|
||||
beginpressure = array_uint16_le(data + offset + 2 * header->ngases);
|
||||
@ -578,6 +582,7 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
|
||||
case MERIDIAN:
|
||||
case CHROMIS:
|
||||
case MANTIS2:
|
||||
case G2:
|
||||
parser->headersize = 152;
|
||||
parser->header = &uwatec_smart_galileo_header;
|
||||
parser->samples = uwatec_smart_galileo_samples;
|
||||
@ -928,7 +933,8 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
unsigned int id = 0;
|
||||
if (parser->model == GALILEO || parser->model == GALILEOTRIMIX ||
|
||||
parser->model == ALADIN2G || parser->model == MERIDIAN ||
|
||||
parser->model == CHROMIS || parser->model == MANTIS2) {
|
||||
parser->model == CHROMIS || parser->model == MANTIS2 ||
|
||||
parser->model == G2) {
|
||||
// Uwatec Galileo
|
||||
id = uwatec_galileo_identify (data[offset]);
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user