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:
Linus Torvalds 2017-08-19 13:51:56 -07:00
commit 928be1f45b
71 changed files with 822 additions and 278 deletions

View File

@ -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.

View File

@ -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 \

View File

@ -63,6 +63,7 @@ static const dctool_command_t *g_commands[] = {
&dctool_parse,
&dctool_read,
&dctool_write,
&dctool_timesync,
&dctool_fwupdate,
NULL
};

View File

@ -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
View 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
};

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 */
};

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);
};

View File

@ -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)
{

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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[] = {

View File

@ -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) {

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;
}

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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.

View File

@ -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 */
};