//
// USB.mm
// iOUSB
//
// Created by Brandon on 2018-05-26.
// Copyright © 2018 XIO. All rights reserved.
//
#import "USB.h"
#import <IOKit/IOKitLib.h>
#import <IOKit/usb/IOUSBLib.h>
#import <IOKit/IOCFPlugIn.h>
#define kNintendoSwitchVendorID 0x0955
#define kNintendoSwitchProductID 0x7321
@implementation USB
- (NSArray<NSString *> *)getUSBDevices {
NSMutableArray *strings = [[NSMutableArray alloc] init];
CFMutableDictionaryRef matchingDict;
io_iterator_t iter;
kern_return_t kr;
io_service_t device;
matchingDict = IOServiceMatching("IOUSBHostDevice"); //kIOUSBDeviceClassName for OSX but "IOUSBHostDevice" for iOS..
if (matchingDict == NULL)
{
return nil;
}
//Add Nintendo Switch
long usbVendor = kNintendoSwitchVendorID;
long usbProduct = kNintendoSwitchProductID;
CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbVendor);
CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef);
CFRelease(numberRef);
// Create a CFNumber for the idProduct and set the value in the dictionary
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbProduct);
CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef);
CFRelease(numberRef);
numberRef = NULL;
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr != KERN_SUCCESS)
{
return nil;
}
while ((device = IOIteratorNext(iter)))
{
io_name_t devName;
io_name_t className;
io_string_t pathName;
io_string_t planeName;
IORegistryEntryGetName(device, devName);
IOObjectGetClass(device, className);
IORegistryEntryGetPath(device, kIOServicePlane, pathName);
IORegistryEntryGetPath(device, kIOUSBPlane, planeName);
[strings addObject:[NSString stringWithFormat:@"Device Name: %s", devName]];
[strings addObject:[NSString stringWithFormat:@"Device Class: %s", className]];
[strings addObject:[NSString stringWithFormat:@"Device Plane: %s", pathName]];
[strings addObject:[NSString stringWithFormat:@"Device Path: %s", planeName]];
[strings addObject:@"\n"];
if ([[NSString stringWithUTF8String:devName] isEqualToString:@"APX"]) {
int vendorId = [self getDeviceVendorId:device];
int productId = [self getDeviceProductId:device];
NSString *serialNumber = [self getDeviceSerialNumber:device];
NSString *manufacturer = [self getDeviceManufacturer:device];
[strings addObject:[NSString stringWithFormat:@"VendorID: %04X", vendorId]];
[strings addObject:[NSString stringWithFormat:@"ProductID: %04X", productId]];
[strings addObject:[NSString stringWithFormat:@"Device Serial Number: %@", serialNumber]];
[strings addObject:[NSString stringWithFormat:@"Device Manufacturer: %@", manufacturer]];
}
IOObjectRelease(device);
}
IOObjectRelease(iter);
return strings;
}
- (int)getDeviceVendorId:(io_service_t)device {
return [self getUSBProperty:device propertyName:@"idVendor"];
}
- (int)getDeviceProductId:(io_service_t)device {
return [self getUSBProperty:device propertyName:@"idProduct"];
}
- (NSString *)getDeviceSerialNumber:(io_service_t)device {
IOUSBDeviceInterface182 **deviceInterface = [self getDeviceInterface:device];
if (deviceInterface) {
UInt8 index;
(*deviceInterface)->USBGetSerialNumberStringIndex(deviceInterface, &index);
return [self getDeviceStringDescriptor:deviceInterface index:index];
}
return nil;
}
- (NSString *)getDeviceManufacturer:(io_service_t)device {
IOUSBDeviceInterface182 **deviceInterface = [self getDeviceInterface:device];
if (deviceInterface) {
UInt8 index;
(*deviceInterface)->USBGetManufacturerStringIndex(deviceInterface, &index);
return [self getDeviceStringDescriptor:deviceInterface index:index];
}
return nil;
}
/// MARK: -
- (int)getUSBProperty:(io_service_t)device propertyName:(NSString *)propertyName {
CFNumberRef number = (CFNumberRef)IORegistryEntryCreateCFProperty(device, (__bridge CFStringRef)(propertyName), kCFAllocatorDefault, 0);
int value = 0;
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
CFRelease(number);
return value;
}
- (IOUSBDeviceInterface182 **)getDeviceInterface:(io_service_t)device {
kern_return_t result;
SInt32 score;
IOCFPlugInInterface **plugin = nil;
result = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
if (result != KERN_SUCCESS)
{
return nil;
}
IOUSBDeviceInterface182 **deviceInterface = nil;
result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182), (void **)&deviceInterface);
if (result != KERN_SUCCESS)
{
IODestroyPlugInInterface(plugin);
return nil;
}
IODestroyPlugInInterface(plugin);
return deviceInterface;
}
- (NSString *)getDeviceStringDescriptor:(IOUSBDeviceInterface182 **)deviceInterface index:(uint8_t)index {
UInt8 requestBuffer[256];
IOUSBDevRequest request = {
.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice),
.bRequest = kUSBRqGetDescriptor,
.wValue = (kUSBStringDesc << 8) | index,
.wIndex = 0x409, //English
.wLength = sizeof(requestBuffer),
.pData = requestBuffer
};
kern_return_t result;
result = (*deviceInterface)->DeviceRequest(deviceInterface, &request);
if (result != KERN_SUCCESS)
{
return nil;
}
int strLength = requestBuffer[0] - 2;
CFStringRef serialNumberString = CFStringCreateWithBytes(kCFAllocatorDefault, &requestBuffer[2], strLength, kCFStringEncodingUTF16LE, false);
return (__bridge NSString *)serialNumberString;
}
@end