Compare commits
168 Commits
Subsurface
...
Subsurface
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ae735a4d7 | ||
|
|
a1472a7247 | ||
|
|
8a34d822ff | ||
|
|
db70c581a6 | ||
|
|
f5aee8198f | ||
|
|
506bcec3cd | ||
|
|
77f87bebe3 | ||
|
|
54f6bff929 | ||
|
|
8c443c5abc | ||
|
|
238a3734a5 | ||
|
|
c44d2a8faf | ||
|
|
a2f358f0b5 | ||
|
|
9d7335dc6d | ||
|
|
4dce920c79 | ||
|
|
7de3a549ee | ||
|
|
06c34fc8b0 | ||
|
|
e177b28627 | ||
|
|
8a84ece7d0 | ||
|
|
0099aeeb70 | ||
|
|
07f5777c71 | ||
|
|
c0f025b019 | ||
|
|
3602a324b8 | ||
|
|
aa0b522ab8 | ||
|
|
d7503b05e2 | ||
|
|
aed80fe7fd | ||
|
|
928be1f45b | ||
|
|
9fd6635cf6 | ||
|
|
eed75cb0be | ||
|
|
4a3f7a7cce | ||
|
|
8ea8cebb4e | ||
|
|
6f4776d6c4 | ||
|
|
04073759a8 | ||
|
|
84ad5aa5ad | ||
|
|
41417fbe59 | ||
|
|
8736a6dca1 | ||
|
|
d85481cefd | ||
|
|
0143be5497 | ||
|
|
208807180a | ||
|
|
3aa40c6135 | ||
|
|
accc63df11 | ||
|
|
ca115b97e2 | ||
|
|
c863db02f0 | ||
|
|
70526a47eb | ||
|
|
b7699b6985 | ||
|
|
e590739260 | ||
|
|
60efc308d2 | ||
|
|
e9754bac64 | ||
|
|
323a606688 | ||
|
|
bae506397e | ||
|
|
7f21c27b7a | ||
|
|
df9984e123 | ||
|
|
a0c5b5b53b | ||
|
|
9df029a704 | ||
|
|
29135bcdf8 | ||
|
|
b04f393b97 | ||
|
|
84eb59c3ce | ||
|
|
ca04147126 | ||
|
|
71cf0a5c69 | ||
|
|
3f74840f4c | ||
|
|
2e650b68ae | ||
|
|
6fccf5923f | ||
|
|
a6421b2f7b | ||
|
|
19b560d9b2 | ||
|
|
534dd2f34b | ||
|
|
4ec7283841 | ||
|
|
11fec04865 | ||
|
|
af84bd10fd | ||
|
|
a568c756a1 | ||
|
|
3ea2795bf1 | ||
|
|
89111ebb2a | ||
|
|
a6538ae7da | ||
|
|
f7d2086675 | ||
|
|
4c5b51f851 | ||
|
|
20d268acdb | ||
|
|
94e7a77d01 | ||
|
|
7e086f697d | ||
|
|
f9db4ca97c | ||
|
|
ed8b8f1221 | ||
|
|
6fa9f1566f | ||
|
|
d11d30999b | ||
|
|
cba786df2f | ||
|
|
4527c77a6b | ||
|
|
406dbf3dae | ||
|
|
8fb4018db3 | ||
|
|
8eb1c1232e | ||
|
|
7fe87f36b5 | ||
|
|
4599405078 | ||
|
|
69d26e00b0 | ||
|
|
5d4e1f97db | ||
|
|
3547c72111 | ||
|
|
1b5ccf5983 | ||
|
|
79e2e3b0b5 | ||
|
|
2e26ae4708 | ||
|
|
f38e8fe9ee | ||
|
|
0aeae321dc | ||
|
|
de7ed23bec | ||
|
|
341b7c2dad | ||
|
|
26b027cad8 | ||
|
|
5361bc06bd | ||
|
|
87facc940b | ||
|
|
bdc1c1349e | ||
|
|
f67bdcd080 | ||
|
|
2015546952 | ||
|
|
480e54ace7 | ||
|
|
ecebda3b19 | ||
|
|
6c6752d87e | ||
|
|
8356e22e61 | ||
|
|
515dc17914 | ||
|
|
cb0584adc1 | ||
|
|
f37b4b06db | ||
|
|
aa2f670224 | ||
|
|
af30fbb3f8 | ||
|
|
b13d8da426 | ||
|
|
7f6348b066 | ||
|
|
95d69ea30d | ||
|
|
cc12560b29 | ||
|
|
784844d8dd | ||
|
|
2fc31b72e7 | ||
|
|
2e0db552ad | ||
|
|
6bb60bba31 | ||
|
|
28f8cf6e19 | ||
|
|
55a44a73bb | ||
|
|
7c33c633fb | ||
|
|
e227a4c5a7 | ||
|
|
8bb373e514 | ||
|
|
290f2781ea | ||
|
|
4f321aef40 | ||
|
|
92a1919cf5 | ||
|
|
2106cb2dab | ||
|
|
854f58fa32 | ||
|
|
058538e628 | ||
|
|
138fd856bd | ||
|
|
1ccf937aa3 | ||
|
|
da0073c971 | ||
|
|
8932b2a936 | ||
|
|
9c7d1102d4 | ||
|
|
ad4bcffa23 | ||
|
|
f103553df1 | ||
|
|
a6274a3a55 | ||
|
|
adfff2d4a7 | ||
|
|
f0e695b089 | ||
|
|
fe2448e34f | ||
|
|
7fd201a400 | ||
|
|
c39dda89bc | ||
|
|
0c8886ce70 | ||
|
|
53ccc4f43b | ||
|
|
8c3e44aa0c | ||
|
|
5dbaa7a053 | ||
|
|
69bd993233 | ||
|
|
a50a1e0688 | ||
|
|
b9e3f40d59 | ||
|
|
f184b45e09 | ||
|
|
0ff1dd15f5 | ||
|
|
fb15ef18f6 | ||
|
|
3a17156be6 | ||
|
|
1f24f67565 | ||
|
|
5ecc65799c | ||
|
|
ddfdb6c306 | ||
|
|
b85f2333be | ||
|
|
06426af656 | ||
|
|
f459155b54 | ||
|
|
ebe6704747 | ||
|
|
5f76249923 | ||
|
|
0ddec2b50f | ||
|
|
8f4038d4cd | ||
|
|
d90417da38 | ||
|
|
f902f5882c | ||
|
|
eac8e98ee7 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -82,3 +82,5 @@ Makefile.in
|
||||
/src/libdivecomputer.la
|
||||
/src/libdivecomputer.rc
|
||||
/src/revision.h
|
||||
|
||||
/build
|
||||
|
||||
13
configure.ac
13
configure.ac
@ -2,7 +2,7 @@
|
||||
m4_define([dc_version_major],[0])
|
||||
m4_define([dc_version_minor],[7])
|
||||
m4_define([dc_version_micro],[0])
|
||||
m4_define([dc_version_suffix],[devel])
|
||||
m4_define([dc_version_suffix],[devel-Subsurface-branch])
|
||||
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
|
||||
|
||||
# Libtool versioning.
|
||||
@ -60,6 +60,15 @@ AC_ARG_ENABLE([doc],
|
||||
[], [enable_doc=yes])
|
||||
AM_CONDITIONAL([ENABLE_DOC], [test "x$enable_doc" = "xyes"])
|
||||
|
||||
# BLE support
|
||||
AC_ARG_ENABLE([ble],
|
||||
[AS_HELP_STRING([--enable-ble=@<:@yes/no@:>@],
|
||||
[Enable BLE dive computers @<:@default=yes@:>@])],
|
||||
[], [enable_ble=no])
|
||||
AS_IF([test "x$enable_ble" = "xyes"], [
|
||||
AC_DEFINE(ENABLE_BLE, [1], [Enable support for BLE dive computers.])
|
||||
])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_C99
|
||||
@ -173,9 +182,7 @@ AC_CHECK_FUNCS([getopt_long])
|
||||
|
||||
# Checks for supported compiler options.
|
||||
AX_APPEND_COMPILE_FLAGS([ \
|
||||
-pedantic \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wrestrict \
|
||||
-Wformat=2 \
|
||||
|
||||
@ -400,6 +400,24 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
|
||||
convert_pressure(atmospheric, output->units));
|
||||
}
|
||||
|
||||
message ("Parsing strings.\n");
|
||||
int idx;
|
||||
for (idx = 0; idx < 100; idx++) {
|
||||
dc_field_string_t str = { NULL };
|
||||
status = dc_parser_get_field(parser, DC_FIELD_STRING, idx, &str);
|
||||
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
|
||||
ERROR ("Error parsing strings");
|
||||
goto cleanup;
|
||||
}
|
||||
if (status == DC_STATUS_UNSUPPORTED)
|
||||
break;
|
||||
if (!str.desc || !str.value)
|
||||
break;
|
||||
fprintf (output->ostream, "<extradata key='%s' value='%s' />\n",
|
||||
str.desc, str.value);
|
||||
|
||||
}
|
||||
|
||||
// Parse the sample data.
|
||||
message ("Parsing the sample data.\n");
|
||||
status = dc_parser_samples_foreach (parser, sample_cb, &sampledata);
|
||||
|
||||
@ -3,6 +3,7 @@ libdivecomputer_HEADERS = \
|
||||
version.h \
|
||||
common.h \
|
||||
context.h \
|
||||
custom_io.h \
|
||||
buffer.h \
|
||||
descriptor.h \
|
||||
iterator.h \
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#define DC_CONTEXT_H
|
||||
|
||||
#include "common.h"
|
||||
#include "custom_io.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -30,6 +31,9 @@ extern "C" {
|
||||
|
||||
typedef struct dc_context_t dc_context_t;
|
||||
|
||||
/* Opaque libdivecomputer *user* data structure */
|
||||
typedef struct dc_user_device_t dc_user_device_t;
|
||||
|
||||
typedef enum dc_loglevel_t {
|
||||
DC_LOGLEVEL_NONE,
|
||||
DC_LOGLEVEL_ERROR,
|
||||
@ -47,6 +51,9 @@ dc_context_new (dc_context_t **context);
|
||||
dc_status_t
|
||||
dc_context_free (dc_context_t *context);
|
||||
|
||||
dc_status_t
|
||||
dc_context_set_custom_io (dc_context_t *context, dc_custom_io_t *custom_io, dc_user_device_t *);
|
||||
|
||||
dc_status_t
|
||||
dc_context_set_loglevel (dc_context_t *context, dc_loglevel_t loglevel);
|
||||
|
||||
|
||||
71
include/libdivecomputer/custom_io.h
Normal file
71
include/libdivecomputer/custom_io.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef CUSTOM_IO_H
|
||||
#define CUSTOM_IO_H
|
||||
|
||||
#include <libdivecomputer/iostream.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct dc_context_t;
|
||||
struct dc_user_device_t;
|
||||
|
||||
/*
|
||||
* Two different pointers to user-supplied data.
|
||||
*
|
||||
* The 'userdata' pointer is for the IO routines themselves,
|
||||
* generally filled in by the 'xyz_open()' routine with whatever
|
||||
* file descriptor etc information.
|
||||
*
|
||||
* The 'user_device' pointer is set when registering the
|
||||
* custom IO with the download context, and has whatever
|
||||
* data the downloader needs.
|
||||
*
|
||||
* The two are very different. The userdata is "per instance",
|
||||
* and when nesting custom IO handlers, each level would
|
||||
* generally have its own userdata, that would be specific
|
||||
* to that particular set of IO routines.
|
||||
*
|
||||
* In contrast, the user_device is filled in when the
|
||||
* download context is created, before open() is even called,
|
||||
* and isn't specific to the IO routines, but to the download
|
||||
* as a whole.
|
||||
*/
|
||||
typedef struct dc_custom_io_t
|
||||
{
|
||||
void *userdata;
|
||||
struct dc_user_device_t *user_device;
|
||||
|
||||
// Custom serial (generally BT rfcomm)
|
||||
dc_status_t (*serial_open) (struct dc_custom_io_t *io, struct dc_context_t *, const char *name);
|
||||
dc_status_t (*serial_close) (struct dc_custom_io_t *io);
|
||||
dc_status_t (*serial_read) (struct dc_custom_io_t *io, void* data, size_t size, size_t *actual);
|
||||
dc_status_t (*serial_write) (struct dc_custom_io_t *io, const void* data, size_t size, size_t *actual);
|
||||
dc_status_t (*serial_purge) (struct dc_custom_io_t *io, dc_direction_t);
|
||||
dc_status_t (*serial_get_available) (struct dc_custom_io_t *io, size_t *value);
|
||||
dc_status_t (*serial_set_timeout) (struct dc_custom_io_t *io, long timeout);
|
||||
dc_status_t (*serial_configure) (struct dc_custom_io_t *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
|
||||
dc_status_t (*serial_set_dtr) (struct dc_custom_io_t *io, int level);
|
||||
dc_status_t (*serial_set_rts) (struct dc_custom_io_t *io, int level);
|
||||
dc_status_t (*serial_set_halfduplex) (struct dc_custom_io_t *io, unsigned int value);
|
||||
dc_status_t (*serial_set_break) (struct dc_custom_io_t *io, unsigned int level);
|
||||
//dc_serial_set_latency (dc_serial_t *device, unsigned int milliseconds) - Unused
|
||||
//dc_serial_get_lines (dc_serial_t *device, unsigned int *value) - Unused
|
||||
//dc_serial_flush (dc_serial_t *device) - No device interaction
|
||||
//dc_serial_sleep (dc_serial_t *device, unsigned int timeout) - No device interaction
|
||||
|
||||
// Custom packet transfer (generally BLE GATT)
|
||||
int packet_size;
|
||||
dc_status_t (*packet_open) (struct dc_custom_io_t *, struct dc_context_t *, const char *);
|
||||
dc_status_t (*packet_close) (struct dc_custom_io_t *);
|
||||
dc_status_t (*packet_read) (struct dc_custom_io_t *, void* data, size_t size, size_t *actual);
|
||||
dc_status_t (*packet_write) (struct dc_custom_io_t *, const void* data, size_t size, size_t *actual);
|
||||
} dc_custom_io_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* CUSTOM_IO_H */
|
||||
@ -58,6 +58,9 @@ dc_descriptor_get_type (dc_descriptor_t *descriptor);
|
||||
unsigned int
|
||||
dc_descriptor_get_model (dc_descriptor_t *descriptor);
|
||||
|
||||
unsigned int
|
||||
dc_descriptor_get_serial (dc_descriptor_t *descriptor);
|
||||
|
||||
dc_transport_t
|
||||
dc_descriptor_get_transport (dc_descriptor_t *descriptor);
|
||||
|
||||
|
||||
@ -62,9 +62,13 @@ typedef enum dc_field_type_t {
|
||||
DC_FIELD_TEMPERATURE_MAXIMUM,
|
||||
DC_FIELD_TANK_COUNT,
|
||||
DC_FIELD_TANK,
|
||||
DC_FIELD_DIVEMODE
|
||||
DC_FIELD_DIVEMODE,
|
||||
DC_FIELD_STRING,
|
||||
} dc_field_type_t;
|
||||
|
||||
// Make it easy to test support compile-time with "#ifdef DC_FIELD_STRING"
|
||||
#define DC_FIELD_STRING DC_FIELD_STRING
|
||||
|
||||
typedef enum parser_sample_event_t {
|
||||
SAMPLE_EVENT_NONE,
|
||||
SAMPLE_EVENT_DECOSTOP,
|
||||
@ -92,24 +96,31 @@ typedef enum parser_sample_event_t {
|
||||
SAMPLE_EVENT_HEADING,
|
||||
SAMPLE_EVENT_TISSUELEVEL,
|
||||
SAMPLE_EVENT_GASCHANGE2, /* Deprecated: replaced by DC_SAMPLE_GASMIX. */
|
||||
SAMPLE_EVENT_STRING,
|
||||
} parser_sample_event_t;
|
||||
|
||||
/* To let the compile know we have this */
|
||||
#define SAMPLE_EVENT_STRING SAMPLE_EVENT_STRING
|
||||
|
||||
/* For backwards compatibility */
|
||||
#define SAMPLE_EVENT_UNKNOWN SAMPLE_EVENT_FLOOR
|
||||
|
||||
typedef enum parser_sample_flags_t {
|
||||
SAMPLE_FLAGS_NONE = 0,
|
||||
SAMPLE_FLAGS_BEGIN = (1 << 0),
|
||||
SAMPLE_FLAGS_END = (1 << 1)
|
||||
SAMPLE_FLAGS_END = (1 << 1),
|
||||
SAMPLE_FLAGS_SEVERITY_MASK = (7 << 2),
|
||||
} parser_sample_flags_t;
|
||||
|
||||
#define SAMPLE_FLAGS_SEVERITY_SHIFT 2
|
||||
|
||||
typedef enum parser_sample_vendor_t {
|
||||
SAMPLE_VENDOR_NONE,
|
||||
SAMPLE_VENDOR_UWATEC_ALADIN,
|
||||
SAMPLE_VENDOR_UWATEC_SMART,
|
||||
SAMPLE_VENDOR_OCEANIC_VTPRO,
|
||||
SAMPLE_VENDOR_OCEANIC_VEO250,
|
||||
SAMPLE_VENDOR_OCEANIC_ATOM2
|
||||
SAMPLE_VENDOR_OCEANIC_ATOM2,
|
||||
} parser_sample_vendor_t;
|
||||
|
||||
typedef enum dc_water_t {
|
||||
@ -148,11 +159,17 @@ typedef struct dc_gasmix_t {
|
||||
|
||||
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF
|
||||
|
||||
typedef enum dc_tankvolume_t {
|
||||
DC_TANKVOLUME_NONE,
|
||||
DC_TANKVOLUME_METRIC,
|
||||
DC_TANKVOLUME_IMPERIAL,
|
||||
} dc_tankvolume_t;
|
||||
typedef unsigned int dc_tankinfo_t;
|
||||
#define DC_TANKINFO_METRIC 1
|
||||
#define DC_TANKINFO_IMPERIAL 2
|
||||
#define DC_TANKINFO_CC_DILUENT 4
|
||||
#define DC_TANKINFO_CC_O2 8
|
||||
|
||||
// For backwards compatibility
|
||||
#define DC_TANKVOLUME_NONE 0
|
||||
#define DC_TANKVOLUME_METRIC DC_TANKINFO_METRIC
|
||||
#define DC_TANKVOLUME_IMPERIAL DC_TANKINFO_IMPERIAL
|
||||
|
||||
|
||||
/*
|
||||
* Tank volume
|
||||
@ -179,13 +196,18 @@ typedef enum dc_tankvolume_t {
|
||||
|
||||
typedef struct dc_tank_t {
|
||||
unsigned int gasmix; /* Gas mix index, or DC_GASMIX_UNKNOWN */
|
||||
dc_tankvolume_t type; /* Tank type */
|
||||
dc_tankinfo_t type; /* Tank type - metric/imperial and oc/cc */
|
||||
double volume; /* Volume (liter) */
|
||||
double workpressure; /* Work pressure (bar) */
|
||||
double beginpressure; /* Begin pressure (bar) */
|
||||
double endpressure; /* End pressure (bar) */
|
||||
} dc_tank_t;
|
||||
|
||||
typedef struct dc_field_string_t {
|
||||
const char *desc;
|
||||
const char *value;
|
||||
} dc_field_string_t;
|
||||
|
||||
typedef union dc_sample_value_t {
|
||||
unsigned int time;
|
||||
double depth;
|
||||
@ -197,6 +219,7 @@ typedef union dc_sample_value_t {
|
||||
struct {
|
||||
unsigned int type;
|
||||
unsigned int time;
|
||||
const char *name;
|
||||
unsigned int flags;
|
||||
unsigned int value;
|
||||
} event;
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* use these defines to detect Subsurface specific features */
|
||||
#define SSRF_LIBDC_VERSION 2
|
||||
#define SSRF_CUSTOM_IO 2
|
||||
|
||||
#define DC_VERSION "@DC_VERSION@"
|
||||
#define DC_VERSION_MAJOR @DC_VERSION_MAJOR@
|
||||
#define DC_VERSION_MINOR @DC_VERSION_MINOR@
|
||||
|
||||
@ -3,7 +3,7 @@ AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = libdivecomputer.la
|
||||
|
||||
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm
|
||||
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm -lz
|
||||
libdivecomputer_la_LDFLAGS = \
|
||||
-version-info $(DC_VERSION_LIBTOOL) \
|
||||
-no-undefined \
|
||||
@ -32,6 +32,7 @@ libdivecomputer_la_SOURCES = \
|
||||
suunto_vyper2.h suunto_vyper2.c \
|
||||
suunto_d9.h suunto_d9.c suunto_d9_parser.c \
|
||||
suunto_eonsteel.h suunto_eonsteel.c suunto_eonsteel_parser.c \
|
||||
scubapro_g2.h scubapro_g2.c \
|
||||
reefnet_sensus.h reefnet_sensus.c reefnet_sensus_parser.c \
|
||||
reefnet_sensuspro.h reefnet_sensuspro.c reefnet_sensuspro_parser.c \
|
||||
reefnet_sensusultra.h reefnet_sensusultra.c reefnet_sensusultra_parser.c \
|
||||
@ -39,7 +40,6 @@ libdivecomputer_la_SOURCES = \
|
||||
uwatec_memomouse.h uwatec_memomouse.c uwatec_memomouse_parser.c \
|
||||
uwatec_smart.h uwatec_smart.c uwatec_smart_parser.c \
|
||||
uwatec_meridian.h uwatec_meridian.c \
|
||||
uwatec_g2.h uwatec_g2.c \
|
||||
oceanic_common.h oceanic_common.c \
|
||||
oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \
|
||||
oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \
|
||||
@ -83,6 +83,7 @@ libdivecomputer_la_SOURCES += irda.h irda.c
|
||||
libdivecomputer_la_SOURCES += usbhid.h usbhid.c
|
||||
libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c
|
||||
libdivecomputer_la_SOURCES += custom.h custom.c
|
||||
libdivecomputer_la_SOURCES += custom_io.c
|
||||
|
||||
if OS_WIN32
|
||||
libdivecomputer_la_SOURCES += libdivecomputer.rc
|
||||
|
||||
@ -20,6 +20,12 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#include <libdivecomputer/units.h>
|
||||
|
||||
@ -129,6 +135,9 @@ atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dateti
|
||||
}
|
||||
|
||||
|
||||
#define BUFLEN 16
|
||||
|
||||
|
||||
static dc_status_t
|
||||
atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
@ -143,6 +152,9 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
|
||||
dc_tank_t *tank = (dc_tank_t *) value;
|
||||
|
||||
double atmospheric = 0.0;
|
||||
char buf[BUFLEN];
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
if (parser->atmospheric)
|
||||
atmospheric = parser->atmospheric;
|
||||
else
|
||||
@ -208,6 +220,29 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch(flags) {
|
||||
case 0: // Serialnr
|
||||
string->desc = "Serial";
|
||||
snprintf(buf, BUFLEN, "%c%c%c%c-%c%c%c%c", p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11]);
|
||||
break;
|
||||
case 1: // Program Version
|
||||
string->desc = "Program Version";
|
||||
snprintf(buf, BUFLEN, "%.2f", array_uint16_le(p + 30) / 100.0);
|
||||
break;
|
||||
case 2: // Boot Version
|
||||
string->desc = "Boot Version";
|
||||
snprintf(buf, BUFLEN, "%.2f", array_uint16_le(p + 32) / 100.0);
|
||||
break;
|
||||
case 3: // Nofly
|
||||
string->desc = "NoFly Time";
|
||||
snprintf(buf, BUFLEN, "%0u:%02u", p[0x52], p[0x53]);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
string->value = strdup(buf);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#endif
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/custom_io.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -71,6 +72,12 @@ dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *
|
||||
dc_status_t
|
||||
dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *prefix, const unsigned char data[], unsigned int size);
|
||||
|
||||
dc_custom_io_t*
|
||||
_dc_context_custom_io (dc_context_t *context);
|
||||
|
||||
dc_status_t
|
||||
dc_custom_io_serial_open(dc_iostream_t **out, dc_context_t *context, const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#endif
|
||||
|
||||
#include "context-private.h"
|
||||
#include <libdivecomputer/custom_io.h>
|
||||
|
||||
struct dc_context_t {
|
||||
dc_loglevel_t loglevel;
|
||||
@ -45,6 +46,8 @@ struct dc_context_t {
|
||||
struct timeval timestamp;
|
||||
#endif
|
||||
#endif
|
||||
dc_custom_io_t *custom_io;
|
||||
dc_user_device_t *user_device;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
@ -195,6 +198,8 @@ dc_context_new (dc_context_t **out)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
context->custom_io = NULL;
|
||||
|
||||
*out = context;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -208,6 +213,24 @@ dc_context_free (dc_context_t *context)
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_context_set_custom_io (dc_context_t *context, dc_custom_io_t *custom_io, dc_user_device_t *user_device)
|
||||
{
|
||||
if (context == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
context->custom_io = custom_io;
|
||||
custom_io->user_device = user_device;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_custom_io_t*
|
||||
_dc_context_custom_io (dc_context_t *context)
|
||||
{
|
||||
return context->custom_io;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_context_set_loglevel (dc_context_t *context, dc_loglevel_t loglevel)
|
||||
{
|
||||
|
||||
231
src/custom_io.c
Normal file
231
src/custom_io.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2017 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
|
||||
#include "iostream-private.h"
|
||||
#include "common-private.h"
|
||||
#include "context-private.h"
|
||||
|
||||
/*
|
||||
* This is shamelessly stolen from src/custom.c, to make it
|
||||
* work with the subsurface custom_io model.
|
||||
*/
|
||||
typedef struct dc_custom_t {
|
||||
/* Base class. */
|
||||
dc_iostream_t base;
|
||||
/* Internal state. */
|
||||
dc_context_t *context;
|
||||
} dc_custom_t;
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_timeout (dc_iostream_t *abstract, int timeout)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_set_timeout)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_set_timeout(io, timeout);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value)
|
||||
{
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_set_halfduplex)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_set_halfduplex(io, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_break (dc_iostream_t *abstract, unsigned int value)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_set_break)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_set_break(io, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_dtr (dc_iostream_t *abstract, unsigned int value)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_set_dtr)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_set_dtr(io, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_set_rts (dc_iostream_t *abstract, unsigned int value)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_set_rts)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_set_rts(io, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_get_lines (dc_iostream_t *abstract, unsigned int *value)
|
||||
{
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_get_available (dc_iostream_t *abstract, size_t *value)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_get_available)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_get_available(io, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_configure)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_configure(io, baudrate, databits, parity, stopbits, flowcontrol);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_read)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_read(io, data, size, actual);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_write)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_write(io, data, size, actual);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_flush (dc_iostream_t *abstract)
|
||||
{
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_purge (dc_iostream_t *abstract, dc_direction_t direction)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_purge)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_purge(io, direction);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_sleep (dc_iostream_t *abstract, unsigned int milliseconds)
|
||||
{
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_custom_close (dc_iostream_t *abstract)
|
||||
{
|
||||
dc_custom_t *custom = (dc_custom_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(custom->context);
|
||||
|
||||
if (!io->serial_close)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
return io->serial_close(io);
|
||||
}
|
||||
|
||||
static const dc_iostream_vtable_t dc_custom_vtable = {
|
||||
sizeof(dc_custom_t),
|
||||
dc_custom_set_timeout, /* set_timeout */
|
||||
dc_custom_set_latency, /* set_latency */
|
||||
dc_custom_set_halfduplex, /* set_halfduplex */
|
||||
dc_custom_set_break, /* set_break */
|
||||
dc_custom_set_dtr, /* set_dtr */
|
||||
dc_custom_set_rts, /* set_rts */
|
||||
dc_custom_get_lines, /* get_lines */
|
||||
dc_custom_get_available, /* get_received */
|
||||
dc_custom_configure, /* configure */
|
||||
dc_custom_read, /* read */
|
||||
dc_custom_write, /* write */
|
||||
dc_custom_flush, /* flush */
|
||||
dc_custom_purge, /* purge */
|
||||
dc_custom_sleep, /* sleep */
|
||||
dc_custom_close, /* close */
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
dc_custom_io_serial_open(dc_iostream_t **out, dc_context_t *context, const char *name)
|
||||
{
|
||||
dc_custom_io_t *io = _dc_context_custom_io(context);
|
||||
dc_custom_t *custom;
|
||||
|
||||
custom = (dc_custom_t *) dc_iostream_allocate (context, &dc_custom_vtable);
|
||||
if (!custom) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
custom->context = context;
|
||||
*out = (dc_iostream_t *) custom;
|
||||
return io->serial_open(io, context, name);
|
||||
}
|
||||
300
src/descriptor.c
300
src/descriptor.c
@ -53,6 +53,7 @@ struct dc_descriptor_t {
|
||||
const char *product;
|
||||
dc_family_t type;
|
||||
unsigned int model;
|
||||
unsigned int serial;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -64,53 +65,53 @@ struct dc_descriptor_t {
|
||||
|
||||
static const dc_descriptor_t g_descriptors[] = {
|
||||
/* Suunto Solution */
|
||||
{"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0},
|
||||
{"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0}, // FTDI
|
||||
/* Suunto Eon */
|
||||
{"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0},
|
||||
{"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0},
|
||||
{"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0},
|
||||
{"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0}, // FTDI
|
||||
{"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0}, // FTDI
|
||||
{"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0}, // FTDI
|
||||
/* Suunto Vyper */
|
||||
{"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01},
|
||||
{"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03},
|
||||
{"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04},
|
||||
{"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05},
|
||||
{"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A},
|
||||
{"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B},
|
||||
{"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C},
|
||||
{"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D},
|
||||
{"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16},
|
||||
{"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01}, // FTDI
|
||||
{"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03}, // FTDI
|
||||
{"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04}, // FTDI
|
||||
{"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05}, // FTDI
|
||||
{"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A}, // FTDI
|
||||
{"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B}, // FTDI
|
||||
{"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C}, // FTDI
|
||||
{"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D}, // FTDI
|
||||
{"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16}, // FTDI
|
||||
/* Suunto Vyper 2 */
|
||||
{"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10},
|
||||
{"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11},
|
||||
{"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13},
|
||||
{"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14},
|
||||
{"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15},
|
||||
{"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10}, // FTDI
|
||||
{"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11}, // FTDI
|
||||
{"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13}, // FTDI
|
||||
{"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14}, // FTDI
|
||||
{"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15}, // FTDI
|
||||
/* Suunto D9 */
|
||||
{"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E},
|
||||
{"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F},
|
||||
{"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12},
|
||||
{"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19},
|
||||
{"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A},
|
||||
{"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B},
|
||||
{"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C},
|
||||
{"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D},
|
||||
{"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E},
|
||||
{"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20},
|
||||
{"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E}, // FTDI
|
||||
{"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F}, // FTDI
|
||||
{"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12}, // FTDI
|
||||
{"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19}, // FTDI
|
||||
{"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A}, // FTDI
|
||||
{"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B}, // FTDI
|
||||
{"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C}, // FTDI
|
||||
{"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, // FTDI
|
||||
{"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, // FTDI
|
||||
{"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20}, // FTDI
|
||||
/* Suunto EON Steel */
|
||||
#ifdef USBHID
|
||||
{"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0},
|
||||
{"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1},
|
||||
#if defined(USBHID) || defined(ENABLE_BLE)
|
||||
{"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, // BLE
|
||||
{"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, // BLE
|
||||
#endif
|
||||
/* Uwatec Aladin */
|
||||
{"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C},
|
||||
{"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E},
|
||||
{"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F},
|
||||
{"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44},
|
||||
{"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4},
|
||||
{"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4},
|
||||
{"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF},
|
||||
{"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, // FTDI
|
||||
{"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E}, // FTDI
|
||||
{"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F}, // FTDI
|
||||
{"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44}, // FTDI
|
||||
{"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4}, // FTDI
|
||||
{"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4}, // FTDI
|
||||
{"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF}, // FTDI
|
||||
/* Uwatec Memomouse */
|
||||
{"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0},
|
||||
{"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, // FTDI
|
||||
/* Uwatec Smart */
|
||||
#ifdef IRDA
|
||||
{"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10},
|
||||
@ -138,91 +139,91 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24},
|
||||
{"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26},
|
||||
/* Scubapro G2 */
|
||||
#ifdef USBHID
|
||||
{"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17},
|
||||
#if defined(USBHID) || defined(ENABLE_BLE)
|
||||
{"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17}, // BLE
|
||||
{"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22},
|
||||
{"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32},
|
||||
{"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},
|
||||
{"Reefnet", "Sensus Ultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3},
|
||||
/* Oceanic VT Pro */
|
||||
{"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151},
|
||||
{"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155},
|
||||
{"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158},
|
||||
{"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159},
|
||||
{"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244},
|
||||
{"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245},
|
||||
{"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246},
|
||||
{"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F},
|
||||
{"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151}, // FTDI
|
||||
{"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, // FTDI
|
||||
{"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, // FTDI
|
||||
{"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, // FTDI
|
||||
{"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244}, // FTDI
|
||||
{"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, // FTDI
|
||||
{"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246}, // FTDI
|
||||
{"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F}, // FTDI
|
||||
/* Oceanic Veo 250 */
|
||||
{"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247},
|
||||
{"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B},
|
||||
{"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C},
|
||||
{"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251},
|
||||
{"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252},
|
||||
{"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255},
|
||||
{"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A},
|
||||
{"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352},
|
||||
{"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247}, // FTDI
|
||||
{"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B}, // FTDI
|
||||
{"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, // FTDI
|
||||
{"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251}, // FTDI
|
||||
{"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252}, // FTDI
|
||||
{"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255}, // FTDI
|
||||
{"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A}, // FTDI
|
||||
{"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352}, // FTDI
|
||||
/* Oceanic Atom 2.0 */
|
||||
{"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250},
|
||||
{"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257},
|
||||
{"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258},
|
||||
{"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259},
|
||||
{"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342},
|
||||
{"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344},
|
||||
{"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345},
|
||||
{"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346},
|
||||
{"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347},
|
||||
{"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348},
|
||||
{"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D},
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E},
|
||||
{"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350},
|
||||
{"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353},
|
||||
{"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354},
|
||||
{"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357},
|
||||
{"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358},
|
||||
{"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359},
|
||||
{"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A},
|
||||
{"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441},
|
||||
{"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442},
|
||||
{"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443},
|
||||
{"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444},
|
||||
{"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446},
|
||||
{"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447},
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449},
|
||||
{"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B},
|
||||
{"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C},
|
||||
{"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D},
|
||||
{"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450},
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451},
|
||||
{"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452},
|
||||
{"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453},
|
||||
{"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455},
|
||||
{"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456},
|
||||
{"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457},
|
||||
{"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458},
|
||||
{"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A},
|
||||
{"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542},
|
||||
{"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543},
|
||||
{"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545},
|
||||
{"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546},
|
||||
{"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548},
|
||||
{"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549},
|
||||
{"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B},
|
||||
{"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C},
|
||||
{"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550},
|
||||
{"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553},
|
||||
{"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554},
|
||||
{"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555},
|
||||
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556},
|
||||
{"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557},
|
||||
{"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559},
|
||||
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A},
|
||||
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641},
|
||||
{"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642},
|
||||
{"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646},
|
||||
{"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250}, // FTDI
|
||||
{"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257}, // FTDI
|
||||
{"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258}, // FTDI
|
||||
{"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259}, // FTDI
|
||||
{"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, // FTDI
|
||||
{"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344}, // FTDI
|
||||
{"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345}, // FTDI
|
||||
{"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346}, // FTDI
|
||||
{"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347}, // FTDI
|
||||
{"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348}, // FTDI
|
||||
{"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D}, // FTDI
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E}, // FTDI
|
||||
{"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350}, // FTDI
|
||||
{"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353}, // FTDI
|
||||
{"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354}, // FTDI
|
||||
{"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357}, // FTDI
|
||||
{"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358}, // FTDI
|
||||
{"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359}, // FTDI
|
||||
{"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A}, // FTDI
|
||||
{"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441}, // FTDI
|
||||
{"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442}, // FTDI
|
||||
{"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443}, // FTDI
|
||||
{"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444}, // FTDI
|
||||
{"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446}, // FTDI
|
||||
{"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447}, // FTDI
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449}, // FTDI
|
||||
{"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B}, // FTDI
|
||||
{"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C}, // FTDI
|
||||
{"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D}, // FTDI
|
||||
{"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450}, // FTDI
|
||||
{"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451}, // FTDI
|
||||
{"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452}, // FTDI
|
||||
{"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453}, // FTDI
|
||||
{"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455}, // FTDI
|
||||
{"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456}, // FTDI
|
||||
{"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457}, // FTDI
|
||||
{"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458}, // FTDI
|
||||
{"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A}, // FTDI
|
||||
{"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542}, // FTDI
|
||||
{"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543}, // FTDI
|
||||
{"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545}, // FTDI
|
||||
{"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546}, // FTDI
|
||||
{"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548}, // FTDI
|
||||
{"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549}, // FTDI
|
||||
{"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B}, // FTDI
|
||||
{"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C}, // FTDI
|
||||
{"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550}, // FTDI
|
||||
{"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553}, // FTDI
|
||||
{"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554}, // FTDI
|
||||
{"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555}, // FTDI
|
||||
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556}, // FTDI
|
||||
{"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557}, // FTDI
|
||||
{"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, // FTDI
|
||||
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, // FTDI
|
||||
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, // FTDI
|
||||
{"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, // FTDI
|
||||
{"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646}, // FTDI
|
||||
/* Mares Nemo */
|
||||
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0},
|
||||
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0},
|
||||
@ -251,22 +252,22 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F},
|
||||
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29},
|
||||
/* Heinrichs Weikamp */
|
||||
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0},
|
||||
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1},
|
||||
{"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2},
|
||||
{"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3},
|
||||
{"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0},
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11},
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13},
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B},
|
||||
{"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A},
|
||||
{"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13},
|
||||
{"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A},
|
||||
{"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B},
|
||||
{"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05},
|
||||
{"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07},
|
||||
{"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12},
|
||||
{"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13},
|
||||
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3}, // FTDI
|
||||
{"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT
|
||||
{"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A}, // FTDI // BT
|
||||
{"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B}, // BT
|
||||
{"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07}, // FTDI
|
||||
{"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12}, // FTDI // BT
|
||||
{"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT
|
||||
/* Cressi Edy */
|
||||
{"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05},
|
||||
{"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08},
|
||||
@ -286,14 +287,14 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2},
|
||||
#endif
|
||||
/* Shearwater Predator */
|
||||
{"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2},
|
||||
/* Shearwater Petrel */
|
||||
{"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3},
|
||||
{"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3},
|
||||
{"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4},
|
||||
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 4},
|
||||
{"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5},
|
||||
{"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6},
|
||||
{"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, // BT
|
||||
/* Shearwater Petrel family */
|
||||
{"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE
|
||||
{"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE
|
||||
{"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, // BT
|
||||
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 4}, // BLE
|
||||
{"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, // BT // BLE
|
||||
{"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, // BLE
|
||||
/* Dive Rite NiTek Q */
|
||||
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0},
|
||||
/* Citizen Hyper Aqualand */
|
||||
@ -321,12 +322,12 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Ratio", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44},
|
||||
{"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45},
|
||||
/* Cochran Commander */
|
||||
{"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||
{"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1},
|
||||
{"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2},
|
||||
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3},
|
||||
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4},
|
||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5},
|
||||
{"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0}, // FTDI
|
||||
{"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, // FTDI
|
||||
{"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, // FTDI
|
||||
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, // FTDI
|
||||
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, // FTDI
|
||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, // FTDI
|
||||
};
|
||||
|
||||
typedef struct dc_descriptor_iterator_t {
|
||||
@ -433,6 +434,15 @@ dc_descriptor_get_model (dc_descriptor_t *descriptor)
|
||||
return descriptor->model;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
dc_descriptor_get_serial (dc_descriptor_t *descriptor)
|
||||
{
|
||||
if (descriptor == NULL)
|
||||
return 0;
|
||||
|
||||
return descriptor->serial;
|
||||
}
|
||||
|
||||
dc_transport_t
|
||||
dc_descriptor_get_transport (dc_descriptor_t *descriptor)
|
||||
{
|
||||
|
||||
@ -33,7 +33,6 @@
|
||||
#include "reefnet_sensuspro.h"
|
||||
#include "reefnet_sensusultra.h"
|
||||
#include "uwatec_aladin.h"
|
||||
#include "uwatec_g2.h"
|
||||
#include "uwatec_memomouse.h"
|
||||
#include "uwatec_meridian.h"
|
||||
#include "uwatec_smart.h"
|
||||
@ -51,6 +50,7 @@
|
||||
#include "cressi_leonardo.h"
|
||||
#include "zeagle_n2ition3.h"
|
||||
#include "atomics_cobalt.h"
|
||||
#include "scubapro_g2.h"
|
||||
#include "shearwater_petrel.h"
|
||||
#include "shearwater_predator.h"
|
||||
#include "diverite_nitekq.h"
|
||||
@ -125,7 +125,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
rc = suunto_d9_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
|
||||
break;
|
||||
case DC_FAMILY_SUUNTO_EONSTEEL:
|
||||
rc = suunto_eonsteel_device_open (&device, context, dc_descriptor_get_model (descriptor));
|
||||
rc = suunto_eonsteel_device_open (&device, context, name, dc_descriptor_get_model(descriptor));
|
||||
break;
|
||||
case DC_FAMILY_UWATEC_ALADIN:
|
||||
rc = uwatec_aladin_device_open (&device, context, name);
|
||||
@ -140,7 +140,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
rc = uwatec_meridian_device_open (&device, context, name);
|
||||
break;
|
||||
case DC_FAMILY_UWATEC_G2:
|
||||
rc = uwatec_g2_device_open (&device, context, dc_descriptor_get_model (descriptor));
|
||||
rc = scubapro_g2_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
|
||||
break;
|
||||
case DC_FAMILY_REEFNET_SENSUS:
|
||||
rc = reefnet_sensus_device_open (&device, context, name);
|
||||
|
||||
@ -35,7 +35,7 @@ dc_status_t
|
||||
hw_ostc_device_open (dc_device_t **device, dc_context_t *context, const char *name);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int hwos);
|
||||
hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int serial, unsigned int hwos);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ dc_status_t
|
||||
hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, const char *name);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model);
|
||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -20,6 +20,12 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#include "libdivecomputer/units.h"
|
||||
|
||||
@ -61,8 +67,14 @@
|
||||
#define OSTC3_APNEA 3
|
||||
#define OSTC3_PSCR 4
|
||||
|
||||
#define OSTC3_ZHL16 0
|
||||
#define OSTC3_ZHL16_GF 1
|
||||
#define OSTC4_VPM 2
|
||||
|
||||
#define OSTC4 0x3B
|
||||
|
||||
#define UNSUPPORTED 0xFFFFFFFF
|
||||
|
||||
typedef struct hw_ostc_sample_info_t {
|
||||
unsigned int type;
|
||||
unsigned int divisor;
|
||||
@ -78,7 +90,13 @@ typedef struct hw_ostc_layout_t {
|
||||
unsigned int salinity;
|
||||
unsigned int duration;
|
||||
unsigned int temperature;
|
||||
unsigned int battery;
|
||||
unsigned int desat;
|
||||
unsigned int firmware;
|
||||
unsigned int deco_info1;
|
||||
unsigned int deco_info2;
|
||||
unsigned int decomode;
|
||||
unsigned int battery_percentage;
|
||||
} hw_ostc_layout_t;
|
||||
|
||||
typedef struct hw_ostc_gasmix_t {
|
||||
@ -90,6 +108,7 @@ typedef struct hw_ostc_parser_t {
|
||||
dc_parser_t base;
|
||||
unsigned int hwos;
|
||||
unsigned int model;
|
||||
unsigned int serial;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
unsigned int version;
|
||||
@ -127,7 +146,13 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc = {
|
||||
43, /* salinity */
|
||||
47, /* duration */
|
||||
13, /* temperature */
|
||||
34, /* battery volt after dive */
|
||||
17, /* desat */
|
||||
32, /* firmware */
|
||||
49, /* deco_info1 */
|
||||
50, /* deco_info1 */
|
||||
51, /* decomode */
|
||||
0, /* battery percentage TBD */
|
||||
};
|
||||
|
||||
static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
||||
@ -139,7 +164,13 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
||||
43, /* salinity */
|
||||
47, /* duration */
|
||||
19, /* temperature */
|
||||
34, /* battery volt after dive */
|
||||
23, /* desat */
|
||||
32, /* firmware */
|
||||
49, /* deco_info1 */
|
||||
50, /* deco_info2 */
|
||||
51, /* decomode */
|
||||
0, /* battery percentage TBD */
|
||||
};
|
||||
|
||||
static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
||||
@ -151,7 +182,13 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
||||
70, /* salinity */
|
||||
75, /* duration */
|
||||
22, /* temperature */
|
||||
50, /* battery volt after dive */
|
||||
26, /* desat */
|
||||
48, /* firmware */
|
||||
77, /* deco_info1 */
|
||||
78, /* deco_info2 */
|
||||
79, /* decomode */
|
||||
59, /* battery percentage */
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
@ -294,7 +331,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int hwos, unsigned int model)
|
||||
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int hwos, unsigned int model)
|
||||
{
|
||||
hw_ostc_parser_t *parser = NULL;
|
||||
|
||||
@ -324,6 +361,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
}
|
||||
parser->serial = serial;
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
@ -332,15 +370,15 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
|
||||
|
||||
|
||||
dc_status_t
|
||||
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int hwos)
|
||||
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int hwos)
|
||||
{
|
||||
return hw_ostc_parser_create_internal (out, context, hwos, 0);
|
||||
return hw_ostc_parser_create_internal (out, context, serial, hwos, 0);
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int model)
|
||||
{
|
||||
return hw_ostc_parser_create_internal (out, context, 1, model);
|
||||
return hw_ostc_parser_create_internal (out, context, serial, 1, model);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -428,6 +466,8 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define BUFLEN 32
|
||||
|
||||
static dc_status_t
|
||||
hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
@ -452,9 +492,12 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
unsigned int salinity = data[layout->salinity];
|
||||
if (version == 0x23 || version == 0x24)
|
||||
salinity += 100;
|
||||
char buf[BUFLEN];
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -550,6 +593,79 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch(flags) {
|
||||
case 0: /* serial */
|
||||
string->desc = "Serial";
|
||||
snprintf(buf, BUFLEN, "%u", parser->serial);
|
||||
break;
|
||||
case 1: /* battery */
|
||||
string->desc = "Battery at end";
|
||||
unsigned int percentage = (unsigned int) data[layout->battery_percentage];
|
||||
if (percentage != 0xFF && (version == 0x23 || version == 0x24)) {
|
||||
percentage = percentage>100? 100: percentage;
|
||||
snprintf(buf, BUFLEN, "%.2fV, %u%% remaining",
|
||||
array_uint16_le (data + layout->battery) / 1000.0,
|
||||
percentage);
|
||||
} else {
|
||||
snprintf(buf, BUFLEN, "%.2fV", array_uint16_le (data + layout->battery) / 1000.0);
|
||||
}
|
||||
break;
|
||||
case 2: /* desat */
|
||||
string->desc = "Desat time";
|
||||
snprintf(buf, BUFLEN, "%0u:%02u", array_uint16_le (data + layout->desat) / 60,
|
||||
array_uint16_le (data + layout->desat) % 60);
|
||||
break;
|
||||
case 3: /* firmware */
|
||||
string->desc = "FW Version";
|
||||
/* OSTC4 stores firmware as XXXX XYYY YYZZ ZZZB, -> X.Y.Z beta? */
|
||||
if (parser->model == OSTC4) {
|
||||
int firmwareOnDevice = array_uint16_le (data + layout->firmware);
|
||||
unsigned char X = 0, Y = 0, Z = 0, beta = 0;
|
||||
X = (firmwareOnDevice & 0xF800) >> 11;
|
||||
Y = (firmwareOnDevice & 0x07C0) >> 6;
|
||||
Z = (firmwareOnDevice & 0x003E) >> 1;
|
||||
beta = firmwareOnDevice & 0x0001;
|
||||
|
||||
snprintf(buf, BUFLEN, "%u.%u.%u%s\n", X, Y, Z, beta? "beta": "");
|
||||
} else {
|
||||
snprintf(buf, BUFLEN, "%0u.%02u", data[layout->firmware], data[layout->firmware + 1]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: /* Deco model */
|
||||
string->desc = "Deco model";
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16) ||
|
||||
(version == 0x22 && data[layout->decomode] == FROG_ZHL16) ||
|
||||
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC)))
|
||||
strncpy(buf, "ZH-L16", BUFLEN);
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16_GF) ||
|
||||
(version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) ||
|
||||
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF)))
|
||||
strncpy(buf, "ZH-L16-GF", BUFLEN);
|
||||
else if (((version == 0x24) && data[layout->decomode] == OSTC4_VPM))
|
||||
strncpy(buf, "VPM", BUFLEN);
|
||||
else
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
break;
|
||||
case 5: /* Deco model info */
|
||||
string->desc = "Deco model info";
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16) ||
|
||||
(version == 0x22 && data[layout->decomode] == FROG_ZHL16) ||
|
||||
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC)))
|
||||
snprintf(buf, BUFLEN, "Saturation %u, Desaturation %u", layout->deco_info1, layout->deco_info2);
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16_GF) ||
|
||||
(version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) ||
|
||||
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF)))
|
||||
snprintf(buf, BUFLEN, "GF %u/%u", data[layout->deco_info1], data[layout->deco_info2]);
|
||||
else
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
string->value = strdup(buf);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ dc_context_new
|
||||
dc_context_free
|
||||
dc_context_set_loglevel
|
||||
dc_context_set_logfunc
|
||||
dc_context_set_custom_io
|
||||
|
||||
dc_iterator_next
|
||||
dc_iterator_free
|
||||
|
||||
@ -36,7 +36,7 @@ dc_status_t
|
||||
oceanic_atom2_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
|
||||
|
||||
dc_status_t
|
||||
oceanic_atom2_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
||||
oceanic_atom2_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libdivecomputer/units.h>
|
||||
|
||||
@ -102,6 +104,7 @@ struct oceanic_atom2_parser_t {
|
||||
unsigned int model;
|
||||
unsigned int headersize;
|
||||
unsigned int footersize;
|
||||
unsigned int serial;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
unsigned int header;
|
||||
@ -131,7 +134,7 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = {
|
||||
|
||||
|
||||
dc_status_t
|
||||
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
||||
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||
{
|
||||
oceanic_atom2_parser_t *parser = NULL;
|
||||
|
||||
@ -176,6 +179,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
|
||||
parser->headersize = 5 * PAGESIZE;
|
||||
}
|
||||
|
||||
parser->serial = serial;
|
||||
parser->cached = 0;
|
||||
parser->header = 0;
|
||||
parser->footer = 0;
|
||||
@ -366,6 +370,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define BUF_LEN 16
|
||||
|
||||
static dc_status_t
|
||||
oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
|
||||
@ -502,6 +507,9 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
char buf[BUF_LEN];
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -557,6 +565,17 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch(flags) {
|
||||
case 0: /* Serial */
|
||||
string->desc = "Serial";
|
||||
snprintf(buf, BUF_LEN, "%06u", parser->serial);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
string->value = strdup(buf);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
23
src/parser.c
23
src/parser.c
@ -51,7 +51,6 @@
|
||||
#include "zeagle_n2ition3.h"
|
||||
#include "atomics_cobalt.h"
|
||||
#include "shearwater_petrel.h"
|
||||
#include "shearwater_predator.h"
|
||||
#include "diverite_nitekq.h"
|
||||
#include "citizen_aqualand.h"
|
||||
#include "divesystem_idive.h"
|
||||
@ -64,7 +63,7 @@
|
||||
#define REACTPROWHITE 0x4354
|
||||
|
||||
static dc_status_t
|
||||
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t family, unsigned int model, unsigned int devtime, dc_ticks_t systime)
|
||||
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t family, unsigned int model, unsigned int serial, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_parser_t *parser = NULL;
|
||||
@ -87,7 +86,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
break;
|
||||
case DC_FAMILY_SUUNTO_VYPER2:
|
||||
case DC_FAMILY_SUUNTO_D9:
|
||||
rc = suunto_d9_parser_create (&parser, context, model);
|
||||
rc = suunto_d9_parser_create (&parser, context, model, serial);
|
||||
break;
|
||||
case DC_FAMILY_SUUNTO_EONSTEEL:
|
||||
rc = suunto_eonsteel_parser_create(&parser, context, model);
|
||||
@ -120,7 +119,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
if (model == REACTPROWHITE)
|
||||
rc = oceanic_veo250_parser_create (&parser, context, model);
|
||||
else
|
||||
rc = oceanic_atom2_parser_create (&parser, context, model);
|
||||
rc = oceanic_atom2_parser_create (&parser, context, model, serial);
|
||||
break;
|
||||
case DC_FAMILY_MARES_NEMO:
|
||||
case DC_FAMILY_MARES_PUCK:
|
||||
@ -133,11 +132,11 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
rc = mares_iconhd_parser_create (&parser, context, model);
|
||||
break;
|
||||
case DC_FAMILY_HW_OSTC:
|
||||
rc = hw_ostc_parser_create (&parser, context, 0);
|
||||
rc = hw_ostc_parser_create (&parser, context, serial, 0);
|
||||
break;
|
||||
case DC_FAMILY_HW_FROG:
|
||||
case DC_FAMILY_HW_OSTC3:
|
||||
rc = hw_ostc3_parser_create (&parser, context, model);
|
||||
rc = hw_ostc3_parser_create (&parser, context, serial, model);
|
||||
break;
|
||||
case DC_FAMILY_CRESSI_EDY:
|
||||
case DC_FAMILY_ZEAGLE_N2ITION3:
|
||||
@ -150,10 +149,8 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
rc = atomics_cobalt_parser_create (&parser, context);
|
||||
break;
|
||||
case DC_FAMILY_SHEARWATER_PREDATOR:
|
||||
rc = shearwater_predator_parser_create (&parser, context, model);
|
||||
break;
|
||||
case DC_FAMILY_SHEARWATER_PETREL:
|
||||
rc = shearwater_petrel_parser_create (&parser, context, model);
|
||||
rc = shearwater_common_parser_create (&parser, context, model, serial);
|
||||
break;
|
||||
case DC_FAMILY_DIVERITE_NITEKQ:
|
||||
rc = diverite_nitekq_parser_create (&parser, context);
|
||||
@ -183,7 +180,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
return dc_parser_new_internal (out, device->context,
|
||||
dc_device_get_type (device), device->devinfo.model,
|
||||
dc_device_get_type (device),
|
||||
device->devinfo.model,
|
||||
device->devinfo.serial,
|
||||
device->clock.devtime, device->clock.systime);
|
||||
}
|
||||
|
||||
@ -191,7 +190,9 @@ dc_status_t
|
||||
dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
return dc_parser_new_internal (out, context,
|
||||
dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor),
|
||||
dc_descriptor_get_type (descriptor),
|
||||
dc_descriptor_get_model (descriptor),
|
||||
dc_descriptor_get_serial (descriptor),
|
||||
devtime, systime);
|
||||
}
|
||||
|
||||
|
||||
@ -23,119 +23,129 @@
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <string.h> // strncmp, strstr
|
||||
|
||||
#include "uwatec_g2.h"
|
||||
#include "scubapro_g2.h"
|
||||
#include "context-private.h"
|
||||
#include "device-private.h"
|
||||
#include "usbhid.h"
|
||||
#include "array.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_g2_device_vtable)
|
||||
#define ISINSTANCE(device) dc_device_isinstance((device), &scubapro_g2_device_vtable)
|
||||
|
||||
#define RX_PACKET_SIZE 64
|
||||
#define TX_PACKET_SIZE 32
|
||||
|
||||
#define ALADINSPORTMATRIX 0x17
|
||||
#define ALADINSQUARE 0x22
|
||||
#define G2 0x32
|
||||
|
||||
typedef struct uwatec_g2_device_t {
|
||||
typedef struct scubapro_g2_device_t {
|
||||
dc_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned int timestamp;
|
||||
unsigned int devtime;
|
||||
dc_ticks_t systime;
|
||||
} uwatec_g2_device_t;
|
||||
} scubapro_g2_device_t;
|
||||
|
||||
static dc_status_t uwatec_g2_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
|
||||
static dc_status_t uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t uwatec_g2_device_close (dc_device_t *abstract);
|
||||
static dc_status_t scubapro_g2_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t scubapro_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
|
||||
static dc_status_t scubapro_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t scubapro_g2_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t uwatec_g2_device_vtable = {
|
||||
sizeof(uwatec_g2_device_t),
|
||||
static const dc_device_vtable_t scubapro_g2_device_vtable = {
|
||||
sizeof(scubapro_g2_device_t),
|
||||
DC_FAMILY_UWATEC_G2,
|
||||
uwatec_g2_device_set_fingerprint, /* set_fingerprint */
|
||||
scubapro_g2_device_set_fingerprint, /* set_fingerprint */
|
||||
NULL, /* read */
|
||||
NULL, /* write */
|
||||
uwatec_g2_device_dump, /* dump */
|
||||
uwatec_g2_device_foreach, /* foreach */
|
||||
scubapro_g2_device_dump, /* dump */
|
||||
scubapro_g2_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
uwatec_g2_device_close /* close */
|
||||
scubapro_g2_device_close /* close */
|
||||
};
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
|
||||
scubapro_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
|
||||
|
||||
static dc_status_t
|
||||
receive_data (uwatec_g2_device_t *device, dc_event_progress_t *progress, unsigned char *data, unsigned int size)
|
||||
static int receive_data(scubapro_g2_device_t *g2, unsigned char *buffer, int size, dc_event_progress_t *progress)
|
||||
{
|
||||
dc_custom_io_t *io = _dc_context_custom_io(g2->base.context);
|
||||
while (size) {
|
||||
unsigned char buf[RX_PACKET_SIZE];
|
||||
unsigned char buf[RX_PACKET_SIZE] = { 0 };
|
||||
size_t transferred = 0;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned int len = 0;
|
||||
dc_status_t rc;
|
||||
int len;
|
||||
|
||||
rc = dc_iostream_read (device->iostream, buf, sizeof(buf), &transferred);
|
||||
rc = io->packet_read(io, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (device->base.context, "read interrupt transfer failed");
|
||||
return rc;
|
||||
ERROR(g2->base.context, "read interrupt transfer failed");
|
||||
return -1;
|
||||
}
|
||||
if (transferred != sizeof(buf)) {
|
||||
ERROR (device->base.context, "incomplete read interrupt transfer (got " DC_PRINTF_SIZE ", expected " DC_PRINTF_SIZE ")", transferred, sizeof(buf));
|
||||
return DC_STATUS_PROTOCOL;
|
||||
if (transferred < 1) {
|
||||
ERROR(g2->base.context, "incomplete read interrupt transfer (got empty packet)");
|
||||
return -1;
|
||||
}
|
||||
len = buf[0];
|
||||
if (transferred < len + 1) {
|
||||
ERROR(g2->base.context, "small packet read (got %zu, expected at least %d)", transferred, len + 1);
|
||||
return -1;
|
||||
}
|
||||
if (len >= sizeof(buf)) {
|
||||
ERROR (device->base.context, "read interrupt transfer returns impossible packet size (%d)", len);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
ERROR(g2->base.context, "read interrupt transfer returns impossible packet size (%d)", len);
|
||||
return -1;
|
||||
}
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len);
|
||||
HEXDUMP (g2->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf+1, len);
|
||||
if (len > size) {
|
||||
ERROR (device->base.context, "receive result buffer too small");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
ERROR(g2->base.context, "receive result buffer too small - truncating");
|
||||
len = size;
|
||||
}
|
||||
memcpy(buffer, buf+1, len);
|
||||
size -= len;
|
||||
buffer += len;
|
||||
|
||||
// Update and emit a progress event.
|
||||
// Update and emit a progress event?
|
||||
if (progress) {
|
||||
progress->current += len;
|
||||
device_event_emit (&device->base, DC_EVENT_PROGRESS, progress);
|
||||
device_event_emit(&g2->base, DC_EVENT_PROGRESS, progress);
|
||||
}
|
||||
|
||||
memcpy(data, buf + 1, len);
|
||||
size -= len;
|
||||
data += len;
|
||||
}
|
||||
return DC_STATUS_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
|
||||
scubapro_g2_transfer(scubapro_g2_device_t *g2, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
|
||||
{
|
||||
unsigned char buf[TX_PACKET_SIZE + 1];
|
||||
dc_custom_io_t *io = _dc_context_custom_io(g2->base.context);
|
||||
unsigned char buf[TX_PACKET_SIZE+1] = { 0 }; // the +1 is for the report type byte
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t transferred = 0;
|
||||
|
||||
if (csize + 2 > sizeof(buf)) {
|
||||
ERROR (device->base.context, "command too big (%d)", csize);
|
||||
if (csize > sizeof(buf)-2) {
|
||||
ERROR(g2->base.context, "command too big (%d)", csize);
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", command, csize);
|
||||
HEXDUMP (g2->base.context, DC_LOGLEVEL_DEBUG, "cmd", command, csize);
|
||||
|
||||
buf[0] = 0; // USBHID report type
|
||||
buf[1] = csize; // command size
|
||||
memcpy(buf+2, command, csize); // command bytes
|
||||
|
||||
// BLE GATT protocol?
|
||||
if (io->packet_size < 64) {
|
||||
// No report type byte
|
||||
status = io->packet_write(io, buf+1, csize+1, &transferred);
|
||||
} else {
|
||||
status = io->packet_write(io, buf, sizeof(buf), &transferred);
|
||||
}
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = csize;
|
||||
memcpy(buf + 2, command, csize);
|
||||
memset(buf + 2 + csize, 0, sizeof(buf) - (csize + 2));
|
||||
status = dc_iostream_write (device->iostream, buf, sizeof(buf), &transferred);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (device->base.context, "Failed to send the command.");
|
||||
ERROR(g2->base.context, "Failed to send the command.");
|
||||
return status;
|
||||
}
|
||||
|
||||
status = receive_data (device, NULL, answer, asize);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (device->base.context, "Failed to receive the answer.");
|
||||
return status;
|
||||
if (receive_data(g2, answer, asize, NULL) < 0) {
|
||||
ERROR(g2->base.context, "Failed to receive the answer.");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -143,7 +153,7 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_handshake (uwatec_g2_device_t *device)
|
||||
scubapro_g2_handshake (scubapro_g2_device_t *device, unsigned int model)
|
||||
{
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
|
||||
@ -151,9 +161,14 @@ uwatec_g2_handshake (uwatec_g2_device_t *device)
|
||||
unsigned char answer[1] = {0};
|
||||
unsigned char command[5] = {0x00, 0x10, 0x27, 0, 0};
|
||||
|
||||
// The vendor software does not do a handshake for the Aladin Sport Matrix,
|
||||
// so let's not do any either.
|
||||
if (model == ALADINSPORTMATRIX)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
// Handshake (stage 1).
|
||||
command[0] = 0x1B;
|
||||
dc_status_t rc = uwatec_g2_transfer (device, command, 1, answer, 1);
|
||||
dc_status_t rc = scubapro_g2_transfer (device, command, 1, answer, 1);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -165,7 +180,7 @@ uwatec_g2_handshake (uwatec_g2_device_t *device)
|
||||
|
||||
// Handshake (stage 2).
|
||||
command[0] = 0x1C;
|
||||
rc = uwatec_g2_transfer (device, command, 5, answer, 1);
|
||||
rc = scubapro_g2_transfer (device, command, 5, answer, 1);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -178,46 +193,70 @@ uwatec_g2_handshake (uwatec_g2_device_t *device)
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
struct usb_id {
|
||||
unsigned int model;
|
||||
unsigned short vendor, device;
|
||||
};
|
||||
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||
|
||||
static const struct usb_id *get_usb_id(unsigned int model)
|
||||
{
|
||||
int i;
|
||||
static const struct usb_id model_to_usb[] = {
|
||||
{ G2, 0x2e6c, 0x3201 }, // Scubapro G2
|
||||
{ ALADINSQUARE, 0xc251, 0x2006 }, // Scubapro Aladin Square
|
||||
};
|
||||
|
||||
for (i = 0; i < C_ARRAY_SIZE(model_to_usb); i++) {
|
||||
const struct usb_id *id = model_to_usb+i;
|
||||
|
||||
if (id->model == model)
|
||||
return id;
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
uwatec_g2_device_open (dc_device_t **out, dc_context_t *context, unsigned int model)
|
||||
scubapro_g2_device_open(dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
uwatec_g2_device_t *device = NULL;
|
||||
scubapro_g2_device_t *device = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
device = (uwatec_g2_device_t *) dc_device_allocate (context, &uwatec_g2_device_vtable);
|
||||
device = (scubapro_g2_device_t *) dc_device_allocate (context, &scubapro_g2_device_vtable);
|
||||
if (device == NULL) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
ERROR(context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
|
||||
// Set the default values.
|
||||
device->iostream = NULL;
|
||||
device->timestamp = 0;
|
||||
device->systime = (dc_ticks_t) -1;
|
||||
device->devtime = 0;
|
||||
|
||||
// Open the irda socket.
|
||||
unsigned int vid = 0, pid = 0;
|
||||
if (model == ALADINSQUARE) {
|
||||
vid = 0xc251;
|
||||
pid = 0x2006;
|
||||
} else {
|
||||
vid = 0x2e6c;
|
||||
pid = 0x3201;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(context);
|
||||
if (io && io->packet_open)
|
||||
status = io->packet_open(io, context, name);
|
||||
else {
|
||||
const struct usb_id *id = get_usb_id(model);
|
||||
if (!id) {
|
||||
ERROR(context, "Unknown USB ID for Scubapro model %#04x", model);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
status = dc_usbhid_open (&device->iostream, context, vid, pid);
|
||||
status = dc_usbhid_custom_io(context, id->vendor, id->device);
|
||||
}
|
||||
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to open USB device");
|
||||
ERROR (context, "Failed to open Scubapro G2 device");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
// Perform the handshaking.
|
||||
status = uwatec_g2_handshake (device);
|
||||
status = scubapro_g2_handshake(device, model);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to handshake with the device.");
|
||||
goto error_close;
|
||||
@ -228,7 +267,7 @@ uwatec_g2_device_open (dc_device_t **out, dc_context_t *context, unsigned int mo
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_close:
|
||||
dc_iostream_close (device->iostream);
|
||||
scubapro_g2_device_close((dc_device_t *) device);
|
||||
error_free:
|
||||
dc_device_deallocate ((dc_device_t *) device);
|
||||
return status;
|
||||
@ -236,26 +275,18 @@ error_free:
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_device_close (dc_device_t *abstract)
|
||||
scubapro_g2_device_close (dc_device_t *abstract)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(abstract->context);
|
||||
|
||||
// Close the device.
|
||||
rc = dc_iostream_close (device->iostream);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
dc_status_set_error(&status, rc);
|
||||
}
|
||||
|
||||
return status;
|
||||
return io->packet_close(io);
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||
scubapro_g2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||
{
|
||||
uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract;
|
||||
scubapro_g2_device_t *device = (scubapro_g2_device_t*) abstract;
|
||||
|
||||
if (size && size != 4)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
@ -270,9 +301,9 @@ uwatec_g2_device_set_fingerprint (dc_device_t *abstract, const unsigned char dat
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
scubapro_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
{
|
||||
uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract;
|
||||
scubapro_g2_device_t *device = (scubapro_g2_device_t*) abstract;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
|
||||
// Erase the current contents of the buffer.
|
||||
@ -288,21 +319,21 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
// Read the model number.
|
||||
unsigned char cmd_model[1] = {0x10};
|
||||
unsigned char model[1] = {0};
|
||||
rc = uwatec_g2_transfer (device, cmd_model, sizeof (cmd_model), model, sizeof (model));
|
||||
rc = scubapro_g2_transfer (device, cmd_model, sizeof (cmd_model), model, sizeof (model));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the serial number.
|
||||
unsigned char cmd_serial[1] = {0x14};
|
||||
unsigned char serial[4] = {0};
|
||||
rc = uwatec_g2_transfer (device, cmd_serial, sizeof (cmd_serial), serial, sizeof (serial));
|
||||
rc = scubapro_g2_transfer (device, cmd_serial, sizeof (cmd_serial), serial, sizeof (serial));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the device clock.
|
||||
unsigned char cmd_devtime[1] = {0x1A};
|
||||
unsigned char devtime[4] = {0};
|
||||
rc = uwatec_g2_transfer (device, cmd_devtime, sizeof (cmd_devtime), devtime, sizeof (devtime));
|
||||
rc = scubapro_g2_transfer (device, cmd_devtime, sizeof (cmd_devtime), devtime, sizeof (devtime));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -341,7 +372,7 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
// Data Length.
|
||||
command[0] = 0xC6;
|
||||
unsigned char answer[4] = {0};
|
||||
rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer));
|
||||
rc = scubapro_g2_transfer (device, command, sizeof (command), answer, sizeof (answer));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -365,7 +396,7 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
|
||||
// Data.
|
||||
command[0] = 0xC4;
|
||||
rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer));
|
||||
rc = scubapro_g2_transfer (device, command, sizeof (command), answer, sizeof (answer));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -380,10 +411,9 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
rc = receive_data (device, &progress, data, length);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return rc;
|
||||
if (receive_data(device, data, length, &progress)) {
|
||||
ERROR (abstract->context, "Received an unexpected size.");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -391,19 +421,19 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
scubapro_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
dc_buffer_t *buffer = dc_buffer_new (0);
|
||||
if (buffer == NULL)
|
||||
return DC_STATUS_NOMEMORY;
|
||||
|
||||
dc_status_t rc = uwatec_g2_device_dump (abstract, buffer);
|
||||
dc_status_t rc = scubapro_g2_device_dump (abstract, buffer);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
dc_buffer_free (buffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = uwatec_g2_extract_dives (abstract,
|
||||
rc = scubapro_g2_extract_dives (abstract,
|
||||
dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata);
|
||||
|
||||
dc_buffer_free (buffer);
|
||||
@ -413,7 +443,7 @@ uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, vo
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_g2_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
|
||||
scubapro_g2_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
if (abstract && !ISINSTANCE (abstract))
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
@ -19,8 +19,8 @@
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef UWATEC_G2_H
|
||||
#define UWATEC_G2_H
|
||||
#ifndef SCUBAPRO_G2_H
|
||||
#define SCUBAPRO_G2_H
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/device.h>
|
||||
@ -31,9 +31,9 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
dc_status_t
|
||||
uwatec_g2_device_open (dc_device_t **device, dc_context_t *context, unsigned int model);
|
||||
scubapro_g2_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* UWATEC_G2_H */
|
||||
#endif /* SCUBAPRO_G2_H */
|
||||
@ -184,6 +184,10 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
|
||||
if (out == NULL || name == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Are we using custom IO?
|
||||
if (_dc_context_custom_io(context))
|
||||
return dc_custom_io_serial_open(out, context, name);
|
||||
|
||||
INFO (context, "Open: name=%s", name);
|
||||
|
||||
// Allocate memory.
|
||||
|
||||
@ -164,6 +164,10 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
|
||||
if (out == NULL || name == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Are we using custom IO?
|
||||
if (_dc_context_custom_io(context))
|
||||
return dc_custom_io_serial_open(out, context, name);
|
||||
|
||||
INFO (context, "Open: name=%s", name);
|
||||
|
||||
// Build the device name.
|
||||
|
||||
@ -58,6 +58,7 @@ shearwater_common_open (shearwater_common_device_t *device, dc_context_t *contex
|
||||
status = dc_iostream_set_timeout (device->iostream, 3000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to set the timeout.");
|
||||
status = DC_STATUS_IO;
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
|
||||
@ -218,23 +218,35 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
|
||||
unsigned int model = 0;
|
||||
switch (hardware) {
|
||||
case 0x0808: // Petrel 2
|
||||
case 0x0909: // Petrel 1
|
||||
case 0x0B0B: // Petrel 1 (newer hardware)
|
||||
model = PETREL;
|
||||
case 0x0101:
|
||||
case 0x0202:
|
||||
model = PREDATOR;
|
||||
break;
|
||||
case 0x0606:
|
||||
case 0x0A0A: // Nerd 1
|
||||
case 0x0E0D: // Nerd 2
|
||||
model = NERD;
|
||||
break;
|
||||
case 0x0707:
|
||||
case 0x0404:
|
||||
case 0x0909: // Petrel 1
|
||||
case 0x0B0B: // Petrel 1 (newer hardware)
|
||||
model = PETREL;
|
||||
break;
|
||||
case 0x0505:
|
||||
case 0x0808: // Petrel 2
|
||||
model = PETREL;
|
||||
break;
|
||||
case 0x0707: // documentation list 0C0D for both Perdix and Perdix AI :-(
|
||||
model = PERDIX;
|
||||
break;
|
||||
case 0x0C0C:
|
||||
case 0x0C0D:
|
||||
case 0x0D0D:
|
||||
model = PERDIXAI;
|
||||
break;
|
||||
default:
|
||||
WARNING (abstract->context, "Unknown hardware type %04x.", hardware);
|
||||
model = PETREL;
|
||||
WARNING (abstract->context, "Unknown hardware type %04x. Assuming Petrel.", hardware);
|
||||
}
|
||||
|
||||
// Emit a device info event.
|
||||
|
||||
@ -34,7 +34,7 @@ dc_status_t
|
||||
shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, const char *name);
|
||||
|
||||
dc_status_t
|
||||
shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
||||
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -33,9 +33,6 @@ extern "C" {
|
||||
dc_status_t
|
||||
shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, const char *name);
|
||||
|
||||
dc_status_t
|
||||
shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@ -20,11 +20,19 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#include <libdivecomputer/units.h>
|
||||
|
||||
#include "shearwater_predator.h"
|
||||
#include "shearwater_petrel.h"
|
||||
#include "shearwater_common.h"
|
||||
#include "context-private.h"
|
||||
#include "parser-private.h"
|
||||
#include "array.h"
|
||||
@ -47,16 +55,13 @@
|
||||
#define IMPERIAL 1
|
||||
|
||||
#define NGASMIXES 10
|
||||
|
||||
#define PREDATOR 2
|
||||
#define PETREL 3
|
||||
#define MAXSTRINGS 32
|
||||
|
||||
typedef struct shearwater_predator_parser_t shearwater_predator_parser_t;
|
||||
|
||||
struct shearwater_predator_parser_t {
|
||||
dc_parser_t base;
|
||||
unsigned int model;
|
||||
unsigned int petrel;
|
||||
unsigned int samplesize;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
@ -67,7 +72,11 @@ struct shearwater_predator_parser_t {
|
||||
unsigned int oxygen[NGASMIXES];
|
||||
unsigned int helium[NGASMIXES];
|
||||
double calibration[3];
|
||||
unsigned int serial;
|
||||
dc_divemode_t mode;
|
||||
|
||||
/* String fields */
|
||||
dc_field_string_t strings[MAXSTRINGS];
|
||||
};
|
||||
|
||||
static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
@ -110,8 +119,8 @@ shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int petrel)
|
||||
dc_status_t
|
||||
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||
{
|
||||
shearwater_predator_parser_t *parser = NULL;
|
||||
const dc_parser_vtable_t *vtable = NULL;
|
||||
@ -120,7 +129,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (petrel) {
|
||||
if (model != PREDATOR) {
|
||||
vtable = &shearwater_petrel_parser_vtable;
|
||||
samplesize = SZ_SAMPLE_PETREL;
|
||||
} else {
|
||||
@ -137,8 +146,10 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
|
||||
// Set the default values.
|
||||
parser->model = model;
|
||||
parser->petrel = petrel;
|
||||
parser->samplesize = samplesize;
|
||||
parser->serial = serial;
|
||||
|
||||
// Set the default values.
|
||||
parser->cached = 0;
|
||||
parser->logversion = 0;
|
||||
parser->headersize = 0;
|
||||
@ -156,20 +167,6 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
||||
{
|
||||
return shearwater_common_parser_create (out, context, model, 0);
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
||||
{
|
||||
return shearwater_common_parser_create (out, context, model, 1);
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
@ -210,6 +207,146 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* These string cache interfaces should be some generic
|
||||
* library rather than copied for all the dive computers.
|
||||
*
|
||||
* This is just copied from the EON Steel code.
|
||||
*/
|
||||
static void
|
||||
add_string(shearwater_predator_parser_t *parser, const char *desc, const char *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAXSTRINGS; i++) {
|
||||
dc_field_string_t *str = parser->strings+i;
|
||||
if (str->desc)
|
||||
continue;
|
||||
str->desc = desc;
|
||||
str->value = strdup(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_string_fmt(shearwater_predator_parser_t *parser, const char *desc, const char *fmt, ...)
|
||||
{
|
||||
char buffer[256];
|
||||
va_list ap;
|
||||
|
||||
/*
|
||||
* We ignore the return value from vsnprintf, and we
|
||||
* always NUL-terminate the destination buffer ourselves.
|
||||
*
|
||||
* That way we don't have to worry about random bad legacy
|
||||
* implementations.
|
||||
*/
|
||||
va_start(ap, fmt);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return add_string(parser, desc, buffer);
|
||||
}
|
||||
|
||||
// The Battery state is a big-endian word:
|
||||
//
|
||||
// ffff = not paired / no comms for 90 s
|
||||
// fffe = no comms for 30 s
|
||||
//
|
||||
// Otherwise:
|
||||
// - top four bits are battery state (0 - normal, 1 - critical, 2 - warning)
|
||||
// - bottom 12 bits are pressure in 2 psi increments (0..8k psi)
|
||||
//
|
||||
// This returns the state as a bitmask (so you can see all states it had
|
||||
// during the dive). Note that we currently do not report pairing and
|
||||
// communication lapses. Todo?
|
||||
static unsigned int
|
||||
battery_state(const unsigned char *data)
|
||||
{
|
||||
unsigned int pressure = array_uint16_be(data);
|
||||
unsigned int state;
|
||||
|
||||
if ((pressure & 0xFFF0) == 0xFFF0)
|
||||
return 0;
|
||||
state = pressure >> 12;
|
||||
if (state > 2)
|
||||
return 0;
|
||||
return 1u << state;
|
||||
}
|
||||
|
||||
// Show the battery state
|
||||
//
|
||||
// NOTE! Right now it only shows the most serious bit
|
||||
// but the code is set up so that we could perhaps
|
||||
// indicate that the battery is on the edge (ie it
|
||||
// reported both "normal" _and_ "warning" during the
|
||||
// dive - maybe that would be a "starting to warn")
|
||||
//
|
||||
// We could also report unpaired and comm errors.
|
||||
static void
|
||||
add_battery_info(shearwater_predator_parser_t *parser, const char *desc, unsigned int state)
|
||||
{
|
||||
if (state >= 1 && state <= 7) {
|
||||
static const char *states[8] = {
|
||||
"", // 000 - No state bits, not used
|
||||
"normal", // 001 - only normal
|
||||
"critical", // 010 - only critical
|
||||
"critical", // 011 - both normal and critical
|
||||
"warning", // 100 - only warning
|
||||
"warning", // 101 - normal and warning
|
||||
"critical", // 110 - warning and critical
|
||||
"critical", // 111 - normal, warning and critical
|
||||
};
|
||||
add_string(parser, desc, states[state]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data)
|
||||
{
|
||||
switch (data[67]) {
|
||||
case 0:
|
||||
add_string_fmt(parser, "Deco model", "GF %u/%u", data[4], data[5]);
|
||||
break;
|
||||
case 1:
|
||||
add_string_fmt(parser, "Deco model", "VPM-B +%u", data[68]);
|
||||
break;
|
||||
case 2:
|
||||
add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[68], data[85]);
|
||||
break;
|
||||
default:
|
||||
add_string_fmt(parser, "Deco model", "Unknown model %d", data[67]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data)
|
||||
{
|
||||
if (parser->logversion < 7)
|
||||
return;
|
||||
|
||||
switch (data[120]) {
|
||||
case 1:
|
||||
add_string(parser, "Battery type", "1.5V Alkaline");
|
||||
break;
|
||||
case 2:
|
||||
add_string(parser, "Battery type", "1.5V Lithium");
|
||||
break;
|
||||
case 3:
|
||||
add_string(parser, "Battery type", "1.2V NiMH");
|
||||
break;
|
||||
case 4:
|
||||
add_string(parser, "Battery type", "3.6V Saft");
|
||||
break;
|
||||
case 5:
|
||||
add_string(parser, "Battery type", "3.7V Li-Ion");
|
||||
break;
|
||||
default:
|
||||
add_string_fmt(parser, "Battery type", "unknown type %d", data[120]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
@ -234,9 +371,12 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
unsigned int logversion = 6;
|
||||
if (data[127] > 6)
|
||||
logversion = data[127];
|
||||
INFO(abstract->context, "Shearwater log version %u\n", logversion);
|
||||
|
||||
memset(parser->strings, 0, sizeof(parser->strings));
|
||||
|
||||
// Adjust the footersize for the final block.
|
||||
if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) {
|
||||
if (parser->model > PREDATOR || array_uint16_be (data + size - footersize) == 0xFFFD) {
|
||||
footersize += SZ_BLOCK;
|
||||
if (size < headersize + footersize) {
|
||||
ERROR (abstract->context, "Invalid data length.");
|
||||
@ -253,6 +393,9 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
unsigned int helium[NGASMIXES] = {0};
|
||||
unsigned int o2_previous = 0, he_previous = 0;
|
||||
|
||||
// Transmitter battery levels
|
||||
unsigned int t1_battery = 0, t2_battery = 0;
|
||||
|
||||
unsigned int offset = headersize;
|
||||
unsigned int length = size - footersize;
|
||||
while (offset < length) {
|
||||
@ -295,6 +438,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
he_previous = he;
|
||||
}
|
||||
|
||||
// Transmitter battery levels
|
||||
if (logversion >= 7) {
|
||||
// T1 at offset 27, T2 at offset 19
|
||||
t1_battery |= battery_state(data + offset + 27);
|
||||
t2_battery |= battery_state(data + offset + 19);
|
||||
}
|
||||
|
||||
offset += parser->samplesize;
|
||||
}
|
||||
|
||||
@ -322,6 +472,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
parser->helium[i] = helium[i];
|
||||
}
|
||||
parser->mode = mode;
|
||||
add_string_fmt(parser, "Serial", "%08x", parser->serial);
|
||||
add_string_fmt(parser, "FW Version", "%2x", data[19]);
|
||||
add_deco_model(parser, data);
|
||||
add_battery_type(parser, data);
|
||||
add_string_fmt(parser, "Battery at end", "%.1f V", data[9] / 10.0);
|
||||
add_battery_info(parser, "T1 battery", t1_battery);
|
||||
add_battery_info(parser, "T2 battery", t2_battery);
|
||||
parser->cached = 1;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -348,6 +505,7 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
unsigned int density = 0;
|
||||
|
||||
if (value) {
|
||||
@ -383,6 +541,15 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
||||
case DC_FIELD_DIVEMODE:
|
||||
*((dc_divemode_t *) value) = parser->mode;
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
if (flags < MAXSTRINGS) {
|
||||
dc_field_string_t *p = parser->strings + flags;
|
||||
if (p->desc) {
|
||||
*string = *p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -414,6 +581,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
unsigned int time = 0;
|
||||
unsigned int offset = parser->headersize;
|
||||
unsigned int length = size - parser->footersize;
|
||||
|
||||
while (offset < length) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
@ -473,7 +641,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
}
|
||||
|
||||
// Setpoint
|
||||
if (parser->petrel) {
|
||||
if (parser->model > PREDATOR) {
|
||||
sample.setpoint = data[offset + 18] / 100.0;
|
||||
} else {
|
||||
if (status & SETPOINT_HIGH) {
|
||||
@ -486,7 +654,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
}
|
||||
|
||||
// CNS
|
||||
if (parser->petrel) {
|
||||
if (parser->model > PREDATOR) {
|
||||
sample.cns = data[offset + 22] / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
|
||||
}
|
||||
@ -564,6 +732,5 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
|
||||
offset += parser->samplesize;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ dc_status_t
|
||||
suunto_d9_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
|
||||
|
||||
dc_status_t
|
||||
suunto_d9_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
||||
suunto_d9_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // memcmp
|
||||
#include <string.h> // memcmp, strdup
|
||||
#include <stdio.h> // snprintf
|
||||
|
||||
#include "suunto_d9.h"
|
||||
#include "context-private.h"
|
||||
@ -72,6 +73,7 @@ typedef struct suunto_d9_parser_t suunto_d9_parser_t;
|
||||
struct suunto_d9_parser_t {
|
||||
dc_parser_t base;
|
||||
unsigned int model;
|
||||
unsigned int serial;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
unsigned int mode;
|
||||
@ -231,7 +233,7 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
||||
suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||
{
|
||||
suunto_d9_parser_t *parser = NULL;
|
||||
|
||||
@ -247,6 +249,7 @@ suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
|
||||
|
||||
// Set the default values.
|
||||
parser->model = model;
|
||||
parser->serial = serial;
|
||||
parser->cached = 0;
|
||||
parser->mode = AIR;
|
||||
parser->ngasmixes = 0;
|
||||
@ -326,6 +329,7 @@ suunto_d9_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define BUFLEN 16
|
||||
|
||||
static dc_status_t
|
||||
suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
@ -341,6 +345,9 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
||||
return rc;
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
char buf[BUFLEN];
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -388,6 +395,17 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch (flags) {
|
||||
case 0: /* serial */
|
||||
string->desc = "Serial";
|
||||
snprintf(buf, BUFLEN, "%08u", parser->serial);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
string->value = strdup(buf);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h> /* For crc32() */
|
||||
|
||||
#include "suunto_eonsteel.h"
|
||||
#include "context-private.h"
|
||||
@ -35,7 +36,6 @@
|
||||
|
||||
typedef struct suunto_eonsteel_device_t {
|
||||
dc_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned int model;
|
||||
unsigned int magic;
|
||||
unsigned short seq;
|
||||
@ -134,14 +134,14 @@ static void put_le32(unsigned int val, unsigned char *p)
|
||||
* The maximum payload is 62 bytes.
|
||||
*/
|
||||
#define PACKET_SIZE 64
|
||||
static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
static int receive_usbhid_packet(dc_custom_io_t *io, suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
unsigned char buf[64];
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
size_t transferred = 0;
|
||||
int len;
|
||||
|
||||
rc = dc_iostream_read(eon->iostream, buf, PACKET_SIZE, &transferred);
|
||||
rc = io->packet_read(io, buf, PACKET_SIZE, &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "read interrupt transfer failed");
|
||||
return -1;
|
||||
@ -168,6 +168,159 @@ static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer,
|
||||
return len;
|
||||
}
|
||||
|
||||
static int fill_ble_buffer(dc_custom_io_t *io, suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
int state = 0;
|
||||
int bytes = 0;
|
||||
unsigned int crc;
|
||||
|
||||
for (;;) {
|
||||
unsigned char packet[32];
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
size_t transferred = 0;
|
||||
int i;
|
||||
|
||||
rc = io->packet_read(io, packet, sizeof(packet), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "BLE GATT read transfer failed");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < transferred; i++) {
|
||||
unsigned char c = packet[i];
|
||||
|
||||
if (c == 0x7e) {
|
||||
if (state == 1)
|
||||
goto done;
|
||||
if (state == 2) {
|
||||
ERROR(eon->base.context, "BLE GATT stream has escaped 7e character");
|
||||
return -1;
|
||||
}
|
||||
/* Initial 7e character - good */
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state) {
|
||||
ERROR(eon->base.context, "BLE GATT stream did not start with 7e");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c == 0x7d) {
|
||||
if (state == 2) {
|
||||
ERROR(eon->base.context, "BLE GATT stream has escaped 7d character");
|
||||
return -1;
|
||||
}
|
||||
state = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == 2) {
|
||||
c ^= 0x20;
|
||||
state = 1;
|
||||
}
|
||||
if (bytes < size)
|
||||
buffer[bytes] = c;
|
||||
bytes++;
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (bytes < 4) {
|
||||
ERROR(eon->base.context, "did not receive BLE CRC32 data");
|
||||
return -1;
|
||||
}
|
||||
if (bytes > size) {
|
||||
ERROR(eon->base.context, "BLE GATT stream too long (%d bytes, buffer is %d)", bytes, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remove and check CRC */
|
||||
bytes -= 4;
|
||||
crc = crc32(0, buffer, bytes);
|
||||
if (crc != array_uint32_le(buffer + bytes)) {
|
||||
ERROR(eon->base.context, "incorrect BLE CRC32 data");
|
||||
return -1;
|
||||
}
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "rcv", buffer, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#define HDRSIZE 12
|
||||
#define MAXDATA 2048
|
||||
#define CRCSIZE 4
|
||||
|
||||
static struct {
|
||||
unsigned int len, offset;
|
||||
unsigned char buffer[HDRSIZE + MAXDATA + CRCSIZE];
|
||||
} ble_data;
|
||||
|
||||
static void fill_ble_data(dc_custom_io_t *io, suunto_eonsteel_device_t *eon)
|
||||
{
|
||||
int received;
|
||||
|
||||
received = fill_ble_buffer(io, eon, ble_data.buffer, sizeof(ble_data.buffer));
|
||||
if (received < 0)
|
||||
received = 0;
|
||||
ble_data.offset = 0;
|
||||
ble_data.len = received;
|
||||
}
|
||||
|
||||
static int receive_ble_packet(dc_custom_io_t *io, suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
int maxsize;
|
||||
|
||||
if (ble_data.offset >= ble_data.len)
|
||||
return 0;
|
||||
maxsize = ble_data.len - ble_data.offset;
|
||||
if (size > maxsize)
|
||||
size = maxsize;
|
||||
memcpy(buffer, ble_data.buffer + ble_data.offset, size);
|
||||
ble_data.offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int receive_packet(dc_custom_io_t *io, suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
if (io->packet_size < 64)
|
||||
return receive_ble_packet(io, eon, buffer, size);
|
||||
return receive_usbhid_packet(io, eon, buffer, size);
|
||||
}
|
||||
|
||||
static int add_hdlc(unsigned char *dst, unsigned char val)
|
||||
{
|
||||
int chars = 1;
|
||||
switch (val) {
|
||||
case 0x7e: case 0x7d:
|
||||
*dst++ = 0x7d;
|
||||
val ^= 0x20;
|
||||
chars++;
|
||||
/* fallthrough */
|
||||
default:
|
||||
*dst = val;
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
static int hdlc_reencode(unsigned char *dst, unsigned char *src, int len)
|
||||
{
|
||||
unsigned int crc = crc32(0, src, len);
|
||||
int result = 0, i;
|
||||
|
||||
*dst++ = 0x7e; result++;
|
||||
for (i = 0; i < len; i++) {
|
||||
int chars = add_hdlc(dst, src[i]);
|
||||
dst += chars;
|
||||
result += chars;
|
||||
}
|
||||
for (i = 0; i < 4; i++) {
|
||||
int chars = add_hdlc(dst, crc & 255);
|
||||
dst += chars;
|
||||
result += chars;
|
||||
crc >>= 8;
|
||||
}
|
||||
*dst++ = 0x7e; result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int send_cmd(suunto_eonsteel_device_t *eon,
|
||||
unsigned short cmd,
|
||||
unsigned int len,
|
||||
@ -176,6 +329,7 @@ static int send_cmd(suunto_eonsteel_device_t *eon,
|
||||
unsigned char buf[64];
|
||||
unsigned short seq = eon->seq;
|
||||
unsigned int magic = eon->magic;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(eon->base.context);
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
size_t transferred = 0;
|
||||
|
||||
@ -207,7 +361,29 @@ static int send_cmd(suunto_eonsteel_device_t *eon,
|
||||
memcpy(buf+14, buffer, len);
|
||||
}
|
||||
|
||||
rc = dc_iostream_write(eon->iostream, buf, sizeof(buf), &transferred);
|
||||
// BLE GATT protocol?
|
||||
if (io->packet_size < 64) {
|
||||
int hdlc_len;
|
||||
unsigned char hdlc[2+2*(62+4)]; /* start/stop + escaping*(maxbuf+crc32) */
|
||||
unsigned char *ptr;
|
||||
|
||||
hdlc_len = hdlc_reencode(hdlc, buf+2, buf[1]);
|
||||
|
||||
ptr = hdlc;
|
||||
do {
|
||||
int len = hdlc_len;
|
||||
|
||||
if (len > io->packet_size)
|
||||
len = io->packet_size;
|
||||
rc = io->packet_write(io, ptr, len, &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
break;
|
||||
ptr += len;
|
||||
hdlc_len -= len;
|
||||
} while (hdlc_len);
|
||||
} else {
|
||||
rc = io->packet_write(io, buf, sizeof(buf), &transferred);
|
||||
}
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "write interrupt transfer failed");
|
||||
return -1;
|
||||
@ -229,8 +405,11 @@ static int receive_header(suunto_eonsteel_device_t *eon, struct eon_hdr *hdr, un
|
||||
{
|
||||
int ret;
|
||||
unsigned char header[64];
|
||||
dc_custom_io_t *io = _dc_context_custom_io(eon->base.context);
|
||||
|
||||
ret = receive_packet(eon, header, sizeof(header));
|
||||
if (io->packet_size < 64)
|
||||
fill_ble_data(io, eon);
|
||||
ret = receive_packet(io, eon, header, sizeof(header));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret < 12) {
|
||||
@ -256,11 +435,12 @@ static int receive_header(suunto_eonsteel_device_t *eon, struct eon_hdr *hdr, un
|
||||
static int receive_data(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(eon->base.context);
|
||||
|
||||
while (size > 0) {
|
||||
int len;
|
||||
|
||||
len = receive_packet(eon, buffer + ret, size);
|
||||
len = receive_packet(io, eon, buffer + ret, size);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
@ -294,8 +474,7 @@ static int send_receive(suunto_eonsteel_device_t *eon,
|
||||
unsigned int len_out, const unsigned char *out,
|
||||
unsigned int len_in, unsigned char *in)
|
||||
{
|
||||
int len, actual, max;
|
||||
unsigned char buf[2048];
|
||||
int len, actual;
|
||||
struct eon_hdr hdr;
|
||||
|
||||
if (send_cmd(eon, cmd, len_out, out) < 0)
|
||||
@ -436,6 +615,20 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
|
||||
* with the last dirent first. That's intentional: for dives,
|
||||
* we will want to look up the last dive first.
|
||||
*/
|
||||
static struct directory_entry *add_dirent(struct directory_entry *new, struct directory_entry *list)
|
||||
{
|
||||
struct directory_entry **pp = &list, *p;
|
||||
|
||||
/* Skip any entries that are later than the new one */
|
||||
while ((p = *pp) != NULL && strcmp(p->name, new->name) > 0)
|
||||
pp = &p->next;
|
||||
|
||||
/* Add the new one to that location and return the new list pointer */
|
||||
new->next = p;
|
||||
*pp = new;
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int nr, const unsigned char *p, int len, struct directory_entry *old)
|
||||
{
|
||||
while (len > 8) {
|
||||
@ -445,7 +638,7 @@ static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int n
|
||||
struct directory_entry *entry;
|
||||
|
||||
if (namelen + 8 + 1 > len || name[namelen] != 0) {
|
||||
ERROR(eon->base.context, "corrupt dirent entry");
|
||||
ERROR(eon->base.context, "corrupt dirent entry: len=%d namelen=%d name='%s'", len, namelen, name);
|
||||
break;
|
||||
}
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "dir entry", p, 8);
|
||||
@ -457,8 +650,7 @@ static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int n
|
||||
ERROR(eon->base.context, "out of memory");
|
||||
break;
|
||||
}
|
||||
entry->next = old;
|
||||
old = entry;
|
||||
old = add_dirent(entry, old);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
@ -470,7 +662,6 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry *
|
||||
unsigned char result[2048];
|
||||
int rc, cmdlen;
|
||||
|
||||
|
||||
*res = NULL;
|
||||
put_le32(0, cmd);
|
||||
memcpy(cmd + 4, dive_directory, sizeof(dive_directory));
|
||||
@ -520,24 +711,8 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry *
|
||||
static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
|
||||
{
|
||||
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
|
||||
unsigned char buf[64];
|
||||
struct eon_hdr hdr;
|
||||
|
||||
dc_iostream_set_timeout(eon->iostream, 10);
|
||||
|
||||
/* Get rid of any pending stale input first */
|
||||
for (;;) {
|
||||
size_t transferred = 0;
|
||||
|
||||
dc_status_t rc = dc_iostream_read(eon->iostream, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
break;
|
||||
if (!transferred)
|
||||
break;
|
||||
}
|
||||
|
||||
dc_iostream_set_timeout(eon->iostream, 5000);
|
||||
|
||||
if (send_cmd(eon, CMD_INIT, sizeof(init), init)) {
|
||||
ERROR(eon->base.context, "Failed to send initialization command");
|
||||
return -1;
|
||||
@ -555,7 +730,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, unsigned int model)
|
||||
suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
suunto_eonsteel_device_t *eon = NULL;
|
||||
@ -574,13 +749,16 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, unsigned i
|
||||
memset (eon->version, 0, sizeof (eon->version));
|
||||
memset (eon->fingerprint, 0, sizeof (eon->fingerprint));
|
||||
|
||||
unsigned int vid = 0x1493, pid = 0;
|
||||
if (model == EONCORE) {
|
||||
pid = 0x0033;
|
||||
} else {
|
||||
pid = 0x0030;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(eon->base.context);
|
||||
if (io && io->packet_open)
|
||||
status = io->packet_open(io, context, name);
|
||||
else {
|
||||
/* We really need some way to specify USB ID's in the descriptor */
|
||||
unsigned int vendor_id = 0x1493;
|
||||
unsigned int device_id = model ? 0x0033 : 0x0030;
|
||||
status = dc_usbhid_custom_io(context, vendor_id, device_id);
|
||||
}
|
||||
status = dc_usbhid_open(&eon->iostream, context, vid, pid);
|
||||
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR(context, "unable to open device");
|
||||
goto error_free;
|
||||
@ -597,12 +775,22 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, unsigned i
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_close:
|
||||
dc_iostream_close(eon->iostream);
|
||||
suunto_eonsteel_device_close((dc_device_t *) eon);
|
||||
error_free:
|
||||
free(eon);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int count_dir_entries(struct directory_entry *de)
|
||||
{
|
||||
int count = 0;
|
||||
while (de) {
|
||||
count++;
|
||||
de = de->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||
{
|
||||
@ -628,7 +816,6 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
dc_buffer_t *file;
|
||||
char pathname[64];
|
||||
unsigned int time;
|
||||
unsigned int count = 0;
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
|
||||
// Emit a device info event.
|
||||
@ -645,40 +832,11 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Locate the most recent dive.
|
||||
// The filename represent the time of the dive, encoded as a hexadecimal
|
||||
// number. Thus the most recent dive can be found by simply sorting the
|
||||
// filenames alphabetically.
|
||||
struct directory_entry *head = de, *tail = de, *latest = de;
|
||||
while (de) {
|
||||
if (strcmp (de->name, latest->name) > 0) {
|
||||
latest = de;
|
||||
}
|
||||
tail = de;
|
||||
count++;
|
||||
de = de->next;
|
||||
}
|
||||
|
||||
// Make the most recent dive the head of the list.
|
||||
// The linked list is made circular, by attaching the head to the tail and
|
||||
// then cut open again just before the most recent dive.
|
||||
de = head;
|
||||
while (de) {
|
||||
if (de->next == latest) {
|
||||
de->next = NULL;
|
||||
tail->next = head;
|
||||
break;
|
||||
}
|
||||
|
||||
de = de->next;
|
||||
}
|
||||
|
||||
file = dc_buffer_new(0);
|
||||
progress.maximum = count;
|
||||
progress.maximum = count_dir_entries(de);
|
||||
progress.current = 0;
|
||||
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
de = latest;
|
||||
while (de) {
|
||||
int len;
|
||||
struct directory_entry *next = de->next;
|
||||
@ -769,9 +927,7 @@ static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const
|
||||
static dc_status_t
|
||||
suunto_eonsteel_device_close(dc_device_t *abstract)
|
||||
{
|
||||
suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract;
|
||||
dc_custom_io_t *io = _dc_context_custom_io(abstract->context);
|
||||
|
||||
dc_iostream_close(eon->iostream);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
return io->packet_close(io);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
dc_status_t
|
||||
suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context, unsigned int model);
|
||||
suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
|
||||
|
||||
dc_status_t
|
||||
suunto_eonsteel_parser_create(dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
||||
|
||||
@ -21,8 +21,16 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Wow. MSC is truly crap */
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
||||
#include "suunto_eonsteel.h"
|
||||
#include "context-private.h"
|
||||
@ -71,6 +79,7 @@ struct type_desc {
|
||||
|
||||
#define MAXTYPE 512
|
||||
#define MAXGASES 16
|
||||
#define MAXSTRINGS 32
|
||||
|
||||
typedef struct suunto_eonsteel_parser_t {
|
||||
dc_parser_t base;
|
||||
@ -89,7 +98,8 @@ typedef struct suunto_eonsteel_parser_t {
|
||||
double lowsetpoint;
|
||||
double highsetpoint;
|
||||
double customsetpoint;
|
||||
dc_tankvolume_t tankinfo[MAXGASES];
|
||||
dc_field_string_t strings[MAXSTRINGS];
|
||||
dc_tankinfo_t tankinfo[MAXGASES];
|
||||
double tanksize[MAXGASES];
|
||||
double tankworkingpressure[MAXGASES];
|
||||
} cache;
|
||||
@ -97,11 +107,6 @@ typedef struct suunto_eonsteel_parser_t {
|
||||
|
||||
typedef int (*eon_data_cb_t)(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user);
|
||||
|
||||
typedef struct eon_event_t {
|
||||
const char *name;
|
||||
parser_sample_event_t type;
|
||||
} eon_event_t;
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum eon_sample type;
|
||||
@ -167,16 +172,6 @@ static enum eon_sample lookup_descriptor_type(suunto_eonsteel_parser_t *eon, str
|
||||
return ES_none;
|
||||
}
|
||||
|
||||
static parser_sample_event_t lookup_event(const char *name, const eon_event_t events[], size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (!strcasecmp(name, events[i].name))
|
||||
return events[i].type;
|
||||
}
|
||||
|
||||
return SAMPLE_EVENT_NONE;
|
||||
}
|
||||
|
||||
static const char *desc_type_name(enum eon_sample type)
|
||||
{
|
||||
int i;
|
||||
@ -680,26 +675,17 @@ static void sample_event_state_type(const struct type_desc *desc, struct sample_
|
||||
static void sample_event_state_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||
{
|
||||
dc_sample_value_t sample = {0};
|
||||
static const eon_event_t states[] = {
|
||||
{"Wet Outside", SAMPLE_EVENT_NONE},
|
||||
{"Below Wet Activation Depth", SAMPLE_EVENT_NONE},
|
||||
{"Below Surface", SAMPLE_EVENT_NONE},
|
||||
{"Dive Active", SAMPLE_EVENT_NONE},
|
||||
{"Surface Calculation", SAMPLE_EVENT_NONE},
|
||||
{"Tank pressure available", SAMPLE_EVENT_NONE},
|
||||
{"Closed Circuit Mode", SAMPLE_EVENT_NONE},
|
||||
};
|
||||
const char *name;
|
||||
|
||||
name = info->state_type;
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
sample.event.type = lookup_event(name, states, C_ARRAY_SIZE(states));
|
||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
||||
return;
|
||||
|
||||
sample.event.type = SAMPLE_EVENT_STRING;
|
||||
sample.event.name = name;
|
||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||
sample.event.flags |= 1 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||
|
||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||
}
|
||||
|
||||
@ -710,25 +696,6 @@ static void sample_event_notify_type(const struct type_desc *desc, struct sample
|
||||
|
||||
static void sample_event_notify_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||
{
|
||||
static const eon_event_t notifications[] = {
|
||||
{"NoFly Time", SAMPLE_EVENT_NONE},
|
||||
{"Depth", SAMPLE_EVENT_NONE},
|
||||
{"Surface Time", SAMPLE_EVENT_NONE},
|
||||
{"Tissue Level", SAMPLE_EVENT_TISSUELEVEL},
|
||||
{"Deco", SAMPLE_EVENT_NONE},
|
||||
{"Deco Window", SAMPLE_EVENT_NONE},
|
||||
{"Safety Stop Ahead", SAMPLE_EVENT_NONE},
|
||||
{"Safety Stop", SAMPLE_EVENT_SAFETYSTOP},
|
||||
{"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
||||
{"Deep Stop Ahead", SAMPLE_EVENT_NONE},
|
||||
{"Deep Stop", SAMPLE_EVENT_DEEPSTOP},
|
||||
{"Dive Time", SAMPLE_EVENT_DIVETIME},
|
||||
{"Gas Available", SAMPLE_EVENT_NONE},
|
||||
{"SetPoint Switch", SAMPLE_EVENT_NONE},
|
||||
{"Diluent Hypoxia", SAMPLE_EVENT_NONE},
|
||||
{"Air Time", SAMPLE_EVENT_NONE},
|
||||
{"Tank Pressure", SAMPLE_EVENT_NONE},
|
||||
};
|
||||
dc_sample_value_t sample = {0};
|
||||
const char *name;
|
||||
|
||||
@ -736,11 +703,11 @@ static void sample_event_notify_value(const struct type_desc *desc, struct sampl
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
sample.event.type = lookup_event(name, notifications, C_ARRAY_SIZE(notifications));
|
||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
||||
return;
|
||||
|
||||
sample.event.type = SAMPLE_EVENT_STRING;
|
||||
sample.event.name = name;
|
||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||
sample.event.flags |= 2 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||
|
||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||
}
|
||||
|
||||
@ -752,22 +719,6 @@ static void sample_event_warning_type(const struct type_desc *desc, struct sampl
|
||||
|
||||
static void sample_event_warning_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||
{
|
||||
static const eon_event_t warnings[] = {
|
||||
{"ICD Penalty", SAMPLE_EVENT_NONE},
|
||||
{"Deep Stop Penalty", SAMPLE_EVENT_VIOLATION},
|
||||
{"Mandatory Safety Stop", SAMPLE_EVENT_SAFETYSTOP_MANDATORY},
|
||||
{"OTU250", SAMPLE_EVENT_NONE},
|
||||
{"OTU300", SAMPLE_EVENT_NONE},
|
||||
{"CNS80%", SAMPLE_EVENT_NONE},
|
||||
{"CNS100%", SAMPLE_EVENT_NONE},
|
||||
{"Max.Depth", SAMPLE_EVENT_MAXDEPTH},
|
||||
{"Air Time", SAMPLE_EVENT_AIRTIME},
|
||||
{"Tank Pressure", SAMPLE_EVENT_NONE},
|
||||
{"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
||||
{"Deep Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
||||
{"Ceiling Broken", SAMPLE_EVENT_CEILING},
|
||||
{"PO2 High", SAMPLE_EVENT_PO2},
|
||||
};
|
||||
dc_sample_value_t sample = {0};
|
||||
const char *name;
|
||||
|
||||
@ -775,11 +726,11 @@ static void sample_event_warning_value(const struct type_desc *desc, struct samp
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
sample.event.type = lookup_event(name, warnings, C_ARRAY_SIZE(warnings));
|
||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
||||
return;
|
||||
|
||||
sample.event.type = SAMPLE_EVENT_STRING;
|
||||
sample.event.name = name;
|
||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||
sample.event.flags |= 3 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||
|
||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||
}
|
||||
|
||||
@ -791,27 +742,18 @@ static void sample_event_alarm_type(const struct type_desc *desc, struct sample_
|
||||
|
||||
static void sample_event_alarm_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||
{
|
||||
static const eon_event_t alarms[] = {
|
||||
{"Mandatory Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
||||
{"Ascent Speed", SAMPLE_EVENT_ASCENT},
|
||||
{"Diluent Hyperoxia", SAMPLE_EVENT_NONE},
|
||||
{"Violated Deep Stop", SAMPLE_EVENT_VIOLATION},
|
||||
{"Ceiling Broken", SAMPLE_EVENT_CEILING},
|
||||
{"PO2 High", SAMPLE_EVENT_PO2},
|
||||
{"PO2 Low", SAMPLE_EVENT_PO2},
|
||||
};
|
||||
dc_sample_value_t sample = {0};
|
||||
const char *name;
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
name = info->alarm_type;
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
sample.event.type = lookup_event(name, alarms, C_ARRAY_SIZE(alarms));
|
||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
||||
return;
|
||||
|
||||
sample.event.type = SAMPLE_EVENT_STRING;
|
||||
sample.event.name = name;
|
||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||
sample.event.flags |= 4 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||
|
||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||
}
|
||||
|
||||
@ -1014,6 +956,19 @@ suunto_eonsteel_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t get_string_field(suunto_eonsteel_parser_t *eon, unsigned idx, dc_field_string_t *value)
|
||||
{
|
||||
if (idx < MAXSTRINGS) {
|
||||
dc_field_string_t *res = eon->cache.strings+idx;
|
||||
if (res->desc && res->value) {
|
||||
*value = *res;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
// Ugly define thing makes the code much easier to read
|
||||
// I'd love to use __typeof__, but that's a gcc'ism
|
||||
#define field_value(p, set) \
|
||||
@ -1082,11 +1037,13 @@ suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsi
|
||||
* We need to have workpressure and a valid tank. In that case,
|
||||
* a fractional tank size implies imperial.
|
||||
*/
|
||||
if (tank->workpressure && (tank->type == DC_TANKVOLUME_METRIC)) {
|
||||
if (tank->workpressure && (tank->type & DC_TANKINFO_METRIC)) {
|
||||
if (fabs(tank->volume - rint(tank->volume)) > 0.001)
|
||||
tank->type = DC_TANKVOLUME_IMPERIAL;
|
||||
tank->type += DC_TANKINFO_IMPERIAL - DC_TANKINFO_METRIC;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
return get_string_field(eon, flags, (dc_field_string_t *)value);
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -1137,7 +1094,7 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
|
||||
// "enum:0=Off,1=Primary,2=?,3=Diluent"
|
||||
// "enum:0=Off,1=Primary,3=Diluent,4=Oxygen"
|
||||
//
|
||||
// We turn that into the DC_TANKVOLUME data here, but
|
||||
// We turn that into the DC_TANKINFO data here, but
|
||||
// initially consider all non-off tanks to me METRIC.
|
||||
//
|
||||
// We may later turn the METRIC tank size into IMPERIAL if we
|
||||
@ -1145,7 +1102,7 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
|
||||
static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, unsigned char type)
|
||||
{
|
||||
int idx = eon->cache.ngases;
|
||||
dc_tankvolume_t tankinfo = DC_TANKVOLUME_METRIC;
|
||||
dc_tankinfo_t tankinfo = DC_TANKINFO_METRIC;
|
||||
const char *name;
|
||||
|
||||
if (idx >= MAXGASES)
|
||||
@ -1156,9 +1113,9 @@ static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *d
|
||||
if (!name)
|
||||
DEBUG(eon->base.context, "Unable to look up gas type %u in %s", type, desc->format);
|
||||
else if (!strcasecmp(name, "Diluent"))
|
||||
;
|
||||
tankinfo |= DC_TANKINFO_CC_DILUENT;
|
||||
else if (!strcasecmp(name, "Oxygen"))
|
||||
;
|
||||
tankinfo |= DC_TANKINFO_CC_O2;
|
||||
else if (!strcasecmp(name, "None"))
|
||||
tankinfo = DC_TANKVOLUME_NONE;
|
||||
else if (strcasecmp(name, "Primary"))
|
||||
@ -1210,6 +1167,42 @@ static int add_gas_workpressure(suunto_eonsteel_parser_t *eon, float wp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_string(suunto_eonsteel_parser_t *eon, const char *desc, const char *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
eon->cache.initialized |= 1 << DC_FIELD_STRING;
|
||||
for (i = 0; i < MAXSTRINGS; i++) {
|
||||
dc_field_string_t *str = eon->cache.strings+i;
|
||||
if (str->desc)
|
||||
continue;
|
||||
str->desc = desc;
|
||||
str->value = strdup(value);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_string_fmt(suunto_eonsteel_parser_t *eon, const char *desc, const char *fmt, ...)
|
||||
{
|
||||
char buffer[256];
|
||||
va_list ap;
|
||||
|
||||
/*
|
||||
* We ignore the return value from vsnprintf, and we
|
||||
* always NUL-terminate the destination buffer ourselves.
|
||||
*
|
||||
* That way we don't have to worry about random bad legacy
|
||||
* implementations.
|
||||
*/
|
||||
va_start(ap, fmt);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return add_string(eon, desc, buffer);
|
||||
}
|
||||
|
||||
static float get_le32_float(const unsigned char *src)
|
||||
{
|
||||
union {
|
||||
@ -1233,7 +1226,16 @@ static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
||||
const unsigned char *data, int len)
|
||||
{
|
||||
const char *name = desc->desc + strlen("sml.DeviceLog.Device.");
|
||||
|
||||
if (!strcmp(name, "SerialNumber"))
|
||||
return add_string(eon, "Serial", data);
|
||||
if (!strcmp(name, "Info.HW"))
|
||||
return add_string(eon, "HW Version", data);
|
||||
if (!strcmp(name, "Info.SW"))
|
||||
return add_string(eon, "FW Version", data);
|
||||
if (!strcmp(name, "Info.BatteryAtStart"))
|
||||
return add_string(eon, "Battery at start", data);
|
||||
if (!strcmp(name, "Info.BatteryAtEnd"))
|
||||
return add_string(eon, "Battery at end", data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1264,12 +1266,30 @@ static int traverse_gas_fields(suunto_eonsteel_parser_t *eon, const struct type_
|
||||
if (!strcmp(name, ".Gas.Helium"))
|
||||
return add_gas_he(eon, data[0]);
|
||||
|
||||
if (!strcmp(name, ".Gas.TransmitterID"))
|
||||
return add_string(eon, "Transmitter ID", data);
|
||||
|
||||
if (!strcmp(name, ".Gas.TankSize"))
|
||||
return add_gas_size(eon, get_le32_float(data));
|
||||
|
||||
if (!strcmp(name, ".Gas.TankFillPressure"))
|
||||
return add_gas_workpressure(eon, get_le32_float(data));
|
||||
|
||||
// There is a bug with older transmitters, where the transmitter
|
||||
// battery charge returns zero. Rather than returning that bogus
|
||||
// data, just don't return any battery charge information at all.
|
||||
//
|
||||
// Make sure to add all non-battery-charge field checks above this
|
||||
// test, so that it doesn't trigger for anything else.
|
||||
if (!data[0])
|
||||
return 0;
|
||||
|
||||
if (!strcmp(name, ".Gas.TransmitterStartBatteryCharge"))
|
||||
return add_string_fmt(eon, "Transmitter Battery at start", "%d %%", data[0]);
|
||||
|
||||
if (!strcmp(name, ".Gas.TransmitterEndBatteryCharge"))
|
||||
return add_string_fmt(eon, "Transmitter Battery at end", "%d %%", data[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1326,12 +1346,22 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(name, "Algorithm"))
|
||||
return add_string(eon, "Deco algorithm", data);
|
||||
|
||||
if (!strcmp(name, "DiveMode")) {
|
||||
if (!strncmp((const char *)data, "CCR", 3)) {
|
||||
eon->cache.divemode = DC_DIVEMODE_CCR;
|
||||
eon->cache.initialized |= 1 << DC_FIELD_DIVEMODE;
|
||||
}
|
||||
return 0;
|
||||
return add_string(eon, "Dive Mode", data);
|
||||
}
|
||||
|
||||
/* Signed byte of conservatism (-2 .. +2) */
|
||||
if (!strcmp(name, "Conservatism")) {
|
||||
int val = *(signed char *)data;
|
||||
|
||||
return add_string_fmt(eon, "Personal Adjustment", "P%d", val);
|
||||
}
|
||||
|
||||
if (!strcmp(name, "LowSetPoint")) {
|
||||
@ -1346,6 +1376,18 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Time recoded in seconds.
|
||||
// Let's just agree to ignore seconds
|
||||
if (!strcmp(name, "DesaturationTime")) {
|
||||
unsigned int time = array_uint32_le(data) / 60;
|
||||
return add_string_fmt(eon, "Desaturation Time", "%d:%02d", time / 60, time % 60);
|
||||
}
|
||||
|
||||
if (!strcmp(name, "SurfaceTime")) {
|
||||
unsigned int time = array_uint32_le(data) / 60;
|
||||
return add_string_fmt(eon, "Surface Time", "%d:%02d", time / 60, time % 60);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1372,6 +1414,8 @@ static int traverse_header_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
||||
eon->cache.maxdepth = d;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(name, "DateTime"))
|
||||
return add_string(eon, "Dive ID", data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
77
src/usbhid.c
77
src/usbhid.c
@ -131,6 +131,81 @@ syserror(int errcode)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static dc_status_t
|
||||
usbhid_packet_close(dc_custom_io_t *io)
|
||||
{
|
||||
dc_iostream_t *usbhid = (dc_iostream_t *)io->userdata;
|
||||
return dc_usbhid_close(usbhid);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
usbhid_packet_read(dc_custom_io_t *io, void* data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_iostream_t *usbhid = (dc_iostream_t *)io->userdata;
|
||||
return dc_usbhid_read(usbhid, data, size, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME! The USB HID "report type" is a disaster, and there's confusion
|
||||
* between libusb and HIDAPI. The Scubapro G2 seems to need an explicit
|
||||
* report type of 0 for HIDAPI, but not for libusb.
|
||||
*
|
||||
* See commit d251b37 ("Add a zero report ID to the commands") for the
|
||||
* Scubapro G2 - but that doesn't actually work with the BLE case, so
|
||||
* I really suspect that we need to do something _here_ in the packet
|
||||
* IO layer, and have the USBHID registration set the report type to
|
||||
* use (ie an extra new argument to dc_usbhid_custom_io() to set the
|
||||
* report type, or something).
|
||||
*
|
||||
* The Suunto EON Steel just uses 0x3f and does that in the caller.
|
||||
*/
|
||||
static dc_status_t
|
||||
usbhid_packet_write(dc_custom_io_t *io, const void* data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_iostream_t *usbhid = (dc_iostream_t *)io->userdata;
|
||||
return dc_usbhid_write(usbhid, data, size, actual);
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_usbhid_custom_io (dc_context_t *context, unsigned int vid, unsigned int pid)
|
||||
{
|
||||
dc_iostream_t *usbhid;
|
||||
dc_status_t status;
|
||||
|
||||
static dc_custom_io_t custom = {
|
||||
.packet_size = 64,
|
||||
.packet_close = usbhid_packet_close,
|
||||
.packet_read = usbhid_packet_read,
|
||||
.packet_write = usbhid_packet_write,
|
||||
};
|
||||
|
||||
status = dc_usbhid_open(&usbhid, context, vid, pid);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
custom.userdata = (void *)usbhid;
|
||||
dc_context_set_custom_io(context, &custom, NULL);
|
||||
|
||||
dc_usbhid_set_timeout(usbhid, 10);
|
||||
|
||||
/* Get rid of any pending stale input first */
|
||||
/* NOTE! This will cause an annoying warning from dc_usbhid_read() */
|
||||
for (;;) {
|
||||
size_t transferred = 0;
|
||||
unsigned char buf[64];
|
||||
|
||||
dc_status_t rc = dc_usbhid_read(usbhid, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
break;
|
||||
if (!transferred)
|
||||
break;
|
||||
}
|
||||
|
||||
dc_usbhid_set_timeout(usbhid, 5000);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef USBHID
|
||||
static dc_mutex_t g_usbhid_mutex = DC_MUTEX_INIT;
|
||||
static size_t g_usbhid_refcount = 0;
|
||||
@ -271,7 +346,7 @@ dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, un
|
||||
}
|
||||
|
||||
if (device == NULL) {
|
||||
ERROR (context, "No matching USB device found.");
|
||||
ERROR (context, "No matching USB device (%04x:%04x) found.", vid, pid);
|
||||
status = DC_STATUS_NODEVICE;
|
||||
goto error_usb_free_list;
|
||||
}
|
||||
|
||||
@ -43,6 +43,10 @@ extern "C" {
|
||||
dc_status_t
|
||||
dc_usbhid_open (dc_iostream_t **iostream, dc_context_t *context, unsigned int vid, unsigned int pid);
|
||||
|
||||
/* Create a dc_custom_io_t that uses usbhid for packet transfer */
|
||||
dc_status_t
|
||||
dc_usbhid_custom_io(dc_context_t *context, unsigned int vid, unsigned int pid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user