diff options
author | Anton Staaf <robotboy@chromium.org> | 2014-09-02 10:36:00 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-09-23 00:05:07 +0000 |
commit | ece4481cd10202d1bd5f819b9178697a81144e63 (patch) | |
tree | b45799c086cd0759920d2f11c4770f4aee443427 /chip/stm32/usb-stream.c | |
parent | be0bd9b83538427cc350ad38d64b821dfcad82a0 (diff) | |
download | chrome-ec-ece4481cd10202d1bd5f819b9178697a81144e63.tar.gz |
stm32-USB: Initial USB bulk endpoint stream driver
This stream driver works like the USART stream driver
but connects to two bulk USB endpoints.
Signed-off-by: Anton Staaf <robotboy@chromium.org>
BRANCH=None
BUG=None
TEST=make buildall -j
Change-Id: I9cbd2e54a811d3e32c68a820f7ab5de693c29569
Reviewed-on: https://chromium-review.googlesource.com/216002
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Tested-by: Anton Staaf <robotboy@chromium.org>
Commit-Queue: Anton Staaf <robotboy@chromium.org>
Diffstat (limited to 'chip/stm32/usb-stream.c')
-rw-r--r-- | chip/stm32/usb-stream.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/chip/stm32/usb-stream.c b/chip/stm32/usb-stream.c new file mode 100644 index 0000000000..644ac88dbb --- /dev/null +++ b/chip/stm32/usb-stream.c @@ -0,0 +1,216 @@ +/* Copyright (c) 2014 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 "atomic.h" +#include "common.h" +#include "config.h" +#include "link_defs.h" +#include "printf.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "usb.h" +#include "usb-stream.h" + +/* + * The USB packet RAM is attached to the processor via the AHB2APB bridge. This + * bridge performs manipulations of read and write accesses as per the note in + * section 2.1 of RM0091. The upshot is that it is OK to read from the packet + * RAM using 8-bit or 16-bit accesses, but not 32-bit, and it is only really OK + * to write to the packet RAM using 16-bit accesses. Thus custom memcpy like + * routines need to be employed. Furthermore, reading from and writing to the + * RX and TX queues uses memcpy, which will try to do 32-bit accesses if it can. + * so we must read and write single bytes at a time and construct 16-bit + * accesses to the packet RAM. + * + * This could be improved by adding a set of operations on the queue to get + * a pointer and size of the largest contiguous free/full region, then that + * region could be operated on and a commit operation could be performed on + * the queue. + */ +static size_t rx_read(struct usb_stream_config const *config) +{ + size_t count = btable_ep[config->endpoint].rx_count & 0x3ff; + + if (count < queue_space(&config->rx)) { + usb_uint *buffer = config->rx_ram; + size_t i; + + for (i = 0; i < count / 2; i++) { + usb_uint word = *buffer++; + uint8_t lsb = (word >> 0) & 0xff; + uint8_t msb = (word >> 8) & 0xff; + + queue_add_unit(&config->rx, &lsb); + queue_add_unit(&config->rx, &msb); + } + + if (count & 1) { + usb_uint word = *buffer++; + uint8_t lsb = (word >> 0) & 0xff; + + queue_add_unit(&config->rx, &lsb); + } + + return count; + } + + return 0; +} + +static size_t tx_write(struct usb_stream_config const *config) +{ + usb_uint *buffer = config->tx_ram; + size_t count = MIN(USB_MAX_PACKET_SIZE, queue_count(&config->tx)); + size_t i; + + for (i = 0; i < count / 2; i++) { + uint8_t lsb; + uint8_t msb; + + queue_remove_unit(&config->tx, &lsb); + queue_remove_unit(&config->tx, &msb); + + *buffer++ = (msb << 8) | lsb; + } + + if (count & 1) { + uint8_t lsb; + + queue_remove_unit(&config->tx, &lsb); + + *buffer++ = lsb; + } + + btable_ep[config->endpoint].tx_count = count; + + return count; +} + +static size_t usb_read(struct in_stream const *stream, + uint8_t *buffer, + size_t count) +{ + struct usb_stream_config const *config = + DOWNCAST(stream, struct usb_stream_config, in); + + size_t read = QUEUE_REMOVE_UNITS(&config->rx, buffer, count); + + if (config->state->rx_waiting && rx_read(config)) { + config->state->rx_waiting = 0; + + STM32_TOGGLE_EP(config->endpoint, EP_RX_MASK, EP_RX_VALID, 0); + + /* + * Make sure that the reader of this queue knows that there is + * more to read. + */ + in_stream_ready(&config->in); + + /* + * If there is still space left in the callers buffer fill it + * up with the additional bytes just added to the queue. + */ + if (count - read > 0) + read += QUEUE_REMOVE_UNITS(&config->rx, + buffer + read, + count - read); + } + + return read; +} + +static int tx_valid(struct usb_stream_config const *config) +{ + return (STM32_USB_EP(config->endpoint) & EP_TX_MASK) == EP_TX_VALID; +} + +static size_t usb_write(struct out_stream const *stream, + uint8_t const *buffer, + size_t count) +{ + struct usb_stream_config const *config = + DOWNCAST(stream, struct usb_stream_config, out); + + size_t wrote = QUEUE_ADD_UNITS(&config->tx, buffer, count); + + /* + * If we are not currently in a valid transmission state and we had + * something for the TX buffer, then mark the TX endpoint as valid. + */ + if (!tx_valid(config) && tx_write(config)) + STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_VALID, 0); + + return wrote; +} + +static void usb_flush(struct out_stream const *stream) +{ + struct usb_stream_config const *config = + DOWNCAST(stream, struct usb_stream_config, out); + + while (tx_valid(config) || queue_count(&config->tx)) + ; +} + +struct in_stream_ops const usb_stream_in_stream_ops = { + .read = usb_read, +}; + +struct out_stream_ops const usb_stream_out_stream_ops = { + .write = usb_write, + .flush = usb_flush, +}; + +void usb_stream_tx(struct usb_stream_config const *config) +{ + if (tx_write(config)) + STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_VALID, 0); + else + STM32_TOGGLE_EP(config->endpoint, 0, 0, 0); + + out_stream_ready(&config->out); +} + +void usb_stream_rx(struct usb_stream_config const *config) +{ + if (rx_read(config)) { + /* + * RX packet consumed, mark the packet as VALID. + */ + STM32_TOGGLE_EP(config->endpoint, EP_RX_MASK, EP_RX_VALID, 0); + } else { + /* + * There is not enough space in the RX queue to receive this + * packet. Leave the RX endpoint in a NAK state, clear the + * interrupt, and indicate to the usb_read function that when + * there is enough space in the queue to hold it there is an + * RX packet waiting. + */ + config->state->rx_waiting = 1; + STM32_TOGGLE_EP(config->endpoint, 0, 0, 0); + } + + in_stream_ready(&config->in); +} + +void usb_stream_reset(struct usb_stream_config const *config) +{ + int i = config->endpoint; + + btable_ep[i].tx_addr = usb_sram_addr(config->tx_ram); + btable_ep[i].tx_count = 0; + + btable_ep[i].rx_addr = usb_sram_addr(config->rx_ram); + btable_ep[i].rx_count = 0x8000 | ((USB_MAX_PACKET_SIZE / 32 - 1) << 10); + + config->state->rx_waiting = 0; + + STM32_USB_EP(i) = ((i << 0) | /* Endpoint Addr*/ + (2 << 4) | /* TX NAK */ + (0 << 9) | /* Bulk EP */ + (3 << 12)); /* RX VALID */ +} |