hmdwake.swift (3502B)
1 // Wakes the HP Reverb G1 (sequence from Monado wmr_hmd_activate_reverb), starts the IMU, and logs 2 // every control-device report with timestamps. Args: `passive` = send nothing, just listen. 3 // SIGUSR1 = resend screen_on, SIGUSR2 = screen off then on. 4 import Foundation 5 import IOKit.hid 6 setvbuf(stdout, nil, _IOLBF, 0) 7 8 func hex(_ b: [UInt8]) -> String { b.map { String(format: "%02x", $0) }.joined(separator: " ") } 9 func log(_ s: String) { print(String(format: "%.3f ", Date().timeIntervalSince1970) + s) } 10 var managers: [IOHIDManager] = [] // must outlive the devices, or they get closed 11 func open(_ vid: Int, _ pid: Int) -> IOHIDDevice? { 12 let m = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone)) 13 IOHIDManagerSetDeviceMatching(m, [kIOHIDVendorIDKey: vid, kIOHIDProductIDKey: pid] as CFDictionary) 14 guard IOHIDManagerOpen(m, IOOptionBits(kIOHIDOptionsTypeNone)) == kIOReturnSuccess else { return nil } 15 managers.append(m) 16 return (IOHIDManagerCopyDevices(m) as? Set<IOHIDDevice>)?.first 17 } 18 guard let ctl = open(0x03f0, 0x0c6a) else { log("QHMD control device not found"); exit(1) } 19 log("opened QHMD control device") 20 21 func setFeature(_ b: [UInt8], _ what: String) { 22 let r = IOHIDDeviceSetReport(ctl, kIOHIDReportTypeFeature, CFIndex(b[0]), b, b.count) 23 log("set \(what): " + (r == kIOReturnSuccess ? "ok" : String(format: "err 0x%08x", r))) 24 } 25 func getFeature(_ id: UInt8) { 26 var b = [UInt8](repeating: 0, count: 64); b[0] = id; var n = CFIndex(64) 27 let r = IOHIDDeviceGetReport(ctl, kIOHIDReportTypeFeature, CFIndex(id), &b, &n) 28 log("get 0x\(String(format: "%02x", id)): " + (r == kIOReturnSuccess ? "ok (feature payload omitted for privacy)" : String(format: "err 0x%08x", r))) 29 } 30 31 let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: 64) 32 IOHIDDeviceRegisterInputReportCallback(ctl, buf, 64, { _, _, _, _, _, r, n in 33 log("control report received (payload omitted for privacy)") 34 }, nil) 35 IOHIDDeviceScheduleWithRunLoop(ctl, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) 36 37 if CommandLine.arguments.contains("passive") { 38 log("PASSIVE: listening only") 39 } else { 40 usleep(300_000) 41 for _ in 0..<4 { 42 var c = [UInt8](repeating: 0, count: 64); c[0] = 0x50; c[1] = 0x01 43 setFeature(c, "activate 0x50"); getFeature(0x50); usleep(10_000) 44 } 45 getFeature(0x09); getFeature(0x08); getFeature(0x06) 46 setFeature([0x04, 0x01], "screen_on") 47 if let sens = open(0x045e, 0x0659) { 48 var c = [UInt8](repeating: 0, count: 64); c[0] = 0x02; c[1] = 0x07 49 let r = IOHIDDeviceSetReport(sens, kIOHIDReportTypeOutput, 0x02, c, c.count) 50 log("imu_on: " + (r == kIOReturnSuccess ? "ok" : String(format: "err 0x%08x", r))) 51 let sb = UnsafeMutablePointer<UInt8>.allocate(capacity: 512) 52 IOHIDDeviceRegisterInputReportCallback(sens, sb, 512, { _, _, _, _, _, _, _ in }, nil) // keep stream drained 53 IOHIDDeviceScheduleWithRunLoop(sens, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) 54 } else { log("HoloLens Sensors not found") } 55 } 56 signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN) 57 let s1 = DispatchSource.makeSignalSource(signal: SIGUSR1, queue: .main) 58 s1.setEventHandler { setFeature([0x04, 0x01], "screen_on (USR1)") }; s1.resume() 59 let s2 = DispatchSource.makeSignalSource(signal: SIGUSR2, queue: .main) 60 s2.setEventHandler { setFeature([0x04, 0x00], "screen_off (USR2)"); usleep(500_000); setFeature([0x04, 0x01], "screen_on (USR2)") }; s2.resume() 61 CFRunLoopRun()