From 62775a3981ed7f57b5b49b31643db42eb944575c Mon Sep 17 00:00:00 2001 From: Wei-Han Chen Date: Wed, 6 Jun 2018 16:58:25 +0800 Subject: 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 Change-Id: I7bc7f8175803895dae8ebc7720bc7e468db20d1c Reviewed-on: https://chromium-review.googlesource.com/1089599 Commit-Ready: Wei-Han Chen Tested-by: Wei-Han Chen Reviewed-by: Nicolas Boichat --- chip/stm32/usb_isochronous.c | 49 +++++++++++++++------ chip/stm32/usb_isochronous.h | 100 +++++++++++++++++++++++++++++++++---------- 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); -- cgit v1.2.1