The gadget documentation doesn't state clearly whether a gadget driver's ->disconnect() callback should be invoked when the D+ pullup is turned off. Some UDC drivers do this and some don't. This patch settles the issue by making the core function usb_gadget_disconnect() issue the callback, so that UDC drivers don't need to worry about it. A description of the new behavior is added to the function's kerneldoc. Also, the patch removes a few superseded callbacks from other core routines. Future patches will remove the ->disconnect() calls from the UDC drivers that make them, as they are now unnecessary. Until all those patches are merged gadget drivers may receive extra ->disconnect() callbacks, but this should be harmless. Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> --- [as1874] drivers/usb/gadget/udc/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) Index: usb-4.x/drivers/usb/gadget/udc/core.c =================================================================== --- usb-4.x.orig/drivers/usb/gadget/udc/core.c +++ usb-4.x/drivers/usb/gadget/udc/core.c @@ -672,6 +672,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_connect); * as a disconnect (when a VBUS session is active). Not all systems * support software pullup controls. * + * Following a successful disconnect, invoke the ->disconnect() callback + * for the current gadget driver so that UDC drivers don't need to. + * * Returns zero on success, else negative errno. */ int usb_gadget_disconnect(struct usb_gadget *gadget) @@ -693,8 +696,10 @@ int usb_gadget_disconnect(struct usb_gad } ret = gadget->ops->pullup(gadget, 0); - if (!ret) + if (!ret) { gadget->connected = 0; + gadget->udc->driver->disconnect(gadget); + } out: trace_usb_gadget_disconnect(gadget, ret); @@ -1263,7 +1268,6 @@ static void usb_gadget_remove_driver(str kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); udc->driver->unbind(udc->gadget); usb_gadget_udc_stop(udc); @@ -1453,7 +1457,6 @@ static ssize_t soft_connect_store(struct usb_gadget_connect(udc->gadget); } else if (sysfs_streq(buf, "disconnect")) { usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); usb_gadget_udc_stop(udc); } else { dev_err(dev, "unsupported command '%s'\n", buf);