The Petrel is slightly different from the Predator because the device reorders the internal ringbuffer before sending the data. The most recent dive is always the first one, and there is no need to search for it, like we have to do for the Predator.
640 lines
18 KiB
C
640 lines
18 KiB
C
/*
|
|
* 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 <string.h> // memcmp, memcpy
|
|
#include <stdlib.h> // malloc, free
|
|
|
|
#include <libdivecomputer/shearwater_predator.h>
|
|
|
|
#include "context-private.h"
|
|
#include "device-private.h"
|
|
#include "serial.h"
|
|
#include "array.h"
|
|
|
|
#define PREDATOR 2
|
|
#define PETREL 3
|
|
|
|
#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;
|
|
}
|
|
|
|
|
|
static dc_status_t
|
|
shearwater_predator_extract_predator (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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
static dc_status_t
|
|
shearwater_predator_extract_petrel (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);
|
|
|
|
// Search the ringbuffer to locate matching header and footer
|
|
// markers. Because the Petrel does reorder the internal ringbuffer
|
|
// before sending the data, the most recent dive is always the first
|
|
// one. Therefore, there is no need to search for it, as we have to
|
|
// do for the Predator.
|
|
unsigned int header = 0;
|
|
unsigned int have_header = 0;
|
|
unsigned int offset = RB_PROFILE_BEGIN;
|
|
while (offset != RB_PROFILE_END) {
|
|
if (array_isequal (data + offset, SZ_BLOCK, 0xFF)) {
|
|
// Ignore empty blocks explicitly, because otherwise they are
|
|
// incorrectly recognized as header markers.
|
|
break;
|
|
} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFF) {
|
|
// Remember the header marker.
|
|
header = offset;
|
|
have_header = 1;
|
|
} else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFE && have_header) {
|
|
// The dive number in the header and footer should be identical.
|
|
if (memcmp (data + header + 2, data + offset + 2, 2) != 0) {
|
|
ERROR (context, "Unexpected dive number.");
|
|
return DC_STATUS_DATAFORMAT;
|
|
}
|
|
|
|
// Check the fingerprint data.
|
|
if (device && memcmp (data + header + 12, device->fingerprint, sizeof (device->fingerprint)) == 0)
|
|
break;
|
|
|
|
if (callback && !callback (data + header, offset + SZ_BLOCK - header, data + header + 12, sizeof (device->fingerprint), userdata))
|
|
break;
|
|
|
|
// Reset the header marker.
|
|
have_header = 0;
|
|
}
|
|
|
|
offset += SZ_BLOCK;
|
|
}
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
if (abstract && !device_is_shearwater_predator (abstract))
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
if (size < SZ_MEMORY)
|
|
return DC_STATUS_DATAFORMAT;
|
|
|
|
unsigned int model = data[0x2000D];
|
|
|
|
if (model == PETREL) {
|
|
return shearwater_predator_extract_petrel (abstract, data, size, callback, userdata);
|
|
} else {
|
|
return shearwater_predator_extract_predator (abstract, data, size, callback, userdata);
|
|
}
|
|
}
|