diff options
Diffstat (limited to 'chip/g/usb-stream.c')
-rw-r--r-- | chip/g/usb-stream.c | 354 |
1 files changed, 0 insertions, 354 deletions
diff --git a/chip/g/usb-stream.c b/chip/g/usb-stream.c deleted file mode 100644 index ae3b42e5c2..0000000000 --- a/chip/g/usb-stream.c +++ /dev/null @@ -1,354 +0,0 @@ -/* 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 "registers.h" -#include "task.h" -#include "timer.h" -#include "usb-stream.h" - -/* Let the USB HW IN-to-host FIFO transmit some bytes */ -static void usb_enable_tx(struct usb_stream_config const *config, - const int len[]) -{ - const uint32_t flags = DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC | DIEPDMA_LAST; - int idx = 0; - - if (len[1]) { - config->in_desc[idx].flags = DIEPDMA_TXBYTES(len[idx]) | - DIEPDMA_BS_HOST_RDY; - idx++; - } - config->in_desc[idx].flags = DIEPDMA_TXBYTES(len[idx]) | flags; - - GR_USB_DIEPCTL(config->endpoint) |= DXEPCTL_CNAK | DXEPCTL_EPENA; -} - -/* Let the USB HW OUT-from-host FIFO receive some bytes */ -static void usb_enable_rx(struct usb_stream_config const *config, int len) -{ - config->out_desc->flags = DOEPDMA_RXBYTES(len) | DOEPDMA_LAST | - DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; - GR_USB_DOEPCTL(config->endpoint) |= DXEPCTL_CNAK | DXEPCTL_EPENA; -} - -/* True if the HW Rx/OUT FIFO has bytes for us. */ -static inline int rx_fifo_is_ready(struct usb_stream_config const *config) -{ - return (config->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). - */ -void rx_stream_handler(struct usb_stream_config const *config) -{ - /* - * The HW FIFO buffer (rx_ram) 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 rx_ram 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. - */ - int rx_handled; - - /* If the HW FIFO isn't ready, then we're waiting for more bytes */ - if (!rx_fifo_is_ready(config)) - return; - - rx_handled = *(config->rx_handled); - /* - * 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 = config->rx_size - - (config->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( - config->producer.queue, config->rx_ram + 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(config, config->rx_size); - } else { - hook_call_deferred(config->deferred_rx, 0); - } - - *(config->rx_handled) = rx_handled; -} - -/* Rx/OUT interrupt handler */ -void usb_stream_rx(struct usb_stream_config const *config) -{ - /* Wake up the Rx FIFO handler */ - hook_call_deferred(config->deferred_rx, 0); - - GR_USB_DOEPINT(config->endpoint) = 0xffffffff; -} - -/* True if the Tx/IN FIFO can take some bytes from us. */ -int tx_fifo_is_ready(struct usb_stream_config const *config) -{ - uint32_t status; - struct g_usb_desc *in_desc = config->in_desc; - - if (!(in_desc->flags & DIEPDMA_LAST)) - ++in_desc; - - status = 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_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; - - /* setup to send bytes to the host */ - count = MIN(queue_count(tx_q), config->tx_size); - 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)) { - /* - * 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. - * - * 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. - */ - 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); - } - } - - /* - * 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]; - - /* - * 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; - - /* - * 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; - - /* 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); -} - -void usb_stream_reset(struct usb_stream_config const *config) -{ - /* - * Mark USB TX transfer is in progress, because it shall be so at - * the end of this function to flush any queued data. - */ - *config->tx_in_progress = 1; - - config->out_desc->flags = DOEPDMA_RXBYTES(config->rx_size) | - DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | - DOEPDMA_IOC; - config->out_desc->addr = config->rx_ram; - GR_USB_DOEPDMA(config->endpoint) = (uint32_t)config->out_desc; - config->in_desc[0].flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | - DIEPDMA_IOC; - config->in_desc[1].flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | - DIEPDMA_IOC; - /* - * No need to set config->in_desc[0].addr here, because it will be set - * in tx_stream_handler() with the queue head pointer at that time. - * Meanwhile, config->in_desc[1].addr is set here once, and it won't be - * changed at all. - */ - config->in_desc[1].addr = (void *)config->consumer.queue->buffer; - GR_USB_DIEPDMA(config->endpoint) = (uint32_t)config->in_desc; - GR_USB_DOEPCTL(config->endpoint) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | - DXEPCTL_EPTYPE_BULK | - DXEPCTL_CNAK | DXEPCTL_EPENA; - GR_USB_DIEPCTL(config->endpoint) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | - DXEPCTL_EPTYPE_BULK | - DXEPCTL_TXFNUM(config->endpoint); - GR_USB_DAINTMSK |= DAINT_INEP(config->endpoint) | - DAINT_OUTEP(config->endpoint); - - *config->is_reset = 1; - - /* Flush any queued data */ - tx_stream_handler(config); - hook_call_deferred(config->deferred_rx, 0); -} - -static void usb_read(struct producer const *producer, size_t count) -{ - struct usb_stream_config const *config = - DOWNCAST(producer, struct usb_stream_config, producer); - - hook_call_deferred(config->deferred_rx, 0); -} - -/* - * NOTE: usb_written() should be called by IRQ handlers, so that - * it can be non-preemptive. - */ -static void usb_written(struct consumer const *consumer, size_t count) -{ - struct usb_stream_config const *config = - DOWNCAST(consumer, struct usb_stream_config, consumer); - - /* USB TX transfer is active. No need to activate it. */ - 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, - * then return. - */ - if (!tx_fifo_is_ready(config)) - return; - - *config->tx_in_progress = 1; - tx_stream_handler(config); -} - -struct producer_ops const usb_stream_producer_ops = { - .read = usb_read, -}; - -struct consumer_ops const usb_stream_consumer_ops = { - .written = usb_written, -}; |