Re: [PATCH 2/3] usb: chipidea: udc: support dynamic gadget add/remove
From: Xu Yang
Date: Thu Apr 23 2026 - 06:16:28 EST
On Tue, Apr 21, 2026 at 11:05:50PM -0400, Frank Li wrote:
> On Tue, Apr 21, 2026 at 04:24:35PM +0800, Xu Yang wrote:
> > When the device is connected and enumerated by the host, switching the
> > role from device to host leaves an asynchronous vbus_event_work() to be
> > run. This can affect EHCI host controller initialization process.
>
> An asynchronous vbus_event_work() keep running when switch the role from
> device to host. This affects EHCI host controller initialization.
>
> >
> > If vbus_event_work() runs after ehci_run() sets USBCMD.RUNSTOP bit, then
> > RUNSTOP bit will be cleared. As a result, the host controller fails to
> > operate.
>
> USBCMD.RUNSTOP bit is set at ehci_run() and cleared by following
> vbus_event_work() if bus_event_work() run after ehci_run().
>
> >
> > The log below shows what happens:
> >
> > [ 87.819925] ci_hdrc ci_hdrc.0: EHCI Host Controller
> > [ 87.819963] ci_hdrc ci_hdrc.0: new USB bus registered, assigned bus number 1
> > [ 87.955634] ci_hdrc ci_hdrc.0: USB 2.0, controller refused to start: -110
> > [ 87.955658] ci_hdrc ci_hdrc.0: startup error -110
> > [ 87.955682] ci_hdrc ci_hdrc.0: USB bus 1 deregistered
> >
> > The problem is that the chipidea UDC driver call usb_udc_vbus_handler() to
> > pull down data line but it doesn't guarantee that this operation has
> > completed before the host controller starts running.
>
> but it don't wait for completion before host controller starts running.
>
> >
> > Now UDC core can properly delete usb gadget device and make sure that vbus
> > work is cancelled or completed after usb_del_gadget_udc() is returned. But
> > the udc.c only call usb_del_gadget_udc() in ci_hdrc_gadget_destroy(). To
> > avoid above issue,
> ...
> > let the gadget device add/remove dynamically during USB
> > role switching.
>
> add/remove the gadget device dynamically during USB role switching.
>
> >
> > To support dynamic gadget add/remove, this firstly clear ci->gadget and
> > ci->ci_hw_ep memory so the driver won't access stale data when initialize
> > and reuse these data structures again. Secondly, this assign udc_start()
> > and udc_stop() to rdrv->start and rdrv->stop, meanwhile it removes
> > udc_id_switch_for_device() and udc_id_switch_for_host(). The things in
> > them also properly be merged to udc_start()/udc_stop().
>
> To support dynamic gadget add/remove, do below steps:
> - clear ci->gadget and ci->gadget at initialization.
> - assign udc_[start|stop]() to rdrv->[start|stop] instead of
> udc_id_switch_for_[device|host]() because it already did in
> udc_[start|stop]()
>
> >
> > Since ci_hdrc_gadget_init() doesn't add gadget anymore, this also adjust
> > the order of ci_handle_vbus_change() and ci_role_start(), otherwise, NULL
> > pointer will be met.
>
> Adjust the order ci_handle_vbus_change() and ci_role_start() to avoid NULL
> pointer reference since ci_hdrc_gadget_init() doesn't add gadget anymore.
OK. Will refine them.
Thanks,
Xu Yang