diff --git a/configure.ac b/configure.ac index ad8fd06..cdb0f83 100644 --- a/configure.ac +++ b/configure.ac @@ -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 +]) + # 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. diff --git a/examples/Makefile.am b/examples/Makefile.am index e830769..6b70f99 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -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 \ diff --git a/examples/dctool.c b/examples/dctool.c index df7fe01..f62b13c 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -63,6 +63,7 @@ static const dctool_command_t *g_commands[] = { &dctool_parse, &dctool_read, &dctool_write, + &dctool_timesync, &dctool_fwupdate, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index cc6a393..06e58a8 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -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 * diff --git a/examples/dctool_timesync.c b/examples/dctool_timesync.c new file mode 100644 index 0000000..3a59af5 --- /dev/null +++ b/examples/dctool_timesync.c @@ -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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#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 +}; diff --git a/examples/output_xml.c b/examples/output_xml.c index 45c95c2..237f929 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -230,9 +230,16 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u goto cleanup; } - fprintf (output->ostream, "%04i-%02i-%02i %02i:%02i:%02i\n", - dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second); + if (dt.timezone == DC_TIMEZONE_NONE) { + fprintf (output->ostream, "%04i-%02i-%02i %02i:%02i:%02i\n", + dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second); + } else { + fprintf (output->ostream, "%04i-%02i-%02i %02i:%02i:%02i %+03i:%02i\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"); diff --git a/include/libdivecomputer/datetime.h b/include/libdivecomputer/datetime.h index d8f596b..768883d 100644 --- a/include/libdivecomputer/datetime.h +++ b/include/libdivecomputer/datetime.h @@ -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 diff --git a/include/libdivecomputer/device.h b/include/libdivecomputer/device.h index 7ba4bd6..f6d415c 100644 --- a/include/libdivecomputer/device.h +++ b/include/libdivecomputer/device.h @@ -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); diff --git a/include/libdivecomputer/hw_frog.h b/include/libdivecomputer/hw_frog.h index 67f624f..0af10ca 100644 --- a/include/libdivecomputer/hw_frog.h +++ b/include/libdivecomputer/hw_frog.h @@ -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); diff --git a/include/libdivecomputer/hw_ostc.h b/include/libdivecomputer/hw_ostc.h index b68e083..b7daca7 100644 --- a/include/libdivecomputer/hw_ostc.h +++ b/include/libdivecomputer/hw_ostc.h @@ -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); diff --git a/include/libdivecomputer/hw_ostc3.h b/include/libdivecomputer/hw_ostc3.h index bd0d2ed..c69dd93 100644 --- a/include/libdivecomputer/hw_ostc3.h +++ b/include/libdivecomputer/hw_ostc3.h @@ -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); diff --git a/src/atomics_cobalt.c b/src/atomics_cobalt.c index 91a6da6..7c7dccb 100644 --- a/src/atomics_cobalt.c +++ b/src/atomics_cobalt.c @@ -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 */ }; diff --git a/src/atomics_cobalt_parser.c b/src/atomics_cobalt_parser.c index 76b9531..a55e672 100644 --- a/src/atomics_cobalt_parser.c +++ b/src/atomics_cobalt_parser.c @@ -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; diff --git a/src/citizen_aqualand.c b/src/citizen_aqualand.c index 5e111da..47ba87f 100644 --- a/src/citizen_aqualand.c +++ b/src/citizen_aqualand.c @@ -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 */ }; diff --git a/src/citizen_aqualand_parser.c b/src/citizen_aqualand_parser.c index f89eacf..319dcf7 100644 --- a/src/citizen_aqualand_parser.c +++ b/src/citizen_aqualand_parser.c @@ -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; diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 1ad38f0..b515909 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -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,15 +488,36 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t break; case 24: // Commander uses 24 byte addressing - command[0] = 0x15; - command[1] = (address ) & 0xff; - command[2] = (address >> 8) & 0xff; - command[3] = (address >> 16) & 0xff; - command[4] = (size ) & 0xff; - command[5] = (size >> 8 ) & 0xff; - command[6] = (size >> 16 ) & 0xff; - command[7] = 0x04; - command_size = 8; + 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; + command[3] = (address >> 16) & 0xff; + command[4] = (size ) & 0xff; + command[5] = (size >> 8 ) & 0xff; + 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,37 +1024,12 @@ 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); - 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; - } + 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; } } @@ -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; } diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 5a0ad55..12e8b42 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -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); +} diff --git a/src/cressi_edy.c b/src/cressi_edy.c index 226f32f..68e13fc 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -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 */ }; diff --git a/src/cressi_edy_parser.c b/src/cressi_edy_parser.c index c9e1d91..eaf55c3 100644 --- a/src/cressi_edy_parser.c +++ b/src/cressi_edy_parser.c @@ -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; diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index 5c1dd29..f21a223 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -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 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; + } - // 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; - } + // Convert to an index. + unsigned int latest = (last - RB_LOGBOOK_BEGIN) / RB_LOGBOOK_SIZE; - count++; + // 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); diff --git a/src/cressi_leonardo_parser.c b/src/cressi_leonardo_parser.c index 6032376..d7cf0de 100644 --- a/src/cressi_leonardo_parser.c +++ b/src/cressi_leonardo_parser.c @@ -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; diff --git a/src/datetime.c b/src/datetime.c index 9d07f8c..3ceed42 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -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; } diff --git a/src/descriptor.c b/src/descriptor.c index 30e4ced..d4bab60 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -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 { diff --git a/src/device-private.h b/src/device-private.h index dcf0b96..7ca6610 100644 --- a/src/device-private.h +++ b/src/device-private.h @@ -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); }; diff --git a/src/device.c b/src/device.c index 9a92966..0a9e2ba 100644 --- a/src/device.c +++ b/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) { diff --git a/src/diverite_nitekq.c b/src/diverite_nitekq.c index 205115a..8c8c442 100644 --- a/src/diverite_nitekq.c +++ b/src/diverite_nitekq.c @@ -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 */ }; diff --git a/src/diverite_nitekq_parser.c b/src/diverite_nitekq_parser.c index 6c7a05f..dce40d8 100644 --- a/src/diverite_nitekq_parser.c +++ b/src/diverite_nitekq_parser.c @@ -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; diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index d26e2b4..8a8f1ae 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -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 */ }; diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index 473dc57..ff9f480 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -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: - *((double *) value) = array_uint16_le (data + 11) / 1000.0; + 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) diff --git a/src/hw_frog.c b/src/hw_frog.c index d8b89ca..83ee998 100644 --- a/src/hw_frog.c +++ b/src/hw_frog.c @@ -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; diff --git a/src/hw_ostc.c b/src/hw_ostc.c index 3f46603..87c2b1d 100644 --- a/src/hw_ostc.c +++ b/src/hw_ostc.c @@ -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; diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index e7b7d9e..a1a5efd 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -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; diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 2b97cf9..78c2c5d 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -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; diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index ba12d56..62f13c3 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -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 diff --git a/src/mares_darwin.c b/src/mares_darwin.c index 9ac56ba..4b1bc24 100644 --- a/src/mares_darwin.c +++ b/src/mares_darwin.c @@ -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 */ }; diff --git a/src/mares_darwin_parser.c b/src/mares_darwin_parser.c index 1201ec6..ba0382f 100644 --- a/src/mares_darwin_parser.c +++ b/src/mares_darwin_parser.c @@ -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; diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 3a73968..e945e9f 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -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 */ }; diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index 0c45a15..76324f3 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -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; diff --git a/src/mares_nemo.c b/src/mares_nemo.c index 2ec3588..c73c24b 100644 --- a/src/mares_nemo.c +++ b/src/mares_nemo.c @@ -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 */ }; diff --git a/src/mares_nemo_parser.c b/src/mares_nemo_parser.c index 9a6cbfd..c0ab03b 100644 --- a/src/mares_nemo_parser.c +++ b/src/mares_nemo_parser.c @@ -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; diff --git a/src/mares_puck.c b/src/mares_puck.c index fc8e00d..b220f54 100644 --- a/src/mares_puck.c +++ b/src/mares_puck.c @@ -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 */ }; diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index da19e7f..bae6537 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -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[] = { diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 137adbc..f527c7e 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -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) { diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c index d47c532..b6021dd 100644 --- a/src/oceanic_veo250.c +++ b/src/oceanic_veo250.c @@ -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, diff --git a/src/oceanic_veo250_parser.c b/src/oceanic_veo250_parser.c index ba69723..a3acf57 100644 --- a/src/oceanic_veo250_parser.c +++ b/src/oceanic_veo250_parser.c @@ -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; diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index 2e86f5f..cc09892 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -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, diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index 9c85854..894b626 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -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; diff --git a/src/reefnet_sensus.c b/src/reefnet_sensus.c index 94d7a7e..37c343a 100644 --- a/src/reefnet_sensus.c +++ b/src/reefnet_sensus.c @@ -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 */ }; diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c index bf88801..4df3c07 100644 --- a/src/reefnet_sensuspro.c +++ b/src/reefnet_sensuspro.c @@ -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 */ }; diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c index f28dbb2..fba7511 100644 --- a/src/reefnet_sensusultra.c +++ b/src/reefnet_sensusultra.c @@ -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 */ }; diff --git a/src/scubapro_g2.c b/src/scubapro_g2.c index 33ecfc4..9a0c336 100644 --- a/src/scubapro_g2.c +++ b/src/scubapro_g2.c @@ -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 */ }; diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 79f167d..7558c54 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -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 */ }; diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index 0e7a0d3..42a579b 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -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 */ }; diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 6d4a3ec..992169c 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -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; } diff --git a/src/suunto_d9.c b/src/suunto_d9.c index 2908792..56f8c40 100644 --- a/src/suunto_d9.c +++ b/src/suunto_d9.c @@ -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 diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index deb3ec1..5404997 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -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; diff --git a/src/suunto_eon.c b/src/suunto_eon.c index ce9ce83..2875654 100644 --- a/src/suunto_eon.c +++ b/src/suunto_eon.c @@ -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 */ }; diff --git a/src/suunto_eon_parser.c b/src/suunto_eon_parser.c index 066c997..2106c69 100644 --- a/src/suunto_eon_parser.c +++ b/src/suunto_eon_parser.c @@ -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; diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index c381668..7267ac8 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -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 */ }; diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index fefaad2..f385bc9 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -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; } diff --git a/src/suunto_solution.c b/src/suunto_solution.c index c708114..cb2481d 100644 --- a/src/suunto_solution.c +++ b/src/suunto_solution.c @@ -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 */ }; diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c index ce44e05..33657dc 100644 --- a/src/suunto_vyper.c +++ b/src/suunto_vyper.c @@ -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 */ }; diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c index 8ab521e..618ee4a 100644 --- a/src/suunto_vyper2.c +++ b/src/suunto_vyper2.c @@ -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 diff --git a/src/suunto_vyper_parser.c b/src/suunto_vyper_parser.c index 0ca8eaa..80f7d54 100644 --- a/src/suunto_vyper_parser.c +++ b/src/suunto_vyper_parser.c @@ -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; diff --git a/src/usbhid.c b/src/usbhid.c index 784b53c..c604cf4 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -25,14 +25,20 @@ #include -#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 -#elif defined(HAVE_HIDAPI) -#define USBHID +#elif defined(USE_HIDAPI) #include #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: diff --git a/src/uwatec_aladin.c b/src/uwatec_aladin.c index d70369a..25c9208 100644 --- a/src/uwatec_aladin.c +++ b/src/uwatec_aladin.c @@ -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 */ }; diff --git a/src/uwatec_memomouse.c b/src/uwatec_memomouse.c index b23c6b2..2f93067 100644 --- a/src/uwatec_memomouse.c +++ b/src/uwatec_memomouse.c @@ -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 */ }; diff --git a/src/uwatec_meridian.c b/src/uwatec_meridian.c index d59b855..c32f3bc 100644 --- a/src/uwatec_meridian.c +++ b/src/uwatec_meridian.c @@ -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 */ }; diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 425e76e..4fd2e2d 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -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 */ }; diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 99bfa28..54bdee5 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -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. diff --git a/src/zeagle_n2ition3.c b/src/zeagle_n2ition3.c index fed6ae2..3d2bb24 100644 --- a/src/zeagle_n2ition3.c +++ b/src/zeagle_n2ition3.c @@ -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 */ };