The OTG profile could not see USB devices in host mode. On a board with a
UVC webcam on the USB tail, `lsusb` showed only the two root hubs, while the
same board running a plain lite image enumerated the webcam normally.
The controller was in the host role and the host controller was running --
mode file "host", xhci-hcd bound, USBCMD 0x5, PORTSC1 port-power set -- and
the port still never reported a connect (PORTSC1 0x00000280, CCS clear).
dr_mode = "otg" makes dwc3_core_init() program GCTL.PRTCAPDIR = OTG(3), and
dwc3_core_init_mode() registers xhci-hcd on top of that. This 4.9 dwc3 has no
working OTG role-detection block, so xHCI probes, resets the controller and
brings up its root hubs while the core is in a role it cannot drive. The port
is then dead for the life of that probe.
S72usbmode's `usb-mode host` did not repair it and could not: writing the
debugfs mode file reaches dwc3_set_mode(), which flips GCTL.PRTCAPDIR and
nothing else, leaving the already-probed xHCI with the port state machine it
built in OTG. It also skipped the bind, because xhci-hcd was already bound.
Fix it where the ordering is: patch dwc3_core_init() to program
PRTCAPDIR = HOST for the OTG case, so xHCI probes into a role the core can
drive. dwc3_gadget_init() only registers a UDC and does not care, and
`usb-mode device` re-aims the controller itself when it takes the port.
usb-mode's host path also learns to re-probe an xHCI that was bound in some
other role, which is what makes a newer rootfs recover on an older kernel.
Booting the otg profile with /etc/usbmode = device made S72usbmode's
`usb-mode device` log, on every single boot:
xhci-hcd xhci-hcd.0.auto: Host not halted after 16000 microseconds.
xhci-hcd xhci-hcd.0.auto: Host controller not halted, aborting reset.
dwc3_core_init() leaves GCTL.PRTCAPDIR = OTG(3) and this 4.9 dwc3 never
moves off OTG on its own. xhci-hcd binds and registers both buses on top
of that anyway, but it is not actually driving the port -- so unbinding
straight from OTG asks xhci_halt() to halt a host that was never really
running, and it times out.
The gadget composed regardless, so this was cosmetic in practice, but it
left the host controller un-halted: exactly the live-DMA-engine state the
unbind is there to avoid.
Flip the controller to host before unbinding, so the already-bound xHCI
is genuinely running and the unbind halts cleanly. A switch from host is
already in that state, so only boot pays for the extra flip.
Measured on gk7205v200 (Xiongmai IPC_GK7205V200_50H20AI_S38): 5/5 cold
boots warned before, 0/5 after. Runtime host<->device round trips stay
clean and still take ~0.35s, with no reboot and no majestic restart.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds gk7205v200_otg_generic, a firmware profile that puts the single DWC3
controller into dual-role so the board can either consume a USB webcam
(existing usbcam path, host role) or present itself to a PC as one (UVC
gadget, device role). The roles are mutually exclusive -- xHCI HCSPARAMS1
reports MaxPorts = 1 -- but usb-mode switches between them at runtime, so
one image covers both and no reboot is needed.
Kernel side, three patches:
0015 sets dr_mode = "otg" in the DTS so dwc3 registers both an xHCI host
and a UDC.
0016 makes device mode actually work on a part that has only ever run as
a host. Three fixes: re-run dwc3_event_buffers_setup() on gadget start
(an xHCI reset wipes GEVNTADR), soft-reset the core before taking the
lock so the PHY is re-initialised (without it high-speed chirp fails),
and swallow the run_stop timeout on pullup(0), which otherwise
permanently poisons cdev->deactivations.
0017 disables the vendor's UVC_SG_REQ path. It hands a whole frame to the
UDC as one scatter-gather request; dwc3 chains those sg entries into a
single isochronous service interval, which on a full-speed link is one
1023-byte packet, so every frame larger than that reached the host
truncated and H.264 decoders reported "bytestream -5" on roughly half the
frames. Goke never shipped this path enabled -- CONFIG_USB_CONFIGFS_F_UVC
is unset in every vendor defconfig, including gk7605v100's, and the SDK
carries no userspace UVC sample -- so it was never exercised. The
mainline path (32 plain one-packet requests) is used instead.
Userspace side: usb-mode switches roles with the unbind/flip/bind
sequencing dwc3_set_mode() does not do on its own, uvc-gadget-setup
composes the configfs gadget, and S72usbmode forces a concrete role at
boot -- not optional, since dr_mode = "otg" leaves GCTL.PRTCAPDIR in a
state this 4.9 dwc3 never moves off by itself.
The descriptors advertise exactly the one format, frame size and interval
majestic is configured to serve, read from /etc/majestic.yaml. majestic
answers every PROBE/COMMIT with its own bFormatIndex/bFrameIndex because
it has one stream and cannot follow the host, so anything extra is an
invitation to select something that will not arrive: asking for MJPEG got
raw H.264 (an SPS where a JPEG SOI belongs, garbage to every consumer),
and asking for 1080p got 720p.
Profile drops the air-unit packages (wifibroadcast, rtl8812au, mavfwd,
mavlink-router, vtund, linux-firmware, wireless-tools) and the whole
wireless kernel stack, which pays for usb_f_uvc.ko and for keeping the
full sensor list -- the fpv trim to IMX307/IMX335 is keyed on
OPENIPC_VARIANT="fpv", and a general-purpose USB webcam should not be
sensor-locked.
Measured on a gk7205v200 lab board over a full-speed link: H.264
1280x720 at 30 fps with zero decoder errors and no dropped buffers, and
MJPEG delivering valid JPEGs at the jpeg channel's own rate.
Requires a majestic built with ENABLE_UVC_GADGET to feed the gadget;
that is a separate change.