1438 lines
38 KiB
C
1438 lines
38 KiB
C
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of The Linux Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* This file implements the UDC (usb device controller) layer to be used with
|
|
* the new dwc controller.
|
|
* It exposes APIs to initialize UDC (and thus usb) and perform data transfer
|
|
* over usb.
|
|
*/
|
|
|
|
#include <reg.h>
|
|
#include <debug.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <arch/defines.h>
|
|
#include <dev/udc.h>
|
|
#include <platform/iomap.h>
|
|
#include <usb30_dwc.h>
|
|
#include <usb30_wrapper.h>
|
|
#include <usb30_udc.h>
|
|
#include <smem.h>
|
|
#include <board.h>
|
|
#include <platform/timer.h>
|
|
#include <qmp_phy.h>
|
|
#include <usb30_dwc_hw.h>
|
|
|
|
//#define DEBUG_USB
|
|
|
|
#ifdef DEBUG_USB
|
|
#define DBG(...) dprintf(ALWAYS, __VA_ARGS__)
|
|
#else
|
|
#define DBG(...)
|
|
#endif
|
|
|
|
#define ERR(...) dprintf(ALWAYS, __VA_ARGS__)
|
|
|
|
/* control data transfer is max 512 bytes */
|
|
#define UDC_CONTROL_RX_BUF_SIZE 512
|
|
#define UDC_CONTROL_TX_BUF_SIZE 512
|
|
|
|
/* Buffer used by dwc driver to process events.
|
|
* Must be multiple of 4: snps 6.2.7.2.
|
|
*/
|
|
#define UDC_DWC_EVENT_BUF_SIZE 4096
|
|
|
|
/* macro to parse setup request */
|
|
#define SETUP(type,request) (((type) << 8) | (request))
|
|
|
|
/* macro to generate bit representation of an EP */
|
|
#define EPT_TX(n) (1 << ((n) + 16))
|
|
#define EPT_RX(n) (1 << (n))
|
|
|
|
/* Macro for bulk SS EP descriptors */
|
|
#define EP_BULK_IN_INDEX 21
|
|
#define EP_BULK_OUT_INDEX 34
|
|
|
|
/* Local functions */
|
|
static struct udc_descriptor *udc_descriptor_alloc(uint32_t type,
|
|
uint32_t num,
|
|
uint32_t len,
|
|
udc_desc_spec_t spec);
|
|
static uint8_t udc_string_desc_alloc(udc_t *udc, const char *str);
|
|
|
|
static void udc_descriptor_register(udc_t *udc, struct udc_descriptor *desc);
|
|
static void udc_register_language_desc(udc_t *udc);
|
|
static void udc_register_bos_desc(udc_t *udc);
|
|
static void udc_register_device_desc_usb_20(udc_t *udc, struct udc_device *dev_info);
|
|
static void udc_register_device_desc_usb_30(udc_t *udc, struct udc_device *dev_info);
|
|
static void udc_register_config_desc_usb20(udc_t *udc, struct udc_gadget *gadget, uint8_t type);
|
|
static void udc_register_config_desc_usb30(udc_t *udc, struct udc_gadget *gadget);
|
|
|
|
static void udc_ept_desc_fill(struct udc_endpoint *ept, uint8_t *data, uint8_t type);
|
|
static void udc_ept_comp_desc_fill(struct udc_endpoint *ept, uint8_t *data);
|
|
|
|
static void udc_dwc_notify(void *context, dwc_notify_event_t event);
|
|
static int udc_handle_setup(void *context, uint8_t *data);
|
|
static bool stall_ep;
|
|
static bool udc_other_speed_cfg;
|
|
static bool udc_ss_capable;
|
|
|
|
/* TODO: This must be the only global var in this file, for now.
|
|
* Ideally, all APIs should be sending
|
|
* this to us and this ptr should be kept outside of this code.
|
|
* This needs change in the common udc APIs and thus keeping it here until that
|
|
* is done.
|
|
*/
|
|
static udc_t *udc_dev = NULL;
|
|
|
|
|
|
__WEAK int platform_is_8974()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
__WEAK int platform_is_8974Pro()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void phy_reset(usb_wrapper_dev_t *wrapper, struct udc_device *dev_info)
|
|
{
|
|
/* phy reset is different for some platforms. */
|
|
if (platform_is_8974() || platform_is_8974Pro())
|
|
{
|
|
/* SS PHY */
|
|
usb_wrapper_ss_phy_reset(wrapper);
|
|
|
|
/* For 8974: hs phy is reset as part of soft reset.
|
|
* No need for explicit reset.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if (dev_info->t_usb_if->phy_reset)
|
|
dev_info->t_usb_if->phy_reset();
|
|
|
|
/* On some CDPs PHY_COMMON reset does not set
|
|
* reset values in the phy_ctrl_common register.
|
|
* Due to this USB does not get enumerated in fastboot
|
|
* Force write the reset value
|
|
*/
|
|
if (board_platform_id() == APQ8084)
|
|
usb_wrapper_hs_phy_ctrl_force_write(wrapper);
|
|
}
|
|
}
|
|
|
|
/* Initialize HS phy */
|
|
void hs_phy_init(udc_t *dev)
|
|
{
|
|
/* only for 8974 */
|
|
if (platform_is_8974() || platform_is_8974Pro())
|
|
{
|
|
/* 5.a, 5.b */
|
|
usb_wrapper_hs_phy_init(dev->wrapper_dev);
|
|
|
|
/* 5.d */
|
|
dwc_usb2_phy_soft_reset(dev->dwc);
|
|
}
|
|
}
|
|
|
|
/* vbus override */
|
|
void vbus_override(udc_t *dev)
|
|
{
|
|
/* when vbus signal is not available directly to the controller,
|
|
* simulate vbus presense.
|
|
*/
|
|
usb_wrapper_vbus_override(dev->wrapper_dev);
|
|
}
|
|
|
|
|
|
/* Initialize usb wrapper and dwc h/w blocks. */
|
|
static void usb30_init(struct udc_device *dev_info)
|
|
{
|
|
usb_wrapper_dev_t* wrapper;
|
|
usb_wrapper_config_t wrapper_config;
|
|
|
|
dwc_dev_t *dwc;
|
|
dwc_config_t dwc_config;
|
|
|
|
/* initialize usb clocks */
|
|
if (dev_info->t_usb_if->clock_init)
|
|
dev_info->t_usb_if->clock_init();
|
|
|
|
/* initialize the usb wrapper h/w block */
|
|
wrapper_config.qscratch_base = (void*) MSM_USB30_QSCRATCH_BASE;
|
|
|
|
wrapper = usb_wrapper_init(&wrapper_config);
|
|
ASSERT(wrapper);
|
|
|
|
/* save the wrapper ptr */
|
|
udc_dev->wrapper_dev = wrapper;
|
|
|
|
/* initialize the dwc device block */
|
|
dwc_config.base = (void*) MSM_USB30_BASE;
|
|
|
|
/* buffer must be aligned to buf size. snps 8.2.2 */
|
|
dwc_config.event_buf = memalign(lcm(CACHE_LINE, UDC_DWC_EVENT_BUF_SIZE),
|
|
ROUNDUP(UDC_DWC_EVENT_BUF_SIZE, CACHE_LINE));
|
|
ASSERT(dwc_config.event_buf);
|
|
|
|
dwc_config.event_buf_size = UDC_DWC_EVENT_BUF_SIZE;
|
|
|
|
/* notify handler */
|
|
dwc_config.notify_context = udc_dev;
|
|
dwc_config.notify = udc_dwc_notify;
|
|
|
|
/* setup handler */
|
|
dwc_config.setup_context = udc_dev;
|
|
dwc_config.setup_handler = udc_handle_setup;
|
|
|
|
dwc = dwc_init(&dwc_config);
|
|
ASSERT(dwc);
|
|
|
|
/* save the dwc dev ptr */
|
|
udc_dev->dwc = dwc;
|
|
|
|
|
|
/* USB3.0 core and phy initialization as described in HPG */
|
|
|
|
/* section 4.4.1 Control sequence */
|
|
usb_wrapper_dbm_mode(wrapper, DBM_MODE_BYPASS);
|
|
|
|
/* section 4.4.1: use config 0 - all of RAM1 */
|
|
usb_wrapper_ram_configure(wrapper);
|
|
|
|
/* section 4.4.2: Initialization and configuration sequences */
|
|
|
|
/* 1. UTMI Mux configuration */
|
|
if (dev_info->t_usb_if->mux_config)
|
|
dev_info->t_usb_if->mux_config();
|
|
|
|
/* 2. Put controller in reset */
|
|
dwc_reset(dwc, 1);
|
|
|
|
|
|
/* Steps 3 - 7 must be done while dwc is in reset condition */
|
|
|
|
/* 3. Reset PHY */
|
|
phy_reset(wrapper, dev_info);
|
|
|
|
/* 4. SS phy config */
|
|
if (!use_hsonly_mode())
|
|
usb_wrapper_ss_phy_configure(wrapper);
|
|
|
|
/* 5. HS phy init */
|
|
usb_wrapper_hs_phy_init(wrapper);
|
|
|
|
/* 6. hs phy config */
|
|
usb_wrapper_hs_phy_configure(wrapper);
|
|
|
|
/* 7. Reset PHY digital interface */
|
|
dwc_phy_digital_reset(dwc);
|
|
|
|
/* 8. Bring dwc controller out of reset */
|
|
dwc_reset(dwc, 0);
|
|
|
|
/* 9. */
|
|
usb_wrapper_ss_phy_electrical_config(wrapper);
|
|
|
|
/* Perform phy init */
|
|
if (dev_info->t_usb_if->phy_init)
|
|
dev_info->t_usb_if->phy_init();
|
|
|
|
/* HS only mode support */
|
|
if (use_hsonly_mode())
|
|
usb_wrapper_hsonly_mode(wrapper);
|
|
|
|
/* 10. */
|
|
usb_wrapper_workaround_10(wrapper);
|
|
|
|
/* 11. */
|
|
usb_wrapper_workaround_11(wrapper);
|
|
|
|
/* 12. */
|
|
dwc_ss_phy_workaround_12(dwc);
|
|
|
|
/* 13. */
|
|
usb_wrapper_workaround_13(wrapper);
|
|
|
|
/* 14. needed only for host mode. ignored. */
|
|
|
|
/* If the target does not support vbus detection in controller,
|
|
* simulate vbus presence.
|
|
*/
|
|
if (dev_info->t_usb_if->vbus_override)
|
|
vbus_override(udc_dev);
|
|
|
|
/* 15 - 20 */
|
|
dwc_device_init(dwc);
|
|
}
|
|
|
|
/* udc_init: creates and registers various usb descriptor */
|
|
int usb30_udc_init(struct udc_device *dev_info)
|
|
{
|
|
/* create and initialize udc instance */
|
|
udc_dev = (udc_t*) malloc(sizeof(udc_t));
|
|
ASSERT(udc_dev);
|
|
|
|
/* initialize everything to 0 */
|
|
memset(udc_dev, 0 , sizeof(udc_t));
|
|
|
|
/* malloc control data buffers */
|
|
udc_dev->ctrl_rx_buf = memalign(CACHE_LINE, ROUNDUP(UDC_CONTROL_RX_BUF_SIZE, CACHE_LINE));
|
|
ASSERT(udc_dev->ctrl_rx_buf);
|
|
|
|
udc_dev->ctrl_tx_buf = memalign(CACHE_LINE, ROUNDUP(UDC_CONTROL_TX_BUF_SIZE, CACHE_LINE));
|
|
ASSERT(udc_dev->ctrl_tx_buf);
|
|
|
|
/* initialize string id */
|
|
udc_dev->next_string_id = 1;
|
|
|
|
/* Initialize ept data */
|
|
/* alloc table to assume EP0 In/OUT are already allocated.*/
|
|
udc_dev->ept_alloc_table = EPT_TX(0) | EPT_RX(0);
|
|
udc_dev->ept_list = NULL;
|
|
|
|
usb30_init(dev_info);
|
|
|
|
/* register descriptors */
|
|
udc_register_language_desc(udc_dev);
|
|
udc_register_device_desc_usb_20(udc_dev, dev_info);
|
|
udc_register_device_desc_usb_30(udc_dev, dev_info);
|
|
udc_register_bos_desc(udc_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* application registers its gadget by calling this func.
|
|
* gadget == interface descriptor
|
|
*/
|
|
int usb30_udc_register_gadget(struct udc_gadget *gadget)
|
|
{
|
|
ASSERT(gadget);
|
|
|
|
/* check if already registered */
|
|
if (udc_dev->gadget)
|
|
{
|
|
ERR("\nonly one gadget supported\n");
|
|
return -1;
|
|
}
|
|
|
|
/* create our configuration descriptors based on this gadget data */
|
|
udc_register_config_desc_usb20(udc_dev, gadget, TYPE_CONFIGURATION);
|
|
udc_register_config_desc_usb30(udc_dev, gadget);
|
|
|
|
/* save the gadget */
|
|
udc_dev->gadget = gadget;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* udc_start: */
|
|
int usb30_udc_start(void)
|
|
{
|
|
/* 19. run
|
|
* enable device to receive SOF packets and
|
|
* respond to control transfers on EP0 and generate events.
|
|
*/
|
|
dwc_device_run(udc_dev->dwc, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Control data rx callback. Called by DWC layer when it receives control
|
|
* data from host.
|
|
*/
|
|
void udc_control_rx_callback(void *context, unsigned actual, int status)
|
|
{
|
|
udc_t *udc = (udc_t *) context;
|
|
|
|
/* Force reload of buffer update by controller from memory */
|
|
arch_invalidate_cache_range((addr_t) udc->ctrl_rx_buf, actual);
|
|
|
|
/* TODO: for now, there is only one 3-stage write during 3.0 enumeration
|
|
* (SET_SEL), which causes this callback. Ideally, set_periodic() must
|
|
* be based on which control rx just happened.
|
|
* Also, the value of 0x65 should depend on the data received for SET_SEL.
|
|
* For now, this value works just fine.
|
|
*/
|
|
dwc_device_set_periodic_param(udc->dwc, 0x65);
|
|
}
|
|
|
|
/* lookup request name for debug purposes */
|
|
static const char *reqname(uint32_t r)
|
|
{
|
|
switch (r) {
|
|
case GET_STATUS:
|
|
return "GET_STATUS";
|
|
case CLEAR_FEATURE:
|
|
return "CLEAR_FEATURE";
|
|
case SET_FEATURE:
|
|
return "SET_FEATURE";
|
|
case SET_ADDRESS:
|
|
return "SET_ADDRESS";
|
|
case GET_DESCRIPTOR:
|
|
return "GET_DESCRIPTOR";
|
|
case SET_DESCRIPTOR:
|
|
return "SET_DESCRIPTOR";
|
|
case GET_CONFIGURATION:
|
|
return "GET_CONFIGURATION";
|
|
case SET_CONFIGURATION:
|
|
return "SET_CONFIGURATION";
|
|
case GET_INTERFACE:
|
|
return "GET_INTERFACE";
|
|
case SET_INTERFACE:
|
|
return "SET_INTERFACE";
|
|
case SET_SEL:
|
|
return "SET_SEL";
|
|
default:
|
|
return "*UNKNOWN*";
|
|
}
|
|
}
|
|
|
|
/* callback function called by DWC layer when a setup packed is received.
|
|
* the return value tells dwc layer whether this setup pkt results in
|
|
* a 2-stage or a 3-stage control transfer or stall.
|
|
*/
|
|
static int udc_handle_setup(void *context, uint8_t *data)
|
|
{
|
|
udc_t *udc = (udc_t *) context;
|
|
uint32_t len;
|
|
|
|
ASSERT(udc);
|
|
|
|
dwc_dev_t *dwc = udc->dwc;
|
|
ASSERT(dwc);
|
|
|
|
struct setup_packet s = *((struct setup_packet*) data);
|
|
|
|
DBG("\n SETUP request: \n type = 0x%x \n request = 0x%x \n value = 0x%x"
|
|
" \n index = 0x%x \n length = 0x%x\n",
|
|
s.type, s.request, s.value, s.index, s.length);
|
|
|
|
switch (SETUP(s.type, s.request))
|
|
{
|
|
case SETUP(ENDPOINT_READ, GET_STATUS):
|
|
case SETUP(INTERFACE_READ, GET_STATUS):
|
|
case SETUP(DEVICE_READ, GET_STATUS):
|
|
{
|
|
if (s.length == 2) {
|
|
|
|
uint16_t status = 0;
|
|
if (s.type == DEVICE_READ || (s.type == ENDPOINT_READ && stall_ep == true))
|
|
status = 1; /* Self-powered is set for device read and Halt bit set for end point read */
|
|
|
|
len = 2;
|
|
|
|
if (udc->usb_state == UDC_CONFIGURED_STATE && udc->speed == UDC_SPEED_SS)
|
|
{
|
|
if (s.type == DEVICE_READ && dwc_device_u1_enabled(dwc))
|
|
status |= (1 << 2); /* Set D2 to indicate U1 is enabled */
|
|
if (s.type == DEVICE_READ && dwc_device_u2_enabled(dwc))
|
|
status |= (1 << 3); /* Set D3 to indicate U3 is enabled */
|
|
}
|
|
|
|
/* copy to tx buffer */
|
|
memcpy(udc->ctrl_tx_buf, &status, len);
|
|
|
|
/* flush buffer to main memory before queueing the request */
|
|
arch_clean_invalidate_cache_range((addr_t) udc->ctrl_tx_buf, len);
|
|
|
|
dwc_transfer_request(udc->dwc,
|
|
0,
|
|
DWC_EP_DIRECTION_IN,
|
|
udc->ctrl_tx_buf,
|
|
len,
|
|
NULL,
|
|
NULL);
|
|
|
|
return DWC_SETUP_3_STAGE;
|
|
}
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_READ, GET_DESCRIPTOR):
|
|
{
|
|
DBG("\n DEVICE_READ : GET_DESCRIPTOR: value = %x\n", s.value);
|
|
|
|
/* setup usb ep0-IN to send our device descriptor */
|
|
struct udc_descriptor *desc;
|
|
/* Device Qualifier */
|
|
if (((s.value >> 8) == TYPE_DEVICE_QUALIFIER) && (udc->speed != UDC_SPEED_SS))
|
|
{
|
|
struct usb_qualifier_desc qual = {0};
|
|
qual.bLength = sizeof(qual);
|
|
qual.bDescriptorType = TYPE_DEVICE_QUALIFIER;
|
|
qual.bcdUSB = 0x0200; /* USB2.0 version */
|
|
qual.bDeviceClass = udc_dev->gadget->ifc_class;
|
|
qual.bDeviceSubClass = udc_dev->gadget->ifc_subclass;
|
|
qual.bDeviceProtocol = udc_dev->gadget->ifc_protocol;
|
|
qual.bMaxPacketSize0 = (udc_dev->speed == UDC_SPEED_HS) ? 64 : 512;
|
|
qual.bNumConfigurations = 1;
|
|
qual.bReserved = 0;
|
|
|
|
if (sizeof(qual) > s.length)
|
|
len = s.length;
|
|
else
|
|
len = sizeof(qual);
|
|
|
|
/* copy to tx buffer */
|
|
memcpy(udc->ctrl_tx_buf, (void *)&qual, len);
|
|
|
|
/* flush buffer to main memory before queueing the request */
|
|
arch_clean_invalidate_cache_range((addr_t) udc->ctrl_tx_buf, len);
|
|
|
|
dwc_transfer_request(udc->dwc,
|
|
0,
|
|
DWC_EP_DIRECTION_IN,
|
|
udc->ctrl_tx_buf,
|
|
len,
|
|
NULL,
|
|
NULL);
|
|
|
|
return DWC_SETUP_3_STAGE;
|
|
}
|
|
if (((s.value >> 8) == TYPE_OTHER_SPEED_CONFIG) && (udc->speed != UDC_SPEED_SS)) /* Other speed config */
|
|
{
|
|
if (!udc_other_speed_cfg)
|
|
{
|
|
udc_register_config_desc_usb20(udc, udc->gadget, TYPE_OTHER_SPEED_CONFIG);
|
|
udc_other_speed_cfg = true;
|
|
}
|
|
}
|
|
|
|
for (desc = udc->desc_list; desc; desc = desc->next)
|
|
{
|
|
/* tag must match the value AND
|
|
* if speed is SS, desc must comply with 30 spec OR
|
|
* if speed is not SS, desc must comply with 20 spec.
|
|
*/
|
|
if ((desc->tag == s.value) &&
|
|
(((udc->speed == UDC_SPEED_SS) && (desc->spec & UDC_DESC_SPEC_30)) ||
|
|
((udc->speed != UDC_SPEED_SS) && (desc->spec & UDC_DESC_SPEC_20)))
|
|
)
|
|
{
|
|
if (desc->len > s.length)
|
|
len = s.length;
|
|
else
|
|
len = desc->len;
|
|
|
|
/* copy to tx buffer */
|
|
memcpy(udc->ctrl_tx_buf, desc->data, len);
|
|
|
|
/* flush buffer to main memory before queueing the request */
|
|
arch_clean_invalidate_cache_range((addr_t) udc->ctrl_tx_buf, len);
|
|
|
|
dwc_transfer_request(udc->dwc,
|
|
0,
|
|
DWC_EP_DIRECTION_IN,
|
|
udc->ctrl_tx_buf,
|
|
len,
|
|
NULL,
|
|
NULL);
|
|
|
|
return DWC_SETUP_3_STAGE;
|
|
}
|
|
}
|
|
DBG("\n Did not find matching descriptor: = 0x%x", s.value);
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_READ, GET_CONFIGURATION):
|
|
{
|
|
DBG("\n DEVICE_READ : GET_CONFIGURATION");
|
|
|
|
if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
|
|
|
|
len = 1;
|
|
|
|
/* copy to tx buffer */
|
|
memcpy(udc->ctrl_tx_buf, &udc->config_selected, len);
|
|
|
|
/* flush buffer to main memory before queueing the request */
|
|
arch_clean_invalidate_cache_range((addr_t) udc->ctrl_tx_buf, len);
|
|
|
|
dwc_transfer_request(udc->dwc,
|
|
0,
|
|
DWC_EP_DIRECTION_IN,
|
|
udc->ctrl_tx_buf,
|
|
len,
|
|
NULL,
|
|
NULL);
|
|
|
|
return DWC_SETUP_3_STAGE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
|
|
{
|
|
DBG("\n DEVICE_WRITE : SET_CONFIGURATION");
|
|
|
|
/* select configuration 1 */
|
|
/* Return the config if configuration value is not 0 and move the state to
|
|
* configured state
|
|
*/
|
|
if (s.value) {
|
|
struct udc_endpoint *ept;
|
|
/* enable endpoints */
|
|
for (ept = udc->ept_list; ept; ept = ept->next) {
|
|
if (ept->num == 0)
|
|
continue;
|
|
else
|
|
{
|
|
/* add this ep to dwc ep list */
|
|
dwc_ep_t *ep = (dwc_ep_t *) malloc(sizeof(dwc_ep_t));
|
|
|
|
if(!ep)
|
|
{
|
|
dprintf(CRITICAL, "udc_handle_setup: DEVICE_WRITE : SET_CONFIGURATION malloc failed for ep\n");
|
|
ASSERT(0);
|
|
}
|
|
|
|
ep->number = ept->num;
|
|
ep->dir = ept->in;
|
|
ep->type = ept->type;
|
|
ep->max_pkt_size = ept->maxpkt;
|
|
ep->burst_size = ept->maxburst;
|
|
ep->zlp = 0;
|
|
ep->trb_count = ept->trb_count;
|
|
ep->trb = ept->trb;
|
|
|
|
dwc_device_add_ep(dwc, ep);
|
|
|
|
if(ep)
|
|
free(ep);
|
|
}
|
|
}
|
|
|
|
/* now that we have saved the non-control EP details, set config */
|
|
dwc_device_set_configuration(dwc);
|
|
if (udc->speed == UDC_SPEED_SS)
|
|
dwc_device_accept_u1u2(dwc);
|
|
|
|
/* inform client that we are configured. */
|
|
udc->gadget->notify(udc_dev->gadget, UDC_EVENT_ONLINE);
|
|
|
|
udc->config_selected = 1;
|
|
udc->usb_state = UDC_CONFIGURED_STATE;
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
else if (s.value == 0)
|
|
{
|
|
/* 0 == de-configure. */
|
|
udc->config_selected = 0;
|
|
DBG("\n\n CONFIG = 0 !!!!!!!!!\n\n");
|
|
/* If config value is '0' change the state to addressed state */
|
|
udc->usb_state = UDC_ADDRESSED_STATE;
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_WRITE, SET_ADDRESS):
|
|
{
|
|
DBG("\n DEVICE_WRITE : SET_ADDRESS");
|
|
|
|
dwc_device_set_addr(dwc, s.value);
|
|
udc->usb_state = UDC_ADDRESSED_STATE;
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
break;
|
|
case SETUP(INTERFACE_WRITE, SET_INTERFACE):
|
|
{
|
|
DBG("\n INTERFACE_WRITE : SET_INTERFACE");
|
|
/* if we ack this everything hangs */
|
|
/* per spec, STALL is valid if there is not alt func */
|
|
goto stall;
|
|
}
|
|
break;
|
|
case SETUP(INTERFACE_WRITE, SET_FEATURE):
|
|
{
|
|
DBG("\n INTERFACE_WRITE : SET_FEATURE");
|
|
if (s.value == FUNCTION_SUSPEND && udc->speed == UDC_SPEED_SS)
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
break;
|
|
case SETUP(INTERFACE_READ, GET_INTERFACE):
|
|
{
|
|
DBG("\n INTERFACE_READ : GET_INTERFACE");
|
|
/* per spec, STALL is valid if there is not alt func */
|
|
goto stall;
|
|
}
|
|
break;
|
|
case SETUP(ENDPOINT_WRITE, SET_FEATURE):
|
|
{
|
|
DBG("\n ENDPOINT_WRITE : SET_FEATURE");
|
|
if (s.value == ENDPOINT_HALT)
|
|
{
|
|
uint8_t usb_epnum;
|
|
uint8_t dir;
|
|
|
|
usb_epnum = (s.index & USB_EP_NUM_MASK);
|
|
dir = ((s.index & USB_EP_DIR_MASK) == USB_EP_DIR_IN) ? 0x1 : 0x0;
|
|
dwc_ep_cmd_stall(dwc, DWC_EP_PHY_NUM(usb_epnum, dir));
|
|
stall_ep = true;
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
else
|
|
goto stall;
|
|
}
|
|
case SETUP(DEVICE_WRITE, SET_FEATURE):
|
|
{
|
|
DBG("\n DEVICE_WRITE : SET_FEATURE");
|
|
|
|
if (s.value == TEST_MODE)
|
|
{
|
|
dwc->test_mode = s.index;
|
|
|
|
switch(dwc->test_mode)
|
|
{
|
|
case TEST_J:
|
|
case TEST_K:
|
|
case TEST_SE0_NAK:
|
|
case TEST_PACKET:
|
|
case TEST_FORCE_ENABLE:
|
|
/* Upper byte of Windex contain test mode */
|
|
dwc->test_mode >>= 8;
|
|
dwc->is_test_mode = true;
|
|
break;
|
|
default:
|
|
DBG("\n Unknown test mode: %x\n", dwc->test_mode);
|
|
}
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
if (udc->usb_state == UDC_CONFIGURED_STATE && udc->speed == UDC_SPEED_SS)
|
|
{
|
|
/* Set U1 & U2 only in configured state */
|
|
if (s.value == U1_ENABLE)
|
|
{
|
|
dwc_device_enable_u1(dwc, 1);
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
if (s.value == U2_ENABLE)
|
|
{
|
|
dwc_device_enable_u2(dwc, 1);
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
}
|
|
goto stall;
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_WRITE, CLEAR_FEATURE):
|
|
{
|
|
DBG("\n DEVICE_WRITE : CLEAR_FEATURE");
|
|
/* Clear U1 & U2 only in configured state */
|
|
if (udc->usb_state == UDC_CONFIGURED_STATE && udc->speed == UDC_SPEED_SS)
|
|
{
|
|
if (s.value == U1_ENABLE)
|
|
{
|
|
dwc_device_enable_u1(dwc, 0);
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
if (s.value == U2_ENABLE)
|
|
{
|
|
dwc_device_enable_u2(dwc, 0);
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
}
|
|
goto stall;
|
|
}
|
|
break;
|
|
case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE):
|
|
{
|
|
uint8_t usb_epnum;
|
|
uint8_t dir;
|
|
|
|
DBG("\n ENDPOINT_WRITE : CLEAR_FEATURE");
|
|
|
|
/*
|
|
* setup packet received from the host has
|
|
* index field containing information about the USB
|
|
* endpoint as below:
|
|
* __________________________________
|
|
* | (7) | (6 - 4) | (3 - 0) |
|
|
* |DIR | Reserved | EP number |
|
|
* |______|_____________|_____________|
|
|
*/
|
|
usb_epnum = (s.index & USB_EP_NUM_MASK);
|
|
dir = ((s.index & USB_EP_DIR_MASK) == USB_EP_DIR_IN) ? 0x1 : 0x0;
|
|
|
|
/*
|
|
* Convert the logical ep number to physical before
|
|
* sending the clear stall command.
|
|
* As per the data book we use fixed mapping as
|
|
* below:
|
|
* physical ep 0 --> logical ep0 OUT
|
|
* physical ep 1 --> logical ep0 IN
|
|
* physical ep 2 --> logical ep 1 OUT
|
|
* physical ep 3 --> logical ep 1 IN
|
|
* :
|
|
* :
|
|
* physical ep 30 --> logical ep 15 OUT
|
|
* physical ep 31 --> logical ep 15 IN
|
|
*/
|
|
dwc_ep_cmd_clear_stall(dwc, DWC_EP_PHY_NUM(usb_epnum, dir));
|
|
stall_ep = false;
|
|
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_WRITE, SET_SEL):
|
|
{
|
|
DBG("\n DEVICE_WRITE : SET_SEL");
|
|
|
|
/* this is 3-stage write. need to receive data of s.length size. */
|
|
if (s.length > 0) {
|
|
dwc_transfer_request(udc->dwc,
|
|
0,
|
|
DWC_EP_DIRECTION_OUT,
|
|
udc->ctrl_rx_buf,
|
|
UDC_CONTROL_RX_BUF_SIZE,
|
|
udc_control_rx_callback,
|
|
(void *) udc);
|
|
return DWC_SETUP_3_STAGE;
|
|
}
|
|
else
|
|
{
|
|
/* length must be non-zero */
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
break;
|
|
case SETUP(DEVICE_WRITE, SET_ISOCH_DELAY):
|
|
{
|
|
DBG("\n DEVICE_WRITE: SET_ISOCH_DELAY\n");
|
|
return DWC_SETUP_2_STAGE;
|
|
}
|
|
break;
|
|
default:
|
|
/* some of the requests from host are not handled, add a debug
|
|
* for the command not being handled, this is not fatal
|
|
*/
|
|
DBG("\n Unknown setup req.\n type = 0x%x value = %d index = %d"
|
|
" length = %d\n", s.type, s.value, s.index, s.length);
|
|
}
|
|
|
|
stall:
|
|
ERR("\nSTALL. Unsupported setup req: %s %d %d %d %d %d\n",
|
|
reqname(s.request), s.type, s.request, s.value, s.index, s.length);
|
|
|
|
return DWC_SETUP_ERROR;
|
|
}
|
|
|
|
/* Callback function called by DWC layer when a request to transfer data
|
|
* on non-control EP is completed.
|
|
*/
|
|
void udc_request_complete(void *context, uint32_t actual, int status)
|
|
{
|
|
struct udc_request *req = ((udc_t *) context)->queued_req;
|
|
|
|
DBG("\n UDC: udc_request_callback: xferred %d bytes status = %d\n",
|
|
actual, status);
|
|
|
|
/* clear the queued request. */
|
|
((udc_t *) context)->queued_req = NULL;
|
|
|
|
if (req->complete)
|
|
{
|
|
req->complete(req, actual, status);
|
|
}
|
|
|
|
DBG("\n UDC: udc_request_callback: done fastboot callback\n");
|
|
}
|
|
|
|
/* App interface to queue in data transfer requests for control and data ep */
|
|
int usb30_udc_request_queue(struct udc_endpoint *ept, struct udc_request *req)
|
|
{
|
|
int ret;
|
|
dwc_dev_t *dwc_dev = udc_dev->dwc;
|
|
|
|
/* ensure device is initialized before queuing request */
|
|
ASSERT(dwc_dev);
|
|
|
|
/* if device is not configured, return error */
|
|
if(udc_dev->config_selected == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* only one request at a time is supported.
|
|
* check if a request is already queued.
|
|
*/
|
|
if(udc_dev->queued_req)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
DBG("\n udc_request_queue: entry: ep_usb_num = %d", ept->num);
|
|
|
|
/* save the queued request. */
|
|
udc_dev->queued_req = req;
|
|
|
|
ret = dwc_transfer_request(dwc_dev,
|
|
ept->num,
|
|
ept->in ? DWC_EP_DIRECTION_IN : DWC_EP_DIRECTION_OUT,
|
|
req->buf,
|
|
req->length,
|
|
udc_request_complete,
|
|
(void *) udc_dev);
|
|
|
|
DBG("\n udc_request_queue: exit: ep_usb_num = %d", ept->num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* For HS device should have the version number as 0x0200.
|
|
* Update the minor version to 0x00 when we receive the connect
|
|
* event with HS or FS mode
|
|
*/
|
|
static void udc_update_usb20_desc(udc_t *udc)
|
|
{
|
|
struct udc_descriptor *desc= NULL;
|
|
if (udc_ss_capable)
|
|
return;
|
|
|
|
for (desc = udc->desc_list; desc; desc = desc->next)
|
|
{
|
|
if (desc->spec == UDC_DESC_SPEC_20 && desc->data[1] == TYPE_DEVICE)
|
|
desc->data[2] = 0x00; /* usb spec minor rev */
|
|
}
|
|
}
|
|
|
|
static void udc_update_ep_desc(udc_t *udc, uint16_t max_pkt_sz_bulk)
|
|
{
|
|
struct udc_descriptor *desc= NULL;
|
|
struct udc_endpoint *ept = NULL;
|
|
|
|
/*
|
|
* By default the bulk EP are registered with 512 Bytes
|
|
* as the max packet size. As per SS spec the max packet
|
|
* size for bulk is 1024. Some hosts treat the descriptor
|
|
* as invalid if the packet size is < 1024. Update the
|
|
* descriptors once we are notifed with SS connect event
|
|
*/
|
|
for (desc = udc->desc_list; desc; desc = desc->next)
|
|
{
|
|
if (desc->data[EP_BULK_IN_INDEX] == EP_TYPE_BULK)
|
|
{
|
|
desc->data[EP_BULK_IN_INDEX + 1] = max_pkt_sz_bulk;
|
|
desc->data[EP_BULK_IN_INDEX + 2] = max_pkt_sz_bulk >> 8;
|
|
}
|
|
|
|
if (desc->data[EP_BULK_OUT_INDEX] == EP_TYPE_BULK)
|
|
{
|
|
desc->data[EP_BULK_OUT_INDEX + 1] = max_pkt_sz_bulk;
|
|
desc->data[EP_BULK_OUT_INDEX + 2] = max_pkt_sz_bulk >> 8;
|
|
}
|
|
}
|
|
|
|
for (ept = udc->ept_list; ept; ept = ept->next)
|
|
{
|
|
ept->maxpkt = max_pkt_sz_bulk;
|
|
}
|
|
}
|
|
|
|
/* callback function called by dwc layer if any dwc event occurs */
|
|
void udc_dwc_notify(void *context, dwc_notify_event_t event)
|
|
{
|
|
udc_t *udc = (udc_t *) context;
|
|
uint32_t max_pkt_size = 0;
|
|
|
|
switch (event)
|
|
{
|
|
case DWC_NOTIFY_EVENT_CONNECTED_LS:
|
|
udc->speed = UDC_SPEED_LS;
|
|
break;
|
|
case DWC_NOTIFY_EVENT_CONNECTED_FS:
|
|
udc->speed = UDC_SPEED_FS;
|
|
/* For FS connection update the ep descriptor
|
|
* with FS max packet size
|
|
*/
|
|
max_pkt_size = 64;
|
|
udc_update_ep_desc(udc, max_pkt_size);
|
|
/* Update the spec version for FS */
|
|
udc_update_usb20_desc(udc);
|
|
break;
|
|
case DWC_NOTIFY_EVENT_CONNECTED_HS:
|
|
udc->speed = UDC_SPEED_HS;
|
|
/* For HS connection update the ep descriptor
|
|
* with HS max packet size
|
|
*/
|
|
max_pkt_size = 512;
|
|
udc_update_ep_desc(udc, max_pkt_size);
|
|
/* Update the spec version for HS */
|
|
udc_update_usb20_desc(udc);
|
|
break;
|
|
case DWC_NOTIFY_EVENT_CONNECTED_SS:
|
|
udc->speed = UDC_SPEED_SS;
|
|
/* For SS connection update the ep descriptor
|
|
* with SS max packet size
|
|
*/
|
|
max_pkt_size = 1024;
|
|
udc_ss_capable = true;
|
|
udc_update_ep_desc(udc, max_pkt_size);
|
|
break;
|
|
case DWC_NOTIFY_EVENT_DISCONNECTED:
|
|
case DWC_NOTIFY_EVENT_OFFLINE:
|
|
udc->config_selected = 0;
|
|
if (udc->gadget && udc->gadget->notify)
|
|
udc->gadget->notify(udc->gadget, UDC_EVENT_OFFLINE);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
|
|
/******************* Function related to descriptor allocation etc.************/
|
|
|
|
static struct udc_endpoint *_udc_endpoint_alloc(uint8_t num,
|
|
uint8_t in,
|
|
uint8_t type,
|
|
uint16_t max_pkt)
|
|
{
|
|
struct udc_endpoint *ept;
|
|
udc_t *udc = udc_dev;
|
|
|
|
ept = malloc(sizeof(*ept));
|
|
ASSERT(ept);
|
|
|
|
ept->maxpkt = max_pkt;
|
|
ept->num = num;
|
|
ept->type = type;
|
|
ept->in = !!in;
|
|
ept->maxburst = 4; /* no performance improvement is seen beyond burst size of 4 */
|
|
ept->trb_count = 66; /* each trb can transfer (16MB - 1). 65 for 1GB transfer + 1 for roundup/zero length pkt. */
|
|
ept->trb = memalign(lcm(CACHE_LINE, 16), ROUNDUP(ept->trb_count*sizeof(dwc_trb_t), CACHE_LINE)); /* TRB must be aligned to 16 */
|
|
ASSERT(ept->trb);
|
|
|
|
/* push it on top of ept_list */
|
|
ept->next = udc->ept_list;
|
|
udc->ept_list = ept;
|
|
|
|
return ept;
|
|
}
|
|
|
|
/* Called to create non-control in/out End Point structures by the APP */
|
|
struct udc_endpoint *usb30_udc_endpoint_alloc(unsigned type, unsigned maxpkt)
|
|
{
|
|
struct udc_endpoint *ept;
|
|
uint8_t in;
|
|
uint8_t n;
|
|
udc_t *udc = udc_dev;
|
|
|
|
if (type == UDC_TYPE_BULK_IN) {
|
|
in = 1;
|
|
type = EP_TYPE_BULK;
|
|
} else if (type == UDC_TYPE_BULK_OUT) {
|
|
in = 0;
|
|
type = EP_TYPE_BULK;
|
|
} else if (type == UDC_TYPE_INTR_IN) {
|
|
in = 1;
|
|
type = EP_TYPE_INTERRUPT;
|
|
} else if (type == UDC_TYPE_INTR_OUT){
|
|
in = 0;
|
|
type = EP_TYPE_INTERRUPT;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
for (n = 1; n < 16; n++) {
|
|
uint32_t bit = in ? EPT_TX(n) : EPT_RX(n);
|
|
if (udc->ept_alloc_table & bit)
|
|
continue;
|
|
ept = _udc_endpoint_alloc(n, in, type, maxpkt);
|
|
if (ept)
|
|
udc->ept_alloc_table |= bit;
|
|
return ept;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* create config + interface + ep desc for 2.0 */
|
|
static void udc_register_config_desc_usb20(udc_t *udc,
|
|
struct udc_gadget *gadget, uint8_t type)
|
|
{
|
|
uint8_t *data;
|
|
uint16_t size;
|
|
struct udc_descriptor *desc;
|
|
|
|
ASSERT(udc);
|
|
ASSERT(gadget);
|
|
|
|
/* create our configuration descriptor */
|
|
|
|
/* size is the total size of (config + interface + all EPs) descriptor */
|
|
size = UDC_DESC_SIZE_CONFIGURATION +
|
|
UDC_DESC_SIZE_INTERFACE +
|
|
(gadget->ifc_endpoints*UDC_DESC_SIZE_ENDPOINT);
|
|
|
|
desc = udc_descriptor_alloc(type, 0, size, UDC_DESC_SPEC_20);
|
|
|
|
data = desc->data;
|
|
|
|
/* Config desc */
|
|
data[0] = 0x09;
|
|
data[1] = type;
|
|
data[2] = size;
|
|
data[3] = size >> 8;
|
|
data[4] = 0x01; /* number of interfaces */
|
|
data[5] = 0x01; /* configuration value */
|
|
data[6] = 0x00; /* configuration string */
|
|
data[7] = 0xC0; /* attributes: reserved and self-powered set */
|
|
data[8] = 0x00; /* max power: 0ma since we are self powered */
|
|
data += 9;
|
|
|
|
/* Interface desc */
|
|
data[0] = 0x09;
|
|
data[1] = TYPE_INTERFACE;
|
|
data[2] = 0x00; /* ifc number */
|
|
data[3] = 0x00; /* alt number */
|
|
data[4] = gadget->ifc_endpoints;
|
|
data[5] = gadget->ifc_class;
|
|
data[6] = gadget->ifc_subclass;
|
|
data[7] = gadget->ifc_protocol;
|
|
data[8] = udc_string_desc_alloc(udc, gadget->ifc_string);
|
|
data += 9;
|
|
|
|
for (uint8_t n = 0; n < gadget->ifc_endpoints; n++) {
|
|
udc_ept_desc_fill(gadget->ept[n], data, type);
|
|
data += UDC_DESC_SIZE_ENDPOINT;
|
|
}
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
/* create config + interface + ep desc for 3.0 */
|
|
static void udc_register_config_desc_usb30(udc_t *udc,
|
|
struct udc_gadget *gadget)
|
|
{
|
|
uint8_t *data;
|
|
uint16_t size;
|
|
struct udc_descriptor *desc;
|
|
|
|
ASSERT(udc);
|
|
ASSERT(gadget);
|
|
|
|
/* create our configuration descriptor */
|
|
|
|
/* size is the total size of (config + interface + all EPs) descriptor */
|
|
size = UDC_DESC_SIZE_CONFIGURATION +
|
|
UDC_DESC_SIZE_INTERFACE +
|
|
(gadget->ifc_endpoints*(UDC_DESC_SIZE_ENDPOINT + UDC_DESC_SIZE_ENDPOINT_COMP));
|
|
|
|
desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size, UDC_DESC_SPEC_30);
|
|
|
|
data = desc->data;
|
|
|
|
/* Config desc */
|
|
data[0] = 0x09;
|
|
data[1] = TYPE_CONFIGURATION;
|
|
data[2] = size;
|
|
data[3] = size >> 8;
|
|
data[4] = 0x01; /* number of interfaces */
|
|
data[5] = 0x01; /* configuration value */
|
|
data[6] = 0x00; /* configuration string */
|
|
data[7] = 0xC0; /* attributes: reserved and self-powered set */
|
|
data[8] = 0x00; /* max power: 0ma since we are self powered */
|
|
data += 9;
|
|
|
|
/* Interface desc */
|
|
data[0] = 0x09;
|
|
data[1] = TYPE_INTERFACE;
|
|
data[2] = 0x00; /* ifc number */
|
|
data[3] = 0x00; /* alt number */
|
|
data[4] = gadget->ifc_endpoints;
|
|
data[5] = gadget->ifc_class;
|
|
data[6] = gadget->ifc_subclass;
|
|
data[7] = gadget->ifc_protocol;
|
|
data[8] = udc_string_desc_alloc(udc, gadget->ifc_string);
|
|
data += 9;
|
|
|
|
for (uint8_t n = 0; n < gadget->ifc_endpoints; n++)
|
|
{
|
|
/* fill EP desc */
|
|
udc_ept_desc_fill(gadget->ept[n], data, 0);
|
|
data += UDC_DESC_SIZE_ENDPOINT;
|
|
|
|
/* fill EP companion desc */
|
|
udc_ept_comp_desc_fill(gadget->ept[n], data);
|
|
data += UDC_DESC_SIZE_ENDPOINT_COMP;
|
|
}
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
|
|
static void udc_register_device_desc_usb_20(udc_t *udc,
|
|
struct udc_device *dev_info)
|
|
{
|
|
uint8_t *data;
|
|
struct udc_descriptor *desc;
|
|
|
|
/* create our device descriptor */
|
|
desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18, UDC_DESC_SPEC_20);
|
|
data = desc->data;
|
|
|
|
/* data 0 and 1 is filled by descriptor alloc routine.
|
|
* fill in the remaining entries.
|
|
*/
|
|
data[2] = 0x10; /* usb spec minor rev */
|
|
data[3] = 0x02; /* usb spec major rev */
|
|
data[4] = 0x00; /* class */
|
|
data[5] = 0x00; /* subclass */
|
|
data[6] = 0x00; /* protocol */
|
|
data[7] = 0x40; /* max packet size on ept 0 */
|
|
|
|
memcpy(data + 8, &dev_info->vendor_id, sizeof(short));
|
|
memcpy(data + 10, &dev_info->product_id, sizeof(short));
|
|
memcpy(data + 12, &dev_info->version_id, sizeof(short));
|
|
|
|
data[14] = udc_string_desc_alloc(udc, dev_info->manufacturer);
|
|
data[15] = udc_string_desc_alloc(udc, dev_info->product);
|
|
data[16] = udc_string_desc_alloc(udc, dev_info->serialno);
|
|
data[17] = 1; /* number of configurations */
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
static void udc_register_device_desc_usb_30(udc_t *udc, struct udc_device *dev_info)
|
|
{
|
|
uint8_t *data;
|
|
struct udc_descriptor *desc;
|
|
|
|
/* create our device descriptor */
|
|
desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18, UDC_DESC_SPEC_30);
|
|
data = desc->data;
|
|
|
|
/* data 0 and 1 is filled by descriptor alloc routine.
|
|
* fill in the remaining entries.
|
|
*/
|
|
data[2] = 0x00; /* usb spec minor rev */
|
|
data[3] = 0x03; /* usb spec major rev */
|
|
data[4] = 0x00; /* class */
|
|
data[5] = 0x00; /* subclass */
|
|
data[6] = 0x00; /* protocol */
|
|
data[7] = 0x09; /* max packet size on ept 0 */
|
|
memcpy(data + 8, &dev_info->vendor_id, sizeof(short));
|
|
memcpy(data + 10, &dev_info->product_id, sizeof(short));
|
|
memcpy(data + 12, &dev_info->version_id, sizeof(short));
|
|
data[14] = udc_string_desc_alloc(udc, dev_info->manufacturer);
|
|
data[15] = udc_string_desc_alloc(udc, dev_info->product);
|
|
data[16] = udc_string_desc_alloc(udc, dev_info->serialno);
|
|
data[17] = 1; /* number of configurations */
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
static void udc_register_bos_desc(udc_t *udc)
|
|
{
|
|
uint8_t *data;
|
|
struct udc_descriptor *desc;
|
|
|
|
/* create our device descriptor */
|
|
desc = udc_descriptor_alloc(TYPE_BOS, 0, 0x16, UDC_DESC_SPEC_30); /* 22 is total length of bos + other descriptors inside it */
|
|
data = desc->data;
|
|
|
|
/* data 0 and 1 is filled by descriptor alloc routine.
|
|
* fill in the remaining entries.
|
|
*/
|
|
data[0] = 0x05; /* BOS desc len */
|
|
data[1] = TYPE_BOS; /* BOS desc type */
|
|
data[2] = 0x16; /* total len of bos desc and its sub desc */
|
|
data[3] = 0x00; /* total len of bos desc and its sub desc */
|
|
data[4] = 0x02; /* num of sub desc inside bos */
|
|
|
|
/* USB2.0 extension Descriptor */
|
|
data[5] = 0x07; /* Size of USB2.0 extension desc */
|
|
data[6] = 0x10; /* Device capability desc */
|
|
data[7] = 0x02; /* USB2.0 extension descriptor */
|
|
data[8] = 0x02; /* LPM mode */
|
|
data[9] = 0x00; /* Reserved */
|
|
data[10] = 0x00; /* Reserved */
|
|
data[11] = 0x00; /* Reserved */
|
|
|
|
/* Super Speed device capability */
|
|
data[12] = 0x0A; /* desc len */
|
|
data[13] = 0x10; /* Device Capability desc */
|
|
data[14] = 0x03; /* 3 == SuperSpeed capable */
|
|
data[15] = 0x00; /* Attribute: latency tolerance msg: No */
|
|
data[16] = 0x0F; /* Supported Speeds (bit mask): LS, FS, HS, SS */
|
|
data[17] = 0x00; /* Reserved part of supported wSupportedSpeeds */
|
|
data[18] = 0x01; /* lowest supported speed with full functionality: FS */
|
|
data[19] = 0x00; /* U1 device exit latency */
|
|
data[20] = 0x00; /* U2 device exit latency (lsb) */
|
|
data[21] = 0x00; /* U2 device exit latency (msb) */
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
static void udc_register_language_desc(udc_t *udc)
|
|
{
|
|
/* create and register a language table descriptor */
|
|
/* language 0x0409 is US English */
|
|
struct udc_descriptor *desc = udc_descriptor_alloc(TYPE_STRING,
|
|
0,
|
|
4,
|
|
UDC_DESC_SPEC_20 | UDC_DESC_SPEC_30);
|
|
desc->data[2] = 0x09;
|
|
desc->data[3] = 0x04;
|
|
udc_descriptor_register(udc, desc);
|
|
}
|
|
|
|
static void udc_ept_desc_fill(struct udc_endpoint *ept, uint8_t *data, uint8_t type)
|
|
{
|
|
uint16_t max_pkt_sz = 0;
|
|
|
|
/* For other speed configuration, populate the max packet size for the other speed
|
|
* mode. For eg: if currently host is operating in HS mode, return the configuration
|
|
* for FS mode
|
|
*/
|
|
if (type == TYPE_OTHER_SPEED_CONFIG && udc_dev->speed != UDC_SPEED_SS)
|
|
max_pkt_sz = (udc_dev->speed == UDC_SPEED_FS) ? 512 : 64;
|
|
else
|
|
max_pkt_sz = ept->maxpkt;
|
|
|
|
data[0] = 7;
|
|
data[1] = TYPE_ENDPOINT;
|
|
data[2] = ept->num | (ept->in ? 0x80 : 0x00);
|
|
data[3] = 0x02; /* bulk -- the only kind we support */
|
|
data[4] = max_pkt_sz;
|
|
data[5] = max_pkt_sz >> 8;
|
|
data[6] = 0; /* bInterval: must be 0 for bulk. */
|
|
}
|
|
|
|
static void udc_ept_comp_desc_fill(struct udc_endpoint *ept, uint8_t *data)
|
|
{
|
|
data[0] = 6; /* bLength */
|
|
data[1] = TYPE_SS_EP_COMP; /* ep type */
|
|
data[2] = ept->maxburst; /* maxBurst */
|
|
data[3] = 0x0; /* maxStreams */
|
|
data[4] = 0x0; /* wBytesPerInterval */
|
|
data[5] = 0x0; /* wBytesPerInterval */
|
|
}
|
|
|
|
static uint8_t udc_string_desc_alloc(udc_t *udc, const char *str)
|
|
{
|
|
uint32_t len;
|
|
struct udc_descriptor *desc;
|
|
uint8_t *data;
|
|
|
|
if (udc->next_string_id > 255)
|
|
return 0;
|
|
|
|
if (!str)
|
|
return 0;
|
|
|
|
len = strlen(str);
|
|
desc = udc_descriptor_alloc(TYPE_STRING,
|
|
udc->next_string_id,
|
|
len * 2 + 2,
|
|
UDC_DESC_SPEC_20 | UDC_DESC_SPEC_30);
|
|
if (!desc)
|
|
return 0;
|
|
udc->next_string_id++;
|
|
|
|
/* expand ascii string to utf16 */
|
|
data = desc->data + 2;
|
|
while (len-- > 0) {
|
|
*data++ = *str++;
|
|
*data++ = 0;
|
|
}
|
|
|
|
udc_descriptor_register(udc, desc);
|
|
return desc->tag & 0xff;
|
|
}
|
|
|
|
|
|
static struct udc_descriptor *udc_descriptor_alloc(uint32_t type,
|
|
uint32_t num,
|
|
uint32_t len,
|
|
udc_desc_spec_t spec)
|
|
{
|
|
struct udc_descriptor *desc;
|
|
if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
|
|
return 0;
|
|
|
|
desc = malloc(sizeof(struct udc_descriptor) + len);
|
|
ASSERT(desc);
|
|
|
|
desc->next = 0;
|
|
desc->tag = (type << 8) | num;
|
|
desc->len = len;
|
|
desc->spec = spec;
|
|
|
|
/* descriptor data */
|
|
desc->data[0] = len;
|
|
desc->data[1] = type;
|
|
|
|
return desc;
|
|
}
|
|
|
|
static void udc_descriptor_register(udc_t *udc, struct udc_descriptor *desc)
|
|
{
|
|
desc->next = udc->desc_list;
|
|
udc->desc_list = desc;
|
|
}
|
|
|
|
|
|
struct udc_request *usb30_udc_request_alloc(void)
|
|
{
|
|
struct udc_request *req;
|
|
|
|
req = malloc(sizeof(*req));
|
|
ASSERT(req);
|
|
|
|
req->buf = 0;
|
|
req->length = 0;
|
|
req->complete = NULL;
|
|
req->context = 0;
|
|
|
|
return req;
|
|
}
|
|
|
|
void usb30_udc_request_free(struct udc_request *req)
|
|
{
|
|
free(req);
|
|
}
|
|
|
|
void usb30_udc_endpoint_free(struct udc_endpoint *ept)
|
|
{
|
|
/* TODO */
|
|
}
|
|
|
|
int usb30_udc_stop(void)
|
|
{
|
|
dwc_device_run(udc_dev->dwc, 0);
|
|
|
|
return 0;
|
|
}
|