The following scenario: - use a third party tool - issue SetConfiguration, start a transfer - g_zero enqueues 4k reqs. Be rude and stop after a multiple of maxpacketsize but before the 4k - Assume the UDC can handle 4k transfer in one go and will interrupt after the 4k arrived or a short transfer. That the transfer is still "busy". - now soft the disconnect the device. - the UDC gets a SetConfiguration 0 and will disable all endpoints - disabling an endpoint means cancles all pending transfers. On DWC3 we issue a cancel transfer command and wait for the command/transfer to complete. That means we return immediately from ->disable(). The _gadget_ has to wait for the ->complete() callback. g_zero does not and the result is: | dwc3 dwc3.0: ep0out: Transfer Complete | dwc3 dwc3.0: Transfer Complete while ep0out in state 'Setup Phase' | dwc3 dwc3.0: Inspecting Setup Bytes | dwc3 dwc3.0: USB_REQ_SET_CONFIGURATION | zero gadget: reset config | dwc3 dwc3.0: ep1in: cmd 'End Transfer' params 00000000 00000000 00000000 | dwc3 dwc3.0: Command Complete --> 0 Issue the end transfer command to the udc. | dwc3 dwc3.0: request dd7e5060 from ep1out completed 0/4096 ===> -108 This completes the one request which in our internal list and not yet prepared on HW for transfers so we complete it right away. | zero gadget: high speed config #0: unconfigured | dwc3 dwc3.0: queueing request dd7e5360 to ep0out length 0, state 'Setup Phase' | dwc3 dwc3.0: ep0in: Transfer Not Ready | dwc3 dwc3.0: Transfer Not Ready while ep0in in state 'Setup Phase' | dwc3 dwc3.0: Control Status | dwc3 dwc3.0: ep0in: cmd 'Start Transfer' params 00000000 83887000 00000000 | dwc3 dwc3.0: Command Complete --> 0 | dwc3 dwc3.0: ep1in: Endpoint Command Complete | dwc3 dwc3.0: incomplete IN transfer ep1in | dwc3 dwc3.0: request dd7e5420 from ep1in completed 0/4096 ===> -104 This is the response to our "End Transfer" command. | Unable to handle kernel NULL pointer dereference at virtual address 00000014 dereference of ep->driver_data which is NULL due to cleanup in disable by source sink. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/f_loopback.c | 5 +++-- drivers/usb/gadget/f_sourcesink.c | 13 +++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index ca660d4..6f3e14a 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -243,7 +243,7 @@ loopback_unbind(struct usb_configuration *c, struct usb_function *f) static void loopback_complete(struct usb_ep *ep, struct usb_request *req) { struct f_loopback *loop = ep->driver_data; - struct usb_composite_dev *cdev = loop->function.config->cdev; + struct usb_composite_dev *cdev; int status = req->status; switch (status) { @@ -258,6 +258,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) return; /* "should never get here" */ + cdev = loop->function.config->cdev; ERROR(cdev, "can't loop %s to %s: %d\n", ep->name, loop->in_ep->name, status); @@ -273,7 +274,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) /* FALLTHROUGH */ default: - ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, + pr_err("%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); /* FALLTHROUGH */ diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index e18b4f5..5c67247 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -306,7 +306,7 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) { struct f_sourcesink *ss = ep->driver_data; - struct usb_composite_dev *cdev = ss->function.config->cdev; + struct usb_composite_dev *cdev; int status = req->status; switch (status) { @@ -323,10 +323,8 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */ - VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + pr_debug("%s gone (%d), %d/%d\n", ep->name, status, req->actual, req->length); - if (ep == ss->out_ep) - check_read_data(ss, req); free_ep_req(ep, req); return; @@ -335,17 +333,16 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) * buffer. */ default: -#if 1 - DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + pr_debug("%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); -#endif case -EREMOTEIO: /* short read */ break; } status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status) { - ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + cdev = loop->function.config->cdev; + ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, ep->name, req->length, status); usb_ep_set_halt(ep); /* FIXME recover later ... somehow */ -- 1.7.8.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html