Use the new ringbuffer stream
All dive computer backends are updated to use the new ringbuffer stream.
This commit is contained in:
parent
3f82a553bd
commit
2646e4df86
101
src/cressi_edy.c
101
src/cressi_edy.c
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user