From 248d9fed29638d79cd9e31a6f924939ecddcbe99 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Mon, 27 Oct 2014 17:19:48 -0700
Subject: [PATCH 3/3] Suunto EON Steel: populate dive surface pressure, and
 various string data

The EON Steel saves the dive computer firmware versions etc per dive,
which is really nice for upgrades: old dives done with older firmware
still show the firmware at the time of the dive.  That, in turn, is nice
because we can use it for a reliable dive ID - dive time with serial
number etc.

This uses the new DC_FIELD_STRING model to feed the hw/sw information to
the application, since we need the parser to access it.  It also returns
battery state and deco model information.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 src/suunto_eonsteel_parser.c | 153 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 152 insertions(+), 1 deletion(-)

diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c
index 1d035b93f7e9..29be4ee1e3df 100644
--- a/src/suunto_eonsteel_parser.c
+++ b/src/suunto_eonsteel_parser.c
@@ -41,6 +41,7 @@ struct type_desc {
 
 #define MAXTYPE 512
 #define MAXGASES 16
+#define MAXSTRINGS 16
 
 struct eon_parser {
 	dc_parser_t base;
@@ -55,6 +56,7 @@ struct eon_parser {
 		dc_gasmix_t gasmix[MAXGASES];
 		dc_salinity_t salinity;
 		double surface_pressure;
+		dc_field_string_t strings[MAXSTRINGS];
 	} cache;
 };
 
@@ -76,6 +78,17 @@ static unsigned char get_u8(const void *src)
 	return *(const unsigned char *)src;
 }
 
+static float get_le32_float(const void *src)
+{
+	union {
+		unsigned int val;
+		float result;
+	} u;
+
+	u.val = array_uint32_le(src);
+	return u.result;
+}
+
 static void debug(const char *name, const char *buf, int len)
 {
 	int i;
@@ -349,11 +362,23 @@ static dc_status_t eonsteel_parser_samples_foreach(dc_parser_t *abstract, dc_sam
 {
 	struct eon_parser *eon = (void *)abstract;
 	struct sample_data data = { eon, callback, userdata, 0 };
-
 	traverse_data(eon, traverse_samples, &data);
 	return DC_STATUS_SUCCESS;
 }
 
+static dc_status_t get_string_field(struct eon_parser *eon, unsigned idx, dc_field_string_t *value)
+{
+	if (idx < MAXSTRINGS) {
+		dc_field_string_t *res = eon->cache.strings+idx;
+		if (res->desc && res->value) {
+			*value = *res;
+			return DC_STATUS_SUCCESS;
+		}
+
+	}
+	return DC_STATUS_UNSUPPORTED;
+}
+
 // Ugly define thing makes the code much easier to read
 // I'd love to use __typeof__, but that's a gcc'ism
 #define field_value(p, set) \
@@ -390,6 +415,8 @@ static dc_status_t eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_
 	case DC_FIELD_ATMOSPHERIC:
 		field_value(value, eon->cache.surface_pressure);
 		break;
+	case DC_FIELD_STRING:
+		return get_string_field(eon, flags, (dc_field_string_t *)value);
 	}
 	return DC_STATUS_SUCCESS;
 }
@@ -453,6 +480,123 @@ static void add_gas_he(struct eon_parser *eon, unsigned char he)
 	eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
 }
 
+static int add_string(struct eon_parser *eon, const char *desc, const char *value)
+{
+	int i;
+
+	eon->cache.initialized |= 1 << DC_FIELD_STRING;
+	for (i = 0; i < MAXSTRINGS; i++) {
+		dc_field_string_t *str = eon->cache.strings+i;
+		if (str->desc)
+			continue;
+		str->desc = desc;
+		str->value = strdup(value);
+		break;
+	}
+	return 0;
+}
+
+// "Device" fields are all utf8:
+//   Info.BatteryAtEnd
+//   Info.BatteryAtStart
+//   Info.BSL
+//   Info.HW
+//   Info.SW
+//   Name
+//   SerialNumber
+static int traverse_device_fields(struct eon_parser *eon, const char *name, const void *data, int len)
+{
+	if (!strcmp(name, "SerialNumber"))
+		return add_string(eon, "Serial", data);
+	if (!strcmp(name, "Info.HW"))
+		return add_string(eon, "HW Version", data);
+	if (!strcmp(name, "Info.SW"))
+		return add_string(eon, "FW Version", data);
+	if (!strcmp(name, "Info.BatteryAtStart"))
+		return add_string(eon, "Battery at start", data);
+	if (!strcmp(name, "Info.BatteryAtEnd"))
+		return add_string(eon, "Battery at end", data);
+	return 0;
+}
+
+// "Header" fields are:
+//   Activity (utf8)
+//   DateTime (utf8)
+//   Depth.Avg (float32,precision=2)
+//   Depth.Max (float32,precision=2)
+//   Diving.AlgorithmAscentTime (uint32)
+//   Diving.AlgorithmBottomMixture.Helium (uint8,precision=2) (0.01*x,100*x)
+//   Diving.AlgorithmBottomMixture.Oxygen (uint8,precision=2) (0.01*x,100*x)
+//   Diving.AlgorithmBottomTime (uint32)
+//   Diving.AlgorithmTransitionDepth (uint8)
+//   Diving.Algorithm (utf8)
+//   Diving.Altitude (uint16)
+//   Diving.Conservatism (int8)
+//   Diving.DaysInSeries (uint32)
+//   Diving.DesaturationTime (uint32)
+//   Diving.DiveMode (utf8)
+//   Diving.EndTissue.CNS (float32,precision=3)
+//   Diving.EndTissue.Helium+Pressure (uint32)
+//   Diving.EndTissue.Nitrogen+Pressure (uint32)
+//   Diving.EndTissue.OLF (float32,precision=3)
+//   Diving.EndTissue.OTU (float32)
+//   Diving.EndTissue.RgbmHelium (float32,precision=3)
+//   Diving.EndTissue.RgbmNitrogen (float32,precision=3)
+//   Diving.NumberInSeries (uint32)
+//   Diving.PreviousDiveDepth (float32,precision=2)
+//   Diving.StartTissue.CNS (float32,precision=3)
+//   Diving.StartTissue.Helium+Pressure (uint32)
+//   Diving.StartTissue.Nitrogen+Pressure (uint32)
+//   Diving.StartTissue.OLF (float32,precision=3)
+//   Diving.StartTissue.OTU (float32)
+//   Diving.StartTissue.RgbmHelium (float32,precision=3)
+//   Diving.StartTissue.RgbmNitrogen (float32,precision=3)
+//   Diving.SurfacePressure (uint32)
+//   Diving.SurfaceTime (uint32)
+//   Duration (uint32)
+//   PauseDuration (uint32)
+//   SampleInterval (uint8)
+static int traverse_header_fields(struct eon_parser *eon, const char *name, const void *data, int len)
+{
+	if (!strcmp(name, "Depth.Max")) {
+		double d = get_le32_float(data);
+		if (d > eon->cache.maxdepth)
+			eon->cache.maxdepth = d;
+		return 0;
+	}
+	if (!strcmp(name, "Diving.SurfacePressure")) {
+		unsigned int pressure = array_uint32_le(data); // in SI units - Pascal
+		eon->cache.surface_pressure = pressure / 100000.0; // bar
+		eon->cache.initialized |= 1 << DC_FIELD_ATMOSPHERIC;
+		return 0;
+	}
+	if (!strcmp(name, "DateTime"))
+		return add_string(eon, "Dive ID", data);
+
+	if (!strcmp(name, "Diving.Algorithm"))
+		return add_string(eon, "Deco algorithm", data);
+
+	return 0;
+}
+
+static int traverse_dynamic_fields(struct eon_parser *eon, const struct type_desc *desc, const void *data, int len)
+{
+	const char *name = desc->desc;
+
+	if (!strncmp(name, "sml.", 4)) {
+		name += 4;
+		if (!strncmp(name, "DeviceLog.", 10)) {
+			name += 10;
+			if (!strncmp(name, "Device.", 7))
+				return traverse_device_fields(eon, name+7, data, len);
+			if (!strncmp(name, "Header.", 7)) {
+				return traverse_header_fields(eon, name+7, data, len);
+			}
+		}
+	}
+	return 0;
+}
+
 static int traverse_fields(unsigned short type, const struct type_desc *desc, const void *data, int len, void *user)
 {
 	struct eon_parser *eon = user;
@@ -477,6 +621,13 @@ static int traverse_fields(unsigned short type, const struct type_desc *desc, co
 	case 0x000f: // Helium percentage in first byte
 		add_gas_he(eon, get_u8(data));
 		break;
+	default:
+	// The types with the high byte set seem to be dynamic
+	// although not all of them seem to change. But let's
+	// just check the descriptor name for them.
+		if (type > 255)
+			traverse_dynamic_fields(eon, desc, data, len);
+		break;
 	}
 	return 0;
 }
-- 
2.1.2.452.gd5ca7ae

