1326 lines
28 KiB
C
Executable File
1326 lines
28 KiB
C
Executable File
#include "usb3.h"
|
|
#include "usb3_hw.h"
|
|
#include "usb3_drv.h"
|
|
#include "usb3_prot.h"
|
|
#include "usb3_pcd.h"
|
|
#include <linux/string.h>
|
|
|
|
extern void usb3_bulk_out_transfer(void *dev);
|
|
|
|
const struct usb_interface_descriptor
|
|
intf = {
|
|
sizeof(struct usb_interface_descriptor), /* bLength */
|
|
UDESC_INTERFACE, /* bDescriptorType */
|
|
|
|
0, /* bInterfaceNumber */
|
|
0, /* bAlternateSetting */
|
|
2, /* bNumEndpoints */
|
|
USB_CLASS_VENDOR_SPEC, /* bInterfaceClass */
|
|
USB_SC_VENDOR_SPEC, /* bInterfaceSubClass */
|
|
USB_PR_VENDOR_SPEC, /* bInterfaceProtocol */
|
|
0, /* iInterface */
|
|
};
|
|
|
|
/* Two endpoint descriptors: bulk-in, bulk-out. */
|
|
|
|
const struct usb_ss_ep_comp_descriptor
|
|
ep_comp = {
|
|
sizeof(struct usb_ss_ep_comp_descriptor), /* bLength */
|
|
UDESC_SS_USB_COMPANION, /* bDescriptorType */
|
|
0, /* bMaxBurst */
|
|
0, /* bmAttributes */
|
|
0, /* wBytesPerInterval */
|
|
};
|
|
|
|
const struct usb_endpoint_descriptor
|
|
hs_bulk_in = {
|
|
sizeof(struct usb_endpoint_descriptor), /* bLength */
|
|
UDESC_ENDPOINT, /* bDescriptorType */
|
|
|
|
UE_DIR_IN | USB3_BULK_IN_EP, /* bEndpointAddress */
|
|
USB_ENDPOINT_XFER_BULK, /* bmAttributes */
|
|
0x200, /* wMaxPacketSize: 512 of high-speed */
|
|
0, /* bInterval */
|
|
};
|
|
|
|
const struct usb_endpoint_descriptor
|
|
hs_bulk_out = {
|
|
sizeof(struct usb_endpoint_descriptor), /* bLength */
|
|
UDESC_ENDPOINT, /* bDescriptorType */
|
|
|
|
UE_DIR_OUT | USB3_BULK_OUT_EP, /* bEndpointAddress */
|
|
USB_ENDPOINT_XFER_BULK, /* bmAttributes */
|
|
0x200, /* wMaxPacketSize: 512 of high-speed */
|
|
1, /* bInterval */
|
|
};
|
|
|
|
const struct usb_endpoint_descriptor
|
|
ss_bulk_in = {
|
|
sizeof(struct usb_endpoint_descriptor), /* bLength */
|
|
UDESC_ENDPOINT, /* bDescriptorType */
|
|
|
|
UE_DIR_IN | USB3_BULK_IN_EP, /* bEndpointAddress */
|
|
USB_ENDPOINT_XFER_BULK, /* bmAttributes */
|
|
0x400, /* wMaxPacketSize: 1024 of super-speed */
|
|
0, /* bInterval */
|
|
};
|
|
|
|
const struct usb_endpoint_descriptor
|
|
ss_bulk_out = {
|
|
sizeof(struct usb_endpoint_descriptor), /* bLength */
|
|
UDESC_ENDPOINT, /* bDescriptorType */
|
|
|
|
UE_DIR_OUT | USB3_BULK_OUT_EP, /* bEndpointAddress */
|
|
USB_ENDPOINT_XFER_BULK, /* bmAttributes */
|
|
0x400, /* wMaxPacketSize: 1024 of super-speed */
|
|
0, /* bInterval */
|
|
};
|
|
|
|
/** The BOS Descriptor */
|
|
|
|
const struct usb_dev_cap_20_ext_desc
|
|
cap1 = {
|
|
sizeof(struct usb_dev_cap_20_ext_desc), /* bLength */
|
|
UDESC_DEVICE_CAPABILITY, /* bDescriptorType */
|
|
USB_DEVICE_CAPABILITY_20_EXTENSION, /* bDevCapabilityType */
|
|
0x2, /* bmAttributes */
|
|
};
|
|
|
|
const struct usb_dev_cap_ss_usb
|
|
cap2 = {
|
|
sizeof(struct usb_dev_cap_ss_usb), /* bLength */
|
|
UDESC_DEVICE_CAPABILITY, /* bDescriptorType */
|
|
USB_DEVICE_CAPABILITY_SS_USB, /* bDevCapabilityType */
|
|
0x0, /* bmAttributes */
|
|
(USB_DC_SS_USB_SPEED_SUPPORT_SS |
|
|
USB_DC_SS_USB_SPEED_SUPPORT_HIGH), /* wSpeedsSupported */
|
|
0x2, /* bFunctionalitySupport */
|
|
/* @todo set these to correct value */
|
|
0xa, /* bU1DevExitLat */
|
|
0x100, /* wU2DevExitLat */
|
|
};
|
|
|
|
const struct usb_dev_cap_container_id
|
|
cap3 = {
|
|
sizeof(struct usb_dev_cap_container_id),/* bLength */
|
|
UDESC_DEVICE_CAPABILITY, /* bDescriptorType */
|
|
USB_DEVICE_CAPABILITY_CONTAINER_ID, /* bDevCapabilityType */
|
|
0, /* bReserved */
|
|
/* @todo Create UUID */
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* containerID */
|
|
};
|
|
|
|
const struct wusb_bos_desc
|
|
bos = {
|
|
sizeof(struct wusb_bos_desc), /* bLength */
|
|
UDESC_BOS, /* bDescriptorType */
|
|
(sizeof(struct wusb_bos_desc) /* wTotalLength */
|
|
+ sizeof(cap1) + sizeof(cap2) + sizeof(cap3)),
|
|
3, /* bNumDeviceCaps */
|
|
};
|
|
|
|
usb3_pcd_ep_t *usb3_get_out_ep(usb3_pcd_t *pcd, uint32_t ep_num)
|
|
{
|
|
if (ep_num == 0) {
|
|
return &pcd->ep0;
|
|
} else {
|
|
return &pcd->out_ep;
|
|
}
|
|
}
|
|
|
|
usb3_pcd_ep_t *usb3_get_in_ep(usb3_pcd_t *pcd, uint32_t ep_num)
|
|
{
|
|
if (ep_num == 0) {
|
|
return &pcd->ep0;
|
|
} else {
|
|
return &pcd->in_ep;
|
|
}
|
|
}
|
|
|
|
usb3_pcd_ep_t *usb3_get_ep_by_addr(usb3_pcd_t *pcd, uint16_t index)
|
|
{
|
|
uint32_t ep_num = UE_GET_ADDR(index);
|
|
|
|
if (UE_GET_DIR(index) == UE_DIR_IN) {
|
|
return usb3_get_in_ep(pcd, ep_num);
|
|
} else {
|
|
return usb3_get_out_ep(pcd, ep_num);
|
|
}
|
|
}
|
|
|
|
|
|
void usb3_ep_clear_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep)
|
|
{
|
|
usb3_dev_ep_regs_t *ep_reg;
|
|
|
|
if (ep->is_in) {
|
|
ep_reg = ep->in_ep_reg;
|
|
} else {
|
|
ep_reg = ep->out_ep_reg;
|
|
}
|
|
|
|
usb3_dep_cstall(pcd, ep_reg);
|
|
}
|
|
|
|
|
|
uint32_t usb3_get_device_speed(usb3_pcd_t *pcd)
|
|
{
|
|
uint32_t dsts;
|
|
uint32_t speed = USB_SPEED_UNKNOWN;
|
|
|
|
dsts = usb3_rd32(&pcd->dev_global_regs->dsts);
|
|
|
|
switch ((dsts >> USB3_DSTS_CONNSPD_SHIFT) &
|
|
(USB3_DSTS_CONNSPD_BITS >> USB3_DSTS_CONNSPD_SHIFT)) {
|
|
case USB3_SPEED_HS_PHY_30MHZ_OR_60MHZ:
|
|
speed = USB_SPEED_HIGH;
|
|
break;
|
|
|
|
case USB3_SPEED_FS_PHY_30MHZ_OR_60MHZ:
|
|
case USB3_SPEED_FS_PHY_48MHZ:
|
|
speed = USB_SPEED_FULL;
|
|
break;
|
|
|
|
case USB3_SPEED_LS_PHY_6MHZ:
|
|
speed = USB_SPEED_LOW;
|
|
break;
|
|
|
|
case USB3_SPEED_SS_PHY_125MHZ_OR_250MHZ:
|
|
speed = USB_SPEED_SUPER;
|
|
break;
|
|
}
|
|
|
|
return speed;
|
|
}
|
|
|
|
void usb3_pcd_set_speed(usb3_pcd_t *pcd, int speed)
|
|
{
|
|
/* Set the MPS of EP0 based on the connection speed */
|
|
switch (speed) {
|
|
case USB_SPEED_SUPER:
|
|
pcd->ep0.maxpacket = 512;
|
|
pcd->in_ep.maxpacket = USB3_MAX_PACKET_SIZE;
|
|
pcd->out_ep.maxpacket = USB3_MAX_PACKET_SIZE;
|
|
break;
|
|
|
|
case USB_SPEED_HIGH:
|
|
pcd->ep0.maxpacket = 64;
|
|
pcd->in_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE;
|
|
pcd->out_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE;
|
|
break;
|
|
|
|
case USB_SPEED_FULL:
|
|
pcd->ep0.maxpacket = 64;
|
|
pcd->in_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE;
|
|
pcd->out_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE;
|
|
break;
|
|
|
|
case USB_SPEED_LOW:
|
|
pcd->ep0.maxpacket = 8;
|
|
pcd->in_ep.maxpacket = 0;
|
|
pcd->out_ep.maxpacket = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void usb3_ep_set_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep)
|
|
{
|
|
usb3_dev_ep_regs_t *ep_reg;
|
|
|
|
if (ep->is_in) {
|
|
ep_reg = ep->in_ep_reg;
|
|
} else {
|
|
ep_reg = ep->out_ep_reg;
|
|
}
|
|
|
|
usb3_dep_sstall(pcd, ep_reg);
|
|
}
|
|
|
|
static void ep0_do_stall(usb3_pcd_t *pcd)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
|
|
/* Stall EP0 IN & OUT simultanelusly */
|
|
ep0->is_in = 1;
|
|
usb3_ep_set_stall(pcd, ep0);
|
|
ep0->is_in = 0;
|
|
usb3_ep_set_stall(pcd, ep0);
|
|
|
|
/* Prepare for the next setup transfer */
|
|
ep0->stopped = 1;
|
|
pcd->ep0state = EP0_IDLE;
|
|
usb3_ep0_out_start(pcd);
|
|
}
|
|
|
|
static void do_clear_halt(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep)
|
|
{
|
|
usb3_ep_clear_stall(pcd, ep);
|
|
|
|
if (ep->stopped) {
|
|
ep->stopped = 0;
|
|
}
|
|
}
|
|
|
|
void usb3_pcd_ep_enable(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep)
|
|
{
|
|
/*
|
|
* Activate the EP
|
|
*/
|
|
ep->stopped = 0;
|
|
ep->xfer_started = 0;
|
|
|
|
/* Set initial data PID. */
|
|
if (ep->type == USB3_EP_TYPE_BULK)
|
|
ep->data_pid_start = 0;
|
|
|
|
usb3_ep_activate(pcd, ep);
|
|
}
|
|
|
|
static void usb3_do_get_status(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
uint8_t *status = pcd->ep0_status_buf;
|
|
usb3_pcd_ep_t *ep;
|
|
|
|
if (ctrl.wLength != 2) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
|
|
case UT_DEVICE:
|
|
*status = 0; /* Bus powered */
|
|
|
|
if (pcd->speed == USB_SPEED_SUPER) {
|
|
if (pcd->state == USB3_STATE_CONFIGURED) {
|
|
if (usb3_u1_enabled(pcd))
|
|
*status |= 1 << 2;
|
|
|
|
if (usb3_u2_enabled(pcd))
|
|
*status |= 1 << 3;
|
|
|
|
*status |= pcd->ltm_enable << 4;
|
|
}
|
|
}
|
|
|
|
*(status + 1) = 0;
|
|
break;
|
|
|
|
case UT_INTERFACE:
|
|
*status = 0;
|
|
*(status + 1) = 0;
|
|
break;
|
|
|
|
case UT_ENDPOINT:
|
|
ep = usb3_get_ep_by_addr(pcd, ctrl.wIndex);
|
|
|
|
/* @todo check for EP stall */
|
|
*status = ep->stopped;
|
|
*(status + 1) = 0;
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
pcd->ep0_req.bufdma = status;
|
|
pcd->ep0_req.length = 2;
|
|
pcd->ep0_req.actual = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
}
|
|
|
|
static void usb3_do_clear_feature(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
usb3_pcd_ep_t *ep;
|
|
|
|
switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
|
|
case UT_DEVICE:
|
|
switch (ctrl.wValue) {
|
|
case UF_DEVICE_REMOTE_WAKEUP:
|
|
break;
|
|
|
|
case UF_TEST_MODE:
|
|
/* @todo Add CLEAR_FEATURE for TEST modes. */
|
|
break;
|
|
|
|
case UF_U1_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
usb3_disable_u1(pcd);
|
|
break;
|
|
|
|
case UF_U2_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
usb3_disable_u2(pcd);
|
|
break;
|
|
|
|
case UF_LTM_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED ||
|
|
ctrl.wIndex != 0) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
pcd->ltm_enable = 0;
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case UT_INTERFACE:
|
|
/* if FUNCTION_SUSPEND ... */
|
|
if (ctrl.wValue) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case UT_ENDPOINT:
|
|
ep = usb3_get_ep_by_addr(pcd, ctrl.wIndex);
|
|
if (ctrl.wValue != UF_ENDPOINT_HALT) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
do_clear_halt(pcd, ep);
|
|
break;
|
|
}
|
|
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
}
|
|
|
|
static void usb3_do_set_feature(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
usb3_pcd_ep_t *ep;
|
|
|
|
switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
|
|
case UT_DEVICE:
|
|
switch (ctrl.wValue) {
|
|
case UF_DEVICE_REMOTE_WAKEUP:
|
|
break;
|
|
|
|
case UF_TEST_MODE:
|
|
break;
|
|
|
|
case UF_DEVICE_B_HNP_ENABLE:
|
|
break;
|
|
|
|
case UOTG_NTF_HOST_REL:
|
|
break;
|
|
|
|
case UOTG_B3_RSP_ENABLE:
|
|
break;
|
|
|
|
case UF_DEVICE_A_HNP_SUPPORT:
|
|
/* RH port supports HNP */
|
|
break;
|
|
|
|
case UF_DEVICE_A_ALT_HNP_SUPPORT:
|
|
/* other RH port does */
|
|
break;
|
|
|
|
case UF_U1_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
usb3_enable_u1(pcd);
|
|
break;
|
|
|
|
case UF_U2_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
usb3_enable_u2(pcd);
|
|
break;
|
|
|
|
case UF_LTM_ENABLE:
|
|
if (pcd->speed != USB_SPEED_SUPER ||
|
|
pcd->state != USB3_STATE_CONFIGURED ||
|
|
ctrl.wIndex != 0) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
pcd->ltm_enable = 1;
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case UT_INTERFACE:
|
|
/* if FUNCTION_SUSPEND ... */
|
|
if (ctrl.wValue) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case UT_ENDPOINT:
|
|
ep = usb3_get_ep_by_addr(pcd, ctrl.wIndex);
|
|
if (ctrl.wValue != UF_ENDPOINT_HALT) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
ep->stopped = 1;
|
|
usb3_ep_set_stall(pcd, ep);
|
|
break;
|
|
}
|
|
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
}
|
|
|
|
static void usb3_do_set_address(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
|
|
if (ctrl.bmRequestType == UT_DEVICE) {
|
|
usb3_set_address(pcd, ctrl.wValue);
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
if (ctrl.wValue) {
|
|
pcd->state = USB3_STATE_ADDRESSED;
|
|
} else {
|
|
pcd->state = USB3_STATE_DEFAULT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usb3_do_set_config(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
uint16_t wvalue = ctrl.wValue;
|
|
usb3_pcd_ep_t *ep;
|
|
|
|
if (ctrl.bmRequestType != (UT_WRITE | UT_STANDARD |
|
|
UT_DEVICE)) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
if (!wvalue ||(wvalue == CONFIG_VALUE)) {
|
|
pcd->new_config = wvalue;
|
|
/* Set new configuration */
|
|
if (wvalue) {
|
|
/* Activate bulk in endpoint */
|
|
ep = &pcd->in_ep;
|
|
usb3_pcd_ep_enable(pcd, ep);
|
|
|
|
/* Activate bulk out endpoint */
|
|
ep = &pcd->out_ep;
|
|
usb3_pcd_ep_enable(pcd, ep);
|
|
|
|
/* Prepare for next bulk transfer */
|
|
usb3_bulk_out_transfer((void *)pcd);
|
|
usb3_bulk_in_transfer((void *)pcd, "start download process.");
|
|
pcd->state = USB3_STATE_CONFIGURED;
|
|
} else {
|
|
pcd->state = USB3_STATE_ADDRESSED;
|
|
}
|
|
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
} else {
|
|
ep0_do_stall(pcd);
|
|
}
|
|
}
|
|
|
|
static void usb3_do_get_config(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
uint8_t *status = pcd->ep0_status_buf;
|
|
|
|
if (ctrl.bmRequestType != (UT_READ | UT_STANDARD |
|
|
UT_DEVICE)) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
/* Notify host the current config value */
|
|
*status = pcd->new_config;
|
|
|
|
pcd->ep0_req.bufdma = status;
|
|
pcd->ep0_req.length = 1;
|
|
pcd->ep0_req.actual = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
}
|
|
|
|
static void usb3_do_get_descriptor(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
usb3_device_t *usb3_dev = pcd->usb3_dev;
|
|
uint8_t dt = ctrl.wValue >> 8;
|
|
uint8_t index = (uint8_t)ctrl.wValue;
|
|
uint16_t len = ctrl.wLength;
|
|
uint8_t *buf = pcd->ep0_status_buf;
|
|
uint16_t value = 0;
|
|
|
|
if (ctrl.bmRequestType != (UT_READ | UT_STANDARD |
|
|
UT_DEVICE)) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
switch (dt) {
|
|
case UDESC_DEVICE:
|
|
{
|
|
struct usb_device_descriptor *dev =
|
|
(struct usb_device_descriptor *)usb3_dev->dev_desc;
|
|
|
|
dev->bLength = sizeof(struct usb_device_descriptor);
|
|
dev->bDescriptorType = UDESC_DEVICE;
|
|
|
|
dev->bDeviceClass = 0;
|
|
dev->bDeviceSubClass = 0;
|
|
dev->bDeviceProtocol = 0;
|
|
|
|
if (pcd->speed == USB_SPEED_SUPER) {
|
|
dev->bcdUSB = 0x300;
|
|
dev->bMaxPacketSize0 = 9; // NOTE! 2 ^ 9 = 512 for USB3
|
|
} else if (pcd->speed == USB_SPEED_HIGH) {
|
|
dev->bcdUSB = 0x0200;
|
|
dev->bMaxPacketSize0 = pcd->ep0.maxpacket;
|
|
} else {
|
|
dev->bcdUSB = 0x0110;
|
|
dev->bMaxPacketSize0 = pcd->ep0.maxpacket;
|
|
}
|
|
|
|
dev->idVendor = 0x1D6B;
|
|
dev->idProduct = 0xA001;
|
|
dev->bcdDevice = 0x0100;
|
|
|
|
dev->iManufacturer = STRING_MANUFACTURER;
|
|
dev->iProduct = STRING_PRODUCT;
|
|
dev->iSerialNumber = 0;
|
|
|
|
dev->bNumConfigurations = 1;
|
|
|
|
value = sizeof(struct usb_device_descriptor);
|
|
usb3_memcpy((void *)buf, (void *)dev, value);
|
|
}
|
|
break;
|
|
|
|
case UDESC_DEVICE_QUALIFIER:
|
|
{
|
|
struct usb_qualifier_descriptor *qual =
|
|
(struct usb_qualifier_descriptor *)buf;
|
|
struct usb_device_descriptor *dev =
|
|
(struct usb_device_descriptor *)usb3_dev->dev_desc;
|
|
|
|
qual->bLength = sizeof(*qual);
|
|
qual->bDescriptorType = UDESC_DEVICE_QUALIFIER;
|
|
qual->bcdUSB = dev->bcdUSB;
|
|
qual->bDeviceClass = dev->bDeviceClass;
|
|
qual->bDeviceSubClass = dev->bDeviceSubClass;
|
|
qual->bDeviceProtocol = dev->bDeviceProtocol;
|
|
qual->bMaxPacketSize0 = dev->bMaxPacketSize0;
|
|
qual->bNumConfigurations = 1;
|
|
qual->bRESERVED = 0;
|
|
|
|
value = sizeof(usb_qualifier_descriptor_t);
|
|
}
|
|
break;
|
|
|
|
case UDESC_CONFIG:
|
|
{
|
|
struct usb_config_descriptor *config =
|
|
(struct usb_config_descriptor *)buf;
|
|
|
|
config->bLength = sizeof(*config);
|
|
config->bDescriptorType = UDESC_CONFIG;
|
|
config->bNumInterfaces = 1;
|
|
config->bConfigurationValue = CONFIG_VALUE;
|
|
config->iConfiguration = 0;
|
|
config->bmAttributes = USB_CONFIG_ATT_ONE;
|
|
|
|
if (pcd->speed == USB_SPEED_SUPER) {
|
|
config->bMaxPower = USB_CONFIG_VBUS_DRAW / 8;
|
|
} else {
|
|
config->bMaxPower = USB_CONFIG_VBUS_DRAW / 2;
|
|
}
|
|
|
|
buf += sizeof(*config);
|
|
usb3_memcpy((void *)buf, (void *)&intf, sizeof(intf));
|
|
buf += sizeof(intf);
|
|
|
|
switch (pcd->speed) {
|
|
case USB_SPEED_SUPER:
|
|
usb3_memcpy((void *)buf, (void *)&ss_bulk_in, sizeof(ss_bulk_in));
|
|
buf += sizeof(ss_bulk_in);
|
|
usb3_memcpy((void *)buf, (void *)&ep_comp, sizeof(ep_comp));
|
|
buf += sizeof(ep_comp);
|
|
usb3_memcpy((void *)buf, (void *)&ss_bulk_out, sizeof(ss_bulk_out));
|
|
buf += sizeof(ss_bulk_out);
|
|
usb3_memcpy((void *)buf, (void *)&ep_comp, sizeof(ep_comp));
|
|
|
|
config->wTotalLength = sizeof(*config) + sizeof(intf)
|
|
+ sizeof(ss_bulk_in) + sizeof(ep_comp)
|
|
+ sizeof(ss_bulk_out) + sizeof(ep_comp);
|
|
break;
|
|
|
|
default: /* HS/FS */
|
|
{
|
|
struct usb_endpoint_descriptor *endp =
|
|
(struct usb_endpoint_descriptor *)buf;
|
|
|
|
usb3_memcpy((void *)buf, (void *)&hs_bulk_in, sizeof(hs_bulk_in));
|
|
(endp++)->wMaxPacketSize = pcd->in_ep.maxpacket;
|
|
buf += sizeof(hs_bulk_in);
|
|
usb3_memcpy((void *)buf, (void *)&hs_bulk_out, sizeof(hs_bulk_out));
|
|
(endp++)->wMaxPacketSize = pcd->out_ep.maxpacket;
|
|
}
|
|
config->wTotalLength = sizeof(*config) + sizeof(intf)
|
|
+ sizeof(hs_bulk_in) + sizeof(hs_bulk_out);
|
|
break;
|
|
}
|
|
value = config->wTotalLength;
|
|
}
|
|
break;
|
|
|
|
case UDESC_STRING:
|
|
{
|
|
switch (index) {
|
|
case STRING_LANGUAGE:
|
|
buf[0] = 0x04;
|
|
buf[1] = UDESC_STRING;
|
|
buf[2] = 0x09;
|
|
buf[3] = 0x04;
|
|
|
|
value = 0x04;
|
|
break;
|
|
|
|
case STRING_MANUFACTURER:
|
|
buf[0] = usb3_dev->string_manu_len + 2;
|
|
buf[1] = UDESC_STRING;
|
|
usb3_memcpy((void *)(buf+2), (void *)usb3_dev->string_manu,
|
|
usb3_dev->string_manu_len);
|
|
|
|
value = usb3_dev->string_manu_len + 2;
|
|
break;
|
|
|
|
case STRING_PRODUCT:
|
|
buf[0] = usb3_dev->string_prod_len + 2;
|
|
buf[1] = UDESC_STRING;
|
|
|
|
usb3_memcpy((void *)(buf+2), (void *)usb3_dev->string_prod,
|
|
usb3_dev->string_prod_len);
|
|
|
|
value = usb3_dev->string_prod_len + 2;
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UDESC_BOS:
|
|
if (pcd->speed != USB_SPEED_SUPER) {
|
|
/*
|
|
* The USB compliance test (USB 2.0 Command Verifier)
|
|
* issues this request. We should not run into the
|
|
* default path here. But return for now until
|
|
* the superspeed support is added.
|
|
*/
|
|
}
|
|
|
|
value = bos.wTotalLength;
|
|
|
|
usb3_memcpy((void *)buf, (void *)&bos, sizeof(bos));
|
|
buf += sizeof(bos);
|
|
usb3_memcpy((void *)buf, (void *)&cap1, sizeof(cap1));
|
|
buf += sizeof(cap1);
|
|
usb3_memcpy((void *)buf, (void *)&cap2, sizeof(cap2));
|
|
buf += sizeof(cap2);
|
|
usb3_memcpy((void *)buf, (void *)&cap3, sizeof(cap3));
|
|
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
pcd->ep0_req.bufdma = pcd->ep0_status_buf;
|
|
pcd->ep0_req.length = value < len ? value : len;
|
|
pcd->ep0_req.actual = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
}
|
|
|
|
void usb3_do_setup(usb3_pcd_t *pcd)
|
|
{
|
|
usb_device_request_t ctrl = pcd->ep0_setup_pkt->req;
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
uint16_t wlength;
|
|
|
|
wlength = ctrl.wLength;
|
|
|
|
ep0->stopped = 0;
|
|
ep0->three_stage = 1;
|
|
|
|
if (ctrl.bmRequestType & UE_DIR_IN) {
|
|
ep0->is_in = 1;
|
|
pcd->ep0state = EP0_IN_DATA_PHASE;
|
|
} else {
|
|
ep0->is_in = 0;
|
|
pcd->ep0state = EP0_OUT_DATA_PHASE;
|
|
}
|
|
|
|
if (wlength == 0) {
|
|
ep0->is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
ep0->three_stage = 0;
|
|
}
|
|
|
|
if ((UT_GET_TYPE(ctrl.bmRequestType)) != UT_STANDARD) {
|
|
ep0_do_stall(pcd);
|
|
return;
|
|
}
|
|
|
|
switch (ctrl.bRequest) {
|
|
case UR_GET_STATUS:
|
|
usb3_do_get_status(pcd);
|
|
break;
|
|
|
|
case UR_CLEAR_FEATURE:
|
|
usb3_do_clear_feature(pcd);
|
|
break;
|
|
|
|
case UR_SET_FEATURE:
|
|
usb3_do_set_feature(pcd);
|
|
break;
|
|
|
|
case UR_SET_ADDRESS:
|
|
usb3_do_set_address(pcd);
|
|
break;
|
|
|
|
case UR_SET_CONFIG:
|
|
usb3_do_set_config(pcd);
|
|
|
|
/* Must wait until SetConfig before accepting U1/U2 link
|
|
* control, otherwise we have problems with VIA hubs
|
|
*/
|
|
usb3_accept_u1(pcd);
|
|
usb3_accept_u2(pcd);
|
|
|
|
usb_info("usb enum done\n");
|
|
|
|
pcd->ltm_enable = 0;
|
|
break;
|
|
|
|
case UR_GET_CONFIG:
|
|
usb3_do_get_config(pcd);
|
|
break;
|
|
|
|
case UR_GET_DESCRIPTOR:
|
|
usb3_do_get_descriptor(pcd);
|
|
break;
|
|
|
|
case UR_SET_SEL:
|
|
/* For now this is a no-op */
|
|
pcd->ep0_req.bufdma = pcd->ep0_status_buf;
|
|
pcd->ep0_req.length = USB3_STATUS_BUF_SIZE;
|
|
pcd->ep0_req.actual = 0;
|
|
ep0->send_zlp = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
break;
|
|
|
|
case UR_SET_ISOC_DELAY:
|
|
/* For now this is a no-op */
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
break;
|
|
|
|
default:
|
|
ep0_do_stall(pcd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void usb3_os_get_trb(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep,
|
|
usb3_pcd_req_t *req)
|
|
{
|
|
/* If EP0, fill request with EP0 IN/OUT data TRB */
|
|
if (ep == &pcd->ep0) {
|
|
if (ep->is_in) {
|
|
req->trb = pcd->ep0_in_desc;
|
|
req->trbdma = (phys_addr_t)pcd->ep0_in_desc;
|
|
} else {
|
|
req->trb = pcd->ep0_out_desc;
|
|
req->trbdma = (phys_addr_t)pcd->ep0_out_desc;
|
|
}
|
|
/* Else fill request with TRB from the non-EP0 allocation */
|
|
} else {
|
|
req->trb = ep->ep_desc;
|
|
req->trbdma = (phys_addr_t)ep->ep_desc;
|
|
}
|
|
}
|
|
|
|
void usb3_ep0_start_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
usb3_dev_ep_regs_t *ep_reg;
|
|
usb3_dma_desc_t *desc;
|
|
phys_addr_t desc_dma;
|
|
uint32_t desc_type, len;
|
|
uint8_t tri;
|
|
|
|
/* Get the DMA Descriptor (TRB) for this request */
|
|
usb3_os_get_trb(pcd, ep0, req);
|
|
desc = req->trb;
|
|
desc_dma = req->trbdma;
|
|
|
|
if (ep0->is_in) {
|
|
/*
|
|
* Start DMA on EP0-IN
|
|
*/
|
|
ep_reg = ep0->in_ep_reg;
|
|
|
|
/* DMA Descriptor (TRB) setup */
|
|
len = req->length;
|
|
|
|
if (pcd->ep0state == EP0_IN_STATUS_PHASE) {
|
|
if (ep0->three_stage)
|
|
desc_type = USB3_DSCCTL_TRBCTL_STATUS_3;
|
|
else
|
|
desc_type = USB3_DSCCTL_TRBCTL_STATUS_2;
|
|
} else {
|
|
desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST;
|
|
}
|
|
|
|
usb3_fill_desc(desc, (phys_addr_t)req->bufdma,
|
|
len, 0, desc_type,
|
|
USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT |
|
|
USB3_DSCCTL_LST_BIT, 1);
|
|
/* Issue "DEPSTRTXFER" command to EP0-IN */
|
|
tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0);
|
|
ep0->tri_in = tri;
|
|
} else {
|
|
/*
|
|
* Start DMA on EP0-OUT
|
|
*/
|
|
ep_reg = ep0->out_ep_reg;
|
|
|
|
/* DMA Descriptor (TRB) setup */
|
|
len = (req->length + ep0->maxpacket - 1) &
|
|
~(ep0->maxpacket - 1);
|
|
|
|
if (pcd->ep0state == EP0_OUT_STATUS_PHASE) {
|
|
if (ep0->three_stage)
|
|
desc_type = USB3_DSCCTL_TRBCTL_STATUS_3;
|
|
else
|
|
desc_type = USB3_DSCCTL_TRBCTL_STATUS_2;
|
|
} else {
|
|
desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST;
|
|
}
|
|
|
|
usb3_fill_desc(desc, (phys_addr_t)req->bufdma,
|
|
len, 0, desc_type,
|
|
USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT |
|
|
USB3_DSCCTL_LST_BIT, 1);
|
|
/* Issue "DEPSTRTXFER" command to EP0-OUT */
|
|
tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0);
|
|
ep0->tri_out = tri;
|
|
}
|
|
}
|
|
|
|
static void ep0_continue_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
usb3_dev_ep_regs_t *ep_reg;
|
|
usb3_dma_desc_t *desc;
|
|
phys_addr_t desc_dma;
|
|
uint8_t tri;
|
|
|
|
/* It can be called to send a 0-length packet after the end of a transfer, so the code here
|
|
* only supports that case.
|
|
*/
|
|
|
|
if (ep0->is_in) {
|
|
desc = pcd->ep0_in_desc;
|
|
desc_dma = (phys_addr_t)(pcd->ep0_in_desc);
|
|
ep_reg = ep0->in_ep_reg;
|
|
|
|
/* DMA Descriptor Setup */
|
|
usb3_fill_desc(desc, (phys_addr_t)req->bufdma,
|
|
0, 0, USB3_DSCCTL_TRBCTL_NORMAL,
|
|
USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT |
|
|
USB3_DSCCTL_LST_BIT, 1);
|
|
|
|
tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0);
|
|
ep0->tri_in = tri;
|
|
}
|
|
}
|
|
|
|
void usb3_ep_start_transfer(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep)
|
|
{
|
|
usb3_pcd_req_t *req = &ep->req;
|
|
usb3_dev_ep_regs_t *ep_reg;
|
|
usb3_dma_desc_t *desc;
|
|
phys_addr_t desc_dma;
|
|
uint32_t len;
|
|
uint8_t tri;
|
|
|
|
/* Get the TRB for this request */
|
|
usb3_os_get_trb(pcd, ep, req);
|
|
|
|
ep->send_zlp = 0;
|
|
desc = req->trb;
|
|
desc_dma = req->trbdma;
|
|
|
|
if (ep->is_in) {
|
|
/* For IN, TRB length is just xfer length */
|
|
len = req->length;
|
|
} else {
|
|
/* For OUT, TRB length must be multiple of maxpacket */
|
|
/* Must be power of 2, use cheap AND */
|
|
len = (req->length + ep->maxpacket - 1)
|
|
& ~(ep->maxpacket - 1);
|
|
|
|
req->length = len;
|
|
}
|
|
|
|
/* DMA Descriptor Setup */
|
|
usb3_fill_desc(desc, (phys_addr_t)req->bufdma,
|
|
len, 0, USB3_DSCCTL_TRBCTL_NORMAL,
|
|
USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_IOC_BIT |
|
|
USB3_DSCCTL_LST_BIT, 1);
|
|
|
|
if (ep->is_in) {
|
|
/*
|
|
* Start DMA on EPn-IN
|
|
*/
|
|
ep_reg = ep->in_ep_reg;
|
|
/* Issue "DEPSTRTXFER" command to EP */
|
|
// printf("\n########%s,%d\n",__func__,__LINE__);
|
|
tri = usb3_dep_startxfer(pcd, ep_reg,
|
|
desc_dma, 0);
|
|
ep->tri_in = tri;
|
|
} else {
|
|
/*
|
|
* Start DMA on EPn-OUT
|
|
*/
|
|
ep_reg = ep->out_ep_reg;
|
|
#if 1
|
|
if (ep->xfer_started) {
|
|
/* Issue "DEPUPDTXFER" command to EP */
|
|
usb3_dep_updatexfer(pcd, ep_reg,
|
|
ep->tri_out);
|
|
} else {
|
|
#endif
|
|
/* Issue "DEPSTRTXFER" command to EP */
|
|
tri = usb3_dep_startxfer(pcd, ep_reg,
|
|
desc_dma, 0);
|
|
ep->tri_out = tri;
|
|
ep->xfer_started = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void usb3_bulk_out_transfer(void *dev)
|
|
{
|
|
usb3_pcd_t *pcd = (usb3_pcd_t *)dev;
|
|
usb3_pcd_ep_t *ep = &pcd->out_ep;
|
|
usb3_pcd_req_t *req = &ep->req;
|
|
|
|
/* req->actual = 0; */
|
|
req->length = ep->maxpacket;
|
|
req->bufdma = pcd->ss_bulk_buf;
|
|
req->complete = usb3_handle_protocol;
|
|
|
|
usb3_ep_start_transfer(pcd, ep);
|
|
|
|
return;
|
|
}
|
|
|
|
void usb3_bulk_out_continue_transfer(void *dev)
|
|
{
|
|
usb3_pcd_t *pcd = (usb3_pcd_t *)dev;
|
|
usb3_pcd_ep_t *ep = &pcd->out_ep;
|
|
|
|
usb3_ep_start_transfer(pcd, ep);
|
|
}
|
|
|
|
void usb_tx_status_complete(void *dev)
|
|
{
|
|
}
|
|
|
|
void usb3_bulk_in_transfer(void *dev, const char *status)
|
|
{
|
|
usb3_pcd_t *pcd = (usb3_pcd_t *)dev;
|
|
usb3_pcd_ep_t *ep = &pcd->in_ep;
|
|
usb3_pcd_req_t *req = &ep->req;
|
|
int len;
|
|
|
|
len = strlen(status)+1;
|
|
memset(req->bufdma, 0, 512);
|
|
memcpy(req->bufdma,status,len);
|
|
req->length = len;
|
|
|
|
/* req->actual = 0; */
|
|
req->complete = usb_tx_status_complete;
|
|
|
|
usb3_ep_start_transfer(pcd, ep);
|
|
}
|
|
|
|
static void ep0_complete_request(usb3_pcd_t *pcd, usb3_pcd_req_t *req,
|
|
usb3_dma_desc_t *desc, int status)
|
|
{
|
|
usb3_pcd_ep_t *ep = &pcd->ep0;
|
|
|
|
if (!req)
|
|
return;
|
|
|
|
if (pcd->ep0state == EP0_OUT_DATA_PHASE ||
|
|
pcd->ep0state == EP0_IN_DATA_PHASE) {
|
|
if (ep->is_in) {
|
|
if (usb3_get_xfercnt(desc) == 0) {
|
|
pcd->ep0.is_in = 0;
|
|
pcd->ep0state = EP0_OUT_WAIT_NRDY;
|
|
}
|
|
} else {
|
|
pcd->ep0.is_in = 1;
|
|
pcd->ep0state = EP0_IN_WAIT_NRDY;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void setup_in_status_phase(usb3_pcd_t *pcd, void *buf)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
|
|
if (pcd->ep0state == EP0_STALL) {
|
|
return;
|
|
}
|
|
|
|
ep0->is_in = 1;
|
|
pcd->ep0state = EP0_IN_STATUS_PHASE;
|
|
|
|
pcd->ep0_req.bufdma = buf;
|
|
pcd->ep0_req.length = 0;
|
|
pcd->ep0_req.actual = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
}
|
|
|
|
static void setup_out_status_phase(usb3_pcd_t *pcd, void *buf)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
|
|
if (pcd->ep0state == EP0_STALL) {
|
|
return;
|
|
}
|
|
|
|
ep0->is_in = 0;
|
|
pcd->ep0state = EP0_OUT_STATUS_PHASE;
|
|
|
|
pcd->ep0_req.bufdma = buf;
|
|
pcd->ep0_req.length = 0;
|
|
pcd->ep0_req.actual = 0;
|
|
usb3_ep0_start_transfer(pcd, &pcd->ep0_req);
|
|
}
|
|
|
|
void usb3_handle_ep0(usb3_pcd_t *pcd, usb3_pcd_req_t *req,
|
|
uint32_t event)
|
|
{
|
|
usb3_pcd_ep_t *ep0 = &pcd->ep0;
|
|
usb3_dma_desc_t *desc;
|
|
uint32_t byte_count, len;
|
|
uint32_t status;
|
|
|
|
switch (pcd->ep0state) {
|
|
case EP0_IN_DATA_PHASE:
|
|
if (!req)
|
|
req = &pcd->ep0_req;
|
|
desc = pcd->ep0_in_desc;
|
|
|
|
if (dwc_usb3_is_hwo(desc)) {
|
|
goto out;
|
|
}
|
|
|
|
status = usb3_get_xfersts(desc);
|
|
if (status & USB3_TRBRSP_SETUP_PEND) {
|
|
/* Start of a new Control transfer */
|
|
desc->status = 0;
|
|
}
|
|
|
|
byte_count = req->length - usb3_get_xfercnt(desc);
|
|
req->actual += byte_count;
|
|
req->bufdma += byte_count;
|
|
|
|
if (req->actual < req->length) {
|
|
/* IN CONTINUE, Stall EP0 */
|
|
ep0_do_stall(pcd);
|
|
} else if (ep0->send_zlp) {
|
|
/* CONTINUE TRANSFER IN ZLP */
|
|
ep0_continue_transfer(pcd, req);
|
|
ep0->send_zlp = 0;
|
|
} else {
|
|
/* COMPLETE IN TRANSFER */
|
|
ep0_complete_request(pcd, req, desc, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case EP0_OUT_DATA_PHASE:
|
|
if (!req)
|
|
req = &pcd->ep0_req;
|
|
desc = pcd->ep0_out_desc;
|
|
|
|
if (dwc_usb3_is_hwo(desc)) {
|
|
goto out;
|
|
}
|
|
|
|
status = usb3_get_xfersts(desc);
|
|
if (status & USB3_TRBRSP_SETUP_PEND) {
|
|
/* Start of a new Control transfer */
|
|
}
|
|
|
|
len = (req->length + ep0->maxpacket - 1) &
|
|
~(ep0->maxpacket - 1);
|
|
byte_count = len - usb3_get_xfercnt(desc);
|
|
req->actual += byte_count;
|
|
req->bufdma += byte_count;
|
|
|
|
if (ep0->send_zlp) {
|
|
/* CONTINUE TRANSFER OUT ZLP */
|
|
ep0_continue_transfer(pcd, req);
|
|
ep0->send_zlp = 0;
|
|
|
|
} else {
|
|
/* COMPLETE OUT TRANSFER */
|
|
ep0_complete_request(pcd, req, desc, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case EP0_IN_WAIT_NRDY:
|
|
case EP0_OUT_WAIT_NRDY:
|
|
if (ep0->is_in)
|
|
setup_in_status_phase(pcd, pcd->ep0_setup_pkt);
|
|
else
|
|
setup_out_status_phase(pcd, pcd->ep0_setup_pkt);
|
|
|
|
break;
|
|
|
|
case EP0_IN_STATUS_PHASE:
|
|
case EP0_OUT_STATUS_PHASE:
|
|
if (ep0->is_in)
|
|
desc = pcd->ep0_in_desc;
|
|
else
|
|
desc = pcd->ep0_out_desc;
|
|
ep0_complete_request(pcd, req, desc, 0);
|
|
|
|
pcd->ep0state = EP0_IDLE;
|
|
ep0->stopped = 1;
|
|
ep0->is_in = 0; /* OUT for next SETUP */
|
|
|
|
/* Prepare for more SETUP Packets */
|
|
usb3_ep0_out_start(pcd);
|
|
break;
|
|
|
|
case EP0_STALL:
|
|
break;
|
|
|
|
case EP0_IDLE:
|
|
break;
|
|
}
|
|
out:
|
|
return;
|
|
}
|
|
|
|
void usb3_os_handle_ep0(usb3_pcd_t *pcd, uint32_t event)
|
|
{
|
|
usb3_pcd_req_t *req = NULL;
|
|
|
|
if (pcd->ep0state == EP0_IDLE) {
|
|
usb3_do_setup(pcd);
|
|
} else {
|
|
usb3_handle_ep0(pcd, req, event);
|
|
}
|
|
}
|
|
|
|
void usb3_request_done(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep,
|
|
usb3_pcd_req_t *req, int status)
|
|
{
|
|
|
|
if (ep != &pcd->ep0)
|
|
req->trb = NULL;
|
|
if (req->complete)
|
|
req->complete(pcd);
|
|
|
|
req->actual = 0;
|
|
|
|
return;
|
|
}
|
|
extern int usb_out_open;
|
|
int usb3_ep_complete_request(usb3_pcd_t *pcd,
|
|
usb3_pcd_ep_t *ep, uint32_t event)
|
|
{
|
|
usb3_pcd_req_t *req = &ep->req;
|
|
usb3_dma_desc_t *desc = req->trb;
|
|
uint32_t byte_count;
|
|
int error = USB_NO_ERR;
|
|
|
|
ep->send_zlp = 0;
|
|
|
|
if (!desc) {
|
|
return USB_PROCESS_ERR;
|
|
}
|
|
#if 0
|
|
if (dwc_usb3_is_hwo(desc)) {
|
|
return USB_PROCESS_ERR;
|
|
}
|
|
#endif
|
|
if (ep->is_in) {
|
|
/* IN endpoint */
|
|
if (usb3_get_xfercnt(desc) == 0) {
|
|
req->actual += req->length;
|
|
}
|
|
/* Reset IN tri */
|
|
ep->tri_in = 0;
|
|
|
|
/* Complete the IN request */
|
|
usb3_request_done(pcd, ep, req, 0);
|
|
} else { /* OUT endpoint */
|
|
byte_count = req->length -
|
|
usb3_get_xfercnt(desc);
|
|
req->actual += byte_count;
|
|
req->bufdma += byte_count;
|
|
pcd->file_received += byte_count;
|
|
/* Reset OUT tri */
|
|
ep->tri_out = 0;
|
|
|
|
/* OUT transfer complete or not */
|
|
if ((byte_count < ep->maxpacket) ||
|
|
(pcd->file_capacity <= pcd->file_received - FRAME_HEAD_LEN)) {
|
|
/* Complete the OUT request */
|
|
usb3_request_done(pcd, ep, req, 0);
|
|
} else {
|
|
usb3_bulk_out_continue_transfer((void *)pcd);
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|