Skip to content

Instantly share code, notes, and snippets.

@zachbadgett
Created December 15, 2015 16:29
Show Gist options
  • Save zachbadgett/471d72e83fee413d0f38 to your computer and use it in GitHub Desktop.
Save zachbadgett/471d72e83fee413d0f38 to your computer and use it in GitHub Desktop.
import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib
print("Scanning USB Bus.....\n\n\n")
//
// These constants are not imported into Swift from IOUSBLib.h as they
// are all #define constants
//
let kIOUSBDeviceUserClientTypeID: CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
let kIOCFPlugInInterfaceID: CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)
let kIOUSBDeviceInterfaceID: CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
var usbIterator: io_iterator_t = io_iterator_t()
var usbDevice: io_service_t = io_service_t()
var usbVendorID: UInt16 = 0
var score: Int32 = 0
var plugInInterfacePtrPtr = UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>>()
// From: CFPlugInCOM.h: public typealias LPVOID = UnsafeMutablePointer<Void>()
var deviceInterfaceVoidPtr = UnsafeMutablePointer<Void>()
// create dictionary with IOUSBDevice as IOProviderClass
let matchingDictionary: NSMutableDictionary = IOServiceMatching(kIOUSBDeviceClassName)
// get iterator for matching USB devices
let matchingServicesResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &usbIterator)
if (matchingServicesResult != kIOReturnSuccess) {
print("Error getting deviceList!")
exit(EXIT_FAILURE)
}
// usbDevice = 0 when finished iterating all devices
repeat {
usbDevice = IOIteratorNext(usbIterator)
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](count: 128, repeatedValue: 0)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
exit(EXIT_FAILURE)
}
let deviceName = String.fromCString(&deviceNameCString)!
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
if ( (plugInInterfacePtrPtr == nil) || (plugInInterfaceResult != kIOReturnSuccess)) {
print("Unable to get Plug-In Interface")
continue
}
// dereference pointer for the plug in interface
let plugInInterface: IOCFPlugInInterface = plugInInterfacePtrPtr.memory.memory
// use plug in interface to get a device interface
// public var QueryInterface: (@convention(c) (UnsafeMutablePointer<Void>, REFIID, UnsafeMutablePointer<LPVOID>) -> HRESULT)!
let deviceInterfaceResult = plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
&deviceInterfaceVoidPtr)
if( (deviceInterfaceResult != kIOReturnSuccess) || (deviceInterfaceVoidPtr == nil) ) {
print("Unable to get Device Interface")
exit(EXIT_FAILURE)
}
// dereference the IOUSBDeviceInterface struct from pointer var after
// converting from a void to a IOUSBDeviceInterface pointer
let deviceInterface = (UnsafeMutablePointer<IOUSBDeviceInterface>(deviceInterfaceVoidPtr)).memory
// get USB Vendor ID --> CRASH
let vendorResult = deviceInterface.GetDeviceVendor(deviceInterfaceVoidPtr, &usbVendorID)
if(vendorResult != kIOReturnSuccess) {
print("Unable to get Device Vendor ID")
exit(EXIT_FAILURE)
}
print("usb Vendor ID: \(usbVendorID)")
usbDevice = IOIteratorNext(usbIterator)
} while (usbDevice != 0);
exit(EXIT_SUCCESS)
@cellininicholas
Copy link

Hey!
You shouldn't have

usbDevice = IOIteratorNext(usbIterator)

on the first AND last line of your loop, you are skipping over elements from the iterator

@Arti3DPlayer
Copy link

I have created USBDeviceSwift library for convenient work with IOKit.usb and IOKit.hid

@Mikegim
Copy link

Mikegim commented Oct 9, 2017

@Arti3DPlayer
I started very little to study to program for Mac OSX, as by Apple's advice I'm studying Swift, I just updated to Xcode9 with swift 4.
I wanted to use the USB and found your USBDeviceSwift project interesting, only I have difficulty using it.
I followed the instructions to install the wrap only through cocoapods.
I get an error in the line:

let stm32DeviceMonitor = USBDeviceMonitor ([
         VIDPID (vendorId: 0x0483, productId: 0xdf11)
     ])
  
What can it depend on?
Greetings
Mike

@Mikegim
Copy link

Mikegim commented Oct 9, 2017

the VIDPID command is not recognized.

@ogtega
Copy link

ogtega commented Apr 16, 2025

Hate to necro this gist ten years later, but this is my swift 5/6 implementation of using IOKit to find USB devices.

Hope this helps anyone else who stumbles upon this!

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

let kIOUSBDeviceUserClientTypeID: CFUUID = CFUUIDGetConstantUUIDWithBytes(
    kCFAllocatorDefault,
    0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xd4,
    0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

let kIOCFPlugInInterfaceID: CFUUID = CFUUIDGetConstantUUIDWithBytes(
    kCFAllocatorDefault,
    0xc2, 0x44, 0xe8, 0x58, 0x10, 0x9c, 0x11, 0xd4,
    0x91, 0xd4, 0x00, 0x50, 0xe4, 0xc6, 0x42, 0x6f)

let kIOUSBDeviceInterfaceID: CFUUID = CFUUIDGetConstantUUIDWithBytes(
    kCFAllocatorDefault,
    0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xd4,
    0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

let targetVendorId: UInt16 = 0x0000  // Replace with your Vendor ID
let targetProductId: UInt16 = 0x0000  // Replace with your Product ID

func findAndOpenDevice() -> UnsafeMutablePointer<
    UnsafeMutablePointer<IOUSBDeviceInterface>
>? {
    var iterator: io_iterator_t = 0
    let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
    let kIoDefaultMatchPort =
        if #available(macOS 12, *) {
            kIOMainPortDefault
        } else {
            kIOMasterPortDefault
        }

    guard
        IOServiceGetMatchingServices(
            kIoDefaultMatchPort, matchingDict, &iterator)
            == KERN_SUCCESS
    else {
        print("Could not find matching USB devices")
        return nil
    }

    defer { IOObjectRelease(iterator) }

    while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
        var score: Int32 = 0
        var plugInPtr:
            UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
        var devicePtr: LPVOID? = nil

        defer { IOObjectRelease(usbDevice) }

        let deviceName = String(
            cString: {
                var cstring: [CChar] = [CChar](repeating: 0, count: 128)

                guard
                    IORegistryEntryGetName(usbDevice, &cstring)
                        == kIOReturnSuccess
                else {
                    return [CChar](repeating: 0, count: 1)
                }

                return cstring
            }())

        let kr = IOCreatePlugInInterfaceForService(
            usbDevice,
            kIOUSBDeviceUserClientTypeID,
            kIOCFPlugInInterfaceID,
            &plugInPtr,
            &score
        )

        guard
            kr == kIOReturnSuccess, let plugInPtr = plugInPtr,
            let plugInInterfacePtr = plugInPtr.pointee
        else {
            print("Unable to create a plug-in \(kr)")
            continue
        }

        defer {
            _ = plugInInterfacePtr.pointee.Release(plugInPtr)
        }

        let result = plugInInterfacePtr.pointee.QueryInterface(
            plugInPtr,
            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
            &devicePtr
        )

        guard result == 0,
            let devicePtr:
                UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>>
                = devicePtr?
                .assumingMemoryBound(
                    to: UnsafeMutablePointer<IOUSBDeviceInterface>.self)
        else {
            print("Couldn't create a device interface \(result)")
            continue
        }

        let deviceInterfacePtr = devicePtr.pointee

        var vid: UInt16 = 0
        var pid: UInt16 = 0
        _ = deviceInterfacePtr.pointee.GetDeviceVendor(devicePtr, &vid)
        _ = deviceInterfacePtr.pointee.GetDeviceProduct(devicePtr, &pid)

        print(
            "VID: \(String(format:"%04X", vid)) | PID: \(String(format:"%04X", pid)) | Name: \(deviceName)"
        )

        if vid == targetVendorId && pid == targetProductId {
            // Open the device before using it
            let openResult = deviceInterfacePtr.pointee.USBDeviceOpen(devicePtr)
            if openResult != kIOReturnSuccess {
                print("Failed to open device: \(openResult)")
                _ = deviceInterfacePtr.pointee.Release(devicePtr)
                return nil
            }
            print("Found and opened \(deviceName)")
            return devicePtr
        } else {
            _ = deviceInterfacePtr.pointee.Release(devicePtr)
        }
    }
    return nil
}

if let devicePtr = findAndOpenDevice() {
    let deviceInterface = devicePtr.pointee

    // Always close and release the device interface when done
    _ = deviceInterface.pointee.USBDeviceClose(devicePtr)
    _ = deviceInterface.pointee.Release(devicePtr)
} else {
    print("Device not found or interface setup failed.")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment