Skip to content

Stop leaking _usbif to applications #10

Description

@cursor

The C usermod stays _usbif. Applications should not import it.

Package internals (native_usb.py, native_midi.py, uac_audio.py) may still import _usbif. Examples, README snippets, and the public usbif docs must go through usbif.auto.host() / usbif.auto.device() / usbif.auto.open_midi().

This is a surface cleanup, not a structural merge: MicroPython cannot register the C module as usbif while a frozen Python package also occupies that name.

Related: #8 is the costume-name gap this plan has to fix first (Device.FUNCTIONS / NativeDevice._BITS omit video).

Plan

Work is in this repo. The C module stays _usbif.

flowchart LR
  examples["examples and apps"] --> auto["usbif.auto"]
  auto --> native["usbif.native_usb"]
  auto --> midi["usbif.native_midi"]
  examples --> uac_audio["usbif.uac_audio"]
  examples --> parsers["usbif.uac / usbif.uvc parsers"]
  native --> cmod["_usbif C usermod"]
  midi --> cmod
  uac_audio --> cmod
Loading

Rule

_usbif is imported only inside lib/usbif/ (native_usb.py, native_midi.py, uac_audio.py). Everything else goes through usbif.auto.host() / usbif.auto.device() / usbif.auto.open_midi().

uac.py and uvc.py stay portable descriptor parsers with no _usbif import — they already run on CPython in the conformance suite. Device/host streaming for those classes lands on NativeDevice / NativeHost, next to the wrappers that already exist (NativeDevice.functions, NativeHost.start, uac_audio for hosted PCM).

Facade: expand NativeDevice and NativeHost

lib/usbif/native_usb.py already has _require() and the lifecycle. Add thin passthroughs; do not invent a second API.

Fix the costume gap first. NativeDevice._BITS and Device.FUNCTIONS omit video (FN_VIDEO = 32). usbif_webcam.py cannot move off _usbif.FN_VIDEO until "uvc": 32 is added in native_usb.py and "uvc" is added to FUNCTIONS in lib/usbif/__init__.py. That is #8.

NativeDevice (board as peripheral) — passthroughs named without the C dev_ / class prefix where the object already is the device:

  • Costume / diagnostics: state(), reinit(), pid(), desc_check(), builtin_desc_cfg()
  • MSC: msc_attach, msc_attach_blockdev, msc_detach, msc_status, msc_buffer, msc_bd_stats
  • HID: hid_send, hid_leds; HID_KEYBOARD / HID_MOUSE as class attributes
  • UAC device: uac_enable, uac_pump_start, uac_pump_stop, uac_pump_stats, uac_available, uac_volume, uac_read, uac_stats
  • UVC device: uvc_format, uvc_reset, uvc_streaming, uvc_ready, uvc_submit, uvc_stats

MIDI device I/O is already usbif.auto.open_midi("dev:midi"). Do not add a second midi_read on NativeDevice.

NativeHost (board drives peripherals):

  • Already: start / stop / devices / poll / capabilities
  • Add: desc(id), stats(), port_cycle(), intr_dump()
  • MSC: msc_open/info/read/write/close/diag/provoke
  • UVC: uvc_negotiate/open/read_frame/frame_ready/stats/close
  • CDC: cdc_open/read/write/close
  • HID: hid_open/read/close

Host MIDI stays usbif.auto.open_midi("host:<id>"). Host UAC stays lib/usbif/uac_audio.py.

Lift the duplicated MSC partition. USBPartition is copy-pasted in examples/usb_drive_mount.py and examples/usb_drive_log.py. Move it to native_usb.py as MscPartition calling NativeHost.msc_read / msc_write.

Examples: import usbif, never _usbif

Twelve files under examples/ import _usbif today. Rewrite each to usbif.auto plus the new methods. Typical shapes:

Device costume (webcam, sd_drive, midi_device_in, costume_selftest):

import usbif.auto
dev = usbif.auto.device()
dev.functions("cdc", "uvc")   # names, not FN_* bitmasks

Device MIDI (harmonizer, harmonizer_ui, midi_device_in, midi_latency):

port = usbif.auto.open_midi("dev:midi")
port.read(buf)
port.write(msg)

Host (uvc_display, midi_host, usb_drive_*):

host = usbif.auto.host(classes=("uvc",)).start()
for info in host.find("uvc"):
    blob = host.desc(info.id)

uac_pump.py / README snippet: dev.uac_volume(), dev.uac_read(), dev.uac_pump_start(...) instead of _usbif.uac_*.

costume_selftest.py: iterate name combinations from dev.functions_available() and call dev.desc_check(); drop FN_* bitmasks.

Leave src/mod_usbif.c and historical findings text that name the C module alone. Update only application-facing docs: root README.md (the import usbif.auto, _usbif snippet) and lib/usbif/README.md.

Guardrail

Add tests/test_no_usbif_leak.py: walk examples/ and lib/usbif/README.md (and the root README example block) and fail if import _usbif or from _usbif appears. Allow _usbif only under lib/usbif/native_usb.py, native_midi.py, and uac_audio.py.

CPython unit test: constructing NativeDevice / NativeHost and calling a wrapped method without the C module raises ImportError (the existing _require() path).

python -m flake8 on the touched Python. On-device proof is a later board run of the rewritten examples; this change is a rename of the Python surface, not of USB behaviour.

Out of scope

  • Renaming the C module to usbif
  • Implementing Host/Device in C
  • Merging desktop backends into the usermod
  • Rewriting docs/phase0-findings.md evidence that recorded _usbif on the REPL

Todos

  • Add uvc to NativeDevice._BITS and Device.FUNCTIONS (Portable Device.functions() omits video (FN_VIDEO exists in C) #8)
  • Passthrough remaining C globals on NativeDevice / NativeHost; lift MscPartition
  • Rewrite all 12 examples and README snippets to usbif.auto / Native*
  • Add test_no_usbif_leak.py and ImportError coverage for wrapped methods

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions