summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Staaf <robotboy@chromium.org>2014-09-02 10:36:00 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-09-23 00:05:07 +0000
commitece4481cd10202d1bd5f819b9178697a81144e63 (patch)
treeb45799c086cd0759920d2f11c4770f4aee443427
parentbe0bd9b83538427cc350ad38d64b821dfcad82a0 (diff)
downloadchrome-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>
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/usb-stream.c216
-rw-r--r--chip/stm32/usb-stream.h201
-rw-r--r--include/config.h4
4 files changed, 422 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index 8c6b0cd2e6..5a360bd8a2 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -31,6 +31,7 @@ chip-$(CONFIG_COMMON_GPIO)+=gpio.o gpio-$(CHIP_FAMILY).o
chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o
chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o i2c.o
chip-$(CONFIG_STREAM_USART)+=usart.o usart-$(CHIP_FAMILY).o
+chip-$(CONFIG_STREAM_USB)+=usb-stream.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_CONSOLE)+=uart.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
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 */
+}
diff --git a/chip/stm32/usb-stream.h b/chip/stm32/usb-stream.h
new file mode 100644
index 0000000000..ae6735a281
--- /dev/null
+++ b/chip/stm32/usb-stream.h
@@ -0,0 +1,201 @@
+/* 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.
+ */
+#ifndef CHIP_STM32_USB_STREAM_H
+#define CHIP_STM32_USB_STREAM_H
+
+/* STM32 USB STREAM driver for Chrome EC */
+
+#include "compile_time_macros.h"
+#include "in_stream.h"
+#include "out_stream.h"
+#include "queue.h"
+#include "usb.h"
+
+#include <stdint.h>
+
+/*
+ * Per-USB stream state stored in RAM. Zero initialization of this structure
+ * by the BSS initialization leaves it in a valid and correctly initialized
+ * state, so there is no need currently for a usb_stream_init style function.
+ *
+ * If this structure is changed to require non-zero initialization such a
+ * function should be added.
+ */
+struct usb_stream_state {
+ struct queue_state rx;
+ struct queue_state tx;
+
+ /*
+ * Flag indicating that there is a full RX buffer in the USB packet RAM
+ * that we were not able to move into the RX queue because there was
+ * not enough room when the packet was initially received. The
+ * in_stream read operation checks this flag so that once there is
+ * room in the queue it can copy the RX buffer into the queue and
+ * restart USB reception by marking the RX buffer as VALID.
+ */
+ int rx_waiting;
+};
+
+/*
+ * Compile time Per-USB stream configuration stored in flash. Instances of this
+ * structure are provided by the user of the USB stream. This structure binds
+ * together all information required to operate a USB stream.
+ */
+struct usb_stream_config {
+ /*
+ * Pointer to usb_stream_state structure. The state structure
+ * maintains per USB stream information (head and tail pointers for
+ * the queues for instance).
+ */
+ struct usb_stream_state volatile *state;
+
+ /*
+ * Endpoint index, and pointers to the USB packet RAM buffers.
+ */
+ int endpoint;
+
+ usb_uint *rx_ram;
+ usb_uint *tx_ram;
+
+ /*
+ * RX and TX queue config. The state for the queue is stored
+ * separately in the usb_stream_state structure.
+ */
+ struct queue rx;
+ struct queue tx;
+
+ /*
+ * In and Out streams, these contain pointers to the virtual function
+ * tables that implement in and out streams. They can be used by any
+ * code that wants to read or write to a stream interface.
+ */
+ struct in_stream in;
+ struct out_stream out;
+};
+
+/*
+ * These function tables are defined by the USB stream driver and are used to
+ * initialize the in and out streams in the usb_stream_config.
+ */
+extern struct in_stream_ops const usb_stream_in_stream_ops;
+extern struct out_stream_ops const usb_stream_out_stream_ops;
+
+/*
+ * Convenience macro for defining USB streams and their associated state and
+ * buffers.
+ *
+ * NAME is used to construct the names of the queue buffers, trampoline
+ * functions, usb_stream_state struct, and usb_stream_config struct, the
+ * latter is just called NAME.
+ *
+ * INTERFACE is the index of the USB interface to associate with this
+ * stream.
+ *
+ * ENDPOINT is the index of the USB bulk endpoint used for receiving and
+ * transmitting bytes.
+ *
+ * RX_SIZE and TX_SIZE are the size in bytes of the RX and TX queue buffers
+ * respectively.
+ *
+ * RX_READY and TX_READY are the callback functions for the in and out streams.
+ * These functions are called when there are bytes to read or space for bytes
+ * to write respectively.
+ */
+#define USB_STREAM_CONFIG(NAME, \
+ INTERFACE, \
+ ENDPOINT, \
+ RX_SIZE, \
+ TX_SIZE, \
+ RX_READY, \
+ TX_READY) \
+ BUILD_ASSERT(RX_SIZE >= USB_MAX_PACKET_SIZE); \
+ BUILD_ASSERT(TX_SIZE >= USB_MAX_PACKET_SIZE); \
+ static uint8_t CONCAT2(NAME, _rx_buffer)[RX_SIZE]; \
+ static uint8_t CONCAT2(NAME, _tx_buffer)[TX_SIZE]; \
+ static usb_uint CONCAT2(NAME, _ep_rx_buffer)[USB_MAX_PACKET_SIZE / 2] __usb_ram; \
+ static usb_uint CONCAT2(NAME, _ep_tx_buffer)[USB_MAX_PACKET_SIZE / 2] __usb_ram; \
+ static struct usb_stream_state CONCAT2(NAME, _state); \
+ struct usb_stream_config const NAME = { \
+ .state = &CONCAT2(NAME, _state), \
+ .endpoint = ENDPOINT, \
+ .rx_ram = CONCAT2(NAME, _ep_rx_buffer), \
+ .tx_ram = CONCAT2(NAME, _ep_tx_buffer), \
+ .rx = { \
+ .state = &CONCAT2(NAME, _state.rx), \
+ .buffer_units = RX_SIZE, \
+ .unit_bytes = 1, \
+ .buffer = CONCAT2(NAME, _rx_buffer), \
+ }, \
+ .tx = { \
+ .state = &CONCAT2(NAME, _state.tx), \
+ .buffer_units = TX_SIZE, \
+ .unit_bytes = 1, \
+ .buffer = CONCAT2(NAME, _tx_buffer), \
+ }, \
+ .in = { \
+ .ready = RX_READY, \
+ .ops = &usb_stream_in_stream_ops, \
+ }, \
+ .out = { \
+ .ready = TX_READY, \
+ .ops = &usb_stream_out_stream_ops, \
+ }, \
+ }; \
+ const struct usb_interface_descriptor \
+ USB_IFACE_DESC(INTERFACE) = { \
+ .bLength = USB_DT_INTERFACE_SIZE, \
+ .bDescriptorType = USB_DT_INTERFACE, \
+ .bInterfaceNumber = INTERFACE, \
+ .bAlternateSetting = 0, \
+ .bNumEndpoints = 2, \
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+ .bInterfaceSubClass = 0, \
+ .bInterfaceProtocol = 0, \
+ .iInterface = 0, \
+ }; \
+ const struct usb_endpoint_descriptor \
+ USB_EP_DESC(INTERFACE, 0) = { \
+ .bLength = USB_DT_ENDPOINT_SIZE, \
+ .bDescriptorType = USB_DT_ENDPOINT, \
+ .bEndpointAddress = 0x80 | ENDPOINT, \
+ .bmAttributes = 0x02 /* Bulk IN */, \
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE, \
+ .bInterval = 10, \
+ }; \
+ const struct usb_endpoint_descriptor \
+ USB_EP_DESC(INTERFACE, 1) = { \
+ .bLength = USB_DT_ENDPOINT_SIZE, \
+ .bDescriptorType = USB_DT_ENDPOINT, \
+ .bEndpointAddress = ENDPOINT, \
+ .bmAttributes = 0x02 /* Bulk OUT */, \
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE, \
+ .bInterval = 0, \
+ }; \
+ static void CONCAT2(NAME, _ep_tx)(void) \
+ { \
+ usb_stream_tx(&NAME); \
+ } \
+ static void CONCAT2(NAME, _ep_rx)(void) \
+ { \
+ usb_stream_rx(&NAME); \
+ } \
+ static void CONCAT2(NAME, _ep_reset)(void) \
+ { \
+ usb_stream_reset(&NAME); \
+ } \
+ USB_DECLARE_EP(ENDPOINT, \
+ CONCAT2(NAME, _ep_tx), \
+ CONCAT2(NAME, _ep_rx), \
+ CONCAT2(NAME, _ep_reset));
+
+/*
+ * These functions are used by the trampoline functions defined above to
+ * connect USB endpoint events with the generic USB stream driver.
+ */
+void usb_stream_tx(struct usb_stream_config const *config);
+void usb_stream_rx(struct usb_stream_config const *config);
+void usb_stream_reset(struct usb_stream_config const *config);
+
+#endif /* CHIP_STM32_USB_STREAM_H */
diff --git a/include/config.h b/include/config.h
index 5ae76fa5dd..ffd95fe0f2 100644
--- a/include/config.h
+++ b/include/config.h
@@ -925,6 +925,10 @@
#undef CONFIG_STREAM_USART4
/*****************************************************************************/
+/* USB stream config */
+#undef CONFIG_STREAM_USB
+
+/*****************************************************************************/
/* UART config */
/* Baud rate for UARTs */