Skip to content

Clarify USB media UI after host gadget fixes - #1

Open
mjc wants to merge 15 commits into
mjc/fix-usb-virtual-media-hostfrom
mjc/cleanup-usb-gadget-media
Open

Clarify USB media UI after host gadget fixes#1
mjc wants to merge 15 commits into
mjc/fix-usb-virtual-media-hostfrom
mjc/cleanup-usb-gadget-media

Conversation

@mjc

@mjc mjc commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Context

This PR is the cleanup/UI branch stacked on top of the host-visible USB fixes:

The host-visible behavior changes live in those lower branches. This PR keeps the remaining user-facing cleanup: making the USB media controls describe the separated concepts clearly, adding tentative i18n strings for those labels, and wiring the full validation target that includes the web build.

What changed

  • Updated the desktop image/media menu wording so selected virtual image media is not presented as the NanoKVM data disk.
  • Updated virtual-device settings wording so the data-disk export and selected virtual image media are distinct concepts.
  • Added locale entries for the new USB media labels across the existing locale files.
  • Added Makefile test-web and check targets:
    • make test comes from the virtual-media branch and runs USB script tests plus Go server tests
    • make check in this PR runs make test plus the web TypeScript production build

Scope table

Area Done in this PR Not changed in this PR
USB media UI wording Clarifies selected image media vs NanoKVM data-disk export. Host-visible gadget behavior is unchanged here.
CD-ROM wording Keeps CD/ISO wording tied to the explicit ISO/CD-ROM state. ISO mounting mechanics are unchanged here.
i18n Adds tentative locale strings so the UI builds and all languages have matching keys. Native-speaker-quality translation review is not included.
Validation wiring Adds test-web and check so the full stack can be validated with one target. Nix files are not added to this branch.

Validation

Validated locally with the Nix dev shell from the companion mjc/setup-nix-build-env branch:

nix develop /home/mjc/projects/NanoKVM-nix-build-env -c make -C /home/mjc/projects/NanoKVM check

The make check target runs:

  • USB init script tests against fake configfs
  • Go server tests, including USB gadget and HID status tests
  • web TypeScript and production build

Copilot AI review requested due to automatic review settings June 5, 2026 17:42
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c2a9f808-fa9b-4dbc-97ef-d14446f9694f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request refactors USB gadget initialization by extracting shared shell helpers, introducing a new Go USB control package, and integrating it across device and storage services. It adds virtual media device support to the web frontend alongside existing virtual disk and network options.

Changes

USB gadget initialization and control refactoring

Layer / File(s) Summary
USB gadget shell common library
kvmapp/system/init.d/S03usb-common
New shell functions (prepare_fake_gadget, write_usb_strings, add_hid_function, bind_first_udc, restart_udc, etc.) define shared USB gadget initialization helpers with configurable paths and test-mode support.
Refactored USB device init scripts
kvmapp/system/init.d/S03usbdev, kvmapp/system/init.d/S03usbhid
S03usbdev and S03usbhid source common helpers and delegate gadget creation, descriptor writes, HID function setup, and UDC binding to helper functions instead of hardcoded sysfs manipulation.
Go USB gadget control package
server/service/usb/gadget.go, server/service/usb/gadget_test.go
New usb package with Linux configfs gadget lifecycle, feature toggles (SetVirtualMediaEnabled, SetDataDiskEnabled, SetRNDISEnabled), LUN image mounting, UDC helpers, and filesystem utilities. Comprehensive test suite validates device enable/disable, image mounting, and error handling.
Go service integration
server/service/hid/status.go, server/service/hid/status_test.go, server/service/storage/image.go, server/service/vm/virtual-device.go
hid/status.go uses usb package constants and RestartPHY; image.go delegates mount/unmount to usb.SetLUNImage; vm/virtual-device.go calls feature toggles instead of shell commands. Includes tests for mode detection via USB gadget state.
C++ state detection
support/sg2002/kvm_system/main/lib/system_state/system_state.cpp
Uses centralized USB sysfs path constants and path_exists() helper for HID/UDisk presence detection instead of hardcoded paths and glob patterns.
Test infrastructure and build targets
tests/usb-init-scripts-test.sh, Makefile
New shell test suite validates USB initialization scripts with temporary gadget/sysfs mocking. Makefile extended with test, test-server, test-usb-scripts, test-web, and check targets.

Frontend virtual media device support

Layer / File(s) Summary
Virtual device UI with media support
web/src/pages/desktop/menu/settings/device/virtual-devices.tsx, web/src/pages/desktop/menu/image/index.tsx, web/src/i18n/locales/en.ts, web/src/i18n/locales/zh.ts
virtual-devices.tsx adds media enablement state and Switch control; image/index.tsx renames mount modes to usb-disk/iso-cdrom; i18n adds "Virtual Image" translations for English and Chinese locales.

Sequence Diagram

sequenceDiagram
  participant App as Web App
  participant VM as vm.Service
  participant USB as usb.Package
  participant Shell as Shell Init
  App->>VM: UpdateVirtualDevice(media)
  VM->>USB: SetVirtualMediaEnabled(true)
  USB->>USB: WithDetachedUDC()
  USB->>Shell: exec ActiveInitScript
  Shell->>Shell: add_hid_function()
  Shell->>Shell: bind_first_udc()
  USB-->>VM: success
  VM-->>App: device enabled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A gadget reborn, from scripts untied,
Helpers shared, and state unified,
From shell to Go, from disk to screen,
Virtual media streams in between!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately reflects the main change: clarifying USB media UI after fixing underlying USB gadget host behavior.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mjc/cleanup-usb-gadget-media

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors USB gadget/configfs handling to better separate “virtual image” media from the NanoKVM data disk, centralizes UDC rebinding + HID reopen logic, and updates both backend APIs and the UI to reflect the new device split.

Changes:

  • Introduces a shared Go USB gadget helper (server/service/usb/gadget.go) for UDC detach/attach, HID reopen coordination, and toggling virtual media / data disk / RNDIS.
  • Splits the UI and API surface so “Virtual Image” (disk0) is independent from “Virtual Disk” (disk1 / data storage export), with updated i18n strings.
  • Adds Go + shell tests (including fake-configfs init-script tests) and wires them into make check.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/src/pages/desktop/menu/settings/device/virtual-devices.tsx Adds a new “Virtual Image” toggle and supports media device updates.
web/src/pages/desktop/menu/image/index.tsx Renames image mode labels/values to make ISO/CD-ROM explicit.
web/src/i18n/locales/en.ts Adds media strings and updates disk description text.
web/src/i18n/locales/zh.ts Adds media strings and updates disk description text.
tests/usb-init-scripts-test.sh Adds init-script tests using a fake configfs/UDC environment.
support/sg2002/kvm_system/main/lib/system_state/system_state.cpp Fixes HID/UDisk state detection by checking explicit configfs links.
server/service/vm/virtual-device.go Extends virtual device API to include media and delegates toggles to the new USB helper.
server/service/usb/gadget.go New shared gadget/configfs helper (UDC rebind, function toggles, LUN updates).
server/service/usb/gadget_test.go New unit tests for gadget helper behavior with a fake gadget filesystem.
server/service/storage/image.go Switches mount/unmount behavior to use the shared gadget helper.
server/service/hid/status.go Uses shared USB constants/helpers for mode switching and PHY reset.
server/service/hid/status_test.go Adds tests for HID mode detection via bcdDevice.
Makefile Adds test-* targets and wires them into check.
kvmapp/system/init.d/S03usbdev Shares boot-time USB/HID setup via S03usb-common and adds disk1 export.
kvmapp/system/init.d/S03usbhid Shares boot-time USB/HID setup via S03usb-common.
kvmapp/system/init.d/S03usb-common New shared init-script helper for repeated gadget setup steps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/service/usb/gadget.go
Comment thread web/src/i18n/locales/en.ts
Comment thread tests/usb-init-scripts-test.sh
@mjc mjc changed the title Clean up USB gadget media handling Refactor USB gadget handling and fix virtual media state Jun 5, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8443c41579

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/service/usb/gadget.go Outdated
Comment thread kvmapp/system/init.d/S03usbdev Outdated
Comment thread server/service/usb/gadget.go
Comment thread server/service/usb/gadget.go
Comment thread tests/usb-init-scripts-test.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@kvmapp/system/init.d/S03usb-common`:
- Around line 92-94: In the unbind_udc() function, replace the echo-based write
that writes a newline with an explicit empty write to the UDC file; use printf
'' > "${USB_GADGET_ROOT}/${USB_GADGET_NAME}/UDC" or shell truncation (e.g., >
"${USB_GADGET_ROOT}/${USB_GADGET_NAME}/UDC") so the UDC receives an actual empty
write rather than a newline.
- Around line 96-100: The restart_udc function currently uses "echo >" to clear
the UDC file which writes a newline; change this to use printf '' > to write an
empty string for proper unbind semantics: in restart_udc, replace the echo
redirection that targets "${USB_GADGET_ROOT}/${USB_GADGET_NAME}/UDC" with a
printf '' redirection so the UDC file receives an empty string (keep the
subsequent sleep and ls "${USB_UDC_CLASS}" | cat > step intact).
- Around line 24-32: In write_usb_config_string replace the conditional mkdir
calls with a single unconditional mkdir -p usage: remove the if/else block and
always run mkdir -p configs/c.1/strings/0x409 before writing the configuration
file so the directory is created safely in both test and normal modes.

In `@server/service/usb/gadget.go`:
- Around line 77-91: The RestartPHY function currently calls
exec.Command(...).Run() while holding the HID lock; switch to using
context.WithTimeout and exec.CommandContext with a reasonable timeout (e.g.,
usbPhyRestartTimeout) to prevent indefinite blocking, and ensure you do not hold
the HID lock while waiting for the external script: call h.CloseNoLock() while
locked, then h.Unlock() before creating and running the CommandContext, and only
proceed to call h.OpenNoLockWithRetry(hidReopenTimeout, hidReopenDelay) after
the command completes; reference RestartPHY, ActiveInitScript,
exec.CommandContext, context.WithTimeout, CloseNoLock, Lock, Unlock, and
OpenNoLockWithRetry when applying the change.

In `@support/sg2002/kvm_system/main/lib/system_state/system_state.cpp`:
- Around line 230-233: When fopen(USB_UDC_STATE, "r") fails you must preserve
the “unknown/unreadable” semantics by setting kvm_sys_state.hid_state and
kvm_sys_state.udisk_state to -1 (same as kvm_sys_state.usb_state) instead of 0;
update the error/early-return path in the function that opens USB_UDC_STATE so
that on fopen failure it assigns -1 to kvm_sys_state.hid_state and
kvm_sys_state.udisk_state and then returns, keeping the -1 contract from
config.h consistent.

In `@web/src/pages/desktop/menu/settings/device/virtual-devices.tsx`:
- Around line 85-97: Update the inline comment above the media toggle to match
the displayed i18n text: replace the "Virtual Media" comment with "Virtual
Image" (or a comment that mirrors t('settings.device.media')) so it aligns with
the rendered label; locate the block containing the Switch controlled by
isMediaEnabled and update('media') and change the comment text accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9bff9faa-6a63-48d7-bbe9-d2a13d6dde2c

📥 Commits

Reviewing files that changed from the base of the PR and between 46179ce and 8443c41.

📒 Files selected for processing (16)
  • Makefile
  • kvmapp/system/init.d/S03usb-common
  • kvmapp/system/init.d/S03usbdev
  • kvmapp/system/init.d/S03usbhid
  • server/service/hid/status.go
  • server/service/hid/status_test.go
  • server/service/storage/image.go
  • server/service/usb/gadget.go
  • server/service/usb/gadget_test.go
  • server/service/vm/virtual-device.go
  • support/sg2002/kvm_system/main/lib/system_state/system_state.cpp
  • tests/usb-init-scripts-test.sh
  • web/src/i18n/locales/en.ts
  • web/src/i18n/locales/zh.ts
  • web/src/pages/desktop/menu/image/index.tsx
  • web/src/pages/desktop/menu/settings/device/virtual-devices.tsx

Comment thread kvmapp/system/init.d/S03usb-common
Comment thread kvmapp/system/init.d/S03usb-common
Comment thread kvmapp/system/init.d/S03usb-common
Comment thread server/service/usb/gadget.go
Comment thread support/sg2002/kvm_system/main/lib/system_state/system_state.cpp
Comment thread web/src/pages/desktop/menu/settings/device/virtual-devices.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31f8cc2f62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if image == "" {
return nil
}
return WriteString(LUNFile, image)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist the mounted media path for reboot

When an image is mounted and the device later reboots or rebuilds the gadget, S03usbdev restores lun.0/file from /boot/usb.media0, but this path only writes the selected image to the live configfs LUN and leaves the boot flag as the empty file created by EnsureFile. In that reboot/reset context the virtual media switch stays enabled but the selected ISO/img is lost and comes back as empty media, so the mount state shown to the host is not preserved; write the normalized image (and the corresponding mode flag if needed) into the persisted media flag when mounting.

Useful? React with 👍 / 👎.

if err := EnsureFile(MassStorageFlag); err != nil {
return err
}
if err := ensureMassStorageLink(); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject image mounts in HID-only mode

When the active gadget is in HID-only mode, the image-mount API now reaches this helper and creates/links mass_storage.disk0 on demand, so opening the existing image menu or calling the storage endpoint can add a mass-storage function even though bcdDevice still reports HID-only. Previously the same request failed because the LUN path did not exist after a HID-only boot; this should check the current mode or active script and refuse media mutations while HID-only is selected.

Useful? React with 👍 / 👎.

Comment on lines +68 to +69
detachErr := DetachUDC()
mutateErr := mutate()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop before mutating when UDC detach fails

If clearing UDC fails in an environment where the gadget is still bound (for example because configfs/sysfs rejects the write or the path is temporarily unavailable), this still proceeds into mutate() and can change LUNs or config links while the host-visible gadget remains attached. The old command sequences stopped on a failed detach step, so this helper should return before running the mutation when detaching does not succeed.

Useful? React with 👍 / 👎.

@mjc
mjc force-pushed the mjc/cleanup-usb-gadget-media branch from 31f8cc2 to b781c1f Compare June 5, 2026 18:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b781c1f6d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


ls /sys/class/udc/ | cat > UDC
echo device > /proc/cviusb/otg_role
if [ -e "${USB_BOOT_DIR}/usb.disk0" ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Migrate non-empty legacy media flags

When upgrading from the previous script, a mounted image was persisted as a non-empty /boot/usb.disk0 and the old boot path copied that value into the media LUN. This new condition treats every existing usb.disk0 as the data-disk flag and the block below always backs it with /dev/mmcblk0p3, so users who had an ISO mounted before upgrading reboot into the data disk and lose their virtual media. Please migrate non-empty legacy usb.disk0 values to usb.media0 (or branch on empty vs non-empty) before enabling the data disk.

Useful? React with 👍 / 👎.

@mjc
mjc force-pushed the mjc/cleanup-usb-gadget-media branch 2 times, most recently from 3bc09f5 to e53288f Compare June 5, 2026 18:27
@mjc mjc changed the title Refactor USB gadget handling and fix virtual media state Clarify USB media UI after host gadget fixes Jun 5, 2026
@mjc
mjc changed the base branch from mjc/fix-usb-hid-boot-subclass to mjc/fix-usb-virtual-media-host June 5, 2026 18:28
@mjc
mjc force-pushed the mjc/cleanup-usb-gadget-media branch from e53288f to b63331c Compare June 5, 2026 19:39
@mjc
mjc force-pushed the mjc/fix-usb-virtual-media-host branch from 877aa7d to 288422b Compare June 5, 2026 19:39
@mjc
mjc force-pushed the mjc/fix-usb-virtual-media-host branch from 288422b to f16e3c2 Compare June 5, 2026 23:14
@mjc
mjc force-pushed the mjc/cleanup-usb-gadget-media branch from b63331c to e483fbc Compare June 5, 2026 23:14
mjc pushed a commit that referenced this pull request Jun 8, 2026
@mjc
mjc force-pushed the mjc/fix-usb-virtual-media-host branch from f36ee45 to 295cffb Compare June 11, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants