diff options
Diffstat (limited to 'adb/usb_osx.c')
-rw-r--r-- | adb/usb_osx.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/adb/usb_osx.c b/adb/usb_osx.c new file mode 100644 index 0000000..49e1eef --- /dev/null +++ b/adb/usb_osx.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <CoreFoundation/CoreFoundation.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/IOMessage.h> +#include <mach/mach_port.h> + +#include "sysdeps.h" + +#include <stdio.h> + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +#define DBG D + +typedef struct { + int vid; + int pid; +} VendorProduct; + +#define kSupportedDeviceCount 4 +VendorProduct kSupportedDevices[kSupportedDeviceCount] = { + { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER }, + { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP }, + { VENDOR_ID_HTC, PRODUCT_ID_DREAM }, + { VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP }, +}; + +static IONotificationPortRef notificationPort = 0; +static io_iterator_t notificationIterators[kSupportedDeviceCount]; + +struct usb_handle +{ + UInt8 bulkIn; + UInt8 bulkOut; + IOUSBInterfaceInterface **interface; + io_object_t usbNotification; + unsigned int zero_mask; +}; + +static CFRunLoopRef currentRunLoop = 0; +static pthread_mutex_t start_lock; +static pthread_cond_t start_cond; + + +static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator); +static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument); +static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product); + +static int +InitUSB() +{ + CFMutableDictionaryRef matchingDict; + CFRunLoopSourceRef runLoopSource; + SInt32 vendor, product; + int i; + + //* To set up asynchronous notifications, create a notification port and + //* add its run loop event source to the program's run loop + notificationPort = IONotificationPortCreate(kIOMasterPortDefault); + runLoopSource = IONotificationPortGetRunLoopSource(notificationPort); + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode); + + memset(notificationIterators, 0, sizeof(notificationIterators)); + + //* loop through all supported vendor/product pairs + for (i = 0; i < kSupportedDeviceCount; i++) { + //* Create our matching dictionary to find the Android device + //* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (!matchingDict) { + DBG("ERR: Couldn't create USB matching dictionary.\n"); + return -1; + } + + //* Set up two matching dictionaries, one for each product ID we support. + //* This will cause the kernel to notify us only if the vendor and product IDs match. + vendor = kSupportedDevices[i].vid; + product = kSupportedDevices[i].pid; + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor)); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product)); + + //* Now set up two notifications: one to be called when a raw device + //* is first matched by the I/O Kit and another to be called when the + //* device is terminated. + //* we need to do this with each matching dictionary. + IOServiceAddMatchingNotification( + notificationPort, + kIOFirstMatchNotification, + matchingDict, + AndroidDeviceAdded, + NULL, + ¬ificationIterators[i]); + + //* Iterate over set of matching devices to access already-present devices + //* and to arm the notification + AndroidDeviceAdded(NULL, notificationIterators[i]); + } + + return 0; +} + +static void +AndroidDeviceAdded(void *refCon, io_iterator_t iterator) +{ + kern_return_t kr; + io_service_t usbDevice; + IOCFPlugInInterface **plugInInterface = NULL; + IOUSBDeviceInterface182 **dev = NULL; + HRESULT result; + SInt32 score; + UInt16 vendor; + UInt16 product; + UInt8 serialIndex; + char serial[256]; + + while ((usbDevice = IOIteratorNext(iterator))) { + //* Create an intermediate plugin + kr = IOCreatePlugInInterfaceForService(usbDevice, + kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, &score); + + if ((kIOReturnSuccess != kr) || (!plugInInterface)) { + DBG("ERR: Unable to create a plug-in (%08x)\n", kr); + goto continue1; + } + + //* Now create the device interface + result = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev); + + if (result || !dev) { + DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result); + goto continue2; + } + + //* Check the device to see if it's ours + kr = (*dev)->GetDeviceVendor(dev, &vendor); + kr = (*dev)->GetDeviceProduct(dev, &product); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); + + if (serialIndex > 0) { + IOUSBDevRequest req; + UInt16 buffer[256]; + + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = (kUSBStringDesc << 8) | serialIndex; + req.wIndex = 0; + req.pData = buffer; + req.wLength = sizeof(buffer); + kr = (*dev)->DeviceRequest(dev, &req); + + if (kr == kIOReturnSuccess && req.wLenDone > 0) { + int i, count; + + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + count = (req.wLenDone - 1) / 2; + for (i = 0; i < count; i++) + serial[i] = buffer[i + 1]; + serial[i] = 0; + } + } + + usb_handle* handle = NULL; + + //* Open the device + kr = (*dev)->USBDeviceOpen(dev); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Could not open device: %08x\n", kr); + goto continue3; + } else { + //* Find an interface for the device + handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product); + } + + if (handle == NULL) { + DBG("ERR: Could not find device interface: %08x\n", kr); + (*dev)->USBDeviceClose(dev); + goto continue3; + } + + DBG("AndroidDeviceAdded calling register_usb_transport\n"); + register_usb_transport(handle, (serial[0] ? serial : NULL)); + + // Register for an interest notification of this device being removed. Pass the reference to our + // private data as the refCon for the notification. + kr = IOServiceAddInterestNotification(notificationPort, + usbDevice, + kIOGeneralInterest, + AndroidDeviceNotify, + handle, + &handle->usbNotification); + if (kIOReturnSuccess != kr) { + DBG("ERR: Unable to create interest notification (%08x)\n", kr); + } + +continue3: + (void)(*dev)->Release(dev); +continue2: + IODestroyPlugInInterface(plugInInterface); +continue1: + IOObjectRelease(usbDevice); + } +} + +static void +AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) +{ + usb_handle *handle = (usb_handle *)refCon; + + if (messageType == kIOMessageServiceIsTerminated) { + DBG("AndroidDeviceNotify\n"); + IOObjectRelease(handle->usbNotification); + usb_kick(handle); + } +} + +static usb_handle* +FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product) +{ + usb_handle* handle = NULL; + IOReturn kr; + IOUSBFindInterfaceRequest request; + io_iterator_t iterator; + io_service_t usbInterface; + IOCFPlugInInterface **plugInInterface; + IOUSBInterfaceInterface **interface = NULL; + HRESULT result; + SInt32 score; + UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol; + UInt8 endpoint, configuration; + + //* Placing the constant KIOUSBFindInterfaceDontCare into the following + //* fields of the IOUSBFindInterfaceRequest structure will allow us to + //* find all of the interfaces + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + //* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary. + configuration = 0; + (*dev)->GetConfiguration(dev, &configuration); + if (configuration != 1) + (*dev)->SetConfiguration(dev, 1); + + //* Get an iterator for the interfaces on the device + kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr); + return NULL; + } + + while ((usbInterface = IOIteratorNext(iterator))) { + //* Create an intermediate plugin + kr = IOCreatePlugInInterfaceForService( + usbInterface, + kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score); + + //* No longer need the usbInterface object now that we have the plugin + (void) IOObjectRelease(usbInterface); + + if ((kr != kIOReturnSuccess) || (!plugInInterface)) { + DBG("ERR: Unable to create plugin (%08x)\n", kr); + break; + } + + //* Now create the interface interface for the interface + result = (*plugInInterface)->QueryInterface( + plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID) &interface); + + //* No longer need the intermediate plugin + (*plugInInterface)->Release(plugInInterface); + + if (result || !interface) { + DBG("ERR: Couldn't create interface interface: (%08x)\n", + (unsigned int) result); + break; + } + + //* Now open the interface. This will cause the pipes associated with + //* the endpoints in the interface descriptor to be instantiated + kr = (*interface)->USBInterfaceOpen(interface); + + if (kr != kIOReturnSuccess) + { + DBG("ERR: Could not open interface: (%08x)\n", kr); + (void) (*interface)->Release(interface); + //* continue so we can try the next interface + continue; + } + + //* Get the number of endpoints associated with this interface + kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints); + + if (kr != kIOReturnSuccess) { + DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr); + goto next_interface; + } + + //* Get interface class, subclass and protocol + if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess || + (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess || + (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) + { + DBG("ERR: Unable to get interface class, subclass and protocol\n"); + goto next_interface; + } + + //* check to make sure interface class, subclass and protocol match ADB + //* avoid opening mass storage endpoints + if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) { + handle = calloc(1, sizeof(usb_handle)); + + //* Iterate over the endpoints for this interface and find the first + //* bulk in/out pipes available. These will be our read/write pipes. + for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) { + UInt8 transferType; + UInt16 maxPacketSize; + UInt8 interval; + UInt8 number; + UInt8 direction; + + kr = (*interface)->GetPipeProperties(interface, endpoint, &direction, + &number, &transferType, &maxPacketSize, &interval); + + if (kIOReturnSuccess == kr) { + if (kUSBBulk != transferType) + continue; + + if (kUSBIn == direction) + handle->bulkIn = endpoint; + + if (kUSBOut == direction) + handle->bulkOut = endpoint; + + if (interfaceProtocol == 0x01) { + handle->zero_mask = maxPacketSize - 1; + } + + } else { + DBG("ERR: FindDeviceInterface - could not get pipe properties\n"); + } + } + + handle->interface = interface; + break; + } + +next_interface: + (*interface)->USBInterfaceClose(interface); + (*interface)->Release(interface); + } + + return handle; +} + + +void* RunLoopThread(void* unused) +{ + int i; + + InitUSB(); + + currentRunLoop = CFRunLoopGetCurrent(); + + // Signal the parent that we are running + adb_mutex_lock(&start_lock); + adb_cond_signal(&start_cond); + adb_mutex_unlock(&start_lock); + + CFRunLoopRun(); + currentRunLoop = 0; + + for (i = 0; i < kSupportedDeviceCount; i++) { + IOObjectRelease(notificationIterators[i]); + } + IONotificationPortDestroy(notificationPort); + + DBG("RunLoopThread done\n"); + return NULL; +} + + +static int initialized = 0; +void usb_init() +{ + if (!initialized) + { + adb_thread_t tid; + + adb_mutex_init(&start_lock, NULL); + adb_cond_init(&start_cond, NULL); + + if(adb_thread_create(&tid, RunLoopThread, NULL)) + fatal_errno("cannot create input thread"); + + // Wait for initialization to finish + adb_mutex_lock(&start_lock); + adb_cond_wait(&start_cond, &start_lock); + adb_mutex_unlock(&start_lock); + + adb_mutex_destroy(&start_lock); + adb_cond_destroy(&start_cond); + + initialized = 1; + } +} + +void usb_cleanup() +{ + DBG("usb_cleanup\n"); + close_usb_devices(); + if (currentRunLoop) + CFRunLoopStop(currentRunLoop); +} + +int usb_write(usb_handle *handle, const void *buf, int len) +{ + IOReturn result; + + if (!len) + return 0; + + if (!handle) + return -1; + + if (NULL == handle->interface) { + DBG("ERR: usb_write interface was null\n"); + return -1; + } + + if (0 == handle->bulkOut) { + DBG("ERR: bulkOut endpoint not assigned\n"); + return -1; + } + + result = + (*handle->interface)->WritePipe( + handle->interface, handle->bulkOut, (void *)buf, len); + + if ((result == 0) && (handle->zero_mask)) { + /* we need 0-markers and our transfer */ + if(!(len & handle->zero_mask)) { + result = + (*handle->interface)->WritePipe( + handle->interface, handle->bulkOut, (void *)buf, 0); + } + } + + if (0 == result) + return 0; + + DBG("ERR: usb_write failed with status %d\n", result); + return -1; +} + +int usb_read(usb_handle *handle, void *buf, int len) +{ + IOReturn result; + UInt32 numBytes = len; + + if (!len) { + return 0; + } + + if (!handle) { + return -1; + } + + if (NULL == handle->interface) { + DBG("ERR: usb_read interface was null\n"); + return -1; + } + + if (0 == handle->bulkIn) { + DBG("ERR: bulkIn endpoint not assigned\n"); + return -1; + } + + result = + (*handle->interface)->ReadPipe(handle->interface, + handle->bulkIn, buf, &numBytes); + + if (0 == result) + return 0; + else { + DBG("ERR: usb_read failed with status %d\n", result); + } + + return -1; +} + +int usb_close(usb_handle *handle) +{ + return 0; +} + +void usb_kick(usb_handle *handle) +{ + /* release the interface */ + if (handle->interface) + { + (*handle->interface)->USBInterfaceClose(handle->interface); + (*handle->interface)->Release(handle->interface); + handle->interface = 0; + } +} |