[firmware] Add beginnings of usb proto support.

Change-Id: Iaa4f51f650f4e58fff0c576a5a22a7fd85de7b7e
diff --git a/firmware/app/zedmon/include/app/zedmon/usb.h b/firmware/app/zedmon/include/app/zedmon/usb.h
new file mode 100644
index 0000000..e41d7b9
--- /dev/null
+++ b/firmware/app/zedmon/include/app/zedmon/usb.h
@@ -0,0 +1,7 @@
+#ifndef __APP_ZEDMON_USB_H
+#define __APP_ZEDMON_USB_H
+
+void zedmon_usb_init(int data_ep_addr);
+
+#endif  // __APP_ZEDMON_USB_H
+
diff --git a/firmware/app/zedmon/include/app/zedmon/usb_proto.h b/firmware/app/zedmon/include/app/zedmon/usb_proto.h
new file mode 100644
index 0000000..ccf10b2
--- /dev/null
+++ b/firmware/app/zedmon/include/app/zedmon/usb_proto.h
@@ -0,0 +1,47 @@
+#ifndef __APP_ZEDMON_USB_PROTO_H_
+#define __APP_ZEDMON_USB_PROTO_H_
+
+#include <assert.h>
+#include <stdint.h>
+
+typedef enum {
+    ZEDMON_USB_PACKET_QUERY             = 0x00,
+    ZEDMON_USB_PACKET_ENABLE_REPORTING  = 0x10,
+    ZEDMON_USB_PACKET_DISABLE_REPORTING = 0x11,
+    ZEDMON_USB_PACKET_REPORT_FORMAT     = 0x80,
+    ZEDMON_USB_PACKET_REPORT            = 0x81,
+} zedmon_usb_packet_type_t;
+
+typedef enum {
+    ZEDMON_USB_TYPE_UINT8   = 0x00,
+    ZEDMON_USB_TYPE_UINT16  = 0x01,
+    ZEDMON_USB_TYPE_UINT32  = 0x02,
+    ZEDMON_USB_TYPE_UINT64  = 0x03,
+    ZEDMON_USB_TYPE_FLOAT32 = 0x10,
+    ZEDMON_USB_TYPE_STRING  = 0x11,
+} zedmon_usb_type_t;
+
+typedef enum {
+    ZEDMON_USB_UNIT_AMPERE = 0x00,
+    ZEDMON_USB_UNIT_VOLTS  = 0x01,
+} zedmon_usb_unit_t;
+
+typedef struct {
+    uint8_t packet_type;
+    uint8_t index;
+    uint8_t type;  // zedmon_usb_type_t
+    uint8_t unit;  // zedmon_usb_unit_t
+    float scale;
+    char name[56];
+} zedmon_usb_report_format_t;
+
+static_assert(sizeof(zedmon_usb_report_format_t) == 64);
+static_assert(offsetof(zedmon_usb_report_format_t, packet_type) == 0);
+static_assert(offsetof(zedmon_usb_report_format_t, index) == 1);
+static_assert(offsetof(zedmon_usb_report_format_t, type) == 2);
+static_assert(offsetof(zedmon_usb_report_format_t, unit) == 3);
+static_assert(offsetof(zedmon_usb_report_format_t, scale) == 4);
+static_assert(offsetof(zedmon_usb_report_format_t, name) == 8);
+
+#endif  // __APP_ZEDMON_USB_PROTO_H_
+
diff --git a/firmware/app/zedmon/rules.mk b/firmware/app/zedmon/rules.mk
index 23676bb..24651ef 100644
--- a/firmware/app/zedmon/rules.mk
+++ b/firmware/app/zedmon/rules.mk
@@ -4,6 +4,8 @@
 
 MODULE_SRCS += \
 	$(LOCAL_DIR)/ina231.c \
-	$(LOCAL_DIR)/main.c
+	$(LOCAL_DIR)/main.c \
+	$(LOCAL_DIR)/usb.c
+
 
 include make/module.mk
diff --git a/firmware/app/zedmon/usb.c b/firmware/app/zedmon/usb.c
new file mode 100644
index 0000000..093310a
--- /dev/null
+++ b/firmware/app/zedmon/usb.c
@@ -0,0 +1,336 @@
+#include <app/zedmon/usb.h>
+
+#include <assert.h>
+#include <app/zedmon/usb_proto.h>
+#include <compiler.h>
+#include <debug.h>
+#include <dev/udc.h>
+#include <dev/usb.h>
+#include <dev/usbc.h>
+#include <err.h>
+#include <kernel/mutex.h>
+#include <kernel/port.h>
+#include <kernel/thread.h>
+#include <lib/console.h>
+#include <stdint.h>
+#include <string.h>
+#include <trace.h>
+
+#define DATA_IN_EP_ADDR_OFFSET (0x0B)
+#define DATA_OUT_EP_ADDR_OFFSET (0x12)
+
+#define W(w) (w & 0xff), (w >> 8)
+
+static uint8_t zedmon_if_descriptor[] = {
+    0x09,           /* length */
+    INTERFACE,      /* type */
+    0x00,           /* interface num */
+    0x00,           /* alternates */
+    0x02,           /* endpoint count */
+    0xff,           /* interface class */
+    0xff,           /* interface subclass */
+    0x00,           /* interface protocol */
+    0x00,           /* string index */
+
+    /* endpoint 1 IN */
+    0x07,           /* length */
+    ENDPOINT,       /* type */
+    0x83,           /* address: 1 IN */
+    0x02,           /* type: bulk */
+    W(64),          /* max packet size: 64 */
+    00,             /* interval */
+
+    /* endpoint 1 OUT */
+    0x07,           /* length */
+    ENDPOINT,       /* type */
+    0x03,           /* address: 1 OUT */
+    0x02,           /* type: bulk */
+    W(64),          /* max packet size: 64 */
+    00,             /* interval */
+};
+
+static zedmon_usb_report_format_t zedmon_report_formats[] = {
+    {
+        .packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
+        .index = 0,
+        .type = ZEDMON_USB_TYPE_UINT16,
+        .unit = ZEDMON_USB_UNIT_VOLTS,
+        .scale = 2.5e-6f,
+        .name = "v_shunt",
+    },
+    {
+        .packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
+        .index = 1,
+        .type = ZEDMON_USB_TYPE_UINT16,
+        .unit = ZEDMON_USB_UNIT_VOLTS,
+        .scale = 1.5e-3f,
+        .name = "v_bus",
+    },
+    {
+        .packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
+        .index = 0xff,
+        .type = 0,
+        .unit = 0,
+        .scale =0.0f,
+        .name = "",
+    },
+};
+
+static int zedmon_ep;
+
+typedef enum {
+    ZEDMON_PORT_CTX_USB_RX = 0x0,
+    ZEDMON_PORT_CTX_USB_TX = 0x1,
+    ZEDMON_PORT_CTX_I2C = 0x2,
+} zedmon_port_context_t;
+
+static port_t zedmon_usb_rx_port;
+static port_t zedmon_usb_tx_port;
+static port_t zedmon_usb_port_group;
+
+static thread_t *zedmon_usb_thread;
+static bool zedmon_tx_busy;
+
+static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t);
+static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t);
+
+static void queue_rx(void)
+{
+    static usbc_transfer_t transfer;
+    static uint8_t buf[512];
+
+    transfer.callback = &ep_cb_rx;
+    transfer.result = 0;
+    transfer.buf = &buf;
+    transfer.buflen = sizeof(buf);
+    transfer.bufpos = 0;
+    transfer.extra = 0;
+
+    usbc_queue_rx(zedmon_ep, &transfer);
+}
+
+static status_t queue_tx(void *data, size_t len)
+{
+    if (zedmon_tx_busy) {
+        return ERR_BUSY;
+    }
+
+    static usbc_transfer_t transfer;
+    transfer.callback = &ep_cb_tx;
+    transfer.result = 0;
+    transfer.buf = data;
+    transfer.buflen = len;
+    transfer.bufpos = 0;
+    transfer.extra = 0;
+
+    zedmon_tx_busy = true;
+    usbc_queue_tx(zedmon_ep, &transfer);
+    return NO_ERROR;
+}
+
+static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t)
+{
+#if LOCAL_TRACE
+    LTRACEF("ep %u transfer %p\n", endpoint, t);
+    usbc_dump_transfer(t);
+
+    if (t->result >= 0) {
+        hexdump8(t->buf, t->bufpos);
+    }
+#endif
+
+    //printf("ep %u transfer %p\n", endpoint, t);
+
+    if (t->result >= 0) {
+        if (t->bufpos > 1) {
+            port_packet_t packet;
+            int command_length =
+                t->bufpos < sizeof(packet.value) ? t->bufpos : sizeof(packet.value);
+            memcpy(&packet.value, t->buf, command_length);
+            packet.value[0] = ((uint8_t *)t->buf)[0];
+            port_write(zedmon_usb_rx_port, &packet, 1);
+        }
+                    queue_rx();
+    } else {
+        printf("bad rx\n");
+    }
+
+
+    return NO_ERROR;
+}
+
+static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t)
+{
+    //printf("ep %u transfer %p\n", endpoint, t);
+#if LOCAL_TRACE
+    LTRACEF("ep %u transfer %p\n", endpoint, t);
+    usbc_dump_transfer(t);
+#endif
+
+    if (t->result >= 0) {
+        port_packet_t packet;
+        packet.value[0] = 0x0;
+        port_write(zedmon_usb_tx_port, &packet, 1);
+    } else {
+        printf("bad tx\n");
+    }
+    return NO_ERROR;
+}
+
+
+static status_t zedmon_usb_callback(void *cookie, usb_callback_op_t op,
+                                    const union usb_callback_args *args)
+{
+#if LOCAL_TRACE
+    LTRACEF("cookie %p, op %u, args %p\n", cookie, op, args);
+#endif
+
+    if (op == USB_CB_ONLINE) {
+        usbc_setup_endpoint(zedmon_ep, USB_IN, 0x40, USB_BULK);
+        usbc_setup_endpoint(zedmon_ep, USB_OUT, 0x40, USB_BULK);
+
+        queue_rx();
+    } else if (op == USB_CB_SETUP_MSG) {
+        //usbc_ep0_ack();
+    }
+    return NO_ERROR;
+}
+
+
+static void zedmon_usb_handle_query_packet(uint8_t values[7]) {
+    uint8_t index = values[0];
+
+    if (index >= countof(zedmon_report_formats)) {
+        index = countof(zedmon_report_formats) - 1;
+    }
+
+    queue_tx(&zedmon_report_formats[index], sizeof(zedmon_report_formats[index]));
+}
+
+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];
+
+    switch (type) {
+        case ZEDMON_USB_PACKET_QUERY:
+            zedmon_usb_handle_query_packet((uint8_t *)result->packet.value + 1);
+            break;
+        case ZEDMON_USB_PACKET_ENABLE_REPORTING:
+            break;
+        case ZEDMON_USB_PACKET_DISABLE_REPORTING:
+            break;
+
+            // These are Device -> Host packets which we should never see.
+        case ZEDMON_USB_PACKET_REPORT_FORMAT:
+            break;
+        case ZEDMON_USB_PACKET_REPORT:
+            break;
+    }
+}
+
+static int zedmon_usb_thread_entry(void *arg) {
+    while (true) {
+        port_result_t result;
+        port_read(zedmon_usb_port_group, INFINITE_TIME, &result);
+        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);
+                if (zedmon_tx_busy == false) {
+                    //queue_rx();
+                }
+                break;
+
+            case ZEDMON_PORT_CTX_USB_TX:
+                zedmon_tx_busy = false;
+                //queue_rx();
+                break;
+
+            case ZEDMON_PORT_CTX_I2C:
+                break;
+
+        }
+    }
+    return 0;
+}
+
+void zedmon_usb_init(int data_ep_addr) {
+    status_t ret;
+
+    ret = port_create("zm_usb_rx", PORT_MODE_UNICAST, &zedmon_usb_rx_port);
+    if (ret != NO_ERROR) {
+        printf("error creating zm_usb_rx: %d\n", ret);
+    }
+
+    port_t usb_rx_reader;
+    ret = port_open("zm_usb_rx", (void *)ZEDMON_PORT_CTX_USB_RX, &usb_rx_reader);
+    if (ret != NO_ERROR) {
+        printf("error opening zm_usb_rx: %d\n", ret);
+    }
+
+    ret = port_create("zm_usb_tx", PORT_MODE_UNICAST, &zedmon_usb_tx_port);
+    if (ret != NO_ERROR) {
+        printf("error creating zm_usb_rx: %d\n", ret);
+    }
+
+    port_t usb_tx_reader;
+    ret = port_open("zm_usb_tx", (void *)ZEDMON_PORT_CTX_USB_TX, &usb_tx_reader);
+    if (ret != NO_ERROR) {
+        printf("error opening zm_usb_tx: %d\n", ret);
+    }
+
+
+    port_t ports[] = {usb_rx_reader, usb_tx_reader};
+
+    ret = port_group(ports, countof(ports), &zedmon_usb_port_group);
+
+    zedmon_ep = data_ep_addr;
+
+    zedmon_if_descriptor[DATA_IN_EP_ADDR_OFFSET]  = data_ep_addr | 0x80;
+    zedmon_if_descriptor[DATA_OUT_EP_ADDR_OFFSET] = data_ep_addr;
+
+    usb_append_interface_lowspeed(zedmon_if_descriptor, sizeof(zedmon_if_descriptor));
+    usb_append_interface_highspeed(zedmon_if_descriptor, sizeof(zedmon_if_descriptor));
+
+    usb_register_callback(&zedmon_usb_callback, NULL);
+
+    zedmon_tx_busy = false;
+    zedmon_usb_thread = thread_create("zedmon_usb", &zedmon_usb_thread_entry, 0x0,
+                                      DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+    thread_detach(zedmon_usb_thread);
+    thread_resume(zedmon_usb_thread);
+}
+
+#include "stm32f0xx_hal.h"
+static void print_epr(int i, uint16_t epr) {
+    printf("ep%dr: ea:%d stat_tx:%d dtog_tx:%d ctr_tx:%d ep_kind:%d ep_type:%d\n"
+           "       setup:%d stat_rx:%d dtog_rx:%d ctr_rx:%d\n",
+           i,
+           epr & 0xf,
+           (epr >> 4) & 0x3,
+           (epr >> 6) & 0x1,
+           (epr >> 7) & 0x1,
+           (epr >> 8) & 0x1,
+           (epr >> 9) & 0x3,
+           (epr >> 11) & 0x1,
+           (epr >> 12) & 0x3,
+           (epr >> 14) & 0x1,
+           (epr >> 15) & 0x1);
+}
+
+static int cmd_usb_dump(int argc, const cmd_args *argv) {
+    print_epr(0, USB->EP0R);
+    print_epr(1, USB->EP1R);
+    print_epr(2, USB->EP2R);
+    print_epr(3, USB->EP3R);
+    print_epr(4, USB->EP4R);
+    print_epr(5, USB->EP5R);
+    print_epr(6, USB->EP6R);
+    print_epr(7, USB->EP7R);
+
+    return NO_ERROR;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("usb_dump", "usb dump", &cmd_usb_dump)
+STATIC_COMMAND_END(usb);
diff --git a/firmware/target/zedmon/usb.c b/firmware/target/zedmon/usb.c
index 3e98cf6..b12fe53 100644
--- a/firmware/target/zedmon/usb.c
+++ b/firmware/target/zedmon/usb.c
@@ -21,17 +21,19 @@
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
-#include <err.h>
-#include <debug.h>
-#include <stdio.h>
-#include <trace.h>
-#include <target.h>
+#include <app/zedmon/usb.h>
 #include <compiler.h>
+#include <debug.h>
 #include <dev/usb.h>
 #include <dev/usbc.h>
 #include <dev/usb/class/cdcserial.h>
+#include <err.h>
 #include <hw/usb.h>
 #include <lk/init.h>
+#include <stdio.h>
+#include <stm32f0xx.h>
+#include <target.h>
+#include <trace.h>
 
 #define LOCAL_TRACE 0
 
@@ -52,7 +54,7 @@
     W(0x0000),      /* release */
     0x2,            /* manufacturer string */
     0x1,            /* product string */
-    0x0,            /* serialno string */
+    0x3,            /* serialno string */
     0x1,            /* num configs */
 };
 
@@ -82,34 +84,6 @@
 
 static const uchar langid[] = { 0x04, 0x03, 0x09, 0x04 };
 
-static const uint8_t if_descriptor_lowspeed[] = {
-    0x09,           /* length */
-    INTERFACE,      /* type */
-    0x01,           /* interface num */
-    0x00,           /* alternates */
-    0x02,           /* endpoint count */
-    0xff,           /* interface class */
-    0xff,           /* interface subclass */
-    0x00,           /* interface protocol */
-    0x00,           /* string index */
-
-    /* endpoint 1 IN */
-    0x07,           /* length */
-    ENDPOINT,       /* type */
-    0x83,           /* address: 1 IN */
-    0x02,           /* type: bulk */
-    W(64),          /* max packet size: 64 */
-    00,             /* interval */
-
-    /* endpoint 1 OUT */
-    0x07,           /* length */
-    ENDPOINT,       /* type */
-    0x03,           /* address: 1 OUT */
-    0x02,           /* type: bulk */
-    W(64),          /* max packet size: 64 */
-    00,             /* interval */
-};
-
 usb_config config = {
     .lowspeed = {
         .device = USB_DESC_STATIC(dev_descr),
@@ -125,85 +99,14 @@
     .langid = USB_DESC_STATIC(langid),
 };
 
-static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t);
-static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t);
+static const char *hex_digits = "0123456789abcdef";
 
-static void queue_rx(void)
-{
-    static usbc_transfer_t transfer;
-    static uint8_t buf[512];
-
-    transfer.callback = &ep_cb_rx;
-    transfer.result = 0;
-    transfer.buf = &buf;
-    transfer.buflen = sizeof(buf);
-    transfer.bufpos = 0;
-    transfer.extra = 0;
-
-    usbc_queue_rx(3, &transfer);
-}
-
-static void queue_tx(void)
-{
-    static usbc_transfer_t transfer;
-    static uint8_t buf[512];
-
-    for (uint i = 0; i < sizeof(buf); i++) {
-        buf[i] = ~i;
+void set_serial_substr(uint32_t val, char *str) {
+    int i;
+    for (i = 0; i < 8; i++) {
+        *str++ = hex_digits[(val >> 28) & 0xf];
+        val <<= 4;
     }
-
-    transfer.callback = &ep_cb_tx;
-    transfer.result = 0;
-    transfer.buf = &buf;
-    transfer.buflen = sizeof(buf);
-    transfer.bufpos = 0;
-    transfer.extra = 0;
-
-    usbc_queue_tx(3, &transfer);
-}
-
-static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t)
-{
-#if LOCAL_TRACE
-    LTRACEF("ep %u transfer %p\n", endpoint, t);
-    usbc_dump_transfer(t);
-
-    if (t->result >= 0) {
-        hexdump8(t->buf, t->bufpos);
-    }
-#endif
-
-    if (t->result >= 0)
-        queue_rx();
-
-    return NO_ERROR;
-}
-
-static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t)
-{
-#if LOCAL_TRACE
-    LTRACEF("ep %u transfer %p\n", endpoint, t);
-    usbc_dump_transfer(t);
-#endif
-
-    if (t->result >= 0)
-        queue_tx();
-
-    return NO_ERROR;
-}
-
-static status_t usb_cb(void *cookie, usb_callback_op_t op, const union usb_callback_args *args)
-{
-    LTRACEF("cookie %p, op %u, args %p\n", cookie, op, args);
-
-    if (op == USB_CB_ONLINE) {
-        usbc_setup_endpoint(3, USB_IN, 0x40, USB_BULK);
-        usbc_setup_endpoint(3, USB_OUT, 0x40, USB_BULK);
-
-        queue_rx();
-        queue_tx();
-    }
-    return NO_ERROR;
 }
 
 void target_usb_setup(void)
@@ -213,12 +116,19 @@
     cdcserial_create_channel(0x1, 0x2);
     cdcserial_init();
 
-    usb_append_interface_lowspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
-    usb_append_interface_highspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
-    usb_register_callback(&usb_cb, NULL);
+    zedmon_usb_init(0x3);
 
     usb_add_string("Zedmon", 1);
     usb_add_string("Google Inc.", 2);
 
+    char serial_no[25];
+    volatile uint32_t *uid_regs = (volatile uint32_t *)UID_BASE;
+    int i;
+    for (i = 0; i < 3; i++) {
+        set_serial_substr(uid_regs[i], &serial_no[(2 - i) * 8]);
+    }
+    serial_no[24] = '\0';
+    usb_add_string(serial_no, 3);
+
     usb_start();
 }