blob: 49e1eef5b6986ed81bbf08b213808e7c161032e4 [file] [log] [blame]
/*
* 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;
}
}