From 23f1b33c5f88f07510ca5dc01b3afd7af6843d6c Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 30 Dec 2010 15:38:45 -0500 Subject: New APIs for MTP and PTP host support This replaces the previous ContentProvider based interface Change-Id: I4cea2544854adb9fdcc04345e4d73d8ef05380f3 Signed-off-by: Mike Lockwood --- media/mtp/MtpDevice.cpp | 220 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 215 insertions(+), 5 deletions(-) (limited to 'media/mtp/MtpDevice.cpp') diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index d22c72f..d02ed90 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -38,6 +38,121 @@ namespace android { +static bool isMtpDevice(uint16_t vendor, uint16_t product) { + // Sandisk Sansa Fuze + if (vendor == 0x0781 && product == 0x74c2) + return true; + // Samsung YP-Z5 + if (vendor == 0x04e8 && product == 0x503c) + return true; + return false; +} + +MtpDevice* MtpDevice::open(const char* deviceName, int fd) { + struct usb_device *device = usb_device_new(deviceName, fd); + if (!device) { + LOGE("usb_device_new failed for %s", deviceName); + return NULL; + } + + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && + interface->bInterfaceSubClass == 1 && // Still Image Capture + interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) + { + LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), + usb_device_get_product_name(device)); + } else if (interface->bInterfaceClass == 0xFF && + interface->bInterfaceSubClass == 0xFF && + interface->bInterfaceProtocol == 0) { + char* interfaceName = usb_device_get_string(device, interface->iInterface); + if (!interfaceName || strcmp(interfaceName, "MTP")) + continue; + // Looks like an android style MTP device + LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), + usb_device_get_product_name(device)); + } else { + // look for special cased devices based on vendor/product ID + // we are doing this mainly for testing purposes + uint16_t vendor = usb_device_get_vendor_id(device); + uint16_t product = usb_device_get_product_id(device); + if (!isMtpDevice(vendor, product)) { + // not an MTP or PTP device + continue; + } + // request MTP OS string and descriptor + // some music players need to see this before entering MTP mode. + char buffer[256]; + memset(buffer, 0, sizeof(buffer)); + int ret = usb_device_send_control(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, + USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, + 0, sizeof(buffer), buffer); + printf("usb_device_send_control returned %d errno: %d\n", ret, errno); + if (ret > 0) { + printf("got MTP string %s\n", buffer); + ret = usb_device_send_control(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, + 0, 4, sizeof(buffer), buffer); + printf("OS descriptor got %d\n", ret); + } else { + printf("no MTP string\n"); + } + } + + // if we got here, then we have a likely MTP or PTP device + + // interface should be followed by three endpoints + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in_desc = NULL; + struct usb_endpoint_descriptor *ep_out_desc = NULL; + struct usb_endpoint_descriptor *ep_intr_desc = NULL; + for (int i = 0; i < 3; i++) { + ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); + if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { + LOGE("endpoints not found\n"); + return NULL; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + ep_in_desc = ep; + else + ep_out_desc = ep; + } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && + ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + ep_intr_desc = ep; + } + } + if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { + LOGE("endpoints not found\n"); + return NULL; + } + + if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { + LOGE("usb_device_claim_interface failed errno: %d\n", errno); + return NULL; + } + + MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, + ep_in_desc, ep_out_desc, ep_intr_desc); + mtpDevice->initialize(); + return mtpDevice; + } + } + + usb_device_close(device); + LOGE("device not found"); + return NULL; +} + MtpDevice::MtpDevice(struct usb_device* device, int interface, const struct usb_endpoint_descriptor *ep_in, const struct usb_endpoint_descriptor *ep_out, @@ -49,7 +164,6 @@ MtpDevice::MtpDevice(struct usb_device* device, int interface, mRequestOut(NULL), mRequestIntr(NULL), mDeviceInfo(NULL), - mID(usb_device_get_unique_id(device)), mSessionID(0), mTransactionID(0), mReceivedResponse(false) @@ -430,6 +544,98 @@ MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectForma return NULL; } +bool MtpDevice::readObject(MtpObjectHandle handle, + bool (* callback)(void* data, int offset, int length, void* clientData), + int objectSize, void* clientData) { + Mutex::Autolock autoLock(mMutex); + bool result = false; + + mRequest.reset(); + mRequest.setParameter(1, handle); + if (sendRequest(MTP_OPERATION_GET_OBJECT) + && mData.readDataHeader(mRequestIn1)) { + uint32_t length = mData.getContainerLength(); + if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) { + LOGE("readObject error objectSize: %d, length: %d", + objectSize, length); + goto fail; + } + length -= MTP_CONTAINER_HEADER_SIZE; + uint32_t remaining = length; + int offset = 0; + + int initialDataLength = 0; + void* initialData = mData.getData(initialDataLength); + if (initialData) { + if (initialDataLength > 0) { + if (!callback(initialData, 0, initialDataLength, clientData)) + goto fail; + remaining -= initialDataLength; + offset += initialDataLength; + } + free(initialData); + } + + // USB reads greater than 16K don't work + char buffer1[16384], buffer2[16384]; + mRequestIn1->buffer = buffer1; + mRequestIn2->buffer = buffer2; + struct usb_request* req = mRequestIn1; + void* writeBuffer = NULL; + int writeLength = 0; + + while (remaining > 0 || writeBuffer) { + if (remaining > 0) { + // queue up a read request + req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); + if (mData.readDataAsync(req)) { + LOGE("readDataAsync failed"); + goto fail; + } + } else { + req = NULL; + } + + if (writeBuffer) { + // write previous buffer + if (!callback(writeBuffer, offset, writeLength, clientData)) { + LOGE("write failed"); + // wait for pending read before failing + if (req) + mData.readDataWait(mDevice); + goto fail; + } + offset += writeLength; + writeBuffer = NULL; + } + + // wait for read to complete + if (req) { + int read = mData.readDataWait(mDevice); + if (read < 0) + goto fail; + + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } + } + } + + MtpResponseCode response = readResponse(); + if (response == MTP_RESPONSE_OK) + result = true; + } + +fail: + return result; +} + + // reads the object's data and writes it to the specified file path bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { LOGD("readObject: %s", destPath); @@ -507,10 +713,14 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro if (read < 0) goto fail; - writeBuffer = req->buffer; - writeLength = read; - remaining -= read; - req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } } } -- cgit v1.1