Since commit 62911bc82b03 ("usb: gadget: udc: skip pullup() if already connected"), usb_gadget_connect_locked() returns early when gadget->connected is set. A connection requested while the UDC is stopped sets that flag too, without enabling the pullup. A later gadget_bind_driver() or soft_connect "connect" then finds it set and never enables the pullup, so the host doesn't see the gadget until it is unbound and bound again. This happens whenever a UDC driver reports VBUS through usb_udc_vbus_handler() while no gadget driver is bound, e.g. when a Type-C port controller switches the controller to the device role at boot, seconds before userspace binds a configfs gadget. The pullup is never enabled while the UDC is stopped, so clear gadget->connected once it starts. The bind connects according to the VBUS state that the core tracks anyway, and a function that deactivates the gadget during bind still has the connection recorded for usb_gadget_activate(). Fixes: 62911bc82b03 ("usb: gadget: udc: skip pullup() if already connected") Cc: stable@vger.kernel.org Signed-off-by: Alexey Charkov --- drivers/usb/gadget/udc/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d5844384c368..577bc15f61a1 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1236,8 +1236,16 @@ static inline int usb_gadget_udc_start_locked(struct usb_udc *udc) } ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver); - if (!ret) + if (!ret) { udc->started = true; + /* + * The pullup is never enabled while the UDC is stopped, so any + * connection recorded meanwhile was only a request. Forget it, + * so that the next usb_gadget_connect_locked() enables the + * pullup rather than returning early. + */ + udc->gadget->connected = false; + } return ret; } --- base-commit: aaa2f954c21b205a0bcd0961e5b2be4e82e58455 change-id: 20261112-udc-connect-61e0f11cf93b Best regards, -- Alexey Charkov