Yes, It's exactly the same issue with ci_udc driver, neither the gadget speed nor the status was not properly handled at reset state, we are luckly fixed this issue finally, here is the solution:
----------------------------------------------------------------------------------
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -374,7 +374,12 @@
static int ci_ep_disable(struct usb_ep *ep)
{
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ const struct usb_endpoint_descriptor *desc = ep->desc;
+ int num;
+ num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ writel(0, &udc->epctrl[num]);
ci_ep->desc = NULL;
ep->desc = NULL;
return 0;
@@ -598,7 +603,10 @@
* check will save a lot of debugging time.
*/
printf("%s: ep0 transaction already in progress\n", __func__);
- return -EPROTO;
+ return -EINPROGRESS;
+ }
+ if (USB_SPEED_UNKNOWN == controller.gadget.speed) {
+ return -ESHUTDOWN;
}
ret = ci_bounce(ci_req, in);
@@ -786,6 +794,27 @@
writel((1<<16) | (1 << 0), &udc->epctrl[0]);
}
+void ci_nuke(struct ci_ep *ep, int status)
+{
+ struct ci_req *req;
+
+ while(!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct ci_req, queue);
+ list_del_init(&req->queue);
+ req->req.status = status;
+ req->req.complete(&ep->ep, &req->req);
+ }
+ ep->req_primed = false;
+}
+
+static void throw_away_all_urb(struct ci_drv *cd)
+{
+ int i;
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ci_nuke(&cd->ep[i], -ECONNRESET);
+ }
+}
+
static void stop_activity(void)
{
int i, num, in;
@@ -820,6 +849,7 @@
unsigned n = readl(&udc->usbsts);
writel(n, &udc->usbsts);
int bit, i, num, in;
+ int speed = USB_SPEED_UNKNOWN;
n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
if (n == 0)
@@ -827,14 +857,21 @@
if (n & STS_URI) {
DBG("-- reset --\n");
+ controller.gadget.speed = speed;
stop_activity();
+ throw_away_all_urb(&controller);
+ if (controller.driver)
+ controller.driver->reset(&controller.gadget);
+ return;
}
- if (n & STS_SLI)
+ if (n & STS_SLI) {
+ if (controller.driver)
+ controller.driver->suspend(&controller.gadget);
DBG("-- suspend --\n");
-
+ }
if (n & STS_PCI) {
int max = 64;
- int speed = USB_SPEED_FULL;
+
#ifdef CONFIG_CI_UDC_HAS_HOSTPC
bit = (readl(&udc->hostpc1_devlc) >> 25) & 3;
@@ -842,15 +879,22 @@
bit = (readl(&udc->portsc) >> 26) & 3;
#endif
DBG("-- portchange %x %s\n", bit, (bit == 2) ? "High" : "Full");
- if (bit == 2) {
- speed = USB_SPEED_HIGH;
- max = 512;
+ if (3 == bit) {
+ if (controller.driver)
+ controller.driver->disconnect(&controller.gadget);
+ } else {
+ if (bit == 2) {
+ speed = USB_SPEED_HIGH;
+ max = 512;
+ } else {
+ speed = USB_SPEED_FULL;
+ }
+ for (i = 1; i < NUM_ENDPOINTS; i++) {
+ if (controller.ep[i].ep.maxpacket > max)
+ controller.ep[i].ep.maxpacket = max;
+ }
}
controller.gadget.speed = speed;
- for (i = 1; i < NUM_ENDPOINTS; i++) {
- if (controller.ep[i].ep.maxpacket > max)
- controller.ep[i].ep.maxpacket = max;
- }
}
if (n & STS_UEI)
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2052,7 +2052,13 @@
struct fsg_common *common = fsg->common;
/* Was this a real packet? Should it be ignored? */
- if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+ if (req->status) {
+ if (-ECONNRESET == req->status)
+ return 0;
+ return -EINVAL;
+ }
+
+ if (test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
return -EINVAL;
/* Is the CBW valid? */
--- a/drivers/usb/gadget/f_sdp.c
+++ b/drivers/usb/gadget/f_sdp.c
@@ -292,7 +292,7 @@
u8 *data = req->buf;
u8 report = data[0];
- if (status != 0) {
+ if ((status) && (-EINPROGRESS != status)) {
pr_err("Status: %d\n", status);
return;
}
@@ -378,7 +378,7 @@
u8 report = data[0];
int datalen = req->actual - 1;
- if (status != 0) {
+ if ((status) && (-EINPROGRESS != status)) {
pr_err("Status: %d\n", status);
return;
}
@@ -431,7 +431,7 @@
struct f_sdp *sdp = req->context;
int status = req->status;
- if (status != 0) {
+ if ((status) && (-EINPROGRESS != status)) {
pr_err("Status: %d\n", status);
return;
}