Creating fake/virtual USB devices on macOS using IOUSBHostControllerInterface
March 7, 2026 · View on GitHub
// ConsumerKeys.swift // Boot-protocol keyboard that reports keycodes in Consumer page (0x0C). // iOS maps consumer page usages received this way to system actions.
import Foundation
public final class ConsumerKeys: SyntheticHID {
// MARK: - Consumer usages sent as boot-keyboard keycodes (page 0x0C, ≤ 0xFF)
public enum Key: UInt8 {
case acHome = 0x40 // Consumer "Menu" → iOS home button
case mute = 0xE2
case volumeUp = 0xE9
case volumeDown = 0xEA
case playPause = 0xCD
case nextTrack = 0xB5
case prevTrack = 0xB6
}
// MARK: - Init
public init() {
super.init(
reportDescriptor: Self.reportDescriptor,
reportSize: 8,
vendorID: 0x05AC,
productID: 0x0002,
manufacturer: "Apple Inc.",
product: "Virtual Consumer Control",
interfaceSubClass: 0x01,
interfaceProtocol: 0x01)
}
// MARK: - Key injection
public func keyDown(_ key: Key) {
sendReport([0, 0, key.rawValue, 0, 0, 0, 0, 0])
}
public func keyUp() {
clearReport()
}
// MARK: - Report descriptor
// Minimal boot-keyboard shell with Consumer page (0x0C) keycodes.
private static let reportDescriptor: [UInt8] = [
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x19, 0x00, // Usage Minimum (0)
0x29, 0xFF, // Usage Maximum (255)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x08, // Report Count (8)
0x81, 0x00, // Input (Data, Array)
0xC0, // End Collection
]
}