From 500fb1e16b8b80e984325b055f076dc661387ae2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 25 Sep 2012 21:19:16 +0200 Subject: [PATCH] Add support for the Shearwater Predator. --- examples/Makefile.am | 5 +- examples/shearwater_predator_test.c | 107 ++++ examples/universal.c | 3 +- include/libdivecomputer/Makefile.am | 4 +- include/libdivecomputer/common.h | 4 +- include/libdivecomputer/shearwater.h | 27 + include/libdivecomputer/shearwater_predator.h | 45 ++ src/Makefile.am | 1 + src/descriptor.c | 2 + src/device.c | 4 + src/libdivecomputer.symbols | 3 + src/parser.c | 4 + src/shearwater_predator.c | 574 ++++++++++++++++++ src/shearwater_predator_parser.c | 259 ++++++++ 14 files changed, 1038 insertions(+), 4 deletions(-) create mode 100644 examples/shearwater_predator_test.c create mode 100644 include/libdivecomputer/shearwater.h create mode 100644 include/libdivecomputer/shearwater_predator.h create mode 100644 src/shearwater_predator.c create mode 100644 src/shearwater_predator_parser.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 37155c2..ad9c4c0 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -23,7 +23,8 @@ bin_PROGRAMS = \ ostc \ frog \ edy \ - n2ition3 + n2ition3 \ + predator if IRDA bin_PROGRAMS += smart @@ -76,6 +77,8 @@ edy_SOURCES = cressi_edy_test.c $(COMMON) n2ition3_SOURCES = zeagle_n2ition3_test.c $(COMMON) +predator_SOURCES = shearwater_predator_test.c $(COMMON) + if IRDA smart_SOURCES = uwatec_smart_test.c $(COMMON) endif diff --git a/examples/shearwater_predator_test.c b/examples/shearwater_predator_test.c new file mode 100644 index 0000000..9f5463a --- /dev/null +++ b/examples/shearwater_predator_test.c @@ -0,0 +1,107 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // fopen, fwrite, fclose + +#include + +#include "utils.h" +#include "common.h" + +dc_status_t +test_dump_memory (const char* name, const char* filename) +{ + dc_context_t *context = NULL; + dc_device_t *device = NULL; + + dc_context_new (&context); + dc_context_set_loglevel (context, DC_LOGLEVEL_ALL); + dc_context_set_logfunc (context, logfunc, NULL); + + message ("shearwater_predator_device_open\n"); + dc_status_t rc = shearwater_predator_device_open (&device, context, name); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Error opening serial port."); + dc_context_free (context); + return rc; + } + + dc_buffer_t *buffer = dc_buffer_new (0); + + message ("dc_device_dump\n"); + rc = dc_device_dump (device, buffer); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Cannot read memory."); + dc_buffer_free (buffer); + dc_device_close (device); + dc_context_free (context); + return rc; + } + + message ("Dumping data\n"); + FILE* fp = fopen (filename, "wb"); + if (fp != NULL) { + fwrite (dc_buffer_get_data (buffer), sizeof (unsigned char), dc_buffer_get_size (buffer), fp); + fclose (fp); + } + + dc_buffer_free (buffer); + + message ("dc_device_close\n"); + rc = dc_device_close (device); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Cannot close device."); + dc_context_free (context); + return rc; + } + + dc_context_free (context); + + return DC_STATUS_SUCCESS; +} + + +int main(int argc, char *argv[]) +{ + message_set_logfile ("PREDATOR.LOG"); + +#ifdef _WIN32 + const char* name = "COM1"; +#else + const char* name = "/dev/ttyS0"; +#endif + + if (argc > 1) { + name = argv[1]; + } + + message ("DEVICE=%s\n", name); + + dc_status_t a = test_dump_memory (name, "PREDATOR.DMP"); + + message ("SUMMARY\n"); + message ("-------\n"); + message ("test_dump_memory: %s\n", errmsg (a)); + + message_set_logfile (NULL); + + return 0; +} diff --git a/examples/universal.c b/examples/universal.c index 34427f9..aacd730 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -95,7 +95,8 @@ static const backend_table_t g_backends[] = { {"frog", DC_FAMILY_HW_FROG}, {"edy", DC_FAMILY_CRESSI_EDY}, {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, - {"cobalt", DC_FAMILY_ATOMICS_COBALT} + {"cobalt", DC_FAMILY_ATOMICS_COBALT}, + {"predator", DC_FAMILY_SHEARWATER_PREDATOR} }; static dc_family_t diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index 80cfd9d..d4cc7bb 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -41,4 +41,6 @@ libdivecomputer_HEADERS = \ zeagle.h \ zeagle_n2ition3.h \ atomics.h \ - atomics_cobalt.h + atomics_cobalt.h \ + shearwater.h \ + shearwater_predator.h diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index ab827b5..7a79975 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -74,7 +74,9 @@ typedef enum dc_family_t { /* Zeagle */ DC_FAMILY_ZEAGLE_N2ITION3 = (8 << 16), /* Atomic Aquatics */ - DC_FAMILY_ATOMICS_COBALT = (9 << 16) + DC_FAMILY_ATOMICS_COBALT = (9 << 16), + /* Shearwater */ + DC_FAMILY_SHEARWATER_PREDATOR = (10 << 16) } dc_family_t; #ifdef __cplusplus diff --git a/include/libdivecomputer/shearwater.h b/include/libdivecomputer/shearwater.h new file mode 100644 index 0000000..fe8b5a1 --- /dev/null +++ b/include/libdivecomputer/shearwater.h @@ -0,0 +1,27 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 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 + */ + +#ifndef SHEARWATER_H +#define SHEARWATER_H + +#include "shearwater_predator.h" + +#endif /* SHEARWATER_H */ diff --git a/include/libdivecomputer/shearwater_predator.h b/include/libdivecomputer/shearwater_predator.h new file mode 100644 index 0000000..40c7ae2 --- /dev/null +++ b/include/libdivecomputer/shearwater_predator.h @@ -0,0 +1,45 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 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 + */ + +#ifndef SHEARWATER_PREDATOR_H +#define SHEARWATER_PREDATOR_H + +#include "context.h" +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, const char *name); + +dc_status_t +shearwater_predator_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); + +dc_status_t +shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* SHEARWATER_PREDATOR_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 89d0ab1..fc5e0fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ libdivecomputer_la_SOURCES = \ cressi_edy.c cressi_edy_parser.c \ zeagle_n2ition3.c \ atomics_cobalt.c atomics_cobalt_parser.c \ + shearwater_predator.c shearwater_predator_parser.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ array.h array.c \ diff --git a/src/descriptor.c b/src/descriptor.c index d35c690..ada4158 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -162,6 +162,8 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveRite", "NiTek Trio", DC_FAMILY_ZEAGLE_N2ITION3, 0}, /* Atomic Aquatics Cobalt */ {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0}, + /* Shearwater Predator */ + {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, }; typedef struct dc_descriptor_iterator_t { diff --git a/src/device.c b/src/device.c index 5f599b4..3fde549 100644 --- a/src/device.c +++ b/src/device.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "device-private.h" #include "context-private.h" @@ -134,6 +135,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_ATOMICS_COBALT: rc = atomics_cobalt_device_open (&device, context); break; + case DC_FAMILY_SHEARWATER_PREDATOR: + rc = shearwater_predator_device_open (&device, context, name); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index b0c062e..7769d03 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -62,6 +62,7 @@ hw_ostc_parser_create cressi_edy_parser_create atomics_cobalt_parser_create atomics_cobalt_parser_set_calibration +shearwater_predator_parser_create dc_device_open dc_device_close @@ -152,3 +153,5 @@ zeagle_n2ition3_device_open atomics_cobalt_device_open atomics_cobalt_device_version atomics_cobalt_device_set_simulation +shearwater_predator_device_open +shearwater_predator_extract_dives diff --git a/src/parser.c b/src/parser.c index f8090f6..0e3a9e0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "parser-private.h" #include "device-private.h" @@ -110,6 +111,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) case DC_FAMILY_ATOMICS_COBALT: rc = atomics_cobalt_parser_create (&parser, context); break; + case DC_FAMILY_SHEARWATER_PREDATOR: + rc = shearwater_predator_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c new file mode 100644 index 0000000..9fc9eac --- /dev/null +++ b/src/shearwater_predator.c @@ -0,0 +1,574 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // memcmp, memcpy +#include // malloc, free + +#include + +#include "context-private.h" +#include "device-private.h" +#include "serial.h" +#include "array.h" + +#define SZ_PACKET 254 +#define SZ_BLOCK 0x80 +#define SZ_MEMORY 0x20080 + +#define RB_PROFILE_BEGIN 0 +#define RB_PROFILE_END 0x1F600 + +// SLIP special character codes +#define END 0xC0 +#define ESC 0xDB +#define ESC_END 0xDC +#define ESC_ESC 0xDD + +#define EXITCODE(n) ((n) < 0 ? (n) : 0) + +typedef struct shearwater_predator_device_t { + dc_device_t base; + serial_t *port; + unsigned char fingerprint[4]; +} shearwater_predator_device_t; + +static dc_status_t shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); +static dc_status_t shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t shearwater_predator_device_close (dc_device_t *abstract); + +static const device_backend_t shearwater_predator_device_backend = { + DC_FAMILY_SHEARWATER_PREDATOR, + shearwater_predator_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + shearwater_predator_device_dump, /* dump */ + shearwater_predator_device_foreach, /* foreach */ + shearwater_predator_device_close /* close */ +}; + + +static int +device_is_shearwater_predator (dc_device_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &shearwater_predator_device_backend; +} + + +static int +shearwater_predator_slip_write (shearwater_predator_device_t *device, const unsigned char data[], unsigned int size) +{ + int n = 0; + const unsigned char end[] = {END}; + const unsigned char esc_end[] = {ESC, ESC_END}; + const unsigned char esc_esc[] = {ESC, ESC_ESC}; + +#if 0 + // Send an initial END character to flush out any data that may have + // accumulated in the receiver due to line noise. + n = serial_write (device->port, end, sizeof (end)); + if (n != sizeof (end)) { + return EXITCODE(n); + } +#endif + + for (unsigned int i = 0; i < size; ++i) { + const unsigned char *seq = NULL; + unsigned int len = 0; + switch (data[i]) { + case END: + // Escape the END character. + seq = esc_end; + len = sizeof (esc_end); + break; + case ESC: + // Escape the ESC character. + seq = esc_esc; + len = sizeof (esc_esc); + break; + default: + // Normal character. + seq = data + i; + len = 1; + break; + } + + n = serial_write (device->port, seq, len); + if (n != len) { + return EXITCODE(n); + } + } + + // Send the END character to indicate the end of the packet. + n = serial_write (device->port, end, sizeof (end)); + if (n != sizeof (end)) { + return EXITCODE(n); + } + + return size; +} + + +static int +shearwater_predator_slip_read (shearwater_predator_device_t *device, unsigned char data[], unsigned int size) +{ + unsigned int received = 0; + + // Read bytes until a complete packet has been received. If the + // buffer runs out of space, bytes are dropped. The caller can + // detect this condition because the return value will be larger + // than the supplied buffer size. + while (1) { + unsigned char c = 0; + int n = 0; + + // Get a single character to process. + n = serial_read (device->port, &c, 1); + if (n != 1) { + return EXITCODE(n); + } + + switch (c) { + case END: + // If it's an END character then we're done. + // As a minor optimization, empty packets are ignored. This + // is to avoid bothering the upper layers with all the empty + // packets generated by the duplicate END characters which + // are sent to try to detect line noise. + if (received) + return received; + else + break; + case ESC: + // If it's an ESC character, get another character and then + // figure out what to store in the packet based on that. + n = serial_read (device->port, &c, 1); + if (n != 1) { + return EXITCODE(n); + } + + // If it's not one of the two escaped characters, then we + // have a protocol violation. The best bet seems to be to + // leave the byte alone and just stuff it into the packet. + switch (c) { + case ESC_END: + c = END; + break; + case ESC_ESC: + c = ESC; + break; + } + // Fall-through! + default: + if (received < size) + data[received] = c; + received++; + } + } + + return received; +} + + +static dc_status_t +shearwater_predator_transfer (shearwater_predator_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual) +{ + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[SZ_PACKET + 4]; + int n = 0; + + if (isize > SZ_PACKET || osize > SZ_PACKET) + return DC_STATUS_INVALIDARGS; + + // Setup the request packet. + packet[0] = 0xFF; + packet[1] = 0x01; + packet[2] = isize + 1; + packet[3] = 0x00; + memcpy (packet + 4, input, isize); + + // Send the request packet. + n = shearwater_predator_slip_write (device, packet, isize + 4); + if (n != isize + 4) { + ERROR (abstract->context, "Failed to send the request packet."); + if (n < 0) + return DC_STATUS_IO; + else + return DC_STATUS_TIMEOUT; + } + + // Receive the response packet. + n = shearwater_predator_slip_read (device, packet, sizeof (packet)); + if (n <= 0 || n > sizeof (packet)) { + ERROR (abstract->context, "Failed to receive the response packet."); + if (n < 0) + return DC_STATUS_IO; + else if (n > sizeof (packet)) + return DC_STATUS_PROTOCOL; + else + return DC_STATUS_TIMEOUT; + } + + // Validate the packet header. + if (n < 4 || packet[0] != 0x01 || packet[1] != 0xFF || packet[3] != 0x00) { + ERROR (abstract->context, "Invalid packet header."); + return DC_STATUS_PROTOCOL; + } + + // Validate the packet length. + unsigned int length = packet[2]; + if (length < 1 || length - 1 + 4 != n || length - 1 > osize) { + ERROR (abstract->context, "Invalid packet header."); + return DC_STATUS_PROTOCOL; + } + + memcpy (output, packet + 4, length - 1); + *actual = length - 1; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + shearwater_predator_device_t *device = (shearwater_predator_device_t *) malloc (sizeof (shearwater_predator_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base, context, &shearwater_predator_device_backend); + + // Set the default values. + device->port = NULL; + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Open the device. + int rc = serial_open (&device->port, context, name); + if (rc == -1) { + ERROR (context, "Failed to open the serial port."); + free (device); + return DC_STATUS_IO; + } + + // Set the serial communication protocol (115200 8N1). + rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + ERROR (context, "Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Set the timeout for receiving data (3000ms). + if (serial_set_timeout (device->port, 3000) == -1) { + ERROR (context, "Failed to set the timeout."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Make sure everything is in a sane state. + serial_sleep (device->port, 300); + serial_flush (device->port, SERIAL_QUEUE_BOTH); + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_device_close (dc_device_t *abstract) +{ + shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract; + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DC_STATUS_IO; + } + + // Free memory. + free (device); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + shearwater_predator_device_t *device = (shearwater_predator_device_t *) abstract; + + if (size && size != sizeof (device->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) +{ + shearwater_predator_device_t *device = (shearwater_predator_device_t *) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + unsigned int n = 0; + + unsigned char req_init[] = { + 0x35, 0x00, 0x34, + 0xDD, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x80}; + unsigned char req_block[] = {0x36, 0x00}; + unsigned char req_quit[] = {0x37}; + unsigned char response[SZ_PACKET]; + + // Erase the current contents of the buffer. + if (!dc_buffer_clear (buffer) || !dc_buffer_reserve (buffer, SZ_MEMORY)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = 3 + SZ_MEMORY + 1; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Transfer the init request. + rc = shearwater_predator_transfer (device, req_init, sizeof (req_init), response, 3, &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the init response. + if (n != 3 || response[0] != 0x75 || response[1] != 0x10 || response[2] != 0x82) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += 3; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + unsigned char block = 1; + unsigned int nbytes = 0; + while (nbytes < SZ_MEMORY) { + // Transfer the block request. + req_block[1] = block; + rc = shearwater_predator_transfer (device, req_block, sizeof (req_block), response, sizeof (response), &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the block header. + if (n < 2 || response[0] != 0x76 || response[1] != block) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Verify the block length. + unsigned int length = n - 2; + if (nbytes + length > SZ_MEMORY) { + ERROR (abstract->context, "Unexpected packet size."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += length; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + dc_buffer_append (buffer, response + 2, length); + + nbytes += length; + block++; + } + + // Transfer the quit request. + rc = shearwater_predator_transfer (device, req_quit, sizeof (req_quit), response, 2, &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the quit response. + if (n != 2 || response[0] != 0x77 || response[1] != 0x00) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += 1; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_buffer_t *buffer = dc_buffer_new (SZ_MEMORY); + if (buffer == NULL) + return DC_STATUS_NOMEMORY; + + dc_status_t rc = shearwater_predator_device_dump (abstract, buffer); + if (rc != DC_STATUS_SUCCESS) { + dc_buffer_free (buffer); + return rc; + } + + // Emit a device info event. + unsigned char *data = dc_buffer_get_data (buffer); + dc_event_devinfo_t devinfo; + devinfo.model = data[0x2000D]; + devinfo.firmware = data[0x2000A]; + devinfo.serial = array_uint32_le (data + 0x20002); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + rc = shearwater_predator_extract_dives (abstract, data, SZ_MEMORY, callback, userdata); + + dc_buffer_free (buffer); + + return rc; +} + +dc_status_t +shearwater_predator_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) +{ + shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract; + dc_context_t *context = (abstract ? abstract->context : NULL); + + if (abstract && !device_is_shearwater_predator (abstract)) + return DC_STATUS_INVALIDARGS; + + 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 maximum = 0; + unsigned int eop = RB_PROFILE_END; + + // Search the ringbuffer backwards to locate matching header and + // footer markers. Because the ringbuffer search algorithm starts at + // some arbitrary position, which does not necessary corresponds + // with a boundary between two dives, the begin position is adjusted + // as soon as the first dive has been found. Without this step, + // dives crossing the ringbuffer wrap point won't be detected when + // searching backwards from the ringbuffer end offset. + unsigned int footer = 0; + unsigned int have_footer = 0; + unsigned int begin = RB_PROFILE_BEGIN; + unsigned int offset = RB_PROFILE_END; + while (offset != begin) { + // Handle the ringbuffer wrap point. + if (offset == RB_PROFILE_BEGIN) + offset = RB_PROFILE_END; + + // Move to the start of the block. + offset -= SZ_BLOCK; + + if (array_isequal (data + offset, SZ_BLOCK, 0xFF)) { + // Ignore empty blocks explicitly, because otherwise they are + // incorrectly recognized as header markers. + } else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFF && have_footer) { + // If the first header marker is found, the begin offset is moved + // after the corresponding footer marker. This is necessary to be + // able to detect dives that cross the ringbuffer wrap point. + if (begin == RB_PROFILE_BEGIN) + begin = footer + SZ_BLOCK; + + // Get the internal dive number. + unsigned int current = array_uint16_be (data + offset + 2); + if (current > maximum) { + maximum = current; + eop = footer + SZ_BLOCK; + } + + // The dive number in the header and footer should be identical. + if (current != array_uint16_be (data + footer + 2)) { + ERROR (context, "Unexpected dive number."); + return DC_STATUS_DATAFORMAT; + } + + // Reset the footer marker. + have_footer = 0; + } else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFE) { + // Remember the footer marker. + footer = offset; + have_footer = 1; + } + } + + // Allocate memory for the profiles. + unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN); + if (buffer == NULL) { + return DC_STATUS_NOMEMORY; + } + + // Linearize the ringbuffer. + memcpy (buffer + 0, data + eop, RB_PROFILE_END - eop); + memcpy (buffer + RB_PROFILE_END - eop, data + RB_PROFILE_BEGIN, eop - RB_PROFILE_BEGIN); + + // Find the dives again in the linear buffer. + footer = 0; + have_footer = 0; + offset = RB_PROFILE_END; + while (offset != RB_PROFILE_BEGIN) { + // Move to the start of the block. + offset -= SZ_BLOCK; + + if (array_isequal (buffer + offset, SZ_BLOCK, 0xFF)) { + break; + } else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFF && have_footer) { + // Check the fingerprint data. + if (device && memcmp (buffer + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0) + break; + + if (callback && !callback (buffer + offset, footer + SZ_BLOCK - offset, buffer + offset + 12, sizeof (device->fingerprint), userdata)) + break; + + have_footer = 0; + } else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFE) { + footer = offset; + have_footer = 1; + } + } + + free (buffer); + + return DC_STATUS_SUCCESS; +} diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c new file mode 100644 index 0000000..4c52ce4 --- /dev/null +++ b/src/shearwater_predator_parser.c @@ -0,0 +1,259 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +#include +#include + +#include "context-private.h" +#include "parser-private.h" +#include "array.h" + +#define SZ_BLOCK 0x80 +#define SZ_SAMPLE 0x10 + +#define METRIC 0 +#define IMPERIAL 1 + +typedef struct shearwater_predator_parser_t shearwater_predator_parser_t; + +struct shearwater_predator_parser_t { + dc_parser_t base; +}; + +static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); +static dc_status_t shearwater_predator_parser_destroy (dc_parser_t *abstract); + +static const parser_backend_t shearwater_predator_parser_backend = { + DC_FAMILY_SHEARWATER_PREDATOR, + shearwater_predator_parser_set_data, /* set_data */ + shearwater_predator_parser_get_datetime, /* datetime */ + shearwater_predator_parser_get_field, /* fields */ + shearwater_predator_parser_samples_foreach, /* samples_foreach */ + shearwater_predator_parser_destroy /* destroy */ +}; + + +static int +parser_is_shearwater_predator (dc_parser_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &shearwater_predator_parser_backend; +} + + +dc_status_t +shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) malloc (sizeof (shearwater_predator_parser_t)); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + parser_init (&parser->base, context, &shearwater_predator_parser_backend); + + *out = (dc_parser_t *) parser; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_parser_destroy (dc_parser_t *abstract) +{ + if (! parser_is_shearwater_predator (abstract)) + return DC_STATUS_INVALIDARGS; + + // Free memory. + free (abstract); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + if (! parser_is_shearwater_predator (abstract)) + return DC_STATUS_INVALIDARGS; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) +{ + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 2 * SZ_BLOCK) + return DC_STATUS_DATAFORMAT; + + unsigned int ticks = array_uint32_be (data + 12); + + if (!dc_datetime_localtime (datetime, ticks)) + return DC_STATUS_DATAFORMAT; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 2 * SZ_BLOCK) + return DC_STATUS_DATAFORMAT; + + // Get the unit system. + unsigned int units = data[8]; + + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_salinity_t *water = (dc_salinity_t *) value; + unsigned int density = 0; + + if (value) { + switch (type) { + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = array_uint16_be (data + size - SZ_BLOCK + 6) * 60; + break; + case DC_FIELD_MAXDEPTH: + if (units == IMPERIAL) + *((double *) value) = array_uint16_be (data + size - SZ_BLOCK + 4) * FEET; + else + *((double *) value) = array_uint16_be (data + size - SZ_BLOCK + 4); + break; + case DC_FIELD_GASMIX_COUNT: + *((unsigned int *) value) = 5; + break; + case DC_FIELD_GASMIX: + gasmix->oxygen = data[20 + flags] / 100.0; + gasmix->helium = data[30 + flags] / 100.0; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + case DC_FIELD_SALINITY: + density = array_uint16_be (data + 83); + if (density == 1000) + water->type = DC_WATER_FRESH; + else + water->type = DC_WATER_SALT; + water->density = density; + break; + case DC_FIELD_ATMOSPHERIC: + *((double *) value) = array_uint16_be (data + 47) / 1000.0; + break; + default: + return DC_STATUS_UNSUPPORTED; + } + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 2 * SZ_BLOCK) + return DC_STATUS_DATAFORMAT; + + // Get the unit system. + unsigned int units = data[8]; + + unsigned int time = 0; + unsigned int offset = SZ_BLOCK; + while (offset + SZ_BLOCK < size) { + dc_sample_value_t sample = {0}; + + // Ignore empty samples. + if (array_isequal (data + offset, SZ_SAMPLE, 0x00)) { + offset += SZ_SAMPLE; + continue; + } + + // Time (seconds). + time += 10; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Depth (1/10 m or ft). + unsigned int depth = array_uint16_be (data + offset); + if (units == IMPERIAL) + sample.depth = depth * FEET / 10.0; + else + sample.depth = depth / 10.0; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + // Temperature (°C or °F). + unsigned int temperature = data[offset + 13]; + if (units == IMPERIAL) + sample.temperature = (temperature - 32.0) * (5.0 / 9.0); + else + sample.temperature = temperature; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + + // Gaschange. + sample.event.type = SAMPLE_EVENT_GASCHANGE2; + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = data[offset + 7] | (data[offset + 8] << 16); + if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + + // Deco stop / NDL. + unsigned int decostop = array_uint16_be (data + offset + 2); + if (decostop) { + sample.event.type = SAMPLE_EVENT_DECOSTOP; + if (units == IMPERIAL) + sample.event.value = decostop * FEET + 0.5; + else + sample.event.value = decostop; + sample.event.value |= (data[offset + 9] * 60) << 16; + } else { + sample.event.type = SAMPLE_EVENT_NDL; + sample.event.value = data[offset + 9] * 60; + } + sample.event.time = 0; + sample.event.flags = 0; + if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); + + offset += SZ_SAMPLE; + } + + return DC_STATUS_SUCCESS; +}