/* Target-only port suspend/resume. Never seizes an owner, detaches a driver, or resets a hub. */ #include #include #include #include #include #include #include #include #define REQUIRE(c) do { if (!(c)) { fprintf(stderr, "Failed at line %d: %s\n", __LINE__, #c); exit(1); } } while (0) static IOUSBDeviceInterface500 **device; static bool opened, suspended; static void cleanup(void) { if (suspended) { IOReturn result = (*device)->USBDeviceSuspend(device, false); fprintf(stderr, "Cleanup resume: 0x%08x\n", (unsigned int)result); } if (opened) (*device)->USBDeviceClose(device); if (device) (*device)->Release(device); } static UInt32 information(const char *stage) { UInt32 value = 0; IOReturn result = (*device)->GetUSBDeviceInformation(device, &value); printf("{\"stage\":\"%s\",\"result\":\"0x%08x\",\"information\":%u,\"suspended\":%s}\n", stage, (unsigned int)result, value, (value & kUSBInformationDeviceIsSuspendedMask) ? "true" : "false"); fflush(stdout); REQUIRE(result == kIOReturnSuccess); return value; } int main(int count, char **arguments) { REQUIRE(count == 2 && strlen(arguments[1]) == 16); CFStringRef expected = CFStringCreateWithCString(NULL, arguments[1], kCFStringEncodingUTF8); REQUIRE(expected != NULL); io_iterator_t iterator; REQUIRE(IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOUSBHostDevice"), &iterator) == KERN_SUCCESS); io_service_t selected = IO_OBJECT_NULL, service; unsigned matches = 0; while ((service = IOIteratorNext(iterator))) { CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR("USB Serial Number"), NULL, 0); CFTypeRef vid = IORegistryEntryCreateCFProperty(service, CFSTR("idVendor"), NULL, 0); CFTypeRef pid = IORegistryEntryCreateCFProperty(service, CFSTR("idProduct"), NULL, 0); int vendor = 0, product = 0; if (vid && CFGetTypeID(vid) == CFNumberGetTypeID()) CFNumberGetValue(vid, kCFNumberIntType, &vendor); if (pid && CFGetTypeID(pid) == CFNumberGetTypeID()) CFNumberGetValue(pid, kCFNumberIntType, &product); bool match = serial && CFGetTypeID(serial) == CFStringGetTypeID() && CFEqual(serial, expected) && vendor == 0xcafe && product == 0x4014; if (serial) CFRelease(serial); if (vid) CFRelease(vid); if (pid) CFRelease(pid); if (match) { ++matches; if (!selected) selected = service; else IOObjectRelease(service); } else IOObjectRelease(service); } IOObjectRelease(iterator); CFRelease(expected); REQUIRE(matches == 1); IOCFPlugInInterface **plugin = NULL; SInt32 score = 0; REQUIRE(IOCreatePlugInInterfaceForService(selected, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score) == kIOReturnSuccess); IOObjectRelease(selected); REQUIRE((*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID *)&device) == S_OK); (*plugin)->Release(plugin); REQUIRE(atexit(cleanup) == 0); UInt16 vendor = 0, product = 0; REQUIRE((*device)->GetDeviceVendor(device, &vendor) == kIOReturnSuccess && vendor == 0xcafe); REQUIRE((*device)->GetDeviceProduct(device, &product) == kIOReturnSuccess && product == 0x4014); REQUIRE(!(information("before_open") & kUSBInformationDeviceIsSuspendedMask)); IOReturn result = (*device)->USBDeviceOpen(device); printf("{\"stage\":\"open_without_seize\",\"result\":\"0x%08x\"}\n", (unsigned int)result); fflush(stdout); REQUIRE(result == kIOReturnSuccess); opened = true; result = (*device)->USBDeviceSuspend(device, true); printf("{\"stage\":\"suspend\",\"result\":\"0x%08x\"}\n", (unsigned int)result); REQUIRE(result == kIOReturnSuccess); suspended = true; struct timespec pause = {.tv_sec = 1}; nanosleep(&pause, NULL); REQUIRE(information("after_suspend") & kUSBInformationDeviceIsSuspendedMask); result = (*device)->USBDeviceSuspend(device, false); printf("{\"stage\":\"resume\",\"result\":\"0x%08x\"}\n", (unsigned int)result); REQUIRE(result == kIOReturnSuccess); suspended = false; REQUIRE(!(information("after_resume") & kUSBInformationDeviceIsSuspendedMask)); return 0; }