summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/cr50/board.c1
-rw-r--r--board/cr50/board.h1
-rw-r--r--chip/g/build.mk2
-rw-r--r--chip/g/usb_blob.c218
-rw-r--r--common/blob.c113
-rw-r--r--common/build.mk1
-rw-r--r--include/blob.h26
7 files changed, 362 insertions, 0 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c
index 4e122b56aa..c02d7f19f6 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -87,6 +87,7 @@ const void * const usb_strings[] = {
[USB_STR_PRODUCT] = USB_STRING_DESC("Cr50"),
[USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
[USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Shell"),
+ [USB_STR_BLOB_NAME] = USB_STRING_DESC("Blob"),
};
BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
#endif
diff --git a/board/cr50/board.h b/board/cr50/board.h
index ba922ca357..fa0d5f7010 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -52,6 +52,7 @@ enum usb_strings {
USB_STR_PRODUCT,
USB_STR_VERSION,
USB_STR_CONSOLE_NAME,
+ USB_STR_BLOB_NAME,
USB_STR_COUNT
};
diff --git a/chip/g/build.mk b/chip/g/build.mk
index 9d26880083..ff1c607881 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -24,3 +24,5 @@ chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
chip-$(CONFIG_USB_HID)+=usb_hid.o
+# TODO(wfrichar): Document this (and all other CONFIG_USB_*) in config.h
+chip-$(CONFIG_USB_BLOB)+=usb_blob.o
diff --git a/chip/g/usb_blob.c b/chip/g/usb_blob.c
new file mode 100644
index 0000000000..31ef590025
--- /dev/null
+++ b/chip/g/usb_blob.c
@@ -0,0 +1,218 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "blob.h"
+#include "common.h"
+#include "link_defs.h"
+#include "printf.h"
+#include "registers.h"
+#include "timer.h"
+#include "usb.h"
+
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+
+static int is_reset;
+
+/* USB-Serial descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_BLOB) =
+{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_BLOB,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0, /* TODO(wfrichar): TBD */
+ .bInterfaceProtocol = 0, /* TODO(wfrichar): TBD */
+ .iInterface = USB_STR_BLOB_NAME,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_BLOB, 0) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x80 | USB_EP_BLOB,
+ .bmAttributes = 0x02 /* Bulk IN */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 10
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_BLOB, 1) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_EP_BLOB,
+ .bmAttributes = 0x02 /* Bulk OUT */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 0
+};
+
+static uint8_t ep_buf_tx[USB_MAX_PACKET_SIZE];
+static uint8_t ep_buf_rx[USB_MAX_PACKET_SIZE];
+static struct g_usb_desc ep_out_desc;
+static struct g_usb_desc ep_in_desc;
+
+/* Let the USB HW IN-to-host FIFO transmit some bytes */
+static void usb_enable_tx(int len)
+{
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(len);
+ GR_USB_DIEPCTL(USB_EP_BLOB) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+/* Let the USB HW OUT-from-host FIFO receive some bytes */
+static void usb_enable_rx(int len)
+{
+ ep_out_desc.flags = DOEPDMA_RXBYTES(len) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ GR_USB_DOEPCTL(USB_EP_BLOB) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+/* True if the HW Rx/OUT FIFO has bytes for us. */
+static inline int rx_fifo_is_ready(void)
+{
+ return (ep_out_desc.flags & DOEPDMA_BS_MASK) == DOEPDMA_BS_DMA_DONE;
+}
+
+/*
+ * This function tries to shove new bytes from the USB host into the queue for
+ * consumption elsewhere. It is invoked either by a HW interrupt (telling us we
+ * have new bytes from the USB host), or by whoever is reading bytes out of the
+ * other end of the queue (telling us that there's now more room in the queue
+ * if we still have bytes to shove in there).
+ */
+static void rx_fifo_handler(void)
+{
+ /*
+ * The HW FIFO buffer (ep_buf_rx) is always filled from [0] by the
+ * hardware. The rx_in_fifo variable counts how many bytes of that
+ * buffer are actually valid, and is calculated from the HW DMA
+ * descriptor table. The descriptor is updated by the hardware, and it
+ * and ep_buf_rx remains valid and unchanged until software tells the
+ * the hardware engine to accept more input.
+ */
+ int rx_in_fifo, rx_left;
+
+ /*
+ * The rx_handled variable tracks how many of the bytes in the HW FIFO
+ * we've copied into the incoming queue. The queue may not accept all
+ * of them at once, so we have to keep track of where we are so that
+ * the next time this function is called we can try to shove the rest
+ * of the HW FIFO bytes into the queue.
+ */
+ static int rx_handled;
+
+ /* If the HW FIFO isn't ready, then we're waiting for more bytes */
+ if (!rx_fifo_is_ready())
+ return;
+
+ /*
+ * How many of the HW FIFO bytes have we not yet handled? We need to
+ * know both where we are in the buffer and how many bytes we haven't
+ * yet enqueued. One can be calculated from the other as long as we
+ * know rx_in_fifo, but we need at least one static variable.
+ */
+ rx_in_fifo = USB_MAX_PACKET_SIZE
+ - (ep_out_desc.flags & DOEPDMA_RXBYTES_MASK);
+ rx_left = rx_in_fifo - rx_handled;
+
+ /* If we have some, try to shove them into the queue */
+ if (rx_left) {
+ size_t added = put_bytes_to_blob(ep_buf_rx + rx_handled,
+ rx_left);
+ rx_handled += added;
+ rx_left -= added;
+ }
+
+ /*
+ * When we've handled all the bytes in the queue ("rx_in_fifo ==
+ * rx_handled" and "rx_left == 0" indicate the same thing), we can
+ * reenable the USB HW to go fetch more.
+ */
+ if (!rx_left) {
+ rx_handled = 0;
+ usb_enable_rx(USB_MAX_PACKET_SIZE);
+ }
+}
+DECLARE_DEFERRED(rx_fifo_handler);
+
+void blob_is_ready_for_more_bytes(void)
+{
+ hook_call_deferred(rx_fifo_handler, 0);
+}
+
+/* Rx/OUT interrupt handler */
+static void con_ep_rx(void)
+{
+ /* Wake up the Rx FIFO handler */
+ hook_call_deferred(rx_fifo_handler, 0);
+
+ /* clear the RX/OUT interrupts */
+ GR_USB_DOEPINT(USB_EP_BLOB) = 0xffffffff;
+}
+
+/* True if the Tx/IN FIFO can take some bytes from us. */
+static inline int tx_fifo_is_ready(void)
+{
+ uint32_t status = ep_in_desc.flags & DIEPDMA_BS_MASK;
+ return status == DIEPDMA_BS_DMA_DONE || status == DIEPDMA_BS_HOST_BSY;
+}
+
+/* Try to send some bytes to the host */
+static void tx_fifo_handler(void)
+{
+ size_t count;
+
+ if (!is_reset)
+ return;
+
+ /* If the HW FIFO isn't ready, then we can't do anything right now. */
+ if (!tx_fifo_is_ready())
+ return;
+
+ count = get_bytes_from_blob(ep_buf_tx, USB_MAX_PACKET_SIZE);
+ if (count)
+ usb_enable_tx(count);
+}
+DECLARE_DEFERRED(tx_fifo_handler);
+
+void blob_is_ready_to_emit_bytes(void)
+{
+ hook_call_deferred(tx_fifo_handler, 0);
+}
+
+/* Tx/IN interrupt handler */
+static void con_ep_tx(void)
+{
+ /* Wake up the Tx FIFO handler */
+ hook_call_deferred(tx_fifo_handler, 0);
+
+ /* clear the Tx/IN interrupts */
+ GR_USB_DIEPINT(USB_EP_BLOB) = 0xffffffff;
+}
+
+static void ep_reset(void)
+{
+ ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ ep_out_desc.addr = ep_buf_rx;
+ GR_USB_DOEPDMA(USB_EP_BLOB) = (uint32_t)&ep_out_desc;
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
+ ep_in_desc.addr = ep_buf_tx;
+ GR_USB_DIEPDMA(USB_EP_BLOB) = (uint32_t)&ep_in_desc;
+ GR_USB_DOEPCTL(USB_EP_BLOB) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_CNAK | DXEPCTL_EPENA;
+ GR_USB_DIEPCTL(USB_EP_BLOB) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_TXFNUM(USB_EP_BLOB);
+ GR_USB_DAINTMSK |= (1<<USB_EP_BLOB) | (1 << (USB_EP_BLOB+16));
+
+ is_reset = 1;
+
+ /* Flush any queued data */
+ hook_call_deferred(tx_fifo_handler, 0);
+ hook_call_deferred(rx_fifo_handler, 0);
+}
+
+USB_DECLARE_EP(USB_EP_BLOB, con_ep_tx, con_ep_rx, ep_reset);
diff --git a/common/blob.c b/common/blob.c
new file mode 100644
index 0000000000..5e563598a3
--- /dev/null
+++ b/common/blob.c
@@ -0,0 +1,113 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Handle an opaque blob of data */
+
+#include "blob.h"
+#include "common.h"
+#include "console.h"
+#include "printf.h"
+#include "queue.h"
+#include "task.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+
+#define INCOMING_QUEUE_SIZE 100
+#define OUTGOING_QUEUE_SIZE 100
+
+
+static void incoming_add(struct queue_policy const *queue_policy, size_t count)
+{
+ task_wake(TASK_ID_BLOB);
+}
+
+static void incoming_remove(struct queue_policy const *queue_policy,
+ size_t count)
+{
+ blob_is_ready_for_more_bytes();
+}
+
+static struct queue_policy const incoming_policy = {
+ .add = incoming_add,
+ .remove = incoming_remove,
+};
+
+static void outgoing_add(struct queue_policy const *queue_policy, size_t count)
+{
+ blob_is_ready_to_emit_bytes();
+}
+
+static void outgoing_remove(struct queue_policy const *queue_policy,
+ size_t count)
+{
+ /* we don't care */
+}
+
+static struct queue_policy const outgoing_policy = {
+ .add = outgoing_add,
+ .remove = outgoing_remove,
+};
+
+static struct queue const incoming_q = QUEUE(INCOMING_QUEUE_SIZE, uint8_t,
+ incoming_policy);
+
+static struct queue const outgoing_q = QUEUE(OUTGOING_QUEUE_SIZE, uint8_t,
+ outgoing_policy);
+
+
+/* Call this to send data to the blob-handler */
+size_t put_bytes_to_blob(uint8_t *buffer, size_t count)
+{
+ return QUEUE_ADD_UNITS(&incoming_q, buffer, count);
+}
+
+/* Call this to get data back fom the blob-handler */
+size_t get_bytes_from_blob(uint8_t *buffer, size_t count)
+{
+ return QUEUE_REMOVE_UNITS(&outgoing_q, buffer, count);
+}
+
+#define WEAK_FUNC(FOO) \
+ void __ ## FOO(void) {} \
+ void FOO(void) \
+ __attribute__((weak, alias(STRINGIFY(CONCAT2(__, FOO)))))
+
+/* Default callbacks for outsiders */
+WEAK_FUNC(blob_is_ready_for_more_bytes);
+WEAK_FUNC(blob_is_ready_to_emit_bytes);
+
+/* Do the magic */
+void blob_task(void)
+{
+ static uint8_t buf[INCOMING_QUEUE_SIZE];
+ size_t count, i;
+ task_id_t me = task_get_current();
+
+ while (1) {
+ CPRINTS("task %d waiting for events...", me);
+ task_wait_event(-1);
+ CPRINTS("task %d awakened!", me);
+
+ count = QUEUE_REMOVE_UNITS(&incoming_q, buf, sizeof(buf));
+
+ CPRINTS("task %d gets: count=%d buf=((%s))", me, count, buf);
+
+ /*
+ * Just to have something to test to begin with, we'll
+ * implement "tr a-zA-Z A-Za-z" and return the result.
+ */
+ for (i = 0; i < count; i++) {
+ char tmp = buf[i];
+ if (tmp >= 'a' && tmp <= 'z')
+ buf[i] = tmp - ('a' - 'A');
+ else if (tmp >= 'A' && tmp <= 'Z')
+ buf[i] = tmp + ('a' - 'A');
+ }
+
+ count = QUEUE_ADD_UNITS(&outgoing_q, buf, count);
+ CPRINTS("task %d puts: count=%d buf=((%s))", me, buf);
+ }
+}
diff --git a/common/build.mk b/common/build.mk
index ee6c3975e2..cdde1f7fd3 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -88,6 +88,7 @@ common-$(CONFIG_USB_PD_TCPM_STUB)+=usb_pd_tcpm_stub.o
common-$(CONFIG_USB_PD_TCPM_TCPCI)+=usb_pd_tcpm.o
common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o
common-$(CONFIG_WIRELESS)+=wireless.o
+common-$(HAS_TASK_BLOB)+=blob.o
common-$(HAS_TASK_CHIPSET)+=chipset.o
common-$(HAS_TASK_CONSOLE)+=console.o console_output.o uart_buffering.o
common-$(HAS_TASK_CONSOLE)+=memory_commands.o
diff --git a/include/blob.h b/include/blob.h
new file mode 100644
index 0000000000..844e8d638a
--- /dev/null
+++ b/include/blob.h
@@ -0,0 +1,26 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Generic API for handling opaque blobs of data. */
+
+#ifndef __CROS_EC_BLOB_H
+#define __CROS_EC_BLOB_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Call this to send data to the blob-handler */
+size_t put_bytes_to_blob(uint8_t *buffer, size_t count);
+
+/* Call this to get data back fom the blob-handler */
+size_t get_bytes_from_blob(uint8_t *buffer, size_t count);
+
+/* Implement this to be notified when the blob-handler can take more data */
+void blob_is_ready_for_more_bytes(void);
+
+/* Implement this to be notified when the blob-handler has data to give us */
+void blob_is_ready_to_emit_bytes(void);
+
+#endif /* __CROS_EC_BLOB_H */