stm32: add usb_isochronous

Templates for USB isochronous implementation.  Current implementation
only supports TX transmit.  Example of usage can be found in CL:803414.

Basically, declare an USB isochronous interface by

USB_ISOCHRONOUS_CONFIG_FULL(<NAME>,
                            <INTERFACE_NUM>,
                            <USB_CLASS>,
                            <USB_SUBCLASS>,
                            <SUB_PROTOCOL>,
                            <USB_STR_FOR_INTERFACE_NAME>,
                            <USB_EP_NUM>,
                            <PACKET_SIZE>,
                            <TX_CALLBACK>,
                            <SET_INTERFACE>)

where <PACKET_SIZE> is size of each USB packet, <TX_CALLBACK> is called
when USB hardware has completed a packet.  The buffer that USB is not
currently using will be passed to <TX_CALLBACK>, allow applications to
write next packet to it.

When a SET_INTERFACE packet is received, <SET_INTERFACE> will be called
with bAlternateSetting and bInterfaceNumber.

We will declare interface descriptor with bAlternateSetting = 0 and 1
for you, if you need more alternate settings, you need to declare by
yourself.

BUG=b:70482333
TEST=manually on reworked staff board
Signed-off-by: Wei-Han Chen <stimim@google.com>

Change-Id: Ic6d41da6ddd7945edf0bdfff55ede38a97661783
Reviewed-on: https://chromium-review.googlesource.com/818853
Commit-Ready: Wei-Han Chen <stimim@chromium.org>
Tested-by: Wei-Han Chen <stimim@chromium.org>
Reviewed-by: Wei-Han Chen <stimim@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
This commit is contained in:
Wei-Han Chen
2017-12-11 14:41:46 +08:00
committed by chrome-bot
parent 924d21d904
commit b245e71e82
4 changed files with 289 additions and 0 deletions

View File

@@ -80,6 +80,7 @@ chip-$(CONFIG_USB_GPIO)+=usb_gpio.o
chip-$(CONFIG_USB_HID)+=usb_hid.o
chip-$(CONFIG_USB_HID_KEYBOARD)+=usb_hid_keyboard.o
chip-$(CONFIG_USB_HID_TOUCHPAD)+=usb_hid_touchpad.o
chip-$(CONFIG_USB_ISOCHRONOUS)+=usb_isochronous.o
chip-$(CONFIG_USB_PD_TCPC)+=usb_pd_phy.o
chip-$(CONFIG_USB_SPI)+=usb_spi.o
endif

View File

@@ -0,0 +1,142 @@
/* Copyright 2017 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 "stddef.h"
#include "common.h"
#include "config.h"
#include "link_defs.h"
#include "registers.h"
#include "util.h"
#include "usb_api.h"
#include "usb_hw.h"
#include "usb_isochronous.h"
/* Console output macro */
#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.
*
* According to RM0091, isochronous transfer is always double buffered.
* Addresses of buffers are pointed by `btable_ep[<endpoint>].tx_addr` and
* `btable_ep[<endpoint>].rx_addr`.
*
* DTOG | USB Buffer | App Buffer
* -----+------------+-----------
* 0 | tx_addr | rx_addr
* 1 | rx_addr | tx_addr
*
* That is, when DTOG bit is 0 (see `get_tx_dtog()`), USB hardware will read
* from `tx_addr`, and our application can write new data to `rx_addr` at the
* same time.
*
* 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()`.
*/
static int get_tx_dtog(struct usb_isochronous_config const *config)
{
return !!(STM32_USB_EP(config->endpoint) & EP_TX_DTOG);
}
/*
* Gets buffer address that can be used by software (application).
*
* The mapping between application buffer address and current TX DTOG value is
* shown in table above.
*/
static usb_uint *get_app_addr(struct usb_isochronous_config const *config,
int dtog_value)
{
return config->tx_ram[dtog_value];
}
/*
* Sets number of bytes written to application buffer.
*/
static void set_app_count(struct usb_isochronous_config const *config,
int dtog_value,
usb_uint count)
{
if (dtog_value)
btable_ep[config->endpoint].tx_count = count;
else
btable_ep[config->endpoint].rx_count = count;
}
void usb_isochronous_init(struct usb_isochronous_config const *config)
{
int ep = config->endpoint;
btable_ep[ep].tx_addr = usb_sram_addr(get_app_addr(config, 1));
btable_ep[ep].rx_addr = usb_sram_addr(get_app_addr(config, 0));
set_app_count(config, 0, 0);
set_app_count(config, 1, 0);
STM32_USB_EP(ep) = ((ep << 0) | /* Endpoint Addr */
EP_TX_VALID | /* start transmit */
(2 << 9) | /* ISO EP */
EP_RX_DISAB);
}
void usb_isochronous_event(struct usb_isochronous_config const *config,
enum usb_ep_event evt)
{
if (evt == USB_EVENT_RESET)
usb_isochronous_init(config);
}
void usb_isochronous_tx(struct usb_isochronous_config const *config)
{
/*
* Clear CTR_TX, note that EP_TX_VALID will *NOT* be cleared by
* hardware, so we don't need to toggle it.
*/
STM32_TOGGLE_EP(config->endpoint, 0, 0, 0);
/*
* Clear buffer count for buffer we just transmitted, so we do not
* transmit the data twice.
*/
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);
}
int usb_isochronous_iface_handler(struct usb_isochronous_config const *config,
usb_uint *ep0_buf_rx,
usb_uint *ep0_buf_tx)
{
int ret = -1;
if (ep0_buf_rx[0] == (USB_DIR_OUT |
USB_TYPE_STANDARD |
USB_RECIP_INTERFACE |
USB_REQ_SET_INTERFACE << 8)) {
ret = config->set_interface(ep0_buf_rx[1], ep0_buf_rx[2]);
if (ret == 0) {
/* ACK */
btable_ep[0].tx_count = 0;
STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, 0);
}
}
return ret;
}

View File

@@ -0,0 +1,143 @@
/* Copyright 2017 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 __CROS_EC_USB_ISOCHRONOUS_H
#define __CROS_EC_USB_ISOCHRONOUS_H
#include "common.h"
#include "compile_time_macros.h"
#include "hooks.h"
#include "usb_descriptor.h"
#include "usb_hw.h"
/* Currently, we only support TX direction for USB isochronous transfer. */
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.
*
* @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
*/
size_t (*tx_callback)(usb_uint *usb_addr, size_t tx_size);
/*
* Received SET_INTERFACE request.
*
* @param alternate_setting new bAlternateSetting value.
* @param interface interface number.
* @return int 0 for success, -1 for unknown setting.
*/
int (*set_interface)(usb_uint alternate_setting, usb_uint interface);
/* USB packet RAM buffer size. */
size_t tx_size;
/* USB packet RAM buffers. */
usb_uint *tx_ram[2];
};
/* Define an USB isochronous interface */
#define USB_ISOCHRONOUS_CONFIG_FULL(NAME, \
INTERFACE, \
INTERFACE_CLASS, \
INTERFACE_SUBCLASS, \
INTERFACE_PROTOCOL, \
INTERFACE_NAME, \
ENDPOINT, \
TX_SIZE, \
TX_CALLBACK, \
SET_INTERFACE) \
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, \
.tx_ram = { \
CONCAT2(NAME, _ep_tx_buffer_0), \
CONCAT2(NAME, _ep_tx_buffer_1), \
}, \
}; \
const struct usb_interface_descriptor \
USB_IFACE_DESC(INTERFACE) = { \
.bLength = USB_DT_INTERFACE_SIZE, \
.bDescriptorType = USB_DT_INTERFACE, \
.bInterfaceNumber = INTERFACE, \
.bAlternateSetting = 0, \
.bNumEndpoints = 0, \
.bInterfaceClass = INTERFACE_CLASS, \
.bInterfaceSubClass = INTERFACE_SUBCLASS, \
.bInterfaceProtocol = INTERFACE_PROTOCOL, \
.iInterface = INTERFACE_NAME, \
}; \
const struct usb_interface_descriptor \
USB_CONF_DESC(CONCAT3(iface, INTERFACE, _1iface)) = { \
.bLength = USB_DT_INTERFACE_SIZE, \
.bDescriptorType = USB_DT_INTERFACE, \
.bInterfaceNumber = INTERFACE, \
.bAlternateSetting = 1, \
.bNumEndpoints = 1, \
.bInterfaceClass = INTERFACE_CLASS, \
.bInterfaceSubClass = INTERFACE_SUBCLASS, \
.bInterfaceProtocol = INTERFACE_PROTOCOL, \
.iInterface = INTERFACE_NAME, \
}; \
const struct usb_endpoint_descriptor \
USB_EP_DESC(INTERFACE, 0) = { \
.bLength = USB_DT_ENDPOINT_SIZE, \
.bDescriptorType = USB_DT_ENDPOINT, \
.bEndpointAddress = 0x80 | ENDPOINT, \
.bmAttributes = 0x01 /* Isochronous IN */, \
.wMaxPacketSize = TX_SIZE, \
.bInterval = 1, \
}; \
static void CONCAT2(NAME, _ep_tx)(void) \
{ \
usb_isochronous_tx(&NAME); \
} \
static void CONCAT2(NAME, _ep_event)(enum usb_ep_event evt) \
{ \
usb_isochronous_event(&NAME, evt); \
} \
static int CONCAT2(NAME, _handler)(usb_uint *rx, usb_uint *tx) \
{ \
return usb_isochronous_iface_handler(&NAME, rx, tx); \
} \
USB_DECLARE_IFACE(INTERFACE, CONCAT2(NAME, _handler)); \
USB_DECLARE_EP(ENDPOINT, \
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);
int usb_isochronous_iface_handler(struct usb_isochronous_config const *config,
usb_uint *ep0_buf_rx,
usb_uint *ep0_buf_tx);
#endif /* __CROS_EC_USB_ISOCHRONOUS_H */

View File

@@ -2830,6 +2830,9 @@
/* Compile chip support for the USB device controller */
#undef CONFIG_USB
/* Support USB isochronous handler */
#undef CONFIG_USB_ISOCHRONOUS
/* Support USB blob handler. */
#undef CONFIG_USB_BLOB