mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
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:
@@ -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
|
||||
|
||||
142
chip/stm32/usb_isochronous.c
Normal file
142
chip/stm32/usb_isochronous.c
Normal 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;
|
||||
}
|
||||
143
chip/stm32/usb_isochronous.h
Normal file
143
chip/stm32/usb_isochronous.h
Normal 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 */
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user