Family is not COCHRAN_COMMANDER and functions are cochran_commander_*. --- examples/cochran_download.c | 2 +- include/libdivecomputer/cochran.h | 19 +- include/libdivecomputer/cochran_commander.h | 44 ++ include/libdivecomputer/common.h | 2 +- src/Makefile.am | 4 +- src/cochran_cmdr_parser.c | 6 +- src/cochran_commander.c | 890 ++++++++++++++++++++++++++++ src/cochran_commander.h | 157 +++++ src/cochran_commander_parser.c | 218 +++++++ src/cochran_commander_parser.h | 115 ++++ src/cochran_common.c | 890 ---------------------------- src/cochran_common.h | 157 ----- src/cochran_common_parser.c | 218 ------- src/cochran_common_parser.h | 115 ---- src/cochran_emc_parser.c | 6 +- src/descriptor.c | 6 +- src/device.c | 4 +- src/parser.c | 4 +- 18 files changed, 1442 insertions(+), 1415 deletions(-) create mode 100644 include/libdivecomputer/cochran_commander.h create mode 100644 src/cochran_commander.c create mode 100644 src/cochran_commander.h create mode 100644 src/cochran_commander_parser.c create mode 100644 src/cochran_commander_parser.h delete mode 100644 src/cochran_common.c delete mode 100644 src/cochran_common.h delete mode 100644 src/cochran_common_parser.c delete mode 100644 src/cochran_common_parser.h
diff --git a/examples/cochran_download.c b/examples/cochran_download.c index a9253af..01df25d 100644 --- a/examples/cochran_download.c +++ b/examples/cochran_download.c @@ -309,7 +309,7 @@ main (int argc, char *argv[]) extern int optind, opterr, optopt;
// Default values. - dc_family_t backend = DC_FAMILY_COCHRAN; + dc_family_t backend = DC_FAMILY_COCHRAN_COMMANDER;
// Command line arguments const char *devname = NULL, *dirname; diff --git a/include/libdivecomputer/cochran.h b/include/libdivecomputer/cochran.h index 90b1bb5..a2a0962 100644 --- a/include/libdivecomputer/cochran.h +++ b/include/libdivecomputer/cochran.h @@ -22,23 +22,6 @@ #ifndef COCHRAN_H #define COCHRAN_H
-#include "context.h" -#include "device.h" -#include "parser.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -dc_status_t -cochran_common_device_open (dc_device_t **device, dc_context_t *context, - const char *name); - -dc_status_t -cochran_common_parser_create (dc_parser_t **parser, dc_context_t *context); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +#include "cochran_commander.h"
#endif /* COCHRAN_H */ diff --git a/include/libdivecomputer/cochran_commander.h b/include/libdivecomputer/cochran_commander.h new file mode 100644 index 0000000..143c451 --- /dev/null +++ b/include/libdivecomputer/cochran_commander.h @@ -0,0 +1,44 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 John Van Ostrand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef COCHRAN_COMMANDER_H +#define COCHRAN_COMMANDER_H + +#include "context.h" +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +cochran_commander_device_open (dc_device_t **device, dc_context_t *context, + const char *name); + +dc_status_t +cochran_commander_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* COCHRAN_COMMANDER_H */ diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 218548f..a2ff47f 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -84,7 +84,7 @@ typedef enum dc_family_t { /* Dive Rite */ DC_FAMILY_DIVERITE_NITEKQ = (11 << 16), /* Cochran */ - DC_FAMILY_COCHRAN = (12 << 16), + DC_FAMILY_COCHRAN_COMMANDER = (12 << 16), } dc_family_t;
#ifdef __cplusplus diff --git a/src/Makefile.am b/src/Makefile.am index 33d4713..33bd5e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -56,8 +56,8 @@ libdivecomputer_la_SOURCES = \ checksum.h checksum.c \ array.h array.c \ buffer.c \ - cochran_common.h cochran_common.c \ - cochran_common_parser.h cochran_common_parser.c \ + cochran_commander.h cochran_commander.c \ + cochran_commander_parser.h cochran_commander_parser.c \ cochran_cmdr_parser.c cochran_emc_parser.c
if OS_WIN32 diff --git a/src/cochran_cmdr_parser.c b/src/cochran_cmdr_parser.c index 8b94fcf..7cfd650 100644 --- a/src/cochran_cmdr_parser.c +++ b/src/cochran_cmdr_parser.c @@ -31,8 +31,8 @@ #include "serial.h" #include "array.h"
-#include "cochran_common.h" -#include "cochran_common_parser.h" +#include "cochran_commander.h" +#include "cochran_commander_parser.h"
#define SZ_SAMPLE 2 @@ -151,7 +151,7 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
// Check for event if (s[0] & 0x80) { - offset += cochran_common_handle_event(abstract, callback, userdata, + offset += cochran_commander_handle_event(abstract, callback, userdata, s[0], offset, time); if (s[0] == 0xC5) deco_obligation = 1; // Deco obligation begins diff --git a/src/cochran_commander.c b/src/cochran_commander.c new file mode 100644 index 0000000..fc25932 --- /dev/null +++ b/src/cochran_commander.c @@ -0,0 +1,890 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 John Van Ostrand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <string.h> // memcpy, memcmp +#include <stdlib.h> // malloc, free +#include <assert.h> // assert +#include <time.h> + +#include <libdivecomputer/cochran.h> + +#include "context-private.h" +#include "device-private.h" +#include "serial.h" +#include "array.h" + +#include "cochran_commander.h" + + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \ +) + +static const dc_device_vtable_t cochran_commander_device_vtable = { + DC_FAMILY_COCHRAN_COMMANDER, + cochran_commander_device_set_fingerprint, /* set_fingerprint */ + cochran_commander_device_read, /* read */ + NULL, /* write */ + cochran_commander_device_dump, /* dump */ + cochran_commander_device_foreach, /* foreach */ + cochran_commander_device_close /* close */ +}; + + +dc_status_t +cochran_packet (cochran_device_t *device, const unsigned char command[], + unsigned int csize, unsigned char answer[], unsigned int asize, + int high_speed) +{ + dc_device_t *abstract = (dc_device_t *) device; + unsigned int bytes_read = 0, n, read_size; + unsigned int ptr; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Send the command to the device, one byte at a time + for (ptr = 0; ptr < csize; ptr++) { + if (ptr) serial_sleep(device->port, 16); // 16 ms + n = serial_write(device->port, command + ptr, 1); + if (n != 1) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + } + + if (high_speed) { + serial_sleep(device->port, 45); + + // Weird but I only get the right result when I do it twice + // Rates are odd, like 825600 for the EMC, 115200 for commander + serial_configure(device->port, device->data.high_baud, 8, + SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE); + serial_configure(device->port, device->data.high_baud, 8, + SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE); + } + + // Receive the answer from the device. + // Use 1024 byte "packets" so we can display progress. + while (bytes_read < asize) { + if (asize - bytes_read > 1024) + read_size = 1024; + else + read_size = asize - bytes_read; + + n = serial_read (device->port, answer + bytes_read, read_size); + if (n != read_size) { + ERROR (abstract->context, "Failed to receive data, expected %u," + "read %u.", read_size, n); + return EXITCODE (n); + } + + bytes_read += n; + + if (device->progress) { + device->progress->current = bytes_read; + device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); + } + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cochran_commander_serial_setup (cochran_device_t *device, dc_context_t *context) +{ + int rc; + + // Set the serial communication protocol (9600 8N2, no FC). + rc = serial_configure (device->port, 9600, 8, SERIAL_PARITY_NONE, + 2, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + ERROR (context, "Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + serial_set_queue_size(device->port, 4096, 4096); + + // Make sure everything is in a sane state. + // Mimicing Analyst software with excessive flushes + serial_flush (device->port, SERIAL_QUEUE_OUTPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + serial_flush (device->port, SERIAL_QUEUE_INPUT); + + serial_set_break(device->port, 1); + serial_sleep(device->port, 16); + + serial_set_break(device->port, 0); + + // Set the timeout for receiving data (5000 ms). + if (serial_set_timeout (device->port, 5000) == -1) { + ERROR (context, "Failed to set the timeout."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Wait for heartbeat byte before send + int n; + char answer[1]; + if ((n = serial_read(device->port, answer, 1)) != 1) { + ERROR (context, "Failed to receive device heartbeat."); + return EXITCODE (n); + } + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_serial_open(cochran_device_t *device, dc_context_t *context) +{ + // Open the device. + int rc = serial_open (&device->port, context, device->name); + if (rc == -1) { + ERROR (context, "Failed to open the serial port."); + free (device); + return DC_STATUS_IO; + } + + return cochran_commander_serial_setup(device, context); +} + + +dc_status_t +cochran_commander_device_open (dc_device_t **out, dc_context_t *context, + const char *name) +{ + dc_status_t rc; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + cochran_device_t *device = (cochran_device_t *) malloc ( + sizeof (cochran_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base, context, &cochran_commander_device_vtable); + + // Set the default values. + device->port = NULL; + device->name = name; + device->progress = NULL; + device->data.logbook = NULL; + device->data.sample = NULL; + cochran_commander_device_set_fingerprint((dc_device_t *) device, "", 0); + + if ((rc = cochran_commander_serial_open(device, context)) + != DC_STATUS_SUCCESS) + return rc; + + // Read ID from the device + rc = cochran_read_id((dc_device_t *) device); + + if (rc != DC_STATUS_SUCCESS) { + ERROR (context, "Device not responding."); + serial_close (device->port); + free (device); + return rc; + } + + // Check ID + if ((device->data.model & 0xFF0000) == COCHRAN_MODEL_UNKNOWN) { + ERROR (context, "Device not recognized."); + serial_close (device->port); + free (device); + return DC_STATUS_UNSUPPORTED; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_device_close (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t*) abstract; + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DC_STATUS_IO; + } + + // Free memory. + free (device->data.logbook); + free (device->data.sample); + free (device); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_device_set_fingerprint (dc_device_t *abstract, + const unsigned char data[], unsigned int size) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = &(device->data); + + if (size && size != sizeof (d->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (&(d->fingerprint), data, sizeof (d->fingerprint)); + else + memset (&(d->fingerprint), 0xFF, sizeof (d->fingerprint)); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_device_read (dc_device_t *abstract, unsigned int address, + unsigned char data[], unsigned int size) +{ + cochran_device_t *device = (cochran_device_t*) abstract; + + // Build the command + unsigned char command[10]; + unsigned char command_size; + + switch (device->data.address_length) + { + case COCHRAN_ADDRESS_LENGTH_32: + // EMC uses 32 bit addressing + command[0] = 0x15; + command[1] = (address ) & 0xff; + command[2] = (address >> 8) & 0xff; + command[3] = (address >> 16) & 0xff; + command[4] = (address >> 24) & 0xff; + command[5] = (size ) & 0xff; + command[6] = (size >> 8 ) & 0xff; + command[7] = (size >> 16 ) & 0xff; + command[8] = (size >> 24 ) & 0xff; + command[9] = 0x05; + command_size = 10; + break; + case COCHRAN_ADDRESS_LENGTH_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; + break; default: + return DC_STATUS_UNSUPPORTED; + } + + // Read data at high speed + dc_status_t rc = cochran_packet (device, command, command_size, data, + size, 1); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} + + +static void +cochran_set_device_config (cochran_device_t *device) +{ + dc_device_t *abstract = (dc_device_t *) device; + // Determine model + if (memcmp(device->data.id + 0x3B, "AM2315\xA3\x71", 8) == 0) + { + device->data.model = COCHRAN_MODEL_EMC_20; + device->data.log_size = 512; + device->data.sample_memory_start_address = 0x94000; + device->data.dive_num_ptr = 0x56; + device->data.dive_count_ptr = 0xD2; + device->data.dive_count_endian = COCHRAN_LE_TYPE; + device->data.sample_end_ptr = 256; + device->data.log_pre_dive_ptr = 30; + device->data.log_end_dive_ptr = 256; + device->data.last_interdive_ptr = 233; + device->data.last_entry_ptr = 194; + device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY; + device->data.address_length = COCHRAN_ADDRESS_LENGTH_32; + device->data.high_baud = 825600; + } + else if (memcmp(device->data.id + 0x3B, "AMA315\xC3\xC5", 8) == 0) + { + device->data.model = COCHRAN_MODEL_EMC_16; + device->data.log_size = 512; + device->data.sample_memory_start_address = 0x94000; + device->data.dive_num_ptr = 0x56; + device->data.dive_count_ptr = 0xD2; + device->data.dive_count_endian = COCHRAN_LE_TYPE; + device->data.sample_end_ptr = 256; + device->data.log_pre_dive_ptr = 30; + device->data.log_end_dive_ptr = 256; + device->data.last_interdive_ptr = 233; + device->data.last_entry_ptr = 194; + device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY; + device->data.address_length = COCHRAN_ADDRESS_LENGTH_32; + device->data.high_baud = 825600; + } + else if (memcmp(device->data.id + 0x3B, "AM\x11""2212\x02", 8) == 0) + { + device->data.model = COCHRAN_MODEL_COMMANDER_AIR_NITROX; + device->data.log_size = 256; + device->data.sample_memory_start_address = 0x20000; + device->data.dive_num_ptr = 0x46; + device->data.dive_count_ptr = 0x46; + device->data.dive_count_endian = COCHRAN_BE_TYPE; + device->data.sample_end_ptr = 256; + device->data.log_pre_dive_ptr = 30; + device->data.log_end_dive_ptr = 128; + device->data.last_interdive_ptr = 167; + device->data.last_entry_ptr = -1; + device->data.date_format = COCHRAN_DATE_FORMAT_MSDHYM; + device->data.address_length = COCHRAN_ADDRESS_LENGTH_24; + device->data.high_baud = 115200; + } + else + { + device->data.model = 0; + ERROR (abstract->context, + "Unknown Cochran model %02x %02x %02x %02x %02x %02x %02x %02x", + *(device->data.id + 0x3B), *(device->data.id + 0x3B + 1), + *(device->data.id + 0x3B + 2), *(device->data.id + 0x3B + 3), + *(device->data.id + 0x3B + 4), *(device->data.id + 0x3B + 5), + *(device->data.id + 0x3B + 6), *(device->data.id + 0x3B + 7)); + } + + return; +} + + +static dc_status_t +cochran_read_id (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + dc_status_t rc; + unsigned char command[6] = {0x05, 0x9D, 0xFF, 0x00, 0x43, 0x00}; + + rc = cochran_packet(device, command, 6, device->data.id, 67, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + + if (strncmp(device->data.id, "(C)", 3) != 0) { + // It's a Commander, read again + memcpy(device->data.id0, device->data.id, 67); + + command[1] = 0xBD; + command[2] = 0x7F; + + rc = cochran_packet(device, command, 6, device->data.id, 67, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + } + + cochran_set_device_config(device); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cochran_read_config (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *data = &device->data; + dc_status_t rc; + unsigned char command[2] = { 0x96, 0x00 }; + + if ((data->model & 0xFF0000) == COCHRAN_MODEL_EMC_FAMILY) + data->config_count = 2; + else + data->config_count = 4; + + int n; + for (n = 0; n < data->config_count; n++) { + command[1] = n; + rc = cochran_packet(device, command, 2, data->config[n], 512, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cochran_read_misc (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + + unsigned char command[7] = { 0x89, 0x05, 0x00, 0x00, 0x00, 0xDC, 0x05 }; + + switch (device->data.model & 0xFF0000) + { + case COCHRAN_MODEL_COMMANDER_FAMILY: + command[2] = 0xCA; + command[3] = 0xFD; + break; + case COCHRAN_MODEL_EMC_FAMILY: + command[2] = 0xE0; + command[3] = 0x03; + break; + default: + return DC_STATUS_UNSUPPORTED; + } + + // Send first byte then wait for heartbeat before sending the rest + serial_write(device->port, command, 1); + + int n; + char answer[1]; + if ((n = serial_read(device->port, answer, 1)) != 1) { + ERROR (abstract->context, "Failed to receive device heartbeat."); + return EXITCODE (n); + } + + return cochran_packet(device, command + 1, 6, device->data.misc, 1500, 0); +} + + +static dc_status_t +cochran_read_logbook (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = &(device->data); + dc_status_t rc; + + if (d->logbook) + free(d->logbook); + + // Allocate space for log book. + d->logbook = (unsigned char *) malloc(d->logbook_size); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + device->progress = malloc(sizeof(dc_event_progress_t)); + if (device->progress == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + device->progress->current = 0; + device->progress->maximum = d->logbook_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); + + serial_sleep(device->port, 800); + + // set back to 9600 baud + cochran_commander_serial_setup(device, abstract->context); + + // Request log book + rc = cochran_commander_device_read(abstract, 0, d->logbook, d->logbook_size); + + device->progress->current = d->logbook_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); + free (device->progress); + + return rc; +} + + +static void +cochran_find_fingerprint(dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = (cochran_data_t *) &(device->data); + + // Skip to fingerprint to reduce time + d->fp_dive_num = d->dive_count - 1; + + while (d->fp_dive_num >= 0 && memcmp(&(d->fingerprint), + d->logbook + d->fp_dive_num * d->log_size + + d->dive_num_ptr, + sizeof(d->fingerprint))) + d->fp_dive_num--; +} + + +static void +cochran_get_sample_parms(dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = (cochran_data_t *) &(device->data); + unsigned int pre_dive_offset, end_dive_offset; + unsigned int low_offset, high_offset; + + // Find lowest and highest offsets into sample data + low_offset = 0xFFFFFFFF; + high_offset = 0; + + int i; + for (i = d->fp_dive_num + 1; i < d->dive_count; i++) { + pre_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size + + d->log_pre_dive_ptr])); + end_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size + + d->log_end_dive_ptr])); + + // 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) { + // Since I can't tell how much memory it has, I'll round. + // I'll round to 128K, dives longer than 12 hrs aren't likely + // and memory in sizes not rounded to 128K might be odd. + high_offset = ((pre_dive_offset - 1) & 0xE0000) + 0x20000; + d->sample_memory_end_address = high_offset; + low_offset = d->sample_memory_start_address; + d->sample_data_offset = low_offset; + d->sample_size = high_offset - low_offset; + } else if (low_offset < 0xFFFFFFFF && high_offset > 0) { + // Round offset and size to 16K boundary + d->sample_data_offset = low_offset & 0xFFFFC000; + high_offset = ((high_offset - 1) & 0xFFFFC000) + 0x4000; + d->sample_size = high_offset - d->sample_data_offset; + } else { + d->sample_data_offset = 0; + d->sample_size = 0; + } +} + + +static dc_status_t +cochran_read_samples(dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = (cochran_data_t *) &(device->data); + dc_status_t rc; + + + if (d->sample_size > 0) { + if (d->sample) + free(d->sample); + + d->sample = (unsigned char *) malloc(d->sample_size); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + device->progress = malloc(sizeof(dc_event_progress_t)); + if (device->progress == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + device->progress->current = 0; + device->progress->maximum = d->sample_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); + + serial_sleep(device->port, 800); + + // set back to 9600 baud + cochran_commander_serial_setup(device, abstract->context); + + // Read the sample data + rc = cochran_commander_device_read (abstract, d->sample_data_offset, + d->sample, d->sample_size); + if (rc != DC_STATUS_SUCCESS) { + free (device->progress); + ERROR (abstract->context, "Failed to read the sample data."); + return rc; + } + + device->progress->current = d->sample_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); + free (device->progress); + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cochran_commander_device_read_all (dc_device_t *abstract) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = (cochran_data_t *) &(device->data); + + dc_status_t rc; + + // Read config + rc = cochran_read_config(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + + rc = cochran_read_misc(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Determine size of dive list to read. Round up to nearest 16K + if (d->dive_count_endian == COCHRAN_LE_TYPE) + d->dive_count = array_uint16_le (d->config[0] + d->dive_count_ptr); + else + d->dive_count = array_uint16_be (d->config[0] + d->dive_count_ptr); + + d->logbook_size = ((d->dive_count * d->log_size) & 0xFFFFC000) + + 0x4000; + + rc = cochran_read_logbook(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Determine sample memory to read + cochran_find_fingerprint(abstract); + cochran_get_sample_parms(abstract); + + rc = cochran_read_samples(abstract); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} + + +#define pack_uint32_array_le(d, i) ((d)[0] = (i) & 0xff, \ + (d)[1] = ((i) >> 8) & 0xff, \ + (d)[2] = ((i) >> 16) & 0xff, \ + (d)[3] = ((i) >> 24) & 0xff) + +dc_status_t +cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = (cochran_data_t *) &(device->data); + int ptr = 0; + + dc_status_t rc; + char *b; + int size; + + rc = cochran_commander_device_read_all (abstract); + + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Reserve space for block pointers + // Structure is: + // int ptr; + // char data_valid_flag; + dc_buffer_resize(data, 10 * 5); + + // Set pointer to first block + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + if (d->extra_id_flag) { + dc_buffer_append(data, d->id0, 67); + *(dc_buffer_get_data(data) + ptr + 4) = 1; + } else { + dc_buffer_resize(data, size + 67); + b = dc_buffer_get_data(data); + + memset(b + size, 0, 67); + } + + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + dc_buffer_append (data, d->id, 67); + *(dc_buffer_get_data(data) + ptr + 4) = 1; + + // Add config blocks + int n; + for (n = 0; n < d->config_count; n++) { + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + dc_buffer_append (data, d->config[n], 512); + + *(dc_buffer_get_data(data) + ptr + 4) = 1; + } + + // Add blank config blocks + for (n = d->config_count; n < 4; n++) { + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + dc_buffer_resize(data, size + 512); + + b = dc_buffer_get_data(data); + memset(b + size, 0, 512); + } + + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + if (d->misc) { + dc_buffer_append (data, d->misc, 1500); + *(dc_buffer_get_data(data) + ptr + 4) = 1; + } else { + dc_buffer_resize(data, size + 1500); + + b = dc_buffer_get_data(data); + memset(b + size, 0, 1500); + } + + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + if (d->logbook) { + dc_buffer_append (data, d->logbook, d->logbook_size); + *(dc_buffer_get_data(data) + ptr + 4) = 1; + } else { + dc_buffer_resize(data, size + d->logbook_size); + + b = dc_buffer_get_data(data); + memset(b + size, 0, d->logbook_size); + } + + // Set pointer to next block + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + if (d->sample) { + dc_buffer_append (data, d->sample, d->sample_size); + *(dc_buffer_get_data(data) + ptr + 4) = 1; + } else { + dc_buffer_resize(data, dc_buffer_get_size(data) + d->sample_size); + + b = dc_buffer_get_data(data); + memset(b + size, 0, d->logbook_size); + } + + // Set pointer to end + ptr += 5; + size = dc_buffer_get_size(data); + pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); + + return DC_STATUS_SUCCESS; +} + + + +dc_status_t +cochran_commander_device_foreach (dc_device_t *abstract, + dc_dive_callback_t callback, void *userdata) +{ + cochran_device_t *device = (cochran_device_t *) abstract; + cochran_data_t *d = &(device->data); + unsigned int sample_start_offset, sample_end_offset; + struct tm t; + dc_status_t rc; + + rc = cochran_commander_device_read_all (abstract); + + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Loop through each dive + int i; + for (i = d->dive_count - 1; i > d->fp_dive_num; i--) { + + d->current_log = d->logbook + i * d->log_size; + + sample_start_offset = array_uint32_le (d->current_log + 6); + sample_end_offset = array_uint32_le (d->current_log + + d->log_size/2); + + d->current_sample = d->sample + sample_start_offset + - d->sample_data_offset; + + // Check for corrupt post-dive section + if (array_uint32_le(d->current_log + d->log_size/2) == 0xFFFFFFFF) + d->corrupt_dive = 1; + else + d->corrupt_dive = 0; + + // Check for ring buffer wrap + if (sample_start_offset > sample_end_offset) + d->current_sample_size = d->sample_memory_end_address + - sample_start_offset + 1 + sample_end_offset + - d->sample_memory_start_address + 1; + else + d->current_sample_size = sample_end_offset - sample_start_offset; + + d->current_fingerprint = d->current_log + d->dive_num_ptr; + + if (d->date_format == COCHRAN_DATE_FORMAT_SMHDMY) { + t.tm_sec = d->current_log[0]; + t.tm_min = d->current_log[1]; + t.tm_hour = d->current_log[2]; + t.tm_mday = d->current_log[3]; + t.tm_mon = d->current_log[4]; + t.tm_year = d->current_log[5]; + t.tm_wday = t.tm_yday = t.tm_isdst = 0; + } else { + t.tm_sec = d->current_log[1]; + t.tm_min = d->current_log[0]; + t.tm_hour = d->current_log[3]; + t.tm_mday = d->current_log[2]; + t.tm_mon = d->current_log[5]; + t.tm_year = d->current_log[4]; + t.tm_wday = t.tm_yday = t.tm_isdst = 0; + } + d->current_dive_start_time = mktime(&t); + + if (callback && !callback ((unsigned char *) d, sizeof(device->data), + d->current_fingerprint, sizeof (d->fingerprint), userdata)) + return DC_STATUS_SUCCESS; + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/cochran_commander.h b/src/cochran_commander.h new file mode 100644 index 0000000..d16de42 --- /dev/null +++ b/src/cochran_commander.h @@ -0,0 +1,157 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 John Van Ostrand + * + * 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 + */ + +// seconds to add to Cochran time stamps to get unix time +// Equivalent to Jan 1, 1992 00:00:00 +#define COCHRAN_TIMESTAMP_OFFSET 694224000 + +#define COCHRAN_LE_TYPE 0 +#define COCHRAN_BE_TYPE 1 + +#define COCHRAN_DATE_FORMAT_SMHDMY 0 +#define COCHRAN_DATE_FORMAT_MSDHYM 1 + +#define COCHRAN_ADDRESS_LENGTH_32 0 +#define COCHRAN_ADDRESS_LENGTH_24 1 + +typedef enum cochran_model_t { + COCHRAN_MODEL_UNKNOWN = 0, + COCHRAN_MODEL_EMC_FAMILY = 1 << 16, + COCHRAN_MODEL_EMC_14, + COCHRAN_MODEL_EMC_16, + COCHRAN_MODEL_EMC_20, + COCHRAN_MODEL_COMMANDER_FAMILY = 2 << 16, + COCHRAN_MODEL_COMMANDER_AIR_NITROX, +} cochran_model_t; + +typedef struct cochran_data_t { + cochran_model_t model; + + unsigned char id0[67]; + unsigned char id[67]; + unsigned char config[4][512]; + unsigned char misc[1500]; + unsigned char *logbook; + unsigned char *sample; + + unsigned int extra_id_flag; + unsigned int config_count; + + unsigned short int dive_count; + unsigned char fingerprint[2]; + int fp_dive_num; + + unsigned int logbook_size; + unsigned int current_sample_size; + + unsigned int sample_data_offset; + unsigned int sample_size; + unsigned int last_interdive_offset; + unsigned int last_entry_offset; + + unsigned char *current_fingerprint; + unsigned char *current_log; + unsigned char *current_sample; + time_t current_dive_start_time; + + // Config items + int log_size; + int sample_memory_start_address; + int sample_memory_end_address; + int dive_num_ptr; + int dive_count_ptr; + int dive_count_endian; + int sample_end_ptr; + int log_pre_dive_ptr; + int log_end_dive_ptr; + int last_interdive_ptr; + int last_entry_ptr; + int date_format; + int address_length; + int high_baud; // baud rate to switch to for log/sample download + + unsigned char corrupt_dive; +} cochran_data_t; + +typedef struct cochran_device_t { + dc_device_t base; + const char *name; // serial port name + serial_t *port; + cochran_data_t data; // dive data used in parsing + dc_event_progress_t *progress; // for progress in the _read function +} cochran_device_t; + + +// Commander log fields +#define CMD_SEC 1 +#define CMD_MIN 0 +#define CMD_HOUR 3 +#define CMD_DAY 2 +#define CMD_MON 5 +#define CMD_YEAR 4 +#define CME_START_OFFSET 6 // 4 bytes +#define CMD_WATER_CONDUCTIVITY 24 // 1 byte, 0=low, 2=high +#define CMD_START_TEMP 45 // 1 byte, F +#define CMD_START_DEPTH 56 // 2 byte, /4=ft +#define CMD_ALTITUDE 73 // 1 byte, /4=Kilofeet +#define CMD_END_OFFSET 128 // 4 bytes +#define CMD_MIN_TEMP 153 // 1 byte, F +#define CMD_BT 166 // 2 bytes, minutes +#define CMD_MAX_DEPTH 168 // 2 bytes, /4=ft +#define CMD_AVG_DEPTH 170 // 2 bytes, /4=ft +#define CMD_O2_PERCENT 210 // 8 bytes, 4 x 2 byte, /256=% + +// EMC log fields +#define EMC_SEC 0 +#define EMC_MIN 1 +#define EMC_HOUR 2 +#define EMC_DAY 3 +#define EMC_MON 4 +#define EMC_YEAR 5 +#define EMC_START_OFFSET 6 // 4 bytes +#define EMC_WATER_CONDUCTIVITY 25 // 1 byte, 0=low, 2=high +#define EMC_START_DEPTH 42 // 2 byte, /256=ft +#define EMC_START_TEMP 55 // 1 byte, F +#define EMC_ALTITUDE 89 // 1 byte, /4=Kilofeet +#define EMC_O2_PERCENT 144 // 20 bytes, 10 x 2 bytes, /256=% +#define EMC_HE_PERCENT 164 // 20 bytes, 10 x 2 bytes, /256=% +#define EMC_END_OFFSET 256 // 4 bytes +#define EMC_MIN_TEMP 293 // 1 byte, F +#define EMC_BT 304 // 2 bytes, minutes +#define EMC_MAX_DEPTH 306 // 2 bytes, /4=ft +#define EMC_AVG_DEPTH 310 // 2 bytes, /4=ft + + +dc_status_t cochran_packet (cochran_device_t *device, + const unsigned char command[], unsigned int csize, + unsigned char answer[], unsigned int asize, int high_speed); +dc_status_t cochran_commander_device_open (dc_device_t **out, + dc_context_t *context, const char *name); +dc_status_t cochran_commander_device_close (dc_device_t *abstract); +dc_status_t cochran_commander_device_set_fingerprint (dc_device_t *abstract, + const unsigned char data[], unsigned int size); +dc_status_t cochran_commander_device_read (dc_device_t *abstract, + unsigned int address, unsigned char data[], unsigned int size); +static dc_status_t cochran_read_id (dc_device_t *abstract); +dc_status_t cochran_commander_device_foreach (dc_device_t *abstract, + dc_dive_callback_t callback, void *userdata); +dc_status_t cochran_commander_device_dump (dc_device_t *abstract, + dc_buffer_t *data); diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c new file mode 100644 index 0000000..004ab7e --- /dev/null +++ b/src/cochran_commander_parser.c @@ -0,0 +1,218 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 John Van Ostrand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <time.h> + +#include <libdivecomputer/units.h> +#include <libdivecomputer/cochran.h> + +#include "context-private.h" +#include "device-private.h" +#include "parser-private.h" +#include "serial.h" +#include "array.h" + +#include "cochran_commander.h" +#include "cochran_commander_parser.h" + + +dc_status_t cochran_commander_parser_set_data (dc_parser_t *abstract, + const unsigned char *data, unsigned int size); +dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *abstract, + dc_datetime_t *datetime); +static dc_status_t cochran_commander_parser_get_field (dc_parser_t *abstract, + dc_field_type_t type, unsigned int flags, void *value); +dc_status_t cochran_commander_parser_destroy (dc_parser_t *abstract); +static dc_status_t cochran_commander_parser_samples_foreach + (dc_parser_t *abstract, dc_sample_callback_t callback, + void *userdata); +int cochran_commander_handle_event (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata, unsigned char code, + unsigned int offset, unsigned int time); + + +static dc_parser_vtable_t cochran_commander_parser_vtable = { + DC_FAMILY_COCHRAN_COMMANDER, + cochran_commander_parser_set_data, /* set_data */ + cochran_commander_parser_get_datetime, /* datetime */ + cochran_commander_parser_get_field, /* fields */ + cochran_commander_parser_samples_foreach, /* samples_foreach */ + cochran_commander_parser_destroy /* destroy */ +}; + + + +dc_status_t +cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + dc_parser_t *parser = (dc_parser_t *) malloc (sizeof (dc_parser_t)); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + parser_init (parser, context, &cochran_commander_parser_vtable); + + *out = parser; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_parser_destroy (dc_parser_t *abstract) +{ + // Free memory. + free (abstract); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +cochran_commander_parser_set_data (dc_parser_t *abstract, + const unsigned char *data, unsigned int size) +{ + abstract->data = data; + abstract->size = size; + + return DC_STATUS_SUCCESS; +} + + +// There are two date formats used by Cochran +dc_status_t +cochran_commander_parser_get_datetime (dc_parser_t *abstract, + dc_datetime_t *datetime) +{ + cochran_data_t *data = (cochran_data_t *) abstract->data; + const unsigned char *log = data->current_log; + + if (data->date_format == COCHRAN_DATE_FORMAT_SMHDMY) { + datetime->second = log[0]; + datetime->minute = log[1]; + datetime->hour = log[2]; + datetime->day = log[3]; + datetime->month = log[4]; + datetime->year = log[5] + (log[5] > 91 ? 1900 : 2000); + } else { + datetime->second = log[1]; + datetime->minute = log[0]; + datetime->hour = log[3]; + datetime->day = log[2]; + datetime->month = log[5]; + datetime->year = log[4] + (log[5] > 91 ? 1900 : 2000); + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, + unsigned int flags, void *value) +{ + cochran_data_t *data = (cochran_data_t *) abstract->data; + + switch (data->model & 0xFF0000) + { + case COCHRAN_MODEL_COMMANDER_FAMILY: + return cochran_cmdr_parser_get_field(abstract, type, flags, value); + break; + case COCHRAN_MODEL_EMC_FAMILY: + return cochran_emc_parser_get_field(abstract, type, flags, value); + break; + } +} + +static dc_status_t +cochran_commander_parser_samples_foreach (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata) +{ + cochran_data_t *data = (cochran_data_t *) abstract->data; + + switch (data->model & 0xFF0000) + { + case COCHRAN_MODEL_COMMANDER_FAMILY: + return cochran_cmdr_parser_samples_foreach(abstract, callback, + userdata); + break; + case COCHRAN_MODEL_EMC_FAMILY: + return cochran_emc_parser_samples_foreach(abstract, callback, + userdata); + break; + } +} + + + +int +cochran_commander_handle_event (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata, unsigned char code, + unsigned int offset, unsigned int time) +{ + cochran_data_t *data = (cochran_data_t *) abstract->data; + cochran_events_t *e = cochran_events; + + dc_sample_value_t sample = {0}; + + unsigned char event_ptr = 0; + + while (e[event_ptr].code && e[event_ptr].code != code) + event_ptr++; + + sample.event.time = 0; + + if (e[event_ptr].code) { + switch (e[event_ptr].code) + { + case 0xAB: // Ceiling decrease + // Indicated to lower ceiling by 10 ft (deeper) + // Bytes 1-2: first stop duration (min) + // Bytes 3-4: total stop duration (min) + // Handled in calling function + break; + case 0xAD: // Ceiling increase + // Indicates to raise ceiling by 10 ft (shallower) + // Handled in calling function + break; + default: + // Don't send known events of type NONE + if (! e[event_ptr].type == SAMPLE_EVENT_NONE) { + sample.event.type = e[event_ptr].type; + sample.event.flags = e[event_ptr].flag; + if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + } + } + } else { + // Unknown event, send it so we know we missed something + sample.event.type = SAMPLE_EVENT_NONE; + sample.event.flags = SAMPLE_FLAGS_NONE; + sample.event.value = code; + if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + } + + return e[event_ptr].data_bytes; +} diff --git a/src/cochran_commander_parser.h b/src/cochran_commander_parser.h new file mode 100644 index 0000000..314d538 --- /dev/null +++ b/src/cochran_commander_parser.h @@ -0,0 +1,115 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 John Van Ostrand + * + * 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 + */ + +typedef struct cochran_events_t { + unsigned char code; + unsigned char data_bytes; + char *name; + parser_sample_event_t type; + parser_sample_flags_t flag; +} cochran_events_t; + +static cochran_events_t cochran_events[] = { + { 0xA8, 1, "Entered PDI mode", + SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_BEGIN }, + { 0xA9, 1, "Exited PDI mode", + SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_END }, + { 0xAB, 5, "Ceiling decrease", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xAD, 5, "Ceiling increase", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xBD, 1, "Switched to nomal PO2 setting", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xC0, 1, "Switched to FO2 21% mode", + SAMPLE_EVENT_GASCHANGE, SAMPLE_FLAGS_NONE }, + { 0xC1, 1, "Ascent rate greater than limit", + SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN }, + { 0xC2, 1, "Low battery warning", + SAMPLE_EVENT_BATTERY, SAMPLE_FLAGS_NONE }, + { 0xC3, 1, "CNS Oxygen toxicity warning", + SAMPLE_EVENT_OLF, SAMPLE_FLAGS_NONE }, + { 0xC4, 1, "Depth exceeds user set point", + SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_NONE }, + { 0xC5, 1, "Entered decompression mode", + SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_BEGIN }, + { 0xC8, 1, "PO2 too high", + SAMPLE_EVENT_FLOOR, SAMPLE_FLAGS_BEGIN }, + { 0xCC, 1, "Low Cylinder 1 pressure", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, + { 0xCE, 1, "Non-decompression warning", + SAMPLE_EVENT_RBT, SAMPLE_FLAGS_BEGIN }, + { 0xCD, 1, "Switched to deco blend", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, + { 0xD0, 1, "Breathing rate alarm", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, + { 0xD3, 1, "Low gas 1 flow rate", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xD6, 1, "Depth is less than ceiling", + SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN }, + { 0xD8, 1, "End decompression mode", + SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_END }, + { 0xE1, 1, "End ascent rate warning", + SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_END }, + { 0xE2, 1, "Low SBAT battery warning", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xE3, 1, "Switched to FO2 mode", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xE5, 1, "Switched to PO2 mode", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, + { 0xEE, 1, "End non-decompresison warning", + SAMPLE_EVENT_RBT, SAMPLE_FLAGS_END }, + { 0xEF, 1, "Switch to blend 2", + SAMPLE_EVENT_GASCHANGE2, SAMPLE_FLAGS_NONE }, + { 0xF0, 1, "Breathing rate alarm", + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_END }, + { 0xF3, 1, "Switch to blend 1", + SAMPLE_EVENT_GASCHANGE2, SAMPLE_FLAGS_NONE }, + { 0xF6, 1, "End Depth is less than ceiling", + SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END }, + { 0x00, 1, NULL, + SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE } +}; + + +// Common +dc_status_t cochran_commander_parser_create (dc_parser_t **out, + dc_context_t *context); +dc_status_t cochran_commander_parser_destroy (dc_parser_t *abstract); +dc_status_t cochran_commander_parser_set_data (dc_parser_t *abstract, + const unsigned char *data, unsigned int size); +dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *abstract, + dc_datetime_t *datetime); +int cochran_commander_handle_event (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata, unsigned char code, + unsigned int offset, unsigned int time); + +// EMC FAMILY +dc_status_t cochran_emc_parser_get_field (dc_parser_t *abstract, + dc_field_type_t type, unsigned int flags, void *value); +dc_status_t cochran_emc_parser_samples_foreach (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata); + +// COMMANDER FAMILY +dc_status_t cochran_cmdr_parser_get_field(dc_parser_t *abstract, + dc_field_type_t type, unsigned int flags, void *value); +dc_status_t cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, + dc_sample_callback_t callback, void *userdata); + diff --git a/src/cochran_common.c b/src/cochran_common.c deleted file mode 100644 index 4fcd751..0000000 --- a/src/cochran_common.c +++ /dev/null @@ -1,890 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2014 John Van Ostrand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <string.h> // memcpy, memcmp -#include <stdlib.h> // malloc, free -#include <assert.h> // assert -#include <time.h> - -#include <libdivecomputer/cochran.h> - -#include "context-private.h" -#include "device-private.h" -#include "serial.h" -#include "array.h" - -#include "cochran_common.h" - - -#define EXITCODE(rc) \ -( \ - rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \ -) - -static const dc_device_vtable_t cochran_common_device_vtable = { - DC_FAMILY_COCHRAN, - cochran_common_device_set_fingerprint, /* set_fingerprint */ - cochran_common_device_read, /* read */ - NULL, /* write */ - cochran_common_device_dump, /* dump */ - cochran_common_device_foreach, /* foreach */ - cochran_common_device_close /* close */ -}; - - -dc_status_t -cochran_packet (cochran_device_t *device, const unsigned char command[], - unsigned int csize, unsigned char answer[], unsigned int asize, - int high_speed) -{ - dc_device_t *abstract = (dc_device_t *) device; - unsigned int bytes_read = 0, n, read_size; - unsigned int ptr; - - if (device_is_cancelled (abstract)) - return DC_STATUS_CANCELLED; - - // Send the command to the device, one byte at a time - for (ptr = 0; ptr < csize; ptr++) { - if (ptr) serial_sleep(device->port, 16); // 16 ms - n = serial_write(device->port, command + ptr, 1); - if (n != 1) { - ERROR (abstract->context, "Failed to send the command."); - return EXITCODE (n); - } - } - - if (high_speed) { - serial_sleep(device->port, 45); - - // Weird but I only get the right result when I do it twice - // Rates are odd, like 825600 for the EMC, 115200 for commander - serial_configure(device->port, device->data.high_baud, 8, - SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE); - serial_configure(device->port, device->data.high_baud, 8, - SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE); - } - - // Receive the answer from the device. - // Use 1024 byte "packets" so we can display progress. - while (bytes_read < asize) { - if (asize - bytes_read > 1024) - read_size = 1024; - else - read_size = asize - bytes_read; - - n = serial_read (device->port, answer + bytes_read, read_size); - if (n != read_size) { - ERROR (abstract->context, "Failed to receive data, expected %u," - "read %u.", read_size, n); - return EXITCODE (n); - } - - bytes_read += n; - - if (device->progress) { - device->progress->current = bytes_read; - device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); - } - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -cochran_common_serial_setup (cochran_device_t *device, dc_context_t *context) -{ - int rc; - - // Set the serial communication protocol (9600 8N2, no FC). - rc = serial_configure (device->port, 9600, 8, SERIAL_PARITY_NONE, - 2, SERIAL_FLOWCONTROL_NONE); - if (rc == -1) { - ERROR (context, "Failed to set the terminal attributes."); - serial_close (device->port); - free (device); - return DC_STATUS_IO; - } - - serial_set_queue_size(device->port, 4096, 4096); - - // Make sure everything is in a sane state. - // Mimicing Analyst software with excessive flushes - serial_flush (device->port, SERIAL_QUEUE_OUTPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - - serial_set_break(device->port, 1); - serial_sleep(device->port, 16); - - serial_set_break(device->port, 0); - - // Set the timeout for receiving data (5000 ms). - if (serial_set_timeout (device->port, 5000) == -1) { - ERROR (context, "Failed to set the timeout."); - serial_close (device->port); - free (device); - return DC_STATUS_IO; - } - - // Wait for heartbeat byte before send - int n; - char answer[1]; - if ((n = serial_read(device->port, answer, 1)) != 1) { - ERROR (context, "Failed to receive device heartbeat."); - return EXITCODE (n); - } - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_serial_open(cochran_device_t *device, dc_context_t *context) -{ - // Open the device. - int rc = serial_open (&device->port, context, device->name); - if (rc == -1) { - ERROR (context, "Failed to open the serial port."); - free (device); - return DC_STATUS_IO; - } - - return cochran_common_serial_setup(device, context); -} - - -dc_status_t -cochran_common_device_open (dc_device_t **out, dc_context_t *context, - const char *name) -{ - dc_status_t rc; - - if (out == NULL) - return DC_STATUS_INVALIDARGS; - - // Allocate memory. - cochran_device_t *device = (cochran_device_t *) malloc ( - sizeof (cochran_device_t)); - if (device == NULL) { - ERROR (context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Initialize the base class. - device_init (&device->base, context, &cochran_common_device_vtable); - - // Set the default values. - device->port = NULL; - device->name = name; - device->progress = NULL; - device->data.logbook = NULL; - device->data.sample = NULL; - cochran_common_device_set_fingerprint((dc_device_t *) device, "", 0); - - if ((rc = cochran_common_serial_open(device, context)) - != DC_STATUS_SUCCESS) - return rc; - - // Read ID from the device - rc = cochran_read_id((dc_device_t *) device); - - if (rc != DC_STATUS_SUCCESS) { - ERROR (context, "Device not responding."); - serial_close (device->port); - free (device); - return rc; - } - - // Check ID - if ((device->data.model & 0xFF0000) == COCHRAN_MODEL_UNKNOWN) { - ERROR (context, "Device not recognized."); - serial_close (device->port); - free (device); - return DC_STATUS_UNSUPPORTED; - } - - *out = (dc_device_t *) device; - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_device_close (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t*) abstract; - - // Close the device. - if (serial_close (device->port) == -1) { - free (device); - return DC_STATUS_IO; - } - - // Free memory. - free (device->data.logbook); - free (device->data.sample); - free (device); - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_device_set_fingerprint (dc_device_t *abstract, - const unsigned char data[], unsigned int size) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = &(device->data); - - if (size && size != sizeof (d->fingerprint)) - return DC_STATUS_INVALIDARGS; - - if (size) - memcpy (&(d->fingerprint), data, sizeof (d->fingerprint)); - else - memset (&(d->fingerprint), 0xFF, sizeof (d->fingerprint)); - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_device_read (dc_device_t *abstract, unsigned int address, - unsigned char data[], unsigned int size) -{ - cochran_device_t *device = (cochran_device_t*) abstract; - - // Build the command - unsigned char command[10]; - unsigned char command_size; - - switch (device->data.address_length) - { - case COCHRAN_ADDRESS_LENGTH_32: - // EMC uses 32 bit addressing - command[0] = 0x15; - command[1] = (address ) & 0xff; - command[2] = (address >> 8) & 0xff; - command[3] = (address >> 16) & 0xff; - command[4] = (address >> 24) & 0xff; - command[5] = (size ) & 0xff; - command[6] = (size >> 8 ) & 0xff; - command[7] = (size >> 16 ) & 0xff; - command[8] = (size >> 24 ) & 0xff; - command[9] = 0x05; - command_size = 10; - break; - case COCHRAN_ADDRESS_LENGTH_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; - break; default: - return DC_STATUS_UNSUPPORTED; - } - - // Read data at high speed - dc_status_t rc = cochran_packet (device, command, command_size, data, - size, 1); - if (rc != DC_STATUS_SUCCESS) - return rc; - - return DC_STATUS_SUCCESS; -} - - -static void -cochran_set_device_config (cochran_device_t *device) -{ - dc_device_t *abstract = (dc_device_t *) device; - // Determine model - if (memcmp(device->data.id + 0x3B, "AM2315\xA3\x71", 8) == 0) - { - device->data.model = COCHRAN_MODEL_EMC_20; - device->data.log_size = 512; - device->data.sample_memory_start_address = 0x94000; - device->data.dive_num_ptr = 0x56; - device->data.dive_count_ptr = 0xD2; - device->data.dive_count_endian = COCHRAN_LE_TYPE; - device->data.sample_end_ptr = 256; - device->data.log_pre_dive_ptr = 30; - device->data.log_end_dive_ptr = 256; - device->data.last_interdive_ptr = 233; - device->data.last_entry_ptr = 194; - device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY; - device->data.address_length = COCHRAN_ADDRESS_LENGTH_32; - device->data.high_baud = 825600; - } - else if (memcmp(device->data.id + 0x3B, "AMA315\xC3\xC5", 8) == 0) - { - device->data.model = COCHRAN_MODEL_EMC_16; - device->data.log_size = 512; - device->data.sample_memory_start_address = 0x94000; - device->data.dive_num_ptr = 0x56; - device->data.dive_count_ptr = 0xD2; - device->data.dive_count_endian = COCHRAN_LE_TYPE; - device->data.sample_end_ptr = 256; - device->data.log_pre_dive_ptr = 30; - device->data.log_end_dive_ptr = 256; - device->data.last_interdive_ptr = 233; - device->data.last_entry_ptr = 194; - device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY; - device->data.address_length = COCHRAN_ADDRESS_LENGTH_32; - device->data.high_baud = 825600; - } - else if (memcmp(device->data.id + 0x3B, "AM\x11""2212\x02", 8) == 0) - { - device->data.model = COCHRAN_MODEL_COMMANDER_AIR_NITROX; - device->data.log_size = 256; - device->data.sample_memory_start_address = 0x20000; - device->data.dive_num_ptr = 0x46; - device->data.dive_count_ptr = 0x46; - device->data.dive_count_endian = COCHRAN_BE_TYPE; - device->data.sample_end_ptr = 256; - device->data.log_pre_dive_ptr = 30; - device->data.log_end_dive_ptr = 128; - device->data.last_interdive_ptr = 167; - device->data.last_entry_ptr = -1; - device->data.date_format = COCHRAN_DATE_FORMAT_MSDHYM; - device->data.address_length = COCHRAN_ADDRESS_LENGTH_24; - device->data.high_baud = 115200; - } - else - { - device->data.model = 0; - ERROR (abstract->context, - "Unknown Cochran model %02x %02x %02x %02x %02x %02x %02x %02x", - *(device->data.id + 0x3B), *(device->data.id + 0x3B + 1), - *(device->data.id + 0x3B + 2), *(device->data.id + 0x3B + 3), - *(device->data.id + 0x3B + 4), *(device->data.id + 0x3B + 5), - *(device->data.id + 0x3B + 6), *(device->data.id + 0x3B + 7)); - } - - return; -} - - -static dc_status_t -cochran_read_id (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - dc_status_t rc; - unsigned char command[6] = {0x05, 0x9D, 0xFF, 0x00, 0x43, 0x00}; - - rc = cochran_packet(device, command, 6, device->data.id, 67, 0); - if (rc != DC_STATUS_SUCCESS) - return rc; - - if (strncmp(device->data.id, "(C)", 3) != 0) { - // It's a Commander, read again - memcpy(device->data.id0, device->data.id, 67); - - command[1] = 0xBD; - command[2] = 0x7F; - - rc = cochran_packet(device, command, 6, device->data.id, 67, 0); - if (rc != DC_STATUS_SUCCESS) - return rc; - } - - cochran_set_device_config(device); - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -cochran_read_config (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *data = &device->data; - dc_status_t rc; - unsigned char command[2] = { 0x96, 0x00 }; - - if ((data->model & 0xFF0000) == COCHRAN_MODEL_EMC_FAMILY) - data->config_count = 2; - else - data->config_count = 4; - - int n; - for (n = 0; n < data->config_count; n++) { - command[1] = n; - rc = cochran_packet(device, command, 2, data->config[n], 512, 0); - if (rc != DC_STATUS_SUCCESS) - return rc; - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -cochran_read_misc (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - - unsigned char command[7] = { 0x89, 0x05, 0x00, 0x00, 0x00, 0xDC, 0x05 }; - - switch (device->data.model & 0xFF0000) - { - case COCHRAN_MODEL_COMMANDER_FAMILY: - command[2] = 0xCA; - command[3] = 0xFD; - break; - case COCHRAN_MODEL_EMC_FAMILY: - command[2] = 0xE0; - command[3] = 0x03; - break; - default: - return DC_STATUS_UNSUPPORTED; - } - - // Send first byte then wait for heartbeat before sending the rest - serial_write(device->port, command, 1); - - int n; - char answer[1]; - if ((n = serial_read(device->port, answer, 1)) != 1) { - ERROR (abstract->context, "Failed to receive device heartbeat."); - return EXITCODE (n); - } - - return cochran_packet(device, command + 1, 6, device->data.misc, 1500, 0); -} - - -static dc_status_t -cochran_read_logbook (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = &(device->data); - dc_status_t rc; - - if (d->logbook) - free(d->logbook); - - // Allocate space for log book. - d->logbook = (unsigned char *) malloc(d->logbook_size); - if (device == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Enable progress notifications. - device->progress = malloc(sizeof(dc_event_progress_t)); - if (device->progress == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - device->progress->current = 0; - device->progress->maximum = d->logbook_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); - - serial_sleep(device->port, 800); - - // set back to 9600 baud - cochran_common_serial_setup(device, abstract->context); - - // Request log book - rc = cochran_common_device_read(abstract, 0, d->logbook, d->logbook_size); - - device->progress->current = d->logbook_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); - free (device->progress); - - return rc; -} - - -static void -cochran_find_fingerprint(dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &(device->data); - - // Skip to fingerprint to reduce time - d->fp_dive_num = d->dive_count - 1; - - while (d->fp_dive_num >= 0 && memcmp(&(d->fingerprint), - d->logbook + d->fp_dive_num * d->log_size - + d->dive_num_ptr, - sizeof(d->fingerprint))) - d->fp_dive_num--; -} - - -static void -cochran_get_sample_parms(dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &(device->data); - unsigned int pre_dive_offset, end_dive_offset; - unsigned int low_offset, high_offset; - - // Find lowest and highest offsets into sample data - low_offset = 0xFFFFFFFF; - high_offset = 0; - - int i; - for (i = d->fp_dive_num + 1; i < d->dive_count; i++) { - pre_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size - + d->log_pre_dive_ptr])); - end_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size - + d->log_end_dive_ptr])); - - // 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) { - // Since I can't tell how much memory it has, I'll round. - // I'll round to 128K, dives longer than 12 hrs aren't likely - // and memory in sizes not rounded to 128K might be odd. - high_offset = ((pre_dive_offset - 1) & 0xE0000) + 0x20000; - d->sample_memory_end_address = high_offset; - low_offset = d->sample_memory_start_address; - d->sample_data_offset = low_offset; - d->sample_size = high_offset - low_offset; - } else if (low_offset < 0xFFFFFFFF && high_offset > 0) { - // Round offset and size to 16K boundary - d->sample_data_offset = low_offset & 0xFFFFC000; - high_offset = ((high_offset - 1) & 0xFFFFC000) + 0x4000; - d->sample_size = high_offset - d->sample_data_offset; - } else { - d->sample_data_offset = 0; - d->sample_size = 0; - } -} - - -static dc_status_t -cochran_read_samples(dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &(device->data); - dc_status_t rc; - - - if (d->sample_size > 0) { - if (d->sample) - free(d->sample); - - d->sample = (unsigned char *) malloc(d->sample_size); - if (device == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Enable progress notifications. - device->progress = malloc(sizeof(dc_event_progress_t)); - if (device->progress == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - device->progress->current = 0; - device->progress->maximum = d->sample_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); - - serial_sleep(device->port, 800); - - // set back to 9600 baud - cochran_common_serial_setup(device, abstract->context); - - // Read the sample data - rc = cochran_common_device_read (abstract, d->sample_data_offset, - d->sample, d->sample_size); - if (rc != DC_STATUS_SUCCESS) { - free (device->progress); - ERROR (abstract->context, "Failed to read the sample data."); - return rc; - } - - device->progress->current = d->sample_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress); - free (device->progress); - } - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -cochran_common_device_read_all (dc_device_t *abstract) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &(device->data); - - dc_status_t rc; - - // Read config - rc = cochran_read_config(abstract); - if (rc != DC_STATUS_SUCCESS) - return rc; - - rc = cochran_read_misc(abstract); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine size of dive list to read. Round up to nearest 16K - if (d->dive_count_endian == COCHRAN_LE_TYPE) - d->dive_count = array_uint16_le (d->config[0] + d->dive_count_ptr); - else - d->dive_count = array_uint16_be (d->config[0] + d->dive_count_ptr); - - d->logbook_size = ((d->dive_count * d->log_size) & 0xFFFFC000) - + 0x4000; - - rc = cochran_read_logbook(abstract); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine sample memory to read - cochran_find_fingerprint(abstract); - cochran_get_sample_parms(abstract); - - rc = cochran_read_samples(abstract); - if (rc != DC_STATUS_SUCCESS) - return rc; - - return DC_STATUS_SUCCESS; -} - - -#define pack_uint32_array_le(d, i) ((d)[0] = (i) & 0xff, \ - (d)[1] = ((i) >> 8) & 0xff, \ - (d)[2] = ((i) >> 16) & 0xff, \ - (d)[3] = ((i) >> 24) & 0xff) - -dc_status_t -cochran_common_device_dump (dc_device_t *abstract, dc_buffer_t *data) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &(device->data); - int ptr = 0; - - dc_status_t rc; - char *b; - int size; - - rc = cochran_common_device_read_all (abstract); - - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Reserve space for block pointers - // Structure is: - // int ptr; - // char data_valid_flag; - dc_buffer_resize(data, 10 * 5); - - // Set pointer to first block - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - if (d->extra_id_flag) { - dc_buffer_append(data, d->id0, 67); - *(dc_buffer_get_data(data) + ptr + 4) = 1; - } else { - dc_buffer_resize(data, size + 67); - b = dc_buffer_get_data(data); - - memset(b + size, 0, 67); - } - - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - dc_buffer_append (data, d->id, 67); - *(dc_buffer_get_data(data) + ptr + 4) = 1; - - // Add config blocks - int n; - for (n = 0; n < d->config_count; n++) { - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - dc_buffer_append (data, d->config[n], 512); - - *(dc_buffer_get_data(data) + ptr + 4) = 1; - } - - // Add blank config blocks - for (n = d->config_count; n < 4; n++) { - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - dc_buffer_resize(data, size + 512); - - b = dc_buffer_get_data(data); - memset(b + size, 0, 512); - } - - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - if (d->misc) { - dc_buffer_append (data, d->misc, 1500); - *(dc_buffer_get_data(data) + ptr + 4) = 1; - } else { - dc_buffer_resize(data, size + 1500); - - b = dc_buffer_get_data(data); - memset(b + size, 0, 1500); - } - - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - if (d->logbook) { - dc_buffer_append (data, d->logbook, d->logbook_size); - *(dc_buffer_get_data(data) + ptr + 4) = 1; - } else { - dc_buffer_resize(data, size + d->logbook_size); - - b = dc_buffer_get_data(data); - memset(b + size, 0, d->logbook_size); - } - - // Set pointer to next block - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - if (d->sample) { - dc_buffer_append (data, d->sample, d->sample_size); - *(dc_buffer_get_data(data) + ptr + 4) = 1; - } else { - dc_buffer_resize(data, dc_buffer_get_size(data) + d->sample_size); - - b = dc_buffer_get_data(data); - memset(b + size, 0, d->logbook_size); - } - - // Set pointer to end - ptr += 5; - size = dc_buffer_get_size(data); - pack_uint32_array_le(dc_buffer_get_data(data) + ptr, size); - - return DC_STATUS_SUCCESS; -} - - - -dc_status_t -cochran_common_device_foreach (dc_device_t *abstract, - dc_dive_callback_t callback, void *userdata) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = &(device->data); - unsigned int sample_start_offset, sample_end_offset; - struct tm t; - dc_status_t rc; - - rc = cochran_common_device_read_all (abstract); - - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Loop through each dive - int i; - for (i = d->dive_count - 1; i > d->fp_dive_num; i--) { - - d->current_log = d->logbook + i * d->log_size; - - sample_start_offset = array_uint32_le (d->current_log + 6); - sample_end_offset = array_uint32_le (d->current_log - + d->log_size/2); - - d->current_sample = d->sample + sample_start_offset - - d->sample_data_offset; - - // Check for corrupt post-dive section - if (array_uint32_le(d->current_log + d->log_size/2) == 0xFFFFFFFF) - d->corrupt_dive = 1; - else - d->corrupt_dive = 0; - - // Check for ring buffer wrap - if (sample_start_offset > sample_end_offset) - d->current_sample_size = d->sample_memory_end_address - - sample_start_offset + 1 + sample_end_offset - - d->sample_memory_start_address + 1; - else - d->current_sample_size = sample_end_offset - sample_start_offset; - - d->current_fingerprint = d->current_log + d->dive_num_ptr; - - if (d->date_format == COCHRAN_DATE_FORMAT_SMHDMY) { - t.tm_sec = d->current_log[0]; - t.tm_min = d->current_log[1]; - t.tm_hour = d->current_log[2]; - t.tm_mday = d->current_log[3]; - t.tm_mon = d->current_log[4]; - t.tm_year = d->current_log[5]; - t.tm_wday = t.tm_yday = t.tm_isdst = 0; - } else { - t.tm_sec = d->current_log[1]; - t.tm_min = d->current_log[0]; - t.tm_hour = d->current_log[3]; - t.tm_mday = d->current_log[2]; - t.tm_mon = d->current_log[5]; - t.tm_year = d->current_log[4]; - t.tm_wday = t.tm_yday = t.tm_isdst = 0; - } - d->current_dive_start_time = mktime(&t); - - if (callback && !callback ((unsigned char *) d, sizeof(device->data), - d->current_fingerprint, sizeof (d->fingerprint), userdata)) - return DC_STATUS_SUCCESS; - } - - return DC_STATUS_SUCCESS; -} diff --git a/src/cochran_common.h b/src/cochran_common.h deleted file mode 100644 index 3498696..0000000 --- a/src/cochran_common.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2014 John Van Ostrand - * - * 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 - */ - -// seconds to add to Cochran time stamps to get unix time -// Equivalent to Jan 1, 1992 00:00:00 -#define COCHRAN_TIMESTAMP_OFFSET 694224000 - -#define COCHRAN_LE_TYPE 0 -#define COCHRAN_BE_TYPE 1 - -#define COCHRAN_DATE_FORMAT_SMHDMY 0 -#define COCHRAN_DATE_FORMAT_MSDHYM 1 - -#define COCHRAN_ADDRESS_LENGTH_32 0 -#define COCHRAN_ADDRESS_LENGTH_24 1 - -typedef enum cochran_model_t { - COCHRAN_MODEL_UNKNOWN = 0, - COCHRAN_MODEL_EMC_FAMILY = 1 << 16, - COCHRAN_MODEL_EMC_14, - COCHRAN_MODEL_EMC_16, - COCHRAN_MODEL_EMC_20, - COCHRAN_MODEL_COMMANDER_FAMILY = 2 << 16, - COCHRAN_MODEL_COMMANDER_AIR_NITROX, -} cochran_model_t; - -typedef struct cochran_data_t { - cochran_model_t model; - - unsigned char id0[67]; - unsigned char id[67]; - unsigned char config[4][512]; - unsigned char misc[1500]; - unsigned char *logbook; - unsigned char *sample; - - unsigned int extra_id_flag; - unsigned int config_count; - - unsigned short int dive_count; - unsigned char fingerprint[2]; - int fp_dive_num; - - unsigned int logbook_size; - unsigned int current_sample_size; - - unsigned int sample_data_offset; - unsigned int sample_size; - unsigned int last_interdive_offset; - unsigned int last_entry_offset; - - unsigned char *current_fingerprint; - unsigned char *current_log; - unsigned char *current_sample; - time_t current_dive_start_time; - - // Config items - int log_size; - int sample_memory_start_address; - int sample_memory_end_address; - int dive_num_ptr; - int dive_count_ptr; - int dive_count_endian; - int sample_end_ptr; - int log_pre_dive_ptr; - int log_end_dive_ptr; - int last_interdive_ptr; - int last_entry_ptr; - int date_format; - int address_length; - int high_baud; // baud rate to switch to for log/sample download - - unsigned char corrupt_dive; -} cochran_data_t; - -typedef struct cochran_device_t { - dc_device_t base; - const char *name; // serial port name - serial_t *port; - cochran_data_t data; // dive data used in parsing - dc_event_progress_t *progress; // for progress in the _read function -} cochran_device_t; - - -// Commander log fields -#define CMD_SEC 1 -#define CMD_MIN 0 -#define CMD_HOUR 3 -#define CMD_DAY 2 -#define CMD_MON 5 -#define CMD_YEAR 4 -#define CME_START_OFFSET 6 // 4 bytes -#define CMD_WATER_CONDUCTIVITY 24 // 1 byte, 0=low, 2=high -#define CMD_START_TEMP 45 // 1 byte, F -#define CMD_START_DEPTH 56 // 2 byte, /4=ft -#define CMD_ALTITUDE 73 // 1 byte, /4=Kilofeet -#define CMD_END_OFFSET 128 // 4 bytes -#define CMD_MIN_TEMP 153 // 1 byte, F -#define CMD_BT 166 // 2 bytes, minutes -#define CMD_MAX_DEPTH 168 // 2 bytes, /4=ft -#define CMD_AVG_DEPTH 170 // 2 bytes, /4=ft -#define CMD_O2_PERCENT 210 // 8 bytes, 4 x 2 byte, /256=% - -// EMC log fields -#define EMC_SEC 0 -#define EMC_MIN 1 -#define EMC_HOUR 2 -#define EMC_DAY 3 -#define EMC_MON 4 -#define EMC_YEAR 5 -#define EMC_START_OFFSET 6 // 4 bytes -#define EMC_WATER_CONDUCTIVITY 25 // 1 byte, 0=low, 2=high -#define EMC_START_DEPTH 42 // 2 byte, /256=ft -#define EMC_START_TEMP 55 // 1 byte, F -#define EMC_ALTITUDE 89 // 1 byte, /4=Kilofeet -#define EMC_O2_PERCENT 144 // 20 bytes, 10 x 2 bytes, /256=% -#define EMC_HE_PERCENT 164 // 20 bytes, 10 x 2 bytes, /256=% -#define EMC_END_OFFSET 256 // 4 bytes -#define EMC_MIN_TEMP 293 // 1 byte, F -#define EMC_BT 304 // 2 bytes, minutes -#define EMC_MAX_DEPTH 306 // 2 bytes, /4=ft -#define EMC_AVG_DEPTH 310 // 2 bytes, /4=ft - - -dc_status_t cochran_packet (cochran_device_t *device, - const unsigned char command[], unsigned int csize, - unsigned char answer[], unsigned int asize, int high_speed); -dc_status_t cochran_common_device_open (dc_device_t **out, - dc_context_t *context, const char *name); -dc_status_t cochran_common_device_close (dc_device_t *abstract); -dc_status_t cochran_common_device_set_fingerprint (dc_device_t *abstract, - const unsigned char data[], unsigned int size); -dc_status_t cochran_common_device_read (dc_device_t *abstract, - unsigned int address, unsigned char data[], unsigned int size); -static dc_status_t cochran_read_id (dc_device_t *abstract); -dc_status_t cochran_common_device_foreach (dc_device_t *abstract, - dc_dive_callback_t callback, void *userdata); -dc_status_t cochran_common_device_dump (dc_device_t *abstract, - dc_buffer_t *data); diff --git a/src/cochran_common_parser.c b/src/cochran_common_parser.c deleted file mode 100644 index fefbc98..0000000 --- a/src/cochran_common_parser.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2014 John Van Ostrand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <stdlib.h> -#include <time.h> - -#include <libdivecomputer/units.h> -#include <libdivecomputer/cochran.h> - -#include "context-private.h" -#include "device-private.h" -#include "parser-private.h" -#include "serial.h" -#include "array.h" - -#include "cochran_common.h" -#include "cochran_common_parser.h" - - -dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract, - const unsigned char *data, unsigned int size); -dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract, - dc_datetime_t *datetime); -static dc_status_t cochran_common_parser_get_field (dc_parser_t *abstract, - dc_field_type_t type, unsigned int flags, void *value); -dc_status_t cochran_common_parser_destroy (dc_parser_t *abstract); -static dc_status_t cochran_common_parser_samples_foreach - (dc_parser_t *abstract, dc_sample_callback_t callback, - void *userdata); -int cochran_common_handle_event (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata, unsigned char code, - unsigned int offset, unsigned int time); - - -static dc_parser_vtable_t cochran_common_parser_vtable = { - DC_FAMILY_COCHRAN, - cochran_common_parser_set_data, /* set_data */ - cochran_common_parser_get_datetime, /* datetime */ - cochran_common_parser_get_field, /* fields */ - cochran_common_parser_samples_foreach, /* samples_foreach */ - cochran_common_parser_destroy /* destroy */ -}; - - - -dc_status_t -cochran_common_parser_create (dc_parser_t **out, dc_context_t *context) -{ - if (out == NULL) - return DC_STATUS_INVALIDARGS; - - // Allocate memory. - dc_parser_t *parser = (dc_parser_t *) malloc (sizeof (dc_parser_t)); - if (parser == NULL) { - ERROR (context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - parser_init (parser, context, &cochran_common_parser_vtable); - - *out = parser; - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_parser_destroy (dc_parser_t *abstract) -{ - // Free memory. - free (abstract); - - return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_common_parser_set_data (dc_parser_t *abstract, - const unsigned char *data, unsigned int size) -{ - abstract->data = data; - abstract->size = size; - - return DC_STATUS_SUCCESS; -} - - -// There are two date formats used by Cochran -dc_status_t -cochran_common_parser_get_datetime (dc_parser_t *abstract, - dc_datetime_t *datetime) -{ - cochran_data_t *data = (cochran_data_t *) abstract->data; - const unsigned char *log = data->current_log; - - if (data->date_format == COCHRAN_DATE_FORMAT_SMHDMY) { - datetime->second = log[0]; - datetime->minute = log[1]; - datetime->hour = log[2]; - datetime->day = log[3]; - datetime->month = log[4]; - datetime->year = log[5] + (log[5] > 91 ? 1900 : 2000); - } else { - datetime->second = log[1]; - datetime->minute = log[0]; - datetime->hour = log[3]; - datetime->day = log[2]; - datetime->month = log[5]; - datetime->year = log[4] + (log[5] > 91 ? 1900 : 2000); - } - - return DC_STATUS_SUCCESS; -} - -static dc_status_t -cochran_common_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, - unsigned int flags, void *value) -{ - cochran_data_t *data = (cochran_data_t *) abstract->data; - - switch (data->model & 0xFF0000) - { - case COCHRAN_MODEL_COMMANDER_FAMILY: - return cochran_cmdr_parser_get_field(abstract, type, flags, value); - break; - case COCHRAN_MODEL_EMC_FAMILY: - return cochran_emc_parser_get_field(abstract, type, flags, value); - break; - } -} - -static dc_status_t -cochran_common_parser_samples_foreach (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata) -{ - cochran_data_t *data = (cochran_data_t *) abstract->data; - - switch (data->model & 0xFF0000) - { - case COCHRAN_MODEL_COMMANDER_FAMILY: - return cochran_cmdr_parser_samples_foreach(abstract, callback, - userdata); - break; - case COCHRAN_MODEL_EMC_FAMILY: - return cochran_emc_parser_samples_foreach(abstract, callback, - userdata); - break; - } -} - - - -int -cochran_common_handle_event (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata, unsigned char code, - unsigned int offset, unsigned int time) -{ - cochran_data_t *data = (cochran_data_t *) abstract->data; - cochran_events_t *e = cochran_events; - - dc_sample_value_t sample = {0}; - - unsigned char event_ptr = 0; - - while (e[event_ptr].code && e[event_ptr].code != code) - event_ptr++; - - sample.event.time = 0; - - if (e[event_ptr].code) { - switch (e[event_ptr].code) - { - case 0xAB: // Ceiling decrease - // Indicated to lower ceiling by 10 ft (deeper) - // Bytes 1-2: first stop duration (min) - // Bytes 3-4: total stop duration (min) - // Handled in calling function - break; - case 0xAD: // Ceiling increase - // Indicates to raise ceiling by 10 ft (shallower) - // Handled in calling function - break; - default: - // Don't send known events of type NONE - if (! e[event_ptr].type == SAMPLE_EVENT_NONE) { - sample.event.type = e[event_ptr].type; - sample.event.flags = e[event_ptr].flag; - if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); - } - } - } else { - // Unknown event, send it so we know we missed something - sample.event.type = SAMPLE_EVENT_NONE; - sample.event.flags = SAMPLE_FLAGS_NONE; - sample.event.value = code; - if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); - } - - return e[event_ptr].data_bytes; -} diff --git a/src/cochran_common_parser.h b/src/cochran_common_parser.h deleted file mode 100644 index 9c4a67d..0000000 --- a/src/cochran_common_parser.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2014 John Van Ostrand - * - * 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 - */ - -typedef struct cochran_events_t { - unsigned char code; - unsigned char data_bytes; - char *name; - parser_sample_event_t type; - parser_sample_flags_t flag; -} cochran_events_t; - -static cochran_events_t cochran_events[] = { - { 0xA8, 1, "Entered PDI mode", - SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_BEGIN }, - { 0xA9, 1, "Exited PDI mode", - SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_END }, - { 0xAB, 5, "Ceiling decrease", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xAD, 5, "Ceiling increase", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xBD, 1, "Switched to nomal PO2 setting", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xC0, 1, "Switched to FO2 21% mode", - SAMPLE_EVENT_GASCHANGE, SAMPLE_FLAGS_NONE }, - { 0xC1, 1, "Ascent rate greater than limit", - SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN }, - { 0xC2, 1, "Low battery warning", - SAMPLE_EVENT_BATTERY, SAMPLE_FLAGS_NONE }, - { 0xC3, 1, "CNS Oxygen toxicity warning", - SAMPLE_EVENT_OLF, SAMPLE_FLAGS_NONE }, - { 0xC4, 1, "Depth exceeds user set point", - SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_NONE }, - { 0xC5, 1, "Entered decompression mode", - SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_BEGIN }, - { 0xC8, 1, "PO2 too high", - SAMPLE_EVENT_FLOOR, SAMPLE_FLAGS_BEGIN }, - { 0xCC, 1, "Low Cylinder 1 pressure", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, - { 0xCE, 1, "Non-decompression warning", - SAMPLE_EVENT_RBT, SAMPLE_FLAGS_BEGIN }, - { 0xCD, 1, "Switched to deco blend", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, - { 0xD0, 1, "Breathing rate alarm", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, - { 0xD3, 1, "Low gas 1 flow rate", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xD6, 1, "Depth is less than ceiling", - SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN }, - { 0xD8, 1, "End decompression mode", - SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_END }, - { 0xE1, 1, "End ascent rate warning", - SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_END }, - { 0xE2, 1, "Low SBAT battery warning", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xE3, 1, "Switched to FO2 mode", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xE5, 1, "Switched to PO2 mode", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, - { 0xEE, 1, "End non-decompresison warning", - SAMPLE_EVENT_RBT, SAMPLE_FLAGS_END }, - { 0xEF, 1, "Switch to blend 2", - SAMPLE_EVENT_GASCHANGE2, SAMPLE_FLAGS_NONE }, - { 0xF0, 1, "Breathing rate alarm", - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_END }, - { 0xF3, 1, "Switch to blend 1", - SAMPLE_EVENT_GASCHANGE2, SAMPLE_FLAGS_NONE }, - { 0xF6, 1, "End Depth is less than ceiling", - SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END }, - { 0x00, 1, NULL, - SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE } -}; - - -// Common -dc_status_t cochran_common_parser_create (dc_parser_t **out, - dc_context_t *context); -dc_status_t cochran_common_parser_destroy (dc_parser_t *abstract); -dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract, - const unsigned char *data, unsigned int size); -dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract, - dc_datetime_t *datetime); -int cochran_common_handle_event (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata, unsigned char code, - unsigned int offset, unsigned int time); - -// EMC FAMILY -dc_status_t cochran_emc_parser_get_field (dc_parser_t *abstract, - dc_field_type_t type, unsigned int flags, void *value); -dc_status_t cochran_emc_parser_samples_foreach (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata); - -// COMMANDER FAMILY -dc_status_t cochran_cmdr_parser_get_field(dc_parser_t *abstract, - dc_field_type_t type, unsigned int flags, void *value); -dc_status_t cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, - dc_sample_callback_t callback, void *userdata); - diff --git a/src/cochran_emc_parser.c b/src/cochran_emc_parser.c index e1f28ac..37089a7 100644 --- a/src/cochran_emc_parser.c +++ b/src/cochran_emc_parser.c @@ -32,8 +32,8 @@ #include "serial.h" #include "array.h"
-#include "cochran_common.h" -#include "cochran_common_parser.h" +#include "cochran_commander.h" +#include "cochran_commander_parser.h"
struct dive_stats { @@ -230,7 +230,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
// Check for event if (s[0] & 0x80) { - offset += cochran_common_handle_event(abstract, callback, userdata, + offset += cochran_commander_handle_event(abstract, callback, userdata, s[0], offset, time); switch (s[0]) { diff --git a/src/descriptor.c b/src/descriptor.c index 8836709..d4c2852 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -223,9 +223,9 @@ static const dc_descriptor_t g_descriptors[] = { {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0}, - {"Cochran", "Commander", DC_FAMILY_COCHRAN, 0}, - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN, 1}, - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN, 2}, + {"Cochran", "Commander", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 1}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 2}, };
typedef struct dc_descriptor_iterator_t { diff --git a/src/device.c b/src/device.c index cdd41ab..abbd6ec 100644 --- a/src/device.c +++ b/src/device.c @@ -155,8 +155,8 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_device_open (&device, context, name); break; - case DC_FAMILY_COCHRAN: - rc = cochran_common_device_open (&device, context, name); + case DC_FAMILY_COCHRAN_COMMANDER: + rc = cochran_commander_device_open (&device, context, name); break; default: return DC_STATUS_INVALIDARGS; diff --git a/src/parser.c b/src/parser.c index 07e9d2d..1759c81 100644 --- a/src/parser.c +++ b/src/parser.c @@ -132,8 +132,8 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_parser_create (&parser, context); break; - case DC_FAMILY_COCHRAN: - rc = cochran_common_parser_create (&parser, context); + case DC_FAMILY_COCHRAN_COMMANDER: + rc = cochran_commander_parser_create (&parser, context); break; default: return DC_STATUS_INVALIDARGS;