summaryrefslogtreecommitdiffstats
path: root/adb/usb_osx.c
diff options
context:
space:
mode:
Diffstat (limited to 'adb/usb_osx.c')
-rw-r--r--adb/usb_osx.c536
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,
+ &notificationIterators[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;
+ }
+}