diff --git a/macos/QMK Toolbox.xcodeproj/project.pbxproj b/macos/QMK Toolbox.xcodeproj/project.pbxproj index d105b48cc8..25c7d86722 100644 --- a/macos/QMK Toolbox.xcodeproj/project.pbxproj +++ b/macos/QMK Toolbox.xcodeproj/project.pbxproj @@ -57,6 +57,8 @@ 3AAB20AB283BEEC700029ABD /* LogTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB20AA283BEEC700029ABD /* LogTextView.swift */; }; 3AB09F1D28B46672006CC212 /* GD32VDFUDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB09F1C28B46672006CC212 /* GD32VDFUDevice.swift */; }; 3AB4BC9D2495540A00204A3F /* bootloadHID in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3AB4BC9C2495540A00204A3F /* bootloadHID */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3AB657102B9EABB4007805DD /* RawDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB6570F2B9EABB4007805DD /* RawDevice.swift */; }; + 3AB657122B9EAC34007805DD /* HIDDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB657112B9EAC34007805DD /* HIDDevice.swift */; }; 3AE86EF8294C9CEC00008D3E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3AE86EFA294C9CEC00008D3E /* Main.storyboard */; }; 9BE10718275F4CFE00C708D5 /* wb32-dfu-updater_cli in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE10717275F4CFE00C708D5 /* wb32-dfu-updater_cli */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C93A0FF42292232E0006C88F /* reset.eep in Resources */ = {isa = PBXBuildFile; fileRef = C93A0FF32292232D0006C88F /* reset.eep */; }; @@ -150,6 +152,8 @@ 3AAB20AA283BEEC700029ABD /* LogTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogTextView.swift; sourceTree = ""; }; 3AB09F1C28B46672006CC212 /* GD32VDFUDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GD32VDFUDevice.swift; sourceTree = ""; }; 3AB4BC9C2495540A00204A3F /* bootloadHID */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = bootloadHID; sourceTree = ""; }; + 3AB6570F2B9EABB4007805DD /* RawDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawDevice.swift; sourceTree = ""; }; + 3AB657112B9EAC34007805DD /* HIDDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIDDevice.swift; sourceTree = ""; }; 3AE86EF9294C9CEC00008D3E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 3AFD4BCF281AB83C00ADCB65 /* libhidapi.0.14.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libhidapi.0.14.0.dylib; sourceTree = ""; }; 9BE10717275F4CFE00C708D5 /* wb32-dfu-updater_cli */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "wb32-dfu-updater_cli"; sourceTree = ""; }; @@ -220,6 +224,8 @@ 3A8DE019284636780012063A /* HIDConsoleDevice.swift */, 3A708D99284901F500394E52 /* HIDListener.swift */, 3A92873C292CF7A10015D961 /* HIDConsoleViewController.swift */, + 3AB657112B9EAC34007805DD /* HIDDevice.swift */, + 3AB6570F2B9EABB4007805DD /* RawDevice.swift */, ); path = HID; sourceTree = ""; @@ -375,6 +381,7 @@ buildActionMask = 2147483647; files = ( 3A8DE01A284636780012063A /* HIDConsoleDevice.swift in Sources */, + 3AB657102B9EABB4007805DD /* RawDevice.swift in Sources */, 3A708D9A284901F500394E52 /* HIDListener.swift in Sources */, 3A92873D292CF7A10015D961 /* HIDConsoleViewController.swift in Sources */, 3A37607A283E769300C19B3F /* KeyView.swift in Sources */, @@ -394,6 +401,7 @@ 3A32CF5F284142D10016D7B7 /* STM32DFUDevice.swift in Sources */, 3A32CF61284143990016D7B7 /* STM32DuinoDevice.swift in Sources */, 3A32CF63284143EC0016D7B7 /* USBAspDevice.swift in Sources */, + 3AB657122B9EAC34007805DD /* HIDDevice.swift in Sources */, 3A32CF652841445E0016D7B7 /* USBTinyISPDevice.swift in Sources */, 3A32CF672841451D0016D7B7 /* WB32DFUDevice.swift in Sources */, 3A5A916C28410F53004DD9BD /* USBDevice.swift in Sources */, diff --git a/macos/QMK Toolbox/HID/HIDConsoleDevice.swift b/macos/QMK Toolbox/HID/HIDConsoleDevice.swift index 9b0962d2ae..4f32d2f7aa 100644 --- a/macos/QMK Toolbox/HID/HIDConsoleDevice.swift +++ b/macos/QMK Toolbox/HID/HIDConsoleDevice.swift @@ -4,37 +4,14 @@ protocol HIDConsoleDeviceDelegate: AnyObject { func consoleDevice(_ device: HIDConsoleDevice, didReceiveReport report: String) } -class HIDConsoleDevice: Equatable, CustomStringConvertible { +class HIDConsoleDevice: HIDDevice { weak var delegate: HIDConsoleDeviceDelegate? private var reportBuffer: UnsafeMutablePointer private var reportBufferSize: Int = 0 - let hidDevice: IOHIDDevice - - var manufacturer: String? { - HIDConsoleDevice.stringProperty(kIOHIDManufacturerKey, for: hidDevice) - } - - var product: String? { - HIDConsoleDevice.stringProperty(kIOHIDProductKey, for: hidDevice) - } - - var vendorID: UInt16 { - HIDConsoleDevice.uint16Property(kIOHIDVendorIDKey, for: hidDevice) - } - - var productID: UInt16 { - HIDConsoleDevice.uint16Property(kIOHIDProductIDKey, for: hidDevice) - } - - var revisionBCD: UInt16 { - HIDConsoleDevice.uint16Property(kIOHIDVersionNumberKey, for: hidDevice) - } - - init(_ device: IOHIDDevice) { - hidDevice = device + override init(_ device: IOHIDDevice) { reportBufferSize = IOHIDDeviceGetProperty(device, kIOHIDMaxInputReportSizeKey as CFString) as! Int reportBuffer = UnsafeMutablePointer.allocate(capacity: reportBufferSize) @@ -44,6 +21,8 @@ class HIDConsoleDevice: Equatable, CustomStringConvertible { device.reportReceived(reportData) } + super.init(device) + let unsafeSelf = Unmanaged.passRetained(self).toOpaque() IOHIDDeviceRegisterInputReportCallback(hidDevice, reportBuffer, reportBufferSize, inputReportCallback, unsafeSelf) } @@ -75,20 +54,4 @@ class HIDConsoleDevice: Equatable, CustomStringConvertible { delegate?.consoleDevice(self, didReceiveReport: completedLine) } } - - var description: String { - String(format: "%@ %@ (%04X:%04X:%04X)", manufacturer ?? "", product ?? "", vendorID, productID, revisionBCD) - } - - static func == (lhs: HIDConsoleDevice, rhs: HIDConsoleDevice) -> Bool { - return lhs.hidDevice === rhs.hidDevice - } - - static func stringProperty(_ propertyName: String, for device: IOHIDDevice) -> String? { - return IOHIDDeviceGetProperty(device, propertyName as CFString) as! String? - } - - static func uint16Property(_ propertyName: String, for device: IOHIDDevice) -> UInt16 { - return (IOHIDDeviceGetProperty(device, propertyName as CFString) as! NSNumber?)!.uint16Value - } } diff --git a/macos/QMK Toolbox/HID/HIDConsoleViewController.swift b/macos/QMK Toolbox/HID/HIDConsoleViewController.swift index 101dca4182..27ad31c44d 100644 --- a/macos/QMK Toolbox/HID/HIDConsoleViewController.swift +++ b/macos/QMK Toolbox/HID/HIDConsoleViewController.swift @@ -24,28 +24,36 @@ class HIDConsoleViewController: NSViewController, HIDListenerDelegate { var hidListener: HIDListener! var lastReportedDevice: HIDConsoleDevice? - - func hidDeviceDidConnect(_ device: HIDConsoleDevice) { - lastReportedDevice = device - updateConsoleList() - logTextView.logHID("HID console connected: \(device)") + func hidDeviceDidConnect(_ device: HIDDevice) { + if device is HIDConsoleDevice { + lastReportedDevice = (device as! HIDConsoleDevice) + updateConsoleList() + logTextView.logHID("HID console connected: \(device)") + } else { + logTextView.logHID("Raw HID device connected: \(device)") + } } - func hidDeviceDidDisconnect(_ device: HIDConsoleDevice) { - lastReportedDevice = nil - updateConsoleList() - logTextView.logHID("HID console disconnected: \(device)") + func hidDeviceDidDisconnect(_ device: HIDDevice) { + if device is HIDConsoleDevice { + lastReportedDevice = nil + updateConsoleList() + logTextView.logHID("HID console disconnected: \(device)") + } else { + logTextView.logHID("Raw HID device disconnected: \(device)") + } } func consoleDevice(_ device: HIDConsoleDevice, didReceiveReport report: String) { let selectedDevice = consoleListBox.indexOfSelectedItem - if selectedDevice == 0 || hidListener.devices[selectedDevice - 1] == device { + let consoleDevices = hidListener.devices.filter { $0 is HIDConsoleDevice } + if selectedDevice == 0 || consoleDevices[selectedDevice - 1] == device { if lastReportedDevice != device { logTextView.logHID("\(device.manufacturer ?? "") \(device.product ?? "")") lastReportedDevice = device } + logTextView.logHIDOutput(report) } - logTextView.logHIDOutput(report) } func updateConsoleList() { @@ -53,7 +61,7 @@ class HIDConsoleViewController: NSViewController, HIDListenerDelegate { consoleListBox.deselectItem(at: selectedItem) consoleListBox.removeAllItems() - hidListener.devices.forEach { device in + hidListener.devices.filter { $0 is HIDConsoleDevice }.forEach { device in consoleListBox.addItem(withObjectValue: device.description) } diff --git a/macos/QMK Toolbox/HID/HIDDevice.swift b/macos/QMK Toolbox/HID/HIDDevice.swift new file mode 100644 index 0000000000..2525db4100 --- /dev/null +++ b/macos/QMK Toolbox/HID/HIDDevice.swift @@ -0,0 +1,53 @@ +import Foundation + +class HIDDevice: Equatable, CustomStringConvertible { + let hidDevice: IOHIDDevice + + var usagePage: UInt16 { + HIDDevice.uint16Property(kIOHIDDeviceUsagePageKey, for: hidDevice)! + } + + var usage: UInt16 { + HIDDevice.uint16Property(kIOHIDDeviceUsageKey, for: hidDevice)! + } + + var manufacturer: String? { + HIDDevice.stringProperty(kIOHIDManufacturerKey, for: hidDevice) + } + + var product: String? { + HIDDevice.stringProperty(kIOHIDProductKey, for: hidDevice) + } + + var vendorID: UInt16 { + HIDDevice.uint16Property(kIOHIDVendorIDKey, for: hidDevice)! + } + + var productID: UInt16 { + HIDDevice.uint16Property(kIOHIDProductIDKey, for: hidDevice)! + } + + var revisionBCD: UInt16 { + HIDDevice.uint16Property(kIOHIDVersionNumberKey, for: hidDevice)! + } + + init(_ device: IOHIDDevice) { + hidDevice = device + } + + var description: String { + String(format: "%@ %@ (%04X:%04X:%04X)", manufacturer ?? "", product ?? "", vendorID, productID, revisionBCD) + } + + static func == (lhs: HIDDevice, rhs: HIDDevice) -> Bool { + return lhs.hidDevice === rhs.hidDevice + } + + static func stringProperty(_ propertyName: String, for device: IOHIDDevice) -> String? { + return IOHIDDeviceGetProperty(device, propertyName as CFString) as! String? + } + + static func uint16Property(_ propertyName: String, for device: IOHIDDevice) -> UInt16? { + return (IOHIDDeviceGetProperty(device, propertyName as CFString) as! NSNumber?)!.uint16Value + } +} diff --git a/macos/QMK Toolbox/HID/HIDListener.swift b/macos/QMK Toolbox/HID/HIDListener.swift index 3c0c7ceb40..4b50cbdf7e 100644 --- a/macos/QMK Toolbox/HID/HIDListener.swift +++ b/macos/QMK Toolbox/HID/HIDListener.swift @@ -3,11 +3,13 @@ import IOKit.hid let CONSOLE_USAGE_PAGE: UInt16 = 0xFF31 let CONSOLE_USAGE: UInt16 = 0x0074 +let RAW_USAGE_PAGE: UInt16 = 0xFF60 +let RAW_USAGE: UInt16 = 0x0061 protocol HIDListenerDelegate: AnyObject { - func hidDeviceDidConnect(_ device: HIDConsoleDevice) + func hidDeviceDidConnect(_ device: HIDDevice) - func hidDeviceDidDisconnect(_ device: HIDConsoleDevice) + func hidDeviceDidDisconnect(_ device: HIDDevice) func consoleDevice(_ device: HIDConsoleDevice, didReceiveReport report: String) } @@ -17,12 +19,11 @@ class HIDListener: HIDConsoleDeviceDelegate { private var hidManager: IOHIDManager - var devices: [HIDConsoleDevice] = [] + var devices: [HIDDevice] = [] init() { hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone)) - let consoleMatcher = [kIOHIDDeviceUsagePageKey: CONSOLE_USAGE_PAGE, kIOHIDDeviceUsageKey: CONSOLE_USAGE] - IOHIDManagerSetDeviceMatching(hidManager, consoleMatcher as CFDictionary?) + IOHIDManagerSetDeviceMatching(hidManager, nil) } func start() { @@ -49,10 +50,17 @@ class HIDListener: HIDConsoleDeviceDelegate { return } - let consoleDevice = HIDConsoleDevice(device) - consoleDevice.delegate = self - devices.append(consoleDevice) - delegate?.hidDeviceDidConnect(consoleDevice) + guard let hidDevice = createDevice(device) else { + return + } + + devices.append(hidDevice) + + if hidDevice is HIDConsoleDevice { + (hidDevice as! HIDConsoleDevice).delegate = self + } + + delegate?.hidDeviceDidConnect(hidDevice) } func deviceDisconnected(_ device: IOHIDDevice) { @@ -75,4 +83,17 @@ class HIDListener: HIDConsoleDeviceDelegate { IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue) IOHIDManagerClose(hidManager, IOOptionBits(kIOHIDOptionsTypeNone)) } + + func createDevice(_ d: IOHIDDevice) -> HIDDevice? { + let usagePage = HIDDevice.uint16Property(kIOHIDPrimaryUsagePageKey, for: d) + let usage = HIDDevice.uint16Property(kIOHIDPrimaryUsageKey, for: d) + + if usagePage == CONSOLE_USAGE_PAGE && usage == CONSOLE_USAGE { + return HIDConsoleDevice(d) + } else if usagePage == RAW_USAGE_PAGE && usage == RAW_USAGE { + return RawDevice(d) + } + + return nil + } } diff --git a/macos/QMK Toolbox/HID/RawDevice.swift b/macos/QMK Toolbox/HID/RawDevice.swift new file mode 100644 index 0000000000..f92f08c6df --- /dev/null +++ b/macos/QMK Toolbox/HID/RawDevice.swift @@ -0,0 +1,3 @@ +import Foundation + +class RawDevice: HIDDevice {} diff --git a/macos/QMK Toolbox/USB/USBListener.swift b/macos/QMK Toolbox/USB/USBListener.swift index bbc28671ea..a124eedf64 100644 --- a/macos/QMK Toolbox/USB/USBListener.swift +++ b/macos/QMK Toolbox/USB/USBListener.swift @@ -193,7 +193,7 @@ class USBListener: BootloaderDeviceDelegate { if productID == 0xB007 { return .kiibohdDfu } - break; + break case 0x1EAF: // Leaflabs if productID == 0x0003 { return .stm32duino