Skip to content

Claculate the OS... even better! (aka many fixes to Host USB) - #562

Open
adriweb wants to merge 11 commits into
masterfrom
test-ci
Open

Claculate the OS... even better! (aka many fixes to Host USB)#562
adriweb wants to merge 11 commits into
masterfrom
test-ci

Conversation

@adriweb

@adriweb adriweb commented Aug 13, 2026

Copy link
Copy Markdown
Member

So I wanted to check if Codex was able to fix/improve the situation regarding USB Host support in CEmu, so that plugging devices into the emulated calc would stop freezing everything - and if possible, actually work.

After hours of looping (and many of manual testing/reporting on my side), it turns out that yes, it was able to fix and improve the situation a whole lot 🎉

Tested and works:

  • TI-Innovator board
    (tested from the Hub app and a sample TI-Basic program)
  • TI-Bluetooth module
    (tested from the SetDash app for robot scanning/pairing)
  • TI CBR 2 / Vernier Go! motion sensor
    (tested in EasyData)
  • A normal HID keyboard
    (tested some specific keys that aren't normally mapped)
  • Another DUSB/CARS-compatible calculator (84+, CE...)
    (tested on a 84+CSE and another CE, as variable receivers)

What still works with all these modifications (regression tests basically):

  • virtual MSD image that Cesium uses
    (tested listing, transfers)
  • normal DUSB transfers
    (variables, OS, apps)

Tested and does NOT work:

  • Actually it "works" but the PyAdapter app wants to re-mount it as MSD or something, which my kernel driver doesn't let me take control of, so it just gets unplugged from the calc and remounted on the computer 🤷‍♂️

Please test the CI artifacts on Windows and Linux 🤷‍♂️


To feast your eyes...

CRB2.mp4

TI-OS reuses the battery comparator configuration after a normal battery-level read,
then selects a high threshold through control ports 0 and 9 before checking USB power safety.
Model that continuous comparator behavior even when port 7 is not rewritten.

Without this update, a successful TI-Innovator bulk write is followed by TI-OS event 0xBD,
which surfaces as ERROR: BREAK. With it, Send("VERSION") and Get(Str0) complete correctly.
Schedule USB frames at the controller's 12 MHz clock rate:
1 ms for full/low speed and 125 us for high speed.
It was around 1500 times too quickly before.

Budgets were swapped for full vs high.
- Make device-mode DMA START self-clear when a response completes, including zero-length status stages

- Only enable a host port after reset has actually completed.
A physical libusb request may remain submitted across multiple emulated frames.
Advancing its iTD, siTD, or queue-head state before the response arrives loses
the original transaction and can stall the async schedule or complete the wrong descriptor.

Let backends mark a request pending, snapshot each schedule object before dispatch,
and restore it while still charging the frame budget. The next frame retries the same
transaction without monopolizing the rest of the schedule, and the response advances it exactly once.
Drive VBUS, session-valid, port connection, and enable state from A_BUSREQ/A_BUSDROP instead of treating the port as permanently present.
Fix A-side unplugging so both connection and enable changes are reported and the B side is actually removed.

Introduce explicit session start/end/request events so controller state can be propagated without advancing backend protocol machines.
MSD and physical devices accept the notifications, while DUSB returns its session-start response directly.
usb_plug_device: do checks before a teardown and use usb_disconnected_device more correctly.

Track claimed and detached interfaces explicitly, so teardown releases only interfaces CEmu owns
and reattaches kernel drivers even after the device no longer has an active configuration descriptor.

Defer DEVICE_LEFT cleanup until libusb event dispatch returns, tear down children before parents,
and identify reset replacements by bus plus port path.

Rebuild hub descendants parent-first, discard stale pending entries, restore hotplug registration
after replacement, and stop retrying permanent open failures.
Keep OTG identity, role, VBUS/session, line state, and host-port state synchronized
without injecting duplicate edge interrupts.

Reset external connector state after image restore and avoid destroying an
already-disconnected backend during no-op detach requests.

Complete successful queue-head transfers with the updated qTD page, offset,
remaining length, and data toggle.

Traverse schedules only while the port is enabled.
Avoid blocking the emulation thread while a physical USB reset re-enumerates a device. Poll replacement handles from the backend timer, ignore the reset hotplug pair, and keep teardown outside libusb hotplug callbacks.

Treat synchronous libusb submission failures as failed transfers instead of tearing down the entire physical backend, so one inaccessible interface does not disconnect an otherwise usable composite device.
Open HID interfaces unavailable to libusb through IOHID while libusb continues to own the remaining interfaces. Keep HID waits separate from libusb transfers and clean them up during reset and detach.

Correlate each IOHID device with its USB interface number, maintain one bridge per interface, and route interrupt-IN endpoints to the correct bridge. Link CoreFoundation and IOKit in both Qt build systems.
@adriweb

adriweb commented Aug 16, 2026

Copy link
Copy Markdown
Member Author

With two more commits, Codex also figured out the necessary changes to make CEmu<->CEmu and CEmu<->calc transfers working. Keep the real one as device and the emulated one as host, though.

Successfully tested on various calc, including 84+CSE and another CE!

CleanShot 2026-08-16 at 02 08 04@2x

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

Development

Successfully merging this pull request may close these issues.

1 participant