summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei-Han Chen <stimim@google.com>2018-06-06 16:58:25 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-07-18 01:40:02 -0700
commit62775a3981ed7f57b5b49b31643db42eb944575c (patch)
treef829f52ab3557fd6549854d1ca0f276f7d6df483
parenta8d34b791540534d6e45aac719cee0e72ce79824 (diff)
downloadchrome-ec-62775a3981ed7f57b5b49b31643db42eb944575c.tar.gz
usb_isochronous.c: change usb_isochronous API
1. Export usb_isochronous_write_buffer function. 2. The tx_callback is called in interrupt context, so users can decide to fill the buffer with deferred function or task thread. 3. Allow adding extra endpoints to the interface. BRANCH=none BUG=b:70482333 TEST=tested on device Signed-off-by: Wei-Han Chen <stimim@chromium.org> Change-Id: I7bc7f8175803895dae8ebc7720bc7e468db20d1c Reviewed-on: https://chromium-review.googlesource.com/1089599 Commit-Ready: Wei-Han Chen <stimim@chromium.org> Tested-by: Wei-Han Chen <stimim@chromium.org> Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
-rw-r--r--chip/stm32/usb_isochronous.c49
-rw-r--r--chip/stm32/usb_isochronous.h100
2 files changed, 112 insertions, 37 deletions
diff --git a/chip/stm32/usb_isochronous.c b/chip/stm32/usb_isochronous.c
index fe8402473c..792507aa75 100644
--- a/chip/stm32/usb_isochronous.c
+++ b/chip/stm32/usb_isochronous.c
@@ -18,7 +18,6 @@
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
-
/*
* Currently, we only support TX direction for USB isochronous transfer.
*
@@ -38,11 +37,13 @@
* Number of bytes in each buffer shall be tracked by `tx_count` and `rx_count`
* respectively.
*
- * `get_app_addr()`, `set_app_addr()`, `set_app_count()` help you to to select
- * the correct variable to use by given DTOG value, which is available by
- * `get_tx_dtog()`.
+ * `get_app_addr()`, `set_app_count()` help you to to select the correct
+ * variable to use by given DTOG value, which is available by `get_tx_dtog()`.
*/
+/*
+ * Gets current DTOG value of given `config`.
+ */
static int get_tx_dtog(struct usb_isochronous_config const *config)
{
return !!(STM32_USB_EP(config->endpoint) & EP_TX_DTOG);
@@ -73,6 +74,35 @@ static void set_app_count(struct usb_isochronous_config const *config,
btable_ep[config->endpoint].rx_count = count;
}
+int usb_isochronous_write_buffer(
+ struct usb_isochronous_config const *config,
+ const uint8_t *src,
+ size_t n,
+ size_t dst_offset,
+ int *buffer_id,
+ int commit)
+{
+ int dtog_value = get_tx_dtog(config);
+ usb_uint *buffer = get_app_addr(config, dtog_value);
+ uintptr_t ptr = usb_sram_addr(buffer);
+
+ if (*buffer_id == -1)
+ *buffer_id = dtog_value;
+ else if (dtog_value != *buffer_id)
+ return -EC_ERROR_TIMEOUT;
+
+ if (dst_offset > config->tx_size)
+ return -EC_ERROR_INVAL;
+
+ n = MIN(n, config->tx_size - dst_offset);
+ memcpy_to_usbram((void *)(ptr + dst_offset), src, n);
+
+ if (commit)
+ set_app_count(config, dtog_value, dst_offset + n);
+
+ return n;
+}
+
void usb_isochronous_init(struct usb_isochronous_config const *config)
{
int ep = config->endpoint;
@@ -108,16 +138,7 @@ void usb_isochronous_tx(struct usb_isochronous_config const *config)
*/
set_app_count(config, get_tx_dtog(config), 0);
- hook_call_deferred(config->deferred, 0);
-}
-
-void usb_isochronous_deferred(struct usb_isochronous_config const *config)
-{
- const int dtog_value = get_tx_dtog(config);
- usb_uint *app_addr = get_app_addr(config, dtog_value);
- size_t count = config->tx_callback(app_addr, config->tx_size);
-
- set_app_count(config, dtog_value, count);
+ config->tx_callback(config);
}
int usb_isochronous_iface_handler(struct usb_isochronous_config const *config,
diff --git a/chip/stm32/usb_isochronous.h b/chip/stm32/usb_isochronous.h
index 423ce339ce..efa4d94ab4 100644
--- a/chip/stm32/usb_isochronous.h
+++ b/chip/stm32/usb_isochronous.h
@@ -12,26 +12,87 @@
#include "usb_descriptor.h"
#include "usb_hw.h"
-/* Currently, we only support TX direction for USB isochronous transfer. */
+struct usb_isochronous_config;
+
+/*
+ * Currently, we only support TX direction for USB isochronous transfer.
+ */
+
+/*
+ * Copy `n` bytes from `src` to USB buffer.
+ *
+ * We are using double buffering, therefore, we need to write to the buffer that
+ * hardware is not currently using. This function will handle this for you.
+ *
+ * Sample usage:
+ *
+ * int buffer_id = -1; // initialize to unknown
+ * int ret;
+ * size_t dst_offset = 0, src_offset = 0;
+ * const uint8_t* buf;
+ * size_t buf_size;
+ *
+ * while (1) {
+ * buf = ...;
+ * buf_size = ...;
+ * if (no more data) {
+ * buf = NULL;
+ * break;
+ * } else {
+ * ret = usb_isochronous_write_buffer(
+ * config, buf, buf_size, dst_offset,
+ * &buffer_id,
+ * 0);
+ * if (ret < 0)
+ * goto FAILED;
+ * dst_offset += ret;
+ * if (ret != buf_size) {
+ * // no more space in TX buffer
+ * src_offset = ret;
+ * break;
+ * }
+ * }
+ * }
+ * // commit
+ * ret = usb_isochronous_write_buffer(
+ * config, NULL, 0, dst_offset,
+ * &buffer_id, 1);
+ * if (ret < 0)
+ * goto FAILED;
+ * if (buf)
+ * // buf[src_offset ... buf_size] haven't been sent yet, send them
+ * // later.
+ *
+ * On the first invocation, on success, `ret` will be number of bytes that have
+ * been written, and `buffer_id` will be 0 or 1, depending on which buffer we
+ * are writing. And commit=0 means there are pending data, so buffer count
+ * won't be set yet.
+ *
+ * On the second invocation, since buffer_id is not -1, we will return an error
+ * if hardware has switched to this buffer (it means we spent too much time
+ * filling buffer). And commit=1 means we are done, and buffer count will be
+ * set to `dst_offset + num_bytes_written` on success.
+ *
+ * @return -EC_ERROR_CODE on failure, or number of bytes written on success.
+ */
+int usb_isochronous_write_buffer(
+ struct usb_isochronous_config const *config,
+ const uint8_t *src,
+ size_t n,
+ size_t dst_offset,
+ int *buffer_id,
+ int commit);
struct usb_isochronous_config {
int endpoint;
/*
- * Deferred function to call to handle USB request.
- */
- const struct deferred_data *deferred;
-
- /*
- * On TX complete, this function will be called to ask for more data to
- * transmit.
+ * On TX complete, this function will be called in **interrupt
+ * context**.
*
- * @param usb_addr USB buffer, an uint8_t pointer that can be
- * passed to memcpy_to_usbram()
- * @param tx_size config->tx_size
- * @return size_t Number of bytes written to USB buffer
+ * @param config the usb_isochronous_config of the USB interface.
*/
- size_t (*tx_callback)(usb_uint *usb_addr, size_t tx_size);
+ void (*tx_callback)(struct usb_isochronous_config const *config);
/*
* Received SET_INTERFACE request.
@@ -58,18 +119,16 @@ struct usb_isochronous_config {
ENDPOINT, \
TX_SIZE, \
TX_CALLBACK, \
- SET_INTERFACE) \
+ SET_INTERFACE, \
+ NUM_EXTRA_ENDPOINTS) \
BUILD_ASSERT(TX_SIZE > 0); \
BUILD_ASSERT((TX_SIZE < 64 && (TX_SIZE & 0x01) == 0) || \
(TX_SIZE < 1024 && (TX_SIZE & 0x1f) == 0)); \
/* Declare buffer */ \
static usb_uint CONCAT2(NAME, _ep_tx_buffer_0)[TX_SIZE / 2] __usb_ram; \
static usb_uint CONCAT2(NAME, _ep_tx_buffer_1)[TX_SIZE / 2] __usb_ram; \
- static void CONCAT2(NAME, _deferred_)(void); \
- DECLARE_DEFERRED(CONCAT2(NAME, _deferred_)); \
struct usb_isochronous_config const NAME = { \
.endpoint = ENDPOINT, \
- .deferred = &CONCAT2(NAME, _deferred__data), \
.tx_callback = TX_CALLBACK, \
.set_interface = SET_INTERFACE, \
.tx_size = TX_SIZE, \
@@ -96,7 +155,7 @@ struct usb_isochronous_config {
.bDescriptorType = USB_DT_INTERFACE, \
.bInterfaceNumber = INTERFACE, \
.bAlternateSetting = 1, \
- .bNumEndpoints = 1, \
+ .bNumEndpoints = 1 + NUM_EXTRA_ENDPOINTS, \
.bInterfaceClass = INTERFACE_CLASS, \
.bInterfaceSubClass = INTERFACE_SUBCLASS, \
.bInterfaceProtocol = INTERFACE_PROTOCOL, \
@@ -128,12 +187,7 @@ struct usb_isochronous_config {
CONCAT2(NAME, _ep_tx), \
CONCAT2(NAME, _ep_tx), \
CONCAT2(NAME, _ep_event)); \
- static void CONCAT2(NAME, _deferred_)(void) \
- { \
- usb_isochronous_deferred(&NAME); \
- }
-void usb_isochronous_deferred(struct usb_isochronous_config const *config);
void usb_isochronous_tx(struct usb_isochronous_config const *config);
void usb_isochronous_event(struct usb_isochronous_config const *config,
enum usb_ep_event event);