Is it possible to programmatically disable USB-C VBUS power on Apple Silicon Macs?

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:

  • uhubctl doesn'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:

  1. Is there any supported API or system-level mechanism for disabling VBUS on a MacBook's built-in USB-C port?
  2. Can the USB-C Power Delivery controller be instructed to stop supplying power without physically disconnecting the device?
  3. 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.

Answered by DTS Engineer in 912547022
  1. Is there any supported API or system-level mechanism for disabling VBUS on a MacBook's built-in USB-C port?

  2. 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

Accepted Answer
  1. Is there any supported API or system-level mechanism for disabling VBUS on a MacBook's built-in USB-C port?

  2. 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

Thank you so much for the detailed explanation, Kevin! I really appreciate you taking the time to explain how this works internally.

Just to give you some context, I'm an iOS engineer, so my iPhone is almost always connected to my MacBook for development and debugging. I've always found the constant charging a little bothersome, which got me thinking about whether I could control it without physically unplugging the device.

That led me to the idea of a simple macOS menu-bar utility that could toggle USB power on and off. I can also see broader applications, like scheduling when specific ports should supply power or automatically cutting power after a certain period.

I'm aware that modern devices use USB Power Delivery (USB-PD) for power negotiation and have sophisticated battery management systems that regulate charging current, voltage, and temperature. Features like Optimized Battery Charging also help reduce battery wear. However, these mechanisms don't entirely eliminate lithium-ion battery degradation, particularly when a battery spends prolonged periods at a high state of charge or experiences elevated temperatures. I figured having some additional control over power delivery could be useful, especially for devices that remain connected for extended periods.

Given the limitations you've explained, I'm now exploring a hardware-based workaround: a tiny USB-C male-to-female adapter with an Arduino-compatible microcontroller that could be configured and controlled directly from macOS.

I'm still figuring out how feasible that would be, but it's been a genuinely interesting problem to explore.

Thanks again for the insight!