Merge git://git.libdivecomputer.org/libdivecomputer into Subsurface-branch
Merge with upstream libdivecomputer from Jef: - more Cochran work from John Van Ostrand - new 'timesync' interface to synchronize the clock on a dive computer - support for Aqualung i200 - misc updates (Cressi Leonardo fix, OSTC 3+ renaming, fix surface pressure on iX3M, idive salinity parsing) - HIDAPI work. It turns out that HIDAPI is not compatible with libusb in the actual packet sending path, so this will need some more cleanups - Jef doesn't see the issue because he doesn't have a generic packet IO layer and doesn't share packets with the BLE code. * git://git.libdivecomputer.org/libdivecomputer: (25 commits) Add basic timezone support Add time synchronization to the example application Implement the new api for the HW devices Add support for synchronizing the device clock Use hidapi as the default USB HID library Workaround for a Windows hidapi issue Reset the number of bytes to zero on error Add a zero report ID to the commands Fix compatibility issue with hidapi Implement the salinity field Fix the atmospheric pressure for the iX3M Rename the OSTC 3+ to OSTC Plus Locate the most recent dive using the logbook pointers Add support for the Aqualung i200 Add event handling to TM model Fix profile buffer size and address size Add three event codes Add support for the Commander TM Dump function no longer assumes reads begin at byte 0 Remove unneeded function ...
This commit is contained in:
commit
928be1f45b
@ -158,9 +158,14 @@ AC_CHECK_HEADERS([sys/param.h])
|
||||
# Checks for global variable declarations.
|
||||
AC_CHECK_DECLS([optreset])
|
||||
|
||||
# Checks for structures.
|
||||
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[
|
||||
#include <time.h>
|
||||
])
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_STRERROR_R
|
||||
AC_CHECK_FUNCS([localtime_r gmtime_r])
|
||||
AC_CHECK_FUNCS([localtime_r gmtime_r timegm _mkgmtime])
|
||||
AC_CHECK_FUNCS([getopt_long])
|
||||
|
||||
# Versioning.
|
||||
|
||||
@ -17,6 +17,7 @@ dctool_SOURCES = \
|
||||
dctool_parse.c \
|
||||
dctool_read.c \
|
||||
dctool_write.c \
|
||||
dctool_timesync.c \
|
||||
dctool_fwupdate.c \
|
||||
output.h \
|
||||
output-private.h \
|
||||
|
||||
@ -63,6 +63,7 @@ static const dctool_command_t *g_commands[] = {
|
||||
&dctool_parse,
|
||||
&dctool_read,
|
||||
&dctool_write,
|
||||
&dctool_timesync,
|
||||
&dctool_fwupdate,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -50,6 +50,7 @@ extern const dctool_command_t dctool_dump;
|
||||
extern const dctool_command_t dctool_parse;
|
||||
extern const dctool_command_t dctool_read;
|
||||
extern const dctool_command_t dctool_write;
|
||||
extern const dctool_command_t dctool_timesync;
|
||||
extern const dctool_command_t dctool_fwupdate;
|
||||
|
||||
const dctool_command_t *
|
||||
|
||||
162
examples/dctool_timesync.c
Normal file
162
examples/dctool_timesync.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/descriptor.h>
|
||||
#include <libdivecomputer/device.h>
|
||||
|
||||
#include "dctool.h"
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
static dc_status_t
|
||||
do_timesync (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const dc_datetime_t *datetime)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_device_t *device = NULL;
|
||||
|
||||
// Open the device.
|
||||
message ("Opening the device (%s %s, %s).\n",
|
||||
dc_descriptor_get_vendor (descriptor),
|
||||
dc_descriptor_get_product (descriptor),
|
||||
devname ? devname : "null");
|
||||
rc = dc_device_open (&device, context, descriptor, devname);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR ("Error opening the device.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Register the event handler.
|
||||
message ("Registering the event handler.\n");
|
||||
int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR;
|
||||
rc = dc_device_set_events (device, events, dctool_event_cb, NULL);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR ("Error registering the event handler.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Register the cancellation handler.
|
||||
message ("Registering the cancellation handler.\n");
|
||||
rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR ("Error registering the cancellation handler.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Syncronize the device clock.
|
||||
message ("Syncronize the device clock.\n");
|
||||
rc = dc_device_timesync (device, datetime);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR ("Error syncronizing the device clock.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
dc_device_close (device);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
dctool_timesync_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor)
|
||||
{
|
||||
int exitcode = EXIT_SUCCESS;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
// Default option values.
|
||||
unsigned int help = 0;
|
||||
|
||||
// Parse the command-line options.
|
||||
int opt = 0;
|
||||
const char *optstring = "h";
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
struct option options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) {
|
||||
#else
|
||||
while ((opt = getopt (argc, argv, optstring)) != -1) {
|
||||
#endif
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help = 1;
|
||||
break;
|
||||
default:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
// Show help message.
|
||||
if (help) {
|
||||
dctool_command_showhelp (&dctool_timesync);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Get the system time.
|
||||
dc_datetime_t datetime = {0};
|
||||
dc_ticks_t now = dc_datetime_now ();
|
||||
if (!dc_datetime_localtime(&datetime, now)) {
|
||||
message ("ERROR: Failed to get the system time.\n");
|
||||
exitcode = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Synchronize the device clock.
|
||||
status = do_timesync (context, descriptor, argv[0], &datetime);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
message ("ERROR: %s\n", dctool_errmsg (status));
|
||||
exitcode = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return exitcode;
|
||||
}
|
||||
|
||||
const dctool_command_t dctool_timesync = {
|
||||
dctool_timesync_run,
|
||||
DCTOOL_CONFIG_DESCRIPTOR,
|
||||
"timesync",
|
||||
"Synchronize the device clock",
|
||||
"Usage:\n"
|
||||
" dctool timesync [options]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
" -h, --help Show help message\n"
|
||||
#else
|
||||
" -h Show help message\n"
|
||||
#endif
|
||||
};
|
||||
@ -230,9 +230,16 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (dt.timezone == DC_TIMEZONE_NONE) {
|
||||
fprintf (output->ostream, "<datetime>%04i-%02i-%02i %02i:%02i:%02i</datetime>\n",
|
||||
dt.year, dt.month, dt.day,
|
||||
dt.hour, dt.minute, dt.second);
|
||||
} else {
|
||||
fprintf (output->ostream, "<datetime>%04i-%02i-%02i %02i:%02i:%02i %+03i:%02i</datetime>\n",
|
||||
dt.year, dt.month, dt.day,
|
||||
dt.hour, dt.minute, dt.second,
|
||||
dt.timezone / 3600, (dt.timezone % 3600) / 60);
|
||||
}
|
||||
|
||||
// Parse the divetime.
|
||||
message ("Parsing the divetime.\n");
|
||||
|
||||
@ -26,6 +26,8 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define DC_TIMEZONE_NONE 0x80000000
|
||||
|
||||
#if defined (_WIN32) && !defined (__GNUC__)
|
||||
typedef __int64 dc_ticks_t;
|
||||
#else
|
||||
@ -39,6 +41,7 @@ typedef struct dc_datetime_t {
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
int timezone;
|
||||
} dc_datetime_t;
|
||||
|
||||
dc_ticks_t
|
||||
|
||||
@ -84,9 +84,6 @@ dc_device_set_events (dc_device_t *device, unsigned int events, dc_event_callbac
|
||||
dc_status_t
|
||||
dc_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
|
||||
|
||||
dc_status_t
|
||||
dc_device_version (dc_device_t *device, unsigned char data[], unsigned int size);
|
||||
|
||||
dc_status_t
|
||||
dc_device_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size);
|
||||
|
||||
@ -99,6 +96,9 @@ dc_device_dump (dc_device_t *device, dc_buffer_t *buffer);
|
||||
dc_status_t
|
||||
dc_device_foreach (dc_device_t *device, dc_dive_callback_t callback, void *userdata);
|
||||
|
||||
dc_status_t
|
||||
dc_device_timesync (dc_device_t *device, const dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t
|
||||
dc_device_close (dc_device_t *device);
|
||||
|
||||
|
||||
@ -36,9 +36,6 @@ extern "C" {
|
||||
dc_status_t
|
||||
hw_frog_device_version (dc_device_t *device, unsigned char data[], unsigned int size);
|
||||
|
||||
dc_status_t
|
||||
hw_frog_device_clock (dc_device_t *device, const dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t
|
||||
hw_frog_device_display (dc_device_t *device, const char *text);
|
||||
|
||||
|
||||
@ -43,9 +43,6 @@ typedef enum hw_ostc_format_t {
|
||||
dc_status_t
|
||||
hw_ostc_device_md2hash (dc_device_t *device, unsigned char data[], unsigned int size);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc_device_clock (dc_device_t *device, const dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc_device_eeprom_read (dc_device_t *device, unsigned int bank, unsigned char data[], unsigned int size);
|
||||
|
||||
|
||||
@ -39,9 +39,6 @@ hw_ostc3_device_version (dc_device_t *device, unsigned char data[], unsigned int
|
||||
dc_status_t
|
||||
hw_ostc3_device_hardware (dc_device_t *device, unsigned char data[], unsigned int size);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc3_device_clock (dc_device_t *device, const dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t
|
||||
hw_ostc3_device_display (dc_device_t *device, const char *text);
|
||||
|
||||
|
||||
@ -75,6 +75,7 @@ static const dc_device_vtable_t atomics_cobalt_device_vtable = {
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
atomics_cobalt_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
atomics_cobalt_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -128,6 +128,7 @@ atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dateti
|
||||
datetime->hour = p[0x18];
|
||||
datetime->minute = p[0x19];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -51,6 +51,7 @@ static const dc_device_vtable_t citizen_aqualand_device_vtable = {
|
||||
NULL, /* write */
|
||||
citizen_aqualand_device_dump, /* dump */
|
||||
citizen_aqualand_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
citizen_aqualand_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -95,6 +95,7 @@ citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *date
|
||||
datetime->hour = bcd2dec(p[0x0A]);
|
||||
datetime->minute = bcd2dec(p[0x0B]);
|
||||
datetime->second = bcd2dec(p[0x0C]);
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -29,16 +29,18 @@
|
||||
#include "serial.h"
|
||||
#include "array.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "rbstream.h"
|
||||
|
||||
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
|
||||
|
||||
#define MAXRETRIES 2
|
||||
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 0
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1
|
||||
#define COCHRAN_MODEL_EMC_14 2
|
||||
#define COCHRAN_MODEL_EMC_16 3
|
||||
#define COCHRAN_MODEL_EMC_20 4
|
||||
#define COCHRAN_MODEL_COMMANDER_TM 0
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 1
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2
|
||||
#define COCHRAN_MODEL_EMC_14 3
|
||||
#define COCHRAN_MODEL_EMC_16 4
|
||||
#define COCHRAN_MODEL_EMC_20 5
|
||||
|
||||
typedef enum cochran_endian_t {
|
||||
ENDIAN_LE,
|
||||
@ -60,9 +62,6 @@ typedef struct cochran_data_t {
|
||||
int invalid_profile_dive_num;
|
||||
|
||||
unsigned int logbook_size;
|
||||
|
||||
unsigned int sample_data_offset;
|
||||
unsigned int sample_size;
|
||||
} cochran_data_t;
|
||||
|
||||
typedef struct cochran_device_layout_t {
|
||||
@ -70,6 +69,7 @@ typedef struct cochran_device_layout_t {
|
||||
unsigned int address_bits;
|
||||
cochran_endian_t endian;
|
||||
unsigned int baudrate;
|
||||
unsigned int rbstream_size;
|
||||
// Config data.
|
||||
unsigned int cf_dive_count;
|
||||
unsigned int cf_last_log;
|
||||
@ -89,6 +89,7 @@ typedef struct cochran_device_layout_t {
|
||||
unsigned int pt_profile_pre;
|
||||
unsigned int pt_profile_begin;
|
||||
unsigned int pt_profile_end;
|
||||
unsigned int pt_dive_number;
|
||||
} cochran_device_layout_t;
|
||||
|
||||
typedef struct cochran_commander_device_t {
|
||||
@ -113,15 +114,42 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
|
||||
NULL, /* write */
|
||||
cochran_commander_device_dump, /* dump */
|
||||
cochran_commander_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
cochran_commander_device_close /* close */
|
||||
};
|
||||
|
||||
// Cochran Commander TM, pre-dates pre-21000 s/n
|
||||
static const cochran_device_layout_t cochran_cmdr_tm_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_TM, // model
|
||||
24, // address_bits
|
||||
ENDIAN_WORD_BE, // endian
|
||||
9600, // baudrate
|
||||
4096, // rbstream_size
|
||||
0x146, // cf_dive_count
|
||||
0x158, // cf_last_log
|
||||
0xffffff, // cf_last_interdive
|
||||
0x15c, // cf_serial_number
|
||||
0x010000, // rb_logbook_begin
|
||||
0x01232b, // rb_logbook_end
|
||||
90, // rb_logbook_entry_size
|
||||
100, // rb_logbook_entry_count
|
||||
0x01232b, // rb_profile_begin
|
||||
0x018000, // rb_profile_end
|
||||
15, // pt_fingerprint
|
||||
4, // fingerprint_size
|
||||
0, // pt_profile_pre
|
||||
0, // pt_profile_begin
|
||||
90, // pt_profile_end (Next begin pointer is the end)
|
||||
20, // pt_dive_number
|
||||
};
|
||||
|
||||
// Cochran Commander pre-21000 s/n
|
||||
static const cochran_device_layout_t cochran_cmdr_1_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_PRE21000, // model
|
||||
24, // address_bits
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
32768, // rbstream_size
|
||||
0x046, // cf_dive_count
|
||||
0x6c, // cf_last_log
|
||||
0x70, // cf_last_interdive
|
||||
@ -137,6 +165,7 @@ static const cochran_device_layout_t cochran_cmdr_1_device_layout = {
|
||||
28, // pt_profile_pre
|
||||
0, // pt_profile_begin
|
||||
128, // pt_profile_end
|
||||
68, // pt_dive_number
|
||||
};
|
||||
|
||||
|
||||
@ -146,6 +175,7 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
24, // address_bits
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
32768, // rbstream_size
|
||||
0x046, // cf_dive_count
|
||||
0x06C, // cf_last_log
|
||||
0x070, // cf_last_interdive
|
||||
@ -161,6 +191,7 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
128, // pt_profile_end
|
||||
70, // pt_dive_number
|
||||
};
|
||||
|
||||
// Cochran EMC-14
|
||||
@ -169,6 +200,7 @@ static const cochran_device_layout_t cochran_emc14_device_layout = {
|
||||
32, // address_bits
|
||||
ENDIAN_LE, // endian
|
||||
806400, // baudrate
|
||||
65536, // rbstream_size
|
||||
0x0D2, // cf_dive_count
|
||||
0x13E, // cf_last_log
|
||||
0x142, // cf_last_interdive
|
||||
@ -184,6 +216,7 @@ static const cochran_device_layout_t cochran_emc14_device_layout = {
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
86, // pt_dive_number
|
||||
};
|
||||
|
||||
// Cochran EMC-16
|
||||
@ -192,6 +225,7 @@ static const cochran_device_layout_t cochran_emc16_device_layout = {
|
||||
32, // address_bits
|
||||
ENDIAN_LE, // endian
|
||||
806400, // baudrate
|
||||
65536, // rbstream_size
|
||||
0x0D2, // cf_dive_count
|
||||
0x13E, // cf_last_log
|
||||
0x142, // cf_last_interdive
|
||||
@ -207,6 +241,7 @@ static const cochran_device_layout_t cochran_emc16_device_layout = {
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
86, // pt_dive_number
|
||||
};
|
||||
|
||||
// Cochran EMC-20
|
||||
@ -215,6 +250,7 @@ static const cochran_device_layout_t cochran_emc20_device_layout = {
|
||||
32, // address_bits
|
||||
ENDIAN_LE, // endian
|
||||
806400, // baudrate
|
||||
65536, // rbstream_size
|
||||
0x0D2, // cf_dive_count
|
||||
0x13E, // cf_last_log
|
||||
0x142, // cf_last_interdive
|
||||
@ -230,6 +266,7 @@ static const cochran_device_layout_t cochran_emc20_device_layout = {
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
256, // pt_profile_end
|
||||
86, // pt_dive_number
|
||||
};
|
||||
|
||||
|
||||
@ -238,6 +275,7 @@ static unsigned int
|
||||
cochran_commander_get_model (cochran_commander_device_t *device)
|
||||
{
|
||||
const cochran_commander_model_t models[] = {
|
||||
{"\x0a""12", COCHRAN_MODEL_COMMANDER_TM},
|
||||
{"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000},
|
||||
{"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX},
|
||||
{"730", COCHRAN_MODEL_EMC_14},
|
||||
@ -330,7 +368,7 @@ cochran_commander_packet (cochran_commander_device_t *device, dc_event_progress_
|
||||
}
|
||||
}
|
||||
|
||||
if (high_speed) {
|
||||
if (high_speed && device->layout->baudrate != 9600) {
|
||||
// Give the DC time to process the command.
|
||||
dc_serial_sleep(device->port, 45);
|
||||
|
||||
@ -399,18 +437,24 @@ cochran_commander_read_config (cochran_commander_device_t *device, dc_event_prog
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
|
||||
// Read two 512 byte blocks into one 1024 byte buffer
|
||||
for (unsigned int i = 0; i < 2; i++) {
|
||||
const unsigned int len = size / 2;
|
||||
if ((size % 512) != 0)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Read two 512 byte blocks into one 1024 byte buffer
|
||||
unsigned int pages = size / 512;
|
||||
for (unsigned int i = 0; i < pages; i++) {
|
||||
unsigned char command[2] = {0x96, i};
|
||||
rc = cochran_commander_packet(device, progress, command, sizeof(command), data + i * len, len, 0);
|
||||
unsigned int command_size = sizeof(command);
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
command_size = 1;
|
||||
|
||||
rc = cochran_commander_packet(device, progress, command, command_size, data + i * 512, 512, 0);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
dc_event_vendor_t vendor;
|
||||
vendor.data = data + i * len;
|
||||
vendor.size = len;
|
||||
vendor.data = data + i * 512;
|
||||
vendor.size = 512;
|
||||
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
|
||||
}
|
||||
|
||||
@ -444,6 +488,26 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
|
||||
break;
|
||||
case 24:
|
||||
// Commander uses 24 byte addressing
|
||||
if (device->layout->baudrate == 9600) {
|
||||
// This read command will return 32K bytes if asked to read
|
||||
// 0 bytes. So we can allow a size of up to 0x10000 but if
|
||||
// the user asks for 0 bytes we should just return success
|
||||
// otherwise we'll end end up running past the buffer.
|
||||
if (size > 0x10000)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
if (size == 0)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
// Older commander, use low-speed read command
|
||||
command[0] = 0x05;
|
||||
command[1] = (address ) & 0xff;
|
||||
command[2] = (address >> 8) & 0xff;
|
||||
command[3] = (address >> 16) & 0xff;
|
||||
command[4] = (size ) & 0xff;
|
||||
command[5] = (size >> 8 ) & 0xff;
|
||||
command_size = 6;
|
||||
} else {
|
||||
// Newer commander with high-speed read command
|
||||
command[0] = 0x15;
|
||||
command[1] = (address ) & 0xff;
|
||||
command[2] = (address >> 8) & 0xff;
|
||||
@ -453,6 +517,7 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
|
||||
command[6] = (size >> 16 ) & 0xff;
|
||||
command[7] = 0x04;
|
||||
command_size = 8;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
@ -473,31 +538,6 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cochran_commander_read_retry (cochran_commander_device_t *device, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size)
|
||||
{
|
||||
// Save the state of the progress events.
|
||||
unsigned int saved = progress->current;
|
||||
|
||||
unsigned int nretries = 0;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
while ((rc = cochran_commander_read (device, progress, address, data, size)) != DC_STATUS_SUCCESS) {
|
||||
// Automatically discard a corrupted packet,
|
||||
// and request a new one.
|
||||
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
|
||||
return rc;
|
||||
|
||||
// Abort if the maximum number of retries is reached.
|
||||
if (nretries++ >= MAXRETRIES)
|
||||
return rc;
|
||||
|
||||
// Restore the state of the progress events.
|
||||
progress->current = saved;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For corrupt dives the end-of-samples pointer is 0xFFFFFFFF
|
||||
@ -547,6 +587,8 @@ cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_
|
||||
static unsigned int
|
||||
cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data)
|
||||
{
|
||||
unsigned int base = device->layout->rb_logbook_begin;
|
||||
|
||||
// We track profile ringbuffer usage to determine which dives have profile data
|
||||
int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin;
|
||||
|
||||
@ -565,10 +607,13 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
|
||||
|
||||
// Remove the pre-dive events that occur after the last dive
|
||||
unsigned int rb_head_ptr = 0;
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
// TM uses SRAM and does not need to erase pages
|
||||
rb_head_ptr = base + array_uint16_be(data->config + device->layout->cf_last_log);
|
||||
else if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
rb_head_ptr = base + (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
else
|
||||
rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
rb_head_ptr = base + (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000;
|
||||
|
||||
unsigned int head_dive = 0, tail_dive = 0;
|
||||
|
||||
@ -582,13 +627,20 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
|
||||
}
|
||||
|
||||
unsigned int last_profile_idx = (device->layout->rb_logbook_entry_count + head_dive - 1) % device->layout->rb_logbook_entry_count;
|
||||
unsigned int last_profile_end = array_uint32_le(data->logbook + last_profile_idx * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end);
|
||||
unsigned int last_profile_pre = 0xFFFFFFFF;
|
||||
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
last_profile_pre = array_uint32_word_be(data->config + device->layout->cf_last_log);
|
||||
unsigned int last_profile_end = 0;
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
// There is no end pointer in this model and no inter-dive
|
||||
// events. We could use profile_begin from the next dive but
|
||||
// since this is the last dive, we'll use rb_head_ptr
|
||||
last_profile_end = rb_head_ptr;
|
||||
else
|
||||
last_profile_pre = array_uint32_le(data->config + device->layout->cf_last_log);
|
||||
last_profile_end = base + array_uint32_le(data->logbook + last_profile_idx * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end);
|
||||
|
||||
unsigned int last_profile_pre = 0xFFFFFFFF;
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
last_profile_pre = base + array_uint32_word_be(data->config + device->layout->cf_last_log);
|
||||
else
|
||||
last_profile_pre = base + array_uint32_le(data->config + device->layout->cf_last_log);
|
||||
|
||||
if (rb_head_ptr > last_profile_end)
|
||||
profile_capacity_remaining -= rb_head_ptr - last_profile_end;
|
||||
@ -606,12 +658,13 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int profile_pre = array_uint32_le(log_entry + device->layout->pt_profile_pre);
|
||||
unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin);
|
||||
unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end);
|
||||
unsigned int profile_pre = 0;
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
profile_pre = base + array_uint16_le(log_entry + device->layout->pt_profile_pre);
|
||||
else
|
||||
profile_pre = base + array_uint32_le(log_entry + device->layout->pt_profile_pre);
|
||||
|
||||
unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre);
|
||||
unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end);
|
||||
last_profile_pre = profile_pre;
|
||||
|
||||
// Determine if sample exists
|
||||
@ -623,7 +676,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
|
||||
data->invalid_profile_dive_num = idx;
|
||||
}
|
||||
// Accumulate read size for progress bar
|
||||
sample_read_size += read_size;
|
||||
sample_read_size += sample_size;
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,69 +684,6 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data)
|
||||
{
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
unsigned int pre_dive_offset = 0, end_dive_offset = 0;
|
||||
|
||||
unsigned int dive_count = 0;
|
||||
if (data->dive_count < device->layout->rb_logbook_entry_count)
|
||||
dive_count = data->dive_count;
|
||||
else
|
||||
dive_count = device->layout->rb_logbook_entry_count;
|
||||
|
||||
// Find lowest and highest offsets into sample data
|
||||
unsigned int low_offset = 0xFFFFFFFF;
|
||||
unsigned int high_offset = 0;
|
||||
|
||||
for (int i = data->fp_dive_num + 1; i < dive_count; i++) {
|
||||
pre_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size
|
||||
+ device->layout->pt_profile_pre);
|
||||
end_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size
|
||||
+ device->layout->pt_profile_end);
|
||||
|
||||
// Validate offsets, allow 0xFFFFFFF for end_dive_offset
|
||||
// because we handle that as a special case.
|
||||
if (pre_dive_offset < device->layout->rb_profile_begin ||
|
||||
pre_dive_offset > device->layout->rb_profile_end) {
|
||||
ERROR(abstract->context, "Invalid pre-dive offset (%08x) on dive %d.", pre_dive_offset, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (end_dive_offset < device->layout->rb_profile_begin ||
|
||||
(end_dive_offset > device->layout->rb_profile_end &&
|
||||
end_dive_offset != 0xFFFFFFFF)) {
|
||||
ERROR(abstract->context, "Invalid end-dive offset (%08x) on dive %d.", end_dive_offset, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for ring buffer wrap-around.
|
||||
if (pre_dive_offset > end_dive_offset)
|
||||
break;
|
||||
|
||||
if (pre_dive_offset < low_offset)
|
||||
low_offset = pre_dive_offset;
|
||||
if (end_dive_offset > high_offset && end_dive_offset != 0xFFFFFFFF )
|
||||
high_offset = end_dive_offset;
|
||||
}
|
||||
|
||||
if (pre_dive_offset > end_dive_offset) {
|
||||
high_offset = device->layout->rb_profile_end;
|
||||
low_offset = device->layout->rb_profile_begin;
|
||||
data->sample_data_offset = low_offset;
|
||||
data->sample_size = high_offset - low_offset;
|
||||
} else if (low_offset < 0xFFFFFFFF && high_offset > 0) {
|
||||
data->sample_data_offset = low_offset;
|
||||
data->sample_size = high_offset - data->sample_data_offset;
|
||||
} else {
|
||||
data->sample_data_offset = 0;
|
||||
data->sample_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name)
|
||||
{
|
||||
@ -735,6 +725,9 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c
|
||||
|
||||
unsigned int model = cochran_commander_get_model(device);
|
||||
switch (model) {
|
||||
case COCHRAN_MODEL_COMMANDER_TM:
|
||||
device->layout = &cochran_cmdr_tm_device_layout;
|
||||
break;
|
||||
case COCHRAN_MODEL_COMMANDER_PRE21000:
|
||||
device->layout = &cochran_cmdr_1_device_layout;
|
||||
break;
|
||||
@ -815,6 +808,8 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char config[1024];
|
||||
unsigned int config_size = sizeof(config);
|
||||
unsigned int size = device->layout->rb_profile_end - device->layout->rb_logbook_begin;
|
||||
|
||||
// Make sure buffer is good.
|
||||
if (!dc_buffer_clear(buffer)) {
|
||||
@ -823,14 +818,17 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
}
|
||||
|
||||
// Reserve space
|
||||
if (!dc_buffer_resize(buffer, device->layout->rb_profile_end)) {
|
||||
if (!dc_buffer_resize(buffer, size)) {
|
||||
ERROR(abstract->context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
config_size = 512;
|
||||
|
||||
// Determine size for progress
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
progress.maximum = sizeof(config) + device->layout->rb_profile_end;
|
||||
progress.maximum = config_size + size;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Emit ID block
|
||||
@ -839,12 +837,12 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
vendor.size = sizeof (device->id);
|
||||
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
|
||||
|
||||
rc = cochran_commander_read_config (device, &progress, config, sizeof(config));
|
||||
rc = cochran_commander_read_config (device, &progress, config, config_size);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the sample data, from 0 to sample end will include logbook
|
||||
rc = cochran_commander_read (device, &progress, 0, dc_buffer_get_data(buffer), device->layout->rb_profile_end);
|
||||
// Read the sample data, logbook and sample data are contiguous
|
||||
rc = cochran_commander_read (device, &progress, device->layout->rb_logbook_begin, dc_buffer_get_data(buffer), size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
return rc;
|
||||
@ -860,6 +858,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
|
||||
const cochran_device_layout_t *layout = device->layout;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_rbstream_t *rbstream = NULL;
|
||||
|
||||
cochran_data_t data;
|
||||
data.logbook = NULL;
|
||||
@ -868,6 +867,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
unsigned int max_config = sizeof(data.config);
|
||||
unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin;
|
||||
unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin;
|
||||
unsigned int base = device->layout->rb_logbook_begin;
|
||||
|
||||
if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
max_config = 512;
|
||||
|
||||
// setup progress indication
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
@ -882,7 +885,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
// Read config
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config));
|
||||
rc = cochran_commander_read_config(device, &progress, data.config, max_config);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -892,9 +895,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
else
|
||||
data.dive_count = array_uint16_be (data.config + layout->cf_dive_count);
|
||||
|
||||
if (data.dive_count == 0)
|
||||
if (data.dive_count == 0) {
|
||||
// No dives to read
|
||||
WARNING(abstract->context, "This dive computer has no recorded dives.");
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (data.dive_count > layout->rb_logbook_entry_count) {
|
||||
data.logbook_size = layout->rb_logbook_entry_count * layout->rb_logbook_entry_size;
|
||||
@ -914,7 +919,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
}
|
||||
|
||||
// Request log book
|
||||
rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size);
|
||||
rc = cochran_commander_read(device, &progress, layout->rb_logbook_begin, data.logbook, data.logbook_size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
status = rc;
|
||||
goto error;
|
||||
@ -922,12 +927,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
// Locate fingerprint, recent dive with invalid profile and calc read size
|
||||
unsigned int profile_read_size = cochran_commander_find_fingerprint(device, &data);
|
||||
|
||||
// Update progress indicator with new maximum
|
||||
progress.maximum -= (max_sample - profile_read_size);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
cochran_commander_get_sample_parms(device, &data);
|
||||
|
||||
// Emit a device info event.
|
||||
dc_event_devinfo_t devinfo;
|
||||
devinfo.model = layout->model;
|
||||
@ -957,6 +961,19 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
// Number of dives to read
|
||||
dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count;
|
||||
|
||||
unsigned int last_start_address = 0;
|
||||
if (layout->endian == ENDIAN_WORD_BE)
|
||||
last_start_address = base + array_uint32_word_be(data.config + layout->cf_last_log );
|
||||
else
|
||||
last_start_address = base + array_uint32_le(data.config + layout->cf_last_log );
|
||||
|
||||
// Create the ringbuffer stream.
|
||||
status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
int invalid_profile_flag = 0;
|
||||
|
||||
// Loop through each dive
|
||||
@ -965,21 +982,39 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
unsigned char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size;
|
||||
|
||||
unsigned int sample_start_address = array_uint32_le (log_entry + layout->pt_profile_begin);
|
||||
unsigned int sample_end_address = array_uint32_le (log_entry + layout->pt_profile_end);
|
||||
unsigned int sample_start_address = 0;
|
||||
unsigned int sample_end_address = 0;
|
||||
if (layout->model == COCHRAN_MODEL_COMMANDER_TM) {
|
||||
sample_start_address = base + array_uint16_le (log_entry + layout->pt_profile_begin);
|
||||
sample_end_address = last_start_address;
|
||||
// Commander TM has SRAM which seems to randomize when they lose power for too long
|
||||
// Check for bad entries.
|
||||
if (sample_start_address < layout->rb_profile_begin || sample_start_address > layout->rb_profile_end ||
|
||||
sample_end_address < layout->rb_profile_begin || sample_end_address > layout->rb_profile_end ||
|
||||
array_uint16_le(log_entry + layout->pt_dive_number) % layout->rb_logbook_entry_count != idx) {
|
||||
ERROR(abstract->context, "Corrupt dive (%d).", idx);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
sample_start_address = base + array_uint32_le (log_entry + layout->pt_profile_begin);
|
||||
sample_end_address = base + array_uint32_le (log_entry + layout->pt_profile_end);
|
||||
}
|
||||
|
||||
int sample_size = 0;
|
||||
int sample_size = 0, pre_size = 0;
|
||||
|
||||
// Determine if profile exists
|
||||
if (idx == data.invalid_profile_dive_num)
|
||||
invalid_profile_flag = 1;
|
||||
|
||||
if (!invalid_profile_flag)
|
||||
if (!invalid_profile_flag) {
|
||||
sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address);
|
||||
pre_size = cochran_commander_profile_size(device, &data, idx, sample_end_address, last_start_address);
|
||||
last_start_address = sample_start_address;
|
||||
}
|
||||
|
||||
// Build dive blob
|
||||
unsigned int dive_size = layout->rb_logbook_entry_size + sample_size;
|
||||
unsigned char *dive = (unsigned char *) malloc(dive_size);
|
||||
unsigned char *dive = (unsigned char *) malloc(dive_size + pre_size);
|
||||
if (dive == NULL) {
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error;
|
||||
@ -989,38 +1024,13 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
// Read profile data
|
||||
if (sample_size) {
|
||||
if (sample_end_address == 0xFFFFFFFF)
|
||||
// Corrupt dive, guess the end address
|
||||
sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx);
|
||||
|
||||
if (sample_start_address <= sample_end_address) {
|
||||
rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size);
|
||||
rc = dc_rbstream_read(rbstream, &progress, dive + layout->rb_logbook_entry_size, sample_size + pre_size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
// It wrapped the buffer, copy two sections
|
||||
unsigned int size = layout->rb_profile_end - sample_start_address;
|
||||
|
||||
rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, size);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = cochran_commander_read_retry (device, &progress, layout->rb_profile_begin, dive + layout->rb_logbook_entry_size + size, sample_end_address - layout->rb_profile_begin);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the sample data.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) {
|
||||
@ -1032,6 +1042,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
}
|
||||
|
||||
error:
|
||||
dc_rbstream_free(rbstream);
|
||||
free(data.logbook);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -31,11 +31,12 @@
|
||||
|
||||
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
|
||||
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 0
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1
|
||||
#define COCHRAN_MODEL_EMC_14 2
|
||||
#define COCHRAN_MODEL_EMC_16 3
|
||||
#define COCHRAN_MODEL_EMC_20 4
|
||||
#define COCHRAN_MODEL_COMMANDER_TM 0
|
||||
#define COCHRAN_MODEL_COMMANDER_PRE21000 1
|
||||
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2
|
||||
#define COCHRAN_MODEL_EMC_14 3
|
||||
#define COCHRAN_MODEL_EMC_16 4
|
||||
#define COCHRAN_MODEL_EMC_20 5
|
||||
|
||||
// Cochran time stamps start at Jan 1, 1992
|
||||
#define COCHRAN_EPOCH 694242000
|
||||
@ -43,6 +44,7 @@
|
||||
#define UNSUPPORTED 0xFFFFFFFF
|
||||
|
||||
typedef enum cochran_sample_format_t {
|
||||
SAMPLE_TM,
|
||||
SAMPLE_CMDR,
|
||||
SAMPLE_EMC,
|
||||
} cochran_sample_format_t;
|
||||
@ -58,6 +60,7 @@ typedef struct cochran_parser_layout_t {
|
||||
cochran_sample_format_t format;
|
||||
unsigned int headersize;
|
||||
unsigned int samplesize;
|
||||
unsigned int pt_sample_interval;
|
||||
cochran_date_encoding_t date_encoding;
|
||||
unsigned int datetime;
|
||||
unsigned int pt_profile_begin;
|
||||
@ -113,10 +116,36 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = {
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_cmdr_tm_parser_layout = {
|
||||
SAMPLE_TM, // format
|
||||
90, // headersize
|
||||
1, // samplesize
|
||||
72, // pt_sample_interval
|
||||
DATE_ENCODING_TICKS, // date_encoding
|
||||
15, // datetime, 4 bytes
|
||||
0, // pt_profile_begin, 4 bytes
|
||||
UNSUPPORTED, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea)
|
||||
0, // pt_profile_pre, 4 bytes
|
||||
83, // start_temp, 1 byte, F
|
||||
UNSUPPORTED, // start_depth, 2 bytes, /4=ft
|
||||
20, // dive_number, 2 bytes
|
||||
UNSUPPORTED, // altitude, 1 byte, /4=kilofeet
|
||||
UNSUPPORTED, // pt_profile_end, 4 bytes
|
||||
UNSUPPORTED, // end_temp, 1 byte F
|
||||
57, // divetime, 2 bytes, minutes
|
||||
49, // max_depth, 2 bytes, /4=ft
|
||||
51, // avg_depth, 2 bytes, /4=ft
|
||||
74, // oxygen, 4 bytes (2 of) 2 bytes, /256=%
|
||||
UNSUPPORTED, // helium, 4 bytes (2 of) 2 bytes, /256=%
|
||||
82, // min_temp, 1 byte, /2+20=F
|
||||
UNSUPPORTED, // max_temp, 1 byte, /2+20=F
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = {
|
||||
SAMPLE_CMDR, // type
|
||||
SAMPLE_CMDR, // format
|
||||
256, // headersize
|
||||
2, // samplesize
|
||||
UNSUPPORTED, // pt_sample_interval
|
||||
DATE_ENCODING_TICKS, // date_encoding
|
||||
8, // datetime, 4 bytes
|
||||
0, // pt_profile_begin, 4 bytes
|
||||
@ -138,9 +167,10 @@ static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = {
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_cmdr_parser_layout = {
|
||||
SAMPLE_CMDR, // type
|
||||
SAMPLE_CMDR, // format
|
||||
256, // headersize
|
||||
2, // samplesize
|
||||
UNSUPPORTED, // pt_sample_interval
|
||||
DATE_ENCODING_MSDHYM, // date_encoding
|
||||
0, // datetime, 6 bytes
|
||||
6, // pt_profile_begin, 4 bytes
|
||||
@ -162,9 +192,10 @@ static const cochran_parser_layout_t cochran_cmdr_parser_layout = {
|
||||
};
|
||||
|
||||
static const cochran_parser_layout_t cochran_emc_parser_layout = {
|
||||
SAMPLE_EMC, // type
|
||||
SAMPLE_EMC, // format
|
||||
512, // headersize
|
||||
3, // samplesize
|
||||
UNSUPPORTED, // pt_sample_interval
|
||||
DATE_ENCODING_SMHDMY, // date_encoding
|
||||
0, // datetime, 6 bytes
|
||||
6, // pt_profile_begin, 4 bytes
|
||||
@ -190,16 +221,20 @@ static const cochran_events_t cochran_events[] = {
|
||||
{0xA9, 1, SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_END}, // Exited PDI mode
|
||||
{0xAB, 5, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling decrease
|
||||
{0xAD, 5, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling increase
|
||||
{0xB5, 1, SAMPLE_EVENT_AIRTIME, SAMPLE_FLAGS_BEGIN}, // Air < 5 mins deco
|
||||
{0xBD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to nomal PO2 setting
|
||||
{0xBE, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling > 60 ft
|
||||
{0xC0, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to FO2 21% mode
|
||||
{0xC1, 1, SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN}, // Ascent rate greater than limit
|
||||
{0xC2, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Low battery warning
|
||||
{0xC3, 1, SAMPLE_EVENT_OLF, SAMPLE_FLAGS_NONE}, // CNS Oxygen toxicity warning
|
||||
{0xC4, 1, SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_NONE}, // Depth exceeds user set point
|
||||
{0xC5, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Entered decompression mode
|
||||
{0xC7, 1, SAMPLE_EVENT_VIOLATION,SAMPLE_FLAGS_BEGIN}, // Entered Gauge mode (e.g. locked out)
|
||||
{0xC8, 1, SAMPLE_EVENT_PO2, SAMPLE_FLAGS_BEGIN}, // PO2 too high
|
||||
{0xCC, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Low Cylinder 1 pressure
|
||||
{0xCE, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Non-decompression warning
|
||||
{0xCF, 1, SAMPLE_EVENT_OLF, SAMPLE_FLAGS_BEGIN}, // O2 Toxicity
|
||||
{0xCD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to deco blend
|
||||
{0xD0, 1, SAMPLE_EVENT_WORKLOAD, SAMPLE_FLAGS_BEGIN}, // Breathing rate alarm
|
||||
{0xD3, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Low gas 1 flow rate
|
||||
@ -334,6 +369,11 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
parser->model = model;
|
||||
|
||||
switch (model) {
|
||||
case COCHRAN_MODEL_COMMANDER_TM:
|
||||
parser->layout = &cochran_cmdr_tm_parser_layout;
|
||||
parser->events = NULL; // No inter-dive events on this model
|
||||
parser->nevents = 0;
|
||||
break;
|
||||
case COCHRAN_MODEL_COMMANDER_PRE21000:
|
||||
parser->layout = &cochran_cmdr_1_parser_layout;
|
||||
parser->events = cochran_cmdr_event_bytes;
|
||||
@ -395,6 +435,7 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat
|
||||
datetime->day = data[layout->datetime + 2];
|
||||
datetime->month = data[layout->datetime + 5];
|
||||
datetime->year = data[layout->datetime + 4] + (data[layout->datetime + 4] > 91 ? 1900 : 2000);
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
break;
|
||||
case DATE_ENCODING_SMHDMY:
|
||||
datetime->second = data[layout->datetime + 0];
|
||||
@ -403,6 +444,7 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat
|
||||
datetime->day = data[layout->datetime + 3];
|
||||
datetime->month = data[layout->datetime + 4];
|
||||
datetime->year = data[layout->datetime + 5] + (data[layout->datetime + 5] > 91 ? 1900 : 2000);
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
break;
|
||||
case DATE_ENCODING_TICKS:
|
||||
ts = array_uint32_le(data + layout->datetime) + COCHRAN_EPOCH;
|
||||
@ -440,6 +482,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
*((unsigned int*) value) = (data[layout->min_temp] / 2.0 + 20 - 32) / 1.8;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_MAXIMUM:
|
||||
if (layout->max_temp == UNSUPPORTED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
if (data[layout->max_temp] == 0xFF)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((unsigned int*) value) = (data[layout->max_temp] / 2.0 + 20 - 32) / 1.8;
|
||||
@ -484,6 +528,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
// for density assume
|
||||
// 0 = 1000kg/m³, 2 = 1025kg/m³
|
||||
// and other values are linear
|
||||
if (layout->water_conductivity == UNSUPPORTED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
if ((data[layout->water_conductivity] & 0x3) == 0)
|
||||
water->type = DC_WATER_FRESH;
|
||||
else
|
||||
@ -493,6 +539,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
// Cochran measures air pressure and stores it as altitude.
|
||||
// Convert altitude (measured in 1/4 kilofeet) back to pressure.
|
||||
if (layout->altitude == UNSUPPORTED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*(double *) value = ATM / BAR * pow(1 - 0.0000225577 * data[layout->altitude] * 250.0 * FEET, 5.25588);
|
||||
break;
|
||||
default:
|
||||
@ -504,8 +552,117 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse early Commander computers
|
||||
*/
|
||||
static dc_status_t
|
||||
cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
|
||||
const cochran_parser_layout_t *layout = parser->layout;
|
||||
const unsigned char *data = abstract->data;
|
||||
const unsigned char *samples = data + layout->headersize;
|
||||
|
||||
if (abstract->size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int size = abstract->size - layout->headersize;
|
||||
unsigned int sample_interval = data[layout->pt_sample_interval];
|
||||
|
||||
dc_sample_value_t sample = {0};
|
||||
unsigned int time = 0, last_sample_time = 0;
|
||||
unsigned int offset = 2;
|
||||
unsigned int deco_ceiling = 0;
|
||||
|
||||
unsigned int temp = samples[0]; // Half degrees F
|
||||
unsigned int depth = samples[1]; // Half feet
|
||||
|
||||
last_sample_time = sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = (depth / 2.0) * FEET;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.temperature = (temp / 2.0 - 32.0) / 1.8;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
|
||||
sample.gasmix = 0;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
|
||||
while (offset < size) {
|
||||
const unsigned char *s = samples + offset;
|
||||
|
||||
sample.time = time;
|
||||
if (last_sample_time != sample.time) {
|
||||
// We haven't issued this time yet.
|
||||
last_sample_time = sample.time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
}
|
||||
|
||||
if (*s & 0x80) {
|
||||
// Event or temperate change byte
|
||||
if (*s & 0x60) {
|
||||
// Event byte
|
||||
switch (*s) {
|
||||
case 0xC5: // Deco obligation begins
|
||||
break;
|
||||
case 0xD8: // Deco obligation ends
|
||||
break;
|
||||
case 0xAB: // Decrement ceiling (deeper)
|
||||
deco_ceiling += 10; // feet
|
||||
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
sample.deco.time = 60; // We don't know the duration
|
||||
sample.deco.depth = deco_ceiling * FEET;
|
||||
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
|
||||
break;
|
||||
case 0xAD: // Increment ceiling (shallower)
|
||||
deco_ceiling -= 10; // feet
|
||||
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
sample.deco.depth = deco_ceiling * FEET;
|
||||
sample.deco.time = 60; // We don't know the duration
|
||||
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
|
||||
break;
|
||||
default:
|
||||
cochran_commander_handle_event(parser, s[0], callback, userdata);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Temp change
|
||||
if (*s & 0x10)
|
||||
temp -= (*s & 0x0f);
|
||||
else
|
||||
temp += (*s & 0x0f);
|
||||
sample.temperature = (temp / 2.0 - 32.0) / 1.8;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
}
|
||||
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Depth sample
|
||||
if (s[0] & 0x40)
|
||||
depth -= s[0] & 0x3f;
|
||||
else
|
||||
depth += s[0] & 0x3f;
|
||||
|
||||
sample.depth = (depth / 2.0) * FEET;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
offset++;
|
||||
time += sample_interval;
|
||||
}
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse Commander I (Pre-21000 s/n), II and EMC computers
|
||||
*/
|
||||
static dc_status_t
|
||||
cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
|
||||
const cochran_parser_layout_t *layout = parser->layout;
|
||||
@ -720,3 +877,15 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
|
||||
|
||||
if (parser->model == COCHRAN_MODEL_COMMANDER_TM)
|
||||
return cochran_commander_parser_samples_foreach_tm (abstract, callback, userdata);
|
||||
else
|
||||
return cochran_commander_parser_samples_foreach_emc (abstract, callback, userdata);
|
||||
}
|
||||
|
||||
@ -75,6 +75,7 @@ static const dc_device_vtable_t cressi_edy_device_vtable = {
|
||||
NULL, /* write */
|
||||
cressi_edy_device_dump, /* dump */
|
||||
cressi_edy_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
cressi_edy_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -115,6 +115,7 @@ cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
datetime->hour = bcd2dec (p[14]);
|
||||
datetime->minute = bcd2dec (p[15]);
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -67,6 +67,7 @@ static const dc_device_vtable_t cressi_leonardo_device_vtable = {
|
||||
NULL, /* write */
|
||||
cressi_leonardo_device_dump, /* dump */
|
||||
cressi_leonardo_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
cressi_leonardo_device_close /* close */
|
||||
};
|
||||
|
||||
@ -448,32 +449,25 @@ cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[]
|
||||
if (size < SZ_MEMORY)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
// Locate the most recent dive.
|
||||
// The device maintains an internal counter which is incremented for every
|
||||
// dive, and the current value at the time of the dive is stored in the
|
||||
// dive header. Thus the most recent dive will have the highest value.
|
||||
unsigned int count = 0;
|
||||
unsigned int latest = 0;
|
||||
unsigned int maximum = 0;
|
||||
for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) {
|
||||
unsigned int offset = RB_LOGBOOK_BEGIN + i * RB_LOGBOOK_SIZE;
|
||||
// Get the number of dives.
|
||||
//unsigned int ndives = array_uint16_le(data + 0x62);
|
||||
|
||||
// Ignore uninitialized header entries.
|
||||
if (array_isequal (data + offset, RB_LOGBOOK_SIZE, 0xFF))
|
||||
break;
|
||||
|
||||
// Get the internal dive number.
|
||||
unsigned int current = array_uint16_le (data + offset);
|
||||
if (current == 0xFFFF) {
|
||||
WARNING (context, "Unexpected internal dive number found.");
|
||||
break;
|
||||
}
|
||||
if (current > maximum) {
|
||||
maximum = current;
|
||||
latest = i;
|
||||
// Get the logbook pointer.
|
||||
unsigned int last = array_uint16_le(data + 0x64);
|
||||
if (last < RB_LOGBOOK_BEGIN || last > RB_LOGBOOK_END ||
|
||||
((last - RB_LOGBOOK_BEGIN) % RB_LOGBOOK_SIZE) != 0) {
|
||||
ERROR (context, "Invalid logbook pointer (0x%04x).", last);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
count++;
|
||||
// Convert to an index.
|
||||
unsigned int latest = (last - RB_LOGBOOK_BEGIN) / RB_LOGBOOK_SIZE;
|
||||
|
||||
// Get the profile pointer.
|
||||
unsigned int eop = array_uint16_le(data + 0x66);
|
||||
if (eop < RB_PROFILE_BEGIN || last > RB_PROFILE_END) {
|
||||
ERROR (context, "Invalid profile pointer (0x%04x).", eop);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned char *buffer = (unsigned char *) malloc (RB_LOGBOOK_SIZE + RB_PROFILE_END - RB_PROFILE_BEGIN);
|
||||
@ -482,12 +476,16 @@ cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[]
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
unsigned int previous = 0;
|
||||
unsigned int previous = eop;
|
||||
unsigned int remaining = RB_PROFILE_END - RB_PROFILE_BEGIN;
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) {
|
||||
unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT;
|
||||
unsigned int offset = RB_LOGBOOK_BEGIN + idx * RB_LOGBOOK_SIZE;
|
||||
|
||||
// Ignore uninitialized header entries.
|
||||
if (array_isequal (data + offset, RB_LOGBOOK_SIZE, 0xFF))
|
||||
break;
|
||||
|
||||
// Get the ringbuffer pointers.
|
||||
unsigned int header = array_uint16_le (data + offset + 2);
|
||||
unsigned int footer = array_uint16_le (data + offset + 4);
|
||||
|
||||
@ -46,7 +46,7 @@ static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract
|
||||
|
||||
static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
|
||||
sizeof(cressi_leonardo_parser_t),
|
||||
DC_FAMILY_CRESSI_EDY,
|
||||
DC_FAMILY_CRESSI_LEONARDO,
|
||||
cressi_leonardo_parser_set_data, /* set_data */
|
||||
cressi_leonardo_parser_get_datetime, /* datetime */
|
||||
cressi_leonardo_parser_get_field, /* fields */
|
||||
@ -100,6 +100,7 @@ cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datet
|
||||
datetime->hour = p[11];
|
||||
datetime->minute = p[12];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -61,6 +61,47 @@ dc_gmtime_r (const time_t *t, struct tm *tm)
|
||||
#endif
|
||||
}
|
||||
|
||||
static time_t
|
||||
dc_timegm (struct tm *tm)
|
||||
{
|
||||
#if defined(HAVE_TIMEGM)
|
||||
return timegm (tm);
|
||||
#elif defined (HAVE__MKGMTIME)
|
||||
return _mkgmtime (tm);
|
||||
#else
|
||||
static const unsigned int mdays[] = {
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
||||
};
|
||||
|
||||
if (tm == NULL ||
|
||||
tm->tm_mon < 0 || tm->tm_mon > 11 ||
|
||||
tm->tm_mday < 1 || tm->tm_mday > 31 ||
|
||||
tm->tm_hour < 0 || tm->tm_hour > 23 ||
|
||||
tm->tm_min < 0 || tm->tm_min > 59 ||
|
||||
tm->tm_sec < 0 || tm->tm_sec > 60)
|
||||
return (time_t) -1;
|
||||
|
||||
/* Number of leap days since 1970-01-01. */
|
||||
int year = tm->tm_year + 1900 - (tm->tm_mon < 2);
|
||||
int leapdays =
|
||||
((year / 4) - (year / 100) + (year / 400)) -
|
||||
((1969 / 4) - (1969 / 100) + (1969 / 400));
|
||||
|
||||
time_t result = 0;
|
||||
result += (tm->tm_year - 70) * 365;
|
||||
result += leapdays;
|
||||
result += mdays[tm->tm_mon];
|
||||
result += tm->tm_mday - 1;
|
||||
result *= 24;
|
||||
result += tm->tm_hour;
|
||||
result *= 60;
|
||||
result += tm->tm_min;
|
||||
result *= 60;
|
||||
result += tm->tm_sec;
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
dc_ticks_t
|
||||
dc_datetime_now (void)
|
||||
{
|
||||
@ -72,11 +113,23 @@ dc_datetime_localtime (dc_datetime_t *result,
|
||||
dc_ticks_t ticks)
|
||||
{
|
||||
time_t t = ticks;
|
||||
int offset = 0;
|
||||
|
||||
struct tm tm;
|
||||
if (dc_localtime_r (&t, &tm) == NULL)
|
||||
return NULL;
|
||||
|
||||
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
||||
offset = tm.tm_gmtoff;
|
||||
#else
|
||||
struct tm tmp = tm;
|
||||
time_t t_local = dc_timegm (&tmp);
|
||||
if (t_local == (time_t) -1)
|
||||
return NULL;
|
||||
|
||||
offset = t_local - t;
|
||||
#endif
|
||||
|
||||
if (result) {
|
||||
result->year = tm.tm_year + 1900;
|
||||
result->month = tm.tm_mon + 1;
|
||||
@ -84,6 +137,7 @@ dc_datetime_localtime (dc_datetime_t *result,
|
||||
result->hour = tm.tm_hour;
|
||||
result->minute = tm.tm_min;
|
||||
result->second = tm.tm_sec;
|
||||
result->timezone = offset;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -106,6 +160,7 @@ dc_datetime_gmtime (dc_datetime_t *result,
|
||||
result->hour = tm.tm_hour;
|
||||
result->minute = tm.tm_min;
|
||||
result->second = tm.tm_sec;
|
||||
result->timezone = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -124,7 +179,15 @@ dc_datetime_mktime (const dc_datetime_t *dt)
|
||||
tm.tm_hour = dt->hour;
|
||||
tm.tm_min = dt->minute;
|
||||
tm.tm_sec = dt->second;
|
||||
tm.tm_isdst = -1;
|
||||
tm.tm_isdst = 0;
|
||||
|
||||
return mktime (&tm);
|
||||
time_t t = dc_timegm (&tm);
|
||||
if (t == (time_t) -1)
|
||||
return t;
|
||||
|
||||
if (dt->timezone != DC_TIMEZONE_NONE) {
|
||||
t -= dt->timezone;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -23,9 +23,9 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(HAVE_HIDAPI)
|
||||
#define USBHID
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#define USBHID
|
||||
#endif
|
||||
|
||||
@ -219,6 +219,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"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},
|
||||
@ -256,8 +257,8 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"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 3+", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT
|
||||
{"Heinrichs Weikamp", "OSTC 3+", DC_FAMILY_HW_OSTC3, 0x1A}, // FTDI // BT
|
||||
{"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
|
||||
@ -316,11 +317,12 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44},
|
||||
{"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45},
|
||||
/* Cochran Commander */
|
||||
{"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||
{"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 1},
|
||||
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 2},
|
||||
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 3},
|
||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 4},
|
||||
{"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},
|
||||
};
|
||||
|
||||
typedef struct dc_descriptor_iterator_t {
|
||||
|
||||
@ -71,6 +71,8 @@ struct dc_device_vtable_t {
|
||||
|
||||
dc_status_t (*foreach) (dc_device_t *device, dc_dive_callback_t callback, void *userdata);
|
||||
|
||||
dc_status_t (*timesync) (dc_device_t *device, const dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t (*close) (dc_device_t *device);
|
||||
};
|
||||
|
||||
|
||||
13
src/device.c
13
src/device.c
@ -370,6 +370,19 @@ dc_device_foreach (dc_device_t *device, dc_dive_callback_t callback, void *userd
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_device_timesync (dc_device_t *device, const dc_datetime_t *datetime)
|
||||
{
|
||||
if (device == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
if (device->vtable->timesync == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
return device->vtable->timesync (device, datetime);
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_device_close (dc_device_t *device)
|
||||
{
|
||||
|
||||
@ -69,6 +69,7 @@ static const dc_device_vtable_t diverite_nitekq_device_vtable = {
|
||||
NULL, /* write */
|
||||
diverite_nitekq_device_dump, /* dump */
|
||||
diverite_nitekq_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
diverite_nitekq_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datet
|
||||
datetime->hour = p[3];
|
||||
datetime->minute = p[4];
|
||||
datetime->second = p[5];
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -86,6 +86,7 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = {
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
divesystem_idive_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
divesystem_idive_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -173,6 +173,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
}
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -191,7 +192,15 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||
break;
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
if (parser->model >= IX3M_EASY) {
|
||||
*((double *) value) = array_uint16_le (data + 11) / 10000.0;
|
||||
} else {
|
||||
*((double *) value) = array_uint16_le (data + 11) / 1000.0;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_SALINITY:
|
||||
water->type = data[34] == 0 ? DC_WATER_SALT : DC_WATER_FRESH;
|
||||
water->density = 0.0;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
if (parser->divemode == 0xFFFFFFFF)
|
||||
|
||||
@ -61,6 +61,7 @@ typedef struct hw_frog_device_t {
|
||||
|
||||
static dc_status_t hw_frog_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t hw_frog_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t hw_frog_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
static dc_status_t hw_frog_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t hw_frog_device_vtable = {
|
||||
@ -71,6 +72,7 @@ static const dc_device_vtable_t hw_frog_device_vtable = {
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
hw_frog_device_foreach, /* foreach */
|
||||
hw_frog_device_timesync, /* timesync */
|
||||
hw_frog_device_close /* close */
|
||||
};
|
||||
|
||||
@ -482,14 +484,11 @@ hw_frog_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
hw_frog_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
static dc_status_t
|
||||
hw_frog_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
hw_frog_device_t *device = (hw_frog_device_t *) abstract;
|
||||
|
||||
if (!ISINSTANCE (abstract))
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -70,6 +70,7 @@ typedef struct hw_ostc_firmware_t {
|
||||
static dc_status_t hw_ostc_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
|
||||
static dc_status_t hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t hw_ostc_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
static dc_status_t hw_ostc_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t hw_ostc_device_vtable = {
|
||||
@ -80,6 +81,7 @@ static const dc_device_vtable_t hw_ostc_device_vtable = {
|
||||
NULL, /* write */
|
||||
hw_ostc_device_dump, /* dump */
|
||||
hw_ostc_device_foreach, /* foreach */
|
||||
hw_ostc_device_timesync, /* timesync */
|
||||
hw_ostc_device_close /* close */
|
||||
};
|
||||
|
||||
@ -377,15 +379,12 @@ hw_ostc_device_md2hash (dc_device_t *abstract, unsigned char data[], unsigned in
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
hw_ostc_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
static dc_status_t
|
||||
hw_ostc_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
hw_ostc_device_t *device = (hw_ostc_device_t *) abstract;
|
||||
|
||||
if (!ISINSTANCE (abstract))
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -129,6 +129,7 @@ static dc_status_t hw_ostc3_device_read (dc_device_t *abstract, unsigned int add
|
||||
static dc_status_t hw_ostc3_device_write (dc_device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
|
||||
static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
static dc_status_t hw_ostc3_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t hw_ostc3_device_vtable = {
|
||||
@ -139,6 +140,7 @@ static const dc_device_vtable_t hw_ostc3_device_vtable = {
|
||||
hw_ostc3_device_write, /* write */
|
||||
hw_ostc3_device_dump, /* dump */
|
||||
hw_ostc3_device_foreach, /* foreach */
|
||||
hw_ostc3_device_timesync, /* timesync */
|
||||
hw_ostc3_device_close /* close */
|
||||
};
|
||||
|
||||
@ -801,14 +803,11 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
hw_ostc3_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
static dc_status_t
|
||||
hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract;
|
||||
|
||||
if (!ISINSTANCE (abstract))
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -432,6 +432,7 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
dt.hour = p[3];
|
||||
dt.minute = p[4];
|
||||
dt.second = 0;
|
||||
dt.timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
if (version == 0x24) {
|
||||
if (datetime)
|
||||
@ -443,8 +444,10 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
|
||||
ticks -= divetime;
|
||||
|
||||
if (!dc_datetime_localtime (datetime, ticks))
|
||||
if (!dc_datetime_gmtime (datetime, ticks))
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -57,6 +57,7 @@ dc_device_read
|
||||
dc_device_set_cancel
|
||||
dc_device_set_events
|
||||
dc_device_set_fingerprint
|
||||
dc_device_timesync
|
||||
dc_device_write
|
||||
|
||||
oceanic_atom2_device_version
|
||||
@ -80,19 +81,16 @@ suunto_eon_device_write_name
|
||||
suunto_vyper2_device_version
|
||||
suunto_vyper2_device_reset_maxdepth
|
||||
hw_ostc_device_md2hash
|
||||
hw_ostc_device_clock
|
||||
hw_ostc_device_eeprom_read
|
||||
hw_ostc_device_eeprom_write
|
||||
hw_ostc_device_reset
|
||||
hw_ostc_device_screenshot
|
||||
hw_ostc_device_fwupdate
|
||||
hw_frog_device_version
|
||||
hw_frog_device_clock
|
||||
hw_frog_device_display
|
||||
hw_frog_device_customtext
|
||||
hw_ostc3_device_version
|
||||
hw_ostc3_device_hardware
|
||||
hw_ostc3_device_clock
|
||||
hw_ostc3_device_display
|
||||
hw_ostc3_device_customtext
|
||||
hw_ostc3_device_config_read
|
||||
|
||||
@ -70,6 +70,7 @@ static const dc_device_vtable_t mares_darwin_device_vtable = {
|
||||
NULL, /* write */
|
||||
mares_darwin_device_dump, /* dump */
|
||||
mares_darwin_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
mares_darwin_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ mares_darwin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
datetime->hour = p[4];
|
||||
datetime->minute = p[5];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -87,6 +87,7 @@ static const dc_device_vtable_t mares_iconhd_device_vtable = {
|
||||
NULL, /* write */
|
||||
mares_iconhd_device_dump, /* dump */
|
||||
mares_iconhd_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
mares_iconhd_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -329,6 +329,7 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
datetime->day = array_uint16_le (p + 4);
|
||||
datetime->month = array_uint16_le (p + 6) + 1;
|
||||
datetime->year = array_uint16_le (p + 8) + 1900;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -62,6 +62,7 @@ static const dc_device_vtable_t mares_nemo_device_vtable = {
|
||||
NULL, /* write */
|
||||
mares_nemo_device_dump, /* dump */
|
||||
mares_nemo_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
mares_nemo_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -200,6 +200,7 @@ mares_nemo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
datetime->hour = p[3];
|
||||
datetime->minute = p[4];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -57,6 +57,7 @@ static const dc_device_vtable_t mares_puck_device_vtable = {
|
||||
NULL, /* write */
|
||||
mares_puck_device_dump, /* dump */
|
||||
mares_puck_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
mares_puck_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ static const oceanic_common_device_vtable_t oceanic_atom2_device_vtable = {
|
||||
oceanic_atom2_device_write, /* write */
|
||||
oceanic_common_device_dump, /* dump */
|
||||
oceanic_common_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
oceanic_atom2_device_close /* close */
|
||||
},
|
||||
oceanic_common_device_logbook,
|
||||
@ -107,6 +108,7 @@ static const oceanic_common_version_t oceanic_atom2a_version[] = {
|
||||
{"PROPLUS2 \0\0 512K"},
|
||||
{"OCEGEO20 \0\0 512K"},
|
||||
{"OCE GEO R\0\0 512K"},
|
||||
{"AQUAI200 \0\0 512K"},
|
||||
};
|
||||
|
||||
static const oceanic_common_version_t oceanic_atom2b_version[] = {
|
||||
|
||||
@ -86,6 +86,7 @@
|
||||
#define I750TC 0x455A
|
||||
#define I450T 0x4641
|
||||
#define I550 0x4642
|
||||
#define I200 0x4646
|
||||
|
||||
#define NORMAL 0
|
||||
#define GAUGE 1
|
||||
@ -157,7 +158,8 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
|
||||
model == OCS || model == PROPLUS3 ||
|
||||
model == A300 || model == MANTA ||
|
||||
model == INSIGHT2 || model == ZEN ||
|
||||
model == I300 || model == I550) {
|
||||
model == I300 || model == I550 ||
|
||||
model == I200) {
|
||||
parser->headersize -= PAGESIZE;
|
||||
} else if (model == VT4 || model == VT41) {
|
||||
parser->headersize += PAGESIZE;
|
||||
@ -270,6 +272,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
||||
case COMPUMASK:
|
||||
case INSIGHT2:
|
||||
case I300:
|
||||
case I200:
|
||||
datetime->year = ((p[3] & 0xE0) >> 1) + (p[4] & 0x0F) + 2000;
|
||||
datetime->month = (p[4] & 0xF0) >> 4;
|
||||
datetime->day = p[3] & 0x1F;
|
||||
@ -325,6 +328,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
||||
break;
|
||||
}
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
// Convert to a 24-hour clock.
|
||||
datetime->hour %= 12;
|
||||
@ -704,7 +708,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == ELEMENT2 || parser->model == VEO20 ||
|
||||
parser->model == A300 || parser->model == ZEN ||
|
||||
parser->model == GEO || parser->model == GEO20 ||
|
||||
parser->model == MANTA || parser->model == I300) {
|
||||
parser->model == MANTA || parser->model == I300 ||
|
||||
parser->model == I200) {
|
||||
have_pressure = 0;
|
||||
}
|
||||
|
||||
@ -858,7 +863,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == VEO30 || parser->model == OC1A ||
|
||||
parser->model == OC1B || parser->model == OC1C ||
|
||||
parser->model == OCI || parser->model == A300 ||
|
||||
parser->model == I450T || parser->model == I300) {
|
||||
parser->model == I450T || parser->model == I300 ||
|
||||
parser->model == I200) {
|
||||
temperature = data[offset + 3];
|
||||
} else if (parser->model == OCS || parser->model == TX1) {
|
||||
temperature = data[offset + 1];
|
||||
@ -925,7 +931,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == VEO30 || parser->model == OC1A ||
|
||||
parser->model == OC1B || parser->model == OC1C ||
|
||||
parser->model == OCI || parser->model == A300 ||
|
||||
parser->model == I450T || parser->model == I300)
|
||||
parser->model == I450T || parser->model == I300 ||
|
||||
parser->model == I200)
|
||||
depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF;
|
||||
else if (parser->model == ATOM1)
|
||||
depth = data[offset + 3] * 16;
|
||||
@ -972,6 +979,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
decostop = (data[offset + 5] & 0xF0) >> 4;
|
||||
decotime = array_uint16_le(data + offset + 4) & 0x03FF;
|
||||
have_deco = 1;
|
||||
} else if (parser->model == I200) {
|
||||
decostop = (data[offset + 7] & 0xF0) >> 4;
|
||||
decotime = array_uint16_le(data + offset + 6) & 0x0FFF;
|
||||
have_deco = 1;
|
||||
}
|
||||
if (have_deco) {
|
||||
if (decostop) {
|
||||
|
||||
@ -56,6 +56,7 @@ static const oceanic_common_device_vtable_t oceanic_veo250_device_vtable = {
|
||||
NULL, /* write */
|
||||
oceanic_common_device_dump, /* dump */
|
||||
oceanic_common_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
oceanic_veo250_device_close /* close */
|
||||
},
|
||||
oceanic_common_device_logbook,
|
||||
|
||||
@ -121,6 +121,7 @@ oceanic_veo250_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dateti
|
||||
datetime->hour = p[3];
|
||||
datetime->minute = p[2];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
if (parser->model == VEO200 || parser->model == VEO250)
|
||||
datetime->year += 3;
|
||||
|
||||
@ -68,6 +68,7 @@ static const oceanic_common_device_vtable_t oceanic_vtpro_device_vtable = {
|
||||
NULL, /* write */
|
||||
oceanic_common_device_dump, /* dump */
|
||||
oceanic_common_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
oceanic_vtpro_device_close /* close */
|
||||
},
|
||||
oceanic_vtpro_device_logbook,
|
||||
|
||||
@ -135,6 +135,7 @@ oceanic_vtpro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
||||
}
|
||||
datetime->minute = bcd2dec (p[0]);
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
// Convert to a 24-hour clock.
|
||||
datetime->hour %= 12;
|
||||
|
||||
@ -57,6 +57,7 @@ static const dc_device_vtable_t reefnet_sensus_device_vtable = {
|
||||
NULL, /* write */
|
||||
reefnet_sensus_device_dump, /* dump */
|
||||
reefnet_sensus_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
reefnet_sensus_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ static const dc_device_vtable_t reefnet_sensuspro_device_vtable = {
|
||||
NULL, /* write */
|
||||
reefnet_sensuspro_device_dump, /* dump */
|
||||
reefnet_sensuspro_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
reefnet_sensuspro_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -65,6 +65,7 @@ static const dc_device_vtable_t reefnet_sensusultra_device_vtable = {
|
||||
NULL, /* write */
|
||||
reefnet_sensusultra_device_dump, /* dump */
|
||||
reefnet_sensusultra_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
reefnet_sensusultra_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -51,6 +51,7 @@ static const dc_device_vtable_t scubapro_g2_device_vtable = {
|
||||
NULL, /* write */
|
||||
scubapro_g2_device_dump, /* dump */
|
||||
scubapro_g2_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
scubapro_g2_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ static const dc_device_vtable_t shearwater_petrel_device_vtable = {
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
shearwater_petrel_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
shearwater_petrel_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -57,6 +57,7 @@ static const dc_device_vtable_t shearwater_predator_device_vtable = {
|
||||
NULL, /* write */
|
||||
shearwater_predator_device_dump, /* dump */
|
||||
shearwater_predator_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
shearwater_predator_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -200,6 +200,8 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d
|
||||
if (!dc_datetime_gmtime (datetime, ticks))
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +58,7 @@ static const suunto_common2_device_vtable_t suunto_d9_device_vtable = {
|
||||
suunto_common2_device_write, /* write */
|
||||
suunto_common2_device_dump, /* dump */
|
||||
suunto_common2_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_d9_device_close /* close */
|
||||
},
|
||||
suunto_d9_device_packet
|
||||
|
||||
@ -320,6 +320,7 @@ suunto_d9_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
datetime->month = p[5];
|
||||
datetime->day = p[6];
|
||||
}
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -51,6 +51,7 @@ static const dc_device_vtable_t suunto_eon_device_vtable = {
|
||||
NULL, /* write */
|
||||
suunto_eon_device_dump, /* dump */
|
||||
suunto_eon_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_eon_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -178,6 +178,7 @@ suunto_eon_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
datetime->minute = bcd2dec (p[4]);
|
||||
}
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -81,6 +81,7 @@ static const dc_device_vtable_t suunto_eonsteel_device_vtable = {
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
suunto_eonsteel_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_eonsteel_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -1071,7 +1071,11 @@ suunto_eonsteel_parser_get_datetime(dc_parser_t *parser, dc_datetime_t *datetime
|
||||
if (parser->size < 4)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
dc_datetime_gmtime(datetime, array_uint32_le(parser->data));
|
||||
if (!dc_datetime_gmtime(datetime, array_uint32_le(parser->data)))
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,7 @@ static const dc_device_vtable_t suunto_solution_device_vtable = {
|
||||
NULL, /* write */
|
||||
suunto_solution_device_dump, /* dump */
|
||||
suunto_solution_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_solution_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -63,6 +63,7 @@ static const dc_device_vtable_t suunto_vyper_device_vtable = {
|
||||
suunto_vyper_device_write, /* write */
|
||||
suunto_vyper_device_dump, /* dump */
|
||||
suunto_vyper_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_vyper_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ static const suunto_common2_device_vtable_t suunto_vyper2_device_vtable = {
|
||||
suunto_common2_device_write, /* write */
|
||||
suunto_common2_device_dump, /* dump */
|
||||
suunto_common2_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
suunto_vyper2_device_close /* close */
|
||||
},
|
||||
suunto_vyper2_device_packet
|
||||
|
||||
@ -223,6 +223,7 @@ suunto_vyper_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
datetime->hour = p[3];
|
||||
datetime->minute = p[4];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
84
src/usbhid.c
84
src/usbhid.c
@ -25,14 +25,20 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(HAVE_HIDAPI)
|
||||
#define USE_HIDAPI
|
||||
#define USBHID
|
||||
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#define USE_LIBUSB
|
||||
#define USBHID
|
||||
#endif
|
||||
|
||||
#if defined(USE_LIBUSB)
|
||||
#ifdef _WIN32
|
||||
#define NOGDI
|
||||
#endif
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#define USBHID
|
||||
#elif defined(USE_HIDAPI)
|
||||
#include <hidapi/hidapi.h>
|
||||
#endif
|
||||
|
||||
@ -44,20 +50,20 @@ struct dc_usbhid_t {
|
||||
/* Library context. */
|
||||
dc_context_t *context;
|
||||
/* Internal state. */
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
libusb_context *ctx;
|
||||
libusb_device_handle *handle;
|
||||
int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
unsigned int timeout;
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
hid_device *handle;
|
||||
int timeout;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
static dc_status_t
|
||||
syserror(int errcode)
|
||||
{
|
||||
@ -94,6 +100,20 @@ usbhid_packet_read(dc_custom_io_t *io, void* data, size_t size, size_t *actual)
|
||||
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)
|
||||
{
|
||||
@ -164,7 +184,7 @@ dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsi
|
||||
// Library context.
|
||||
usbhid->context = context;
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
struct libusb_device **devices = NULL;
|
||||
struct libusb_config_descriptor *config = NULL;
|
||||
|
||||
@ -296,7 +316,7 @@ dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsi
|
||||
libusb_free_config_descriptor (config);
|
||||
libusb_free_device_list (devices, 1);
|
||||
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
|
||||
// Initialize the hidapi library.
|
||||
rc = hid_init();
|
||||
@ -321,7 +341,7 @@ dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsi
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
error_usb_close:
|
||||
libusb_close (usbhid->handle);
|
||||
error_usb_free_config:
|
||||
@ -330,7 +350,7 @@ error_usb_free_list:
|
||||
libusb_free_device_list (devices, 1);
|
||||
error_usb_exit:
|
||||
libusb_exit (usbhid->ctx);
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
error_hid_exit:
|
||||
hid_exit ();
|
||||
#endif
|
||||
@ -351,11 +371,11 @@ dc_usbhid_close (dc_usbhid_t *usbhid)
|
||||
if (usbhid == NULL)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
libusb_release_interface (usbhid->handle, usbhid->interface);
|
||||
libusb_close (usbhid->handle);
|
||||
libusb_exit (usbhid->ctx);
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
hid_close(usbhid->handle);
|
||||
hid_exit();
|
||||
#endif
|
||||
@ -376,7 +396,7 @@ dc_usbhid_set_timeout (dc_usbhid_t *usbhid, int timeout)
|
||||
|
||||
INFO (usbhid->context, "Timeout: value=%i", timeout);
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
if (timeout < 0) {
|
||||
usbhid->timeout = 0;
|
||||
} else if (timeout == 0) {
|
||||
@ -384,7 +404,7 @@ dc_usbhid_set_timeout (dc_usbhid_t *usbhid, int timeout)
|
||||
} else {
|
||||
usbhid->timeout = timeout;
|
||||
}
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
if (timeout < 0) {
|
||||
usbhid->timeout = -1;
|
||||
} else {
|
||||
@ -410,7 +430,7 @@ dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual)
|
||||
goto out_invalidargs;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
#if defined(USE_LIBUSB)
|
||||
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_in, data, size, &nbytes, usbhid->timeout);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (usbhid->context, "Usb read interrupt transfer failed (%s).",
|
||||
@ -418,11 +438,12 @@ dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual)
|
||||
status = syserror (rc);
|
||||
goto out;
|
||||
}
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
#elif defined(USE_HIDAPI)
|
||||
nbytes = hid_read_timeout(usbhid->handle, data, size, usbhid->timeout);
|
||||
if (nbytes < 0) {
|
||||
ERROR (usbhid->context, "Usb read interrupt transfer failed.");
|
||||
status = DC_STATUS_IO;
|
||||
nbytes = 0;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
@ -452,21 +473,46 @@ dc_usbhid_write (dc_usbhid_t *usbhid, const void *data, size_t size, size_t *act
|
||||
goto out_invalidargs;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_out, (void *) data, size, &nbytes, 0);
|
||||
if (size == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if defined(USE_LIBUSB)
|
||||
const unsigned char *buffer = (const unsigned char *) data;
|
||||
size_t length = size;
|
||||
|
||||
// Skip a report id of zero.
|
||||
unsigned char report = buffer[0];
|
||||
if (report == 0) {
|
||||
buffer++;
|
||||
length--;
|
||||
}
|
||||
|
||||
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_out, (void *) buffer, length, &nbytes, 0);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (usbhid->context, "Usb write interrupt transfer failed (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto out;
|
||||
}
|
||||
#elif defined(HAVE_HIDAPI)
|
||||
|
||||
if (report == 0) {
|
||||
nbytes++;
|
||||
}
|
||||
#elif defined(USE_HIDAPI)
|
||||
nbytes = hid_write(usbhid->handle, data, size);
|
||||
if (nbytes < 0) {
|
||||
ERROR (usbhid->context, "Usb write interrupt transfer failed.");
|
||||
status = DC_STATUS_IO;
|
||||
nbytes = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (nbytes > size) {
|
||||
nbytes = size;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
out:
|
||||
|
||||
@ -62,6 +62,7 @@ static const dc_device_vtable_t uwatec_aladin_device_vtable = {
|
||||
NULL, /* write */
|
||||
uwatec_aladin_device_dump, /* dump */
|
||||
uwatec_aladin_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
uwatec_aladin_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -58,6 +58,7 @@ static const dc_device_vtable_t uwatec_memomouse_device_vtable = {
|
||||
NULL, /* write */
|
||||
uwatec_memomouse_device_dump, /* dump */
|
||||
uwatec_memomouse_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
uwatec_memomouse_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ static const dc_device_vtable_t uwatec_meridian_device_vtable = {
|
||||
NULL, /* write */
|
||||
uwatec_meridian_device_dump, /* dump */
|
||||
uwatec_meridian_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
uwatec_meridian_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ static const dc_device_vtable_t uwatec_smart_device_vtable = {
|
||||
NULL, /* write */
|
||||
uwatec_smart_device_dump, /* dump */
|
||||
uwatec_smart_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
uwatec_smart_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
@ -710,6 +710,8 @@ uwatec_smart_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
|
||||
if (!dc_datetime_gmtime (datetime, ticks))
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
datetime->timezone = utc_offset * 900;
|
||||
} else {
|
||||
// For devices without timezone support, the current timezone of
|
||||
// the host system is used.
|
||||
|
||||
@ -64,6 +64,7 @@ static const dc_device_vtable_t zeagle_n2ition3_device_vtable = {
|
||||
NULL, /* write */
|
||||
zeagle_n2ition3_device_dump, /* dump */
|
||||
zeagle_n2ition3_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
zeagle_n2ition3_device_close /* close */
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user