Add watchdog to restart sampling pipeline.

If we miss an alert signal edge, we'll stop sampling.  To protect
against this, we do an extra sample if we've gotten no samples
between two heartbeats.

Change-Id: I420f18b7ae14fb5683bf8609e2c0bdb584c3c814
diff --git a/firmware/app/zedmon/ina.c b/firmware/app/zedmon/ina.c
index 2bb3238..4ad7485 100644
--- a/firmware/app/zedmon/ina.c
+++ b/firmware/app/zedmon/ina.c
@@ -4,6 +4,7 @@
 #include <assert.h>
 #include <err.h>
 #include <dev/i2c.h>
+#include <kernel/timer.h>
 #include <lib/cbuf.h>
 #include <lib/console.h>
 #include <platform.h>
@@ -16,10 +17,18 @@
 
 typedef struct __attribute__((packed)) {
     lk_bigtime_t time;
-    uint8_t channel;
+    uint8_t type:2;
+    uint8_t channel:6;
 } ina_alert_event_t;
 
+
+enum event_type {
+    EVENT_TYPE_SAMPLE = 0x0,
+    EVENT_TYPE_HEARTBEAT = 0x1,
+};
+
 static cbuf_t ina_alert_events;
+static timer_t ina_heartbeat_timer;
 
 static bool (*ina_sample)(uint16_t *v_shunt, uint16_t *v_bus);
 static bool ina_initialized = false;
@@ -28,7 +37,17 @@
 static uint32_t ina_samples;
 static uint16_t ina_v_shunt;
 static uint16_t ina_v_bus;
+static uint16_t ina_samples_since_heartbeat;
 
+// This exepects to be called from irq context.
+bool send_irq_event(ina_alert_event_t *event) {
+    // This won't race because we're in irq context.
+    if (cbuf_space_avail(&ina_alert_events) >= sizeof(*event)) {
+        cbuf_write(&ina_alert_events, event, sizeof(*event), false);
+        return true;
+    }
+    return false;
+}
 
 bool ina_alert_irq(int index, uint64_t timestamp) {
     // We only handle a single channel right now.
@@ -39,16 +58,22 @@
     }
 
     ina_alert_event_t event = {
+        .type = EVENT_TYPE_SAMPLE,
         .channel = 0,
         .time = timestamp,
     };
 
-    // This won't race because we're in interrupt context.
-    if (cbuf_space_avail(&ina_alert_events) >= sizeof(event)) {
-        cbuf_write(&ina_alert_events, &event, sizeof(event), false);
-        return true;
-    }
-    return false;
+    return send_irq_event(&event);
+}
+
+enum handler_return ina_heartbeat_handler(struct timer *timer, lk_time_t now, void *arg){
+    ina_alert_event_t event = {
+        .type = EVENT_TYPE_HEARTBEAT,
+        .channel = 0,
+        .time = 0,
+    };
+
+    return send_irq_event(&event);
 }
 
 void ina_init(void) {
@@ -62,12 +87,15 @@
         ina_sample = ina231_sample;
     }
 
+    timer_initialize(&ina_heartbeat_timer);
     ina_initialized = true;
 
     // call sample once to unstick the conversion ready flag.
     uint16_t v_shunt;
     uint16_t v_bus;
     ina_sample(&v_shunt, &v_bus);
+
+    timer_set_periodic(&ina_heartbeat_timer, 1000, ina_heartbeat_handler, NULL);
 }
 
 
@@ -83,15 +111,32 @@
         uint16_t v_shunt;
         uint16_t v_bus;
 
+        // If we miss an alert signal edge, we'll stop sampling.  To protect
+        // against this, we do an extra sample if we've gotten no samples
+        // between two heartbeats.
+        if (event.type == EVENT_TYPE_HEARTBEAT) {
+            uint16_t samples = ina_samples_since_heartbeat;
+            ina_samples_since_heartbeat = 0;
+            if (samples > 0) {
+                continue;
+            }
+            // Kick off a read below to restart the pipeline.
+        }
+
+
         if (!ina_sample(&v_shunt, &v_bus)) {
             ina_errors++;
             continue;
         }
 
-        ina_v_shunt = v_shunt;
-        ina_v_bus = v_bus;
-        ina_samples++;
-        zedmon_usb_add_sample(event.time, v_shunt, v_bus);
+        // Don't report heartbeat samples.
+        if (event.type == EVENT_TYPE_SAMPLE) {
+            ina_v_shunt = v_shunt;
+            ina_v_bus = v_bus;
+            ina_samples++;
+            ina_samples_since_heartbeat++;
+            zedmon_usb_add_sample(event.time, v_shunt, v_bus);
+        }
     }
 }