/* Copyright (c) 2014 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_STREAM_H
#define __CROS_EC_USB_STREAM_H

#if defined(CHIP_FAMILY_STM32F4)
#include "usb_dwc_stream.h"
#else

/* STM32 USB STREAM driver for Chrome EC */

#include "compile_time_macros.h"
#include "consumer.h"
#include "hooks.h"
#include "producer.h"
#include "queue.h"
#include "usart.h"
#include "usb_descriptor.h"
#include "usb_hw.h"

#include <stdint.h>

/*
 * Per-USB stream state stored in RAM.  Zero initialization of this structure
 * by the BSS initialization leaves it in a valid and correctly initialized
 * state, so there is no need currently for a usb_stream_init style function.
 */
struct usb_stream_state {
	/*
	 * Flag indicating that there is a full RX buffer in the USB packet RAM
	 * that we were not able to move into the RX queue because there was
	 * not enough room when the packet was initially received.  The
	 * producer read operation checks this flag so that once there is
	 * room in the queue it can copy the RX buffer into the queue and
	 * restart USB reception by marking the RX buffer as VALID.
	 */
	int rx_waiting;
	/*
	 * Flag indicating that the incoming data on the USB link are discarded.
	 */
	int rx_disabled;
};

/*
 * Compile time Per-USB stream configuration stored in flash.  Instances of this
 * structure are provided by the user of the USB stream.  This structure binds
 * together all information required to operate a USB stream.
 */
struct usb_stream_config {
	/*
	 * Pointer to usb_stream_state structure.  The state structure
	 * maintains per USB stream information.
	 */
	struct usb_stream_state volatile *state;

	/*
	 * Endpoint index, and pointers to the USB packet RAM buffers.
	 */
	int endpoint;

	/*
	 * Deferred function to call to handle USB and Queue request.
	 */
	const struct deferred_data *deferred;

	size_t rx_size;
	size_t tx_size;

	usb_uint *rx_ram;
	usb_uint *tx_ram;

	struct consumer consumer;
	struct producer producer;
};

/*
 * These function tables are defined by the USB Stream driver and are used to
 * initialize the consumer and producer in the usb_stream_config.
 */
extern struct consumer_ops const usb_stream_consumer_ops;
extern struct producer_ops const usb_stream_producer_ops;

/*
 * Convenience macro for defining USB streams and their associated state and
 * buffers.
 *
 * NAME is used to construct the names of the packet RAM buffers, trampoline
 * functions, usb_stream_state struct, and usb_stream_config struct, the
 * latter is just called NAME.
 *
 * INTERFACE is the index of the USB interface to associate with this
 * stream.
 *
 * INTERFACE_CLASS, INTERFACE_SUBCLASS, INTERFACE_PROTOCOL are the
 * .bInterfaceClass, .bInterfaceSubClass, and .bInterfaceProtocol fields
 * respectively in the USB interface descriptor.
 *
 * INTERFACE_NAME is the index of the USB string descriptor (iInterface).
 *
 * ENDPOINT is the index of the USB bulk endpoint used for receiving and
 * transmitting bytes.
 *
 * RX_SIZE and TX_SIZE are the number of bytes of USB packet RAM to allocate
 * for the RX and TX packets respectively.  The valid values for these
 * parameters are dictated by the USB peripheral.
 *
 * RX_QUEUE and TX_QUEUE are the names of the RX and TX queues that this driver
 * should write to and read from respectively.
 */
/*
 * The following assertions can not be made because they require access to
 * non-const fields, but should be kept in mind.
 *
 * BUILD_ASSERT(RX_QUEUE.buffer_units >= RX_SIZE);
 * BUILD_ASSERT(TX_QUEUE.buffer_units >= TX_SIZE);
 * BUILD_ASSERT(RX_QUEUE.unit_bytes == 1);
 * BUILD_ASSERT(TX_QUEUE.unit_bytes == 1);
 */
#define USB_STREAM_CONFIG_FULL(NAME,					\
			       INTERFACE,				\
			       INTERFACE_CLASS,				\
			       INTERFACE_SUBCLASS,			\
			       INTERFACE_PROTOCOL,			\
			       INTERFACE_NAME,				\
			       ENDPOINT,				\
			       RX_SIZE,					\
			       TX_SIZE,					\
			       RX_QUEUE,				\
			       TX_QUEUE)				\
									\
	BUILD_ASSERT(RX_SIZE <= USB_MAX_PACKET_SIZE);			\
	BUILD_ASSERT(TX_SIZE <= USB_MAX_PACKET_SIZE);			\
	BUILD_ASSERT(RX_SIZE > 0);					\
	BUILD_ASSERT(TX_SIZE > 0);					\
	BUILD_ASSERT((RX_SIZE <   64 && (RX_SIZE & 0x01) == 0) ||	\
		     (RX_SIZE < 1024 && (RX_SIZE & 0x1f) == 0));	\
	BUILD_ASSERT((TX_SIZE <   64 && (TX_SIZE & 0x01) == 0) ||	\
		     (TX_SIZE < 1024 && (TX_SIZE & 0x1f) == 0));	\
									\
	static usb_uint CONCAT2(NAME, _ep_rx_buffer)[RX_SIZE / 2] __usb_ram; \
	static usb_uint CONCAT2(NAME, _ep_tx_buffer)[TX_SIZE / 2] __usb_ram; \
	static struct usb_stream_state CONCAT2(NAME, _state);		\
	static void CONCAT2(NAME, _deferred_)(void);			\
	DECLARE_DEFERRED(CONCAT2(NAME, _deferred_));			\
	struct usb_stream_config const NAME = {				\
		.state     = &CONCAT2(NAME, _state),			\
		.endpoint  = ENDPOINT,					\
		.deferred  = &CONCAT2(NAME, _deferred__data),		\
		.rx_size   = RX_SIZE,					\
		.tx_size   = TX_SIZE,					\
		.rx_ram    = CONCAT2(NAME, _ep_rx_buffer),		\
		.tx_ram    = CONCAT2(NAME, _ep_tx_buffer),		\
		.consumer  = {						\
			.queue = &TX_QUEUE,				\
			.ops   = &usb_stream_consumer_ops,		\
		},							\
		.producer  = {						\
			.queue = &RX_QUEUE,				\
			.ops   = &usb_stream_producer_ops,		\
		},							\
	};								\
	const struct usb_interface_descriptor				\
	USB_IFACE_DESC(INTERFACE) = {					\
		.bLength            = USB_DT_INTERFACE_SIZE,		\
		.bDescriptorType    = USB_DT_INTERFACE,			\
		.bInterfaceNumber   = INTERFACE,			\
		.bAlternateSetting  = 0,				\
		.bNumEndpoints      = 2,				\
		.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     = 0x02 /* Bulk IN */,			\
		.wMaxPacketSize   = TX_SIZE,				\
		.bInterval        = 10,					\
	};								\
	const struct usb_endpoint_descriptor				\
	USB_EP_DESC(INTERFACE, 1) = {					\
		.bLength          = USB_DT_ENDPOINT_SIZE,		\
		.bDescriptorType  = USB_DT_ENDPOINT,			\
		.bEndpointAddress = ENDPOINT,				\
		.bmAttributes     = 0x02 /* Bulk OUT */,		\
		.wMaxPacketSize   = RX_SIZE,				\
		.bInterval        = 0,					\
	};								\
	static void CONCAT2(NAME, _ep_tx)(void)				\
	{								\
		usb_stream_tx(&NAME);					\
	}								\
	static void CONCAT2(NAME, _ep_rx)(void)				\
	{								\
		usb_stream_rx(&NAME);					\
	}								\
	static void CONCAT2(NAME, _ep_event)(enum usb_ep_event evt)	\
	{								\
		usb_stream_event(&NAME, evt);				\
	}								\
	USB_DECLARE_EP(ENDPOINT,					\
		       CONCAT2(NAME, _ep_tx),				\
		       CONCAT2(NAME, _ep_rx),				\
		       CONCAT2(NAME, _ep_event));			\
	static void CONCAT2(NAME, _deferred_)(void)			\
	{ usb_stream_deferred(&NAME); }

/* This is a short version for declaring Google serial endpoints */
#define USB_STREAM_CONFIG(NAME,						\
			  INTERFACE,					\
			  INTERFACE_NAME,				\
			  ENDPOINT,					\
			  RX_SIZE,					\
			  TX_SIZE,					\
			  RX_QUEUE,					\
			  TX_QUEUE)					\
	USB_STREAM_CONFIG_FULL(NAME,					\
			       INTERFACE,				\
			       USB_CLASS_VENDOR_SPEC,			\
			       USB_SUBCLASS_GOOGLE_SERIAL,		\
			       USB_PROTOCOL_GOOGLE_SERIAL,		\
			       INTERFACE_NAME,				\
			       ENDPOINT,				\
			       RX_SIZE,					\
			       TX_SIZE,					\
			       RX_QUEUE,				\
			       TX_QUEUE)

/* Declare a utility interface for setting parity/baud. */
#define USB_USART_IFACE(NAME, INTERFACE, USART_CFG)			\
	static int CONCAT2(NAME,  _interface_)(usb_uint *rx_buf,	\
					      usb_uint *tx_buf)		\
	{ return usb_usart_interface(&NAME, &USART_CFG, INTERFACE,	\
					rx_buf, tx_buf); }		\
	USB_DECLARE_IFACE(INTERFACE,					\
			  CONCAT2(NAME, _interface_))

/* This is a medium version for declaring Google serial endpoints */
#define USB_STREAM_CONFIG_USART_IFACE(NAME,				\
				      INTERFACE,			\
				      INTERFACE_NAME,			\
				      ENDPOINT,				\
				      RX_SIZE,				\
				      TX_SIZE,				\
				      RX_QUEUE,				\
				      TX_QUEUE,				\
				      USART_CFG)			\
	USB_STREAM_CONFIG_FULL(NAME,					\
			       INTERFACE,				\
			       USB_CLASS_VENDOR_SPEC,			\
			       USB_SUBCLASS_GOOGLE_SERIAL,		\
			       USB_PROTOCOL_GOOGLE_SERIAL,		\
			       INTERFACE_NAME,				\
			       ENDPOINT,				\
			       RX_SIZE,					\
			       TX_SIZE,					\
			       RX_QUEUE,				\
			       TX_QUEUE);				\
	USB_USART_IFACE(NAME, INTERFACE, USART_CFG)

/*
 * Handle USB and Queue request in a deferred callback.
 */
void usb_stream_deferred(struct usb_stream_config const *config);

/*
 * Handle control interface requests.
 */
enum usb_usart {
	USB_USART_REQ_PARITY = 0,
	USB_USART_SET_PARITY = 1,
	USB_USART_REQ_BAUD = 2,
	USB_USART_SET_BAUD = 3,
};
int usb_usart_interface(struct usb_stream_config const *config,
		    struct usart_config const *usart,
		    int interface, usb_uint *rx_buf, usb_uint *tx_buf);

/*
 * These functions are used by the trampoline functions defined above to
 * connect USB endpoint events with the generic USB stream driver.
 */
void usb_stream_tx(struct usb_stream_config const *config);
void usb_stream_rx(struct usb_stream_config const *config);
void usb_stream_event(struct usb_stream_config const *config,
		      enum usb_ep_event evt);

#endif /* defined(CHIP_FAMILY_STM32F4) */
#endif /* __CROS_EC_USB_STREAM_H */
