I'm developing a native macOS menu-bar utility that aims to toggle USB power to an iPhone connected directly to a MacBook Pro.
Hardware:
- MacBook Pro with M2 Pro
- iPhone 13 connected via USB-C to Lightning
- No external hubs or adapters
Goal:
I want to temporarily stop the iPhone from charging without physically disconnecting the cable, then restore charging programmatically.
Losing the USB data connection while power is disabled is completely acceptable.
What I've investigated:
uhubctldoesn't expose the built-in USB-C ports as controllable in my setup.- IOKit and USBDriverKit provide USB device management, but I haven't found a supported method to disable physical VBUS.
- I found
IOUSBHostCIMessageTypePortPowerOff, but it appears to relate to host-controller interfaces rather than controlling Apple's existing USB-C hardware.
My questions:
- Is there any supported API or system-level mechanism for disabling VBUS on a MacBook's built-in USB-C port?
- Can the USB-C Power Delivery controller be instructed to stop supplying power without physically disconnecting the device?
- Alternatively, is there a supported mechanism for requesting that a connected iPhone temporarily stop drawing charging current?
I'm open to lower-level macOS development if necessary, but I'd like to understand whether this is technically possible before pursuing it.
Any insight from Apple engineers or developers familiar with Apple Silicon USB controllers would be greatly appreciated.
Is there any supported API or system-level mechanism for disabling VBUS on a MacBook's built-in USB-C port?
Can the USB-C Power Delivery controller be instructed to stop supplying power without physically disconnecting the device?
Basically, no, not really. Architecturally, power management is the host controller's responsibility, not individual devices, so the IOUSBHostDevice itself has no control over this. The host controller itself CAN and, in fact, does do this. For example, what actually happens if you plug in too many devices and exhaust bus power is the host controller turns "off"[1] devices until the power demand is low enough to function, then posts a dialog saying what it disabled.
However, all of this is internal to the host controller's own implementation. There's no way to send it from outside the kernel, nor any good way to do it inside the kernel.
I'll also note that even if you could turn "off" the port, I'm not confident you'd then be able to do this:
...then restore charging programmatically.
You'll notice that our guidance when we cut port power is that accessories need to be plugged/replugged. The issue here is that most accessories implement the initial USB configuration using a relatively simple state machine, which means there's no logic in place to repeat configuration once configuration has failed. The accessory isn't going to try and rejoin the bus until power is fully cycled, something our host controller doesn't actually "do". It's possible an iPhone might do this (its controller is considerably more advanced than a typical peripheral) but I wouldn't bet on it.
uhubctl doesn't expose the built-in USB-C ports as controllable in my setup.
Yes. uhubctl works by sending specific commands to a target hub, but the mac is the bus controller, not simply a hub. I don't think its individual ports are even addressable on the bus as they're simply "there", not something you'd configure/control through the bus.
Alternatively, is there a supported mechanism for requesting that a connected iPhone temporarily stop drawing charging current?
Hmmm... Maybe? Outside of the standard USB spec, DriverKit/IOKit has its own power management system which, in theory, can tell the device to turn "off". What I don't know is what will actually happen when you do that. These methods are actually how the system manages going in and out of sleep, which means they're really sending requests through the power management system, not necessarily forcing a physical transition. The host controller will get the power off request, but it's entirely possible that it will simply register the port as "willing" to turn off but not actually change anything, since the controller isn't going to sleep.
However, keep in mind two things:
Any DEXT that does this is going to seize control of the device, which means it's going to disrupt all the systems normal interaction with the device.
Due to #1, it's VERY unlikely that we'll approve a DEXT entitlement request for this.
In other words, even if it does work, it's very likely to create more problems than it solves.
[1] Strictly speaking, the port isn't actually "off", as what actually happens is that the controller sends a command denying power access to the accessory. Technically, the accessory is still allowed to draw some power (~100mA), but that isn't sufficient power for most device to be "useful" and, more importantly, the accessory is still classified and unconfigured which means it isn't actually "on" the USB bus.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware