Add shunt resistance value to firmware.

Adds support for a "Query Parameter Value" packet to Zedmon firmware,
which may be used to query the shunt resistance of the unit.

The client now fetches resistance from the firmware rather than taking
it as a command line flag.

Change-Id: Id137ff7838aa86676621f6b1c3bca3c1f190aaf7
diff --git a/cmd/zedmon/record.go b/cmd/zedmon/record.go
index df3f51e..2d12369 100644
--- a/cmd/zedmon/record.go
+++ b/cmd/zedmon/record.go
@@ -61,8 +61,6 @@
 	setCommonFlags(flags)
 	flags.StringVar(&out, "out", "zedmon.csv", "Output file. Specify \"-\" for stdout.")
 	flags.BoolVar(&writeHeader, "header", false, "Whether to include a header line in the output.")
-	flags.Float64Var(&resistance, "resistance", -1.0,
-		"Shunt resistance in Ohms. If set to a positive value, power will be included in the output.")
 }
 
 // If a positive resistance and field names including "v_bus" and "v_shunt" are provided, returns a
@@ -128,7 +126,15 @@
 
 	fieldNames := zedmon.GetFieldNames()
 
-	calculatePower := getPowerCalculator(resistance, fieldNames)
+	var calculatePower func(values []float64) float64 = nil
+	resistance, err := zedmon.GetShuntResistance()
+	if err != nil {
+		log.Println("Error fetching resistance:", err)
+		log.Println("Power will not be calculated.")
+	} else {
+		calculatePower = getPowerCalculator(resistance, fieldNames)
+	}
+
 	if calculatePower != nil {
 		fieldNames = append(fieldNames, "power")
 	}
diff --git a/docs/usb_proto.md b/docs/usb_proto.md
index 2f96ea1..3d4c36e 100644
--- a/docs/usb_proto.md
+++ b/docs/usb_proto.md
@@ -36,12 +36,14 @@
 -----|------|---------------------
 0x00 | H->T | Query Report Format
 0x01 | H->T | Query Time
+0x02 | H->T | Query Parameter Value
 0x10 | H->T | Enable Reporting
 0x11 | H->T | Disable Reporting
 0x20 | H->T | Set Output
 0x80 | T->H | Report Format
 0x81 | T->H | Report
-0x82 | H->T | Timestamp
+0x82 | T->H | Timestamp
+0x83 | T->H | Parameter Value
 
 ### Packet Field and Value Types
 
@@ -95,6 +97,21 @@
 
 **Payload**: None
 
+### 0x01 - Query Parameter Value
+
+Requests that the target send a `Parameter Value` packet. If the target responds
+with a `Parameter Value` packet with a `Name` that is the empty string, this
+indicates that no parameter is available at the specified index. To enumerate the
+values, the host should start by requesting `Parameter Index` of 0, and continue to
+request the next index until receiving a packet with an empty `Name`.
+
+**Payload**
+
+Byte(s) | Format  | Description
+--------|---------|------------
+   0    | uint8   | Packet Type
+   1    | uint8   | Parameter Index
+
 ### 0x10 - Enable Reporting
 
 Requests the target start sending `Report` packets.
@@ -152,3 +169,14 @@
 Field     | Format   | Description
 ----------|----------|------------------------------------------------------
 timestamp | uint64   | Timestamp, in microseconds, in the local clock domain.
+
+### 0x83 - Parameter Value
+
+**Payload**:
+
+Byte(s) | Format  | Description
+--------|---------|------------
+   0    | uint8   | Packet Type
+  1..54 | string  | Name
+   55   | uint8   | Value Type
+ 56..63 | uint8   | Value
diff --git a/firmware/app/zedmon/include/app/zedmon/usb_proto.h b/firmware/app/zedmon/include/app/zedmon/usb_proto.h
index 5099443..a8034ea 100644
--- a/firmware/app/zedmon/include/app/zedmon/usb_proto.h
+++ b/firmware/app/zedmon/include/app/zedmon/usb_proto.h
@@ -5,14 +5,16 @@
 #include <stdint.h>
 
 typedef enum {
-    ZEDMON_USB_PACKET_QUERY             = 0x00,
-    ZEDMON_USB_PACKET_QUERY_TIME        = 0x01,
-    ZEDMON_USB_PACKET_ENABLE_REPORTING  = 0x10,
-    ZEDMON_USB_PACKET_DISABLE_REPORTING = 0x11,
-    ZEDMON_USB_PACKET_SET_OUTPUT        = 0x20,
-    ZEDMON_USB_PACKET_REPORT_FORMAT     = 0x80,
-    ZEDMON_USB_PACKET_REPORT            = 0x81,
-    ZEDMON_USB_PACKET_TIMESTAMP         = 0x82,
+    ZEDMON_USB_PACKET_QUERY_REPORT_FORMAT   = 0x00,
+    ZEDMON_USB_PACKET_QUERY_TIME            = 0x01,
+    ZEDMON_USB_PACKET_QUERY_PARAMETER_VALUE = 0x02,
+    ZEDMON_USB_PACKET_ENABLE_REPORTING      = 0x10,
+    ZEDMON_USB_PACKET_DISABLE_REPORTING     = 0x11,
+    ZEDMON_USB_PACKET_SET_OUTPUT            = 0x20,
+    ZEDMON_USB_PACKET_REPORT_FORMAT         = 0x80,
+    ZEDMON_USB_PACKET_REPORT                = 0x81,
+    ZEDMON_USB_PACKET_TIMESTAMP             = 0x82,
+    ZEDMON_USB_PACKET_PARAMETER_VALUE       = 0x83,
 } zedmon_usb_packet_type_t;
 
 typedef enum {
@@ -49,5 +51,21 @@
 static_assert(offsetof(zedmon_usb_report_format_t, scale) == 4);
 static_assert(offsetof(zedmon_usb_report_format_t, name) == 8);
 
+typedef struct {
+    uint8_t packet_type;  // zedmon_usb_packet_type_t
+    char name[54];
+    uint8_t data_type;  // zedmon_usb_type_t
+    union {
+        uint8_t bytes[8];
+        float float_value;
+    } data;
+} zedmon_usb_parameter_value_t;
+
+static_assert(sizeof(zedmon_usb_parameter_value_t) == 64);
+static_assert(offsetof(zedmon_usb_parameter_value_t, packet_type) == 0);
+static_assert(offsetof(zedmon_usb_parameter_value_t, name) == 1);
+static_assert(offsetof(zedmon_usb_parameter_value_t, data_type) == 55);
+static_assert(offsetof(zedmon_usb_parameter_value_t, data) == 56);
+
 #endif  // __APP_ZEDMON_USB_PROTO_H_
 
diff --git a/firmware/app/zedmon/usb.c b/firmware/app/zedmon/usb.c
index 96f0b7e..7eefaa5 100644
--- a/firmware/app/zedmon/usb.c
+++ b/firmware/app/zedmon/usb.c
@@ -18,6 +18,7 @@
 #include <platform.h>
 #include <stdint.h>
 #include <string.h>
+#include <target/parameters.h>
 #include <trace.h>
 
 #define DATA_IN_EP_ADDR_OFFSET (0x0B)
@@ -80,6 +81,21 @@
     },
 };
 
+static zedmon_usb_parameter_value_t zedmon_parameter_values[] = {
+    {
+        .packet_type = ZEDMON_USB_PACKET_PARAMETER_VALUE,
+        .name = "shunt_resistance",
+        .data_type = ZEDMON_USB_TYPE_FLOAT32,
+        .data.float_value = SHUNT_RESISTANCE,
+    },
+    {
+        .packet_type = ZEDMON_USB_PACKET_PARAMETER_VALUE,
+        .name = "",
+        .data_type = ZEDMON_USB_TYPE_UINT8,
+        .data.bytes = {0},
+    }
+};
+
 typedef struct __attribute__((packed)) {
     lk_bigtime_t timestamp;
     uint16_t     v_shunt;
@@ -230,7 +246,7 @@
 }
 
 
-static void zedmon_usb_handle_query_packet(uint8_t values[7]) {
+static void zedmon_usb_handle_query_report_format_packet(const uint8_t values[7]) {
     uint8_t index = values[0];
 
     if (index >= countof(zedmon_report_formats)) {
@@ -240,23 +256,35 @@
     queue_tx(&zedmon_report_formats[index], sizeof(zedmon_report_formats[index]));
 }
 
-static void zedmon_usb_handle_set_output_packet(uint8_t values[7]) {
+static void zedmon_usb_handle_query_parameter_value_packet(const uint8_t values[7]) {
+    uint8_t index = values[0];
+
+    if (index >= countof(zedmon_parameter_values)) {
+        index = countof(zedmon_parameter_values) - 1;
+    }
+
+    queue_tx(&zedmon_parameter_values[index], sizeof(zedmon_parameter_values[index]));
+}
+
+static void zedmon_usb_handle_set_output_packet(const uint8_t values[7]) {
     zedmon_usb_set_target_out(values[0], values[1]);
 }
 
-static void zedmon_usb_handle_usb_rx_port(port_result_t *result) {
-    zedmon_usb_packet_type_t type =
-        (zedmon_usb_packet_type_t) result->packet.value[0];
+static void zedmon_usb_handle_usb_rx_port(const port_packet_t *packet) {
+    zedmon_usb_packet_type_t type = (zedmon_usb_packet_type_t)packet->value[0];
 
     switch (type) {
-        case ZEDMON_USB_PACKET_QUERY:
-            zedmon_usb_handle_query_packet((uint8_t *)result->packet.value + 1);
+        case ZEDMON_USB_PACKET_QUERY_REPORT_FORMAT:
+            zedmon_usb_handle_query_report_format_packet((const uint8_t *)packet->value + 1);
             break;
         case ZEDMON_USB_PACKET_QUERY_TIME:
             zedmon_usb_timestamp_packet.packet_type = ZEDMON_USB_PACKET_TIMESTAMP;
             zedmon_usb_timestamp_packet.timestamp = ina_get_current_time();
             queue_tx(&zedmon_usb_timestamp_packet, sizeof(zedmon_usb_timestamp_packet));
             break;
+        case ZEDMON_USB_PACKET_QUERY_PARAMETER_VALUE:
+            zedmon_usb_handle_query_parameter_value_packet((const uint8_t *)packet->value + 1);
+            break;
         case ZEDMON_USB_PACKET_ENABLE_REPORTING:
             zedmon_usb_reporting_enabled = true;
             break;
@@ -264,15 +292,17 @@
             zedmon_usb_reporting_enabled = false;
             break;
         case ZEDMON_USB_PACKET_SET_OUTPUT:
-            zedmon_usb_handle_set_output_packet((uint8_t *)result->packet.value + 1);
+            zedmon_usb_handle_set_output_packet((const uint8_t *)packet->value + 1);
             break;
 
             // These are Device -> Host packets which we should never see.
         case ZEDMON_USB_PACKET_REPORT_FORMAT:
             break;
+        case ZEDMON_USB_PACKET_REPORT:
+            break;
         case ZEDMON_USB_PACKET_TIMESTAMP:
             break;
-        case ZEDMON_USB_PACKET_REPORT:
+        case ZEDMON_USB_PACKET_PARAMETER_VALUE:
             break;
     }
 }
@@ -300,7 +330,7 @@
         zedmon_port_context_t context = (zedmon_port_context_t)result.ctx;
         switch(context) {
             case ZEDMON_PORT_CTX_USB_RX:
-                zedmon_usb_handle_usb_rx_port(&result);
+                zedmon_usb_handle_usb_rx_port(&result.packet);
                 break;
 
             case ZEDMON_PORT_CTX_USB_TX:
diff --git a/firmware/target/zedmon/include/target/parameters.h b/firmware/target/zedmon/include/target/parameters.h
new file mode 100644
index 0000000..ef23f87
--- /dev/null
+++ b/firmware/target/zedmon/include/target/parameters.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __TARGET_PARAMETERS_H
+#define __TARGET_PARAMETERS_H
+
+#define SHUNT_RESISTANCE 0.01f
+
+#endif
diff --git a/lib/proto.go b/lib/proto.go
index 748730b..e054271 100644
--- a/lib/proto.go
+++ b/lib/proto.go
@@ -28,17 +28,17 @@
 
 package lib
 
-type PacketType uint8
-
 const (
-	PACKET_TYPE_QUERY_REPORT_FORMAT PacketType = 0x00
-	PACKET_TYPE_QUERY_TIME                     = 0x01
-	PACKET_TYPE_ENABLE_REPORTING               = 0x10
-	PACKET_TYPE_DISABLE_REPORTING              = 0x11
-	PACKET_TYPE_SET_OUTPUT                     = 0x20
-	PACKET_TYPE_REPORT_FORMAT                  = 0x80
-	PACKET_TYPE_REPORT                         = 0x81
-	PACKET_TYPE_TIMESTAMP                      = 0x82
+	PACKET_TYPE_QUERY_REPORT_FORMAT   uint8 = 0x00
+	PACKET_TYPE_QUERY_TIME            uint8 = 0x01
+	PACKET_TYPE_QUERY_PARAMETER_VALUE uint8 = 0x02
+	PACKET_TYPE_ENABLE_REPORTING      uint8 = 0x10
+	PACKET_TYPE_DISABLE_REPORTING     uint8 = 0x11
+	PACKET_TYPE_SET_OUTPUT            uint8 = 0x20
+	PACKET_TYPE_REPORT_FORMAT         uint8 = 0x80
+	PACKET_TYPE_REPORT                uint8 = 0x81
+	PACKET_TYPE_TIMESTAMP             uint8 = 0x82
+	PACKET_TYPE_PARAMTER_VALUE        uint8 = 0x83
 )
 
 type setOutputPacket struct {
@@ -55,3 +55,10 @@
 	Scale      float32
 	Name       [56]byte
 }
+
+type parameterValuePacket struct {
+	PacketType uint8
+	Name       [54]byte
+	DataType   uint8
+	Data       [8]byte
+}
diff --git a/lib/zedmon.go b/lib/zedmon.go
index c429a62..214dfcd 100644
--- a/lib/zedmon.go
+++ b/lib/zedmon.go
@@ -54,6 +54,11 @@
 	Values    []float64
 }
 
+type Parameter struct {
+	Name  string
+	Value float64
+}
+
 type Zedmon struct {
 	ctx    *gousb.Context
 	dev    *gousb.Device
@@ -69,7 +74,8 @@
 	readStream       *gousb.ReadStream
 	reportingEnabled bool
 
-	fields []*Field
+	fields     []*Field
+	parameters []*Parameter
 }
 
 func (z *Zedmon) GetFieldNames() []string {
@@ -278,14 +284,25 @@
 	return reports, nil
 }
 
+func parseStringFromBytes(bytesIn []byte) string {
+	var s string
+	n := bytes.IndexByte(bytesIn, 0x00)
+	if n != -1 {
+		s = string(bytesIn[:n])
+	} else {
+		s = string(bytesIn)
+	}
+	return s
+}
+
 // Returns: offset after this field, error
-func (z *Zedmon) getFormatReport(index uint, offset *int) (bool, error) {
+func (z *Zedmon) getReportFormat(index uint, offset *int) (bool, error) {
 	if index >= 0xff {
 		return false, fmt.Errorf("Index %d out of range", index)
 	}
 
 	data := make([]byte, 2)
-	data[0] = 0x00
+	data[0] = PACKET_TYPE_QUERY_REPORT_FORMAT
 	data[1] = uint8(index)
 	numBytes, err := z.outEp.Write(data)
 	if err != nil {
@@ -310,13 +327,7 @@
 
 	// TODO: validate Unit
 
-	var name string
-	n := bytes.IndexByte(packet.Name[:], 0x00)
-	if n != -1 {
-		name = string(packet.Name[:n])
-	} else {
-		name = string(packet.Name[:])
-	}
+	name := parseStringFromBytes(packet.Name[:])
 
 	field := &Field{
 		Offset: *offset,
@@ -332,6 +343,81 @@
 	return false, nil
 }
 
+func (z *Zedmon) getParameter(index uint) (bool, error) {
+	data := [2]byte{PACKET_TYPE_QUERY_PARAMETER_VALUE, uint8(index)}
+
+	numBytes, err := z.outEp.Write(data[:])
+	if err != nil {
+		return false, err
+	}
+	if numBytes != len(data) {
+		return false, fmt.Errorf("Incomplete write %d != %d", numBytes, len(data))
+	}
+
+	response := z.read() //<-z.readChan
+	var packet parameterValuePacket
+
+	binary.Read(bytes.NewReader(response), binary.LittleEndian, &packet)
+
+	if packet.PacketType != PACKET_TYPE_PARAMTER_VALUE {
+		return false, fmt.Errorf("Expected packet type %d, received %d",
+			PACKET_TYPE_PARAMTER_VALUE, packet.PacketType)
+	}
+
+	name := parseStringFromBytes(packet.Name[:])
+	if name == "" {
+		return true, nil
+	}
+
+	dataType := DataType(packet.DataType)
+	if dataType.Size() < 0 {
+		return false, fmt.Errorf("Can't handle Value type %d", dataType)
+	}
+
+	value, err := dataType.Read(bytes.NewReader(packet.Data[:]))
+	if err != nil {
+		return false, err
+	}
+
+	parameter := &Parameter{
+		Name:  name,
+		Value: value,
+	}
+
+	z.parameters = append(z.parameters, parameter)
+
+	return false, nil
+}
+
+func (z *Zedmon) enumerateParameters() error {
+	for i := uint(0); ; i++ {
+		done, err := z.getParameter(i)
+		if err != nil {
+			return err
+		}
+		if done {
+			return nil
+		}
+	}
+}
+
+// GetResistance retreives the shunt resistance from the Zedmon firmware. If the
+// shunt_resistance parameter cannot be found, or if its value is non-positive,
+// the returned resistance is 0.0 and the error is non-nil.
+func (z *Zedmon) GetShuntResistance() (float64, error) {
+	for _, parameter := range z.parameters {
+		if parameter.Name == "shunt_resistance" {
+			resistance := parameter.Value
+			if resistance <= 0.0 {
+				return 0.0, fmt.Errorf("non-positive resistance found: %g", resistance)
+			}
+			return resistance, nil
+		}
+	}
+
+	return 0.0, errors.New("resistance not found")
+}
+
 func findZedmonInterface(dev *gousb.Device) (config int, intf int, setting int, err error) {
 	for cfgNum, cfgDesc := range dev.Desc.Configs {
 		for intfNum, intfDesc := range cfgDesc.Interfaces {
@@ -351,7 +437,7 @@
 func (z *Zedmon) enumerateFields() error {
 	offset := 0
 	for i := uint(0); ; i++ {
-		done, err := z.getFormatReport(i, &offset)
+		done, err := z.getReportFormat(i, &offset)
 		if err != nil {
 			return err
 		}
@@ -474,6 +560,7 @@
 	}
 
 	zedmon.enumerateFields()
+	zedmon.enumerateParameters()
 
 	return zedmon, nil
 }