Swift中的USB设备路径

时间:2016-10-12 21:01:42

标签: swift macos arduino usb

这是我的代码。我能够检测到USB设备何时连接,然后轮询它以查看设备的设备名称是什么。我希望能够使用类似于查找BSD路径的内容(我认为这就是它所称的内容)或TTY / CU安装位置。当我尝试更改密钥时,我最终收到EXCC_BAD_INSTRUCTION错误。

enter image description here

我知道它一定是在某个地方! :P任何帮助将不胜感激!提前谢谢!

    import Foundation
    import Cocoa

    class USBDetector {
        class func monitorUSBEvent() {
            var portIterator: io_iterator_t = 0
            let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
            let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
            let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
            let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
            CFRunLoopAddSource(gRunLoop, runLoopSource.takeRetainedValue(), kCFRunLoopDefaultMode)
            let observer = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
            _ = IOServiceAddMatchingNotification(gNotifyPort,
                                                 kIOMatchedNotification,
                                                 matchingDict,
                                                 deviceAdded,
                                                 observer,
                                                 &portIterator)
            deviceAdded(nil, iterator: portIterator)
            _ = IOServiceAddMatchingNotification(gNotifyPort,
                                                 kIOTerminatedNotification,
                                                 matchingDict,
                                                 deviceRemoved,
                                                 observer,
                                                 &portIterator)
            deviceRemoved(nil, iterator: portIterator)
        }
        class func check() {
            var portIterator: io_iterator_t = 0
            let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
            let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
            let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
            let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
            CFRunLoopAddSource(gRunLoop, runLoopSource.takeRetainedValue(), kCFRunLoopDefaultMode)
            let observer = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
            deviceAdded(nil, iterator: portIterator)
            deviceRemoved(nil, iterator: portIterator)
        }
    }

    func deviceAdded(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) {
        var foundit = false
        var kr: kern_return_t = KERN_FAILURE
        while case let usbDevice = IOIteratorNext(iterator) where usbDevice != 0 {
            let deviceNameAsCFString = UnsafeMutablePointer<io_name_t>.alloc(1)
            defer {deviceNameAsCFString.dealloc(1)}
            kr = IORegistryEntryGetName(usbDevice, UnsafeMutablePointer(deviceNameAsCFString))
            if kr != KERN_SUCCESS {
                deviceNameAsCFString.memory.0 = 0
            }
            let deviceName = String.fromCString(UnsafePointer(deviceNameAsCFString))


            let key: CFString! = "BSD Path"
            let bsdPathAsCFtring: AnyObject? = IORegistryEntryCreateCFProperty(usbDevice, key, kCFAllocatorDefault, 0).takeUnretainedValue()
            let bsdPath = bsdPathAsCFtring as! String?
            if let path = bsdPath {
                print(path)
            }

            print("Found: \(deviceName!)")
            if(deviceName == "Arduino Leonardo") {
                foundit = true
            }
            IOObjectRelease(usbDevice)
        }
        var appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
        if(foundit == true) {
            appDelegate.arduboyhere();
        }
    }

    func deviceRemoved(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) {
        var foundit = false
        var kr: kern_return_t = KERN_FAILURE
        while case let usbDevice = IOIteratorNext(iterator) where usbDevice != 0 {
            let deviceNameAsCFString = UnsafeMutablePointer<io_name_t>.alloc(1)
            defer {deviceNameAsCFString.dealloc(1)}
            kr = IORegistryEntryGetName(usbDevice, UnsafeMutablePointer(deviceNameAsCFString))
            if kr != KERN_SUCCESS {
                deviceNameAsCFString.memory.0 = 0
            }
            let deviceName = String.fromCString(UnsafePointer(deviceNameAsCFString))
            print("Removed: \(deviceName!)")
            if(deviceName == "Arduino Leonardo") {
                foundit = true
            }
            IOObjectRelease(usbDevice)sa
        }
        var appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
        if(foundit == true) {
            appDelegate.arduboygone();
        }
    }

1 个答案:

答案 0 :(得分:2)

这是一个适用于macOS的Swift游乐场。在macOS Sierra下测试。

代码底部的

是一个可以根据需要编辑的Example类。 它为您提供了一个带有Name,DeviceID,VendorID,ProductID,LocationId,VendorName,SerialNr,bsdPath和一些接口指针的struct USBDevice。

这是一个修改过的类,最初在这个溢出问题中制作了bij jtbandes:USB Connection Delegate on Swift

//Working example in XCode 8.3/macOS Sierra
//

import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib
import IOKit.serial

//from IOUSBLib.h
public let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil,
                                                                         0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
                                                                         0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
public let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
                                                                    0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
                                                                    0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

//from IOCFPlugin.h
public let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
                                                                   0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
                                                                   0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)

public struct USBDevice {
    public let id:UInt64
    public let vendorId:UInt16
    public let productId:UInt16
    public let name:String
    public let locationId:UInt32
    public let vendorName:String?
    public let serialNr:String?
    public let bsdPath:String?

    public let deviceInterfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
    public let plugInInterfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?

    public init(id:UInt64,
                vendorId:UInt16,
                productId:UInt16,
                name:String,
                locationId:UInt32,
                vendorName:String?,
                serialNr:String?,
                bsdPath:String?,
                deviceInterfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?,
                plugInInterfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?) {
        self.id = id
        self.vendorId = vendorId
        self.productId = productId
        self.name = name
        self.deviceInterfacePtrPtr = deviceInterfacePtrPtr
        self.plugInInterfacePtrPtr = plugInInterfacePtrPtr
        self.locationId = locationId
        self.vendorName = vendorName
        self.serialNr = serialNr
        self.bsdPath = bsdPath

    }
}

public protocol USBWatcherDelegate: class {
    /// Called on the main thread when a device is connected.
    func deviceAdded(_ device: io_object_t)

    /// Called on the main thread when a device is disconnected.
    func deviceRemoved(_ device: io_object_t)
}

/// An object which observes USB devices added and removed from the system.
/// Abstracts away most of the ugliness of IOKit APIs.
public class USBWatcher {
    private weak var delegate: USBWatcherDelegate?
    private let notificationPort = IONotificationPortCreate(kIOMasterPortDefault)
    private var addedIterator: io_iterator_t = 0
    private var removedIterator: io_iterator_t = 0

    public init(delegate: USBWatcherDelegate) {
        self.delegate = delegate

        func handleNotification(instance: UnsafeMutableRawPointer?, _ iterator: io_iterator_t) {
            //the delay here is very important, because it gives the usb port time to set the bsp path for instance, this is sometimes needed.
            //maybe it should be on another thread?
            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200), execute: {

                let watcher = Unmanaged<USBWatcher>.fromOpaque(instance!).takeUnretainedValue()
                let handler: ((io_iterator_t) -> Void)?
                switch iterator {
                case watcher.addedIterator: handler = watcher.delegate?.deviceAdded
                case watcher.removedIterator: handler = watcher.delegate?.deviceRemoved
                default: assertionFailure("received unexpected IOIterator"); return
                }
                while case let device = IOIteratorNext(iterator), device != IO_OBJECT_NULL {
                    handler?(device)
                    IOObjectRelease(device)
                }
           })

        }

        let query = IOServiceMatching(kIOUSBDeviceClassName)
        let opaqueSelf = Unmanaged.passUnretained(self).toOpaque()

        // Watch for connected devices.
        IOServiceAddMatchingNotification(
            notificationPort, kIOMatchedNotification, query,
            handleNotification, opaqueSelf, &addedIterator)

        handleNotification(instance: opaqueSelf, addedIterator)

        // Watch for disconnected devices.
        IOServiceAddMatchingNotification(
            notificationPort, kIOTerminatedNotification, query,
            handleNotification, opaqueSelf, &removedIterator)

        handleNotification(instance: opaqueSelf, removedIterator)

        // Add the notification to the main run loop to receive future updates.
        CFRunLoopAddSource(
            CFRunLoopGetMain(),
            IONotificationPortGetRunLoopSource(notificationPort).takeUnretainedValue(),
            .commonModes)
    }

    deinit {
        IOObjectRelease(addedIterator)
        IOObjectRelease(removedIterator)
        IONotificationPortDestroy(notificationPort)
    }
}

extension io_object_t {
    /// - Returns: The device's name.
    func name() -> String? {
        let buf = UnsafeMutablePointer<io_name_t>.allocate(capacity: 1)
        defer { buf.deallocate(capacity: 1) }
        return buf.withMemoryRebound(to: CChar.self, capacity: MemoryLayout<io_name_t>.size) {
            if IORegistryEntryGetName(self, $0) == KERN_SUCCESS {
                return String(cString: $0)
            }
            return nil
        }
    }

    func getInfo() -> USBDevice? {
        var score:Int32 = 0
        var kr:Int32 = 0
        var did:UInt64 = 0
        var vid:UInt16 = 0
        var pid:UInt16 = 0
        var lid:UInt32 = 0
        var _serialNr:String?
        var _vendorName:String?
        var _bsdPath:String?

        var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
        var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?

        kr = IORegistryEntryGetRegistryEntryID(self, &did)

        if(kr != kIOReturnSuccess) {
            print("Error getting device id")
        }

        kr = IOCreatePlugInInterfaceForService(
            self,
            kIOUSBDeviceUserClientTypeID,
            kIOCFPlugInInterfaceID,
            &plugInInterfacePtrPtr,
            &score)


        // Get plugInInterface for current USB device
        kr = IOCreatePlugInInterfaceForService(
            self,
            kIOUSBDeviceUserClientTypeID,
            kIOCFPlugInInterfaceID,
            &plugInInterfacePtrPtr,
            &score)

        // Dereference pointer for the plug-in interface
        if (kr != kIOReturnSuccess) {
            return nil
        }

        guard let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Plug-In Interface")
            return nil
        }

        // use plug in interface to get a device interface
        kr = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
            $0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
                plugInInterface.QueryInterface(
                    plugInInterfacePtrPtr,
                    CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                    $0)
            }
        }

        // dereference pointer for the device interface
        if (kr != kIOReturnSuccess) {
            return nil
        }

        guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Device Interface")
            return nil
        }

        kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)

        // kIOReturnExclusiveAccess is not a problem as we can still do some things
        if (kr != kIOReturnSuccess && kr != kIOReturnExclusiveAccess) {
            print("Could not open device (error: \(kr))")
            return nil
        }

        kr = deviceInterface.GetDeviceVendor(deviceInterfacePtrPtr, &vid)
        if (kr != kIOReturnSuccess) {
            return nil
        }

        kr = deviceInterface.GetDeviceProduct(deviceInterfacePtrPtr, &pid)
        if (kr != kIOReturnSuccess) {
            return nil
        }

        kr = deviceInterface.GetLocationID(deviceInterfacePtrPtr, &lid)
        if (kr != kIOReturnSuccess) {
            return nil
        }

        var umDict: Unmanaged<CFMutableDictionary>? = nil
        kr = IORegistryEntryCreateCFProperties(self as io_registry_entry_t, &umDict, kCFAllocatorDefault, 0)


        var dict = umDict?.takeRetainedValue() as? NSDictionary
        if let dict = dict {
         //to show all properties available
            /*
            print("----------------------------")
            for (key,value) in dict {
                print("\(key): \(value)")
            }
            print("----------------------------")
             */


            if let serialNumber = dict.value(forKey: kUSBSerialNumberString) as? String {
                _serialNr = serialNumber
            }


            if let vendorName = dict.value(forKey: "USB Vendor Name") as? String {
                _vendorName = vendorName
            }

        }
        if let deviceBSDName_cf = IORegistryEntrySearchCFProperty (self,
                                                                   kIOServicePlane,
                                                                   kIOCalloutDeviceKey as CFString,
                                                                   kCFAllocatorDefault,
                                                                   UInt32(kIORegistryIterateRecursively )){


            _bsdPath = "\(deviceBSDName_cf)"
        }

        if let name = self.name() {


        return USBDevice(id: did, vendorId: vid, productId: pid, name: name, locationId: lid, vendorName: _vendorName, serialNr: _serialNr, bsdPath: _bsdPath, deviceInterfacePtrPtr: deviceInterfacePtrPtr, plugInInterfacePtrPtr: plugInInterfacePtrPtr)

        }
        return nil
    }
}


import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

class Example: USBWatcherDelegate {
    private var usbWatcher: USBWatcher!
    init() {
        usbWatcher = USBWatcher(delegate: self)
    }

    func deviceAdded(_ device: io_object_t) {
        print("device added: \(device.name() ?? "<unknown>")")
        if let usbDevice = device.getInfo() {
            print("usbDevice.getInfo(): \(usbDevice)")
        }else{
            print("usbDevice: no extra info")
        }
    }

    func deviceRemoved(_ device: io_object_t) {
        print("device removed: \(device.name() ?? "<unknown>")")
    }
}

let example = Example()