blob: fb1b073168ddb1a7d870b9c27fd5bd3874db29f2 [file] [log] [blame]
Mike Lockwood5ed68d22010-05-25 19:08:48 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Mike Lockwooda6c490b2010-06-05 22:45:01 -040017#define LOG_TAG "MtpDevice"
Mike Lockwoodb14e5882010-06-29 18:11:52 -040018
19#include "MtpDebug.h"
20#include "MtpDevice.h"
21#include "MtpDeviceInfo.h"
22#include "MtpObjectInfo.h"
23#include "MtpProperty.h"
24#include "MtpStorageInfo.h"
25#include "MtpStringBuffer.h"
Mike Lockwood0cf89f22010-07-26 20:40:45 -040026#include "MtpUtils.h"
Mike Lockwooda6c490b2010-06-05 22:45:01 -040027
Mike Lockwood5ed68d22010-05-25 19:08:48 -040028#include <stdio.h>
29#include <stdlib.h>
30#include <sys/types.h>
31#include <sys/ioctl.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <errno.h>
Mike Lockwood0cf89f22010-07-26 20:40:45 -040035#include <endian.h>
Mike Lockwood5ed68d22010-05-25 19:08:48 -040036
37#include <usbhost/usbhost.h>
38
Mike Lockwood5ed68d22010-05-25 19:08:48 -040039namespace android {
40
Mike Lockwood23f1b332010-12-30 15:38:45 -050041static bool isMtpDevice(uint16_t vendor, uint16_t product) {
42 // Sandisk Sansa Fuze
43 if (vendor == 0x0781 && product == 0x74c2)
44 return true;
45 // Samsung YP-Z5
46 if (vendor == 0x04e8 && product == 0x503c)
47 return true;
48 return false;
49}
50
51MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
52 struct usb_device *device = usb_device_new(deviceName, fd);
53 if (!device) {
54 LOGE("usb_device_new failed for %s", deviceName);
55 return NULL;
56 }
57
58 struct usb_descriptor_header* desc;
59 struct usb_descriptor_iter iter;
60
61 usb_descriptor_iter_init(device, &iter);
62
63 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
64 if (desc->bDescriptorType == USB_DT_INTERFACE) {
65 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
66
67 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
68 interface->bInterfaceSubClass == 1 && // Still Image Capture
69 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
70 {
Kenny Root31c52e72011-02-02 13:43:55 -080071 char* manufacturerName = usb_device_get_manufacturer_name(device);
72 char* productName = usb_device_get_product_name(device);
73 LOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
74 free(manufacturerName);
75 free(productName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050076 } else if (interface->bInterfaceClass == 0xFF &&
77 interface->bInterfaceSubClass == 0xFF &&
78 interface->bInterfaceProtocol == 0) {
79 char* interfaceName = usb_device_get_string(device, interface->iInterface);
Kenny Root31c52e72011-02-02 13:43:55 -080080 if (!interfaceName) {
Mike Lockwood23f1b332010-12-30 15:38:45 -050081 continue;
Kenny Root31c52e72011-02-02 13:43:55 -080082 } else if (strcmp(interfaceName, "MTP")) {
83 free(interfaceName);
84 continue;
85 }
86 free(interfaceName);
87
Mike Lockwood23f1b332010-12-30 15:38:45 -050088 // Looks like an android style MTP device
Kenny Root31c52e72011-02-02 13:43:55 -080089 char* manufacturerName = usb_device_get_manufacturer_name(device);
90 char* productName = usb_device_get_product_name(device);
91 LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
92 free(manufacturerName);
93 free(productName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050094 } else {
95 // look for special cased devices based on vendor/product ID
96 // we are doing this mainly for testing purposes
97 uint16_t vendor = usb_device_get_vendor_id(device);
98 uint16_t product = usb_device_get_product_id(device);
99 if (!isMtpDevice(vendor, product)) {
100 // not an MTP or PTP device
101 continue;
102 }
103 // request MTP OS string and descriptor
104 // some music players need to see this before entering MTP mode.
105 char buffer[256];
106 memset(buffer, 0, sizeof(buffer));
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800107 int ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500108 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
109 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800110 0, buffer, sizeof(buffer), 0);
111 printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500112 if (ret > 0) {
113 printf("got MTP string %s\n", buffer);
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800114 ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500115 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800116 0, 4, buffer, sizeof(buffer), 0);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500117 printf("OS descriptor got %d\n", ret);
118 } else {
119 printf("no MTP string\n");
120 }
121 }
122
123 // if we got here, then we have a likely MTP or PTP device
124
125 // interface should be followed by three endpoints
126 struct usb_endpoint_descriptor *ep;
127 struct usb_endpoint_descriptor *ep_in_desc = NULL;
128 struct usb_endpoint_descriptor *ep_out_desc = NULL;
129 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
130 for (int i = 0; i < 3; i++) {
131 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
132 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
133 LOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800134 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500135 return NULL;
136 }
137 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
138 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
139 ep_in_desc = ep;
140 else
141 ep_out_desc = ep;
142 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
143 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
144 ep_intr_desc = ep;
145 }
146 }
147 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
148 LOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800149 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500150 return NULL;
151 }
152
153 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
154 LOGE("usb_device_claim_interface failed errno: %d\n", errno);
Kenny Root31c52e72011-02-02 13:43:55 -0800155 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500156 return NULL;
157 }
158
159 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
160 ep_in_desc, ep_out_desc, ep_intr_desc);
161 mtpDevice->initialize();
162 return mtpDevice;
163 }
164 }
165
166 usb_device_close(device);
167 LOGE("device not found");
168 return NULL;
169}
170
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400171MtpDevice::MtpDevice(struct usb_device* device, int interface,
Mike Lockwood42d0b792011-01-04 14:48:57 -0500172 const struct usb_endpoint_descriptor *ep_in,
173 const struct usb_endpoint_descriptor *ep_out,
174 const struct usb_endpoint_descriptor *ep_intr)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400175 : mDevice(device),
176 mInterface(interface),
Mike Lockwood42d0b792011-01-04 14:48:57 -0500177 mRequestIn1(NULL),
178 mRequestIn2(NULL),
179 mRequestOut(NULL),
180 mRequestIntr(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400181 mDeviceInfo(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400182 mSessionID(0),
Mike Lockwoodf7454622010-12-09 18:34:18 -0800183 mTransactionID(0),
184 mReceivedResponse(false)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400185{
Mike Lockwood42d0b792011-01-04 14:48:57 -0500186 mRequestIn1 = usb_request_new(device, ep_in);
187 mRequestIn2 = usb_request_new(device, ep_in);
188 mRequestOut = usb_request_new(device, ep_out);
189 mRequestIntr = usb_request_new(device, ep_intr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400190}
191
192MtpDevice::~MtpDevice() {
193 close();
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400194 for (int i = 0; i < mDeviceProperties.size(); i++)
195 delete mDeviceProperties[i];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500196 usb_request_free(mRequestIn1);
197 usb_request_free(mRequestIn2);
198 usb_request_free(mRequestOut);
199 usb_request_free(mRequestIntr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400200}
201
202void MtpDevice::initialize() {
203 openSession();
204 mDeviceInfo = getDeviceInfo();
205 if (mDeviceInfo) {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400206 if (mDeviceInfo->mDeviceProperties) {
207 int count = mDeviceInfo->mDeviceProperties->size();
208 for (int i = 0; i < count; i++) {
209 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
210 MtpProperty* property = getDevicePropDesc(propCode);
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800211 if (property)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400212 mDeviceProperties.push(property);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400213 }
214 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400215 }
216}
217
218void MtpDevice::close() {
219 if (mDevice) {
220 usb_device_release_interface(mDevice, mInterface);
221 usb_device_close(mDevice);
222 mDevice = NULL;
223 }
224}
225
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800226void MtpDevice::print() {
227 if (mDeviceInfo) {
228 mDeviceInfo->print();
229
230 if (mDeviceInfo->mDeviceProperties) {
231 LOGI("***** DEVICE PROPERTIES *****\n");
232 int count = mDeviceInfo->mDeviceProperties->size();
233 for (int i = 0; i < count; i++) {
234 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
235 MtpProperty* property = getDevicePropDesc(propCode);
236 if (property) {
237 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800238 delete property;
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800239 }
240 }
241 }
242 }
243
244 if (mDeviceInfo->mPlaybackFormats) {
245 LOGI("***** OBJECT PROPERTIES *****\n");
246 int count = mDeviceInfo->mPlaybackFormats->size();
247 for (int i = 0; i < count; i++) {
248 MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
249 LOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
250 MtpObjectPropertyList* props = getObjectPropsSupported(format);
251 if (props) {
252 for (int j = 0; j < props->size(); j++) {
253 MtpObjectProperty prop = (*props)[j];
Mike Lockwood99e393a2010-12-07 18:53:04 -0800254 MtpProperty* property = getObjectPropDesc(prop, format);
Kenny Root31c52e72011-02-02 13:43:55 -0800255 if (property) {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800256 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800257 delete property;
258 } else {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800259 LOGE("could not fetch property: %s",
260 MtpDebug::getObjectPropCodeName(prop));
Kenny Root31c52e72011-02-02 13:43:55 -0800261 }
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800262 }
263 }
264 }
265 }
266}
267
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400268const char* MtpDevice::getDeviceName() {
269 if (mDevice)
270 return usb_device_get_name(mDevice);
271 else
272 return "???";
273}
274
275bool MtpDevice::openSession() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400276 Mutex::Autolock autoLock(mMutex);
277
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400278 mSessionID = 0;
279 mTransactionID = 0;
280 MtpSessionID newSession = 1;
281 mRequest.reset();
282 mRequest.setParameter(1, newSession);
283 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
284 return false;
285 MtpResponseCode ret = readResponse();
286 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
287 newSession = mResponse.getParameter(1);
288 else if (ret != MTP_RESPONSE_OK)
289 return false;
290
291 mSessionID = newSession;
292 mTransactionID = 1;
293 return true;
294}
295
296bool MtpDevice::closeSession() {
297 // FIXME
298 return true;
299}
300
301MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400302 Mutex::Autolock autoLock(mMutex);
303
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400304 mRequest.reset();
305 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
306 return NULL;
307 if (!readData())
308 return NULL;
309 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400310 if (ret == MTP_RESPONSE_OK) {
311 MtpDeviceInfo* info = new MtpDeviceInfo;
312 info->read(mData);
313 return info;
314 }
315 return NULL;
316}
317
318MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400319 Mutex::Autolock autoLock(mMutex);
320
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400321 mRequest.reset();
322 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
323 return NULL;
324 if (!readData())
325 return NULL;
326 MtpResponseCode ret = readResponse();
327 if (ret == MTP_RESPONSE_OK) {
328 return mData.getAUInt32();
329 }
330 return NULL;
331}
332
333MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400334 Mutex::Autolock autoLock(mMutex);
335
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400336 mRequest.reset();
337 mRequest.setParameter(1, storageID);
338 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
339 return NULL;
340 if (!readData())
341 return NULL;
342 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400343 if (ret == MTP_RESPONSE_OK) {
344 MtpStorageInfo* info = new MtpStorageInfo(storageID);
345 info->read(mData);
346 return info;
347 }
348 return NULL;
349}
350
351MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
352 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400353 Mutex::Autolock autoLock(mMutex);
354
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400355 mRequest.reset();
356 mRequest.setParameter(1, storageID);
357 mRequest.setParameter(2, format);
358 mRequest.setParameter(3, parent);
359 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
360 return NULL;
361 if (!readData())
362 return NULL;
363 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400364 if (ret == MTP_RESPONSE_OK) {
365 return mData.getAUInt32();
366 }
367 return NULL;
368}
369
370MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400371 Mutex::Autolock autoLock(mMutex);
372
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400373 // FIXME - we might want to add some caching here
374
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400375 mRequest.reset();
376 mRequest.setParameter(1, handle);
377 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
378 return NULL;
379 if (!readData())
380 return NULL;
381 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400382 if (ret == MTP_RESPONSE_OK) {
383 MtpObjectInfo* info = new MtpObjectInfo(handle);
384 info->read(mData);
385 return info;
386 }
387 return NULL;
388}
389
Mike Lockwood3e072b32010-06-10 16:34:20 -0400390void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400391 Mutex::Autolock autoLock(mMutex);
392
Mike Lockwood3e072b32010-06-10 16:34:20 -0400393 mRequest.reset();
394 mRequest.setParameter(1, handle);
395 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
396 MtpResponseCode ret = readResponse();
397 if (ret == MTP_RESPONSE_OK) {
398 return mData.getData(outLength);
399 }
400 }
401 outLength = 0;
402 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400403}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400404
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400405MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
406 Mutex::Autolock autoLock(mMutex);
407
408 mRequest.reset();
409 MtpObjectHandle parent = info->mParent;
410 if (parent == 0)
411 parent = MTP_PARENT_ROOT;
412
413 mRequest.setParameter(1, info->mStorageID);
414 mRequest.setParameter(2, info->mParent);
415
416 mData.putUInt32(info->mStorageID);
417 mData.putUInt16(info->mFormat);
418 mData.putUInt16(info->mProtectionStatus);
419 mData.putUInt32(info->mCompressedSize);
420 mData.putUInt16(info->mThumbFormat);
421 mData.putUInt32(info->mThumbCompressedSize);
422 mData.putUInt32(info->mThumbPixWidth);
423 mData.putUInt32(info->mThumbPixHeight);
424 mData.putUInt32(info->mImagePixWidth);
425 mData.putUInt32(info->mImagePixHeight);
426 mData.putUInt32(info->mImagePixDepth);
427 mData.putUInt32(info->mParent);
428 mData.putUInt16(info->mAssociationType);
429 mData.putUInt32(info->mAssociationDesc);
430 mData.putUInt32(info->mSequenceNumber);
431 mData.putString(info->mName);
432
433 char created[100], modified[100];
434 formatDateTime(info->mDateCreated, created, sizeof(created));
435 formatDateTime(info->mDateModified, modified, sizeof(modified));
436
437 mData.putString(created);
438 mData.putString(modified);
439 if (info->mKeywords)
440 mData.putString(info->mKeywords);
441 else
442 mData.putEmptyString();
443
444 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400445 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400446 if (ret == MTP_RESPONSE_OK) {
447 info->mStorageID = mResponse.getParameter(1);
448 info->mParent = mResponse.getParameter(2);
449 info->mHandle = mResponse.getParameter(3);
450 return info->mHandle;
451 }
452 }
453 return (MtpObjectHandle)-1;
454}
455
456bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
457 Mutex::Autolock autoLock(mMutex);
458
459 int remaining = info->mCompressedSize;
460 mRequest.reset();
461 mRequest.setParameter(1, info->mHandle);
462 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400463 // send data header
464 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
465
466 char buffer[65536];
467 while (remaining > 0) {
468 int count = read(srcFD, buffer, sizeof(buffer));
469 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500470 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400471 // FIXME check error
472 remaining -= count;
473 } else {
474 break;
475 }
476 }
477 }
478 MtpResponseCode ret = readResponse();
479 return (remaining == 0 && ret == MTP_RESPONSE_OK);
480}
481
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400482bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400483 Mutex::Autolock autoLock(mMutex);
484
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400485 mRequest.reset();
486 mRequest.setParameter(1, handle);
487 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
488 MtpResponseCode ret = readResponse();
489 if (ret == MTP_RESPONSE_OK)
490 return true;
491 }
492 return false;
493}
494
495MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
496 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800497 if (info) {
498 MtpObjectHandle parent = info->mParent;
499 delete info;
500 return parent;
501 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400502 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800503 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400504}
505
506MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
507 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800508 if (info) {
509 MtpObjectHandle storageId = info->mStorageID;
510 delete info;
511 return storageId;
512 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400513 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800514 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400515}
516
Mike Lockwood98693f62010-12-07 10:58:56 -0800517MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
518 Mutex::Autolock autoLock(mMutex);
519
520 mRequest.reset();
521 mRequest.setParameter(1, format);
522 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
523 return NULL;
524 if (!readData())
525 return NULL;
526 MtpResponseCode ret = readResponse();
527 if (ret == MTP_RESPONSE_OK) {
528 return mData.getAUInt16();
529 }
530 return NULL;
531
532}
533
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400534MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400535 Mutex::Autolock autoLock(mMutex);
536
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400537 mRequest.reset();
538 mRequest.setParameter(1, code);
539 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
540 return NULL;
541 if (!readData())
542 return NULL;
543 MtpResponseCode ret = readResponse();
544 if (ret == MTP_RESPONSE_OK) {
545 MtpProperty* property = new MtpProperty;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400546 property->read(mData);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400547 return property;
548 }
549 return NULL;
550}
551
Mike Lockwood99e393a2010-12-07 18:53:04 -0800552MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800553 Mutex::Autolock autoLock(mMutex);
554
555 mRequest.reset();
556 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800557 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800558 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
559 return NULL;
560 if (!readData())
561 return NULL;
562 MtpResponseCode ret = readResponse();
563 if (ret == MTP_RESPONSE_OK) {
564 MtpProperty* property = new MtpProperty;
565 property->read(mData);
566 return property;
567 }
568 return NULL;
569}
570
Mike Lockwood23f1b332010-12-30 15:38:45 -0500571bool MtpDevice::readObject(MtpObjectHandle handle,
572 bool (* callback)(void* data, int offset, int length, void* clientData),
573 int objectSize, void* clientData) {
574 Mutex::Autolock autoLock(mMutex);
575 bool result = false;
576
577 mRequest.reset();
578 mRequest.setParameter(1, handle);
579 if (sendRequest(MTP_OPERATION_GET_OBJECT)
580 && mData.readDataHeader(mRequestIn1)) {
581 uint32_t length = mData.getContainerLength();
582 if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
583 LOGE("readObject error objectSize: %d, length: %d",
584 objectSize, length);
585 goto fail;
586 }
587 length -= MTP_CONTAINER_HEADER_SIZE;
588 uint32_t remaining = length;
589 int offset = 0;
590
591 int initialDataLength = 0;
592 void* initialData = mData.getData(initialDataLength);
593 if (initialData) {
594 if (initialDataLength > 0) {
595 if (!callback(initialData, 0, initialDataLength, clientData))
596 goto fail;
597 remaining -= initialDataLength;
598 offset += initialDataLength;
599 }
600 free(initialData);
601 }
602
603 // USB reads greater than 16K don't work
604 char buffer1[16384], buffer2[16384];
605 mRequestIn1->buffer = buffer1;
606 mRequestIn2->buffer = buffer2;
607 struct usb_request* req = mRequestIn1;
608 void* writeBuffer = NULL;
609 int writeLength = 0;
610
611 while (remaining > 0 || writeBuffer) {
612 if (remaining > 0) {
613 // queue up a read request
614 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
615 if (mData.readDataAsync(req)) {
616 LOGE("readDataAsync failed");
617 goto fail;
618 }
619 } else {
620 req = NULL;
621 }
622
623 if (writeBuffer) {
624 // write previous buffer
625 if (!callback(writeBuffer, offset, writeLength, clientData)) {
626 LOGE("write failed");
627 // wait for pending read before failing
628 if (req)
629 mData.readDataWait(mDevice);
630 goto fail;
631 }
632 offset += writeLength;
633 writeBuffer = NULL;
634 }
635
636 // wait for read to complete
637 if (req) {
638 int read = mData.readDataWait(mDevice);
639 if (read < 0)
640 goto fail;
641
642 if (read > 0) {
643 writeBuffer = req->buffer;
644 writeLength = read;
645 remaining -= read;
646 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
647 } else {
648 writeBuffer = NULL;
649 }
650 }
651 }
652
653 MtpResponseCode response = readResponse();
654 if (response == MTP_RESPONSE_OK)
655 result = true;
656 }
657
658fail:
659 return result;
660}
661
662
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500663// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500664bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500665 LOGD("readObject: %s", destPath);
666 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
667 if (fd < 0) {
668 LOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400669 return false;
670 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400671
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500672 fchown(fd, getuid(), group);
673 // set permissions
674 int mask = umask(0);
675 fchmod(fd, perm);
676 umask(mask);
677
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500678 Mutex::Autolock autoLock(mMutex);
679 bool result = false;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400680
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500681 mRequest.reset();
682 mRequest.setParameter(1, handle);
683 if (sendRequest(MTP_OPERATION_GET_OBJECT)
Mike Lockwood42d0b792011-01-04 14:48:57 -0500684 && mData.readDataHeader(mRequestIn1)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500685 uint32_t length = mData.getContainerLength();
686 if (length < MTP_CONTAINER_HEADER_SIZE)
687 goto fail;
688 length -= MTP_CONTAINER_HEADER_SIZE;
689 uint32_t remaining = length;
690
691 int initialDataLength = 0;
692 void* initialData = mData.getData(initialDataLength);
693 if (initialData) {
694 if (initialDataLength > 0) {
Kenny Root31c52e72011-02-02 13:43:55 -0800695 if (write(fd, initialData, initialDataLength) != initialDataLength) {
696 free(initialData);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500697 goto fail;
Kenny Root31c52e72011-02-02 13:43:55 -0800698 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500699 remaining -= initialDataLength;
700 }
701 free(initialData);
702 }
703
704 // USB reads greater than 16K don't work
705 char buffer1[16384], buffer2[16384];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500706 mRequestIn1->buffer = buffer1;
707 mRequestIn2->buffer = buffer2;
708 struct usb_request* req = mRequestIn1;
709 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500710 int writeLength = 0;
711
712 while (remaining > 0 || writeBuffer) {
713 if (remaining > 0) {
714 // queue up a read request
Mike Lockwood42d0b792011-01-04 14:48:57 -0500715 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
716 if (mData.readDataAsync(req)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500717 LOGE("readDataAsync failed");
718 goto fail;
719 }
720 } else {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500721 req = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500722 }
723
724 if (writeBuffer) {
725 // write previous buffer
726 if (write(fd, writeBuffer, writeLength) != writeLength) {
727 LOGE("write failed");
728 // wait for pending read before failing
Mike Lockwood42d0b792011-01-04 14:48:57 -0500729 if (req)
730 mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500731 goto fail;
732 }
733 writeBuffer = NULL;
734 }
735
736 // wait for read to complete
Mike Lockwood42d0b792011-01-04 14:48:57 -0500737 if (req) {
738 int read = mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500739 if (read < 0)
740 goto fail;
741
Mike Lockwood23f1b332010-12-30 15:38:45 -0500742 if (read > 0) {
743 writeBuffer = req->buffer;
744 writeLength = read;
745 remaining -= read;
746 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
747 } else {
748 writeBuffer = NULL;
749 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500750 }
751 }
752
753 MtpResponseCode response = readResponse();
754 if (response == MTP_RESPONSE_OK)
755 result = true;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400756 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500757
758fail:
759 ::close(fd);
760 return result;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400761}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400762
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400763bool MtpDevice::sendRequest(MtpOperationCode operation) {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400764 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800765 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400766 mRequest.setOperationCode(operation);
767 if (mTransactionID > 0)
768 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500769 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400770 mRequest.dump();
771 return (ret > 0);
772}
773
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400774bool MtpDevice::sendData() {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400775 LOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400776 mData.setOperationCode(mRequest.getOperationCode());
777 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500778 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400779 mData.dump();
780 return (ret > 0);
781}
782
783bool MtpDevice::readData() {
784 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500785 int ret = mData.read(mRequestIn1);
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400786 LOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400787 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800788 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
789 LOGD("got response packet instead of data packet");
790 // we got a response packet rather than data
791 // copy it to mResponse
792 mResponse.copyFrom(mData);
793 mReceivedResponse = true;
794 return false;
795 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400796 mData.dump();
797 return true;
798 }
799 else {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400800 LOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400801 return false;
802 }
803}
804
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400805bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
806 mData.setOperationCode(operation);
807 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500808 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400809}
810
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400811MtpResponseCode MtpDevice::readResponse() {
Mike Lockwoodf43c6412010-07-27 11:50:34 -0400812 LOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800813 if (mReceivedResponse) {
814 mReceivedResponse = false;
815 return mResponse.getResponseCode();
816 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500817 int ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400818 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
819 mResponse.dump();
820 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800821 } else {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400822 LOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400823 return -1;
824 }
825}
826
827} // namespace android