From 4882881c401f432f2ef067d1103c5fd31826c06a Mon Sep 17 00:00:00 2001
From: Jef Driesen <jefdriesen@users.sourceforge.net>
Date: Mon, 12 Jan 2015 22:00:27 +0100
Subject: [PATCH] cochran wip

---
 src/cochran_commander.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/cochran_commander.h |  16 +++++
 2 files changed, 186 insertions(+), 1 deletion(-)

diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index e8bae88..a0c239f 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -27,6 +27,7 @@
 
 #include "context-private.h"
 #include "device-private.h"
+#include "ringbuffer.h"
 #include "serial.h"
 #include "array.h"
 
@@ -38,16 +39,42 @@
 	rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \
 )
 
+static dc_status_t
+cochran_commander_device_foreach2 (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
+
 static const dc_device_vtable_t cochran_commander_device_vtable = {
 	DC_FAMILY_COCHRAN_COMMANDER,
 	cochran_commander_device_set_fingerprint,	/* set_fingerprint */
 	cochran_commander_device_read,				/* read */
 	NULL,										/* write */
 	cochran_commander_device_dump,				/* dump */
-	cochran_commander_device_foreach,			/* foreach */
+	cochran_commander_device_foreach2,			/* foreach */
 	cochran_commander_device_close				/* close */
 };
 
+static const cochran_layout_t cochran_cmdr_layout = {
+	0x100000, /* memsize */
+	0x000000, /* rb_logbook_begin */
+	0x020000, /* rb_logbook_end */
+	256,      /* rb_logbook_size */
+	0x020000, /* rb_profile_begin */
+	0x100000, /* rb_profile_end */
+	30,       /* pt_profile_pre */
+	6,        /* pt_profile_begin */
+	128,      /* pt_profile_end */
+};
+
+static const cochran_layout_t cochran_emc_layout = {
+	0x1000000, /* memsize */
+	0x0000000, /* rb_logbook_begin */
+	0x0094000, /* rb_logbook_end */
+	512,       /* rb_logbook_size */
+	0x0094000, /* rb_profile_begin */
+	0x1000000, /* rb_profile_end */
+	30,        /* pt_profile_pre */
+	6,         /* pt_profile_begin */
+	256,       /* pt_profile_end */
+};
 
 static dc_status_t
 cochran_read_id(dc_device_t *device);
@@ -824,6 +851,148 @@ cochran_guess_sample_end_address(cochran_data_t *data, unsigned int log_num)
 }
 
 
+static dc_status_t
+cochran_commander_device_foreach2 (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+{
+	dc_status_t rc = DC_STATUS_SUCCESS;
+	cochran_device_t *device = (cochran_device_t *) abstract;
+
+	// Enable progress notifications.
+	dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
+	device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+	// Detect the model.
+	unsigned int model = 0;
+	const cochran_layout_t *layout = NULL;
+	const unsigned char *id = device->data.id + 0x3B;
+	if (memcmp(id, "AM\x11""2212\x02", 8) == 0) {
+		model = COCHRAN_MODEL_COMMANDER_AIR_NITROX;
+		layout = &cochran_cmdr_layout;
+	} else if (memcmp(id, "AM7303\x8b\x43", 8) == 0) {
+		model = COCHRAN_MODEL_EMC_14;
+		layout = &cochran_emc_layout;
+	} else if (memcmp(id, "AMA315\xC3\xC5", 8) == 0) {
+		model = COCHRAN_MODEL_EMC_16;
+		layout = &cochran_emc_layout;
+	} else if (memcmp(id, "AM2315\xA3\x71", 8) == 0) {
+		model = COCHRAN_MODEL_EMC_20;
+		layout = &cochran_emc_layout;
+	} else {
+		return DC_STATUS_UNSUPPORTED;
+	}
+
+	// Emit a device info event.
+	dc_event_devinfo_t devinfo;
+	devinfo.model = model; /* FIXME: This number should match with the one in descriptor.c */
+	devinfo.firmware = 0;
+	devinfo.serial = 0;
+	device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
+
+	// Read config
+	// FIXME: We only need page zero here.
+	rc = cochran_read_config(abstract, NULL);
+	if (rc != DC_STATUS_SUCCESS)
+		return rc;
+
+	unsigned int eop = 0;
+	unsigned int ndives = 0;
+	if (model == COCHRAN_MODEL_COMMANDER_AIR_NITROX) {
+		eop    = array_uint32_be(device->data.config[0] + 0x6E);
+		ndives = array_uint16_be(device->data.config[0] + 0x46);
+	} else {
+		eop    = array_uint32_le(device->data.config[0] + 0x142);
+		ndives = array_uint16_le(device->data.config[0] + 0xD2);
+	}
+	DEBUG (abstract->context, "ndives=%u, eop=%08x", ndives, eop);
+	if (eop < layout->rb_profile_begin ||
+		eop >= layout->rb_profile_end) {
+		ERROR (abstract->context, "Invalid ringbuffer pointer detected.");
+		return DC_STATUS_DATAFORMAT;
+	}
+
+	// FIXME: ndives should be limited to the maximum number of logbook entries
+	//        (rb_logbook_end - rb_logbook_begin) / rb_logbook_size
+	//        If it's larger, then the most recent entry won't be the last one,
+	//        and we'll have to search for it.
+
+	// Allocate space for the logbook entries.
+	unsigned char *logbooks = (unsigned char *) malloc(ndives * layout->rb_logbook_size);
+	if (logbooks == NULL) {
+		ERROR (abstract->context, "Failed to allocate memory.");
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Read the logbook entries.
+	rc = cochran_commander_read (abstract, NULL, layout->rb_logbook_begin, logbooks, ndives * layout->rb_logbook_size);
+	if (rc != DC_STATUS_SUCCESS) {
+		free (logbooks);
+		return rc;
+	}
+
+	// TODO: When searching for the most recent dive, we can
+	// immediately find the largest one too, and pre-allocate a buffer
+	// of the right size.
+	dc_buffer_t *dive = dc_buffer_new (layout->rb_logbook_size);
+	if (dive == NULL) {
+		ERROR (abstract->context, "Failed to allocate memory.");
+		free (logbooks);
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Maximum amount of profile data.
+	unsigned int remaining = layout->rb_profile_end - layout->rb_profile_begin;
+
+	unsigned int previous = eop;
+	for (unsigned int i = 0; i < ndives; ++i) {
+		unsigned int idx = ndives - 1 - i;
+		unsigned int offset = idx * layout->rb_logbook_size;
+		unsigned char *log = logbooks + offset;
+
+		unsigned int rb_sample_pre   = array_uint32_le(log + layout->pt_profile_pre);
+		unsigned int rb_sample_begin = array_uint32_le(log + layout->pt_profile_begin);
+		unsigned int rb_sample_end   = array_uint32_le(log + layout->pt_profile_end);
+		DEBUG (abstract->context, "logbook: %3u %08x %08x %08x %5u %u", idx,
+			rb_sample_pre, rb_sample_begin, rb_sample_end,
+			rb_sample_begin - rb_sample_pre,
+			previous - rb_sample_end);
+		if (rb_sample_pre < layout->rb_profile_begin || rb_sample_pre >= layout->rb_profile_end ||
+			rb_sample_begin < layout->rb_profile_begin || rb_sample_begin >= layout->rb_profile_end ||
+			((rb_sample_end < layout->rb_profile_begin || rb_sample_end >= layout->rb_profile_end) &&
+				rb_sample_end != 0xFFFFFFFF))
+		{
+			ERROR (abstract->context, "Invalid ringbuffer pointer detected.");
+			free (logbooks);
+			dc_buffer_free (dive);
+			return DC_STATUS_DATAFORMAT;
+		}
+
+		dc_buffer_clear(dive);
+		dc_buffer_append(dive, log, layout->rb_logbook_size);
+
+		unsigned int length = ringbuffer_distance (rb_sample_pre, previous, 0, layout->rb_profile_begin, layout->rb_profile_end);
+		DEBUG(abstract->context, "length: %u", length);
+		if (remaining == 0 || length > remaining) {
+			// The profile ringbuffer is full.
+			remaining = 0;
+			length = 0;
+		}
+
+		// TODO: Get the profile data here.
+
+		if (callback && !callback (dc_buffer_get_data(dive), dc_buffer_get_size(dive), NULL, 0, userdata)) {
+			break;
+		}
+
+		previous = rb_sample_pre;
+		remaining -= length;
+	}
+
+	free (logbooks);
+	dc_buffer_free (dive);
+
+	return DC_STATUS_SUCCESS;
+}
+
 dc_status_t
 cochran_commander_device_foreach (dc_device_t *abstract,
 		dc_dive_callback_t callback, void *userdata)
diff --git a/src/cochran_commander.h b/src/cochran_commander.h
index 05b49ed..7e816d7 100644
--- a/src/cochran_commander.h
+++ b/src/cochran_commander.h
@@ -19,6 +19,22 @@
  * MA 02110-1301 USA
  */
 
+typedef struct cochran_layout_t {
+	// Memory size.
+	unsigned int memsize;
+	// Logbook ringbuffer.
+	unsigned int rb_logbook_begin;
+	unsigned int rb_logbook_end;
+	unsigned int rb_logbook_size;
+	// Profile ringbuffer
+	unsigned int rb_profile_begin;
+	unsigned int rb_profile_end;
+	// Profile pointers
+	unsigned int pt_profile_pre;
+	unsigned int pt_profile_begin;
+	unsigned int pt_profile_end;
+} cochran_layout_t;
+
 // seconds to add to Cochran time stamps to get unix time
 // Equivalent to Jan 1, 1992 00:00:00
 #define COCHRAN_TIMESTAMP_OFFSET	694242000
-- 
1.9.1

