summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2016-03-14 09:44:51 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-04-07 13:00:41 -0700
commita5e74e6b45e2484fba2d7dc81ac054497d2576f3 (patch)
tree3d0fb90ee0b93f66c5e1ec5900a005c6f5961a95
parent8a0d5ba17e60d83a7479287864b2b6735e91f7e6 (diff)
downloadchrome-ec-a5e74e6b45e2484fba2d7dc81ac054497d2576f3.tar.gz
cr50: forward console through USB
This change adds support for forwarding the EC console through USB. BUG=chrome-os-partner:49960 BRANCH=none TEST=load the google-serial udev rules in extra/usb_serial/, build the raiden.ko module, and then check that the console can be accessed from /dev/google/Cr50/serial/Shell Change-Id: I35e0bb39fdc8f9485a14c03eb3a4d2f024884e17 Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/334132 Tested-by: Bill Richardson <wfrichar@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--board/cr50/board.h6
-rw-r--r--chip/g/usb_console.c275
2 files changed, 188 insertions, 93 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 8bb7416d02..07cc8cc49e 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -27,7 +27,7 @@
/* USB configuration */
#define CONFIG_USB
#define CONFIG_USB_HID
-#define CONFIG_USB_BLOB
+#define CONFIG_USB_CONSOLE
#define CONFIG_STREAM_USART
#define CONFIG_STREAM_USB
@@ -80,7 +80,7 @@ enum usb_strings {
#endif /* !__ASSEMBLER__ */
/* USB interface indexes (use define rather than enum to expand them) */
-#define USB_IFACE_BLOB 0
+#define USB_IFACE_CONSOLE 0
#define USB_IFACE_HID 1
#define USB_IFACE_AP 2
#define USB_IFACE_EC 3
@@ -88,7 +88,7 @@ enum usb_strings {
/* USB endpoint indexes (use define rather than enum to expand them) */
#define USB_EP_CONTROL 0
-#define USB_EP_BLOB 1
+#define USB_EP_CONSOLE 1
#define USB_EP_HID 2
#define USB_EP_AP 3
#define USB_EP_EC 4
diff --git a/chip/g/usb_console.c b/chip/g/usb_console.c
index 1414ddbe1e..8195b5b433 100644
--- a/chip/g/usb_console.c
+++ b/chip/g/usb_console.c
@@ -8,6 +8,7 @@
#include "console.h"
#include "link_defs.h"
#include "printf.h"
+#include "queue.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
@@ -16,14 +17,7 @@
/* Console output macro */
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
-
#define USB_CONSOLE_TIMEOUT_US (30 * MSEC)
-#define USB_CONSOLE_RX_BUF_SIZE 16
-#define RX_BUF_NEXT(i) (((i) + 1) & (USB_CONSOLE_RX_BUF_SIZE - 1))
-
-static volatile char rx_buf[USB_CONSOLE_RX_BUF_SIZE];
-static volatile int rx_buf_head;
-static volatile int rx_buf_tail;
static int last_tx_ok = 1;
@@ -68,35 +62,144 @@ 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;
-static void con_ep_tx(void)
+static struct queue const tx_q = QUEUE_NULL(256, uint8_t);
+static struct queue const rx_q = QUEUE_NULL(USB_MAX_PACKET_SIZE, uint8_t);
+
+
+/* Let the USB HW IN-to-host FIFO transmit some bytes */
+static void usb_enable_tx(int len)
{
- /* clear IT */
- GR_USB_DIEPINT(USB_EP_CONSOLE) = 0xffffffff;
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(len);
+ GR_USB_DIEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
}
-static void con_ep_rx(void)
+/* Let the USB HW OUT-from-host FIFO receive some bytes */
+static void usb_enable_rx(int len)
{
- int i;
- int rx_size = is_readonly ? 0 : USB_MAX_PACKET_SIZE
- - (ep_out_desc.flags & DOEPDMA_RXBYTES_MASK);
-
- for (i = 0; i < rx_size; i++) {
- int rx_buf_next = RX_BUF_NEXT(rx_buf_head);
- if (rx_buf_next != rx_buf_tail) {
- rx_buf[rx_buf_head] = ep_buf_rx[i];
- rx_buf_head = rx_buf_next;
- }
- }
-
- ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ ep_out_desc.flags = DOEPDMA_RXBYTES(len) |
DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
GR_USB_DOEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
- /* clear IT */
+}
+
+/* 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 = QUEUE_ADD_UNITS(&rx_q, ep_buf_rx + rx_handled,
+ rx_left);
+ rx_handled += added;
+ rx_left -= added;
+ }
+
+ if (rx_handled)
+ task_wake(TASK_ID_CONSOLE);
+ /*
+ * 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);
+
+/* 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_CONSOLE) = 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 = QUEUE_REMOVE_UNITS(&tx_q, ep_buf_tx, USB_MAX_PACKET_SIZE);
+ if (count)
+ usb_enable_tx(count);
+}
+DECLARE_DEFERRED(tx_fifo_handler);
+
+static void handle_output(void)
+{
+ /* Wake up the Tx FIFO handler */
+ hook_call_deferred(tx_fifo_handler, 0);
+}
- /* wake-up the console task */
- if (!is_readonly)
- console_has_input();
+/* 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_CONSOLE) = 0xffffffff;
}
static void ep_reset(void)
@@ -114,50 +217,27 @@ static void ep_reset(void)
GR_USB_DIEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
DXEPCTL_EPTYPE_BULK |
DXEPCTL_TXFNUM(USB_EP_CONSOLE);
- GR_USB_DAINTMSK |= (1<<USB_EP_CONSOLE) | (1 << (USB_EP_CONSOLE+16));
+ GR_USB_DAINTMSK |= DAINT_INEP(USB_EP_CONSOLE) |
+ DAINT_OUTEP(USB_EP_CONSOLE);
is_reset = 1;
-}
-
-USB_DECLARE_EP(USB_EP_CONSOLE, con_ep_tx, con_ep_rx, ep_reset);
-
-static int __tx_char(void *context, int c)
-{
- int *tx_idx = context;
-
- /* Do newline to CRLF translation */
- if (c == '\n' && __tx_char(context, '\r'))
- return 1;
-
- if (*tx_idx > 63)
- return 1;
-
- ep_buf_tx[*tx_idx] = c;
- (*tx_idx)++;
- return 0;
+ /* Flush any queued data */
+ hook_call_deferred(tx_fifo_handler, 0);
+ hook_call_deferred(rx_fifo_handler, 0);
}
-static void usb_enable_tx(int len)
-{
- if (!is_enabled)
- return;
-
- ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
- DIEPDMA_TXBYTES(len);
- GR_USB_DIEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
-}
-static inline int usb_console_tx_valid(void)
-{
- return (ep_in_desc.flags & DIEPDMA_BS_MASK) == DIEPDMA_BS_DMA_DONE;
-}
+USB_DECLARE_EP(USB_EP_CONSOLE, con_ep_tx, con_ep_rx, ep_reset);
static int usb_wait_console(void)
{
timestamp_t deadline = get_time();
int wait_time_us = 1;
+ if (!is_enabled || !tx_fifo_is_ready())
+ return EC_SUCCESS;
+
deadline.val += USB_CONSOLE_TIMEOUT_US;
/*
@@ -168,7 +248,7 @@ static int usb_wait_console(void)
* we should wait so that we don't clobber the buffer.
*/
if (last_tx_ok) {
- while (usb_console_tx_valid() || !is_reset) {
+ while (queue_space(&tx_q) < USB_MAX_PACKET_SIZE || !is_reset) {
if (timestamp_expired(deadline, NULL) ||
in_interrupt_context()) {
last_tx_ok = 0;
@@ -183,10 +263,21 @@ static int usb_wait_console(void)
return EC_SUCCESS;
} else {
- last_tx_ok = !usb_console_tx_valid();
+ last_tx_ok = queue_space(&tx_q);
return EC_SUCCESS;
}
}
+static int __tx_char(void *context, int c)
+{
+ struct queue *state =
+ (struct queue *) context;
+
+ if (c == '\n' && __tx_char(state, '\r'))
+ return 1;
+
+ QUEUE_ADD_UNITS(state, &c, 1);
+ return 0;
+}
/*
* Public USB console implementation below.
@@ -195,64 +286,68 @@ int usb_getc(void)
{
int c;
- if (rx_buf_tail == rx_buf_head)
- return -1;
-
if (!is_enabled)
return -1;
- c = rx_buf[rx_buf_tail];
- rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
- return c;
+ if (QUEUE_REMOVE_UNITS(&rx_q, &c, 1))
+ return c;
+ return -1;
}
int usb_putc(int c)
{
- int ret;
- int tx_idx = 0;
+ int ret = usb_wait_console();
- ret = usb_wait_console();
if (ret)
return ret;
- ret = __tx_char(&tx_idx, c);
- usb_enable_tx(tx_idx);
-
- return ret;
+ ret = QUEUE_ADD_UNITS(&tx_q, &c, 1);
+ if (ret)
+ handle_output();
+ return ret ? EC_SUCCESS : EC_ERROR_OVERFLOW;
}
int usb_puts(const char *outstr)
{
int ret;
- int tx_idx = 0;
+ struct queue state;
- ret = usb_wait_console();
+ if (is_readonly)
+ return EC_SUCCESS;
+
+ ret = usb_wait_console();
if (ret)
return ret;
- /* Put all characters in the output buffer */
- while (*outstr) {
- if (__tx_char(&tx_idx, *outstr++) != 0)
+ state = tx_q;
+ while (*outstr)
+ if (__tx_char(&state, *outstr++))
break;
- }
- usb_enable_tx(tx_idx);
+ if (queue_count(&state))
+ handle_output();
- /* Successful if we consumed all output */
return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
}
int usb_vprintf(const char *format, va_list args)
{
int ret;
- int tx_idx = 0;
+ struct queue state;
+
+ if (is_readonly)
+ return EC_SUCCESS;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ state = tx_q;
+ ret = vfnprintf(__tx_char, &state, format, args);
+
+ if (queue_count(&state))
+ handle_output();
- ret = vfnprintf(__tx_char, &tx_idx, format, args);
- if (!ret && is_reset) {
- ret = usb_wait_console();
- if (!ret)
- usb_enable_tx(tx_idx);
- }
return ret;
}