From 279bd9efc4f10871dece494308675b038e9859bf Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Tue, 21 Oct 2014 14:30:11 -0700
Subject: [PATCH 3/3] Suunto EON Steel: populate dive surface pressure, and
 sw/hw version info

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_DEVINFO model to feed the hw/sw information
to the application, since we need the parser to access it.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 src/suunto_eon_steel_parser.c | 139 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 136 insertions(+), 3 deletions(-)

diff --git a/src/suunto_eon_steel_parser.c b/src/suunto_eon_steel_parser.c
index 5696a7c39d5a..17d8b90606c8 100644
--- a/src/suunto_eon_steel_parser.c
+++ b/src/suunto_eon_steel_parser.c
@@ -54,6 +54,7 @@ struct eon_parser {
 		dc_gasmix_t gasmix[MAXGASES];
 		dc_salinity_t salinity;
 		double surface_pressure;
+		struct dc_field_devinfo_t devinfo;
 	} cache;
 };
 
@@ -103,6 +104,19 @@ static void put_le32(unsigned int val, void *dst)
 	p[3] = val >> 24;
 }
 
+static float get_le32_float(const void *src)
+{
+	union {
+		unsigned int val;
+		float result;
+	} u;
+	const unsigned char *p = src;
+
+	u.val = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+	return u.result;
+}
+
+
 static void debug(const char *name, const char *buf, int len)
 {
 	int i;
@@ -390,14 +404,14 @@ static dc_status_t eon_steel_parser_samples_foreach(dc_parser_t *abstract, dc_sa
 {
 	struct eon_parser *eon = (void *)abstract;
 	struct sample_data data = { eon, callback, userdata, 0 };
-
 	traverse_data(eon, traverse_samples, &data);
 	return DC_STATUS_SUCCESS;
 }
 
-// Ugly typeof thing makes the code much easier to read
+// 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) \
-	do { *(__typeof__(set) *)(p) = (set); } while (0)
+	memcpy((p), &(set), sizeof(set))
 
 static dc_status_t eon_steel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value)
 {
@@ -430,6 +444,9 @@ static dc_status_t eon_steel_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_DEVINFO:
+		field_value(value, eon->cache.devinfo);
+		break;
 	}
 	return DC_STATUS_SUCCESS;
 }
@@ -493,6 +510,115 @@ static void add_gas_he(struct eon_parser *eon, unsigned char he)
 	eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
 }
 
+// "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)
+{
+	const char **entry;
+
+	if (!strcmp(name, "SerialNumber"))
+		entry = &eon->cache.devinfo.serial;
+	else if (!strcmp(name, "Info.HW"))
+		entry = &eon->cache.devinfo.hw_version;
+	else if (!strcmp(name, "Info.SW"))
+		entry = &eon->cache.devinfo.fw_version;
+	else if (!strcmp(name, "Name"))
+		entry = &eon->cache.devinfo.model;
+	else
+		return 0;
+
+	if (*entry)
+		free((void *)*entry);
+	*entry = strdup(data);
+	eon->cache.initialized |= 1 << DC_FIELD_DEVINFO;
+	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 = get_le32(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")) {
+		if (eon->cache.devinfo.dive_id)
+			free((void *)eon->cache.devinfo.dive_id);
+		eon->cache.devinfo.dive_id = strdup(data);
+		eon->cache.initialized |= 1 << DC_FIELD_DEVINFO;
+		return 0;
+	}
+	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;
@@ -517,6 +643,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

