coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/stm32/usb_isochronous.h

198 lines
6.1 KiB
C

/* 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"
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;
/*
* On TX complete, this function will be called in **interrupt
* context**.
*
* @param config the usb_isochronous_config of the USB interface.
*/
void (*tx_callback)(struct usb_isochronous_config const *config);
/*
* 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, \
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; \
struct usb_isochronous_config const NAME = { \
.endpoint = ENDPOINT, \
.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 + NUM_EXTRA_ENDPOINTS, \
.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)); \
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 */