Skip to content

feat(transport): implement macOS USB discovery using IOKit#39

Closed
skot wants to merge 1 commit into256foundation:mainfrom
skot:macos-usb
Closed

feat(transport): implement macOS USB discovery using IOKit#39
skot wants to merge 1 commit into256foundation:mainfrom
skot:macos-usb

Conversation

@skot
Copy link
Copy Markdown
Collaborator

@skot skot commented Mar 7, 2026

Summary

  • add macOS USB discovery using IOKit
  • support device enumeration and hotplug notifications
  • map USB devices to /dev/cu.* serial ports by location ID
  • document macOS support in the README

Validation

  • cargo test --no-run

- Add core-foundation, io-kit-sys, mach2 dependencies for macOS
- Implement MacOsIoKitDiscovery with device enumeration via IORegistry
- Support hotplug notifications using CFRunLoop and IOKit callbacks
- Map USB devices to /dev/cu.* serial ports by location ID
- Update README to reflect macOS is now supported
@skot
Copy link
Copy Markdown
Collaborator Author

skot commented Mar 7, 2026

tested with a bitaxe-raw bitaxeGamma from MacOS and seems to be working.

@majoal0
Copy link
Copy Markdown

majoal0 commented Mar 9, 2026

Tested on macOS with a BitAxe Gamma 602 connected via USB-C. The device was detected correctly and communication through Mujina worked as expected.

@rkuester
Copy link
Copy Markdown
Collaborator

I assume you needed cargo test --no-run because cargo test hung. That's the bug tracked in #46. It should be fixed now by 9ea2497. Please try running cargo test again and let me know if it works for you on macOS.

@Nickamoto
Copy link
Copy Markdown
Contributor

Nickamoto commented Mar 17, 2026

Tested on: Mac mini (Apple M-series, macOS 26.2) with a Bitaxe Gamma (BM1370, AxeOS v2.13.0) connected via USB.

Build: Clean. No new errors or warnings introduced by this PR.

Hardware verification: The Bitaxe enumerates in IORegistry under IOUSBHostDevice (VID 0x303A / Espressif) with an associated IOSerialBSDClient at /dev/cu.usbmodem*. This is exactly the class and serial port path the PR targets.

Issues found:

  1. Race condition in monitor_blocking — enumerate_devices() is called before IOServiceAddMatchingNotification is registered. A device plugged in during that window will be missed. The standard IOKit pattern avoids this: register the notification first, then drain the returned iterator — the drain step already yields existing devices. The separate enumerate_devices() call is both redundant and racy.

  2. kIOMasterPortDefault is deprecated since macOS 12 — kIOMainPortDefault is the current replacement. Functionally equivalent (both are 0), but worth updating to avoid future warnings.

  3. Stale module doc in usb.rs — Line 10 still reads "macOS: Stub implementation (IOKit support planned for future)". Should be updated to reflect that macOS is now fully supported.

  4. _master_port field in MacOsIoKitDiscovery - Stores kIOMasterPortDefault (the constant 0) but is never used. The field could be removed entirely.

After flashing bitaxe-raw (esp-hal-update branch) onto the Bitaxe, re-ran the same discovery tests. The firmware presents a different USB descriptor than AxeOS:
• bitaxe-raw: VID=c0de PID=cafe, product="Bitaxe", manufacturer="OSMU"
• AxeOS: VID=303a PID=1001, product="USB JTAG/serial debug unit", manufacturer="Espressif"
Both are correctly enumerated by MacOsIoKitDiscovery. Under bitaxe-raw, two serial ports are exposed and both are correctly resolved by find_serial_ports_for_device:
• /dev/cu.usbmodem9e79bedc1 — control serial (I2C, GPIO, ADC)
• /dev/cu.usbmodem9e79bedc3 — data serial (ASIC UART passthrough)

Copy link
Copy Markdown

@johnnyasantoss johnnyasantoss left a comment

Choose a reason for hiding this comment

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

tested locally and lgtm

Comment thread Cargo.toml
Comment on lines +48 to +50
core-foundation = "0.10"
io-kit-sys = "0.4"
mach2 = "0.4"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

these versions are not the latest, any specific reason to prefer those?

rkuester added a commit that referenced this pull request Mar 31, 2026
Implement get_serial_ports on macOS using IOKit to locate
/dev/cu.* devices by walking the IORegistry parent chain and
matching on location ID.

Includes a retry loop for the same device-node-not-yet-created
race that exists on Linux, though the permissions race does not
apply since macOS creates nodes with final permissions.

Adds core-foundation and io-kit-sys as macOS-only dependencies.

Thanks to Skot for PR #39, which inspired this implementation.
This takes a different direction by using nusb for shared
enumeration and hotplug across platforms, reducing the amount
of unsafe code we maintain to just the serial port lookup.

Co-authored-by: Skot <skot@bitnet.cx>
@rkuester
Copy link
Copy Markdown
Collaborator

Merged this feature to main with a slightly different approach in d174cde. I've tested this with Bitaxe 601 connected to an M3 Mac running macOS 26.3 via the hub on a Studio Display. Other test reports are welcome, thanks!

@rkuester rkuester closed this Mar 31, 2026
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.

5 participants