Use the new ringbuffer stream

All dive computer backends are updated to use the new ringbuffer stream.
This commit is contained in:
Jef Driesen 2016-10-08 21:24:14 +02:00
parent 3f82a553bd
commit 2646e4df86
4 changed files with 160 additions and 373 deletions

View File

@ -31,6 +31,7 @@
#include "checksum.h"
#include "array.h"
#include "ringbuffer.h"
#include "rbstream.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &cressi_edy_device_vtable)
@ -100,20 +101,6 @@ static const cressi_edy_layout_t tusa_iq700_layout = {
0x3C, /* config */
};
static unsigned int
ifloor (unsigned int x, unsigned int n)
{
// Round down to next lower multiple.
return (x / n) * n;
}
static unsigned int
iceil (unsigned int x, unsigned int n)
{
// Round up to next higher multiple.
return ((x + n - 1) / n) * n;
}
static dc_status_t
cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, int trailer)
{
@ -492,41 +479,29 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
idx--;
}
// Because dives are not necessary aligned to packet boundaries, and
// we always do aligned reads, there can be padding bytes present on
// both sides of the memory buffer. These extra bytes need to be
// included in the total length.
total += (previous - ifloor(previous, SZ_PACKET)) +
(iceil(eop, SZ_PACKET) - eop);
// Update and emit a progress event.
progress.current += SZ_PACKET;
progress.maximum = SZ_PACKET + total;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
// Memory buffer for the profile data.
unsigned char *buffer = (unsigned char *) malloc (total);
if (buffer == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
dc_rbstream_free (rbstream);
return DC_STATUS_NOMEMORY;
}
unsigned int available = 0;
unsigned int offset = total;
// Align the ringbuffer to packet boundaries. This results in a
// virtual ringbuffer that is slightly larger than the actual
// ringbuffer. Data outside the real ringbuffer is downloaded
// and then immediately dropped.
unsigned int rb_profile_begin = ifloor(layout->rb_profile_begin, SZ_PACKET);
unsigned int rb_profile_end = iceil(layout->rb_profile_end, SZ_PACKET);
// Align the initial memory address to the next packet boundary, and
// calculate the amount of padding bytes, so we can easily skip
// them later.
unsigned int address = iceil(eop, SZ_PACKET);
unsigned int skip = address - eop;
idx = last;
previous = eop;
for (unsigned int i = 0; i < count; ++i) {
@ -534,6 +509,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
unsigned int current = array_uint_le (logbook + idx * layout->rb_logbook_size, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin;
if (current < layout->rb_profile_begin || current >= layout->rb_profile_end) {
ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x).", current);
dc_rbstream_free (rbstream);
free(buffer);
return DC_STATUS_DATAFORMAT;
}
@ -541,51 +517,19 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
// Get the profile length.
unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end);
unsigned nbytes = available;
while (nbytes < length) {
if (address == rb_profile_begin)
address = rb_profile_end;
address -= SZ_PACKET;
// Move to the begin of the current dive.
offset -= length;
// Read the memory page.
unsigned char packet[SZ_PACKET];
rc = cressi_edy_device_read (abstract, address, packet, sizeof(packet));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
free(buffer);
return rc;
}
// At the head and tail of the ringbuffer, the packet can
// contain extra data, originating from the larger virtual
// ringbuffer. This data must be removed from the packet.
unsigned int head = 0;
unsigned int tail = 0;
unsigned int len = SZ_PACKET;
if (address < layout->rb_profile_begin) {
head = layout->rb_profile_begin - address;
}
if (address + SZ_PACKET > layout->rb_profile_end) {
tail = (address + SZ_PACKET) - layout->rb_profile_end;
}
len -= head + tail;
offset -= len;
// Copy the data packet to the buffer.
memcpy(buffer + offset, packet + head, len);
// Update and emit a progress event.
progress.current += len;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
nbytes += len - skip;
skip = 0;
// Read the dive.
rc = dc_rbstream_read (rbstream, &progress, buffer + offset, length);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (buffer);
return rc;
}
available = nbytes - length;
previous = current;
unsigned char *p = buffer + offset + available;
unsigned char *p = buffer + offset;
if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0)
break;
@ -593,11 +537,14 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata))
break;
previous = current;
if (idx == layout->rb_logbook_begin)
idx = layout->rb_logbook_end;
idx--;
}
dc_rbstream_free (rbstream);
free(buffer);
return DC_STATUS_SUCCESS;

View File

@ -27,6 +27,7 @@
#include "context-private.h"
#include "device-private.h"
#include "ringbuffer.h"
#include "rbstream.h"
#include "array.h"
#define VTABLE(abstract) ((oceanic_common_device_vtable_t *) abstract->vtable)
@ -39,22 +40,6 @@
#define INVALID 0
static unsigned int
ifloor (unsigned int x, unsigned int n)
{
// Round down to next lower multiple.
return (x / n) * n;
}
static unsigned int
iceil (unsigned int x, unsigned int n)
{
// Round up to next higher multiple.
return ((x + n - 1) / n) * n;
}
static unsigned int
get_profile_first (const unsigned char data[], const oceanic_common_layout_t *layout)
{
@ -234,177 +219,94 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr
return DC_STATUS_DATAFORMAT;
}
// Convert the first/last pointers to begin/end/count pointers.
unsigned int rb_logbook_entry_begin, rb_logbook_entry_end,
rb_logbook_entry_size;
// Calculate the end pointer and the number of bytes.
unsigned int rb_logbook_end, rb_logbook_size;
if (layout->pt_mode_global == 0) {
rb_logbook_entry_begin = rb_logbook_first;
rb_logbook_entry_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout);
rb_logbook_entry_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size;
rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout);
rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size;
} else {
rb_logbook_entry_begin = rb_logbook_first;
rb_logbook_entry_end = rb_logbook_last;
rb_logbook_entry_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout);
rb_logbook_end = rb_logbook_last;
rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout);
// In a typical ringbuffer implementation with only two begin/end
// pointers, there is no distinction possible between an empty and
// a full ringbuffer. We always consider the ringbuffer full in
// that case, because an empty ringbuffer can be detected by
// inspecting the logbook entries once they are downloaded.
if (rb_logbook_first == rb_logbook_last)
rb_logbook_entry_size = layout->rb_logbook_end - layout->rb_logbook_begin;
rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin;
}
// Check whether the ringbuffer is full.
int full = (rb_logbook_entry_size == (layout->rb_logbook_end - layout->rb_logbook_begin));
// Align the pointers to page boundaries.
unsigned int rb_logbook_page_begin, rb_logbook_page_end,
rb_logbook_page_size;
if (full) {
// Full ringbuffer.
rb_logbook_page_begin = iceil (rb_logbook_entry_end, PAGESIZE);
rb_logbook_page_end = rb_logbook_page_begin;
rb_logbook_page_size = rb_logbook_entry_size;
} else {
// Non-full ringbuffer.
rb_logbook_page_begin = ifloor (rb_logbook_entry_begin, PAGESIZE);
rb_logbook_page_end = iceil (rb_logbook_entry_end, PAGESIZE);
rb_logbook_page_size = rb_logbook_entry_size +
(rb_logbook_entry_begin - rb_logbook_page_begin) +
(rb_logbook_page_end - rb_logbook_entry_end);
}
// Check whether the last entry is not aligned to a page boundary.
int unaligned = (rb_logbook_entry_end != rb_logbook_page_end);
// Update and emit a progress event.
progress->current += PAGESIZE;
progress->maximum += PAGESIZE;
progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_page_size;
progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
// Exit if there are no dives.
if (rb_logbook_page_size == 0) {
if (rb_logbook_size == 0) {
return DC_STATUS_SUCCESS;
}
// Allocate memory for the logbook entries.
if (!dc_buffer_resize (logbook, rb_logbook_page_size))
if (!dc_buffer_resize (logbook, rb_logbook_size))
return DC_STATUS_NOMEMORY;
// Cache the logbook pointer.
unsigned char *logbooks = dc_buffer_get_data (logbook);
// Since entries are not necessary aligned on page boundaries,
// the memory buffer may contain padding entries on both sides.
// The memory area which contains the valid entries is marked
// with a number of additional variables.
unsigned int begin = 0;
unsigned int end = rb_logbook_page_size;
if (!full) {
begin += rb_logbook_entry_begin - rb_logbook_page_begin;
end -= rb_logbook_page_end - rb_logbook_entry_end;
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_logbook_begin, layout->rb_logbook_end, rb_logbook_end);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
// The logbook ringbuffer is read backwards to retrieve the most recent
// entries first. If an already downloaded entry is identified (by means
// of its fingerprint), the transfer is aborted immediately to reduce
// the transfer time. When necessary, padding entries are downloaded
// (but not processed) to align all read requests on page boundaries.
// the transfer time.
unsigned int nbytes = 0;
unsigned int current = end;
unsigned int offset = rb_logbook_page_size;
unsigned int address = rb_logbook_page_end;
while (nbytes < rb_logbook_page_size) {
// Handle the ringbuffer wrap point.
if (address == layout->rb_logbook_begin)
address = layout->rb_logbook_end;
unsigned int offset = rb_logbook_size;
while (nbytes < rb_logbook_size) {
// Move to the start of the current entry.
offset -= layout->rb_logbook_entry_size;
// Calculate the optimal packet size.
unsigned int len = PAGESIZE * device->multipage;
if (layout->rb_logbook_begin + len > address)
len = address - layout->rb_logbook_begin; // End of ringbuffer.
if (nbytes + len > rb_logbook_page_size)
len = rb_logbook_page_size - nbytes; // End of logbooks.
// Move to the start of the current page.
address -= len;
offset -= len;
// Read the logbook page.
rc = dc_device_read (abstract, address, logbooks + offset, len);
// Read the logbook entry.
rc = dc_rbstream_read (rbstream, progress, logbooks + offset, layout->rb_logbook_entry_size);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
ERROR (abstract->context, "Failed to read the memory.");
dc_rbstream_free (rbstream);
return rc;
}
// Update and emit a progress event.
progress->current += len;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
nbytes += layout->rb_logbook_entry_size;
// A full ringbuffer needs some special treatment to avoid
// having to download the first/last page twice. When a full
// ringbuffer is not aligned to page boundaries, this page
// will contain both the most recent and oldest entry.
if (full && unaligned) {
if (nbytes == 0) {
// After downloading the first page, move both the oldest
// and most recent entries to their correct location.
unsigned int oldest = rb_logbook_page_end - rb_logbook_entry_end;
unsigned int newest = len - oldest;
// Move the oldest entries down to the start of the buffer.
memcpy (logbooks, logbooks + offset + newest, oldest);
// Move the newest entries up to the end of the buffer.
memmove (logbooks + offset + oldest, logbooks + offset, newest);
// Adjust the current page offset to the new position.
offset += oldest;
} else if (nbytes + len == rb_logbook_page_size) {
// After downloading the last page, pretend we have also
// downloaded those oldest entries from the first page.
offset = 0;
}
}
nbytes += len;
// Process the logbook entries.
int abort = 0;
while (current >= offset + layout->rb_logbook_entry_size &&
current != offset && current != begin)
{
// Move to the start of the current entry.
current -= layout->rb_logbook_entry_size;
// Check for uninitialized entries. Normally, such entries are
// never present, except when the ringbuffer is actually empty,
// but the ringbuffer pointers are not set to their empty values.
// This appears to happen on some devices, and we attempt to
// fix this here.
if (array_isequal (logbooks + current, layout->rb_logbook_entry_size, 0xFF)) {
WARNING (abstract->context, "Uninitialized logbook entries detected!");
begin = current + layout->rb_logbook_entry_size;
abort = 1;
break;
}
// Compare the fingerprint to identify previously downloaded entries.
if (memcmp (logbooks + current, device->fingerprint, layout->rb_logbook_entry_size) == 0) {
begin = current + layout->rb_logbook_entry_size;
abort = 1;
break;
}
}
// Stop reading pages too.
if (abort)
// Check for uninitialized entries. Normally, such entries are
// never present, except when the ringbuffer is actually empty,
// but the ringbuffer pointers are not set to their empty values.
// This appears to happen on some devices, and we attempt to
// fix this here.
if (array_isequal (logbooks + offset, layout->rb_logbook_entry_size, 0xFF)) {
WARNING (abstract->context, "Uninitialized logbook entries detected!");
offset += layout->rb_logbook_entry_size;
break;
}
// Compare the fingerprint to identify previously downloaded entries.
if (memcmp (logbooks + offset, device->fingerprint, layout->rb_logbook_entry_size) == 0) {
offset += layout->rb_logbook_entry_size;
break;
}
}
// Update and emit a progress event.
progress->maximum -= rb_logbook_page_size - nbytes;
progress->maximum -= rb_logbook_size - nbytes;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
dc_buffer_slice (logbook, begin, end - begin);
dc_buffer_slice (logbook, offset, rb_logbook_size - offset);
dc_rbstream_free (rbstream);
return DC_STATUS_SUCCESS;
}
@ -492,20 +394,24 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
progress->maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
// Memory buffer for the profile data.
unsigned char *profiles = (unsigned char *) malloc (rb_profile_size + rb_logbook_size);
if (profiles == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
dc_rbstream_free (rbstream);
return DC_STATUS_NOMEMORY;
}
// Keep track of the current position.
unsigned int offset = rb_profile_size + rb_logbook_size;
unsigned int address = rb_profile_end;
// When using multipage reads, the last packet can contain data from more
// than one dive. Therefore, the remaining data of this package (and its
// size) needs to be preserved for the next dive.
unsigned int available = 0;
// Traverse the logbook ringbuffer backwards to retrieve the most recent
// dives first. The logbook ringbuffer is linearized at this point, so
@ -528,6 +434,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
{
ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).",
rb_entry_first, rb_entry_last);
dc_rbstream_free (rbstream);
free (profiles);
return DC_STATUS_DATAFORMAT;
}
@ -549,56 +456,33 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
break;
}
// Read the profile data.
unsigned int nbytes = available;
while (nbytes < rb_entry_size + gap) {
// Handle the ringbuffer wrap point.
if (address == layout->rb_profile_begin)
address = layout->rb_profile_end;
// Move to the start of the current dive.
offset -= rb_entry_size + gap;
// Calculate the optimal packet size.
unsigned int len = PAGESIZE * device->multipage;
if (layout->rb_profile_begin + len > address)
len = address - layout->rb_profile_begin; // End of ringbuffer.
if (nbytes + len > remaining)
len = remaining - nbytes; // End of profile.
// Move to the start of the current page.
address -= len;
offset -= len;
// Read the profile page.
rc = dc_device_read (abstract, address, profiles + offset, len);
if (rc != DC_STATUS_SUCCESS) {
free (profiles);
return rc;
}
// Update and emit a progress event.
progress->current += len;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
nbytes += len;
// Read the dive.
rc = dc_rbstream_read (rbstream, progress, profiles + offset, rb_entry_size + gap);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (profiles);
return rc;
}
available = nbytes - (rb_entry_size + gap);
remaining -= rb_entry_size + gap;
previous = rb_entry_first;
// Prepend the logbook entry to the profile data. The memory buffer is
// large enough to store this entry, but any data that belongs to the
// next dive needs to be moved down first.
if (available)
memmove (profiles + offset - layout->rb_logbook_entry_size, profiles + offset, available);
// large enough to store this entry.
offset -= layout->rb_logbook_entry_size;
memcpy (profiles + offset + available, logbooks + entry, layout->rb_logbook_entry_size);
memcpy (profiles + offset, logbooks + entry, layout->rb_logbook_entry_size);
unsigned char *p = profiles + offset + available;
unsigned char *p = profiles + offset;
if (callback && !callback (p, rb_entry_size + layout->rb_logbook_entry_size, p, layout->rb_logbook_entry_size, userdata)) {
break;
}
}
dc_rbstream_free (rbstream);
free (profiles);
return DC_STATUS_SUCCESS;

View File

@ -26,6 +26,7 @@
#include "context-private.h"
#include "suunto_common2.h"
#include "ringbuffer.h"
#include "rbstream.h"
#include "checksum.h"
#include "array.h"
@ -294,102 +295,59 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
return DC_STATUS_DATAFORMAT;
}
// Memory buffer to store all the dives.
unsigned char *data = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin + SZ_MINIMUM);
if (data == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Calculate the total amount of bytes.
unsigned int remaining = RB_PROFILE_DISTANCE (layout, begin, end, count != 0);
// Update and emit a progress event.
progress.maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - remaining;
progress.current += sizeof (header);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// To reduce the number of read operations, we always try to read
// packages with the largest possible size. As a consequence, the
// last package of a dive can contain data from more than one dive.
// Therefore, the remaining data of this package (and its size)
// needs to be preserved for the next dive.
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, end);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
unsigned int available = 0;
// Memory buffer to store all the dives.
unsigned char *data = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin);
if (data == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
dc_rbstream_free (rbstream);
return DC_STATUS_NOMEMORY;
}
// The ring buffer is traversed backwards to retrieve the most recent
// dives first. This allows us to download only the new dives.
unsigned int current = last;
unsigned int previous = end;
unsigned int address = previous;
unsigned int offset = remaining + SZ_MINIMUM;
while (remaining) {
unsigned int offset = remaining;
while (offset) {
// Calculate the size of the current dive.
unsigned int size = RB_PROFILE_DISTANCE (layout, current, previous, 1);
if (size < 4 || size > remaining) {
ERROR (abstract->context, "Unexpected profile size (%u %u).", size, remaining);
if (size < 4 || size > offset) {
ERROR (abstract->context, "Unexpected profile size (%u %u).", size, offset);
dc_rbstream_free (rbstream);
free (data);
return DC_STATUS_DATAFORMAT;
}
unsigned int nbytes = available;
while (nbytes < size) {
// Handle the ringbuffer wrap point.
if (address == layout->rb_profile_begin)
address = layout->rb_profile_end;
// Move to the begin of the current dive.
offset -= size;
// Calculate the package size. Try with the largest possible
// size first, and adjust when the end of the ringbuffer or
// the end of the profile data is reached.
unsigned int len = SZ_PACKET;
if (layout->rb_profile_begin + len > address)
len = address - layout->rb_profile_begin; // End of ringbuffer.
if (nbytes + len > remaining)
len = remaining - nbytes; // End of profile.
/*if (nbytes + len > size)
len = size - nbytes;*/ // End of dive (for testing only).
// Move to the begin of the current package.
offset -= len;
address -= len;
// Always read at least the minimum amount of bytes, because
// reading fewer bytes is unreliable. The memory buffer is
// large enough to prevent buffer overflows, and the extra
// bytes are automatically ignored (due to reading backwards).
unsigned int extra = 0;
if (len < SZ_MINIMUM)
extra = SZ_MINIMUM - len;
// Read the package.
rc = suunto_common2_device_read (abstract, address - extra, data + offset - extra, len + extra);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
free (data);
return rc;
}
// Update and emit a progress event.
progress.current += len;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Next package.
nbytes += len;
// Read the dive.
rc = dc_rbstream_read (rbstream, &progress, data + offset, size);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (data);
return rc;
}
// The last package of the current dive contains the previous and
// next pointers (in a continuous memory area). It can also contain
// a number of bytes from the next dive.
remaining -= size;
available = nbytes - size;
unsigned char *p = data + offset + available;
unsigned char *p = data + offset;
unsigned int prev = array_uint16_le (p + 0);
unsigned int next = array_uint16_le (p + 2);
if (prev < layout->rb_profile_begin ||
@ -398,11 +356,13 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
next >= layout->rb_profile_end)
{
ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x 0x%04x).", prev, next);
dc_rbstream_free (rbstream);
free (data);
return DC_STATUS_DATAFORMAT;
}
if (next != previous && next != current) {
ERROR (abstract->context, "Profiles are not continuous (0x%04x 0x%04x 0x%04x).", current, next, previous);
dc_rbstream_free (rbstream);
free (data);
return DC_STATUS_DATAFORMAT;
}
@ -410,11 +370,13 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
if (next != current) {
unsigned int fp_offset = layout->fingerprint + 4;
if (memcmp (p + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) {
dc_rbstream_free (rbstream);
free (data);
return DC_STATUS_SUCCESS;
}
if (callback && !callback (p + 4, size - 4, p + fp_offset, sizeof (device->fingerprint), userdata)) {
dc_rbstream_free (rbstream);
free (data);
return DC_STATUS_SUCCESS;
}
@ -428,6 +390,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
current = prev;
}
dc_rbstream_free (rbstream);
free (data);
return status;

View File

@ -31,6 +31,7 @@
#include "checksum.h"
#include "array.h"
#include "ringbuffer.h"
#include "rbstream.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &zeagle_n2ition3_device_vtable)
@ -352,16 +353,21 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba
progress.maximum = (RB_LOGBOOK_END - RB_LOGBOOK_BEGIN) * 2 + 8 + total;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, RB_PROFILE_BEGIN, RB_PROFILE_END, eop);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
// Memory buffer for the profile data.
unsigned char buffer[RB_PROFILE_END - RB_PROFILE_BEGIN] = {0};
unsigned int available = 0;
unsigned int remaining = total;
unsigned int offset = RB_PROFILE_END - RB_PROFILE_BEGIN;
idx = last;
previous = eop;
unsigned int address = previous;
for (unsigned int i = 0; i < count; ++i) {
// Get the pointer to the profile data.
unsigned int current = array_uint16_le (config + 2 * idx);
@ -369,50 +375,37 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba
// Get the profile length.
unsigned int length = ringbuffer_distance (current, previous, 1, RB_PROFILE_BEGIN, RB_PROFILE_END);
unsigned nbytes = available;
while (nbytes < length) {
if (address == RB_PROFILE_BEGIN)
address = RB_PROFILE_END;
// Move to the begin of the current dive.
offset -= length;
unsigned int len = SZ_PACKET;
if (RB_PROFILE_BEGIN + len > address)
len = address - RB_PROFILE_BEGIN; // End of ringbuffer.
if (nbytes + len > remaining)
len = remaining - nbytes; // End of profile.
address -= len;
offset -= len;
// Read the memory page.
rc = zeagle_n2ition3_device_read (abstract, address, buffer + offset, len);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return rc;
}
// Update and emit a progress event.
progress.current += len;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
nbytes += len;
// Read the dive.
rc = dc_rbstream_read (rbstream, &progress, buffer + offset, length);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
return rc;
}
unsigned char *p = buffer + offset;
if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) {
dc_rbstream_free (rbstream);
return DC_STATUS_SUCCESS;
}
if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata)) {
dc_rbstream_free (rbstream);
return DC_STATUS_SUCCESS;
}
remaining -= length;
available = nbytes - length;
previous = current;
unsigned char *p = buffer + offset + available;
if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0)
return DC_STATUS_SUCCESS;
if (callback && !callback (p, length, p, sizeof (device->fingerprint), userdata))
return DC_STATUS_SUCCESS;
if (idx == RB_LOGBOOK_BEGIN)
idx = RB_LOGBOOK_END;
idx--;
}
dc_rbstream_free (rbstream);
return DC_STATUS_SUCCESS;
}