Skip to content

Instantly share code, notes, and snippets.

@zydeco
Created March 7, 2017 12:30
Show Gist options
  • Save zydeco/4ba02afa99d3c234c2dfa975fec0b102 to your computer and use it in GitHub Desktop.
Save zydeco/4ba02afa99d3c234c2dfa975fec0b102 to your computer and use it in GitHub Desktop.
Simulate unplugging and plugging in of usb device
// cc reenumerate.c -framework IOKit -framework CoreFoundation -o reenumerate
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <mach/mach.h>
#define kExcAccMaxWait 5
static IONotificationPortRef gNotificationPort;
static io_iterator_t gDevIter;
static mach_port_t gMachPort;
void DeviceConnected (void *refCon, io_iterator_t iterator);
int main (int argc, char const *argv[])
{
if (argc < 3) {
fprintf(stderr, "usage: %s productID vendorID\n", argv[0]);
exit(1);
}
SInt32 productID, vendorID;
productID = strtol(argv[1], NULL, 0);
vendorID = strtol(argv[2], NULL, 0);
if (productID <= 0 || productID > 0xffff || vendorID <= 0 || vendorID > 0xffff) {
fprintf(stderr, "Invalid productID or vendorID\n");
exit(1);
}
printf("Looking for productID=0x%04x vendorID=0x%04x\n", productID, vendorID);
kern_return_t kerr;
kerr = IOMasterPort(MACH_PORT_NULL, &gMachPort);
if (kerr || !gMachPort) return 1;
CFMutableDictionaryRef dict = IOServiceMatching(kIOUSBDeviceClassName);
if (!dict) {
fprintf(stderr, "could not create matching dictionary for device vendor=0x%04X,product=0x%04X\n", vendorID, productID);
exit(1);
}
CFDictionarySetValue(dict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendorID));
CFDictionarySetValue(dict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &productID));
gNotificationPort = IONotificationPortCreate(gMachPort);
kerr = IOServiceAddMatchingNotification(gNotificationPort, kIOFirstMatchNotification, dict, DeviceConnected, NULL, &gDevIter);
if (kerr != kIOReturnSuccess) {
fprintf(stderr, "IOServiceAddMatchingNotification\n");
exit(1);
}
DeviceConnected(NULL, gDevIter);
mach_port_deallocate(mach_task_self(), gMachPort);
return 0;
}
IOReturn ConfigureDevice (IOUSBDeviceInterface245 **dev) {
UInt8 nConf;
IOUSBConfigurationDescriptorPtr confDescPtr;
(*dev)->GetNumberOfConfigurations(dev, &nConf);
if (nConf == 0) return kIOReturnError;
if ((*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDescPtr)) return kIOReturnError;
if ((*dev)->SetConfiguration(dev, confDescPtr->bConfigurationValue)) return kIOReturnError;
return kIOReturnSuccess;
}
void DeviceConnected (void *refCon, io_iterator_t iterator) {
kern_return_t kerr;
io_service_t device;
IOCFPlugInInterface **iodev;
IOUSBDeviceInterface245 **dev = NULL;
UInt16 vendor, product, version;
HRESULT result;
int u;
while ((device = IOIteratorNext(iterator))) {
// get device plugin
kerr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, (SInt32*)&u);
IOObjectRelease(device);
if (kerr != kIOReturnSuccess) {
fprintf(stderr, "could not create plug-in interface: %08x\n", kerr);
continue;
}
// get device interface
result = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245), (LPVOID)&dev);
IODestroyPlugInInterface(iodev);
if (result || !dev) {
fprintf(stderr, "could not get device interface: %08x\n", (int)result);
continue;
}
// get device data
(*dev)->GetDeviceVendor(dev, &vendor);
(*dev)->GetDeviceProduct(dev, &product);
(*dev)->GetDeviceReleaseNumber(dev, &version);
fprintf(stdout, "Found device vendor=0x%04X, product=0x%04X, version=0x%04X\n", vendor, product, version);
// open device
u = 0;
do {
kerr = (*dev)->USBDeviceOpen(dev);
if (kerr == kIOReturnExclusiveAccess) {
fprintf(stdout, "waiting for access (%d)\n", kExcAccMaxWait-u);
u++;
sleep(1);
}
} while ((kerr == kIOReturnExclusiveAccess) && (u < kExcAccMaxWait));
if (kerr != kIOReturnSuccess) {
fprintf(stderr, "could not open device: %08x\n", kerr);
(void) (*dev)->Release(dev);
continue;
}
// configure device
if (ConfigureDevice(dev) != kIOReturnSuccess) {
fprintf(stderr, "could not configure device\n");
(*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
continue;
}
// reenumerate to device
kerr = (*dev)->USBDeviceReEnumerate(dev, 0);
if (kerr != kIOReturnSuccess) {
fprintf(stdout, "USBDeviceReEnumerate: error %d\n", kerr);
}
// close device
(*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
}
}
@swanny8777
Copy link

Hi, so I'm new with a lot of the scripting procedures; is this script executable for Mac? I'm assuming it is as it seems to be utilizing the reenumerate tool from the IO Kit that I've seen in the Apple development tools. Any help would be great! I'm trying to find a script that my keyboard player can use on our soft synth rig so that if there's any time our MIDI/USB keyboard loses connection they can just launch an Automator/Applescript that will execute a process in which one of the subprocesses will be reenumeration. Let me know if you think this would help me out, and thanks in advance!

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