summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
Diffstat (limited to 'chip')
-rw-r--r--chip/g/usb-stream.c176
-rw-r--r--chip/g/usb-stream.h22
2 files changed, 143 insertions, 55 deletions
diff --git a/chip/g/usb-stream.c b/chip/g/usb-stream.c
index 7ab28c37a6..b929cf7d4e 100644
--- a/chip/g/usb-stream.c
+++ b/chip/g/usb-stream.c
@@ -4,6 +4,8 @@
*/
#include "registers.h"
+#include "task.h"
+#include "timer.h"
#include "usb-stream.h"
/* Let the USB HW IN-to-host FIFO transmit some bytes */
@@ -128,79 +130,129 @@ static inline int tx_fifo_is_ready(struct usb_stream_config const *config)
}
/* Try to send some bytes to the host */
-void tx_stream_handler(struct usb_stream_config const *config)
+static void tx_stream_handler(struct usb_stream_config const *config)
{
+ int len[MAX_IN_DESC];
size_t count;
+ size_t head;
struct queue const *tx_q = config->consumer.queue;
- /* handle the completion of the previous transfer, if there was any. */
- count = *(config->tx_handled);
- if (count > 0) {
- /*
- * Since tx completed, let's advance queue head by the value of
- * 'count'.
- */
- queue_advance_head(tx_q, count);
- *(config->tx_handled) = 0;
- }
-
/* setup to send bytes to the host */
count = MIN(queue_count(tx_q), config->tx_size);
- if (count > 0) {
- size_t head = tx_q->state->head & tx_q->buffer_units_mask;
- int len[MAX_IN_DESC];
+ if (!count) {
+ /* Report USB TX transfer is not active any more. */
+ *config->tx_in_progress = 0;
+ return;
+ }
+ head = tx_q->state->head & tx_q->buffer_units_mask;
+
+ if (config->is_uart_console) {
+ if (!*config->kicker_running &&
+ (count < config->tx_size)) {
/*
- * If queue units are not physically continuous, then
- * setup transfer in two USB endpoint descriptors.
+ * Shipping less than full chunk (64 bytes) over usb is
+ * wasteful in case there is a lot of data coming from the
+ * stream source. Let's try collecting more bytes in case more
+ * is coming.
*
- * buffer buffer + buffer_units
- * | tail head |
- * | | | |
- * V V V V
- * tx_q |xxxxxx___________________xxxxx|
- * <----> <--->
- * len[1] len[0]
+ * It takes 5.6 ms to transfer 64 bytes over UART at 115200
+ * bps with one start and one stop bit. Let's set the deferred
+ * function delay to 3 ms, it will take longer in reality as
+ * background tasks will get a chance to run.
*/
- len[0] = MIN(count, tx_q->buffer_units - head);
- len[1] = count - len[0];
+ hook_call_deferred(config->tx_kicker, 3 * MSEC);
+ *config->kicker_running = 1;
+ return;
+ }
+
+ if (*config->kicker_running) {
+ *config->kicker_running = 0;
+ hook_call_deferred(config->tx_kicker, -1);
+ }
+ }
- /*
- * Store the amount to advance head when the transfer is done.
- * Note: 'tx byte' field in the endpoint descriptor decreases to
- * zero as data get transferred. Need to store the
- * transfer size, which is 'count', aside into *config->
- * tx_handlered.
- */
- *(config->tx_handled) = count;
+ /*
+ * If queue units are not physically continuous, then setup transfer
+ * in two USB endpoint descriptors.
+ *
+ * buffer buffer + buffer_units
+ * | tail head |
+ * | | | |
+ * V V V V
+ * tx_q |xxxxxx___________________xxxxx|
+ * <----> <--->
+ * len[1] len[0]
+ */
+ len[0] = MIN(count, tx_q->buffer_units - head);
+ len[1] = count - len[0];
- /*
- * Setup the first endpoint descriptor with start memory address
- * No need to setup for the second endpoint, because it is
- * always the start address of the queue, and already setup in
- * usb_stream_reset().
- */
- config->in_desc[0].addr = (void *)tx_q->buffer + head;
+ /*
+ * Store the amount to advance head when the transfer is done.
+ * Note: 'tx byte' field in the endpoint descriptor decreases to zero
+ * as data get transferred. Need to store the transfer size,
+ * which is 'count', aside into *config-> tx_handlered.
+ */
+ *(config->tx_handled) = count;
- /*
- * Enable USB transfer. usb_enable_tx() will setup the transfer
- * size in the first endpoint descriptor, and the second
- * descriptor as well if it is needed.
- */
- usb_enable_tx(config, len);
- } else {
- /* USB TX transfer is not active. */
- *config->tx_in_progress = 0;
- }
+ /*
+ * Setup the first endpoint descriptor with start memory address No
+ * need to setup for the second endpoint, because it is always the
+ * start address of the queue, and already setup in
+ * usb_stream_reset().
+ */
+ config->in_desc[0].addr = (void *)tx_q->buffer + head;
+
+ /*
+ * Enable USB transfer. usb_enable_tx() will setup the transfer size
+ * in the first endpoint descriptor, and the second descriptor as well
+ * if it is needed.
+ */
+ usb_enable_tx(config, len);
+}
+
+/*
+ * Deferred function which gets to run if a UART console does not supply
+ * enough data to fill a USB chunk (64 bytes).
+ */
+void tx_stream_kicker(struct usb_stream_config const *config)
+{
+ /*
+ * By design this function must run on a task context, i.e. interrupts
+ * are enabled.
+ *
+ * The not so elegant but simplest way to avoid concurrency issues
+ * with the kicker function execution interrupted by a USB or UART
+ * event is to invoke tx_stream_handler() with disabled interrupts.
+ */
+ interrupt_disable();
+
+ if (*config->kicker_running)
+ tx_stream_handler(config);
+
+ interrupt_enable();
}
/* Tx/IN interrupt handler */
void usb_stream_tx(struct usb_stream_config const *config)
{
+ size_t *tx_handled;
+
/* Clear the Tx/IN interrupts */
GR_USB_DIEPINT(config->endpoint) = 0xffffffff;
- /* Call the Tx FIFO handler */
+ /* Address of the size of the most recent chunk. */
+ tx_handled = config->tx_handled;
+
+ /*
+ * Transfer completed, advance queue head by the number of bytes
+ * transmitted in the most recent chunk.
+ */
+ queue_advance_head(config->consumer.queue, *tx_handled);
+
+ *tx_handled = 0;
+
+ /* See if there is more to transmit. */
tx_stream_handler(config);
}
@@ -261,8 +313,24 @@ static void usb_written(struct consumer const *consumer, size_t count)
DOWNCAST(consumer, struct usb_stream_config, consumer);
/* USB TX transfer is active. No need to activate it. */
- if (*config->tx_in_progress)
- return;
+ if (*config->tx_in_progress) {
+ struct queue const *tx_q;
+
+ if (!*config->kicker_running)
+ return;
+
+ /*
+ * If kicker is running for too long and we already have a
+ * certain amount of data accumulated in the buffer, let's
+ * proceed even before the kicker had a chance to kick in.
+ */
+ tx_q = config->consumer.queue;
+ if (queue_count(tx_q) < tx_q->buffer_units_mask)
+ return;
+
+ hook_call_deferred(config->tx_kicker, -1);
+ *config->kicker_running = 0;
+ }
/*
* if USB Endpoint has not been initialized nor in ready status,
diff --git a/chip/g/usb-stream.h b/chip/g/usb-stream.h
index a0c580acea..6e01c3341c 100644
--- a/chip/g/usb-stream.h
+++ b/chip/g/usb-stream.h
@@ -27,15 +27,18 @@ struct usb_stream_config {
/*
* Endpoint index, and pointers to the USB packet RAM buffers.
*/
- int endpoint;
+ uint16_t endpoint;
+ uint16_t is_uart_console;
/* USB TX transfer is in progress */
uint8_t *tx_in_progress;
+ uint8_t *kicker_running;
/*
* Deferred function to call to handle USB and Queue request.
*/
const struct deferred_data *deferred_rx;
+ const struct deferred_data *tx_kicker;
int tx_size;
int rx_size;
@@ -62,6 +65,13 @@ struct usb_stream_config {
extern struct consumer_ops const usb_stream_consumer_ops;
extern struct producer_ops const usb_stream_producer_ops;
+/* Need to define these so that other than Cr50 boards compile cleanly. */
+#ifndef USB_EP_EC
+#define USB_EP_EC -1
+#endif
+#ifndef USB_EP_AP
+#define USB_EP_AP -1
+#endif
/*
* Convenience macro for defining USB streams and their associated state and
@@ -115,16 +125,23 @@ extern struct producer_ops const usb_stream_producer_ops;
static struct g_usb_desc CONCAT2(NAME, _in_desc_)[MAX_IN_DESC]; \
static uint8_t CONCAT2(NAME, _buf_rx_)[RX_SIZE]; \
static uint8_t CONCAT2(NAME, _tx_in_progress_); \
+ static uint8_t CONCAT2(NAME, _kicker_running_); \
static void CONCAT2(NAME, _deferred_rx_)(void); \
+ static void CONCAT2(NAME, _tx_kicker_)(void); \
DECLARE_DEFERRED(CONCAT2(NAME, _deferred_rx_)); \
+ DECLARE_DEFERRED(CONCAT2(NAME, _tx_kicker_)); \
static int CONCAT2(NAME, _rx_handled); \
static size_t CONCAT2(NAME, _tx_handled); \
struct usb_stream_config const NAME = { \
.endpoint = ENDPOINT, \
+ .is_uart_console = ((ENDPOINT == USB_EP_EC) || \
+ (ENDPOINT == USB_EP_AP)), \
.tx_in_progress = &CONCAT2(NAME, _tx_in_progress_), \
+ .kicker_running = &CONCAT2(NAME, _kicker_running_), \
.in_desc = &CONCAT2(NAME, _in_desc_)[0], \
.out_desc = &CONCAT2(NAME, _out_desc_), \
.deferred_rx = &CONCAT2(NAME, _deferred_rx__data), \
+ .tx_kicker = &CONCAT2(NAME, _tx_kicker__data), \
.tx_size = TX_SIZE, \
.rx_size = RX_SIZE, \
.rx_ram = CONCAT2(NAME, _buf_rx_), \
@@ -171,6 +188,8 @@ extern struct producer_ops const usb_stream_producer_ops;
}; \
static void CONCAT2(NAME, _deferred_rx_)(void) \
{ rx_stream_handler(&NAME); } \
+ static void CONCAT2(NAME, _tx_kicker_)(void) \
+ { tx_stream_kicker(&NAME); } \
static void CONCAT2(NAME, _ep_tx)(void) \
{ \
usb_stream_tx(&NAME); \
@@ -213,6 +232,7 @@ extern struct producer_ops const usb_stream_producer_ops;
* Handle USB and Queue request in a deferred callback.
*/
void rx_stream_handler(struct usb_stream_config const *config);
+void tx_stream_kicker(struct usb_stream_config const *config);
/*
* These functions are used by the trampoline functions defined above to