197 lines
12 KiB
Markdown
197 lines
12 KiB
Markdown
# USB — the Pixel 3 as a KVM device
|
|
|
|
## Current — 2026-08-07
|
|
|
|
The direction is settled: another computer sends its display **into** the
|
|
phone over GUD; the phone returns keyboard, pointer and touch as boot HID.
|
|
The same composite keeps NCM for control and carries a smoo FunctionFS
|
|
responder for host-backed storage. This is not the earlier UVC/VNC sketch.
|
|
|
|
| Piece | Current state |
|
|
|---|---|
|
|
| Display in | `souveraine-usb-kvm`: GUD FunctionFS responder, RGB565 surface on Wayland |
|
|
| Hands back | Boot keyboard + relative pointer; window keyboard, mouse and touch become HID reports |
|
|
| Storage | `smoo-gadget` FunctionFS responder; `ublk_drv` on the phone |
|
|
| Control link | NCM remains in the composite |
|
|
| Mode owner | `usb-signaller` exposes `kvm_mode`; GUD and smoo readiness gate the bind, and failure restores the prior mode |
|
|
| Human/agent surface | `sessiond` owns the audited `usb` / `set_usb_mode` verbs; held-power **Power Options** exposes HID and full KVM |
|
|
| Canonical source | receiver/session/UI/package in `~/Projects/souveraine`; device mechanism in `~/Projects/usb-signaller`; package recipes in Pixel3Arch |
|
|
| Build proof | full ARM64 release receiver and sessiond built on ArchDev; receiver tests + strict Clippy pass |
|
|
| Phone | **Not installed yet.** Live phone still has `usb-signaller 0.3.1-3`, no `smoo`, and advertises no HID/KVM mode |
|
|
|
|
TASK-62 owns the package and glass landing. The material below is the
|
|
2026-08-04 exploration ledger; it remains useful for host mode and for why the
|
|
discarded UVC/VNC routes were considered, but it is no longer the build plan.
|
|
|
|
## Baseline measured on the phone (2026-08-04, historical)
|
|
|
|
| Piece | State |
|
|
|---|---|
|
|
| Kernel gadget framework | `CONFIG_USB_GADGET=y`, `CONFIG_USB_CONFIGFS=y` — built in, no modules |
|
|
| **HID gadget** | **`CONFIG_USB_CONFIGFS_F_HID=y` — the keyboard/mouse half of KVM already ships** |
|
|
| FunctionFS | `CONFIG_USB_CONFIGFS_F_FS=y` (flexible userspace gadgets, MTP path) |
|
|
| **UVC gadget** | **`CONFIG_USB_CONFIGFS_F_UVC` not set — the video half is the one missing config line** |
|
|
| Other functions | NCM, ECM, RNDIS, EEM, ACM, serial, OBEX, mass storage; no UAC1/2, no MIDI |
|
|
| Live configfs | Two gadgets bound: `dbg` (acm.GS0, acm.GS1, ncm.usb0) and `usb-signaller-developer` (ncm.usb0) on UDC `a600000.usb` (dwc3, **super-speed capable**, currently high-speed) |
|
|
| Mode switching | `usb-signaller` 0.3.1-3 active (dev mode at boot via `usb-signaller-default-mode`); `blueline-usb-gadget` 1.0.0-2; dev mode = NCM usb0 (172.16.42.1) + ACM |
|
|
| Video hardware | Venus **encoder** `/dev/video12` + **decoder** `/dev/video13`; CAMSS sensors `/dev/video0-11`; no v4l2loopback |
|
|
| Other packages | v4l-utils, libusb, libgusb, libusbmuxd |
|
|
|
|
## Host mode — the dock question (2026-08-04, Casey: "if I plug a USB-C dock into the phone right now…")
|
|
|
|
DeX-style: the phone as the computer, a dock supplying monitors + keyboard
|
|
+ mouse. Measured against the running kernel *and* Google's own vendor DT
|
|
(`sdm845-b1c1-usb.dtsi`, LineageOS kernel lineage-22.2):
|
|
|
|
| Ask | Verdict | Evidence |
|
|
|---|---|---|
|
|
| **Multi-monitor (DP alt mode)** | **Unwired in every tree that exists.** Google's board DT wires the port as **DRP + USB-PD** (`pmi8998_pdphy`, source 5V@0.9A, sink 5V/9V@3A, try-sink) with **no DP alt-mode path** — no redriver, no typec mux, no altmode node; the only "mux" is test GPIO104 for HS enumeration. Matches Google's official spec (USB 3.1 Gen 1, no video out). The *silicon* is capable — mainline `sdm845.dtsi` fully wires `mdss_dp` → QMP USB3-DP PHY (`usb_1_qmpphy_dp_in`) — but no sdm845 board in mainline wires it end-to-end either | vendor DT (fetched), mainline dtsi (local tree) |
|
|
| **USB keyboard/mouse** | **Yes — this is what LineageOS actually gives you.** DRP port + PD = real host mode. Our tree pins `dr_mode = "peripheral"` and usb-signaller holds the port as a gadget, so *we* don't do it today — but the hardware does | vendor DT, our board DT |
|
|
| **Via a plain OTG adapter** | **Works** — classic USB-A keyboard/flash-drive case | standard OTG |
|
|
| **Via a Dell dock (WD19…)** | **Host mode: works on LineageOS, one role-switch away on ours.** Monitors over DP alt mode: no. Monitors over **DisplayLink** (software display over USB host): only with a DisplayLink driver | per-dock, plug it in |
|
|
| **Charging from the dock** | Yes (PD sink 5V/9V@3A wired in the vendor DT) | vendor DT |
|
|
|
|
**The correction (2026-08-04):** an earlier draft called the DP gap a
|
|
"connector mux on the board — hardware." That was asserted from memory and
|
|
is wrong as stated. The evidence: Google wired the port for data+PD and
|
|
never enabled DP alt mode in any tree, and the *hardware question that
|
|
remains* — whether the PCB even routes DP lanes from the QMP PHY to the
|
|
connector — is **unproven in both directions**. If LineageOS drove real
|
|
monitors over the dock, the lanes exist (via DisplayLink, not alt mode).
|
|
Decisive physical test: a USB-C→HDMI **alt-mode** adapter (not DisplayLink)
|
|
with the port in host mode; if the board routes DP, a wired-up kernel has
|
|
something to talk to.
|
|
|
|
**The daily-driver catch:** the USB-C port is the management port — tethering
|
|
and dev mode (SSH over usb0) die while it is a host. Deliberate, documented
|
|
mode switch only; cold boots stay Casey's.
|
|
|
|
## Host mode — the live test (recipe, 2026-08-04)
|
|
|
|
Confirmed on the device: the role switch is real (`/sys/class/usb_role/
|
|
a600000.usb-role-switch`, role currently `device`), passwordless sudo works,
|
|
and usb-signaller owns `com.meego.usb_moded` on the system bus (its modes are
|
|
all device-side today — Developer/Tethering/MTP — so host mode is a direct
|
|
sysfs switch, and usb-signaller's restart is the way *back*).
|
|
|
|
```
|
|
# 1. give up the gadget first — a bound gadget + host role breaks
|
|
echo "" | sudo tee /sys/kernel/config/usb_gadget/usb-signaller-developer/UDC
|
|
|
|
# 2. switch the port to host
|
|
echo host | sudo tee /sys/class/usb_role/a600000.usb-role-switch/role
|
|
|
|
# 3. plug the dock, then observe:
|
|
lsusb
|
|
dmesg | tail -30
|
|
|
|
# 4. back — WiFi SSH survives the whole trip, so this always works:
|
|
echo device | sudo tee /sys/class/usb_role/a600000.usb-role-switch/role
|
|
sudo systemctl restart usb-signaller # re-provisions the developer gadget
|
|
cat /sys/class/usb_role/a600000.usb-role-switch/role
|
|
```
|
|
|
|
Gotchas: the dock is self-powered (Dell docks carry their own PSU), so its
|
|
hub can enumerate even if the phone's VBUS sourcing is weak or absent — the
|
|
dwc3 node has **no `vbus-supply`**, so bus-powered OTG peripherals (plain
|
|
keyboards) may not power, which is itself a measurement. If the dock's hub
|
|
enumerates, its keyboard/mouse are just kernel HID input — the shell gets a
|
|
real keyboard for free. If the dock's *monitors* ever light up, the DP lanes
|
|
exist and we go hunt the mux wiring; with no typec/altmode wired, expect
|
|
nothing from them.
|
|
|
|
## Original KVM notion — superseded by TASK-62
|
|
|
|
Plug the phone into another machine's USB port. The phone controls that
|
|
machine and sees its screen — keyboard, mouse, monitor, in one cable. Two
|
|
halves, with very different states of readiness:
|
|
|
|
**Input half — HID gadget: already built.** The kernel handles the USB HID
|
|
descriptors; userspace writes 8-byte keyboard reports to `/dev/hidg0` and
|
|
4-byte mouse reports to `/dev/hidg1`. This is the half that reaches
|
|
bootloaders/BIOS (the pelya project proved it) — no video ever reaches a
|
|
BIOS, so a BIOS-stage KVM is HID plus whatever screen the machine itself has.
|
|
|
|
**Display half — two routes:**
|
|
|
|
- **Route A (works today, zero kernel change):** the live NCM link. The
|
|
controlled machine serves its display over the USB network (VNC/waypipe);
|
|
the phone shows it in a surface. Touches become HID reports. Worst part is
|
|
latency, and the link is super-speed-capable.
|
|
- **Route B (one config line away):** UVC gadget. The phone presents as a
|
|
UVC camera; the controlled machine runs a "screen-as-webcam" tool (OBS
|
|
virtual cam and friends) which is the stream source; the phone decodes
|
|
(Venus decoder ✓) and shows it. The reverse direction also exists: Venus
|
|
encoder + UVC = the PC sees the phone's *own* screen as a webcam — an
|
|
in-the-cable scrcpy.
|
|
|
|
## Prior art (verified)
|
|
|
|
- **tejado/android-usb-gadget** — USB Gadget Tool (F-Droid): configfs roles
|
|
for keyboard/mouse, FIDO CTAP, CCID, UVC camera; activates gadgets at boot.
|
|
- **Android 14 "Device as Webcam"** — `CONFIG_USB_CONFIGFS_F_UVC` + configfs
|
|
recipe (MJPEG 1080p30, `dwMaxVideoFrameBufferSize` etc.); enabled by
|
|
default in GKI since android12-5.10. The configfs UVC setup pattern is the
|
|
one to copy.
|
|
- **pelya/android-keyboard-gadget** — the classic: `/dev/hidg0` keyboard,
|
|
`/dev/hidg1` mouse, `hid-gadget-test`; works into BIOS/bootloader.
|
|
- **Kernel docs** — `usb/gadget_hid.rst` (hid_gadget_test) and
|
|
`usb/gadget_uvc.html`; the UVC reference app is
|
|
`gitlab.freedesktop.org/camera/uvc-gadget` — "queue image buffers from a
|
|
variety of sources", i.e. the phone-side userspace we would adapt to feed
|
|
viewtop/compositor frames into the gadget.
|
|
|
|
## Original questions — answered by TASK-62
|
|
|
|
1. **Directions:** KVM-into-another-machine only, or also the reverse
|
|
(phone screen out as webcam)? Both share the UVC config; the userspace
|
|
differs.
|
|
2. **Route A or B first?** A proves the whole loop today with no rebuild; B
|
|
is the "real peripheral" feel but needs a kernel rebuild + cold boot —
|
|
and that is Casey's call, on the daily driver.
|
|
3. **Touch → HID mapping:** a KVM mode turns the phone's screen into a
|
|
touchpad/screen for the target machine. The machinery is the mirror of
|
|
TASK-50's touch intent — same input-routing problem, other direction.
|
|
4. **Where does the mode live?** usb-signaller gains a "kvm" mode (profile
|
|
+ service), or sessiond owns it as a mode with a verb. The agent angle
|
|
is real: Ani gets a keyboard/mouse that reaches any host — doctrine §13
|
|
says she should have the verb, not a config file.
|
|
5. **The kernel path:** enabling F_UVC is one config line, but the kernel
|
|
package still publishes unsigned raw `.pkg.tar.zst` (TASK-25's
|
|
`repo-add --sign` is unbuilt) and installs are cold-boot events.
|
|
|
|
## Original next steps — historical
|
|
|
|
1. **Prove HID today** — provision a HID keyboard function into a scratch
|
|
gadget on the phone (configfs, no reboot), bind it, write
|
|
`hid-gadget-test` key reports, watch the laptop enumerate a second
|
|
keyboard. One session, no rebuild.
|
|
2. **Route A latency test** — laptop serves its screen (VNC/waypipe) over
|
|
the live 172.16.42.1 link; phone displays it; measure.
|
|
3. **Route B** — config one-liner + rebuild + cold boot (Casey); configfs
|
|
UVC setup per the AOSP recipe; `uvc-gadget` userspace fed by a capture
|
|
surface.
|
|
4. **usb-signaller "kvm" mode** once the pieces exist, with clean
|
|
teardown/provision against the dev mode.
|
|
|
|
## Original acceptance sketch — historical
|
|
|
|
- Phone plugged into the laptop enumerates as keyboard+mouse; typing from
|
|
the phone lands in the laptop's focused app.
|
|
- Route A: the laptop's screen renders on the phone with usable latency.
|
|
- Route B: the laptop's virtual-cam tool lists the phone as a camera; the
|
|
phone shows the stream.
|
|
- Switching modes never strands the daily driver's tethering/dev mode.
|
|
|
|
## Connects to
|
|
|
|
TASK-25 (kernel package signing — the rebuild path), TASK-50 (her hand on
|
|
the glass — the touch→HID bridge is the mirror of the touch intent),
|
|
doctrine §13 (the KVM mode as a verb), `usb-signaller` (the mode host),
|
|
souveraine-viewtop (the display surface and capture path).
|
|
|
|
## The buildable work lives here
|
|
|
|
TASK-57 (the Pi dock head — phone as gadget, Pi as display/input head over
|
|
NCM) and TASK-58 (host power — the SMB2 OTG boost, one kernel change that
|
|
unlocks host-mode power, power sharing, and phone→Pi power).
|